| author | |
| committer | |
| log | 1a2dd85570a6439f82f7a0dd2cbf452198168c5a |
| tree | 5731c222791af559cb26ce11dcc82b91faf30f33 |
| parent | cd95444e4729761033f35d689a3b6ad6f4630552 |
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 | 27 | const stage1 = @import("stage1.zig"); |
| 28 | 28 | const translate_c = @import("translate_c.zig"); |
| 29 | 29 | const c_codegen = @import("codegen/c.zig"); |
| 30 | const c_link = @import("link/C.zig"); | |
| 31 | 30 | const ThreadPool = @import("ThreadPool.zig"); |
| 32 | 31 | const WaitGroup = @import("WaitGroup.zig"); |
| 33 | 32 | const libtsan = @import("libtsan.zig"); |
| ... | ... | @@ -162,6 +161,8 @@ pub const CSourceFile = struct { |
| 162 | 161 | const Job = union(enum) { |
| 163 | 162 | /// Write the machine code for a Decl to the output file. |
| 164 | 163 | codegen_decl: *Module.Decl, |
| 164 | /// Render the .h file snippet for the Decl. | |
| 165 | emit_h_decl: *Module.Decl, | |
| 165 | 166 | /// The Decl needs to be analyzed and possibly export itself. |
| 166 | 167 | /// It may have already be analyzed, or it may have been determined |
| 167 | 168 | /// to be outdated; in this case perform semantic analysis again. |
| ... | ... | @@ -1312,9 +1313,14 @@ pub fn update(self: *Compilation) !void { |
| 1312 | 1313 | |
| 1313 | 1314 | // This is needed before reading the error flags. |
| 1314 | 1315 | try self.bin_file.flush(self); |
| 1315 | ||
| 1316 | 1316 | self.link_error_flags = self.bin_file.errorFlags(); |
| 1317 | 1317 | |
| 1318 | if (!use_stage1) { | |
| 1319 | if (self.bin_file.options.module) |module| { | |
| 1320 | try link.File.C.flushEmitH(module); | |
| 1321 | } | |
| 1322 | } | |
| 1323 | ||
| 1318 | 1324 | // If there are any errors, we anticipate the source files being loaded |
| 1319 | 1325 | // to report error messages. Otherwise we unload all source files to save memory. |
| 1320 | 1326 | if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { |
| ... | ... | @@ -1340,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 1340 | 1346 | var total: usize = self.failed_c_objects.items().len; |
| 1341 | 1347 | |
| 1342 | 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 | 1351 | module.failed_exports.items().len + |
| 1345 | 1352 | module.failed_files.items().len + |
| 1346 | 1353 | @boolToInt(module.failed_root_src_file != null); |
| ... | ... | @@ -1379,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1379 | 1386 | const source = try decl.scope.getSource(module); |
| 1380 | 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 | 1395 | for (module.failed_exports.items()) |entry| { |
| 1383 | 1396 | const decl = entry.key.owner_decl; |
| 1384 | 1397 | const err_msg = entry.value; |
| ... | ... | @@ -1476,27 +1489,68 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1476 | 1489 | |
| 1477 | 1490 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); |
| 1478 | 1491 | |
| 1479 | self.bin_file.updateDecl(module, decl) catch |err| { | |
| 1480 | switch (err) { | |
| 1481 | error.OutOfMemory => return error.OutOfMemory, | |
| 1482 | error.AnalysisFail => { | |
| 1483 | decl.analysis = .codegen_failure; | |
| 1484 | }, | |
| 1485 | else => { | |
| 1486 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); | |
| 1487 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 1488 | module.gpa, | |
| 1489 | decl.src(), | |
| 1490 | "unable to codegen: {s}", | |
| 1491 | .{@errorName(err)}, | |
| 1492 | )); | |
| 1493 | decl.analysis = .codegen_failure_retryable; | |
| 1494 | }, | |
| 1495 | } | |
| 1496 | return; | |
| 1492 | self.bin_file.updateDecl(module, decl) catch |err| switch (err) { | |
| 1493 | error.OutOfMemory => return error.OutOfMemory, | |
| 1494 | error.AnalysisFail => { | |
| 1495 | decl.analysis = .codegen_failure; | |
| 1496 | continue; | |
| 1497 | }, | |
| 1498 | else => { | |
| 1499 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); | |
| 1500 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 1501 | module.gpa, | |
| 1502 | decl.src(), | |
| 1503 | "unable to codegen: {s}", | |
| 1504 | .{@errorName(err)}, | |
| 1505 | )); | |
| 1506 | decl.analysis = .codegen_failure_retryable; | |
| 1507 | continue; | |
| 1508 | }, | |
| 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 | 1554 | .analyze_decl => |decl| { |
| 1501 | 1555 | if (build_options.omit_stage2) |
| 1502 | 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 | 57 | /// Note that a Decl can succeed but the Fn it represents can fail. In this case, |
| 58 | 58 | /// a Decl can have a failed_decls entry but have analysis status of success. |
| 59 | 59 | failed_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. | |
| 63 | emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, | |
| 60 | 64 | /// Using a map here for consistency with the other fields here. |
| 61 | 65 | /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. |
| 62 | 66 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, |
| ... | ... | @@ -116,6 +120,13 @@ pub const Export = struct { |
| 116 | 120 | }, |
| 117 | 121 | }; |
| 118 | 122 | |
| 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. | |
| 125 | pub const DeclPlusEmitH = struct { | |
| 126 | decl: Decl, | |
| 127 | emit_h: EmitH, | |
| 128 | }; | |
| 129 | ||
| 119 | 130 | pub const Decl = struct { |
| 120 | 131 | /// This name is relative to the containing namespace of the decl. It uses a null-termination |
| 121 | 132 | /// 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 | 215 | /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself` |
| 205 | 216 | pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false); |
| 206 | 217 | |
| 207 | pub fn destroy(self: *Decl, gpa: *Allocator) void { | |
| 218 | pub fn destroy(self: *Decl, module: *Module) void { | |
| 219 | const gpa = module.gpa; | |
| 208 | 220 | gpa.free(mem.spanZ(self.name)); |
| 209 | 221 | if (self.typedValueManaged()) |tvm| { |
| 210 | 222 | tvm.deinit(gpa); |
| 211 | 223 | } |
| 212 | 224 | self.dependants.deinit(gpa); |
| 213 | 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 | } |
| 216 | 234 | |
| 217 | 235 | pub fn src(self: Decl) usize { |
| ... | ... | @@ -277,6 +295,12 @@ pub const Decl = struct { |
| 277 | 295 | return self.scope.cast(Scope.Container).?.file_scope; |
| 278 | 296 | } |
| 279 | 297 | |
| 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 | 304 | fn removeDependant(self: *Decl, other: *Decl) void { |
| 281 | 305 | self.dependants.removeAssertDiscard(other); |
| 282 | 306 | } |
| ... | ... | @@ -286,6 +310,11 @@ pub const Decl = struct { |
| 286 | 310 | } |
| 287 | 311 | }; |
| 288 | 312 | |
| 313 | /// This state is attached to every Decl when Module emit_h is non-null. | |
| 314 | pub const EmitH = struct { | |
| 315 | fwd_decl: std.ArrayListUnmanaged(u8) = .{}, | |
| 316 | }; | |
| 317 | ||
| 289 | 318 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| 290 | 319 | /// Extern functions do not have this data structure; they are represented by |
| 291 | 320 | /// the `Decl` only, with a `Value` tag of `extern_fn`. |
| ... | ... | @@ -883,7 +912,7 @@ pub fn deinit(self: *Module) void { |
| 883 | 912 | self.deletion_set.deinit(gpa); |
| 884 | 913 | |
| 885 | 914 | for (self.decl_table.items()) |entry| { |
| 886 | entry.value.destroy(gpa); | |
| 915 | entry.value.destroy(self); | |
| 887 | 916 | } |
| 888 | 917 | self.decl_table.deinit(gpa); |
| 889 | 918 | |
| ... | ... | @@ -892,6 +921,11 @@ pub fn deinit(self: *Module) void { |
| 892 | 921 | } |
| 893 | 922 | self.failed_decls.deinit(gpa); |
| 894 | 923 | |
| 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 | 929 | for (self.failed_files.items()) |entry| { |
| 896 | 930 | entry.value.destroy(gpa); |
| 897 | 931 | } |
| ... | ... | @@ -1150,6 +1184,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1150 | 1184 | try self.comp.bin_file.allocateDeclIndexes(decl); |
| 1151 | 1185 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1152 | 1186 | |
| 1187 | if (type_changed and self.emit_h != null) { | |
| 1188 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | |
| 1189 | } | |
| 1190 | ||
| 1153 | 1191 | return type_changed; |
| 1154 | 1192 | }; |
| 1155 | 1193 | |
| ... | ... | @@ -1269,6 +1307,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1269 | 1307 | // increasing how many computations can be done in parallel. |
| 1270 | 1308 | try self.comp.bin_file.allocateDeclIndexes(decl); |
| 1271 | 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 | 1313 | } else if (!prev_is_inline and prev_type_has_bits) { |
| 1273 | 1314 | self.comp.bin_file.freeDecl(decl); |
| 1274 | 1315 | } |
| ... | ... | @@ -1837,9 +1878,13 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 1837 | 1878 | if (self.failed_decls.remove(decl)) |entry| { |
| 1838 | 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 | 1884 | self.deleteDeclExports(decl); |
| 1841 | 1885 | self.comp.bin_file.freeDecl(decl); |
| 1842 | decl.destroy(self.gpa); | |
| 1886 | ||
| 1887 | decl.destroy(self); | |
| 1843 | 1888 | } |
| 1844 | 1889 | |
| 1845 | 1890 | /// 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 | 1968 | if (self.failed_decls.remove(decl)) |entry| { |
| 1924 | 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 | 1974 | decl.analysis = .outdated; |
| 1927 | 1975 | } |
| 1928 | 1976 | |
| 1929 | 1977 | fn allocateNewDecl( |
| 1930 | self: *Module, | |
| 1978 | mod: *Module, | |
| 1931 | 1979 | scope: *Scope, |
| 1932 | 1980 | src_index: usize, |
| 1933 | 1981 | contents_hash: std.zig.SrcHash, |
| 1934 | 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 | 1993 | new_decl.* = .{ |
| 1937 | 1994 | .name = "", |
| 1938 | 1995 | .scope = scope.namespace(), |
| ... | ... | @@ -1941,14 +1998,14 @@ fn allocateNewDecl( |
| 1941 | 1998 | .analysis = .unreferenced, |
| 1942 | 1999 | .deletion_flag = false, |
| 1943 | 2000 | .contents_hash = contents_hash, |
| 1944 | .link = switch (self.comp.bin_file.tag) { | |
| 2001 | .link = switch (mod.comp.bin_file.tag) { | |
| 1945 | 2002 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, |
| 1946 | 2003 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 1947 | 2004 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 1948 | 2005 | .c => .{ .c = link.File.C.DeclBlock.empty }, |
| 1949 | 2006 | .wasm => .{ .wasm = {} }, |
| 1950 | 2007 | }, |
| 1951 | .fn_link = switch (self.comp.bin_file.tag) { | |
| 2008 | .fn_link = switch (mod.comp.bin_file.tag) { | |
| 1952 | 2009 | .coff => .{ .coff = {} }, |
| 1953 | 2010 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, |
| 1954 | 2011 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
src/codegen/c.zig+33-27| ... | ... | @@ -9,6 +9,7 @@ const Compilation = @import("../Compilation.zig"); |
| 9 | 9 | const Inst = @import("../ir.zig").Inst; |
| 10 | 10 | const Value = @import("../value.zig").Value; |
| 11 | 11 | const Type = @import("../type.zig").Type; |
| 12 | const TypedValue = @import("../TypedValue.zig"); | |
| 12 | 13 | const C = link.File.C; |
| 13 | 14 | const Decl = Module.Decl; |
| 14 | 15 | const trace = @import("../tracy.zig").trace; |
| ... | ... | @@ -109,7 +110,7 @@ pub const Object = struct { |
| 109 | 110 | }; |
| 110 | 111 | |
| 111 | 112 | /// This data is available both when outputting .c code and when outputting an .h file. |
| 112 | const DeclGen = struct { | |
| 113 | pub const DeclGen = struct { | |
| 113 | 114 | module: *Module, |
| 114 | 115 | decl: *Decl, |
| 115 | 116 | fwd_decl: std.ArrayList(u8), |
| ... | ... | @@ -199,22 +200,11 @@ const DeclGen = struct { |
| 199 | 200 | } |
| 200 | 201 | } |
| 201 | 202 | |
| 202 | fn renderFunctionSignature(dg: *DeclGen, w: Writer) !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 | }; | |
| 203 | fn renderFunctionSignature(dg: *DeclGen, w: Writer, is_global: bool) !void { | |
| 215 | 204 | if (!is_global) { |
| 216 | 205 | try w.writeAll("static "); |
| 217 | 206 | } |
| 207 | const tv = dg.decl.typed_value.most_recent.typed_value; | |
| 218 | 208 | try dg.renderType(w, tv.ty.fnReturnType()); |
| 219 | 209 | const decl_name = mem.span(dg.decl.name); |
| 220 | 210 | try w.print(" {s}(", .{decl_name}); |
| ... | ... | @@ -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 | }; |
| 306 | 307 | |
| 307 | 308 | pub fn genDecl(o: *Object) !void { |
| ... | ... | @@ -311,15 +312,19 @@ pub fn genDecl(o: *Object) !void { |
| 311 | 312 | const tv = o.dg.decl.typed_value.most_recent.typed_value; |
| 312 | 313 | |
| 313 | 314 | if (tv.val.castTag(.function)) |func_payload| { |
| 315 | const is_global = o.dg.functionIsGlobal(tv); | |
| 314 | 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 | 321 | try fwd_decl_writer.writeAll(";\n"); |
| 317 | 322 | |
| 318 | 323 | const func: *Module.Fn = func_payload.data; |
| 319 | 324 | const instructions = func.body.instructions; |
| 320 | 325 | const writer = o.code.writer(); |
| 321 | 326 | try writer.writeAll("\n"); |
| 322 | try o.dg.renderFunctionSignature(writer); | |
| 327 | try o.dg.renderFunctionSignature(writer, is_global); | |
| 323 | 328 | if (instructions.len == 0) { |
| 324 | 329 | try writer.writeAll(" {}\n"); |
| 325 | 330 | return; |
| ... | ... | @@ -363,7 +368,8 @@ pub fn genDecl(o: *Object) !void { |
| 363 | 368 | try writer.writeAll("}\n"); |
| 364 | 369 | } else if (tv.val.tag() == .extern_fn) { |
| 365 | 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 | 373 | try writer.writeAll(";\n"); |
| 368 | 374 | } else { |
| 369 | 375 | const writer = o.code.writer(); |
| ... | ... | @@ -381,20 +387,20 @@ pub fn genDecl(o: *Object) !void { |
| 381 | 387 | } |
| 382 | 388 | } |
| 383 | 389 | |
| 384 | pub fn genHeader(comp: *Compilation, dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { | |
| 390 | pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { | |
| 385 | 391 | const tracy = trace(@src()); |
| 386 | 392 | defer tracy.end(); |
| 387 | 393 | |
| 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 | 398 | .Fn => { |
| 390 | dg.renderFunctionSignature() catch |err| switch (err) { | |
| 391 | error.AnalysisFail => { | |
| 392 | try dg.module.failed_decls.put(dg.module.gpa, decl, dg.error_msg.?); | |
| 393 | dg.error_msg = null; | |
| 394 | return error.AnalysisFail; | |
| 395 | }, | |
| 396 | else => |e| return e, | |
| 397 | }; | |
| 399 | const is_global = dg.functionIsGlobal(tv); | |
| 400 | if (is_global) { | |
| 401 | try writer.writeAll("ZIG_EXTERN_C "); | |
| 402 | } | |
| 403 | try dg.renderFunctionSignature(writer, is_global); | |
| 398 | 404 | try dg.fwd_decl.appendSlice(";\n"); |
| 399 | 405 | }, |
| 400 | 406 | else => {}, |
src/link/C.zig+44-2| ... | ... | @@ -130,8 +130,10 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 130 | 130 | const tracy = trace(@src()); |
| 131 | 131 | defer tracy.end(); |
| 132 | 132 | |
| 133 | const module = self.base.options.module orelse | |
| 134 | return error.LinkingWithoutZigSourceUnimplemented; | |
| 133 | const module = self.base.options.module.?; | |
| 134 | ||
| 135 | // This code path happens exclusively with -ofmt=c. The flush logic for | |
| 136 | // emit-h is in `flushEmitH` below. | |
| 135 | 137 | |
| 136 | 138 | // We collect a list of buffers to write, and write them all at once with pwritev 😎 |
| 137 | 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 | 189 | try file.pwritevAll(all_buffers.items, 0); |
| 188 | 190 | } |
| 189 | 191 | |
| 192 | pub 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 | ||
| 190 | 232 | pub fn updateDeclExports( |
| 191 | 233 | self: *C, |
| 192 | 234 | module: *Module, |
src/link/C/zig.h+10-4| ... | ... | @@ -23,11 +23,17 @@ |
| 23 | 23 | #endif |
| 24 | 24 | |
| 25 | 25 | #if __STDC_VERSION__ >= 199901L |
| 26 | #define zig_restrict restrict | |
| 26 | #define ZIG_RESTRICT restrict | |
| 27 | 27 | #elif defined(__GNUC__) |
| 28 | #define zig_restrict __restrict | |
| 28 | #define ZIG_RESTRICT __restrict | |
| 29 | 29 | #else |
| 30 | #define zig_restrict | |
| 30 | #define ZIG_RESTRICT | |
| 31 | #endif | |
| 32 | ||
| 33 | #ifdef __cplusplus | |
| 34 | #define ZIG_EXTERN_C extern "C" | |
| 35 | #else | |
| 36 | #define ZIG_EXTERN_C | |
| 31 | 37 | #endif |
| 32 | 38 | |
| 33 | 39 | #if defined(_MSC_VER) |
| ... | ... | @@ -48,5 +54,5 @@ |
| 48 | 54 | #include <stddef.h> |
| 49 | 55 | #define int128_t __int128 |
| 50 | 56 | #define uint128_t unsigned __int128 |
| 51 | void *memcpy (void *zig_restrict, const void *zig_restrict, size_t); | |
| 57 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); | |
| 52 | 58 |
test/stage2/cbe.zig+12-12| ... | ... | @@ -180,7 +180,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 180 | 180 | \\ unreachable; |
| 181 | 181 | \\} |
| 182 | 182 | , |
| 183 | \\zig_noreturn void _start(void); | |
| 183 | \\ZIG_EXTERN_C zig_noreturn void _start(void); | |
| 184 | 184 | \\ |
| 185 | 185 | \\zig_noreturn void _start(void) { |
| 186 | 186 | \\ zig_breakpoint(); |
| ... | ... | @@ -191,37 +191,37 @@ pub fn addCases(ctx: *TestContext) !void { |
| 191 | 191 | ctx.h("simple header", linux_x64, |
| 192 | 192 | \\export fn start() void{} |
| 193 | 193 | , |
| 194 | \\void start(void); | |
| 194 | \\ZIG_EXTERN_C void start(void); | |
| 195 | 195 | \\ |
| 196 | 196 | ); |
| 197 | 197 | ctx.h("header with single param function", linux_x64, |
| 198 | 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 | 203 | ctx.h("header with multiple param function", linux_x64, |
| 204 | 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 | 209 | ctx.h("header with u32 param function", linux_x64, |
| 210 | 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 | 215 | ctx.h("header with usize param function", linux_x64, |
| 216 | 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 | 221 | ctx.h("header with bool param function", linux_x64, |
| 222 | 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 | 227 | ctx.h("header with noreturn function", linux_x64, |
| ... | ... | @@ -229,7 +229,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 229 | 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 | 235 | ctx.h("header with multiple functions", linux_x64, |
| ... | ... | @@ -237,15 +237,15 @@ pub fn addCases(ctx: *TestContext) !void { |
| 237 | 237 | \\export fn b() void{} |
| 238 | 238 | \\export fn c() void{} |
| 239 | 239 | , |
| 240 | \\void a(void); | |
| 241 | \\void b(void); | |
| 242 | \\void c(void); | |
| 240 | \\ZIG_EXTERN_C void a(void); | |
| 241 | \\ZIG_EXTERN_C void b(void); | |
| 242 | \\ZIG_EXTERN_C void c(void); | |
| 243 | 243 | \\ |
| 244 | 244 | ); |
| 245 | 245 | ctx.h("header with multiple includes", linux_x64, |
| 246 | 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 | } |