lib/chant/src/lower/local.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 expression = @import("expression/root.zig");
  8 const memory = @import("memory.zig");
  9 const scope = @import("scope.zig");
 10 const Error = @import("error.zig").Error;
 11 
 12 const ArithDialect = choir.dialects.ArithDialect;
 13 const Lowerer = context.Lowerer;
 14 
 15 pub fn declare(lowerer: *Lowerer, variable: ast.Variable) Error!void {
 16     if (variable.is_thread_local) return error.UnsupportedConstruct;
 17     if (variable.type.kind != .array and !ast.types.isArithmetic(variable.type) and variable.type.kind != .pointer) return error.UnsupportedType;
 18     if (variable.type.kind == .pointer) return error.UnsupportedConstruct;
 19 
 20     const slot = try memory.allocaObject(lowerer, variable.type, variable.alignment);
 21     try scope.bind(lowerer, variable.name, .{ .kind = .slot, .value = slot, .c_type = variable.type });
 22 
 23     if (variable.initializer) |initializer| {
 24         if (initializer.* == .initializer_list) {
 25             try initializeList(lowerer, slot, variable.type, initializer);
 26             return;
 27         }
 28         if (initializer.* == .string_literal and variable.type.kind == .array) {
 29             try initializeString(lowerer, slot, variable.type, initializer.string_literal.text);
 30             return;
 31         }
 32         const value = try expression.lowerExpression(lowerer, initializer);
 33         const converted = try convert.convert(lowerer, value.value, value.c_type, variable.type);
 34         const zero = try emit.indexConstant(lowerer, 0);
 35         try memory.storeElement(lowerer, .{ .memref = slot, .index = zero, .c_type = variable.type }, converted);
 36     }
 37 }
 38 
 39 fn initializeList(lowerer: *Lowerer, slot: *choir.Value, c_type: *const ast.Type, initializer: *ast.Expr) Error!void {
 40     if (initializer.* != .initializer_list) return error.UnsupportedConstruct;
 41     const list = initializer.initializer_list;
 42     if (c_type.kind != .array or list.type != c_type) return error.UnsupportedConstruct;
 43     const element_type = try memory.scalarElement(c_type);
 44     const count = try memory.staticElementCount(c_type);
 45     var index: usize = 0;
 46     while (index < count) : (index += 1) {
 47         try storeAt(lowerer, slot, element_type, index, try zeroValue(lowerer, element_type));
 48     }
 49     _ = try writeInitializer(lowerer, slot, c_type, initializer, 0);
 50 }
 51 
 52 fn initializeString(lowerer: *Lowerer, slot: *choir.Value, c_type: *const ast.Type, text: []const u8) Error!void {
 53     if (!isCharArray(c_type)) return error.UnsupportedConstruct;
 54     const count = try memory.staticElementCount(c_type);
 55     if (text.len > count) return error.UnsupportedConstruct;
 56     const element_type = try memory.scalarElement(c_type);
 57     var index: usize = 0;
 58     while (index < count) : (index += 1) {
 59         const byte: u8 = if (index < text.len) text[index] else 0;
 60         try storeAt(lowerer, slot, element_type, index, try intValue(lowerer, element_type, byte));
 61     }
 62 }
 63 
 64 fn writeInitializer(lowerer: *Lowerer, slot: *choir.Value, c_type: *const ast.Type, value_expr: *ast.Expr, offset: usize) Error!usize {
 65     if (value_expr.* == .initializer_list) {
 66         const list = value_expr.initializer_list;
 67         const capacity = try memory.staticElementCount(list.type);
 68         var cursor: usize = 0;
 69         for (list.items) |item| {
 70             const relative_offset = if (item.designator.len == 0) cursor else try designatorOffset(list.type, item.designator);
 71             const target_type = try itemTargetType(list.type, item);
 72             const consumed = try writeInitializer(lowerer, slot, target_type, item.value, offset + relative_offset);
 73             if (relative_offset + consumed > capacity) return error.UnsupportedConstruct;
 74             cursor = relative_offset + consumed;
 75         }
 76         return capacity;
 77     }
 78     if (value_expr.* == .string_literal) {
 79         if (!isCharArray(c_type)) return error.UnsupportedConstruct;
 80         try initializeStringAt(lowerer, slot, c_type, value_expr.string_literal.text, offset);
 81         return try memory.staticElementCount(c_type);
 82     }
 83     const element_type = try memory.scalarElement(c_type);
 84     const value = try expression.lowerExpression(lowerer, value_expr);
 85     const converted = try convert.convert(lowerer, value.value, value.c_type, element_type);
 86     try storeAt(lowerer, slot, element_type, offset, converted);
 87     return 1;
 88 }
 89 
 90 fn initializeStringAt(lowerer: *Lowerer, slot: *choir.Value, c_type: *const ast.Type, text: []const u8, base: usize) Error!void {
 91     const count = try memory.staticElementCount(c_type);
 92     if (text.len > count) return error.UnsupportedConstruct;
 93     const element_type = try memory.scalarElement(c_type);
 94     var index: usize = 0;
 95     while (index < count) : (index += 1) {
 96         const byte: u8 = if (index < text.len) text[index] else 0;
 97         try storeAt(lowerer, slot, element_type, base + index, try intValue(lowerer, element_type, byte));
 98     }
 99 }
