Files
zig/src/link/C.zig
Andrew Kelley 7b8cede61f stage2: rework the C backend
* std.ArrayList gains `moveToUnmanaged` and dead code
   `ArrayListUnmanaged.appendWrite` is deleted.
 * emit_h state is attached to Module rather than Compilation.
 * remove the implementation of emit-h because it did not properly
   integrate with incremental compilation. I will re-implement it
   in a follow-up commit.
 * Compilation: use the .codegen_failure tag rather than
   .dependency_failure tag for when `bin_file.updateDecl` fails.

C backend:
 * Use a CValue tagged union instead of strings for C values.
 * Cleanly separate state into Object and DeclGen:
   - Object is present only when generating a .c file
   - DeclGen is present for both generating a .c and .h
 * Move some functions into their respective Object/DeclGen namespace.
 * Forward decls are managed by the incremental compilation frontend; C
   backend no longer renders function signatures based on callsites.
   For simplicity, all functions always get forward decls.
 * Constants are managed by the incremental compilation frontend. C
   backend no longer has a "constants" section.
 * Participate in incremental compilation. Each Decl gets an ArrayList
   for its generated C code and it is updated when the Decl is updated.
   During flush(), all these are joined together in the output file.
 * The new CValue tagged union is used to clean up using of assigning to
   locals without an additional pointer local.
 * Fix bug with bitcast of non-pointers making the memcpy destination
   immutable.
2021-01-05 17:41:14 -07:00

178 lines
5.0 KiB
Zig

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 C = @This();
pub const base_tag: link.File.Tag = .c;
pub const zig_h = @embedFile("C/zig.h");
base: link.File,
/// Per-declaration data. For functions this is the body, and
/// the forward declaration is stored in the FnBlock.
pub const DeclBlock = struct {
code: std.ArrayListUnmanaged(u8),
pub const empty: DeclBlock = .{
.code = .{},
};
};
/// Per-function data.
pub const FnBlock = struct {
fwd_decl: std.ArrayListUnmanaged(u8),
pub const empty: FnBlock = .{
.fwd_decl = .{},
};
};
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,
.mode = link.determineMode(options),
});
errdefer file.close();
try file.writeAll(zig_h);
var c_file = try allocator.create(C);
errdefer allocator.destroy(c_file);
c_file.* = C{
.base = .{
.tag = .c,
.options = options,
.file = file,
.allocator = allocator,
},
};
return c_file;
}
pub fn deinit(self: *C) void {
const module = self.base.options.module orelse return;
for (module.decl_table.items()) |entry| {
self.freeDecl(entry.value);
}
}
pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
pub fn freeDecl(self: *C, decl: *Module.Decl) void {
decl.link.c.code.deinit(self.base.allocator);
decl.fn_link.c.fwd_decl.deinit(self.base.allocator);
}
pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
const tracy = trace(@src());
defer tracy.end();
const fwd_decl = &decl.fn_link.c.fwd_decl;
const code = &decl.link.c.code;
fwd_decl.shrinkRetainingCapacity(0);
code.shrinkRetainingCapacity(0);
var object: codegen.Object = .{
.dg = .{
.module = module,
.error_msg = null,
.decl = decl,
.fwd_decl = fwd_decl.toManaged(module.gpa),
},
.gpa = module.gpa,
.code = code.toManaged(module.gpa),
.value_map = codegen.CValueMap.init(module.gpa),
};
defer object.value_map.deinit();
defer object.code.deinit();
defer object.dg.fwd_decl.deinit();
codegen.genDecl(&object) catch |err| switch (err) {
error.AnalysisFail => {},
else => |e| return e,
};
// The code may populate this error without returning error.AnalysisFail.
if (object.dg.error_msg) |msg| {
try module.failed_decls.put(module.gpa, decl, msg);
return;
}
fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
code.* = object.code.moveToUnmanaged();
// Free excess allocated memory for this Decl.
fwd_decl.shrink(module.gpa, fwd_decl.items.len);
code.shrink(module.gpa, code.items.len);
}
pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
// The C backend does not have the ability to fix line numbers without re-generating
// the entire Decl.
return self.updateDecl(module, decl);
}
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 file = self.base.file.?;
// The header is written upon opening; here we truncate and seek to after the header.
// TODO: use writev
try file.seekTo(zig_h.len);
try file.setEndPos(zig_h.len);
var buffered_writer = std.io.bufferedWriter(file.writer());
const writer = buffered_writer.writer();
const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
// Forward decls and non-functions first.
// TODO: use writev
for (module.decl_table.items()) |kv| {
const decl = kv.value;
const decl_tv = decl.typed_value.most_recent.typed_value;
if (decl_tv.val.castTag(.function)) |_| {
try writer.writeAll(decl.fn_link.c.fwd_decl.items);
} else {
try writer.writeAll(decl.link.c.code.items);
}
}
// Now the function bodies.
for (module.decl_table.items()) |kv| {
const decl = kv.value;
const decl_tv = decl.typed_value.most_recent.typed_value;
if (decl_tv.val.castTag(.function)) |_| {
try writer.writeAll(decl.link.c.code.items);
}
}
try buffered_writer.flush();
}
pub fn updateDeclExports(
self: *C,
module: *Module,
decl: *Module.Decl,
exports: []const *Module.Export,
) !void {}