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 }