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 }