authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-05 17:33:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-05 17:41:14-07:00
log1a2dd85570a6439f82f7a0dd2cbf452198168c5a
tree5731c222791af559cb26ce11dcc82b91faf30f33
parentcd95444e4729761033f35d689a3b6ad6f4630552

stage2: C backend: re-implement emit-h

and also mark functions as `extern "C"` as appropriate to support c++ compilers.

6 files changed, 239 insertions(+), 74 deletions(-)

src/Compilation.zig+75-21
...@@ -27,7 +27,6 @@ const Cache = @import("Cache.zig");...@@ -27,7 +27,6 @@ const Cache = @import("Cache.zig");
27const stage1 = @import("stage1.zig");27const stage1 = @import("stage1.zig");
28const translate_c = @import("translate_c.zig");28const translate_c = @import("translate_c.zig");
29const c_codegen = @import("codegen/c.zig");29const c_codegen = @import("codegen/c.zig");
30const c_link = @import("link/C.zig");
31const ThreadPool = @import("ThreadPool.zig");30const ThreadPool = @import("ThreadPool.zig");
32const WaitGroup = @import("WaitGroup.zig");31const WaitGroup = @import("WaitGroup.zig");
33const libtsan = @import("libtsan.zig");32const libtsan = @import("libtsan.zig");
...@@ -162,6 +161,8 @@ pub const CSourceFile = struct {...@@ -162,6 +161,8 @@ pub const CSourceFile = struct {
162const Job = union(enum) {161const Job = union(enum) {
163 /// Write the machine code for a Decl to the output file.162 /// Write the machine code for a Decl to the output file.
164 codegen_decl: *Module.Decl,163 codegen_decl: *Module.Decl,
164 /// Render the .h file snippet for the Decl.
165 emit_h_decl: *Module.Decl,
165 /// The Decl needs to be analyzed and possibly export itself.166 /// The Decl needs to be analyzed and possibly export itself.
166 /// It may have already be analyzed, or it may have been determined167 /// It may have already be analyzed, or it may have been determined
167 /// to be outdated; in this case perform semantic analysis again.168 /// to be outdated; in this case perform semantic analysis again.
...@@ -1312,9 +1313,14 @@ pub fn update(self: *Compilation) !void {...@@ -1312,9 +1313,14 @@ pub fn update(self: *Compilation) !void {
13121313
1313 // This is needed before reading the error flags.1314 // This is needed before reading the error flags.
1314 try self.bin_file.flush(self);1315 try self.bin_file.flush(self);
1315
1316 self.link_error_flags = self.bin_file.errorFlags();1316 self.link_error_flags = self.bin_file.errorFlags();
13171317
1318 if (!use_stage1) {
1319 if (self.bin_file.options.module) |module| {
1320 try link.File.C.flushEmitH(module);
1321 }
1322 }
1323
1318 // If there are any errors, we anticipate the source files being loaded1324 // If there are any errors, we anticipate the source files being loaded
1319 // to report error messages. Otherwise we unload all source files to save memory.1325 // to report error messages. Otherwise we unload all source files to save memory.
1320 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {1326 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
...@@ -1340,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize {...@@ -1340,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize {
1340 var total: usize = self.failed_c_objects.items().len;1346 var total: usize = self.failed_c_objects.items().len;
13411347
1342 if (self.bin_file.options.module) |module| {1348 if (self.bin_file.options.module) |module| {
1343 total += module.failed_decls.items().len +1349 total += module.failed_decls.count() +
1350 module.emit_h_failed_decls.count() +
1344 module.failed_exports.items().len +1351 module.failed_exports.items().len +
1345 module.failed_files.items().len +1352 module.failed_files.items().len +
1346 @boolToInt(module.failed_root_src_file != null);1353 @boolToInt(module.failed_root_src_file != null);
...@@ -1379,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -1379,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
1379 const source = try decl.scope.getSource(module);1386 const source = try decl.scope.getSource(module);
1380 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);1387 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
1381 }1388 }
1389 for (module.emit_h_failed_decls.items()) |entry| {
1390 const decl = entry.key;
1391 const err_msg = entry.value;
1392 const source = try decl.scope.getSource(module);
1393 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
1394 }
1382 for (module.failed_exports.items()) |entry| {1395 for (module.failed_exports.items()) |entry| {
1383 const decl = entry.key.owner_decl;1396 const decl = entry.key.owner_decl;
1384 const err_msg = entry.value;1397 const err_msg = entry.value;
...@@ -1476,27 +1489,68 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1476,27 +1489,68 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14761489
1477 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());1490 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
14781491
1479 self.bin_file.updateDecl(module, decl) catch |err| {1492 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
1480 switch (err) {1493 error.OutOfMemory => return error.OutOfMemory,
1481 error.OutOfMemory => return error.OutOfMemory,1494 error.AnalysisFail => {
1482 error.AnalysisFail => {1495 decl.analysis = .codegen_failure;
1483 decl.analysis = .codegen_failure;1496 continue;
1484 },1497 },
1485 else => {1498 else => {
1486 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);1499 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
1487 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1500 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1488 module.gpa,1501 module.gpa,
1489 decl.src(),1502 decl.src(),
1490 "unable to codegen: {s}",1503 "unable to codegen: {s}",
1491 .{@errorName(err)},1504 .{@errorName(err)},
1492 ));1505 ));
1493 decl.analysis = .codegen_failure_retryable;1506 decl.analysis = .codegen_failure_retryable;
1494 },1507 continue;
1495 }1508 },
1496 return;
1497 };1509 };
1498 },1510 },
1499 },1511 },
1512 .emit_h_decl => |decl| switch (decl.analysis) {
1513 .unreferenced => unreachable,
1514 .in_progress => unreachable,
1515 .outdated => unreachable,
1516
1517 .sema_failure,
1518 .dependency_failure,
1519 .sema_failure_retryable,
1520 => continue,
1521
1522 // emit-h only requires semantic analysis of the Decl to be complete,
1523 // it does not depend on machine code generation to succeed.
1524 .codegen_failure, .codegen_failure_retryable, .complete => {
1525 if (build_options.omit_stage2)
1526 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
1527 const module = self.bin_file.options.module.?;
1528 const emit_loc = module.emit_h.?;
1529 const tv = decl.typed_value.most_recent.typed_value;
1530 const emit_h = decl.getEmitH(module);
1531 const fwd_decl = &emit_h.fwd_decl;
1532 fwd_decl.shrinkRetainingCapacity(0);
1533
1534 var dg: c_codegen.DeclGen = .{
1535 .module = module,
1536 .error_msg = null,
1537 .decl = decl,
1538 .fwd_decl = fwd_decl.toManaged(module.gpa),
1539 };
1540 defer dg.fwd_decl.deinit();
1541
1542 c_codegen.genHeader(&dg) catch |err| switch (err) {
1543 error.AnalysisFail => {
1544 try module.emit_h_failed_decls.put(module.gpa, decl, dg.error_msg.?);
1545 continue;
1546 },
1547 else => |e| return e,
1548 };
1549
1550 fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
1551 fwd_decl.shrink(module.gpa, fwd_decl.items.len);
1552 },
1553 },
1500 .analyze_decl => |decl| {1554 .analyze_decl => |decl| {
1501 if (build_options.omit_stage2)1555 if (build_options.omit_stage2)
1502 @panic("sadly stage2 is omitted from this build to save memory on the CI server");1556 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
src/Module.zig+65-8
...@@ -57,6 +57,10 @@ decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_has...@@ -57,6 +57,10 @@ decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_has
57/// Note that a Decl can succeed but the Fn it represents can fail. In this case,57/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
58/// a Decl can have a failed_decls entry but have analysis status of success.58/// a Decl can have a failed_decls entry but have analysis status of success.
59failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},59failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
60/// When emit_h is non-null, each Decl gets one more compile error slot for
61/// emit-h failing for that Decl. This table is also how we tell if a Decl has
62/// failed emit-h or succeeded.
63emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
60/// Using a map here for consistency with the other fields here.64/// Using a map here for consistency with the other fields here.
61/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.65/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
62failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},66failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
...@@ -116,6 +120,13 @@ pub const Export = struct {...@@ -116,6 +120,13 @@ pub const Export = struct {
116 },120 },
117};121};
118122
123/// When Module emit_h field is non-null, each Decl is allocated via this struct, so that
124/// there can be EmitH state attached to each Decl.
125pub const DeclPlusEmitH = struct {
126 decl: Decl,
127 emit_h: EmitH,
128};
129
119pub const Decl = struct {130pub const Decl = struct {
120 /// This name is relative to the containing namespace of the decl. It uses a null-termination131 /// This name is relative to the containing namespace of the decl. It uses a null-termination
121 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed132 /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
...@@ -204,14 +215,21 @@ pub const Decl = struct {...@@ -204,14 +215,21 @@ pub const Decl = struct {
204 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`215 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
205 pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);216 pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
206217
207 pub fn destroy(self: *Decl, gpa: *Allocator) void {218 pub fn destroy(self: *Decl, module: *Module) void {
219 const gpa = module.gpa;
208 gpa.free(mem.spanZ(self.name));220 gpa.free(mem.spanZ(self.name));
209 if (self.typedValueManaged()) |tvm| {221 if (self.typedValueManaged()) |tvm| {
210 tvm.deinit(gpa);222 tvm.deinit(gpa);
211 }223 }
212 self.dependants.deinit(gpa);224 self.dependants.deinit(gpa);
213 self.dependencies.deinit(gpa);225 self.dependencies.deinit(gpa);
214 gpa.destroy(self);226 if (module.emit_h != null) {
227 const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", self);
228 decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa);
229 gpa.destroy(decl_plus_emit_h);
230 } else {
231 gpa.destroy(self);
232 }
215 }233 }
216234
217 pub fn src(self: Decl) usize {235 pub fn src(self: Decl) usize {
...@@ -277,6 +295,12 @@ pub const Decl = struct {...@@ -277,6 +295,12 @@ pub const Decl = struct {
277 return self.scope.cast(Scope.Container).?.file_scope;295 return self.scope.cast(Scope.Container).?.file_scope;
278 }296 }
279297
298 pub fn getEmitH(decl: *Decl, module: *Module) *EmitH {
299 assert(module.emit_h != null);
300 const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl);
301 return &decl_plus_emit_h.emit_h;
302 }
303
280 fn removeDependant(self: *Decl, other: *Decl) void {304 fn removeDependant(self: *Decl, other: *Decl) void {
281 self.dependants.removeAssertDiscard(other);305 self.dependants.removeAssertDiscard(other);
282 }306 }
...@@ -286,6 +310,11 @@ pub const Decl = struct {...@@ -286,6 +310,11 @@ pub const Decl = struct {
286 }310 }
287};311};
288312
313/// This state is attached to every Decl when Module emit_h is non-null.
314pub const EmitH = struct {
315 fwd_decl: std.ArrayListUnmanaged(u8) = .{},
316};
317
289/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.318/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
290/// Extern functions do not have this data structure; they are represented by319/// Extern functions do not have this data structure; they are represented by
291/// the `Decl` only, with a `Value` tag of `extern_fn`.320/// the `Decl` only, with a `Value` tag of `extern_fn`.
...@@ -883,7 +912,7 @@ pub fn deinit(self: *Module) void {...@@ -883,7 +912,7 @@ pub fn deinit(self: *Module) void {
883 self.deletion_set.deinit(gpa);912 self.deletion_set.deinit(gpa);
884913
885 for (self.decl_table.items()) |entry| {914 for (self.decl_table.items()) |entry| {
886 entry.value.destroy(gpa);915 entry.value.destroy(self);
887 }916 }
888 self.decl_table.deinit(gpa);917 self.decl_table.deinit(gpa);
889918
...@@ -892,6 +921,11 @@ pub fn deinit(self: *Module) void {...@@ -892,6 +921,11 @@ pub fn deinit(self: *Module) void {
892 }921 }
893 self.failed_decls.deinit(gpa);922 self.failed_decls.deinit(gpa);
894923
924 for (self.emit_h_failed_decls.items()) |entry| {
925 entry.value.destroy(gpa);
926 }
927 self.emit_h_failed_decls.deinit(gpa);
928
895 for (self.failed_files.items()) |entry| {929 for (self.failed_files.items()) |entry| {
896 entry.value.destroy(gpa);930 entry.value.destroy(gpa);
897 }931 }
...@@ -1150,6 +1184,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1150,6 +1184,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1150 try self.comp.bin_file.allocateDeclIndexes(decl);1184 try self.comp.bin_file.allocateDeclIndexes(decl);
1151 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });1185 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
11521186
1187 if (type_changed and self.emit_h != null) {
1188 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1189 }
1190
1153 return type_changed;1191 return type_changed;
1154 };1192 };
11551193
...@@ -1269,6 +1307,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1269,6 +1307,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1269 // increasing how many computations can be done in parallel.1307 // increasing how many computations can be done in parallel.
1270 try self.comp.bin_file.allocateDeclIndexes(decl);1308 try self.comp.bin_file.allocateDeclIndexes(decl);
1271 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });1309 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1310 if (type_changed and self.emit_h != null) {
1311 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1312 }
1272 } else if (!prev_is_inline and prev_type_has_bits) {1313 } else if (!prev_is_inline and prev_type_has_bits) {
1273 self.comp.bin_file.freeDecl(decl);1314 self.comp.bin_file.freeDecl(decl);
1274 }1315 }
...@@ -1837,9 +1878,13 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -1837,9 +1878,13 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
1837 if (self.failed_decls.remove(decl)) |entry| {1878 if (self.failed_decls.remove(decl)) |entry| {
1838 entry.value.destroy(self.gpa);1879 entry.value.destroy(self.gpa);
1839 }1880 }
1881 if (self.emit_h_failed_decls.remove(decl)) |entry| {
1882 entry.value.destroy(self.gpa);
1883 }
1840 self.deleteDeclExports(decl);1884 self.deleteDeclExports(decl);
1841 self.comp.bin_file.freeDecl(decl);1885 self.comp.bin_file.freeDecl(decl);
1842 decl.destroy(self.gpa);1886
1887 decl.destroy(self);
1843}1888}
18441889
1845/// Delete all the Export objects that are caused by this Decl. Re-analysis of1890/// Delete all the Export objects that are caused by this Decl. Re-analysis of
...@@ -1923,16 +1968,28 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {...@@ -1923,16 +1968,28 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1923 if (self.failed_decls.remove(decl)) |entry| {1968 if (self.failed_decls.remove(decl)) |entry| {
1924 entry.value.destroy(self.gpa);1969 entry.value.destroy(self.gpa);
1925 }1970 }
1971 if (self.emit_h_failed_decls.remove(decl)) |entry| {
1972 entry.value.destroy(self.gpa);
1973 }
1926 decl.analysis = .outdated;1974 decl.analysis = .outdated;
1927}1975}
19281976
1929fn allocateNewDecl(1977fn allocateNewDecl(
1930 self: *Module,1978 mod: *Module,
1931 scope: *Scope,1979 scope: *Scope,
1932 src_index: usize,1980 src_index: usize,
1933 contents_hash: std.zig.SrcHash,1981 contents_hash: std.zig.SrcHash,
1934) !*Decl {1982) !*Decl {
1935 const new_decl = try self.gpa.create(Decl);1983 // If we have emit-h then we must allocate a bigger structure to store the emit-h state.
1984 const new_decl: *Decl = if (mod.emit_h != null) blk: {
1985 const parent_struct = try mod.gpa.create(DeclPlusEmitH);
1986 parent_struct.* = .{
1987 .emit_h = .{},
1988 .decl = undefined,
1989 };
1990 break :blk &parent_struct.decl;
1991 } else try mod.gpa.create(Decl);
1992
1936 new_decl.* = .{1993 new_decl.* = .{
1937 .name = "",1994 .name = "",
1938 .scope = scope.namespace(),1995 .scope = scope.namespace(),
...@@ -1941,14 +1998,14 @@ fn allocateNewDecl(...@@ -1941,14 +1998,14 @@ fn allocateNewDecl(
1941 .analysis = .unreferenced,1998 .analysis = .unreferenced,
1942 .deletion_flag = false,1999 .deletion_flag = false,
1943 .contents_hash = contents_hash,2000 .contents_hash = contents_hash,
1944 .link = switch (self.comp.bin_file.tag) {2001 .link = switch (mod.comp.bin_file.tag) {
1945 .coff => .{ .coff = link.File.Coff.TextBlock.empty },2002 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
1946 .elf => .{ .elf = link.File.Elf.TextBlock.empty },2003 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
1947 .macho => .{ .macho = link.File.MachO.TextBlock.empty },2004 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1948 .c => .{ .c = link.File.C.DeclBlock.empty },2005 .c => .{ .c = link.File.C.DeclBlock.empty },
1949 .wasm => .{ .wasm = {} },2006 .wasm => .{ .wasm = {} },
1950 },2007 },
1951 .fn_link = switch (self.comp.bin_file.tag) {2008 .fn_link = switch (mod.comp.bin_file.tag) {
1952 .coff => .{ .coff = {} },2009 .coff => .{ .coff = {} },
1953 .elf => .{ .elf = link.File.Elf.SrcFn.empty },2010 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
1954 .macho => .{ .macho = link.File.MachO.SrcFn.empty },2011 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
src/codegen/c.zig+33-27
...@@ -9,6 +9,7 @@ const Compilation = @import("../Compilation.zig");...@@ -9,6 +9,7 @@ const Compilation = @import("../Compilation.zig");
9const Inst = @import("../ir.zig").Inst;9const Inst = @import("../ir.zig").Inst;
10const Value = @import("../value.zig").Value;10const Value = @import("../value.zig").Value;
11const Type = @import("../type.zig").Type;11const Type = @import("../type.zig").Type;
12const TypedValue = @import("../TypedValue.zig");
12const C = link.File.C;13const C = link.File.C;
13const Decl = Module.Decl;14const Decl = Module.Decl;
14const trace = @import("../tracy.zig").trace;15const trace = @import("../tracy.zig").trace;
...@@ -109,7 +110,7 @@ pub const Object = struct {...@@ -109,7 +110,7 @@ pub const Object = struct {
109};110};
110111
111/// This data is available both when outputting .c code and when outputting an .h file.112/// This data is available both when outputting .c code and when outputting an .h file.
112const DeclGen = struct {113pub const DeclGen = struct {
113 module: *Module,114 module: *Module,
114 decl: *Decl,115 decl: *Decl,
115 fwd_decl: std.ArrayList(u8),116 fwd_decl: std.ArrayList(u8),
...@@ -199,22 +200,11 @@ const DeclGen = struct {...@@ -199,22 +200,11 @@ const DeclGen = struct {
199 }200 }
200 }201 }
201202
202 fn renderFunctionSignature(dg: *DeclGen, w: Writer) !void {203 fn renderFunctionSignature(dg: *DeclGen, w: Writer, is_global: bool) !void {
203 const tv = dg.decl.typed_value.most_recent.typed_value;
204 // Determine whether the function is globally visible.
205 const is_global = blk: {
206 switch (tv.val.tag()) {
207 .extern_fn => break :blk true,
208 .function => {
209 const func = tv.val.castTag(.function).?.data;
210 break :blk dg.module.decl_exports.contains(func.owner_decl);
211 },
212 else => unreachable,
213 }
214 };
215 if (!is_global) {204 if (!is_global) {
216 try w.writeAll("static ");205 try w.writeAll("static ");
217 }206 }
207 const tv = dg.decl.typed_value.most_recent.typed_value;
218 try dg.renderType(w, tv.ty.fnReturnType());208 try dg.renderType(w, tv.ty.fnReturnType());
219 const decl_name = mem.span(dg.decl.name);209 const decl_name = mem.span(dg.decl.name);
220 try w.print(" {s}(", .{decl_name});210 try w.print(" {s}(", .{decl_name});
...@@ -302,6 +292,17 @@ const DeclGen = struct {...@@ -302,6 +292,17 @@ const DeclGen = struct {
302 }),292 }),
303 }293 }
304 }294 }
295
296 fn functionIsGlobal(dg: *DeclGen, tv: TypedValue) bool {
297 switch (tv.val.tag()) {
298 .extern_fn => return true,
299 .function => {
300 const func = tv.val.castTag(.function).?.data;
301 return dg.module.decl_exports.contains(func.owner_decl);
302 },
303 else => unreachable,
304 }
305 }
305};306};
306307
307pub fn genDecl(o: *Object) !void {308pub fn genDecl(o: *Object) !void {
...@@ -311,15 +312,19 @@ pub fn genDecl(o: *Object) !void {...@@ -311,15 +312,19 @@ pub fn genDecl(o: *Object) !void {
311 const tv = o.dg.decl.typed_value.most_recent.typed_value;312 const tv = o.dg.decl.typed_value.most_recent.typed_value;
312313
313 if (tv.val.castTag(.function)) |func_payload| {314 if (tv.val.castTag(.function)) |func_payload| {
315 const is_global = o.dg.functionIsGlobal(tv);
314 const fwd_decl_writer = o.dg.fwd_decl.writer();316 const fwd_decl_writer = o.dg.fwd_decl.writer();
315 try o.dg.renderFunctionSignature(fwd_decl_writer);317 if (is_global) {
318 try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
319 }
320 try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
316 try fwd_decl_writer.writeAll(";\n");321 try fwd_decl_writer.writeAll(";\n");
317322
318 const func: *Module.Fn = func_payload.data;323 const func: *Module.Fn = func_payload.data;
319 const instructions = func.body.instructions;324 const instructions = func.body.instructions;
320 const writer = o.code.writer();325 const writer = o.code.writer();
321 try writer.writeAll("\n");326 try writer.writeAll("\n");
322 try o.dg.renderFunctionSignature(writer);327 try o.dg.renderFunctionSignature(writer, is_global);
323 if (instructions.len == 0) {328 if (instructions.len == 0) {
324 try writer.writeAll(" {}\n");329 try writer.writeAll(" {}\n");
325 return;330 return;
...@@ -363,7 +368,8 @@ pub fn genDecl(o: *Object) !void {...@@ -363,7 +368,8 @@ pub fn genDecl(o: *Object) !void {
363 try writer.writeAll("}\n");368 try writer.writeAll("}\n");
364 } else if (tv.val.tag() == .extern_fn) {369 } else if (tv.val.tag() == .extern_fn) {
365 const writer = o.code.writer();370 const writer = o.code.writer();
366 try o.dg.renderFunctionSignature(writer);371 try writer.writeAll("ZIG_EXTERN_C ");
372 try o.dg.renderFunctionSignature(writer, true);
367 try writer.writeAll(";\n");373 try writer.writeAll(";\n");
368 } else {374 } else {
369 const writer = o.code.writer();375 const writer = o.code.writer();
...@@ -381,20 +387,20 @@ pub fn genDecl(o: *Object) !void {...@@ -381,20 +387,20 @@ pub fn genDecl(o: *Object) !void {
381 }387 }
382}388}
383389
384pub fn genHeader(comp: *Compilation, dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {390pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
385 const tracy = trace(@src());391 const tracy = trace(@src());
386 defer tracy.end();392 defer tracy.end();
387393
388 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {394 const tv = dg.decl.typed_value.most_recent.typed_value;
395 const writer = dg.fwd_decl.writer();
396
397 switch (tv.ty.zigTypeTag()) {
389 .Fn => {398 .Fn => {
390 dg.renderFunctionSignature() catch |err| switch (err) {399 const is_global = dg.functionIsGlobal(tv);
391 error.AnalysisFail => {400 if (is_global) {
392 try dg.module.failed_decls.put(dg.module.gpa, decl, dg.error_msg.?);401 try writer.writeAll("ZIG_EXTERN_C ");
393 dg.error_msg = null;402 }
394 return error.AnalysisFail;403 try dg.renderFunctionSignature(writer, is_global);
395 },
396 else => |e| return e,
397 };
398 try dg.fwd_decl.appendSlice(";\n");404 try dg.fwd_decl.appendSlice(";\n");
399 },405 },
400 else => {},406 else => {},
src/link/C.zig+44-2
...@@ -130,8 +130,10 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -130,8 +130,10 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
130 const tracy = trace(@src());130 const tracy = trace(@src());
131 defer tracy.end();131 defer tracy.end();
132132
133 const module = self.base.options.module orelse133 const module = self.base.options.module.?;
134 return error.LinkingWithoutZigSourceUnimplemented;134
135 // This code path happens exclusively with -ofmt=c. The flush logic for
136 // emit-h is in `flushEmitH` below.
135137
136 // We collect a list of buffers to write, and write them all at once with pwritev 😎138 // We collect a list of buffers to write, and write them all at once with pwritev 😎
137 var all_buffers = std.ArrayList(std.os.iovec_const).init(comp.gpa);139 var all_buffers = std.ArrayList(std.os.iovec_const).init(comp.gpa);
...@@ -187,6 +189,46 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -187,6 +189,46 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
187 try file.pwritevAll(all_buffers.items, 0);189 try file.pwritevAll(all_buffers.items, 0);
188}190}
189191
192pub fn flushEmitH(module: *Module) !void {
193 const tracy = trace(@src());
194 defer tracy.end();
195
196 const emit_h_loc = module.emit_h orelse return;
197
198 // We collect a list of buffers to write, and write them all at once with pwritev 😎
199 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);
200 defer all_buffers.deinit();
201
202 try all_buffers.ensureCapacity(module.decl_table.count() + 1);
203
204 var file_size: u64 = zig_h.len;
205 all_buffers.appendAssumeCapacity(.{
206 .iov_base = zig_h,
207 .iov_len = zig_h.len,
208 });
209
210 for (module.decl_table.items()) |kv| {
211 const emit_h = kv.value.getEmitH(module);
212 const buf = emit_h.fwd_decl.items;
213 all_buffers.appendAssumeCapacity(.{
214 .iov_base = buf.ptr,
215 .iov_len = buf.len,
216 });
217 file_size += buf.len;
218 }
219
220 const directory = emit_h_loc.directory orelse module.comp.local_cache_directory;
221 const file = try directory.handle.createFile(emit_h_loc.basename, .{
222 // We set the end position explicitly below; by not truncating the file, we possibly
223 // make it easier on the file system by doing 1 reallocation instead of two.
224 .truncate = false,
225 });
226 defer file.close();
227
228 try file.setEndPos(file_size);
229 try file.pwritevAll(all_buffers.items, 0);
230}
231
190pub fn updateDeclExports(232pub fn updateDeclExports(
191 self: *C,233 self: *C,
192 module: *Module,234 module: *Module,
src/link/C/zig.h+10-4
...@@ -23,11 +23,17 @@...@@ -23,11 +23,17 @@
23#endif23#endif
2424
25#if __STDC_VERSION__ >= 199901L25#if __STDC_VERSION__ >= 199901L
26#define zig_restrict restrict26#define ZIG_RESTRICT restrict
27#elif defined(__GNUC__)27#elif defined(__GNUC__)
28#define zig_restrict __restrict28#define ZIG_RESTRICT __restrict
29#else29#else
30#define zig_restrict30#define ZIG_RESTRICT
31#endif
32
33#ifdef __cplusplus
34#define ZIG_EXTERN_C extern "C"
35#else
36#define ZIG_EXTERN_C
31#endif37#endif
3238
33#if defined(_MSC_VER)39#if defined(_MSC_VER)
...@@ -48,5 +54,5 @@...@@ -48,5 +54,5 @@
48#include <stddef.h>54#include <stddef.h>
49#define int128_t __int12855#define int128_t __int128
50#define uint128_t unsigned __int12856#define uint128_t unsigned __int128
51void *memcpy (void *zig_restrict, const void *zig_restrict, size_t);57ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
5258
test/stage2/cbe.zig+12-12
...@@ -180,7 +180,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -180,7 +180,7 @@ pub fn addCases(ctx: *TestContext) !void {
180 \\ unreachable;180 \\ unreachable;
181 \\}181 \\}
182 ,182 ,
183 \\zig_noreturn void _start(void);183 \\ZIG_EXTERN_C zig_noreturn void _start(void);
184 \\184 \\
185 \\zig_noreturn void _start(void) {185 \\zig_noreturn void _start(void) {
186 \\ zig_breakpoint();186 \\ zig_breakpoint();
...@@ -191,37 +191,37 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -191,37 +191,37 @@ pub fn addCases(ctx: *TestContext) !void {
191 ctx.h("simple header", linux_x64,191 ctx.h("simple header", linux_x64,
192 \\export fn start() void{}192 \\export fn start() void{}
193 ,193 ,
194 \\void start(void);194 \\ZIG_EXTERN_C void start(void);
195 \\195 \\
196 );196 );
197 ctx.h("header with single param function", linux_x64,197 ctx.h("header with single param function", linux_x64,
198 \\export fn start(a: u8) void{}198 \\export fn start(a: u8) void{}
199 ,199 ,
200 \\void start(uint8_t arg0);200 \\ZIG_EXTERN_C void start(uint8_t a0);
201 \\201 \\
202 );202 );
203 ctx.h("header with multiple param function", linux_x64,203 ctx.h("header with multiple param function", linux_x64,
204 \\export fn start(a: u8, b: u8, c: u8) void{}204 \\export fn start(a: u8, b: u8, c: u8) void{}
205 ,205 ,
206 \\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2);206 \\ZIG_EXTERN_C void start(uint8_t a0, uint8_t a1, uint8_t a2);
207 \\207 \\
208 );208 );
209 ctx.h("header with u32 param function", linux_x64,209 ctx.h("header with u32 param function", linux_x64,
210 \\export fn start(a: u32) void{}210 \\export fn start(a: u32) void{}
211 ,211 ,
212 \\void start(uint32_t arg0);212 \\ZIG_EXTERN_C void start(uint32_t a0);
213 \\213 \\
214 );214 );
215 ctx.h("header with usize param function", linux_x64,215 ctx.h("header with usize param function", linux_x64,
216 \\export fn start(a: usize) void{}216 \\export fn start(a: usize) void{}
217 ,217 ,
218 \\void start(uintptr_t arg0);218 \\ZIG_EXTERN_C void start(uintptr_t a0);
219 \\219 \\
220 );220 );
221 ctx.h("header with bool param function", linux_x64,221 ctx.h("header with bool param function", linux_x64,
222 \\export fn start(a: bool) void{}222 \\export fn start(a: bool) void{}
223 ,223 ,
224 \\void start(bool arg0);224 \\ZIG_EXTERN_C void start(bool a0);
225 \\225 \\
226 );226 );
227 ctx.h("header with noreturn function", linux_x64,227 ctx.h("header with noreturn function", linux_x64,
...@@ -229,7 +229,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -229,7 +229,7 @@ pub fn addCases(ctx: *TestContext) !void {
229 \\ unreachable;229 \\ unreachable;
230 \\}230 \\}
231 ,231 ,
232 \\zig_noreturn void start(void);232 \\ZIG_EXTERN_C zig_noreturn void start(void);
233 \\233 \\
234 );234 );
235 ctx.h("header with multiple functions", linux_x64,235 ctx.h("header with multiple functions", linux_x64,
...@@ -237,15 +237,15 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -237,15 +237,15 @@ pub fn addCases(ctx: *TestContext) !void {
237 \\export fn b() void{}237 \\export fn b() void{}
238 \\export fn c() void{}238 \\export fn c() void{}
239 ,239 ,
240 \\void a(void);240 \\ZIG_EXTERN_C void a(void);
241 \\void b(void);241 \\ZIG_EXTERN_C void b(void);
242 \\void c(void);242 \\ZIG_EXTERN_C void c(void);
243 \\243 \\
244 );244 );
245 ctx.h("header with multiple includes", linux_x64,245 ctx.h("header with multiple includes", linux_x64,
246 \\export fn start(a: u32, b: usize) void{}246 \\export fn start(a: u32, b: usize) void{}
247 ,247 ,
248 \\void start(uint32_t arg0, uintptr_t arg1);248 \\ZIG_EXTERN_C void start(uint32_t a0, uintptr_t a1);
249 \\249 \\
250 );250 );
251}251}