mirror of
https://github.com/ziglang/zig.git
synced 2026-02-07 15:07:12 +00:00
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.
This commit is contained in:
parent
9360e5887c
commit
7b8cede61f
@ -100,10 +100,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
|
||||
/// of this ArrayList. This ArrayList retains ownership of underlying memory.
|
||||
/// Deprecated: use `moveToUnmanaged` which has different semantics.
|
||||
pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) {
|
||||
return .{ .items = self.items, .capacity = self.capacity };
|
||||
}
|
||||
|
||||
/// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
|
||||
/// of this ArrayList. Empties this ArrayList.
|
||||
pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {
|
||||
const allocator = self.allocator;
|
||||
const result = .{ .items = self.items, .capacity = self.capacity };
|
||||
self.* = init(allocator);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// The caller owns the returned memory. Empties this ArrayList.
|
||||
pub fn toOwnedSlice(self: *Self) Slice {
|
||||
const allocator = self.allocator;
|
||||
@ -551,14 +561,6 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
mem.copy(T, self.items[oldlen..], items);
|
||||
}
|
||||
|
||||
/// Same as `append` except it returns the number of bytes written, which is always the same
|
||||
/// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
|
||||
/// This function may be called only when `T` is `u8`.
|
||||
fn appendWrite(self: *Self, allocator: *Allocator, m: []const u8) !usize {
|
||||
try self.appendSlice(allocator, m);
|
||||
return m.len;
|
||||
}
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// Allocates more memory as necessary.
|
||||
pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void {
|
||||
@ -1129,13 +1131,13 @@ test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" {
|
||||
}
|
||||
}
|
||||
|
||||
test "std.ArrayList(u8) implements outStream" {
|
||||
test "std.ArrayList(u8) implements writer" {
|
||||
var buffer = ArrayList(u8).init(std.testing.allocator);
|
||||
defer buffer.deinit();
|
||||
|
||||
const x: i32 = 42;
|
||||
const y: i32 = 1234;
|
||||
try buffer.outStream().print("x: {}\ny: {}\n", .{ x, y });
|
||||
try buffer.writer().print("x: {}\ny: {}\n", .{ x, y });
|
||||
|
||||
testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items);
|
||||
}
|
||||
|
||||
@ -138,8 +138,6 @@ emit_llvm_ir: ?EmitLoc,
|
||||
emit_analysis: ?EmitLoc,
|
||||
emit_docs: ?EmitLoc,
|
||||
|
||||
c_header: ?c_link.Header,
|
||||
|
||||
work_queue_wait_group: WaitGroup,
|
||||
|
||||
pub const InnerError = Module.InnerError;
|
||||
@ -866,9 +864,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.root_pkg = root_pkg,
|
||||
.root_scope = root_scope,
|
||||
.zig_cache_artifact_directory = zig_cache_artifact_directory,
|
||||
.emit_h = options.emit_h,
|
||||
};
|
||||
break :blk module;
|
||||
} else null;
|
||||
} else blk: {
|
||||
if (options.emit_h != null) return error.NoZigModuleForCHeader;
|
||||
break :blk null;
|
||||
};
|
||||
errdefer if (module) |zm| zm.deinit();
|
||||
|
||||
const error_return_tracing = !strip and switch (options.optimize_mode) {
|
||||
@ -996,7 +998,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.local_cache_directory = options.local_cache_directory,
|
||||
.global_cache_directory = options.global_cache_directory,
|
||||
.bin_file = bin_file,
|
||||
.c_header = if (!use_llvm and options.emit_h != null) c_link.Header.init(gpa, options.emit_h) else null,
|
||||
.emit_asm = options.emit_asm,
|
||||
.emit_llvm_ir = options.emit_llvm_ir,
|
||||
.emit_analysis = options.emit_analysis,
|
||||
@ -1218,10 +1219,6 @@ pub fn destroy(self: *Compilation) void {
|
||||
}
|
||||
self.failed_c_objects.deinit(gpa);
|
||||
|
||||
if (self.c_header) |*header| {
|
||||
header.deinit();
|
||||
}
|
||||
|
||||
self.cache_parent.manifest_dir.close();
|
||||
if (self.owned_link_dir) |*dir| dir.close();
|
||||
|
||||
@ -1325,20 +1322,6 @@ pub fn update(self: *Compilation) !void {
|
||||
module.root_scope.unload(self.gpa);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've chosen to emit a C header, flush the header to the disk.
|
||||
if (self.c_header) |header| {
|
||||
const header_path = header.emit_loc.?;
|
||||
// If a directory has been provided, write the header there. Otherwise, just write it to the
|
||||
// cache directory.
|
||||
const header_dir = if (header_path.directory) |dir|
|
||||
dir.handle
|
||||
else
|
||||
self.local_cache_directory.handle;
|
||||
const header_file = try header_dir.createFile(header_path.basename, .{});
|
||||
defer header_file.close();
|
||||
try header.flush(header_file.writer());
|
||||
}
|
||||
}
|
||||
|
||||
/// Having the file open for writing is problematic as far as executing the
|
||||
@ -1497,7 +1480,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
|
||||
switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.AnalysisFail => {
|
||||
decl.analysis = .dependency_failure;
|
||||
decl.analysis = .codegen_failure;
|
||||
},
|
||||
else => {
|
||||
try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
|
||||
@ -1512,25 +1495,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
if (self.c_header) |*header| {
|
||||
c_codegen.generateHeader(self, module, header, decl) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.AnalysisFail => {
|
||||
decl.analysis = .dependency_failure;
|
||||
},
|
||||
else => {
|
||||
try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
|
||||
module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
|
||||
module.gpa,
|
||||
decl.src(),
|
||||
"unable to generate C header: {s}",
|
||||
.{@errorName(err)},
|
||||
));
|
||||
decl.analysis = .codegen_failure_retryable;
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
.analyze_decl => |decl| {
|
||||
@ -2998,9 +2962,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
man.hash.add(comp.bin_file.options.function_sections);
|
||||
man.hash.add(comp.bin_file.options.is_test);
|
||||
man.hash.add(comp.bin_file.options.emit != null);
|
||||
man.hash.add(comp.c_header != null);
|
||||
if (comp.c_header) |header| {
|
||||
man.hash.addEmitLoc(header.emit_loc.?);
|
||||
man.hash.add(mod.emit_h != null);
|
||||
if (mod.emit_h) |emit_h| {
|
||||
man.hash.addEmitLoc(emit_h);
|
||||
}
|
||||
man.hash.addOptionalEmitLoc(comp.emit_asm);
|
||||
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
|
||||
@ -3105,10 +3069,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
});
|
||||
break :blk try directory.join(arena, &[_][]const u8{bin_basename});
|
||||
} else "";
|
||||
if (comp.c_header != null) {
|
||||
if (comp.emit_h != null) {
|
||||
log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
|
||||
}
|
||||
const emit_h_path = try stage1LocPath(arena, if (comp.c_header) |header| header.emit_loc else null, directory);
|
||||
const emit_h_path = try stage1LocPath(arena, mod.emit_h, directory);
|
||||
const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
|
||||
const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
|
||||
const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
|
||||
|
||||
@ -94,6 +94,8 @@ stage1_flags: packed struct {
|
||||
reserved: u2 = 0,
|
||||
} = .{},
|
||||
|
||||
emit_h: ?Compilation.EmitLoc,
|
||||
|
||||
pub const Export = struct {
|
||||
options: std.builtin.ExportOptions,
|
||||
/// Byte offset into the file that contains the export directive.
|
||||
@ -1943,14 +1945,14 @@ fn allocateNewDecl(
|
||||
.coff => .{ .coff = link.File.Coff.TextBlock.empty },
|
||||
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
|
||||
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
|
||||
.c => .{ .c = {} },
|
||||
.c => .{ .c = link.File.C.DeclBlock.empty },
|
||||
.wasm => .{ .wasm = {} },
|
||||
},
|
||||
.fn_link = switch (self.comp.bin_file.tag) {
|
||||
.coff => .{ .coff = {} },
|
||||
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
|
||||
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
|
||||
.c => .{ .c = {} },
|
||||
.c => .{ .c = link.File.C.FnBlock.empty },
|
||||
.wasm => .{ .wasm = null },
|
||||
},
|
||||
.generation = 0,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
19
src/link.zig
19
src/link.zig
@ -130,7 +130,7 @@ pub const File = struct {
|
||||
elf: Elf.TextBlock,
|
||||
coff: Coff.TextBlock,
|
||||
macho: MachO.TextBlock,
|
||||
c: void,
|
||||
c: C.DeclBlock,
|
||||
wasm: void,
|
||||
};
|
||||
|
||||
@ -138,7 +138,7 @@ pub const File = struct {
|
||||
elf: Elf.SrcFn,
|
||||
coff: Coff.SrcFn,
|
||||
macho: MachO.SrcFn,
|
||||
c: void,
|
||||
c: C.FnBlock,
|
||||
wasm: ?Wasm.FnData,
|
||||
};
|
||||
|
||||
@ -291,7 +291,7 @@ pub const File = struct {
|
||||
.coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
|
||||
.macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
|
||||
.c => {},
|
||||
.c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
|
||||
.wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
|
||||
}
|
||||
}
|
||||
@ -301,7 +301,8 @@ pub const File = struct {
|
||||
.coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
|
||||
.macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
|
||||
.c, .wasm => {},
|
||||
.c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
|
||||
.wasm => {},
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,7 +313,8 @@ pub const File = struct {
|
||||
.coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
|
||||
.macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
|
||||
.c, .wasm => {},
|
||||
.c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
|
||||
.wasm => {},
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,12 +409,13 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when a Decl is deleted from the Module.
|
||||
pub fn freeDecl(base: *File, decl: *Module.Decl) void {
|
||||
switch (base.tag) {
|
||||
.coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
|
||||
.elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
|
||||
.macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
|
||||
.c => {},
|
||||
.c => @fieldParentPtr(C, "base", base).freeDecl(decl),
|
||||
.wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
|
||||
}
|
||||
}
|
||||
@ -432,14 +435,14 @@ pub const File = struct {
|
||||
pub fn updateDeclExports(
|
||||
base: *File,
|
||||
module: *Module,
|
||||
decl: *const Module.Decl,
|
||||
decl: *Module.Decl,
|
||||
exports: []const *Module.Export,
|
||||
) !void {
|
||||
switch (base.tag) {
|
||||
.coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
|
||||
.macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
|
||||
.c => return {},
|
||||
.c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
|
||||
.wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
|
||||
}
|
||||
}
|
||||
|
||||
197
src/link/C.zig
197
src/link/C.zig
@ -11,45 +11,28 @@ const trace = @import("../tracy.zig").trace;
|
||||
const C = @This();
|
||||
|
||||
pub const base_tag: link.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;
|
||||
}
|
||||
};
|
||||
pub const zig_h = @embedFile("C/zig.h");
|
||||
|
||||
base: link.File,
|
||||
|
||||
path: []const u8,
|
||||
/// 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),
|
||||
|
||||
// These are only valid during a flush()!
|
||||
header: Header,
|
||||
constants: std.ArrayList(u8),
|
||||
main: std.ArrayList(u8),
|
||||
called: std.StringHashMap(void),
|
||||
pub const empty: DeclBlock = .{
|
||||
.code = .{},
|
||||
};
|
||||
};
|
||||
|
||||
error_msg: *Compilation.ErrorMsg = undefined,
|
||||
/// 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);
|
||||
@ -57,6 +40,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
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);
|
||||
|
||||
@ -64,25 +55,75 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
.base = .{
|
||||
.tag = .c,
|
||||
.options = options,
|
||||
.file = null,
|
||||
.file = file,
|
||||
.allocator = allocator,
|
||||
},
|
||||
.main = undefined,
|
||||
.header = undefined,
|
||||
.constants = undefined,
|
||||
.called = undefined,
|
||||
.path = sub_path,
|
||||
};
|
||||
|
||||
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 {
|
||||
const module = self.base.options.module orelse return;
|
||||
for (module.decl_table.items()) |entry| {
|
||||
self.freeDecl(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *C) void {}
|
||||
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);
|
||||
@ -92,41 +133,45 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
self.main = std.ArrayList(u8).init(self.base.allocator);
|
||||
self.header = Header.init(self.base.allocator, null);
|
||||
self.constants = std.ArrayList(u8).init(self.base.allocator);
|
||||
self.called = std.StringHashMap(void).init(self.base.allocator);
|
||||
defer self.main.deinit();
|
||||
defer self.header.deinit();
|
||||
defer self.constants.deinit();
|
||||
defer self.called.deinit();
|
||||
const file = self.base.file.?;
|
||||
|
||||
const module = self.base.options.module.?;
|
||||
for (self.base.options.module.?.decl_table.entries.items) |kv| {
|
||||
codegen.generate(self, module, kv.value) catch |err| {
|
||||
if (err == error.AnalysisFail) {
|
||||
try module.failed_decls.put(module.gpa, kv.value, self.error_msg);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
}
|
||||
// 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);
|
||||
|
||||
const file = try self.base.options.emit.?.directory.handle.createFile(self.path, .{ .truncate = true, .read = true, .mode = link.determineMode(self.base.options) });
|
||||
defer file.close();
|
||||
var buffered_writer = std.io.bufferedWriter(file.writer());
|
||||
const writer = buffered_writer.writer();
|
||||
|
||||
const writer = 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("{s}\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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
try writer.writeAll(self.main.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 {}
|
||||
|
||||
@ -42,3 +42,4 @@
|
||||
#define int128_t __int128
|
||||
#define uint128_t unsigned __int128
|
||||
#include <string.h>
|
||||
|
||||
17
src/test.zig
17
src/test.zig
@ -13,7 +13,7 @@ const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_d
|
||||
const ThreadPool = @import("ThreadPool.zig");
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
|
||||
const c_header = @embedFile("link/cbe.h");
|
||||
const zig_h = link.File.C.zig_h;
|
||||
|
||||
test "self-hosted" {
|
||||
var ctx = TestContext.init();
|
||||
@ -324,11 +324,11 @@ pub const TestContext = struct {
|
||||
}
|
||||
|
||||
pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
|
||||
ctx.addC(name, target, .Zig).addCompareObjectFile(src, c_header ++ out);
|
||||
ctx.addC(name, target, .Zig).addCompareObjectFile(src, zig_h ++ out);
|
||||
}
|
||||
|
||||
pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
|
||||
ctx.addC(name, target, .Zig).addHeader(src, c_header ++ out);
|
||||
ctx.addC(name, target, .Zig).addHeader(src, zig_h ++ out);
|
||||
}
|
||||
|
||||
pub fn addCompareOutput(
|
||||
@ -700,11 +700,12 @@ pub const TestContext = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
if (comp.bin_file.cast(link.File.C)) |c_file| {
|
||||
std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{
|
||||
c_file.main.items,
|
||||
});
|
||||
}
|
||||
// TODO print generated C code
|
||||
//if (comp.bin_file.cast(link.File.C)) |c_file| {
|
||||
// std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{
|
||||
// c_file.main.items,
|
||||
// });
|
||||
//}
|
||||
std.debug.print("Test failed.\n", .{});
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
@ -22,8 +22,6 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
, "hello world!" ++ std.cstr.line_sep);
|
||||
|
||||
// Now change the message only
|
||||
// TODO fix C backend not supporting updates
|
||||
// https://github.com/ziglang/zig/issues/7589
|
||||
case.addCompareOutput(
|
||||
\\extern fn puts(s: [*:0]const u8) c_int;
|
||||
\\export fn main() c_int {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user