aboutsummaryrefslogtreecommitdiff
path: root/src/link/C.zig
blob: 1059e52115a7c8485b37cd44070a711888660124 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
const std = @import("std");
const mem = std.mem;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Module = @import("../Module.zig");
const Compilation = @import("../Compilation.zig");
const fs = std.fs;
const codegen = @import("../codegen/c.zig");
const link = @import("../link.zig");
const trace = @import("../tracy.zig").trace;
const File = link.File;
const C = @This();

pub const base_tag: File.Tag = .c;

pub const Header = struct {
    buf: std.ArrayList(u8),
    emit_loc: ?Compilation.EmitLoc,

    pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
        return .{
            .buf = std.ArrayList(u8).init(allocator),
            .emit_loc = emit_loc,
        };
    }

    pub fn flush(self: *const Header, writer: anytype) !void {
        const tracy = trace(@src());
        defer tracy.end();

        try writer.writeAll(@embedFile("cbe.h"));
        if (self.buf.items.len > 0) {
            try writer.print("{s}", .{self.buf.items});
        }
    }

    pub fn deinit(self: *Header) void {
        self.buf.deinit();
        self.* = undefined;
    }
};

base: File,

header: Header,
constants: std.ArrayList(u8),
main: std.ArrayList(u8),

called: std.StringHashMap(void),
error_msg: *Compilation.ErrorMsg = undefined,

pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
    assert(options.object_format == .c);

    if (options.use_llvm) return error.LLVMHasNoCBackend;
    if (options.use_lld) return error.LLDHasNoCBackend;

    const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
    errdefer file.close();

    var c_file = try allocator.create(C);
    errdefer allocator.destroy(c_file);

    c_file.* = C{
        .base = .{
            .tag = .c,
            .options = options,
            .file = file,
            .allocator = allocator,
        },
        .main = std.ArrayList(u8).init(allocator),
        .header = Header.init(allocator, null),
        .constants = std.ArrayList(u8).init(allocator),
        .called = std.StringHashMap(void).init(allocator),
    };

    return c_file;
}

pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
    self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);
    return error.AnalysisFail;
}

pub fn deinit(self: *C) void {
    self.main.deinit();
    self.header.deinit();
    self.constants.deinit();
    self.called.deinit();
}

pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
    codegen.generate(self, module, decl) catch |err| {
        if (err == error.AnalysisFail) {
            try module.failed_decls.put(module.gpa, decl, self.error_msg);
        }
        return err;
    };
}

pub fn flush(self: *C, comp: *Compilation) !void {
    return self.flushModule(comp);
}

pub fn flushModule(self: *C, comp: *Compilation) !void {
    const tracy = trace(@src());
    defer tracy.end();

    const writer = self.base.file.?.writer();
    try self.header.flush(writer);
    if (self.header.buf.items.len > 0) {
        try writer.writeByte('\n');
    }
    if (self.constants.items.len > 0) {
        try writer.print("{}\n", .{self.constants.items});
    }
    if (self.main.items.len > 1) {
        const last_two = self.main.items[self.main.items.len - 2 ..];
        if (std.mem.eql(u8, last_two, "\n\n")) {
            self.main.items.len -= 1;
        }
    }
    try writer.writeAll(self.main.items);
    self.base.file.?.close();
    self.base.file = null;
}