master
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const assert = std.debug.assert;
4
5const aro = @import("aro");
6const Assembly = aro.Assembly;
7const Compilation = aro.Compilation;
8const Node = Tree.Node;
9const Source = aro.Source;
10const Tree = aro.Tree;
11const QualType = aro.QualType;
12const Value = aro.Value;
13
14const AsmCodeGen = @This();
15const Error = aro.Compilation.Error;
16
17tree: *const Tree,
18comp: *Compilation,
19text: *std.Io.Writer,
20data: *std.Io.Writer,
21
22const StorageUnit = enum(u8) {
23 byte = 8,
24 short = 16,
25 long = 32,
26 quad = 64,
27
28 fn trunc(self: StorageUnit, val: u64) u64 {
29 return switch (self) {
30 .byte => @as(u8, @truncate(val)),
31 .short => @as(u16, @truncate(val)),
32 .long => @as(u32, @truncate(val)),
33 .quad => val,
34 };
35 }
36};
37
38fn serializeInt(value: u64, storage_unit: StorageUnit, w: *std.Io.Writer) !void {
39 try w.print(" .{s} 0x{x}\n", .{ @tagName(storage_unit), storage_unit.trunc(value) });
40}
41
42fn serializeFloat(comptime T: type, value: T, w: *std.Io.Writer) !void {
43 switch (T) {
44 f128 => {
45 const bytes = std.mem.asBytes(&value);
46 const first = std.mem.bytesToValue(u64, bytes[0..8]);
47 try serializeInt(first, .quad, w);
48 const second = std.mem.bytesToValue(u64, bytes[8..16]);
49 return serializeInt(second, .quad, w);
50 },
51 f80 => {
52 const bytes = std.mem.asBytes(&value);
53 const first = std.mem.bytesToValue(u64, bytes[0..8]);
54 try serializeInt(first, .quad, w);
55 const second = std.mem.bytesToValue(u16, bytes[8..10]);
56 try serializeInt(second, .short, w);
57 return w.writeAll(" .zero 6\n");
58 },
59 else => {
60 const size = @bitSizeOf(T);
61 const storage_unit = std.meta.intToEnum(StorageUnit, size) catch unreachable;
62 const IntTy = @Int(.unsigned, size);
63 const int_val: IntTy = @bitCast(value);
64 return serializeInt(int_val, storage_unit, w);
65 },
66 }
67}
68
69pub fn todo(c: *AsmCodeGen, msg: []const u8, tok: Tree.TokenIndex) Error {
70 const loc: Source.Location = c.tree.tokens.items(.loc)[tok];
71
72 var sf = std.heap.stackFallback(1024, c.comp.gpa);
73 const allocator = sf.get();
74 var buf: std.ArrayList(u8) = .empty;
75 defer buf.deinit(allocator);
76
77 try buf.print(allocator, "TODO: {s}", .{msg});
78 try c.comp.diagnostics.add(.{
79 .text = buf.items,
80 .kind = .@"error",
81 .location = loc.expand(c.comp),
82 });
83 return error.FatalError;
84}
85
86fn emitAggregate(c: *AsmCodeGen, qt: QualType, node: Node.Index) !void {
87 _ = qt;
88 return c.todo("Codegen aggregates", node.tok(c.tree));
89}
90
91fn emitSingleValue(c: *AsmCodeGen, qt: QualType, node: Node.Index) !void {
92 const value = c.tree.value_map.get(node) orelse return;
93 const bit_size = qt.bitSizeof(c.comp);
94 const scalar_kind = qt.scalarKind(c.comp);
95 if (!scalar_kind.isReal()) {
96 return c.todo("Codegen _Complex values", node.tok(c.tree));
97 } else if (scalar_kind.isInt()) {
98 const storage_unit = std.meta.intToEnum(StorageUnit, bit_size) catch return c.todo("Codegen _BitInt values", node.tok(c.tree));
99 try c.data.print(" .{s} ", .{@tagName(storage_unit)});
100 _ = try value.print(qt, c.comp, c.data);
101 try c.data.writeByte('\n');
102 } else if (scalar_kind.isFloat()) {
103 switch (bit_size) {
104 16 => return serializeFloat(f16, value.toFloat(f16, c.comp), c.data),
105 32 => return serializeFloat(f32, value.toFloat(f32, c.comp), c.data),
106 64 => return serializeFloat(f64, value.toFloat(f64, c.comp), c.data),
107 80 => return serializeFloat(f80, value.toFloat(f80, c.comp), c.data),
108 128 => return serializeFloat(f128, value.toFloat(f128, c.comp), c.data),
109 else => unreachable,
110 }
111 } else if (scalar_kind.isPointer()) {
112 return c.todo("Codegen pointer", node.tok(c.tree));
113 } else if (qt.is(c.comp, .array)) {
114 // Todo:
115 // Handle truncated initializers e.g. char x[3] = "hello";
116 // Zero out remaining bytes if initializer is shorter than storage capacity
117 // Handle non-char strings
118 const bytes = value.toBytes(c.comp);
119 const directive = if (bytes.len > bit_size / 8) "ascii" else "string";
120 try c.data.print(" .{s} ", .{directive});
121 try Value.printString(bytes, qt, c.comp, c.data);
122
123 try c.data.writeByte('\n');
124 } else unreachable;
125}
126
127fn emitValue(c: *AsmCodeGen, qt: QualType, node: Node.Index) !void {
128 switch (node.get(c.tree)) {
129 .array_init_expr,
130 .struct_init_expr,
131 .union_init_expr,
132 => return c.todo("Codegen multiple inits", node.tok(c.tree)),
133 else => return c.emitSingleValue(qt, node),
134 }
135}
136
137pub fn genAsm(tree: *const Tree) Error!Assembly {
138 var data: std.Io.Writer.Allocating = .init(tree.comp.gpa);
139 defer data.deinit();
140
141 var text: std.Io.Writer.Allocating = .init(tree.comp.gpa);
142 defer text.deinit();
143
144 var codegen: AsmCodeGen = .{
145 .tree = tree,
146 .comp = tree.comp,
147 .text = &text.writer,
148 .data = &data.writer,
149 };
150
151 codegen.genDecls() catch |err| switch (err) {
152 error.WriteFailed => return error.OutOfMemory,
153 error.OutOfMemory => return error.OutOfMemory,
154 error.FatalError => return error.FatalError,
155 };
156
157 const text_slice = try text.toOwnedSlice();
158 errdefer tree.comp.gpa.free(text_slice);
159 const data_slice = try data.toOwnedSlice();
160 return .{
161 .text = text_slice,
162 .data = data_slice,
163 };
164}
165
166fn genDecls(c: *AsmCodeGen) !void {
167 if (c.tree.comp.code_gen_options.debug != .strip) {
168 const sources = c.tree.comp.sources.values();
169 for (sources) |source| {
170 try c.data.print(" .file {d} \"{s}\"\n", .{ @intFromEnum(source.id.index) + 1, source.path });
171 }
172 }
173
174 for (c.tree.root_decls.items) |decl| {
175 switch (decl.get(c.tree)) {
176 .static_assert,
177 .typedef,
178 .struct_decl,
179 .union_decl,
180 .enum_decl,
181 => {},
182
183 .function => |function| {
184 if (function.body == null) continue;
185 try c.genFn(function);
186 },
187
188 .variable => |variable| try c.genVar(variable),
189
190 else => unreachable,
191 }
192 }
193 try c.text.writeAll(" .section .note.GNU-stack,\"\",@progbits\n");
194}
195
196fn genFn(c: *AsmCodeGen, function: Node.Function) !void {
197 return c.todo("Codegen functions", function.name_tok);
198}
199
200fn genVar(c: *AsmCodeGen, variable: Node.Variable) !void {
201 const comp = c.comp;
202 const qt = variable.qt;
203
204 const is_tentative = variable.initializer == null;
205 const size = qt.sizeofOrNull(comp) orelse blk: {
206 // tentative array definition assumed to have one element
207 std.debug.assert(is_tentative and qt.is(c.comp, .array));
208 break :blk qt.childType(c.comp).sizeof(comp);
209 };
210
211 const name = c.tree.tokSlice(variable.name_tok);
212 const nat_align = qt.alignof(comp);
213 const alignment = if (qt.is(c.comp, .array) and size >= 16) @max(16, nat_align) else nat_align;
214
215 if (variable.storage_class == .static) {
216 try c.data.print(" .local \"{s}\"\n", .{name});
217 } else {
218 try c.data.print(" .globl \"{s}\"\n", .{name});
219 }
220
221 if (is_tentative and comp.code_gen_options.common) {
222 try c.data.print(" .comm \"{s}\", {d}, {d}\n", .{ name, size, alignment });
223 return;
224 }
225 if (variable.initializer) |init| {
226 if (variable.thread_local and comp.code_gen_options.data_sections) {
227 try c.data.print(" .section .tdata.\"{s}\",\"awT\",@progbits\n", .{name});
228 } else if (variable.thread_local) {
229 try c.data.writeAll(" .section .tdata,\"awT\",@progbits\n");
230 } else if (comp.code_gen_options.data_sections) {
231 try c.data.print(" .section .data.\"{s}\",\"aw\",@progbits\n", .{name});
232 } else {
233 try c.data.writeAll(" .data\n");
234 }
235
236 try c.data.print(" .type \"{s}\", @object\n", .{name});
237 try c.data.print(" .size \"{s}\", {d}\n", .{ name, size });
238 try c.data.print(" .align {d}\n", .{alignment});
239 try c.data.print("\"{s}\":\n", .{name});
240 try c.emitValue(qt, init);
241 return;
242 }
243 if (variable.thread_local and comp.code_gen_options.data_sections) {
244 try c.data.print(" .section .tbss.\"{s}\",\"awT\",@nobits\n", .{name});
245 } else if (variable.thread_local) {
246 try c.data.writeAll(" .section .tbss,\"awT\",@nobits\n");
247 } else if (comp.code_gen_options.data_sections) {
248 try c.data.print(" .section .bss.\"{s}\",\"aw\",@nobits\n", .{name});
249 } else {
250 try c.data.writeAll(" .bss\n");
251 }
252 try c.data.print(" .align {d}\n", .{alignment});
253 try c.data.print("\"{s}\":\n", .{name});
254 try c.data.print(" .zero {d}\n", .{size});
255}