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
559559 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
560560 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
561561 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
562 "${CMAKE_SOURCE_DIR}/src/link/cbe.h"
562 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
563563 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
564564 "${CMAKE_SOURCE_DIR}/src/liveness.zig"
565565 "${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 {
100100
101101 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
102102 /// of this ArrayList. This ArrayList retains ownership of underlying memory.
103 /// Deprecated: use `moveToUnmanaged` which has different semantics.
103104 pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) {
104105 return .{ .items = self.items, .capacity = self.capacity };
105106 }
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
107117 /// The caller owns the returned memory. Empties this ArrayList.
108118 pub fn toOwnedSlice(self: *Self) Slice {
109119 const allocator = self.allocator;
......@@ -551,14 +561,6 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
551561 mem.copy(T, self.items[oldlen..], items);
552562 }
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
562564 /// Append a value to the list `n` times.
563565 /// Allocates more memory as necessary.
564566 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" {
11291131 }
11301132}
11311133
1132test "std.ArrayList(u8) implements outStream" {
1134test "std.ArrayList(u8) implements writer" {
11331135 var buffer = ArrayList(u8).init(std.testing.allocator);
11341136 defer buffer.deinit();
11351137
11361138 const x: i32 = 42;
11371139 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
11401142 testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items);
11411143}
lib/std/fs/file.zig+8
......@@ -459,6 +459,7 @@ pub const File = struct {
459459 return index;
460460 }
461461
462 /// See https://github.com/ziglang/zig/issues/7699
462463 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
463464 if (is_windows) {
464465 // TODO improve this to use ReadFileScatter
......@@ -479,6 +480,7 @@ pub const File = struct {
479480 /// is not an error condition.
480481 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
481482 /// order to handle partial reads from the underlying OS layer.
483 /// See https://github.com/ziglang/zig/issues/7699
482484 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
483485 if (iovecs.len == 0) return;
484486
......@@ -500,6 +502,7 @@ pub const File = struct {
500502 }
501503 }
502504
505 /// See https://github.com/ziglang/zig/issues/7699
503506 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize {
504507 if (is_windows) {
505508 // TODO improve this to use ReadFileScatter
......@@ -520,6 +523,7 @@ pub const File = struct {
520523 /// is not an error condition.
521524 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
522525 /// order to handle partial reads from the underlying OS layer.
526 /// See https://github.com/ziglang/zig/issues/7699
523527 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
524528 if (iovecs.len == 0) return;
525529
......@@ -582,6 +586,7 @@ pub const File = struct {
582586 }
583587 }
584588
589 /// See https://github.com/ziglang/zig/issues/7699
585590 pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize {
586591 if (is_windows) {
587592 // TODO improve this to use WriteFileScatter
......@@ -599,6 +604,7 @@ pub const File = struct {
599604
600605 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
601606 /// order to handle partial writes from the underlying OS layer.
607 /// See https://github.com/ziglang/zig/issues/7699
602608 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
603609 if (iovecs.len == 0) return;
604610
......@@ -615,6 +621,7 @@ pub const File = struct {
615621 }
616622 }
617623
624 /// See https://github.com/ziglang/zig/issues/7699
618625 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {
619626 if (is_windows) {
620627 // TODO improve this to use WriteFileScatter
......@@ -632,6 +639,7 @@ pub const File = struct {
632639
633640 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
634641 /// order to handle partial writes from the underlying OS layer.
642 /// See https://github.com/ziglang/zig/issues/7699
635643 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {
636644 if (iovecs.len == 0) return;
637645
src/Compilation.zig+84-66
......@@ -27,7 +27,6 @@ const Cache = @import("Cache.zig");
2727const stage1 = @import("stage1.zig");
2828const translate_c = @import("translate_c.zig");
2929const c_codegen = @import("codegen/c.zig");
30const c_link = @import("link/C.zig");
3130const ThreadPool = @import("ThreadPool.zig");
3231const WaitGroup = @import("WaitGroup.zig");
3332const libtsan = @import("libtsan.zig");
......@@ -138,8 +137,6 @@ emit_llvm_ir: ?EmitLoc,
138137emit_analysis: ?EmitLoc,
139138emit_docs: ?EmitLoc,
140139
141c_header: ?c_link.Header,
142
143140work_queue_wait_group: WaitGroup,
144141
145142pub const InnerError = Module.InnerError;
......@@ -164,6 +161,8 @@ pub const CSourceFile = struct {
164161const Job = union(enum) {
165162 /// Write the machine code for a Decl to the output file.
166163 codegen_decl: *Module.Decl,
164 /// Render the .h file snippet for the Decl.
165 emit_h_decl: *Module.Decl,
167166 /// The Decl needs to be analyzed and possibly export itself.
168167 /// It may have already be analyzed, or it may have been determined
169168 /// to be outdated; in this case perform semantic analysis again.
......@@ -866,9 +865,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
866865 .root_pkg = root_pkg,
867866 .root_scope = root_scope,
868867 .zig_cache_artifact_directory = zig_cache_artifact_directory,
868 .emit_h = options.emit_h,
869869 };
870870 break :blk module;
871 } else null;
871 } else blk: {
872 if (options.emit_h != null) return error.NoZigModuleForCHeader;
873 break :blk null;
874 };
872875 errdefer if (module) |zm| zm.deinit();
873876
874877 const error_return_tracing = !strip and switch (options.optimize_mode) {
......@@ -996,7 +999,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
996999 .local_cache_directory = options.local_cache_directory,
9971000 .global_cache_directory = options.global_cache_directory,
9981001 .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,
10001002 .emit_asm = options.emit_asm,
10011003 .emit_llvm_ir = options.emit_llvm_ir,
10021004 .emit_analysis = options.emit_analysis,
......@@ -1218,10 +1220,6 @@ pub fn destroy(self: *Compilation) void {
12181220 }
12191221 self.failed_c_objects.deinit(gpa);
12201222
1221 if (self.c_header) |*header| {
1222 header.deinit();
1223 }
1224
12251223 self.cache_parent.manifest_dir.close();
12261224 if (self.owned_link_dir) |*dir| dir.close();
12271225
......@@ -1315,9 +1313,14 @@ pub fn update(self: *Compilation) !void {
13151313
13161314 // This is needed before reading the error flags.
13171315 try self.bin_file.flush(self);
1318
13191316 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
13211324 // If there are any errors, we anticipate the source files being loaded
13221325 // to report error messages. Otherwise we unload all source files to save memory.
13231326 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
......@@ -1325,20 +1328,6 @@ pub fn update(self: *Compilation) !void {
13251328 module.root_scope.unload(self.gpa);
13261329 }
13271330 }
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 }
13421331}
13431332
13441333/// Having the file open for writing is problematic as far as executing the
......@@ -1357,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize {
13571346 var total: usize = self.failed_c_objects.items().len;
13581347
13591348 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() +
13611351 module.failed_exports.items().len +
13621352 module.failed_files.items().len +
13631353 @boolToInt(module.failed_root_src_file != null);
......@@ -1396,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
13961386 const source = try decl.scope.getSource(module);
13971387 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
13981388 }
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 }
13991395 for (module.failed_exports.items()) |entry| {
14001396 const decl = entry.key.owner_decl;
14011397 const err_msg = entry.value;
......@@ -1493,44 +1489,66 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14931489
14941490 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
14951491
1496 self.bin_file.updateDecl(module, decl) catch |err| {
1497 switch (err) {
1498 error.OutOfMemory => return error.OutOfMemory,
1499 error.AnalysisFail => {
1500 decl.analysis = .dependency_failure;
1501 },
1502 else => {
1503 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
1504 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1505 module.gpa,
1506 decl.src(),
1507 "unable to codegen: {s}",
1508 .{@errorName(err)},
1509 ));
1510 decl.analysis = .codegen_failure_retryable;
1511 },
1512 }
1513 return;
1492 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
1493 error.OutOfMemory => return error.OutOfMemory,
1494 error.AnalysisFail => {
1495 decl.analysis = .codegen_failure;
1496 continue;
1497 },
1498 else => {
1499 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
1500 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1501 module.gpa,
1502 decl.src(),
1503 "unable to codegen: {s}",
1504 .{@errorName(err)},
1505 ));
1506 decl.analysis = .codegen_failure_retryable;
1507 continue;
1508 },
15141509 };
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 c_codegen.generateHeader(self, module, header, decl) catch |err| switch (err) {
1518 error.OutOfMemory => return error.OutOfMemory,
1519 error.AnalysisFail => {
1520 decl.analysis = .dependency_failure;
1521 },
1522 else => {
1523 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
1524 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1525 module.gpa,
1526 decl.src(),
1527 "unable to generate C header: {s}",
1528 .{@errorName(err)},
1529 ));
1530 decl.analysis = .codegen_failure_retryable;
1531 },
1532 };
1533 }
1517 .sema_failure,
1518 .dependency_failure,
1519 .sema_failure_retryable,
1520 => continue,
1521
1522 // emit-h only requires semantic analysis of the Decl to be complete,
1523 // it does not depend on machine code generation to succeed.
1524 .codegen_failure, .codegen_failure_retryable, .complete => {
1525 if (build_options.omit_stage2)
1526 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
1527 const module = self.bin_file.options.module.?;
1528 const emit_loc = module.emit_h.?;
1529 const tv = decl.typed_value.most_recent.typed_value;
1530 const emit_h = decl.getEmitH(module);
1531 const fwd_decl = &emit_h.fwd_decl;
1532 fwd_decl.shrinkRetainingCapacity(0);
1533
1534 var dg: c_codegen.DeclGen = .{
1535 .module = module,
1536 .error_msg = null,
1537 .decl = decl,
1538 .fwd_decl = fwd_decl.toManaged(module.gpa),
1539 };
1540 defer dg.fwd_decl.deinit();
1541
1542 c_codegen.genHeader(&dg) catch |err| switch (err) {
1543 error.AnalysisFail => {
1544 try module.emit_h_failed_decls.put(module.gpa, decl, dg.error_msg.?);
1545 continue;
1546 },
1547 else => |e| return e,
1548 };
1549
1550 fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
1551 fwd_decl.shrink(module.gpa, fwd_decl.items.len);
15341552 },
15351553 },
15361554 .analyze_decl => |decl| {
......@@ -2998,9 +3016,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
29983016 man.hash.add(comp.bin_file.options.function_sections);
29993017 man.hash.add(comp.bin_file.options.is_test);
30003018 man.hash.add(comp.bin_file.options.emit != null);
3001 man.hash.add(comp.c_header != null);
3002 if (comp.c_header) |header| {
3003 man.hash.addEmitLoc(header.emit_loc.?);
3019 man.hash.add(mod.emit_h != null);
3020 if (mod.emit_h) |emit_h| {
3021 man.hash.addEmitLoc(emit_h);
30043022 }
30053023 man.hash.addOptionalEmitLoc(comp.emit_asm);
30063024 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
......@@ -3105,10 +3123,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
31053123 });
31063124 break :blk try directory.join(arena, &[_][]const u8{bin_basename});
31073125 } else "";
3108 if (comp.c_header != null) {
3126 if (mod.emit_h != null) {
31093127 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
31103128 }
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);
31123130 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
31133131 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
31143132 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
5757/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
5858/// a Decl can have a failed_decls entry but have analysis status of success.
5959failed_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) = .{},
6064/// Using a map here for consistency with the other fields here.
6165/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
6266failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
......@@ -94,6 +98,8 @@ stage1_flags: packed struct {
9498 reserved: u2 = 0,
9599} = .{},
96100
101emit_h: ?Compilation.EmitLoc,
102
97103pub const Export = struct {
98104 options: std.builtin.ExportOptions,
99105 /// Byte offset into the file that contains the export directive.
......@@ -114,6 +120,13 @@ pub const Export = struct {
114120 },
115121};
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
117130pub const Decl = struct {
118131 /// This name is relative to the containing namespace of the decl. It uses a null-termination
119132 /// 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 {
202215 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
203216 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;
206220 gpa.free(mem.spanZ(self.name));
207221 if (self.typedValueManaged()) |tvm| {
208222 tvm.deinit(gpa);
209223 }
210224 self.dependants.deinit(gpa);
211225 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 }
213233 }
214234
215235 pub fn src(self: Decl) usize {
......@@ -275,6 +295,12 @@ pub const Decl = struct {
275295 return self.scope.cast(Scope.Container).?.file_scope;
276296 }
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
278304 fn removeDependant(self: *Decl, other: *Decl) void {
279305 self.dependants.removeAssertDiscard(other);
280306 }
......@@ -284,6 +310,11 @@ pub const Decl = struct {
284310 }
285311};
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
287318/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
288319/// Extern functions do not have this data structure; they are represented by
289320/// the `Decl` only, with a `Value` tag of `extern_fn`.
......@@ -881,7 +912,7 @@ pub fn deinit(self: *Module) void {
881912 self.deletion_set.deinit(gpa);
882913
883914 for (self.decl_table.items()) |entry| {
884 entry.value.destroy(gpa);
915 entry.value.destroy(self);
885916 }
886917 self.decl_table.deinit(gpa);
887918
......@@ -890,6 +921,11 @@ pub fn deinit(self: *Module) void {
890921 }
891922 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
893929 for (self.failed_files.items()) |entry| {
894930 entry.value.destroy(gpa);
895931 }
......@@ -1148,6 +1184,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11481184 try self.comp.bin_file.allocateDeclIndexes(decl);
11491185 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
11511191 return type_changed;
11521192 };
11531193
......@@ -1267,6 +1307,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12671307 // increasing how many computations can be done in parallel.
12681308 try self.comp.bin_file.allocateDeclIndexes(decl);
12691309 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 }
12701313 } else if (!prev_is_inline and prev_type_has_bits) {
12711314 self.comp.bin_file.freeDecl(decl);
12721315 }
......@@ -1835,9 +1878,13 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
18351878 if (self.failed_decls.remove(decl)) |entry| {
18361879 entry.value.destroy(self.gpa);
18371880 }
1881 if (self.emit_h_failed_decls.remove(decl)) |entry| {
1882 entry.value.destroy(self.gpa);
1883 }
18381884 self.deleteDeclExports(decl);
18391885 self.comp.bin_file.freeDecl(decl);
1840 decl.destroy(self.gpa);
1886
1887 decl.destroy(self);
18411888}
18421889
18431890/// 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 {
19211968 if (self.failed_decls.remove(decl)) |entry| {
19221969 entry.value.destroy(self.gpa);
19231970 }
1971 if (self.emit_h_failed_decls.remove(decl)) |entry| {
1972 entry.value.destroy(self.gpa);
1973 }
19241974 decl.analysis = .outdated;
19251975}
19261976
19271977fn allocateNewDecl(
1928 self: *Module,
1978 mod: *Module,
19291979 scope: *Scope,
19301980 src_index: usize,
19311981 contents_hash: std.zig.SrcHash,
19321982) !*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
19341993 new_decl.* = .{
19351994 .name = "",
19361995 .scope = scope.namespace(),
......@@ -1939,18 +1998,18 @@ fn allocateNewDecl(
19391998 .analysis = .unreferenced,
19401999 .deletion_flag = false,
19412000 .contents_hash = contents_hash,
1942 .link = switch (self.comp.bin_file.tag) {
2001 .link = switch (mod.comp.bin_file.tag) {
19432002 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
19442003 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
19452004 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1946 .c => .{ .c = {} },
2005 .c => .{ .c = link.File.C.DeclBlock.empty },
19472006 .wasm => .{ .wasm = {} },
19482007 },
1949 .fn_link = switch (self.comp.bin_file.tag) {
2008 .fn_link = switch (mod.comp.bin_file.tag) {
19502009 .coff => .{ .coff = {} },
19512010 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
19522011 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
1953 .c => .{ .c = {} },
2012 .c => .{ .c = link.File.C.FnBlock.empty },
19542013 .wasm => .{ .wasm = null },
19552014 },
19562015 .generation = 0,
src/codegen/c.zig+504-456
......@@ -1,495 +1,532 @@
11const std = @import("std");
2const mem = std.mem;
3const log = std.log.scoped(.c);
4const Writer = std.ArrayList(u8).Writer;
25
36const link = @import("../link.zig");
47const Module = @import("../Module.zig");
58const Compilation = @import("../Compilation.zig");
6
79const Inst = @import("../ir.zig").Inst;
810const Value = @import("../value.zig").Value;
911const Type = @import("../type.zig").Type;
10
12const TypedValue = @import("../TypedValue.zig");
1113const C = link.File.C;
1214const Decl = Module.Decl;
13const mem = std.mem;
14const log = std.log.scoped(.c);
15const trace = @import("../tracy.zig").trace;
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 same
19/// output for any given input, sometimes resulting in broken identifiers.
20fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
21 return allocator.dupe(u8, name);
22}
19pub const CValue = union(enum) {
20 none: void,
21 /// Index into local_names
22 local: usize,
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(
27 ctx: *Context,
28 writer: Writer,
29 ty: Type,
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();
52 fn allocLocalValue(o: *Object) CValue {
53 const result = o.next_local_index;
54 o.next_local_index += 1;
55 return .{ .local = result };
4156 }
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) {
46 .Const => "const ",
47 .Mut => "",
48 };
49 try writer.print(" {s}{s}{s}", .{ const_prefix, name, suffix.items });
50}
64 fn indent(o: *Object) !void {
65 const indent_size = 4;
66 const indent_level = 1;
67 const indent_amt = indent_size * indent_level;
68 try o.code.writer().writeByteNTimes(' ', indent_amt);
69 }
5170
52fn renderType(
53 ctx: *Context,
54 writer: Writer,
55 t: Type,
56) error{ OutOfMemory, AnalysisFail }!void {
57 switch (t.zigTypeTag()) {
58 .NoReturn => {
59 try writer.writeAll("zig_noreturn void");
60 },
61 .Void => try writer.writeAll("void"),
62 .Bool => try writer.writeAll("bool"),
63 .Int => {
64 switch (t.tag()) {
65 .u8 => try writer.writeAll("uint8_t"),
66 .i8 => try writer.writeAll("int8_t"),
67 .u16 => try writer.writeAll("uint16_t"),
68 .i16 => try writer.writeAll("int16_t"),
69 .u32 => try writer.writeAll("uint32_t"),
70 .i32 => try writer.writeAll("int32_t"),
71 .u64 => try writer.writeAll("uint64_t"),
72 .i64 => try writer.writeAll("int64_t"),
73 .usize => try writer.writeAll("uintptr_t"),
74 .isize => try writer.writeAll("intptr_t"),
75 .c_short => try writer.writeAll("short"),
76 .c_ushort => try writer.writeAll("unsigned short"),
77 .c_int => try writer.writeAll("int"),
78 .c_uint => try writer.writeAll("unsigned int"),
79 .c_long => try writer.writeAll("long"),
80 .c_ulong => try writer.writeAll("unsigned long"),
81 .c_longlong => try writer.writeAll("long long"),
82 .c_ulonglong => try writer.writeAll("unsigned long long"),
83 .int_signed, .int_unsigned => {
84 const info = t.intInfo(ctx.target);
85 const sign_prefix = switch (info.signedness) {
86 .signed => "i",
87 .unsigned => "",
88 };
89 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
90 if (info.bits <= nbits) {
91 try writer.print("{s}int{d}_t", .{ sign_prefix, nbits });
92 break;
93 }
71 fn writeCValue(o: *Object, writer: Writer, c_value: CValue) !void {
72 switch (c_value) {
73 .none => unreachable,
74 .local => |i| return writer.print("t{d}", .{i}),
75 .local_ref => |i| return writer.print("&t{d}", .{i}),
76 .constant => |inst| return o.dg.renderValue(writer, inst.ty, inst.value().?),
77 .arg => |i| return writer.print("a{d}", .{i}),
78 .decl => |decl| return writer.writeAll(mem.span(decl.name)),
79 }
80 }
81
82 fn renderTypeAndName(
83 o: *Object,
84 writer: Writer,
85 ty: Type,
86 name: CValue,
87 mutability: Mutability,
88 ) error{ OutOfMemory, AnalysisFail }!void {
89 var suffix = std.ArrayList(u8).init(o.gpa);
90 defer suffix.deinit();
91
92 var render_ty = ty;
93 while (render_ty.zigTypeTag() == .Array) {
94 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
95 const c_len = render_ty.arrayLen() + sentinel_bit;
96 try suffix.writer().print("[{d}]", .{c_len});
97 render_ty = render_ty.elemType();
98 }
99
100 try o.dg.renderType(writer, render_ty);
101
102 const const_prefix = switch (mutability) {
103 .Const => "const ",
104 .Mut => "",
105 };
106 try writer.print(" {s}", .{const_prefix});
107 try o.writeCValue(writer, name);
108 try writer.writeAll(suffix.items);
109 }
110};
111
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});
94146 } 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});
96150 }
97151 },
98 else => unreachable,
99 }
100 },
101 .Pointer => {
102 if (t.isSlice()) {
103 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});
104 } else {
105 try renderType(ctx, writer, t.elemType());
106 try writer.writeAll(" *");
107 if (t.isConstPtr()) {
108 try writer.writeAll("const ");
152 .function => {
153 const func = val.castTag(.function).?.data;
154 try writer.print("{s}", .{func.owner_decl.name});
155 },
156 .extern_fn => {
157 const decl = val.castTag(.extern_fn).?.data;
158 try writer.print("{s}", .{decl.name});
159 },
160 else => |e| return dg.fail(
161 dg.decl.src(),
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 },
109195 }
110 if (t.isVolatilePtr()) {
111 try writer.writeAll("volatile ");
196 },
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(", ");
112219 }
220 try dg.renderType(w, tv.ty.fnParamType(index));
221 try w.print(" a{d}", .{index});
113222 }
114 },
115 .Array => {
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 }),
223 }
224 try w.writeByte(')');
122225 }
123}
124226
125fn renderValue(
126 ctx: *Context,
127 writer: Writer,
128 t: Type,
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});
227 fn renderType(dg: *DeclGen, w: Writer, t: Type) error{ OutOfMemory, AnalysisFail }!void {
228 switch (t.zigTypeTag()) {
229 .NoReturn => {
230 try w.writeAll("zig_noreturn void");
156231 },
157 .extern_fn => {
158 const decl = val.castTag(.extern_fn).?.data;
159 try writer.print("{s}", .{decl.name});
232 .Void => try w.writeAll("void"),
233 .Bool => try w.writeAll("bool"),
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 }
160271 },
161 else => |e| return ctx.fail(
162 ctx.decl.src(),
163 "TODO: C backend: implement Pointer value {s}",
164 .{@tagName(e)},
165 ),
166 },
167 .Array => {
168 // First try specific tag representations for more efficiency.
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);
272 .Pointer => {
273 if (t.isSlice()) {
274 return dg.fail(dg.decl.src(), "TODO: C backend: implement slices", .{});
275 } else {
276 try dg.renderType(w, t.elemType());
277 try w.writeAll(" *");
278 if (t.isConstPtr()) {
279 try w.writeAll("const ");
186280 }
187 if (t.sentinel()) |sentinel_val| {
188 if (index != 0) try writer.writeAll(",");
189 try renderValue(ctx, writer, elem_ty, sentinel_val);
281 if (t.isVolatilePtr()) {
282 try w.writeAll("volatile ");
190283 }
191 try writer.writeAll("}");
192 },
193 }
194 },
195 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{
196 @tagName(e),
197 }),
284 }
285 },
286 .Array => {
287 try dg.renderType(w, t.elemType());
288 try w.writeAll(" *");
289 },
290 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
291 @tagName(e),
292 }),
293 }
198294 }
199}
200295
201fn renderFunctionSignature(
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: {
296 fn functionIsGlobal(dg: *DeclGen, tv: TypedValue) bool {
209297 switch (tv.val.tag()) {
210 .extern_fn => break :blk true,
298 .extern_fn => return true,
211299 .function => {
212300 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);
214302 },
215303 else => unreachable,
216304 }
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 }
240305 }
241 try writer.writeByte(')');
242}
306};
243307
244fn indent(file: *C) !void {
245 const indent_size = 4;
246 const indent_level = 1;
247 const indent_amt = indent_size * indent_level;
248 try file.main.writer().writeByteNTimes(' ', indent_amt);
249}
308pub fn genDecl(o: *Object) !void {
309 const tracy = trace(@src());
310 defer tracy.end();
250311
251pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
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 }
312 const tv = o.dg.decl.typed_value.most_recent.typed_value;
270313
271314 if (tv.val.castTag(.function)) |func_payload| {
272 const writer = file.main.writer();
273 try renderFunctionSignature(&ctx, writer, decl);
274
275 try writer.writeAll(" {");
315 const is_global = o.dg.functionIsGlobal(tv);
316 const fwd_decl_writer = o.dg.fwd_decl.writer();
317 if (is_global) {
318 try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
319 }
320 try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
321 try fwd_decl_writer.writeAll(";\n");
276322
277323 const func: *Module.Fn = func_payload.data;
278324 const instructions = func.body.instructions;
279 if (instructions.len > 0) {
280 try writer.writeAll("\n");
281 for (instructions) |inst| {
282 if (switch (inst.tag) {
283 .add => try genBinOp(&ctx, file, inst.castTag(.add).?, "+"),
284 .alloc => try genAlloc(&ctx, file, inst.castTag(.alloc).?),
285 .arg => try genArg(&ctx),
286 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
287 .block => try genBlock(&ctx, file, inst.castTag(.block).?),
288 .bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),
289 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
290 .call => try genCall(&ctx, file, inst.castTag(.call).?),
291 .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
292 .cmp_gt => try genBinOp(&ctx, file, inst.castTag(.cmp_gt).?, ">"),
293 .cmp_gte => try genBinOp(&ctx, file, inst.castTag(.cmp_gte).?, ">="),
294 .cmp_lt => try genBinOp(&ctx, file, inst.castTag(.cmp_lt).?, "<"),
295 .cmp_lte => try genBinOp(&ctx, file, inst.castTag(.cmp_lte).?, "<="),
296 .cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),
297 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
298 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
299 .load => try genLoad(&ctx, file, inst.castTag(.load).?),
300 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),
301 .retvoid => try genRetVoid(file),
302 .store => try genStore(&ctx, file, inst.castTag(.store).?),
303 .sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"),
304 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
305 else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
306 }) |name| {
307 try ctx.inst_map.putNoClobber(inst, name);
308 }
325 const writer = o.code.writer();
326 try writer.writeAll("\n");
327 try o.dg.renderFunctionSignature(writer, is_global);
328 if (instructions.len == 0) {
329 try writer.writeAll(" {}\n");
330 return;
331 }
332
333 try writer.writeAll(" {");
334
335 try writer.writeAll("\n");
336 for (instructions) |inst| {
337 const result_value = switch (inst.tag) {
338 .add => try genBinOp(o, inst.castTag(.add).?, " + "),
339 .alloc => try genAlloc(o, inst.castTag(.alloc).?),
340 .arg => genArg(o),
341 .assembly => try genAsm(o, inst.castTag(.assembly).?),
342 .block => try genBlock(o, inst.castTag(.block).?),
343 .bitcast => try genBitcast(o, inst.castTag(.bitcast).?),
344 .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?),
345 .call => try genCall(o, inst.castTag(.call).?),
346 .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "),
347 .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "),
348 .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "),
349 .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "),
350 .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "),
351 .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "),
352 .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?),
353 .intcast => try genIntCast(o, inst.castTag(.intcast).?),
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),
309365 }
310366 }
311367
312 try writer.writeAll("}\n\n");
368 try writer.writeAll("}\n");
313369 } else if (tv.val.tag() == .extern_fn) {
314 return; // handled when referenced
370 const writer = o.code.writer();
371 try writer.writeAll("ZIG_EXTERN_C ");
372 try o.dg.renderFunctionSignature(writer, true);
373 try writer.writeAll(";\n");
315374 } else {
316 const writer = file.constants.writer();
375 const writer = o.code.writer();
317376 try writer.writeAll("static ");
318377
319378 // TODO ask the Decl if it is const
320379 // 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
324384 try writer.writeAll(" = ");
325 try renderValue(&ctx, writer, tv.ty, tv.val);
385 try o.dg.renderValue(writer, tv.ty, tv.val);
326386 try writer.writeAll(";\n");
327387 }
328388}
329389
330pub fn generateHeader(
331 comp: *Compilation,
332 module: *Module,
333 header: *C.Header,
334 decl: *Decl,
335) error{ AnalysisFail, OutOfMemory }!void {
336 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
390pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
391 const tracy = trace(@src());
392 defer tracy.end();
393
394 const tv = dg.decl.typed_value.most_recent.typed_value;
395 const writer = dg.fwd_decl.writer();
396
397 switch (tv.ty.zigTypeTag()) {
337398 .Fn => {
338 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);
339 defer inst_map.deinit();
340
341 var arena = std.heap.ArenaAllocator.init(comp.gpa);
342 defer arena.deinit();
343
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");
399 const is_global = dg.functionIsGlobal(tv);
400 if (is_global) {
401 try writer.writeAll("ZIG_EXTERN_C ");
402 }
403 try dg.renderFunctionSignature(writer, is_global);
404 try dg.fwd_decl.appendSlice(";\n");
360405 },
361406 else => {},
362407 }
363408}
364409
365const Context = struct {
366 decl: *Decl,
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();
410fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {
411 const writer = o.code.writer();
403412
404413 // First line: the variable used as data storage.
405 try indent(file);
406 const local_name = try ctx.name();
414 try o.indent();
407415 const elem_type = alloc.base.ty.elemType();
408416 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);
410418 try writer.writeAll(";\n");
411419
412 // Second line: a pointer to it so that we can refer to it as the allocation.
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 return CValue{ .local_ref = local.local };
420421}
421422
422fn genArg(ctx: *Context) !?[]u8 {
423 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex});
424 ctx.argdex += 1;
425 return name;
423fn genArg(o: *Object) CValue {
424 const i = o.next_arg_index;
425 o.next_arg_index += 1;
426 return .{ .arg = i };
426427}
427428
428fn genRetVoid(file: *C) !?[]u8 {
429 try indent(file);
430 try file.main.writer().print("return;\n", .{});
431 return null;
429fn genRetVoid(o: *Object) !CValue {
430 try o.indent();
431 try o.code.writer().print("return;\n", .{});
432 return CValue.none;
432433}
433434
434fn genLoad(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
435 const operand = try ctx.resolveInst(inst.operand);
436 const writer = file.main.writer();
437 try indent(file);
438 const local_name = try ctx.name();
439 try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
440 try writer.print(" = *{s};\n", .{operand});
441 return local_name;
435fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
436 const operand = try o.resolveInst(inst.operand);
437 const writer = o.code.writer();
438 try o.indent();
439 const local = try o.allocLocal(inst.base.ty, .Const);
440 switch (operand) {
441 .local_ref => |i| {
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;
442454}
443455
444fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
445 try indent(file);
446 const writer = file.main.writer();
447 try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)});
448 return null;
456fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {
457 const operand = try o.resolveInst(inst.operand);
458 try o.indent();
459 const writer = o.code.writer();
460 try writer.writeAll("return ");
461 try o.writeCValue(writer, operand);
462 try writer.writeAll(";\n");
463 return CValue.none;
449464}
450465
451fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
466fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue {
452467 if (inst.base.isUnused())
453 return null;
454 try indent(file);
455 const writer = file.main.writer();
456 const name = try ctx.name();
457 const from = try ctx.resolveInst(inst.operand);
468 return CValue.none;
469
470 const from = try o.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);
460475 try writer.writeAll(" = (");
461 try renderType(ctx, writer, inst.base.ty);
462 try writer.print("){s};\n", .{from});
463 return name;
476 try o.dg.renderType(writer, inst.base.ty);
477 try writer.writeAll(")");
478 try o.writeCValue(writer, from);
479 try writer.writeAll(";\n");
480 return local;
464481}
465482
466fn genStore(ctx: *Context, file: *C, inst: *Inst.BinOp) !?[]u8 {
483fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
467484 // *a = b;
468 try indent(file);
469 const writer = file.main.writer();
470 const dest_ptr_name = try ctx.resolveInst(inst.lhs);
471 const src_val_name = try ctx.resolveInst(inst.rhs);
472 try writer.print("*{s} = {s};\n", .{ dest_ptr_name, src_val_name });
473 return null;
485 const dest_ptr = try o.resolveInst(inst.lhs);
486 const src_val = try o.resolveInst(inst.rhs);
487
488 try o.indent();
489 const writer = o.code.writer();
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;
474507}
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 {
477510 if (inst.base.isUnused())
478 return null;
479 try indent(file);
480 const lhs = try ctx.resolveInst(inst.lhs);
481 const rhs = try ctx.resolveInst(inst.rhs);
482 const writer = file.main.writer();
483 const name = try ctx.name();
484 try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);
485 try writer.print(" = {s} {s} {s};\n", .{ lhs, operator, rhs });
486 return name;
511 return CValue.none;
512
513 const lhs = try o.resolveInst(inst.lhs);
514 const rhs = try o.resolveInst(inst.rhs);
515
516 try o.indent();
517 const writer = o.code.writer();
518 const local = try o.allocLocal(inst.base.ty, .Const);
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;
487527}
488528
489fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
490 try indent(file);
491 const writer = file.main.writer();
492 const header = file.header.buf.writer();
529fn genCall(o: *Object, inst: *Inst.Call) !CValue {
493530 if (inst.func.castTag(.constant)) |func_inst| {
494531 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|
495532 extern_fn.data
......@@ -501,23 +538,19 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
501538 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
502539 const ret_ty = fn_ty.fnReturnType();
503540 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();
505545 if (unused_result) {
506546 if (ret_ty.hasCodeGenBits()) {
507547 try writer.print("(void)", .{});
508548 }
509549 } else {
510 const local_name = try ctx.name();
511 try renderTypeAndName(ctx, writer, ret_ty, local_name, .Const);
550 result_local = try o.allocLocal(ret_ty, .Const);
512551 try writer.writeAll(" = ");
513 result_name = local_name;
514552 }
515553 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 }
521554 try writer.print("{s}(", .{fn_name});
522555 if (inst.args.len != 0) {
523556 for (inst.args) |arg, i| {
......@@ -525,87 +558,98 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
525558 try writer.writeAll(", ");
526559 }
527560 if (arg.value()) |val| {
528 try renderValue(ctx, writer, arg.ty, val);
561 try o.dg.renderValue(writer, arg.ty, val);
529562 } else {
530 const val = try ctx.resolveInst(arg);
531 try writer.print("{s}", .{val});
563 const val = try o.resolveInst(arg);
564 try o.writeCValue(writer, val);
532565 }
533566 }
534567 }
535568 try writer.writeAll(");\n");
536 return result_name;
569 return result_local;
537570 } 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", .{});
539572 }
540573}
541574
542fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
575fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue {
543576 // TODO emit #line directive here with line number and filename
544 return null;
577 return CValue.none;
545578}
546579
547fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {
548 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});
580fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
581 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement blocks", .{});
549582}
550583
551fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
552 const writer = file.main.writer();
553 try indent(file);
554 const local_name = try ctx.name();
555 const operand = try ctx.resolveInst(inst.operand);
556 try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
584fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
585 const operand = try o.resolveInst(inst.operand);
586
587 const writer = o.code.writer();
588 try o.indent();
557589 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
590 const local = try o.allocLocal(inst.base.ty, .Const);
558591 try writer.writeAll(" = (");
559 try renderType(ctx, writer, inst.base.ty);
560 try writer.print("){s};\n", .{operand});
561 } else {
592 try o.dg.renderType(writer, inst.base.ty);
593
594 try writer.writeAll(")");
595 try o.writeCValue(writer, operand);
562596 try writer.writeAll(";\n");
563 try indent(file);
564 try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name });
597 return local;
565598 }
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;
567613}
568614
569fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
570 try indent(file);
571 try file.main.writer().writeAll("zig_breakpoint();\n");
572 return null;
615fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue {
616 try o.indent();
617 try o.code.writer().writeAll("zig_breakpoint();\n");
618 return CValue.none;
573619}
574620
575fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {
576 try indent(file);
577 try file.main.writer().writeAll("zig_unreachable();\n");
578 return null;
621fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue {
622 try o.indent();
623 try o.code.writer().writeAll("zig_unreachable();\n");
624 return CValue.none;
579625}
580626
581fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
582 try indent(file);
583 const writer = file.main.writer();
627fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
628 if (as.base.isUnused() and !as.is_volatile)
629 return CValue.none;
630
631 const writer = o.code.writer();
584632 for (as.inputs) |i, index| {
585633 if (i[0] == '{' and i[i.len - 1] == '}') {
586634 const reg = i[1 .. i.len - 1];
587635 const arg = as.args[index];
636 const arg_c_value = try o.resolveInst(arg);
637 try o.indent();
588638 try writer.writeAll("register ");
589 try renderType(ctx, writer, arg.ty);
639 try o.dg.renderType(writer, arg.ty);
640
590641 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
591 // TODO merge constant handling into inst_map as well
592 if (arg.castTag(.constant)) |c| {
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 }
642 try o.writeCValue(writer, arg_c_value);
643 try writer.writeAll(";\n");
602644 } 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", .{});
604646 }
605647 }
606 try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
607 if (as.output) |o| {
608 return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{});
648 try o.indent();
649 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
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", .{});
609653 }
610654 if (as.inputs.len > 0) {
611655 if (as.output == null) {
......@@ -619,7 +663,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
619663 if (index > 0) {
620664 try writer.writeAll(", ");
621665 }
622 try writer.print("\"\"({s}_constant)", .{reg});
666 try writer.print("\"r\"({s}_constant)", .{reg});
623667 } else {
624668 // This is blocked by the earlier test
625669 unreachable;
......@@ -627,5 +671,9 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
627671 }
628672 }
629673 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", .{});
631679}
src/link.zig+10-7
......@@ -130,7 +130,7 @@ pub const File = struct {
130130 elf: Elf.TextBlock,
131131 coff: Coff.TextBlock,
132132 macho: MachO.TextBlock,
133 c: void,
133 c: C.DeclBlock,
134134 wasm: void,
135135 };
136136
......@@ -138,7 +138,7 @@ pub const File = struct {
138138 elf: Elf.SrcFn,
139139 coff: Coff.SrcFn,
140140 macho: MachO.SrcFn,
141 c: void,
141 c: C.FnBlock,
142142 wasm: ?Wasm.FnData,
143143 };
144144
......@@ -301,7 +301,8 @@ pub const File = struct {
301301 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
302302 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
303303 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
304 .c, .wasm => {},
304 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
305 .wasm => {},
305306 }
306307 }
307308
......@@ -312,7 +313,8 @@ pub const File = struct {
312313 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
313314 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
314315 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
315 .c, .wasm => {},
316 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
317 .wasm => {},
316318 }
317319 }
318320
......@@ -407,12 +409,13 @@ pub const File = struct {
407409 }
408410 }
409411
412 /// Called when a Decl is deleted from the Module.
410413 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
411414 switch (base.tag) {
412415 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
413416 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
414417 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
415 .c => unreachable,
418 .c => @fieldParentPtr(C, "base", base).freeDecl(decl),
416419 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
417420 }
418421 }
......@@ -432,14 +435,14 @@ pub const File = struct {
432435 pub fn updateDeclExports(
433436 base: *File,
434437 module: *Module,
435 decl: *const Module.Decl,
438 decl: *Module.Decl,
436439 exports: []const *Module.Export,
437440 ) !void {
438441 switch (base.tag) {
439442 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
440443 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
441444 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
442 .c => return {},
445 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
443446 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
444447 }
445448 }
src/link/C.zig+175-64
......@@ -8,46 +8,31 @@ const fs = std.fs;
88const codegen = @import("../codegen/c.zig");
99const link = @import("../link.zig");
1010const trace = @import("../tracy.zig").trace;
11const File = link.File;
1211const 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 {
17 buf: std.ArrayList(u8),
18 emit_loc: ?Compilation.EmitLoc,
16base: link.File,
1917
20 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
21 return .{
22 .buf = std.ArrayList(u8).init(allocator),
23 .emit_loc = emit_loc,
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 }
18/// Per-declaration data. For functions this is the body, and
19/// the forward declaration is stored in the FnBlock.
20pub const DeclBlock = struct {
21 code: std.ArrayListUnmanaged(u8),
3622
37 pub fn deinit(self: *Header) void {
38 self.buf.deinit();
39 self.* = undefined;
40 }
23 pub const empty: DeclBlock = .{
24 .code = .{},
25 };
4126};
4227
43base: File,
44
45header: Header,
46constants: std.ArrayList(u8),
47main: std.ArrayList(u8),
28/// Per-function data.
29pub const FnBlock = struct {
30 fwd_decl: std.ArrayListUnmanaged(u8),
4831
49called: std.StringHashMap(void),
50error_msg: *Compilation.ErrorMsg = undefined,
32 pub const empty: FnBlock = .{
33 .fwd_decl = .{},
34 };
35};
5136
5237pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
5338 assert(options.object_format == .c);
......@@ -55,7 +40,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
5540 if (options.use_llvm) return error.LLVMHasNoCBackend;
5641 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 });
5948 errdefer file.close();
6049
6150 var c_file = try allocator.create(C);
......@@ -68,34 +57,69 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
6857 .file = file,
6958 .allocator = allocator,
7059 },
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),
7560 };
7661
7762 return c_file;
7863}
7964
80pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
81 self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);
82 return error.AnalysisFail;
65pub fn deinit(self: *C) void {
66 const module = self.base.options.module orelse return;
67 for (module.decl_table.items()) |entry| {
68 self.freeDecl(entry.value);
69 }
8370}
8471
85pub fn deinit(self: *C) void {
86 self.main.deinit();
87 self.header.deinit();
88 self.constants.deinit();
89 self.called.deinit();
72pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
73
74pub fn freeDecl(self: *C, decl: *Module.Decl) void {
75 decl.link.c.code.deinit(self.base.allocator);
76 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);
9077}
9178
9279pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
93 codegen.generate(self, module, decl) catch |err| {
94 if (err == error.AnalysisFail) {
95 try module.failed_decls.put(module.gpa, decl, self.error_msg);
96 }
97 return err;
80 const tracy = trace(@src());
81 defer tracy.end();
82
83 const fwd_decl = &decl.fn_link.c.fwd_decl;
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,
98109 };
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);
99123}
100124
101125pub fn flush(self: *C, comp: *Compilation) !void {
......@@ -106,21 +130,108 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
106130 const tracy = trace(@src());
107131 defer tracy.end();
108132
109 const writer = self.base.file.?.writer();
110 try self.header.flush(writer);
111 if (self.header.buf.items.len > 0) {
112 try writer.writeByte('\n');
113 }
114 if (self.constants.items.len > 0) {
115 try writer.print("{s}\n", .{self.constants.items});
133 const module = self.base.options.module.?;
134
135 // This code path happens exclusively with -ofmt=c. The flush logic for
136 // emit-h is in `flushEmitH` below.
137
138 // We collect a list of buffers to write, and write them all at once with pwritev 😎
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;
116170 }
117 if (self.main.items.len > 1) {
118 const last_two = self.main.items[self.main.items.len - 2 ..];
119 if (std.mem.eql(u8, last_two, "\n\n")) {
120 self.main.items.len -= 1;
171
172 // Now the function bodies.
173 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
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;
121184 }
122185 }
123 try writer.writeAll(self.main.items);
124 self.base.file.?.close();
125 self.base.file = null;
186
187 const file = self.base.file.?;
188 try file.setEndPos(file_size);
189 try file.pwritevAll(all_buffers.items, 0);
126190}
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
1313const ThreadPool = @import("ThreadPool.zig");
1414const CrossTarget = std.zig.CrossTarget;
1515
16const c_header = @embedFile("link/cbe.h");
16const zig_h = link.File.C.zig_h;
1717
1818test "self-hosted" {
1919 var ctx = TestContext.init();
......@@ -324,11 +324,11 @@ pub const TestContext = struct {
324324 }
325325
326326 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);
328328 }
329329
330330 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);
332332 }
333333
334334 pub fn addCompareOutput(
......@@ -700,11 +700,12 @@ pub const TestContext = struct {
700700 },
701701 }
702702 }
703 if (comp.bin_file.cast(link.File.C)) |c_file| {
704 std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{
705 c_file.main.items,
706 });
707 }
703 // TODO print generated C code
704 //if (comp.bin_file.cast(link.File.C)) |c_file| {
705 // std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{
706 // c_file.main.items,
707 // });
708 //}
708709 std.debug.print("Test failed.\n", .{});
709710 std.process.exit(1);
710711 }
test/stage2/cbe.zig+114-237
......@@ -22,15 +22,107 @@ pub fn addCases(ctx: *TestContext) !void {
2222 , "hello world!" ++ std.cstr.line_sep);
2323
2424 // Now change the message only
25 // TODO fix C backend not supporting updates
26 // https://github.com/ziglang/zig/issues/7589
27 //case.addCompareOutput(
28 // \\extern fn puts(s: [*:0]const u8) c_int;
29 // \\export fn main() c_int {
30 // \\ _ = puts("yo");
31 // \\ return 0;
32 // \\}
33 //, "yo" ++ std.cstr.line_sep);
25 case.addCompareOutput(
26 \\extern fn puts(s: [*:0]const u8) c_int;
27 \\export fn main() c_int {
28 \\ _ = puts("yo");
29 \\ return 0;
30 \\}
31 , "yo" ++ std.cstr.line_sep);
32 }
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 , "");
34126 }
35127
36128 {
......@@ -88,6 +180,8 @@ pub fn addCases(ctx: *TestContext) !void {
88180 \\ unreachable;
89181 \\}
90182 ,
183 \\ZIG_EXTERN_C zig_noreturn void _start(void);
184 \\
91185 \\zig_noreturn void _start(void) {
92186 \\ zig_breakpoint();
93187 \\ zig_unreachable();
......@@ -97,254 +191,37 @@ pub fn addCases(ctx: *TestContext) !void {
97191 ctx.h("simple header", linux_x64,
98192 \\export fn start() void{}
99193 ,
100 \\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 \\}
194 \\ZIG_EXTERN_C void start(void);
318195 \\
319196 );
320197 ctx.h("header with single param function", linux_x64,
321198 \\export fn start(a: u8) void{}
322199 ,
323 \\void start(uint8_t arg0);
200 \\ZIG_EXTERN_C void start(uint8_t a0);
324201 \\
325202 );
326203 ctx.h("header with multiple param function", linux_x64,
327204 \\export fn start(a: u8, b: u8, c: u8) void{}
328205 ,
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);
330207 \\
331208 );
332209 ctx.h("header with u32 param function", linux_x64,
333210 \\export fn start(a: u32) void{}
334211 ,
335 \\void start(uint32_t arg0);
212 \\ZIG_EXTERN_C void start(uint32_t a0);
336213 \\
337214 );
338215 ctx.h("header with usize param function", linux_x64,
339216 \\export fn start(a: usize) void{}
340217 ,
341 \\void start(uintptr_t arg0);
218 \\ZIG_EXTERN_C void start(uintptr_t a0);
342219 \\
343220 );
344221 ctx.h("header with bool param function", linux_x64,
345222 \\export fn start(a: bool) void{}
346223 ,
347 \\void start(bool arg0);
224 \\ZIG_EXTERN_C void start(bool a0);
348225 \\
349226 );
350227 ctx.h("header with noreturn function", linux_x64,
......@@ -352,7 +229,7 @@ pub fn addCases(ctx: *TestContext) !void {
352229 \\ unreachable;
353230 \\}
354231 ,
355 \\zig_noreturn void start(void);
232 \\ZIG_EXTERN_C zig_noreturn void start(void);
356233 \\
357234 );
358235 ctx.h("header with multiple functions", linux_x64,
......@@ -360,15 +237,15 @@ pub fn addCases(ctx: *TestContext) !void {
360237 \\export fn b() void{}
361238 \\export fn c() void{}
362239 ,
363 \\void a(void);
364 \\void b(void);
365 \\void c(void);
240 \\ZIG_EXTERN_C void a(void);
241 \\ZIG_EXTERN_C void b(void);
242 \\ZIG_EXTERN_C void c(void);
366243 \\
367244 );
368245 ctx.h("header with multiple includes", linux_x64,
369246 \\export fn start(a: u32, b: usize) void{}
370247 ,
371 \\void start(uint32_t arg0, uintptr_t arg1);
248 \\ZIG_EXTERN_C void start(uint32_t a0, uintptr_t a1);
372249 \\
373250 );
374251}