100 
101 fn designatorOffset(c_type: *const ast.Type, designator: []const u64) Error!usize {
102     var current = c_type;
103     var offset: usize = 0;
104     for (designator) |index| {
105         if (current.kind != .array) return error.UnsupportedConstruct;
106         if (current.array_len) |len| {
107             if (index >= len) return error.UnsupportedConstruct;
108         }
109         const child = current.child orelse return error.UnsupportedType;
110         const stride = try memory.staticElementCount(child);
111         const scaled = std.math.mul(u64, index, stride) catch return error.UnsupportedConstruct;
112         const scaled_usize: usize = std.math.cast(usize, scaled) orelse return error.UnsupportedConstruct;
113         offset = std.math.add(usize, offset, scaled_usize) catch return error.UnsupportedConstruct;
114         current = child;
115     }
116     return offset;
117 }
118 
119 fn itemTargetType(c_type: *const ast.Type, item: ast.expr.InitializerItem) Error!*const ast.Type {
120     if (item.designator.len != 0) return designatorType(c_type, item.designator);
121     if (item.value.* == .initializer_list) return item.value.initializer_list.type;
122     if (item.value.* == .string_literal and c_type.kind == .array) {
123         if (c_type.child) |child| {
124             if (child.kind == .array and isCharArray(child)) return child;
125         }
126     }
127     return c_type;
128 }
129 
130 fn designatorType(c_type: *const ast.Type, designator: []const u64) Error!*const ast.Type {
131     var current = c_type;
132     for (designator) |index| {
133         if (current.kind != .array) return error.UnsupportedConstruct;
134         if (current.array_len) |len| {
135             if (index >= len) return error.UnsupportedConstruct;
136         }
137         current = current.child orelse return error.UnsupportedType;
138     }
139     return current;
140 }
141 
142 fn storeAt(lowerer: *Lowerer, slot: *choir.Value, element_type: *const ast.Type, index: usize, value: *choir.Value) Error!void {
143     const offset = try emit.indexConstant(lowerer, @intCast(index));
144     try memory.storeElement(lowerer, .{ .memref = slot, .index = offset, .c_type = element_type }, value);
145 }
146 
147 fn zeroValue(lowerer: *Lowerer, c_type: *const ast.Type) Error!*choir.Value {
148     const result_type = try convert.scalarType(lowerer, c_type);
149     const op = switch (c_type.kind) {
150         .float_type, .double_type, .decimal32_type, .decimal64_type => (ArithDialect.ConstantOp.createFloat(lowerer.ctx, lowerer.loc, result_type, 0.0) catch return error.OutOfMemory).op,
151         else => (ArithDialect.ConstantOp.createInt(lowerer.ctx, lowerer.loc, result_type, 0) catch return error.OutOfMemory).op,
152     };
153     try emit.append(lowerer, op);
154     return op.getResult(0) orelse return error.UnsupportedConstruct;
155 }
156 
157 fn intValue(lowerer: *Lowerer, c_type: *const ast.Type, value: u8) Error!*choir.Value {
158     const result_type = try convert.scalarType(lowerer, c_type);
159     const op = ArithDialect.ConstantOp.createInt(lowerer.ctx, lowerer.loc, result_type, value) catch return error.OutOfMemory;
160     try emit.append(lowerer, op.op);
161     var mutable = op;
162     return mutable.getResult();
163 }
164 
165 fn isCharArray(c_type: *const ast.Type) bool {
166     if (c_type.kind != .array) return false;
167     const element_type = memory.scalarElement(c_type) catch return false;
168     return element_type.kind == .char_type;
169 }