authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-05 17:42:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-05 17:42:16-07:00
log38572ee89492d8499a5881d12c401d9bb0925167
tree2ff6f00b6545697c386740032f9a32d124cfb438
parent6c4924408b1957d493568271a0158b1190574dd0
parent3e39d0c44fbf0cfb56ef0c00fc8ec7c0d5e1c1ec

Merge branch 'stage2-rework-cbe'

Reworks the C backend and -femit-h to properly participate in incremental compilation. closes #7602

12 files changed, 1044 insertions(+), 903 deletions(-)

CMakeLists.txt+1-1
...@@ -559,7 +559,7 @@ set(ZIG_STAGE2_SOURCES...@@ -559,7 +559,7 @@ set(ZIG_STAGE2_SOURCES
559 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"559 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
560 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"560 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
561 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"561 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
562 "${CMAKE_SOURCE_DIR}/src/link/cbe.h"562 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
563 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"563 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
564 "${CMAKE_SOURCE_DIR}/src/liveness.zig"564 "${CMAKE_SOURCE_DIR}/src/liveness.zig"
565 "${CMAKE_SOURCE_DIR}/src/llvm_backend.zig"565 "${CMAKE_SOURCE_DIR}/src/llvm_backend.zig"
lib/std/array_list.zig+12-10
...@@ -100,10 +100,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -100,10 +100,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
100100
101 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields101 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
102 /// of this ArrayList. This ArrayList retains ownership of underlying memory.102 /// of this ArrayList. This ArrayList retains ownership of underlying memory.
103 /// Deprecated: use `moveToUnmanaged` which has different semantics.
103 pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) {104 pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) {
104 return .{ .items = self.items, .capacity = self.capacity };105 return .{ .items = self.items, .capacity = self.capacity };
105 }106 }
106107
108 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
109 /// of this ArrayList. Empties this ArrayList.
110 pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {
111 const allocator = self.allocator;
112 const result = .{ .items = self.items, .capacity = self.capacity };
113 self.* = init(allocator);
114 return result;
115 }
116
107 /// The caller owns the returned memory. Empties this ArrayList.117 /// The caller owns the returned memory. Empties this ArrayList.
108 pub fn toOwnedSlice(self: *Self) Slice {118 pub fn toOwnedSlice(self: *Self) Slice {
109 const allocator = self.allocator;119 const allocator = self.allocator;
...@@ -551,14 +561,6 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -551,14 +561,6 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
551 mem.copy(T, self.items[oldlen..], items);561 mem.copy(T, self.items[oldlen..], items);
552 }562 }
553563
554 /// Same as `append` except it returns the number of bytes written, which is always the same
555 /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
556 /// This function may be called only when `T` is `u8`.
557 fn appendWrite(self: *Self, allocator: *Allocator, m: []const u8) !usize {
558 try self.appendSlice(allocator, m);
559 return m.len;
560 }
561
562 /// Append a value to the list `n` times.564 /// Append a value to the list `n` times.
563 /// Allocates more memory as necessary.565 /// Allocates more memory as necessary.
564 pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void {566 pub fn appendNTimes(self: *Self, allocator: *Allocator, value: T, n: usize) !void {
...@@ -1129,13 +1131,13 @@ test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" {...@@ -1129,13 +1131,13 @@ test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" {
1129 }1131 }
1130}1132}
11311133
1132test "std.ArrayList(u8) implements outStream" {1134test "std.ArrayList(u8) implements writer" {
1133 var buffer = ArrayList(u8).init(std.testing.allocator);1135 var buffer = ArrayList(u8).init(std.testing.allocator);
1134 defer buffer.deinit();1136 defer buffer.deinit();
11351137
1136 const x: i32 = 42;1138 const x: i32 = 42;
1137 const y: i32 = 1234;1139 const y: i32 = 1234;
1138 try buffer.outStream().print("x: {}\ny: {}\n", .{ x, y });1140 try buffer.writer().print("x: {}\ny: {}\n", .{ x, y });
11391141
1140 testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items);1142 testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items);
1141}1143}
lib/std/fs/file.zig+8
...@@ -459,6 +459,7 @@ pub const File = struct {...@@ -459,6 +459,7 @@ pub const File = struct {
459 return index;459 return index;
460 }460 }
461461
462 /// See https://github.com/ziglang/zig/issues/7699
462 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {463 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
463 if (is_windows) {464 if (is_windows) {
464 // TODO improve this to use ReadFileScatter465 // TODO improve this to use ReadFileScatter
...@@ -479,6 +480,7 @@ pub const File = struct {...@@ -479,6 +480,7 @@ pub const File = struct {
479 /// is not an error condition.480 /// is not an error condition.
480 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in481 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
481 /// order to handle partial reads from the underlying OS layer.482 /// order to handle partial reads from the underlying OS layer.
483 /// See https://github.com/ziglang/zig/issues/7699
482 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {484 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
483 if (iovecs.len == 0) return;485 if (iovecs.len == 0) return;
484486
...@@ -500,6 +502,7 @@ pub const File = struct {...@@ -500,6 +502,7 @@ pub const File = struct {
500 }502 }
501 }503 }
502504
505 /// See https://github.com/ziglang/zig/issues/7699
503 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {506 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {
504 if (is_windows) {507 if (is_windows) {
505 // TODO improve this to use ReadFileScatter508 // TODO improve this to use ReadFileScatter
...@@ -520,6 +523,7 @@ pub const File = struct {...@@ -520,6 +523,7 @@ pub const File = struct {
520 /// is not an error condition.523 /// is not an error condition.
521 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in524 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
522 /// order to handle partial reads from the underlying OS layer.525 /// order to handle partial reads from the underlying OS layer.
526 /// See https://github.com/ziglang/zig/issues/7699
523 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {527 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
524 if (iovecs.len == 0) return;528 if (iovecs.len == 0) return;
525529
...@@ -582,6 +586,7 @@ pub const File = struct {...@@ -582,6 +586,7 @@ pub const File = struct {
582 }586 }
583 }587 }
584588
589 /// See https://github.com/ziglang/zig/issues/7699
585 pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {590 pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {
586 if (is_windows) {591 if (is_windows) {
587 // TODO improve this to use WriteFileScatter592 // TODO improve this to use WriteFileScatter
...@@ -599,6 +604,7 @@ pub const File = struct {...@@ -599,6 +604,7 @@ pub const File = struct {
599604
600 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in605 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
601 /// order to handle partial writes from the underlying OS layer.606 /// order to handle partial writes from the underlying OS layer.
607 /// See https://github.com/ziglang/zig/issues/7699
602 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {608 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
603 if (iovecs.len == 0) return;609 if (iovecs.len == 0) return;
604610
...@@ -615,6 +621,7 @@ pub const File = struct {...@@ -615,6 +621,7 @@ pub const File = struct {
615 }621 }
616 }622 }
617623
624 /// See https://github.com/ziglang/zig/issues/7699
618 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {625 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {
619 if (is_windows) {626 if (is_windows) {
620 // TODO improve this to use WriteFileScatter627 // TODO improve this to use WriteFileScatter
...@@ -632,6 +639,7 @@ pub const File = struct {...@@ -632,6 +639,7 @@ pub const File = struct {
632639
633 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in640 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
634 /// order to handle partial writes from the underlying OS layer.641 /// order to handle partial writes from the underlying OS layer.
642 /// See https://github.com/ziglang/zig/issues/7699
635 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {643 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {
636 if (iovecs.len == 0) return;644 if (iovecs.len == 0) return;
637645
src/Compilation.zig+84-66
...@@ -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");
...@@ -138,8 +137,6 @@ emit_llvm_ir: ?EmitLoc,...@@ -138,8 +137,6 @@ emit_llvm_ir: ?EmitLoc,
138emit_analysis: ?EmitLoc,137emit_analysis: ?EmitLoc,
139emit_docs: ?EmitLoc,138emit_docs: ?EmitLoc,
140139
141c_header: ?c_link.Header,
142
143work_queue_wait_group: WaitGroup,140work_queue_wait_group: WaitGroup,
144141
145pub const InnerError = Module.InnerError;142pub const InnerError = Module.InnerError;
...@@ -164,6 +161,8 @@ pub const CSourceFile = struct {...@@ -164,6 +161,8 @@ pub const CSourceFile = struct {
164const Job = union(enum) {161const Job = union(enum) {
165 /// Write the machine code for a Decl to the output file.162 /// Write the machine code for a Decl to the output file.
166 codegen_decl: *Module.Decl,163 codegen_decl: *Module.Decl,
164 /// Render the .h file snippet for the Decl.
165 emit_h_decl: *Module.Decl,
167 /// The Decl needs to be analyzed and possibly export itself.166 /// The Decl needs to be analyzed and possibly export itself.
168 /// It may have already be analyzed, or it may have been determined167 /// It may have already be analyzed, or it may have been determined
169 /// to be outdated; in this case perform semantic analysis again.168 /// to be outdated; in this case perform semantic analysis again.
...@@ -866,9 +865,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -866,9 +865,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
866 .root_pkg = root_pkg,865 .root_pkg = root_pkg,
867 .root_scope = root_scope,866 .root_scope = root_scope,
868 .zig_cache_artifact_directory = zig_cache_artifact_directory,867 .zig_cache_artifact_directory = zig_cache_artifact_directory,
868 .emit_h = options.emit_h,
869 };869 };
870 break :blk module;870 break :blk module;
871 } else null;871 } else blk: {
872 if (options.emit_h != null) return error.NoZigModuleForCHeader;
873 break :blk null;
874 };
872 errdefer if (module) |zm| zm.deinit();875 errdefer if (module) |zm| zm.deinit();
873876
874 const error_return_tracing = !strip and switch (options.optimize_mode) {877 const error_return_tracing = !strip and switch (options.optimize_mode) {
...@@ -996,7 +999,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -996,7 +999,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
996 .local_cache_directory = options.local_cache_directory,999 .local_cache_directory = options.local_cache_directory,
997 .global_cache_directory = options.global_cache_directory,1000 .global_cache_directory = options.global_cache_directory,
998 .bin_file = bin_file,1001 .bin_file = bin_file,
999 .c_header = if (!use_llvm and options.emit_h != null) c_link.Header.init(gpa, options.emit_h) else null,
1000 .emit_asm = options.emit_asm,1002 .emit_asm = options.emit_asm,
1001 .emit_llvm_ir = options.emit_llvm_ir,1003 .emit_llvm_ir = options.emit_llvm_ir,
1002 .emit_analysis = options.emit_analysis,1004 .emit_analysis = options.emit_analysis,
...@@ -1218,10 +1220,6 @@ pub fn destroy(self: *Compilation) void {...@@ -1218,10 +1220,6 @@ pub fn destroy(self: *Compilation) void {
1218 }1220 }
1219 self.failed_c_objects.deinit(gpa);1221 self.failed_c_objects.deinit(gpa);
12201222
1221 if (self.c_header) |*header| {
1222 header.deinit();
1223 }
1224
1225 self.cache_parent.manifest_dir.close();1223 self.cache_parent.manifest_dir.close();
1226 if (self.owned_link_dir) |*dir| dir.close();1224 if (self.owned_link_dir) |*dir| dir.close();
12271225
...@@ -1315,9 +1313,14 @@ pub fn update(self: *Compilation) !void {...@@ -1315,9 +1313,14 @@ pub fn update(self: *Compilation) !void {
13151313
1316 // This is needed before reading the error flags.1314 // This is needed before reading the error flags.
1317 try self.bin_file.flush(self);1315 try self.bin_file.flush(self);
1318
1319 self.link_error_flags = self.bin_file.errorFlags();1316 self.link_error_flags = self.bin_file.errorFlags();
13201317
1318 if (!use_stage1) {
1319 if (self.bin_file.options.module) |module| {
1320 try link.File.C.flushEmitH(module);
1321 }
1322 }
1323
1321 // If there are any errors, we anticipate the source files being loaded1324 // If there are any errors, we anticipate the source files being loaded
1322 // 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.
1323 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {1326 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
...@@ -1325,20 +1328,6 @@ pub fn update(self: *Compilation) !void {...@@ -1325,20 +1328,6 @@ pub fn update(self: *Compilation) !void {
1325 module.root_scope.unload(self.gpa);1328 module.root_scope.unload(self.gpa);
1326 }1329 }
1327 }1330 }
1328
1329 // If we've chosen to emit a C header, flush the header to the disk.
1330 if (self.c_header) |header| {
1331 const header_path = header.emit_loc.?;
1332 // If a directory has been provided, write the header there. Otherwise, just write it to the
1333 // cache directory.
1334 const header_dir = if (header_path.directory) |dir|
1335 dir.handle
1336 else
1337 self.local_cache_directory.handle;
1338 const header_file = try header_dir.createFile(header_path.basename, .{});
1339 defer header_file.close();
1340 try header.flush(header_file.writer());
1341 }
1342}1331}
13431332
1344/// Having the file open for writing is problematic as far as executing the1333/// Having the file open for writing is problematic as far as executing the
...@@ -1357,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize {...@@ -1357,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize {
1357 var total: usize = self.failed_c_objects.items().len;1346 var total: usize = self.failed_c_objects.items().len;
13581347
1359 if (self.bin_file.options.module) |module| {1348 if (self.bin_file.options.module) |module| {
1360 total += module.failed_decls.items().len +1349 total += module.failed_decls.count() +
1350 module.emit_h_failed_decls.count() +
1361 module.failed_exports.items().len +1351 module.failed_exports.items().len +
1362 module.failed_files.items().len +1352 module.failed_files.items().len +
1363 @boolToInt(module.failed_root_src_file != null);1353 @boolToInt(module.failed_root_src_file != null);
...@@ -1396,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -1396,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
1396 const source = try decl.scope.getSource(module);1386 const source = try decl.scope.getSource(module);
1397 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);1387 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
1398 }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 }
1399 for (module.failed_exports.items()) |entry| {1395 for (module.failed_exports.items()) |entry| {
1400 const decl = entry.key.owner_decl;1396 const decl = entry.key.owner_decl;
1401 const err_msg = entry.value;1397 const err_msg = entry.value;
...@@ -1493,44 +1489,66 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1493,44 +1489,66 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14931489
1494 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());1490 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
14951491
1496 self.bin_file.updateDecl(module, decl) catch |err| {1492 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
1497 switch (err) {1493 error.OutOfMemory => return error.OutOfMemory,
1498 error.OutOfMemory => return error.OutOfMemory,1494 error.AnalysisFail => {
1499 error.AnalysisFail => {1495 decl.analysis = .codegen_failure;
1500 decl.analysis = .dependency_failure;1496 continue;
1501 },1497 },
1502 else => {1498 else => {
1503 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);
1504 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1500 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1505 module.gpa,1501 module.gpa,
1506 decl.src(),1502 decl.src(),
1507 "unable to codegen: {s}",1503 "unable to codegen: {s}",
1508 .{@errorName(err)},1504 .{@errorName(err)},
1509 ));1505 ));
1510 decl.analysis = .codegen_failure_retryable;1506 decl.analysis = .codegen_failure_retryable;
1511 },1507 continue;
1512 }1508 },
1513 return;
1514 };1509 };
1510 },
1511 },
1512 .emit_h_decl => |decl| switch (decl.analysis) {
1513 .unreferenced => unreachable,
1514 .in_progress => unreachable,
1515 .outdated => unreachable,
15151516
1516 if (self.c_header) |*header| {1517 .sema_failure,
1517 c_codegen.generateHeader(self, module, header, decl) catch |err| switch (err) {1518 .dependency_failure,
1518 error.OutOfMemory => return error.OutOfMemory,1519 .sema_failure_retryable,
1519 error.AnalysisFail => {1520 => continue,
1520 decl.analysis = .dependency_failure;1521
1521 },1522 // emit-h only requires semantic analysis of the Decl to be complete,
1522 else => {1523 // it does not depend on machine code generation to succeed.
1523 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);1524 .codegen_failure, .codegen_failure_retryable, .complete => {
1524 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1525 if (build_options.omit_stage2)
1525 module.gpa,1526 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
1526 decl.src(),1527 const module = self.bin_file.options.module.?;
1527 "unable to generate C header: {s}",1528 const emit_loc = module.emit_h.?;
1528 .{@errorName(err)},1529 const tv = decl.typed_value.most_recent.typed_value;
1529 ));1530 const emit_h = decl.getEmitH(module);
1530 decl.analysis = .codegen_failure_retryable;1531 const fwd_decl = &emit_h.fwd_decl;
1531 },1532 fwd_decl.shrinkRetainingCapacity(0);
1532 };1533
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);
1534 },1552 },
1535 },1553 },
1536 .analyze_decl => |decl| {1554 .analyze_decl => |decl| {
...@@ -2998,9 +3016,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -2998,9 +3016,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
2998 man.hash.add(comp.bin_file.options.function_sections);3016 man.hash.add(comp.bin_file.options.function_sections);
2999 man.hash.add(comp.bin_file.options.is_test);3017 man.hash.add(comp.bin_file.options.is_test);
3000 man.hash.add(comp.bin_file.options.emit != null);3018 man.hash.add(comp.bin_file.options.emit != null);
3001 man.hash.add(comp.c_header != null);3019 man.hash.add(mod.emit_h != null);
3002 if (comp.c_header) |header| {3020 if (mod.emit_h) |emit_h| {
3003 man.hash.addEmitLoc(header.emit_loc.?);3021 man.hash.addEmitLoc(emit_h);
3004 }3022 }
3005 man.hash.addOptionalEmitLoc(comp.emit_asm);3023 man.hash.addOptionalEmitLoc(comp.emit_asm);
3006 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);3024 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
...@@ -3105,10 +3123,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3105,10 +3123,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3105 });3123 });
3106 break :blk try directory.join(arena, &[_][]const u8{bin_basename});3124 break :blk try directory.join(arena, &[_][]const u8{bin_basename});
3107 } else "";3125 } else "";
3108 if (comp.c_header != null) {3126 if (mod.emit_h != null) {
3109 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});3127 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
3110 }3128 }
3111 const emit_h_path = try stage1LocPath(arena, if (comp.c_header) |header| header.emit_loc else null, directory);3129 const emit_h_path = try stage1LocPath(arena, mod.emit_h, directory);
3112 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);3130 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
3113 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);3131 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
3114 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);3132 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
src/Module.zig+69-10
...@@ -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) = .{},
...@@ -94,6 +98,8 @@ stage1_flags: packed struct {...@@ -94,6 +98,8 @@ stage1_flags: packed struct {
94 reserved: u2 = 0,98 reserved: u2 = 0,
95} = .{},99} = .{},
96100
101emit_h: ?Compilation.EmitLoc,
102
97pub const Export = struct {103pub const Export = struct {
98 options: std.builtin.ExportOptions,104 options: std.builtin.ExportOptions,
99 /// Byte offset into the file that contains the export directive.105 /// Byte offset into the file that contains the export directive.
...@@ -114,6 +120,13 @@ pub const Export = struct {...@@ -114,6 +120,13 @@ pub const Export = struct {
114 },120 },
115};121};
116122
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
117pub const Decl = struct {130pub const Decl = struct {
118 /// 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
119 /// 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
...@@ -202,14 +215,21 @@ pub const Decl = struct {...@@ -202,14 +215,21 @@ pub const Decl = struct {
202 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`215 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
203 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);
204217
205 pub fn destroy(self: *Decl, gpa: *Allocator) void {218 pub fn destroy(self: *Decl, module: *Module) void {
219 const gpa = module.gpa;
206 gpa.free(mem.spanZ(self.name));220 gpa.free(mem.spanZ(self.name));
207 if (self.typedValueManaged()) |tvm| {221 if (self.typedValueManaged()) |tvm| {
208 tvm.deinit(gpa);222 tvm.deinit(gpa);
209 }223 }
210 self.dependants.deinit(gpa);224 self.dependants.deinit(gpa);
211 self.dependencies.deinit(gpa);225 self.dependencies.deinit(gpa);
212 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 }
213 }233 }
214234
215 pub fn src(self: Decl) usize {235 pub fn src(self: Decl) usize {
...@@ -275,6 +295,12 @@ pub const Decl = struct {...@@ -275,6 +295,12 @@ pub const Decl = struct {
275 return self.scope.cast(Scope.Container).?.file_scope;295 return self.scope.cast(Scope.Container).?.file_scope;
276 }296 }
277297
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
278 fn removeDependant(self: *Decl, other: *Decl) void {304 fn removeDependant(self: *Decl, other: *Decl) void {
279 self.dependants.removeAssertDiscard(other);305 self.dependants.removeAssertDiscard(other);
280 }306 }
...@@ -284,6 +310,11 @@ pub const Decl = struct {...@@ -284,6 +310,11 @@ pub const Decl = struct {
284 }310 }
285};311};
286312
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
287/// 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.
288/// Extern functions do not have this data structure; they are represented by319/// Extern functions do not have this data structure; they are represented by
289/// the `Decl` only, with a `Value` tag of `extern_fn`.320/// the `Decl` only, with a `Value` tag of `extern_fn`.
...@@ -881,7 +912,7 @@ pub fn deinit(self: *Module) void {...@@ -881,7 +912,7 @@ pub fn deinit(self: *Module) void {
881 self.deletion_set.deinit(gpa);912 self.deletion_set.deinit(gpa);
882913
883 for (self.decl_table.items()) |entry| {914 for (self.decl_table.items()) |entry| {
884 entry.value.destroy(gpa);915 entry.value.destroy(self);
885 }916 }
886 self.decl_table.deinit(gpa);917 self.decl_table.deinit(gpa);
887918
...@@ -890,6 +921,11 @@ pub fn deinit(self: *Module) void {...@@ -890,6 +921,11 @@ pub fn deinit(self: *Module) void {
890 }921 }
891 self.failed_decls.deinit(gpa);922 self.failed_decls.deinit(gpa);
892923
924 for (self.emit_h_failed_decls.items()) |entry| {
925 entry.value.destroy(gpa);
926 }
927 self.emit_h_failed_decls.deinit(gpa);
928
893 for (self.failed_files.items()) |entry| {929 for (self.failed_files.items()) |entry| {
894 entry.value.destroy(gpa);930 entry.value.destroy(gpa);
895 }931 }
...@@ -1148,6 +1184,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1148,6 +1184,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1148 try self.comp.bin_file.allocateDeclIndexes(decl);1184 try self.comp.bin_file.allocateDeclIndexes(decl);
1149 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });1185 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
11501186
1187 if (type_changed and self.emit_h != null) {
1188 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1189 }
1190
1151 return type_changed;1191 return type_changed;
1152 };1192 };
11531193
...@@ -1267,6 +1307,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1267,6 +1307,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1267 // increasing how many computations can be done in parallel.1307 // increasing how many computations can be done in parallel.
1268 try self.comp.bin_file.allocateDeclIndexes(decl);1308 try self.comp.bin_file.allocateDeclIndexes(decl);
1269 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 }
1270 } else if (!prev_is_inline and prev_type_has_bits) {1313 } else if (!prev_is_inline and prev_type_has_bits) {
1271 self.comp.bin_file.freeDecl(decl);1314 self.comp.bin_file.freeDecl(decl);
1272 }1315 }
...@@ -1835,9 +1878,13 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -1835,9 +1878,13 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
1835 if (self.failed_decls.remove(decl)) |entry| {1878 if (self.failed_decls.remove(decl)) |entry| {
1836 entry.value.destroy(self.gpa);1879 entry.value.destroy(self.gpa);
1837 }1880 }
1881 if (self.emit_h_failed_decls.remove(decl)) |entry| {
1882 entry.value.destroy(self.gpa);
1883 }
1838 self.deleteDeclExports(decl);1884 self.deleteDeclExports(decl);
1839 self.comp.bin_file.freeDecl(decl);1885 self.comp.bin_file.freeDecl(decl);
1840 decl.destroy(self.gpa);1886
1887 decl.destroy(self);
1841}1888}
18421889
1843/// 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
...@@ -1921,16 +1968,28 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {...@@ -1921,16 +1968,28 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1921 if (self.failed_decls.remove(decl)) |entry| {1968 if (self.failed_decls.remove(decl)) |entry| {
1922 entry.value.destroy(self.gpa);1969 entry.value.destroy(self.gpa);
1923 }1970 }
1971 if (self.emit_h_failed_decls.remove(decl)) |entry| {
1972 entry.value.destroy(self.gpa);
1973 }
1924 decl.analysis = .outdated;1974 decl.analysis = .outdated;
1925}1975}
19261976
1927fn allocateNewDecl(1977fn allocateNewDecl(
1928 self: *Module,1978 mod: *Module,
1929 scope: *Scope,1979 scope: *Scope,
1930 src_index: usize,1980 src_index: usize,
1931 contents_hash: std.zig.SrcHash,1981 contents_hash: std.zig.SrcHash,
1932) !*Decl {1982) !*Decl {
1933 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
1934 new_decl.* = .{1993 new_decl.* = .{
1935 .name = "",1994 .name = "",
1936 .scope = scope.namespace(),1995 .scope = scope.namespace(),
...@@ -1939,18 +1998,18 @@ fn allocateNewDecl(...@@ -1939,18 +1998,18 @@ fn allocateNewDecl(
1939 .analysis = .unreferenced,1998 .analysis = .unreferenced,
1940 .deletion_flag = false,1999 .deletion_flag = false,
1941 .contents_hash = contents_hash,2000 .contents_hash = contents_hash,
1942 .link = switch (self.comp.bin_file.tag) {2001 .link = switch (mod.comp.bin_file.tag) {
1943 .coff => .{ .coff = link.File.Coff.TextBlock.empty },2002 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
1944 .elf => .{ .elf = link.File.Elf.TextBlock.empty },2003 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
1945 .macho => .{ .macho = link.File.MachO.TextBlock.empty },2004 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1946 .c => .{ .c = {} },2005 .c => .{ .c = link.File.C.DeclBlock.empty },
1947 .wasm => .{ .wasm = {} },2006 .wasm => .{ .wasm = {} },
1948 },2007 },
1949 .fn_link = switch (self.comp.bin_file.tag) {2008 .fn_link = switch (mod.comp.bin_file.tag) {
1950 .coff => .{ .coff = {} },2009 .coff => .{ .coff = {} },
1951 .elf => .{ .elf = link.File.Elf.SrcFn.empty },2010 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
1952 .macho => .{ .macho = link.File.MachO.SrcFn.empty },2011 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
1953 .c => .{ .c = {} },2012 .c => .{ .c = link.File.C.FnBlock.empty },
1954 .wasm => .{ .wasm = null },2013 .wasm => .{ .wasm = null },
1955 },2014 },
1956 .generation = 0,2015 .generation = 0,
src/codegen/c.zig+504-456
...@@ -1,495 +1,532 @@...@@ -1,495 +1,532 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;
3const log = std.log.scoped(.c);
4const Writer = std.ArrayList(u8).Writer;
25
3const link = @import("../link.zig");6const link = @import("../link.zig");
4const Module = @import("../Module.zig");7const Module = @import("../Module.zig");
5const Compilation = @import("../Compilation.zig");8const Compilation = @import("../Compilation.zig");
6
7const Inst = @import("../ir.zig").Inst;9const Inst = @import("../ir.zig").Inst;
8const Value = @import("../value.zig").Value;10const Value = @import("../value.zig").Value;
9const Type = @import("../type.zig").Type;11const Type = @import("../type.zig").Type;
1012const TypedValue = @import("../TypedValue.zig");
11const C = link.File.C;13const C = link.File.C;
12const Decl = Module.Decl;14const Decl = Module.Decl;
13const mem = std.mem;15const trace = @import("../tracy.zig").trace;
14const log = std.log.scoped(.c);
1516
16const Writer = std.ArrayList(u8).Writer;17const Mutability = enum { Const, Mut };
1718
18/// Maps a name from Zig source to C. Currently, this will always give the same19pub const CValue = union(enum) {
19/// output for any given input, sometimes resulting in broken identifiers.20 none: void,
20fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {21 /// Index into local_names
21 return allocator.dupe(u8, name);22 local: usize,
22}23 /// Index into local_names, but take the address.
24 local_ref: usize,
25 /// A constant instruction, to be rendered inline.
26 constant: *Inst,
27 /// Index into the parameters
28 arg: usize,
29 /// By-value
30 decl: *Decl,
31};
2332
24const Mutability = enum { Const, Mut };33pub const CValueMap = std.AutoHashMap(*Inst, CValue);
34
35/// This data is available when outputting .c code for a Module.
36/// It is not available when generating .h file.
37pub const Object = struct {
38 dg: DeclGen,
39 gpa: *mem.Allocator,
40 code: std.ArrayList(u8),
41 value_map: CValueMap,
42 next_arg_index: usize = 0,
43 next_local_index: usize = 0,
44
45 fn resolveInst(o: *Object, inst: *Inst) !CValue {
46 if (inst.value()) |_| {
47 return CValue{ .constant = inst };
48 }
49 return o.value_map.get(inst).?; // Instruction does not dominate all uses!
50 }
2551
26fn renderTypeAndName(52 fn allocLocalValue(o: *Object) CValue {
27 ctx: *Context,53 const result = o.next_local_index;
28 writer: Writer,54 o.next_local_index += 1;
29 ty: Type,55 return .{ .local = result };
30 name: []const u8,
31 mutability: Mutability,
32) error{ OutOfMemory, AnalysisFail }!void {
33 var suffix = std.ArrayList(u8).init(&ctx.arena.allocator);
34
35 var render_ty = ty;
36 while (render_ty.zigTypeTag() == .Array) {
37 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
38 const c_len = render_ty.arrayLen() + sentinel_bit;
39 try suffix.writer().print("[{d}]", .{c_len});
40 render_ty = render_ty.elemType();
41 }56 }
4257
43 try renderType(ctx, writer, render_ty);58 fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue {
59 const local_value = o.allocLocalValue();
60 try o.renderTypeAndName(o.code.writer(), ty, local_value, mutability);
61 return local_value;
62 }
4463
45 const const_prefix = switch (mutability) {64 fn indent(o: *Object) !void {
46 .Const => "const ",65 const indent_size = 4;
47 .Mut => "",66 const indent_level = 1;
48 };67 const indent_amt = indent_size * indent_level;
49 try writer.print(" {s}{s}{s}", .{ const_prefix, name, suffix.items });68 try o.code.writer().writeByteNTimes(' ', indent_amt);
50}69 }
5170
52fn renderType(71 fn writeCValue(o: *Object, writer: Writer, c_value: CValue) !void {
53 ctx: *Context,72 switch (c_value) {
54 writer: Writer,73 .none => unreachable,
55 t: Type,74 .local => |i| return writer.print("t{d}", .{i}),
56) error{ OutOfMemory, AnalysisFail }!void {75 .local_ref => |i| return writer.print("&t{d}", .{i}),
57 switch (t.zigTypeTag()) {76 .constant => |inst| return o.dg.renderValue(writer, inst.ty, inst.value().?),
58 .NoReturn => {77 .arg => |i| return writer.print("a{d}", .{i}),
59 try writer.writeAll("zig_noreturn void");78 .decl => |decl| return writer.writeAll(mem.span(decl.name)),
60 },79 }
61 .Void => try writer.writeAll("void"),80 }
62 .Bool => try writer.writeAll("bool"),81
63 .Int => {82 fn renderTypeAndName(
64 switch (t.tag()) {83 o: *Object,
65 .u8 => try writer.writeAll("uint8_t"),84 writer: Writer,
66 .i8 => try writer.writeAll("int8_t"),85 ty: Type,
67 .u16 => try writer.writeAll("uint16_t"),86 name: CValue,
68 .i16 => try writer.writeAll("int16_t"),87 mutability: Mutability,
69 .u32 => try writer.writeAll("uint32_t"),88 ) error{ OutOfMemory, AnalysisFail }!void {
70 .i32 => try writer.writeAll("int32_t"),89 var suffix = std.ArrayList(u8).init(o.gpa);
71 .u64 => try writer.writeAll("uint64_t"),90 defer suffix.deinit();
72 .i64 => try writer.writeAll("int64_t"),91
73 .usize => try writer.writeAll("uintptr_t"),92 var render_ty = ty;
74 .isize => try writer.writeAll("intptr_t"),93 while (render_ty.zigTypeTag() == .Array) {
75 .c_short => try writer.writeAll("short"),94 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
76 .c_ushort => try writer.writeAll("unsigned short"),95 const c_len = render_ty.arrayLen() + sentinel_bit;
77 .c_int => try writer.writeAll("int"),96 try suffix.writer().print("[{d}]", .{c_len});
78 .c_uint => try writer.writeAll("unsigned int"),97 render_ty = render_ty.elemType();
79 .c_long => try writer.writeAll("long"),98 }
80 .c_ulong => try writer.writeAll("unsigned long"),99
81 .c_longlong => try writer.writeAll("long long"),100 try o.dg.renderType(writer, render_ty);
82 .c_ulonglong => try writer.writeAll("unsigned long long"),101
83 .int_signed, .int_unsigned => {102 const const_prefix = switch (mutability) {
84 const info = t.intInfo(ctx.target);103 .Const => "const ",
85 const sign_prefix = switch (info.signedness) {104 .Mut => "",
86 .signed => "i",105 };
87 .unsigned => "",106 try writer.print(" {s}", .{const_prefix});
88 };107 try o.writeCValue(writer, name);
89 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {108 try writer.writeAll(suffix.items);
90 if (info.bits <= nbits) {109 }
91 try writer.print("{s}int{d}_t", .{ sign_prefix, nbits });110};
92 break;111
93 }112/// This data is available both when outputting .c code and when outputting an .h file.
113pub const DeclGen = struct {
114 module: *Module,
115 decl: *Decl,
116 fwd_decl: std.ArrayList(u8),
117 error_msg: ?*Compilation.ErrorMsg,
118
119 fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
120 dg.error_msg = try Compilation.ErrorMsg.create(dg.module.gpa, src, format, args);
121 return error.AnalysisFail;
122 }
123
124 fn renderValue(
125 dg: *DeclGen,
126 writer: Writer,
127 t: Type,
128 val: Value,
129 ) error{ OutOfMemory, AnalysisFail }!void {
130 switch (t.zigTypeTag()) {
131 .Int => {
132 if (t.isSignedInt())
133 return writer.print("{d}", .{val.toSignedInt()});
134 return writer.print("{d}", .{val.toUnsignedInt()});
135 },
136 .Pointer => switch (val.tag()) {
137 .undef, .zero => try writer.writeAll("0"),
138 .one => try writer.writeAll("1"),
139 .decl_ref => {
140 const decl = val.castTag(.decl_ref).?.data;
141
142 // Determine if we must pointer cast.
143 const decl_tv = decl.typed_value.most_recent.typed_value;
144 if (t.eql(decl_tv.ty)) {
145 try writer.print("&{s}", .{decl.name});
94 } else {146 } else {
95 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});147 try writer.writeAll("(");
148 try dg.renderType(writer, t);
149 try writer.print(")&{s}", .{decl.name});
96 }150 }
97 },151 },
98 else => unreachable,152 .function => {
99 }153 const func = val.castTag(.function).?.data;
100 },154 try writer.print("{s}", .{func.owner_decl.name});
101 .Pointer => {155 },
102 if (t.isSlice()) {156 .extern_fn => {
103 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});157 const decl = val.castTag(.extern_fn).?.data;
104 } else {158 try writer.print("{s}", .{decl.name});
105 try renderType(ctx, writer, t.elemType());159 },
106 try writer.writeAll(" *");160 else => |e| return dg.fail(
107 if (t.isConstPtr()) {161 dg.decl.src(),
108 try writer.writeAll("const ");162 "TODO: C backend: implement Pointer value {s}",
163 .{@tagName(e)},
164 ),
165 },
166 .Array => {
167 // First try specific tag representations for more efficiency.
168 switch (val.tag()) {
169 .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
170 .bytes => {
171 const bytes = val.castTag(.bytes).?.data;
172 // TODO: make our own C string escape instead of using {Z}
173 try writer.print("\"{Z}\"", .{bytes});
174 },
175 else => {
176 // Fall back to generic implementation.
177 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
178 defer arena.deinit();
179
180 try writer.writeAll("{");
181 var index: usize = 0;
182 const len = t.arrayLen();
183 const elem_ty = t.elemType();
184 while (index < len) : (index += 1) {
185 if (index != 0) try writer.writeAll(",");
186 const elem_val = try val.elemValue(&arena.allocator, index);
187 try dg.renderValue(writer, elem_ty, elem_val);
188 }
189 if (t.sentinel()) |sentinel_val| {
190 if (index != 0) try writer.writeAll(",");
191 try dg.renderValue(writer, elem_ty, sentinel_val);
192 }
193 try writer.writeAll("}");
194 },
109 }195 }
110 if (t.isVolatilePtr()) {196 },
111 try writer.writeAll("volatile ");197 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
198 @tagName(e),
199 }),
200 }
201 }
202
203 fn renderFunctionSignature(dg: *DeclGen, w: Writer, is_global: bool) !void {
204 if (!is_global) {
205 try w.writeAll("static ");
206 }
207 const tv = dg.decl.typed_value.most_recent.typed_value;
208 try dg.renderType(w, tv.ty.fnReturnType());
209 const decl_name = mem.span(dg.decl.name);
210 try w.print(" {s}(", .{decl_name});
211 var param_len = tv.ty.fnParamLen();
212 if (param_len == 0)
213 try w.writeAll("void")
214 else {
215 var index: usize = 0;
216 while (index < param_len) : (index += 1) {
217 if (index > 0) {
218 try w.writeAll(", ");
112 }219 }
220 try dg.renderType(w, tv.ty.fnParamType(index));
221 try w.print(" a{d}", .{index});
113 }222 }
114 },223 }
115 .Array => {224 try w.writeByte(')');
116 try renderType(ctx, writer, t.elemType());
117 try writer.writeAll(" *");
118 },
119 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement type {s}", .{
120 @tagName(e),
121 }),
122 }225 }
123}
124226
125fn renderValue(227 fn renderType(dg: *DeclGen, w: Writer, t: Type) error{ OutOfMemory, AnalysisFail }!void {
126 ctx: *Context,228 switch (t.zigTypeTag()) {
127 writer: Writer,229 .NoReturn => {
128 t: Type,230 try w.writeAll("zig_noreturn void");
129 val: Value,
130) error{ OutOfMemory, AnalysisFail }!void {
131 switch (t.zigTypeTag()) {
132 .Int => {
133 if (t.isSignedInt())
134 return writer.print("{d}", .{val.toSignedInt()});
135 return writer.print("{d}", .{val.toUnsignedInt()});
136 },
137 .Pointer => switch (val.tag()) {
138 .undef, .zero => try writer.writeAll("0"),
139 .one => try writer.writeAll("1"),
140 .decl_ref => {
141 const decl = val.castTag(.decl_ref).?.data;
142
143 // Determine if we must pointer cast.
144 const decl_tv = decl.typed_value.most_recent.typed_value;
145 if (t.eql(decl_tv.ty)) {
146 try writer.print("&{s}", .{decl.name});
147 } else {
148 try writer.writeAll("(");
149 try renderType(ctx, writer, t);
150 try writer.print(")&{s}", .{decl.name});
151 }
152 },
153 .function => {
154 const func = val.castTag(.function).?.data;
155 try writer.print("{s}", .{func.owner_decl.name});
156 },231 },
157 .extern_fn => {232 .Void => try w.writeAll("void"),
158 const decl = val.castTag(.extern_fn).?.data;233 .Bool => try w.writeAll("bool"),
159 try writer.print("{s}", .{decl.name});234 .Int => {
235 switch (t.tag()) {
236 .u8 => try w.writeAll("uint8_t"),
237 .i8 => try w.writeAll("int8_t"),
238 .u16 => try w.writeAll("uint16_t"),
239 .i16 => try w.writeAll("int16_t"),
240 .u32 => try w.writeAll("uint32_t"),
241 .i32 => try w.writeAll("int32_t"),
242 .u64 => try w.writeAll("uint64_t"),
243 .i64 => try w.writeAll("int64_t"),
244 .usize => try w.writeAll("uintptr_t"),
245 .isize => try w.writeAll("intptr_t"),
246 .c_short => try w.writeAll("short"),
247 .c_ushort => try w.writeAll("unsigned short"),
248 .c_int => try w.writeAll("int"),
249 .c_uint => try w.writeAll("unsigned int"),
250 .c_long => try w.writeAll("long"),
251 .c_ulong => try w.writeAll("unsigned long"),
252 .c_longlong => try w.writeAll("long long"),
253 .c_ulonglong => try w.writeAll("unsigned long long"),
254 .int_signed, .int_unsigned => {
255 const info = t.intInfo(dg.module.getTarget());
256 const sign_prefix = switch (info.signedness) {
257 .signed => "i",
258 .unsigned => "",
259 };
260 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
261 if (info.bits <= nbits) {
262 try w.print("{s}int{d}_t", .{ sign_prefix, nbits });
263 break;
264 }
265 } else {
266 return dg.fail(dg.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
267 }
268 },
269 else => unreachable,
270 }
160 },271 },
161 else => |e| return ctx.fail(272 .Pointer => {
162 ctx.decl.src(),273 if (t.isSlice()) {
163 "TODO: C backend: implement Pointer value {s}",274 return dg.fail(dg.decl.src(), "TODO: C backend: implement slices", .{});
164 .{@tagName(e)},275 } else {
165 ),276 try dg.renderType(w, t.elemType());
166 },277 try w.writeAll(" *");
167 .Array => {278 if (t.isConstPtr()) {
168 // First try specific tag representations for more efficiency.279 try w.writeAll("const ");
169 switch (val.tag()) {
170 .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
171 .bytes => {
172 const bytes = val.castTag(.bytes).?.data;
173 // TODO: make our own C string escape instead of using {Z}
174 try writer.print("\"{Z}\"", .{bytes});
175 },
176 else => {
177 // Fall back to generic implementation.
178 try writer.writeAll("{");
179 var index: usize = 0;
180 const len = t.arrayLen();
181 const elem_ty = t.elemType();
182 while (index < len) : (index += 1) {
183 if (index != 0) try writer.writeAll(",");
184 const elem_val = try val.elemValue(&ctx.arena.allocator, index);
185 try renderValue(ctx, writer, elem_ty, elem_val);
186 }280 }
187 if (t.sentinel()) |sentinel_val| {281 if (t.isVolatilePtr()) {
188 if (index != 0) try writer.writeAll(",");282 try w.writeAll("volatile ");
189 try renderValue(ctx, writer, elem_ty, sentinel_val);
190 }283 }
191 try writer.writeAll("}");284 }
192 },285 },
193 }286 .Array => {
194 },287 try dg.renderType(w, t.elemType());
195 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{288 try w.writeAll(" *");
196 @tagName(e),289 },
197 }),290 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
291 @tagName(e),
292 }),
293 }
198 }294 }
199}
200295
201fn renderFunctionSignature(296 fn functionIsGlobal(dg: *DeclGen, tv: TypedValue) bool {
202 ctx: *Context,
203 writer: Writer,
204 decl: *Decl,
205) !void {
206 const tv = decl.typed_value.most_recent.typed_value;
207 // Determine whether the function is globally visible.
208 const is_global = blk: {
209 switch (tv.val.tag()) {297 switch (tv.val.tag()) {
210 .extern_fn => break :blk true,298 .extern_fn => return true,
211 .function => {299 .function => {
212 const func = tv.val.castTag(.function).?.data;300 const func = tv.val.castTag(.function).?.data;
213 break :blk ctx.module.decl_exports.contains(func.owner_decl);301 return dg.module.decl_exports.contains(func.owner_decl);
214 },302 },
215 else => unreachable,303 else => unreachable,
216 }304 }
217 };
218 if (!is_global) {
219 try writer.writeAll("static ");
220 }
221 try renderType(ctx, writer, tv.ty.fnReturnType());
222 // Use the child allocator directly, as we know the name can be freed before
223 // the rest of the arena.
224 const decl_name = mem.span(decl.name);
225 const name = try map(ctx.arena.child_allocator, decl_name);
226 defer ctx.arena.child_allocator.free(name);
227 try writer.print(" {s}(", .{name});
228 var param_len = tv.ty.fnParamLen();
229 if (param_len == 0)
230 try writer.writeAll("void")
231 else {
232 var index: usize = 0;
233 while (index < param_len) : (index += 1) {
234 if (index > 0) {
235 try writer.writeAll(", ");
236 }
237 try renderType(ctx, writer, tv.ty.fnParamType(index));
238 try writer.print(" arg{d}", .{index});
239 }
240 }305 }
241 try writer.writeByte(')');306};
242}
243307
244fn indent(file: *C) !void {308pub fn genDecl(o: *Object) !void {
245 const indent_size = 4;309 const tracy = trace(@src());
246 const indent_level = 1;310 defer tracy.end();
247 const indent_amt = indent_size * indent_level;
248 try file.main.writer().writeByteNTimes(' ', indent_amt);
249}
250311
251pub fn generate(file: *C, module: *Module, decl: *Decl) !void {312 const tv = o.dg.decl.typed_value.most_recent.typed_value;
252 const tv = decl.typed_value.most_recent.typed_value;
253
254 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
255 defer arena.deinit();
256 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
257 defer inst_map.deinit();
258 var ctx = Context{
259 .decl = decl,
260 .arena = &arena,
261 .inst_map = &inst_map,
262 .target = file.base.options.target,
263 .header = &file.header,
264 .module = module,
265 };
266 defer {
267 file.error_msg = ctx.error_msg;
268 ctx.deinit();
269 }
270313
271 if (tv.val.castTag(.function)) |func_payload| {314 if (tv.val.castTag(.function)) |func_payload| {
272 const writer = file.main.writer();315 const is_global = o.dg.functionIsGlobal(tv);
273 try renderFunctionSignature(&ctx, writer, decl);316 const fwd_decl_writer = o.dg.fwd_decl.writer();
274317 if (is_global) {
275 try writer.writeAll(" {");318 try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
319 }
320 try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
321 try fwd_decl_writer.writeAll(";\n");
276322
277 const func: *Module.Fn = func_payload.data;323 const func: *Module.Fn = func_payload.data;
278 const instructions = func.body.instructions;324 const instructions = func.body.instructions;
279 if (instructions.len > 0) {325 const writer = o.code.writer();
280 try writer.writeAll("\n");326 try writer.writeAll("\n");
281 for (instructions) |inst| {327 try o.dg.renderFunctionSignature(writer, is_global);
282 if (switch (inst.tag) {328 if (instructions.len == 0) {
283 .add => try genBinOp(&ctx, file, inst.castTag(.add).?, "+"),329 try writer.writeAll(" {}\n");
284 .alloc => try genAlloc(&ctx, file, inst.castTag(.alloc).?),330 return;
285 .arg => try genArg(&ctx),331 }
286 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),332
287 .block => try genBlock(&ctx, file, inst.castTag(.block).?),333 try writer.writeAll(" {");
288 .bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),334
289 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),335 try writer.writeAll("\n");
290 .call => try genCall(&ctx, file, inst.castTag(.call).?),336 for (instructions) |inst| {
291 .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),337 const result_value = switch (inst.tag) {
292 .cmp_gt => try genBinOp(&ctx, file, inst.castTag(.cmp_gt).?, ">"),338 .add => try genBinOp(o, inst.castTag(.add).?, " + "),
293 .cmp_gte => try genBinOp(&ctx, file, inst.castTag(.cmp_gte).?, ">="),339 .alloc => try genAlloc(o, inst.castTag(.alloc).?),
294 .cmp_lt => try genBinOp(&ctx, file, inst.castTag(.cmp_lt).?, "<"),340 .arg => genArg(o),
295 .cmp_lte => try genBinOp(&ctx, file, inst.castTag(.cmp_lte).?, "<="),341 .assembly => try genAsm(o, inst.castTag(.assembly).?),
296 .cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),342 .block => try genBlock(o, inst.castTag(.block).?),
297 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),343 .bitcast => try genBitcast(o, inst.castTag(.bitcast).?),
298 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),344 .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?),
299 .load => try genLoad(&ctx, file, inst.castTag(.load).?),345 .call => try genCall(o, inst.castTag(.call).?),
300 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),346 .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "),
301 .retvoid => try genRetVoid(file),347 .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "),
302 .store => try genStore(&ctx, file, inst.castTag(.store).?),348 .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "),
303 .sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"),349 .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "),
304 .unreach => try genUnreach(file, inst.castTag(.unreach).?),350 .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "),
305 else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),351 .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "),
306 }) |name| {352 .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?),
307 try ctx.inst_map.putNoClobber(inst, name);353 .intcast => try genIntCast(o, inst.castTag(.intcast).?),
308 }354 .load => try genLoad(o, inst.castTag(.load).?),
355 .ret => try genRet(o, inst.castTag(.ret).?),
356 .retvoid => try genRetVoid(o),
357 .store => try genStore(o, inst.castTag(.store).?),
358 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
359 .unreach => try genUnreach(o, inst.castTag(.unreach).?),
360 else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
361 };
362 switch (result_value) {
363 .none => {},
364 else => try o.value_map.putNoClobber(inst, result_value),
309 }365 }
310 }366 }
311367
312 try writer.writeAll("}\n\n");368 try writer.writeAll("}\n");
313 } else if (tv.val.tag() == .extern_fn) {369 } else if (tv.val.tag() == .extern_fn) {
314 return; // handled when referenced370 const writer = o.code.writer();
371 try writer.writeAll("ZIG_EXTERN_C ");
372 try o.dg.renderFunctionSignature(writer, true);
373 try writer.writeAll(";\n");
315 } else {374 } else {
316 const writer = file.constants.writer();375 const writer = o.code.writer();
317 try writer.writeAll("static ");376 try writer.writeAll("static ");
318377
319 // TODO ask the Decl if it is const378 // TODO ask the Decl if it is const
320 // https://github.com/ziglang/zig/issues/7582379 // https://github.com/ziglang/zig/issues/7582
321380
322 try renderTypeAndName(&ctx, writer, tv.ty, mem.span(decl.name), .Mut);381 const decl_c_value: CValue = .{ .decl = o.dg.decl };
382 try o.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut);
323383
324 try writer.writeAll(" = ");384 try writer.writeAll(" = ");
325 try renderValue(&ctx, writer, tv.ty, tv.val);385 try o.dg.renderValue(writer, tv.ty, tv.val);
326 try writer.writeAll(";\n");386 try writer.writeAll(";\n");
327 }387 }
328}388}
329389
330pub fn generateHeader(390pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
331 comp: *Compilation,391 const tracy = trace(@src());
332 module: *Module,392 defer tracy.end();
333 header: *C.Header,393
334 decl: *Decl,394 const tv = dg.decl.typed_value.most_recent.typed_value;
335) error{ AnalysisFail, OutOfMemory }!void {395 const writer = dg.fwd_decl.writer();
336 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {396
397 switch (tv.ty.zigTypeTag()) {
337 .Fn => {398 .Fn => {
338 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);399 const is_global = dg.functionIsGlobal(tv);
339 defer inst_map.deinit();400 if (is_global) {
340401 try writer.writeAll("ZIG_EXTERN_C ");
341 var arena = std.heap.ArenaAllocator.init(comp.gpa);402 }
342 defer arena.deinit();403 try dg.renderFunctionSignature(writer, is_global);
343404 try dg.fwd_decl.appendSlice(";\n");
344 var ctx = Context{
345 .decl = decl,
346 .arena = &arena,
347 .inst_map = &inst_map,
348 .target = comp.getTarget(),
349 .header = header,
350 .module = module,
351 };
352 const writer = header.buf.writer();
353 renderFunctionSignature(&ctx, writer, decl) catch |err| {
354 if (err == error.AnalysisFail) {
355 try module.failed_decls.put(module.gpa, decl, ctx.error_msg);
356 }
357 return err;
358 };
359 try writer.writeAll(";\n");
360 },405 },
361 else => {},406 else => {},
362 }407 }
363}408}
364409
365const Context = struct {410fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {
366 decl: *Decl,411 const writer = o.code.writer();
367 inst_map: *std.AutoHashMap(*Inst, []u8),
368 arena: *std.heap.ArenaAllocator,
369 argdex: usize = 0,
370 unnamed_index: usize = 0,
371 error_msg: *Compilation.ErrorMsg = undefined,
372 target: std.Target,
373 header: *C.Header,
374 module: *Module,
375
376 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
377 if (inst.value()) |val| {
378 var out = std.ArrayList(u8).init(&self.arena.allocator);
379 try renderValue(self, out.writer(), inst.ty, val);
380 return out.toOwnedSlice();
381 }
382 return self.inst_map.get(inst).?; // Instruction does not dominate all uses!
383 }
384
385 fn name(self: *Context) ![]u8 {
386 const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{d}", .{self.unnamed_index});
387 self.unnamed_index += 1;
388 return val;
389 }
390
391 fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
392 self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args);
393 return error.AnalysisFail;
394 }
395
396 fn deinit(self: *Context) void {
397 self.* = undefined;
398 }
399};
400
401fn genAlloc(ctx: *Context, file: *C, alloc: *Inst.NoOp) !?[]u8 {
402 const writer = file.main.writer();
403412
404 // First line: the variable used as data storage.413 // First line: the variable used as data storage.
405 try indent(file);414 try o.indent();
406 const local_name = try ctx.name();
407 const elem_type = alloc.base.ty.elemType();415 const elem_type = alloc.base.ty.elemType();
408 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;416 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;
409 try renderTypeAndName(ctx, writer, elem_type, local_name, mutability);417 const local = try o.allocLocal(elem_type, mutability);
410 try writer.writeAll(";\n");418 try writer.writeAll(";\n");
411419
412 // Second line: a pointer to it so that we can refer to it as the allocation.420 return CValue{ .local_ref = local.local };
413 // One line for the variable, one line for the pointer to the variable, which we return.
414 try indent(file);
415 const ptr_local_name = try ctx.name();
416 try renderTypeAndName(ctx, writer, alloc.base.ty, ptr_local_name, .Const);
417 try writer.print(" = &{s};\n", .{local_name});
418
419 return ptr_local_name;
420}421}
421422
422fn genArg(ctx: *Context) !?[]u8 {423fn genArg(o: *Object) CValue {
423 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex});424 const i = o.next_arg_index;
424 ctx.argdex += 1;425 o.next_arg_index += 1;
425 return name;426 return .{ .arg = i };
426}427}
427428
428fn genRetVoid(file: *C) !?[]u8 {429fn genRetVoid(o: *Object) !CValue {
429 try indent(file);430 try o.indent();
430 try file.main.writer().print("return;\n", .{});431 try o.code.writer().print("return;\n", .{});
431 return null;432 return CValue.none;
432}433}
433434
434fn genLoad(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {435fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
435 const operand = try ctx.resolveInst(inst.operand);436 const operand = try o.resolveInst(inst.operand);
436 const writer = file.main.writer();437 const writer = o.code.writer();
437 try indent(file);438 try o.indent();
438 const local_name = try ctx.name();439 const local = try o.allocLocal(inst.base.ty, .Const);
439 try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);440 switch (operand) {
440 try writer.print(" = *{s};\n", .{operand});441 .local_ref => |i| {
441 return local_name;442 const wrapped: CValue = .{ .local = i };
443 try writer.writeAll(" = ");
444 try o.writeCValue(writer, wrapped);
445 try writer.writeAll(";\n");
446 },
447 else => {
448 try writer.writeAll(" = *");
449 try o.writeCValue(writer, operand);
450 try writer.writeAll(";\n");
451 },
452 }
453 return local;
442}454}
443455
444fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {456fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {
445 try indent(file);457 const operand = try o.resolveInst(inst.operand);
446 const writer = file.main.writer();458 try o.indent();
447 try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)});459 const writer = o.code.writer();
448 return null;460 try writer.writeAll("return ");
461 try o.writeCValue(writer, operand);
462 try writer.writeAll(";\n");
463 return CValue.none;
449}464}
450465
451fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {466fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue {
452 if (inst.base.isUnused())467 if (inst.base.isUnused())
453 return null;468 return CValue.none;
454 try indent(file);469
455 const writer = file.main.writer();470 const from = try o.resolveInst(inst.operand);
456 const name = try ctx.name();
457 const from = try ctx.resolveInst(inst.operand);
458471
459 try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);472 try o.indent();
473 const writer = o.code.writer();
474 const local = try o.allocLocal(inst.base.ty, .Const);
460 try writer.writeAll(" = (");475 try writer.writeAll(" = (");
461 try renderType(ctx, writer, inst.base.ty);476 try o.dg.renderType(writer, inst.base.ty);
462 try writer.print("){s};\n", .{from});477 try writer.writeAll(")");
463 return name;478 try o.writeCValue(writer, from);
479 try writer.writeAll(";\n");
480 return local;
464}481}
465482
466fn genStore(ctx: *Context, file: *C, inst: *Inst.BinOp) !?[]u8 {483fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
467 // *a = b;484 // *a = b;
468 try indent(file);485 const dest_ptr = try o.resolveInst(inst.lhs);
469 const writer = file.main.writer();486 const src_val = try o.resolveInst(inst.rhs);
470 const dest_ptr_name = try ctx.resolveInst(inst.lhs);487
471 const src_val_name = try ctx.resolveInst(inst.rhs);488 try o.indent();
472 try writer.print("*{s} = {s};\n", .{ dest_ptr_name, src_val_name });489 const writer = o.code.writer();
473 return null;490 switch (dest_ptr) {
491 .local_ref => |i| {
492 const dest: CValue = .{ .local = i };
493 try o.writeCValue(writer, dest);
494 try writer.writeAll(" = ");
495 try o.writeCValue(writer, src_val);
496 try writer.writeAll(";\n");
497 },
498 else => {
499 try writer.writeAll("*");
500 try o.writeCValue(writer, dest_ptr);
501 try writer.writeAll(" = ");
502 try o.writeCValue(writer, src_val);
503 try writer.writeAll(";\n");
504 },
505 }
506 return CValue.none;
474}507}
475508
476fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, operator: []const u8) !?[]u8 {509fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
477 if (inst.base.isUnused())510 if (inst.base.isUnused())
478 return null;511 return CValue.none;
479 try indent(file);512
480 const lhs = try ctx.resolveInst(inst.lhs);513 const lhs = try o.resolveInst(inst.lhs);
481 const rhs = try ctx.resolveInst(inst.rhs);514 const rhs = try o.resolveInst(inst.rhs);
482 const writer = file.main.writer();515
483 const name = try ctx.name();516 try o.indent();
484 try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);517 const writer = o.code.writer();
485 try writer.print(" = {s} {s} {s};\n", .{ lhs, operator, rhs });518 const local = try o.allocLocal(inst.base.ty, .Const);
486 return name;519
520 try writer.writeAll(" = ");
521 try o.writeCValue(writer, lhs);
522 try writer.writeAll(operator);
523 try o.writeCValue(writer, rhs);
524 try writer.writeAll(";\n");
525
526 return local;
487}527}
488528
489fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {529fn genCall(o: *Object, inst: *Inst.Call) !CValue {
490 try indent(file);
491 const writer = file.main.writer();
492 const header = file.header.buf.writer();
493 if (inst.func.castTag(.constant)) |func_inst| {530 if (inst.func.castTag(.constant)) |func_inst| {
494 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|531 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|
495 extern_fn.data532 extern_fn.data
...@@ -501,23 +538,19 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {...@@ -501,23 +538,19 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
501 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;538 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
502 const ret_ty = fn_ty.fnReturnType();539 const ret_ty = fn_ty.fnReturnType();
503 const unused_result = inst.base.isUnused();540 const unused_result = inst.base.isUnused();
504 var result_name: ?[]u8 = null;541 var result_local: CValue = .none;
542
543 try o.indent();
544 const writer = o.code.writer();
505 if (unused_result) {545 if (unused_result) {
506 if (ret_ty.hasCodeGenBits()) {546 if (ret_ty.hasCodeGenBits()) {
507 try writer.print("(void)", .{});547 try writer.print("(void)", .{});
508 }548 }
509 } else {549 } else {
510 const local_name = try ctx.name();550 result_local = try o.allocLocal(ret_ty, .Const);
511 try renderTypeAndName(ctx, writer, ret_ty, local_name, .Const);
512 try writer.writeAll(" = ");551 try writer.writeAll(" = ");
513 result_name = local_name;
514 }552 }
515 const fn_name = mem.spanZ(fn_decl.name);553 const fn_name = mem.spanZ(fn_decl.name);
516 if (file.called.get(fn_name) == null) {
517 try file.called.put(fn_name, {});
518 try renderFunctionSignature(ctx, header, fn_decl);
519 try header.writeAll(";\n");
520 }
521 try writer.print("{s}(", .{fn_name});554 try writer.print("{s}(", .{fn_name});
522 if (inst.args.len != 0) {555 if (inst.args.len != 0) {
523 for (inst.args) |arg, i| {556 for (inst.args) |arg, i| {
...@@ -525,87 +558,98 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {...@@ -525,87 +558,98 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
525 try writer.writeAll(", ");558 try writer.writeAll(", ");
526 }559 }
527 if (arg.value()) |val| {560 if (arg.value()) |val| {
528 try renderValue(ctx, writer, arg.ty, val);561 try o.dg.renderValue(writer, arg.ty, val);
529 } else {562 } else {
530 const val = try ctx.resolveInst(arg);563 const val = try o.resolveInst(arg);
531 try writer.print("{s}", .{val});564 try o.writeCValue(writer, val);
532 }565 }
533 }566 }
534 }567 }
535 try writer.writeAll(");\n");568 try writer.writeAll(");\n");
536 return result_name;569 return result_local;
537 } else {570 } else {
538 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});571 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement function pointers", .{});
539 }572 }
540}573}
541574
542fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {575fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue {
543 // TODO emit #line directive here with line number and filename576 // TODO emit #line directive here with line number and filename
544 return null;577 return CValue.none;
545}578}
546579
547fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {580fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
548 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});581 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement blocks", .{});
549}582}
550583
551fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {584fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
552 const writer = file.main.writer();585 const operand = try o.resolveInst(inst.operand);
553 try indent(file);586
554 const local_name = try ctx.name();587 const writer = o.code.writer();
555 const operand = try ctx.resolveInst(inst.operand);588 try o.indent();
556 try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
557 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {589 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
590 const local = try o.allocLocal(inst.base.ty, .Const);
558 try writer.writeAll(" = (");591 try writer.writeAll(" = (");
559 try renderType(ctx, writer, inst.base.ty);592 try o.dg.renderType(writer, inst.base.ty);
560 try writer.print("){s};\n", .{operand});593
561 } else {594 try writer.writeAll(")");
595 try o.writeCValue(writer, operand);
562 try writer.writeAll(";\n");596 try writer.writeAll(";\n");
563 try indent(file);597 return local;
564 try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name });
565 }598 }
566 return local_name;599
600 const local = try o.allocLocal(inst.base.ty, .Mut);
601 try writer.writeAll(";\n");
602 try o.indent();
603
604 try writer.writeAll("memcpy(&");
605 try o.writeCValue(writer, local);
606 try writer.writeAll(", &");
607 try o.writeCValue(writer, operand);
608 try writer.writeAll(", sizeof ");
609 try o.writeCValue(writer, local);
610 try writer.writeAll(");\n");
611
612 return local;
567}613}
568614
569fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {615fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue {
570 try indent(file);616 try o.indent();
571 try file.main.writer().writeAll("zig_breakpoint();\n");617 try o.code.writer().writeAll("zig_breakpoint();\n");
572 return null;618 return CValue.none;
573}619}
574620
575fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {621fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue {
576 try indent(file);622 try o.indent();
577 try file.main.writer().writeAll("zig_unreachable();\n");623 try o.code.writer().writeAll("zig_unreachable();\n");
578 return null;624 return CValue.none;
579}625}
580626
581fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {627fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
582 try indent(file);628 if (as.base.isUnused() and !as.is_volatile)
583 const writer = file.main.writer();629 return CValue.none;
630
631 const writer = o.code.writer();
584 for (as.inputs) |i, index| {632 for (as.inputs) |i, index| {
585 if (i[0] == '{' and i[i.len - 1] == '}') {633 if (i[0] == '{' and i[i.len - 1] == '}') {
586 const reg = i[1 .. i.len - 1];634 const reg = i[1 .. i.len - 1];
587 const arg = as.args[index];635 const arg = as.args[index];
636 const arg_c_value = try o.resolveInst(arg);
637 try o.indent();
588 try writer.writeAll("register ");638 try writer.writeAll("register ");
589 try renderType(ctx, writer, arg.ty);639 try o.dg.renderType(writer, arg.ty);
640
590 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });641 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
591 // TODO merge constant handling into inst_map as well642 try o.writeCValue(writer, arg_c_value);
592 if (arg.castTag(.constant)) |c| {643 try writer.writeAll(";\n");
593 try renderValue(ctx, writer, arg.ty, c.val);
594 try writer.writeAll(";\n ");
595 } else {
596 const gop = try ctx.inst_map.getOrPut(arg);
597 if (!gop.found_existing) {
598 return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
599 }
600 try writer.print("{s};\n ", .{gop.entry.value});
601 }
602 } else {644 } else {
603 return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});645 return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{});
604 }646 }
605 }647 }
606 try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });648 try o.indent();
607 if (as.output) |o| {649 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
608 return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{});650 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
651 if (as.output) |_| {
652 return o.dg.fail(o.dg.decl.src(), "TODO inline asm output", .{});
609 }653 }
610 if (as.inputs.len > 0) {654 if (as.inputs.len > 0) {
611 if (as.output == null) {655 if (as.output == null) {
...@@ -619,7 +663,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {...@@ -619,7 +663,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
619 if (index > 0) {663 if (index > 0) {
620 try writer.writeAll(", ");664 try writer.writeAll(", ");
621 }665 }
622 try writer.print("\"\"({s}_constant)", .{reg});666 try writer.print("\"r\"({s}_constant)", .{reg});
623 } else {667 } else {
624 // This is blocked by the earlier test668 // This is blocked by the earlier test
625 unreachable;669 unreachable;
...@@ -627,5 +671,9 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {...@@ -627,5 +671,9 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
627 }671 }
628 }672 }
629 try writer.writeAll(");\n");673 try writer.writeAll(");\n");
630 return null;674
675 if (as.base.isUnused())
676 return CValue.none;
677
678 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
631}679}
src/link.zig+10-7
...@@ -130,7 +130,7 @@ pub const File = struct {...@@ -130,7 +130,7 @@ pub const File = struct {
130 elf: Elf.TextBlock,130 elf: Elf.TextBlock,
131 coff: Coff.TextBlock,131 coff: Coff.TextBlock,
132 macho: MachO.TextBlock,132 macho: MachO.TextBlock,
133 c: void,133 c: C.DeclBlock,
134 wasm: void,134 wasm: void,
135 };135 };
136136
...@@ -138,7 +138,7 @@ pub const File = struct {...@@ -138,7 +138,7 @@ pub const File = struct {
138 elf: Elf.SrcFn,138 elf: Elf.SrcFn,
139 coff: Coff.SrcFn,139 coff: Coff.SrcFn,
140 macho: MachO.SrcFn,140 macho: MachO.SrcFn,
141 c: void,141 c: C.FnBlock,
142 wasm: ?Wasm.FnData,142 wasm: ?Wasm.FnData,
143 };143 };
144144
...@@ -301,7 +301,8 @@ pub const File = struct {...@@ -301,7 +301,8 @@ pub const File = struct {
301 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),301 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
302 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),302 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
303 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),303 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
304 .c, .wasm => {},304 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
305 .wasm => {},
305 }306 }
306 }307 }
307308
...@@ -312,7 +313,8 @@ pub const File = struct {...@@ -312,7 +313,8 @@ pub const File = struct {
312 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),313 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
313 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),314 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
314 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),315 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
315 .c, .wasm => {},316 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
317 .wasm => {},
316 }318 }
317 }319 }
318320
...@@ -407,12 +409,13 @@ pub const File = struct {...@@ -407,12 +409,13 @@ pub const File = struct {
407 }409 }
408 }410 }
409411
412 /// Called when a Decl is deleted from the Module.
410 pub fn freeDecl(base: *File, decl: *Module.Decl) void {413 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
411 switch (base.tag) {414 switch (base.tag) {
412 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),415 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
413 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),416 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
414 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),417 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
415 .c => unreachable,418 .c => @fieldParentPtr(C, "base", base).freeDecl(decl),
416 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),419 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
417 }420 }
418 }421 }
...@@ -432,14 +435,14 @@ pub const File = struct {...@@ -432,14 +435,14 @@ pub const File = struct {
432 pub fn updateDeclExports(435 pub fn updateDeclExports(
433 base: *File,436 base: *File,
434 module: *Module,437 module: *Module,
435 decl: *const Module.Decl,438 decl: *Module.Decl,
436 exports: []const *Module.Export,439 exports: []const *Module.Export,
437 ) !void {440 ) !void {
438 switch (base.tag) {441 switch (base.tag) {
439 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),442 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
440 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),443 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
441 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),444 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
442 .c => return {},445 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
443 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),446 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
444 }447 }
445 }448 }
src/link/C.zig+175-64
...@@ -8,46 +8,31 @@ const fs = std.fs;...@@ -8,46 +8,31 @@ const fs = std.fs;
8const codegen = @import("../codegen/c.zig");8const codegen = @import("../codegen/c.zig");
9const link = @import("../link.zig");9const link = @import("../link.zig");
10const trace = @import("../tracy.zig").trace;10const trace = @import("../tracy.zig").trace;
11const File = link.File;
12const C = @This();11const C = @This();
1312
14pub const base_tag: File.Tag = .c;13pub const base_tag: link.File.Tag = .c;
14pub const zig_h = @embedFile("C/zig.h");
1515
16pub const Header = struct {16base: link.File,
17 buf: std.ArrayList(u8),
18 emit_loc: ?Compilation.EmitLoc,
1917
20 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {18/// Per-declaration data. For functions this is the body, and
21 return .{19/// the forward declaration is stored in the FnBlock.
22 .buf = std.ArrayList(u8).init(allocator),20pub const DeclBlock = struct {
23 .emit_loc = emit_loc,21 code: std.ArrayListUnmanaged(u8),
24 };
25 }
26
27 pub fn flush(self: *const Header, writer: anytype) !void {
28 const tracy = trace(@src());
29 defer tracy.end();
30
31 try writer.writeAll(@embedFile("cbe.h"));
32 if (self.buf.items.len > 0) {
33 try writer.print("{s}", .{self.buf.items});
34 }
35 }
3622
37 pub fn deinit(self: *Header) void {23 pub const empty: DeclBlock = .{
38 self.buf.deinit();24 .code = .{},
39 self.* = undefined;25 };
40 }
41};26};
4227
43base: File,28/// Per-function data.
4429pub const FnBlock = struct {
45header: Header,30 fwd_decl: std.ArrayListUnmanaged(u8),
46constants: std.ArrayList(u8),
47main: std.ArrayList(u8),
4831
49called: std.StringHashMap(void),32 pub const empty: FnBlock = .{
50error_msg: *Compilation.ErrorMsg = undefined,33 .fwd_decl = .{},
34 };
35};
5136
52pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {37pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
53 assert(options.object_format == .c);38 assert(options.object_format == .c);
...@@ -55,7 +40,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -55,7 +40,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
55 if (options.use_llvm) return error.LLVMHasNoCBackend;40 if (options.use_llvm) return error.LLVMHasNoCBackend;
56 if (options.use_lld) return error.LLDHasNoCBackend;41 if (options.use_lld) return error.LLDHasNoCBackend;
5742
58 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });43 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
44 // Truncation is done on `flush`.
45 .truncate = false,
46 .mode = link.determineMode(options),
47 });
59 errdefer file.close();48 errdefer file.close();
6049
61 var c_file = try allocator.create(C);50 var c_file = try allocator.create(C);
...@@ -68,34 +57,69 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -68,34 +57,69 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
68 .file = file,57 .file = file,
69 .allocator = allocator,58 .allocator = allocator,
70 },59 },
71 .main = std.ArrayList(u8).init(allocator),
72 .header = Header.init(allocator, null),
73 .constants = std.ArrayList(u8).init(allocator),
74 .called = std.StringHashMap(void).init(allocator),
75 };60 };
7661
77 return c_file;62 return c_file;
78}63}
7964
80pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {65pub fn deinit(self: *C) void {
81 self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);66 const module = self.base.options.module orelse return;
82 return error.AnalysisFail;67 for (module.decl_table.items()) |entry| {
68 self.freeDecl(entry.value);
69 }
83}70}
8471
85pub fn deinit(self: *C) void {72pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
86 self.main.deinit();73
87 self.header.deinit();74pub fn freeDecl(self: *C, decl: *Module.Decl) void {
88 self.constants.deinit();75 decl.link.c.code.deinit(self.base.allocator);
89 self.called.deinit();76 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);
90}77}
9178
92pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {79pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
93 codegen.generate(self, module, decl) catch |err| {80 const tracy = trace(@src());
94 if (err == error.AnalysisFail) {81 defer tracy.end();
95 try module.failed_decls.put(module.gpa, decl, self.error_msg);82
96 }83 const fwd_decl = &decl.fn_link.c.fwd_decl;
97 return err;84 const code = &decl.link.c.code;
85 fwd_decl.shrinkRetainingCapacity(0);
86 code.shrinkRetainingCapacity(0);
87
88 var object: codegen.Object = .{
89 .dg = .{
90 .module = module,
91 .error_msg = null,
92 .decl = decl,
93 .fwd_decl = fwd_decl.toManaged(module.gpa),
94 },
95 .gpa = module.gpa,
96 .code = code.toManaged(module.gpa),
97 .value_map = codegen.CValueMap.init(module.gpa),
98 };
99 defer object.value_map.deinit();
100 defer object.code.deinit();
101 defer object.dg.fwd_decl.deinit();
102
103 codegen.genDecl(&object) catch |err| switch (err) {
104 error.AnalysisFail => {
105 try module.failed_decls.put(module.gpa, decl, object.dg.error_msg.?);
106 return;
107 },
108 else => |e| return e,
98 };109 };
110
111 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
112 code.* = object.code.moveToUnmanaged();
113
114 // Free excess allocated memory for this Decl.
115 fwd_decl.shrink(module.gpa, fwd_decl.items.len);
116 code.shrink(module.gpa, code.items.len);
117}
118
119pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
120 // The C backend does not have the ability to fix line numbers without re-generating
121 // the entire Decl.
122 return self.updateDecl(module, decl);
99}123}
100124
101pub fn flush(self: *C, comp: *Compilation) !void {125pub fn flush(self: *C, comp: *Compilation) !void {
...@@ -106,21 +130,108 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -106,21 +130,108 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
106 const tracy = trace(@src());130 const tracy = trace(@src());
107 defer tracy.end();131 defer tracy.end();
108132
109 const writer = self.base.file.?.writer();133 const module = self.base.options.module.?;
110 try self.header.flush(writer);134
111 if (self.header.buf.items.len > 0) {135 // This code path happens exclusively with -ofmt=c. The flush logic for
112 try writer.writeByte('\n');136 // emit-h is in `flushEmitH` below.
113 }137
114 if (self.constants.items.len > 0) {138 // We collect a list of buffers to write, and write them all at once with pwritev 😎
115 try writer.print("{s}\n", .{self.constants.items});139 var all_buffers = std.ArrayList(std.os.iovec_const).init(comp.gpa);
140 defer all_buffers.deinit();
141
142 // This is at least enough until we get to the function bodies without error handling.
143 try all_buffers.ensureCapacity(module.decl_table.count() + 1);
144
145 var file_size: u64 = zig_h.len;
146 all_buffers.appendAssumeCapacity(.{
147 .iov_base = zig_h,
148 .iov_len = zig_h.len,
149 });
150
151 var fn_count: usize = 0;
152
153 // Forward decls and non-functions first.
154 for (module.decl_table.items()) |kv| {
155 const decl = kv.value;
156 const decl_tv = decl.typed_value.most_recent.typed_value;
157 const buf = buf: {
158 if (decl_tv.val.castTag(.function)) |_| {
159 fn_count += 1;
160 break :buf decl.fn_link.c.fwd_decl.items;
161 } else {
162 break :buf decl.link.c.code.items;
163 }
164 };
165 all_buffers.appendAssumeCapacity(.{
166 .iov_base = buf.ptr,
167 .iov_len = buf.len,
168 });
169 file_size += buf.len;
116 }170 }
117 if (self.main.items.len > 1) {171
118 const last_two = self.main.items[self.main.items.len - 2 ..];172 // Now the function bodies.
119 if (std.mem.eql(u8, last_two, "\n\n")) {173 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
120 self.main.items.len -= 1;174 for (module.decl_table.items()) |kv| {
175 const decl = kv.value;
176 const decl_tv = decl.typed_value.most_recent.typed_value;
177 if (decl_tv.val.castTag(.function)) |_| {
178 const buf = decl.link.c.code.items;
179 all_buffers.appendAssumeCapacity(.{
180 .iov_base = buf.ptr,
181 .iov_len = buf.len,
182 });
183 file_size += buf.len;
121 }184 }
122 }185 }
123 try writer.writeAll(self.main.items);186
124 self.base.file.?.close();187 const file = self.base.file.?;
125 self.base.file = null;188 try file.setEndPos(file_size);
189 try file.pwritevAll(all_buffers.items, 0);
126}190}
191
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
232pub fn updateDeclExports(
233 self: *C,
234 module: *Module,
235 decl: *Module.Decl,
236 exports: []const *Module.Export,
237) !void {}
src/link/C/zig.h created+58
...@@ -0,0 +1,58 @@
1#if __STDC_VERSION__ >= 199901L
2#include <stdbool.h>
3#else
4#define bool unsigned char
5#define true 1
6#define false 0
7#endif
8
9#if __STDC_VERSION__ >= 201112L
10#define zig_noreturn _Noreturn
11#elif __GNUC__
12#define zig_noreturn __attribute__ ((noreturn))
13#elif _MSC_VER
14#define zig_noreturn __declspec(noreturn)
15#else
16#define zig_noreturn
17#endif
18
19#if defined(__GNUC__)
20#define zig_unreachable() __builtin_unreachable()
21#else
22#define zig_unreachable()
23#endif
24
25#if __STDC_VERSION__ >= 199901L
26#define ZIG_RESTRICT restrict
27#elif defined(__GNUC__)
28#define ZIG_RESTRICT __restrict
29#else
30#define ZIG_RESTRICT
31#endif
32
33#ifdef __cplusplus
34#define ZIG_EXTERN_C extern "C"
35#else
36#define ZIG_EXTERN_C
37#endif
38
39#if defined(_MSC_VER)
40#define zig_breakpoint() __debugbreak()
41#elif defined(__MINGW32__) || defined(__MINGW64__)
42#define zig_breakpoint() __debugbreak()
43#elif defined(__clang__)
44#define zig_breakpoint() __builtin_debugtrap()
45#elif defined(__GNUC__)
46#define zig_breakpoint() __builtin_trap()
47#elif defined(__i386__) || defined(__x86_64__)
48#define zig_breakpoint() __asm__ volatile("int $0x03");
49#else
50#define zig_breakpoint() raise(SIGTRAP)
51#endif
52
53#include <stdint.h>
54#include <stddef.h>
55#define int128_t __int128
56#define uint128_t unsigned __int128
57ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
58
src/link/cbe.h deleted-44
...@@ -1,44 +0,0 @@
1#if __STDC_VERSION__ >= 199901L
2#include <stdbool.h>
3#else
4#define bool unsigned char
5#define true 1
6#define false 0
7#endif
8
9#if __STDC_VERSION__ >= 201112L
10#define zig_noreturn _Noreturn
11#elif __GNUC__
12#define zig_noreturn __attribute__ ((noreturn))
13#elif _MSC_VER
14#define zig_noreturn __declspec(noreturn)
15#else
16#define zig_noreturn
17#endif
18
19#if defined(__GNUC__)
20#define zig_unreachable() __builtin_unreachable()
21#else
22#define zig_unreachable()
23#endif
24
25#if defined(_MSC_VER)
26#define zig_breakpoint __debugbreak()
27#else
28#if defined(__MINGW32__) || defined(__MINGW64__)
29#define zig_breakpoint __debugbreak()
30#elif defined(__clang__)
31#define zig_breakpoint __builtin_debugtrap()
32#elif defined(__GNUC__)
33#define zig_breakpoint __builtin_trap()
34#elif defined(__i386__) || defined(__x86_64__)
35#define zig_breakpoint __asm__ volatile("int $0x03");
36#else
37#define zig_breakpoint raise(SIGTRAP)
38#endif
39#endif
40
41#include <stdint.h>
42#define int128_t __int128
43#define uint128_t unsigned __int128
44#include <string.h>
src/test.zig+9-8
...@@ -13,7 +13,7 @@ const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_d...@@ -13,7 +13,7 @@ const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_d
13const ThreadPool = @import("ThreadPool.zig");13const ThreadPool = @import("ThreadPool.zig");
14const CrossTarget = std.zig.CrossTarget;14const CrossTarget = std.zig.CrossTarget;
1515
16const c_header = @embedFile("link/cbe.h");16const zig_h = link.File.C.zig_h;
1717
18test "self-hosted" {18test "self-hosted" {
19 var ctx = TestContext.init();19 var ctx = TestContext.init();
...@@ -324,11 +324,11 @@ pub const TestContext = struct {...@@ -324,11 +324,11 @@ pub const TestContext = struct {
324 }324 }
325325
326 pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {326 pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
327 ctx.addC(name, target, .Zig).addCompareObjectFile(src, c_header ++ out);327 ctx.addC(name, target, .Zig).addCompareObjectFile(src, zig_h ++ out);
328 }328 }
329329
330 pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {330 pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
331 ctx.addC(name, target, .Zig).addHeader(src, c_header ++ out);331 ctx.addC(name, target, .Zig).addHeader(src, zig_h ++ out);
332 }332 }
333333
334 pub fn addCompareOutput(334 pub fn addCompareOutput(
...@@ -700,11 +700,12 @@ pub const TestContext = struct {...@@ -700,11 +700,12 @@ pub const TestContext = struct {
700 },700 },
701 }701 }
702 }702 }
703 if (comp.bin_file.cast(link.File.C)) |c_file| {703 // TODO print generated C code
704 std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{704 //if (comp.bin_file.cast(link.File.C)) |c_file| {
705 c_file.main.items,705 // std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{
706 });706 // c_file.main.items,
707 }707 // });
708 //}
708 std.debug.print("Test failed.\n", .{});709 std.debug.print("Test failed.\n", .{});
709 std.process.exit(1);710 std.process.exit(1);
710 }711 }
test/stage2/cbe.zig+114-237
...@@ -22,15 +22,107 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -22,15 +22,107 @@ pub fn addCases(ctx: *TestContext) !void {
22 , "hello world!" ++ std.cstr.line_sep);22 , "hello world!" ++ std.cstr.line_sep);
2323
24 // Now change the message only24 // Now change the message only
25 // TODO fix C backend not supporting updates25 case.addCompareOutput(
26 // https://github.com/ziglang/zig/issues/758926 \\extern fn puts(s: [*:0]const u8) c_int;
27 //case.addCompareOutput(27 \\export fn main() c_int {
28 // \\extern fn puts(s: [*:0]const u8) c_int;28 \\ _ = puts("yo");
29 // \\export fn main() c_int {29 \\ return 0;
30 // \\ _ = puts("yo");30 \\}
31 // \\ return 0;31 , "yo" ++ std.cstr.line_sep);
32 // \\}32 }
33 //, "yo" ++ std.cstr.line_sep);33
34 {
35 var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
36
37 // Exit with 0
38 case.addCompareOutput(
39 \\fn exitGood() noreturn {
40 \\ asm volatile ("syscall"
41 \\ :
42 \\ : [number] "{rax}" (231),
43 \\ [arg1] "{rdi}" (0)
44 \\ );
45 \\ unreachable;
46 \\}
47 \\
48 \\export fn main() c_int {
49 \\ exitGood();
50 \\}
51 , "");
52
53 // Pass a usize parameter to exit
54 case.addCompareOutput(
55 \\export fn main() c_int {
56 \\ exit(0);
57 \\}
58 \\
59 \\fn exit(code: usize) noreturn {
60 \\ asm volatile ("syscall"
61 \\ :
62 \\ : [number] "{rax}" (231),
63 \\ [arg1] "{rdi}" (code)
64 \\ );
65 \\ unreachable;
66 \\}
67 , "");
68
69 // Change the parameter to u8
70 case.addCompareOutput(
71 \\export fn main() c_int {
72 \\ exit(0);
73 \\}
74 \\
75 \\fn exit(code: u8) noreturn {
76 \\ asm volatile ("syscall"
77 \\ :
78 \\ : [number] "{rax}" (231),
79 \\ [arg1] "{rdi}" (code)
80 \\ );
81 \\ unreachable;
82 \\}
83 , "");
84
85 // Do some arithmetic at the exit callsite
86 case.addCompareOutput(
87 \\export fn main() c_int {
88 \\ exitMath(1);
89 \\}
90 \\
91 \\fn exitMath(a: u8) noreturn {
92 \\ exit(0 + a - a);
93 \\}
94 \\
95 \\fn exit(code: u8) noreturn {
96 \\ asm volatile ("syscall"
97 \\ :
98 \\ : [number] "{rax}" (231),
99 \\ [arg1] "{rdi}" (code)
100 \\ );
101 \\ unreachable;
102 \\}
103 \\
104 , "");
105
106 // Invert the arithmetic
107 case.addCompareOutput(
108 \\export fn main() c_int {
109 \\ exitMath(1);
110 \\}
111 \\
112 \\fn exitMath(a: u8) noreturn {
113 \\ exit(a + 0 - a);
114 \\}
115 \\
116 \\fn exit(code: u8) noreturn {
117 \\ asm volatile ("syscall"
118 \\ :
119 \\ : [number] "{rax}" (231),
120 \\ [arg1] "{rdi}" (code)
121 \\ );
122 \\ unreachable;
123 \\}
124 \\
125 , "");
34 }126 }
35127
36 {128 {
...@@ -88,6 +180,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -88,6 +180,8 @@ pub fn addCases(ctx: *TestContext) !void {
88 \\ unreachable;180 \\ unreachable;
89 \\}181 \\}
90 ,182 ,
183 \\ZIG_EXTERN_C zig_noreturn void _start(void);
184 \\
91 \\zig_noreturn void _start(void) {185 \\zig_noreturn void _start(void) {
92 \\ zig_breakpoint();186 \\ zig_breakpoint();
93 \\ zig_unreachable();187 \\ zig_unreachable();
...@@ -97,254 +191,37 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -97,254 +191,37 @@ pub fn addCases(ctx: *TestContext) !void {
97 ctx.h("simple header", linux_x64,191 ctx.h("simple header", linux_x64,
98 \\export fn start() void{}192 \\export fn start() void{}
99 ,193 ,
100 \\void start(void);194 \\ZIG_EXTERN_C void start(void);
101 \\
102 );
103 ctx.c("less empty start function", linux_x64,
104 \\fn main() noreturn {
105 \\ unreachable;
106 \\}
107 \\
108 \\export fn _start() noreturn {
109 \\ main();
110 \\}
111 ,
112 \\static zig_noreturn void main(void);
113 \\
114 \\zig_noreturn void _start(void) {
115 \\ main();
116 \\}
117 \\
118 \\static zig_noreturn void main(void) {
119 \\ zig_breakpoint();
120 \\ zig_unreachable();
121 \\}
122 \\
123 );
124 // TODO: implement return values
125 // TODO: figure out a way to prevent asm constants from being generated
126 ctx.c("inline asm", linux_x64,
127 \\fn exitGood() noreturn {
128 \\ asm volatile ("syscall"
129 \\ :
130 \\ : [number] "{rax}" (231),
131 \\ [arg1] "{rdi}" (0)
132 \\ );
133 \\ unreachable;
134 \\}
135 \\
136 \\export fn _start() noreturn {
137 \\ exitGood();
138 \\}
139 ,
140 \\static zig_noreturn void exitGood(void);
141 \\
142 \\static uint8_t exitGood__anon_0[6] = "{rax}";
143 \\static uint8_t exitGood__anon_1[6] = "{rdi}";
144 \\static uint8_t exitGood__anon_2[8] = "syscall";
145 \\
146 \\zig_noreturn void _start(void) {
147 \\ exitGood();
148 \\}
149 \\
150 \\static zig_noreturn void exitGood(void) {
151 \\ register uintptr_t rax_constant __asm__("rax") = 231;
152 \\ register uintptr_t rdi_constant __asm__("rdi") = 0;
153 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
154 \\ zig_breakpoint();
155 \\ zig_unreachable();
156 \\}
157 \\
158 );
159 ctx.c("exit with parameter", linux_x64,
160 \\export fn _start() noreturn {
161 \\ exit(0);
162 \\}
163 \\
164 \\fn exit(code: usize) noreturn {
165 \\ asm volatile ("syscall"
166 \\ :
167 \\ : [number] "{rax}" (231),
168 \\ [arg1] "{rdi}" (code)
169 \\ );
170 \\ unreachable;
171 \\}
172 \\
173 ,
174 \\static zig_noreturn void exit(uintptr_t arg0);
175 \\
176 \\static uint8_t exit__anon_0[6] = "{rax}";
177 \\static uint8_t exit__anon_1[6] = "{rdi}";
178 \\static uint8_t exit__anon_2[8] = "syscall";
179 \\
180 \\zig_noreturn void _start(void) {
181 \\ exit(0);
182 \\}
183 \\
184 \\static zig_noreturn void exit(uintptr_t arg0) {
185 \\ register uintptr_t rax_constant __asm__("rax") = 231;
186 \\ register uintptr_t rdi_constant __asm__("rdi") = arg0;
187 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
188 \\ zig_breakpoint();
189 \\ zig_unreachable();
190 \\}
191 \\
192 );
193 ctx.c("exit with u8 parameter", linux_x64,
194 \\export fn _start() noreturn {
195 \\ exit(0);
196 \\}
197 \\
198 \\fn exit(code: u8) noreturn {
199 \\ asm volatile ("syscall"
200 \\ :
201 \\ : [number] "{rax}" (231),
202 \\ [arg1] "{rdi}" (code)
203 \\ );
204 \\ unreachable;
205 \\}
206 \\
207 ,
208 \\static zig_noreturn void exit(uint8_t arg0);
209 \\
210 \\static uint8_t exit__anon_0[6] = "{rax}";
211 \\static uint8_t exit__anon_1[6] = "{rdi}";
212 \\static uint8_t exit__anon_2[8] = "syscall";
213 \\
214 \\zig_noreturn void _start(void) {
215 \\ exit(0);
216 \\}
217 \\
218 \\static zig_noreturn void exit(uint8_t arg0) {
219 \\ uintptr_t const __temp_0 = (uintptr_t)arg0;
220 \\ register uintptr_t rax_constant __asm__("rax") = 231;
221 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
222 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
223 \\ zig_breakpoint();
224 \\ zig_unreachable();
225 \\}
226 \\
227 );
228 ctx.c("exit with u8 arithmetic", linux_x64,
229 \\export fn _start() noreturn {
230 \\ exitMath(1);
231 \\}
232 \\
233 \\fn exitMath(a: u8) noreturn {
234 \\ exit(0 + a - a);
235 \\}
236 \\
237 \\fn exit(code: u8) noreturn {
238 \\ asm volatile ("syscall"
239 \\ :
240 \\ : [number] "{rax}" (231),
241 \\ [arg1] "{rdi}" (code)
242 \\ );
243 \\ unreachable;
244 \\}
245 \\
246 ,
247 \\static zig_noreturn void exitMath(uint8_t arg0);
248 \\static zig_noreturn void exit(uint8_t arg0);
249 \\
250 \\static uint8_t exit__anon_0[6] = "{rax}";
251 \\static uint8_t exit__anon_1[6] = "{rdi}";
252 \\static uint8_t exit__anon_2[8] = "syscall";
253 \\
254 \\zig_noreturn void _start(void) {
255 \\ exitMath(1);
256 \\}
257 \\
258 \\static zig_noreturn void exitMath(uint8_t arg0) {
259 \\ uint8_t const __temp_0 = 0 + arg0;
260 \\ uint8_t const __temp_1 = __temp_0 - arg0;
261 \\ exit(__temp_1);
262 \\}
263 \\
264 \\static zig_noreturn void exit(uint8_t arg0) {
265 \\ uintptr_t const __temp_0 = (uintptr_t)arg0;
266 \\ register uintptr_t rax_constant __asm__("rax") = 231;
267 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
268 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
269 \\ zig_breakpoint();
270 \\ zig_unreachable();
271 \\}
272 \\
273 );
274 ctx.c("exit with u8 arithmetic inverted", linux_x64,
275 \\export fn _start() noreturn {
276 \\ exitMath(1);
277 \\}
278 \\
279 \\fn exitMath(a: u8) noreturn {
280 \\ exit(a + 0 - a);
281 \\}
282 \\
283 \\fn exit(code: u8) noreturn {
284 \\ asm volatile ("syscall"
285 \\ :
286 \\ : [number] "{rax}" (231),
287 \\ [arg1] "{rdi}" (code)
288 \\ );
289 \\ unreachable;
290 \\}
291 \\
292 ,
293 \\static zig_noreturn void exitMath(uint8_t arg0);
294 \\static zig_noreturn void exit(uint8_t arg0);
295 \\
296 \\static uint8_t exit__anon_0[6] = "{rax}";
297 \\static uint8_t exit__anon_1[6] = "{rdi}";
298 \\static uint8_t exit__anon_2[8] = "syscall";
299 \\
300 \\zig_noreturn void _start(void) {
301 \\ exitMath(1);
302 \\}
303 \\
304 \\static zig_noreturn void exitMath(uint8_t arg0) {
305 \\ uint8_t const __temp_0 = arg0 + 0;
306 \\ uint8_t const __temp_1 = __temp_0 - arg0;
307 \\ exit(__temp_1);
308 \\}
309 \\
310 \\static zig_noreturn void exit(uint8_t arg0) {
311 \\ uintptr_t const __temp_0 = (uintptr_t)arg0;
312 \\ register uintptr_t rax_constant __asm__("rax") = 231;
313 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
314 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
315 \\ zig_breakpoint();
316 \\ zig_unreachable();
317 \\}
318 \\195 \\
319 );196 );
320 ctx.h("header with single param function", linux_x64,197 ctx.h("header with single param function", linux_x64,
321 \\export fn start(a: u8) void{}198 \\export fn start(a: u8) void{}
322 ,199 ,
323 \\void start(uint8_t arg0);200 \\ZIG_EXTERN_C void start(uint8_t a0);
324 \\201 \\
325 );202 );
326 ctx.h("header with multiple param function", linux_x64,203 ctx.h("header with multiple param function", linux_x64,
327 \\export fn start(a: u8, b: u8, c: u8) void{}204 \\export fn start(a: u8, b: u8, c: u8) void{}
328 ,205 ,
329 \\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);
330 \\207 \\
331 );208 );
332 ctx.h("header with u32 param function", linux_x64,209 ctx.h("header with u32 param function", linux_x64,
333 \\export fn start(a: u32) void{}210 \\export fn start(a: u32) void{}
334 ,211 ,
335 \\void start(uint32_t arg0);212 \\ZIG_EXTERN_C void start(uint32_t a0);
336 \\213 \\
337 );214 );
338 ctx.h("header with usize param function", linux_x64,215 ctx.h("header with usize param function", linux_x64,
339 \\export fn start(a: usize) void{}216 \\export fn start(a: usize) void{}
340 ,217 ,
341 \\void start(uintptr_t arg0);218 \\ZIG_EXTERN_C void start(uintptr_t a0);
342 \\219 \\
343 );220 );
344 ctx.h("header with bool param function", linux_x64,221 ctx.h("header with bool param function", linux_x64,
345 \\export fn start(a: bool) void{}222 \\export fn start(a: bool) void{}
346 ,223 ,
347 \\void start(bool arg0);224 \\ZIG_EXTERN_C void start(bool a0);
348 \\225 \\
349 );226 );
350 ctx.h("header with noreturn function", linux_x64,227 ctx.h("header with noreturn function", linux_x64,
...@@ -352,7 +229,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -352,7 +229,7 @@ pub fn addCases(ctx: *TestContext) !void {
352 \\ unreachable;229 \\ unreachable;
353 \\}230 \\}
354 ,231 ,
355 \\zig_noreturn void start(void);232 \\ZIG_EXTERN_C zig_noreturn void start(void);
356 \\233 \\
357 );234 );
358 ctx.h("header with multiple functions", linux_x64,235 ctx.h("header with multiple functions", linux_x64,
...@@ -360,15 +237,15 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -360,15 +237,15 @@ pub fn addCases(ctx: *TestContext) !void {
360 \\export fn b() void{}237 \\export fn b() void{}
361 \\export fn c() void{}238 \\export fn c() void{}
362 ,239 ,
363 \\void a(void);240 \\ZIG_EXTERN_C void a(void);
364 \\void b(void);241 \\ZIG_EXTERN_C void b(void);
365 \\void c(void);242 \\ZIG_EXTERN_C void c(void);
366 \\243 \\
367 );244 );
368 ctx.h("header with multiple includes", linux_x64,245 ctx.h("header with multiple includes", linux_x64,
369 \\export fn start(a: u32, b: usize) void{}246 \\export fn start(a: u32, b: usize) void{}
370 ,247 ,
371 \\void start(uint32_t arg0, uintptr_t arg1);248 \\ZIG_EXTERN_C void start(uint32_t a0, uintptr_t a1);
372 \\249 \\
373 );250 );
374}251}