lib/chant/src/lower/unit.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const choir = @import("choir");
3 const ast = @import("../ast/root.zig");
4 const context = @import("context.zig");
5 const convert = @import("convert.zig");
6 const emit = @import("emit.zig");
7 const memory = @import("memory.zig");
8 const scope = @import("scope.zig");
9 const statement = @import("statement/root.zig");
10 const Error = @import("error.zig").Error;
11
12 const FuncDialect = choir.dialects.FuncDialect;
13 const Lowerer = context.Lowerer;
14
15 pub const Unit = struct {
16 module: choir.dialects.BuiltinDialect.ModuleOp,
17 lowered: std.ArrayListUnmanaged([]const u8),
18 skipped: std.ArrayListUnmanaged(Skipped),
19
20 pub const Skipped = struct {
21 name: []const u8,
22 reason: Error,
23 };
24 };
25
26 pub fn lowerFunction(
27 gpa: std.mem.Allocator,
28 arena: std.mem.Allocator,
29 ctx: *choir.Context,
30 module: choir.dialects.BuiltinDialect.ModuleOp,
31 functions: std.StringHashMapUnmanaged(*const ast.Type),
32 function: ast.decl.Function,
33 ) Error!void {
34 const loc = choir.ir.Location.getUnknown();
35 const return_type = function.type.child orelse return error.UnsupportedType;
36 if (return_type.kind != .void_type) return error.UnsupportedConstruct;
37 const body = function.body orelse return error.UnsupportedConstruct;
38
39 var lowerer = Lowerer{
40 .gpa = gpa,
41 .arena = arena,
42 .ctx = ctx,
43 .loc = loc,
44 .block = undefined,
45 .scopes = .empty,
46 .functions = functions,
47 .terminated = false,
48 };
49 defer {
50 while (lowerer.scopes.items.len > 0) scope.pop(&lowerer);
51 lowerer.scopes.deinit(gpa);
52 }
53
54 var arg_types = std.ArrayListUnmanaged(choir.Type).empty;
55 defer arg_types.deinit(gpa);
56 for (function.type.params) |param| {
57 try arg_types.append(gpa, try convert.valueType(&lowerer, param.type));
58 }
59
60 const func = FuncDialect.FuncOp.create(ctx, loc, function.name, arg_types.items, &.{}) catch return error.OutOfMemory;
61 const entry = func.getEntryBlock();
62 lowerer.block = entry;
63
64 var registered = false;
65 errdefer if (!registered) func.op.erase();
66
67 try scope.push(&lowerer);
68 for (function.type.params, 0..) |param, i| {
69 const argument = func.getArgument(i);
70 if (ast.types.isPointerLike(param.type)) {
71 const element = try convert.pointerElement(param.type);
72 const pointer_c_type = try arena.create(ast.Type);
73 pointer_c_type.* = .{ .kind = .pointer, .child = param.type.child };
74 _ = element;
75 try scope.bind(&lowerer, param.name, .{ .kind = .pointer_param, .value = argument, .c_type = pointer_c_type });
76 } else {
77 const slot = try memory.allocaScalar(&lowerer, param.type);
78 const zero = try emit.indexConstant(&lowerer, 0);
79 try memory.storeElement(&lowerer, .{ .memref = slot, .index = zero, .c_type = param.type }, argument);
80 try scope.bind(&lowerer, param.name, .{ .kind = .slot, .value = slot, .c_type = param.type });
81 }
82 }
83
84 try statement.lowerStatement(&lowerer, body);
85 if (!lowerer.terminated) {
86 const ret = FuncDialect.ReturnOp.create(ctx, loc, &.{}) catch return error.OutOfMemory;
87 try emit.append(&lowerer, ret.op);
88 }
89 lowerer.terminated = false;
90
91 module.getBodyBlock().addOperation(func.op) catch return error.OutOfMemory;
92 registered = true;
93 }
94
95 pub fn lowerUnit(
96 gpa: std.mem.Allocator,
97 arena: std.mem.Allocator,
98 ctx: *choir.Context,
99 translation: ast.TranslationUnit,
100 ) Error!Unit {
101 const loc = choir.ir.Location.getUnknown();
102 const module = choir.dialects.BuiltinDialect.ModuleOp.create(ctx, loc) catch return error.OutOfMemory;
103
104 var result = Unit{
105 .module = module,
106 .lowered = .empty,
107 .skipped = .empty,
108 };
109
110 var functions = std.StringHashMapUnmanaged(*const ast.Type).empty;
111 defer functions.deinit(gpa);
112 for (translation.declarations) |decl| {
113 switch (decl) {
114 .function => |decl_function| try functions.put(gpa, decl_function.name, decl_function.type),
115 .variable => {},
116 }
117 }
118
119 for (translation.declarations) |decl| {
120 switch (decl) {
121 .function => |decl_function| {
122 if (decl_function.body == null) continue;
123 if (lowerFunction(gpa, arena, ctx, module, functions, decl_function)) |_| {
124 try result.lowered.append(arena, decl_function.name);
125 } else |err| switch (err) {
126 error.OutOfMemory => return error.OutOfMemory,
127 else => try result.skipped.append(arena, .{ .name = decl_function.name, .reason = err }),
128 }
129 },
130 .variable => {},
131 }
132 }
133 return result;
134 }