1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const math = std.math;
5const assert = std.debug.assert;
6const Air = @import("Air.zig");
7const Zir = @import("Zir.zig");
8const Liveness = @import("Liveness.zig");
9const Type = @import("type.zig").Type;
10const Value = @import("value.zig").Value;
11const TypedValue = @import("TypedValue.zig");
12const link = @import("link.zig");
13const Module = @import("Module.zig");
14const Compilation = @import("Compilation.zig");
15const ErrorMsg = Module.ErrorMsg;
16const Target = std.Target;
17const Allocator = mem.Allocator;
18const trace = @import("tracy.zig").trace;
19const DW = std.dwarf;
20const leb128 = std.leb;
21const log = std.log.scoped(.codegen);
22const build_options = @import("build_options");
23const RegisterManager = @import("register_manager.zig").RegisterManager;
24
25pub const FnResult = union(enum) {
26    /// The `code` parameter passed to `generateSymbol` has the value appended.
27    appended: void,
28    fail: *ErrorMsg,
29};
30pub const Result = union(enum) {
31    /// The `code` parameter passed to `generateSymbol` has the value appended.
32    appended: void,
33    /// The value is available externally, `code` is unused.
34    externally_managed: []const u8,
35    fail: *ErrorMsg,
36};
37
38pub const GenerateSymbolError = error{
39    OutOfMemory,
40    Overflow,
41    /// A Decl that this symbol depends on had a semantic analysis failure.
42    AnalysisFail,
43};
44
45pub const DebugInfoOutput = union(enum) {
46    dwarf: struct {
47        dbg_line: *std.ArrayList(u8),
48        dbg_info: *std.ArrayList(u8),
49        dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
50    },
51    /// the plan9 debuginfo output is a bytecode with 4 opcodes
52    /// assume all numbers/variables are bytes
53    /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset
54    /// x when x < 65 -> add x to line offset
55    /// x when x < 129 -> subtract 64 from x and subtract it from the line offset
56    /// x -> subtract 129 from x, multiply it by the quanta of the instruction size
57    /// (1 on x86_64), and add it to the pc
58    /// after every opcode, add the quanta of the instruction size to the pc
59    plan9: struct {
60        /// the actual opcodes
61        dbg_line: *std.ArrayList(u8),
62        /// what line the debuginfo starts on
63        /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
64        start_line: *?u32,
65        /// what the line count ends on after codegen
66        /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
67        end_line: *u32,
68        /// the last pc change op
69        /// This is very useful for adding quanta
70        /// to it if its not actually the last one.
71        pcop_change_index: *?u32,
72    },
73    none,
74};
75
76pub fn generateFunction(
77    bin_file: *link.File,
78    src_loc: Module.SrcLoc,
79    func: *Module.Fn,
80    air: Air,
81    liveness: Liveness,
82    code: *std.ArrayList(u8),
83    debug_output: DebugInfoOutput,
84) GenerateSymbolError!FnResult {
85    switch (bin_file.options.target.cpu.arch) {
86        .wasm32 => unreachable, // has its own code path
87        .wasm64 => unreachable, // has its own code path
88        .arm,
89        .armeb,
90        => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
91        .aarch64,
92        .aarch64_be,
93        .aarch64_32,
94        => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
95        //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
96        //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
97        //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
98        //.bpfeb => return Function(.bpfeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
99        //.hexagon => return Function(.hexagon).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
100        //.mips => return Function(.mips).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
101        //.mipsel => return Function(.mipsel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
102        //.mips64 => return Function(.mips64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
103        //.mips64el => return Function(.mips64el).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
104        //.msp430 => return Function(.msp430).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
105        //.powerpc => return Function(.powerpc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
106        //.powerpc64 => return Function(.powerpc64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
107        //.powerpc64le => return Function(.powerpc64le).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
108        //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
109        //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
110        //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
111        .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
112        //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
113        //.sparcv9 => return Function(.sparcv9).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
114        //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
115        //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
116        //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
117        //.tcele => return Function(.tcele).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
118        //.thumb => return Function(.thumb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
119        //.thumbeb => return Function(.thumbeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
120        //.i386 => return Function(.i386).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
121        .x86_64 => return @import("arch/x86_64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
122        //.xcore => return Function(.xcore).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
123        //.nvptx => return Function(.nvptx).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
124        //.nvptx64 => return Function(.nvptx64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
125        //.le32 => return Function(.le32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
126        //.le64 => return Function(.le64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
127        //.amdil => return Function(.amdil).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
128        //.amdil64 => return Function(.amdil64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
129        //.hsail => return Function(.hsail).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
130        //.hsail64 => return Function(.hsail64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
131        //.spir => return Function(.spir).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
132        //.spir64 => return Function(.spir64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
133        //.kalimba => return Function(.kalimba).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
134        //.shave => return Function(.shave).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
135        //.lanai => return Function(.lanai).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
136        //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
137        //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
138        //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
139        else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
140    }
141}
142
143pub fn generateSymbol(
144    bin_file: *link.File,
145    src_loc: Module.SrcLoc,
146    typed_value: TypedValue,
147    code: *std.ArrayList(u8),
148    debug_output: DebugInfoOutput,
149) GenerateSymbolError!Result {
150    const tracy = trace(@src());
151    defer tracy.end();
152
153    switch (typed_value.ty.zigTypeTag()) {
154        .Fn => {
155            return Result{
156                .fail = try ErrorMsg.create(
157                    bin_file.allocator,
158                    src_loc,
159                    "TODO implement generateSymbol function pointers",
160                    .{},
161                ),
162            };
163        },
164        .Array => {
165            // TODO populate .debug_info for the array
166            if (typed_value.val.castTag(.bytes)) |payload| {
167                if (typed_value.ty.sentinel()) |sentinel| {
168                    try code.ensureUnusedCapacity(payload.data.len + 1);
169                    code.appendSliceAssumeCapacity(payload.data);
170                    switch (try generateSymbol(bin_file, src_loc, .{
171                        .ty = typed_value.ty.elemType(),
172                        .val = sentinel,
173                    }, code, debug_output)) {
174                        .appended => return Result{ .appended = {} },
175                        .externally_managed => |slice| {
176                            code.appendSliceAssumeCapacity(slice);
177                            return Result{ .appended = {} };
178                        },
179                        .fail => |em| return Result{ .fail = em },
180                    }
181                } else {
182                    return Result{ .externally_managed = payload.data };
183                }
184            }
185            return Result{
186                .fail = try ErrorMsg.create(
187                    bin_file.allocator,
188                    src_loc,
189                    "TODO implement generateSymbol for more kinds of arrays",
190                    .{},
191                ),
192            };
193        },
194        .Pointer => switch (typed_value.ty.ptrSize()) {
195            .Slice => {
196                return Result{
197                    .fail = try ErrorMsg.create(
198                        bin_file.allocator,
199                        src_loc,
200                        "TODO implement generateSymbol for slice {}",
201                        .{typed_value.val},
202                    ),
203                };
204            },
205            else => {
206                // TODO populate .debug_info for the pointer
207                if (typed_value.val.castTag(.decl_ref)) |payload| {
208                    const decl = payload.data;
209                    if (decl.analysis != .complete) return error.AnalysisFail;
210                    decl.alive = true;
211                    // TODO handle the dependency of this symbol on the decl's vaddr.
212                    // If the decl changes vaddr, then this symbol needs to get regenerated.
213                    const vaddr = bin_file.getDeclVAddr(decl);
214                    const endian = bin_file.options.target.cpu.arch.endian();
215                    switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
216                        16 => {
217                            try code.resize(2);
218                            mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
219                        },
220                        32 => {
221                            try code.resize(4);
222                            mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
223                        },
224                        64 => {
225                            try code.resize(8);
226                            mem.writeInt(u64, code.items[0..8], vaddr, endian);
227                        },
228                        else => unreachable,
229                    }
230                    return Result{ .appended = {} };
231                }
232                return Result{
233                    .fail = try ErrorMsg.create(
234                        bin_file.allocator,
235                        src_loc,
236                        "TODO implement generateSymbol for pointer {}",
237                        .{typed_value.val},
238                    ),
239                };
240            },
241        },
242        .Int => {
243            // TODO populate .debug_info for the integer
244            const endian = bin_file.options.target.cpu.arch.endian();
245            const info = typed_value.ty.intInfo(bin_file.options.target);
246            if (info.bits <= 8) {
247                const x = @intCast(u8, typed_value.val.toUnsignedInt());
248                try code.append(x);
249                return Result{ .appended = {} };
250            }
251            if (info.bits > 64) {
252                return Result{
253                    .fail = try ErrorMsg.create(
254                        bin_file.allocator,
255                        src_loc,
256                        "TODO implement generateSymbol for big ints ('{}')",
257                        .{typed_value.ty},
258                    ),
259                };
260            }
261            switch (info.signedness) {
262                .unsigned => {
263                    if (info.bits <= 16) {
264                        const x = @intCast(u16, typed_value.val.toUnsignedInt());
265                        mem.writeInt(u16, try code.addManyAsArray(2), x, endian);
266                    } else if (info.bits <= 32) {
267                        const x = @intCast(u32, typed_value.val.toUnsignedInt());
268                        mem.writeInt(u32, try code.addManyAsArray(4), x, endian);
269                    } else {
270                        const x = typed_value.val.toUnsignedInt();
271                        mem.writeInt(u64, try code.addManyAsArray(8), x, endian);
272                    }
273                },
274                .signed => {
275                    if (info.bits <= 16) {
276                        const x = @intCast(i16, typed_value.val.toSignedInt());
277                        mem.writeInt(i16, try code.addManyAsArray(2), x, endian);
278                    } else if (info.bits <= 32) {
279                        const x = @intCast(i32, typed_value.val.toSignedInt());
280                        mem.writeInt(i32, try code.addManyAsArray(4), x, endian);
281                    } else {
282                        const x = typed_value.val.toSignedInt();
283                        mem.writeInt(i64, try code.addManyAsArray(8), x, endian);
284                    }
285                },
286            }
287            return Result{ .appended = {} };
288        },
289        .Enum => {
290            // TODO populate .debug_info for the enum
291            var int_buffer: Value.Payload.U64 = undefined;
292            const int_val = typed_value.enumToInt(&int_buffer);
293
294            const target = bin_file.options.target;
295            const info = typed_value.ty.intInfo(target);
296            if (info.bits <= 8) {
297                const x = @intCast(u8, int_val.toUnsignedInt());
298                try code.append(x);
299                return Result{ .appended = {} };
300            }
301            if (info.bits > 64) {
302                return Result{
303                    .fail = try ErrorMsg.create(
304                        bin_file.allocator,
305                        src_loc,
306                        "TODO implement generateSymbol for big int enums ('{}')",
307                        .{typed_value.ty},
308                    ),
309                };
310            }
311            const endian = target.cpu.arch.endian();
312            switch (info.signedness) {
313                .unsigned => {
314                    if (info.bits <= 16) {
315                        const x = @intCast(u16, int_val.toUnsignedInt());
316                        mem.writeInt(u16, try code.addManyAsArray(2), x, endian);
317                    } else if (info.bits <= 32) {
318                        const x = @intCast(u32, int_val.toUnsignedInt());
319                        mem.writeInt(u32, try code.addManyAsArray(4), x, endian);
320                    } else {
321                        const x = int_val.toUnsignedInt();
322                        mem.writeInt(u64, try code.addManyAsArray(8), x, endian);
323                    }
324                },
325                .signed => {
326                    if (info.bits <= 16) {
327                        const x = @intCast(i16, int_val.toSignedInt());
328                        mem.writeInt(i16, try code.addManyAsArray(2), x, endian);
329                    } else if (info.bits <= 32) {
330                        const x = @intCast(i32, int_val.toSignedInt());
331                        mem.writeInt(i32, try code.addManyAsArray(4), x, endian);
332                    } else {
333                        const x = int_val.toSignedInt();
334                        mem.writeInt(i64, try code.addManyAsArray(8), x, endian);
335                    }
336                },
337            }
338            return Result{ .appended = {} };
339        },
340        .Bool => {
341            const x: u8 = @boolToInt(typed_value.val.toBool());
342            try code.append(x);
343            return Result{ .appended = {} };
344        },
345        .Struct => {
346            const field_vals = typed_value.val.castTag(.@"struct").?.data;
347            _ = field_vals; // TODO write the fields for real
348            const target = bin_file.options.target;
349            const abi_size = try math.cast(usize, typed_value.ty.abiSize(target));
350            try code.writer().writeByteNTimes(0xaa, abi_size);
351            return Result{ .appended = {} };
352        },
353        else => |t| {
354            return Result{
355                .fail = try ErrorMsg.create(
356                    bin_file.allocator,
357                    src_loc,
358                    "TODO implement generateSymbol for type '{s}'",
359                    .{@tagName(t)},
360                ),
361            };
362        },
363    }
364}
365