| 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,7 +27,6 @@ const Cache = @import("Cache.zig"); |
| 27 | const stage1 = @import("stage1.zig"); | 27 | const stage1 = @import("stage1.zig"); |
| 28 | const translate_c = @import("translate_c.zig"); | 28 | const translate_c = @import("translate_c.zig"); |
| 29 | const c_codegen = @import("codegen/c.zig"); | 29 | const c_codegen = @import("codegen/c.zig"); |
| 30 | const c_link = @import("link/C.zig"); | ||
| 31 | const ThreadPool = @import("ThreadPool.zig"); | 30 | const ThreadPool = @import("ThreadPool.zig"); |
| 32 | const WaitGroup = @import("WaitGroup.zig"); | 31 | const WaitGroup = @import("WaitGroup.zig"); |
| 33 | const libtsan = @import("libtsan.zig"); | 32 | const libtsan = @import("libtsan.zig"); |
| ... | @@ -162,6 +161,8 @@ pub const CSourceFile = struct { | ... | @@ -162,6 +161,8 @@ pub const CSourceFile = struct { |
| 162 | const Job = union(enum) { | 161 | const 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 determined | 167 | /// 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 { |
| 1312 | 1313 | ||
| 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(); |
| 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 | // If there are any errors, we anticipate the source files being loaded | 1324 | // 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; |
| 1341 | 1347 | ||
| 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 |
| 1476 | 1489 | ||
| 1477 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | 1490 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); |
| 1478 | 1491 | ||
| 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. |
| 59 | failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, | 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 | /// 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. |
| 62 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, | 66 | failed_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 | }; |
| 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 | pub const Decl = struct { | 130 | pub const Decl = struct { |
| 120 | /// This name is relative to the containing namespace of the decl. It uses a null-termination | 131 | /// 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 allowed | 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,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); |
| 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 | 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 | } |
| 216 | 234 | ||
| 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 | } |
| 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 | 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 | }; |
| 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 | /// 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 by | 319 | /// 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); |
| 884 | 913 | ||
| 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); |
| 889 | 918 | ||
| ... | @@ -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); |
| 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 | 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 }); |
| 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 | return type_changed; | 1191 | return type_changed; |
| 1154 | }; | 1192 | }; |
| 1155 | 1193 | ||
| ... | @@ -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 | } |
| 1844 | 1889 | ||
| 1845 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of | 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,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 | } |
| 1928 | 1976 | ||
| 1929 | fn allocateNewDecl( | 1977 | fn 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"); |
| 9 | const Inst = @import("../ir.zig").Inst; | 9 | const Inst = @import("../ir.zig").Inst; |
| 10 | const Value = @import("../value.zig").Value; | 10 | const Value = @import("../value.zig").Value; |
| 11 | const Type = @import("../type.zig").Type; | 11 | const Type = @import("../type.zig").Type; |
| 12 | const TypedValue = @import("../TypedValue.zig"); | ||
| 12 | const C = link.File.C; | 13 | const C = link.File.C; |
| 13 | const Decl = Module.Decl; | 14 | const Decl = Module.Decl; |
| 14 | const trace = @import("../tracy.zig").trace; | 15 | const trace = @import("../tracy.zig").trace; |
| ... | @@ -109,7 +110,7 @@ pub const Object = struct { | ... | @@ -109,7 +110,7 @@ pub const Object = struct { |
| 109 | }; | 110 | }; |
| 110 | 111 | ||
| 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. |
| 112 | const DeclGen = struct { | 113 | pub 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 | } |
| 201 | 202 | ||
| 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 | }; |
| 306 | 307 | ||
| 307 | pub fn genDecl(o: *Object) !void { | 308 | pub 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; |
| 312 | 313 | ||
| 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"); |
| 317 | 322 | ||
| 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 | } |
| 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 | const tracy = trace(@src()); | 391 | const tracy = trace(@src()); |
| 386 | defer tracy.end(); | 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 | .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(); |
| 132 | 132 | ||
| 133 | const module = self.base.options.module orelse | 133 | 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. | ||
| 135 | 137 | ||
| 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 | } |
| 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 | pub fn updateDeclExports( | 232 | pub 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 | #endif | 23 | #endif |
| 24 | 24 | ||
| 25 | #if __STDC_VERSION__ >= 199901L | 25 | #if __STDC_VERSION__ >= 199901L |
| 26 | #define zig_restrict restrict | 26 | #define ZIG_RESTRICT restrict |
| 27 | #elif defined(__GNUC__) | 27 | #elif defined(__GNUC__) |
| 28 | #define zig_restrict __restrict | 28 | #define ZIG_RESTRICT __restrict |
| 29 | #else | 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 | #endif | 37 | #endif |
| 32 | 38 | ||
| 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 __int128 | 55 | #define int128_t __int128 |
| 50 | #define uint128_t unsigned __int128 | 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,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 | } |