authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-05 11:08:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-05 17:41:14-07:00
log7b8cede61fc20c137aca4e02425536bfc9a5a400
tree9533e4e3afb63e23ab5a42ecddb18b9998ec1237
parent9360e5887ce0bf0ce204eb49f0d0b253348ef557

stage2: rework the C backend

* std.ArrayList gains `moveToUnmanaged` and dead code `ArrayListUnmanaged.appendWrite` is deleted. * emit_h state is attached to Module rather than Compilation. * remove the implementation of emit-h because it did not properly integrate with incremental compilation. I will re-implement it in a follow-up commit. * Compilation: use the .codegen_failure tag rather than .dependency_failure tag for when `bin_file.updateDecl` fails. C backend: * Use a CValue tagged union instead of strings for C values. * Cleanly separate state into Object and DeclGen: - Object is present only when generating a .c file - DeclGen is present for both generating a .c and .h * Move some functions into their respective Object/DeclGen namespace. * Forward decls are managed by the incremental compilation frontend; C backend no longer renders function signatures based on callsites. For simplicity, all functions always get forward decls. * Constants are managed by the incremental compilation frontend. C backend no longer has a "constants" section. * Participate in incremental compilation. Each Decl gets an ArrayList for its generated C code and it is updated when the Decl is updated. During flush(), all these are joined together in the output file. * The new CValue tagged union is used to clean up using of assigning to locals without an additional pointer local. * Fix bug with bitcast of non-pointers making the memcpy destination immutable.

10 files changed, 703 insertions(+), 655 deletions(-)

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}
src/Compilation.zig+11-47
...@@ -138,8 +138,6 @@ emit_llvm_ir: ?EmitLoc,...@@ -138,8 +138,6 @@ emit_llvm_ir: ?EmitLoc,
138emit_analysis: ?EmitLoc,138emit_analysis: ?EmitLoc,
139emit_docs: ?EmitLoc,139emit_docs: ?EmitLoc,
140140
141c_header: ?c_link.Header,
142
143work_queue_wait_group: WaitGroup,141work_queue_wait_group: WaitGroup,
144142
145pub const InnerError = Module.InnerError;143pub const InnerError = Module.InnerError;
...@@ -866,9 +864,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -866,9 +864,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
866 .root_pkg = root_pkg,864 .root_pkg = root_pkg,
867 .root_scope = root_scope,865 .root_scope = root_scope,
868 .zig_cache_artifact_directory = zig_cache_artifact_directory,866 .zig_cache_artifact_directory = zig_cache_artifact_directory,
867 .emit_h = options.emit_h,
869 };868 };
870 break :blk module;869 break :blk module;
871 } else null;870 } else blk: {
871 if (options.emit_h != null) return error.NoZigModuleForCHeader;
872 break :blk null;
873 };
872 errdefer if (module) |zm| zm.deinit();874 errdefer if (module) |zm| zm.deinit();
873875
874 const error_return_tracing = !strip and switch (options.optimize_mode) {876 const error_return_tracing = !strip and switch (options.optimize_mode) {
...@@ -996,7 +998,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -996,7 +998,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
996 .local_cache_directory = options.local_cache_directory,998 .local_cache_directory = options.local_cache_directory,
997 .global_cache_directory = options.global_cache_directory,999 .global_cache_directory = options.global_cache_directory,
998 .bin_file = bin_file,1000 .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,1001 .emit_asm = options.emit_asm,
1001 .emit_llvm_ir = options.emit_llvm_ir,1002 .emit_llvm_ir = options.emit_llvm_ir,
1002 .emit_analysis = options.emit_analysis,1003 .emit_analysis = options.emit_analysis,
...@@ -1218,10 +1219,6 @@ pub fn destroy(self: *Compilation) void {...@@ -1218,10 +1219,6 @@ pub fn destroy(self: *Compilation) void {
1218 }1219 }
1219 self.failed_c_objects.deinit(gpa);1220 self.failed_c_objects.deinit(gpa);
12201221
1221 if (self.c_header) |*header| {
1222 header.deinit();
1223 }
1224
1225 self.cache_parent.manifest_dir.close();1222 self.cache_parent.manifest_dir.close();
1226 if (self.owned_link_dir) |*dir| dir.close();1223 if (self.owned_link_dir) |*dir| dir.close();
12271224
...@@ -1325,20 +1322,6 @@ pub fn update(self: *Compilation) !void {...@@ -1325,20 +1322,6 @@ pub fn update(self: *Compilation) !void {
1325 module.root_scope.unload(self.gpa);1322 module.root_scope.unload(self.gpa);
1326 }1323 }
1327 }1324 }
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}1325}
13431326
1344/// Having the file open for writing is problematic as far as executing the1327/// Having the file open for writing is problematic as far as executing the
...@@ -1497,7 +1480,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1497,7 +1480,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1497 switch (err) {1480 switch (err) {
1498 error.OutOfMemory => return error.OutOfMemory,1481 error.OutOfMemory => return error.OutOfMemory,
1499 error.AnalysisFail => {1482 error.AnalysisFail => {
1500 decl.analysis = .dependency_failure;1483 decl.analysis = .codegen_failure;
1501 },1484 },
1502 else => {1485 else => {
1503 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);1486 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
...@@ -1512,25 +1495,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1512,25 +1495,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1512 }1495 }
1513 return;1496 return;
1514 };1497 };
1515
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 }
1534 },1498 },
1535 },1499 },
1536 .analyze_decl => |decl| {1500 .analyze_decl => |decl| {
...@@ -2998,9 +2962,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -2998,9 +2962,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
2998 man.hash.add(comp.bin_file.options.function_sections);2962 man.hash.add(comp.bin_file.options.function_sections);
2999 man.hash.add(comp.bin_file.options.is_test);2963 man.hash.add(comp.bin_file.options.is_test);
3000 man.hash.add(comp.bin_file.options.emit != null);2964 man.hash.add(comp.bin_file.options.emit != null);
3001 man.hash.add(comp.c_header != null);2965 man.hash.add(mod.emit_h != null);
3002 if (comp.c_header) |header| {2966 if (mod.emit_h) |emit_h| {
3003 man.hash.addEmitLoc(header.emit_loc.?);2967 man.hash.addEmitLoc(emit_h);
3004 }2968 }
3005 man.hash.addOptionalEmitLoc(comp.emit_asm);2969 man.hash.addOptionalEmitLoc(comp.emit_asm);
3006 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);2970 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
...@@ -3105,10 +3069,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3105,10 +3069,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3105 });3069 });
3106 break :blk try directory.join(arena, &[_][]const u8{bin_basename});3070 break :blk try directory.join(arena, &[_][]const u8{bin_basename});
3107 } else "";3071 } else "";
3108 if (comp.c_header != null) {3072 if (comp.emit_h != null) {
3109 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});3073 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
3110 }3074 }
3111 const emit_h_path = try stage1LocPath(arena, if (comp.c_header) |header| header.emit_loc else null, directory);3075 const emit_h_path = try stage1LocPath(arena, mod.emit_h, directory);
3112 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);3076 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);3077 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);3078 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
src/Module.zig+4-2
...@@ -94,6 +94,8 @@ stage1_flags: packed struct {...@@ -94,6 +94,8 @@ stage1_flags: packed struct {
94 reserved: u2 = 0,94 reserved: u2 = 0,
95} = .{},95} = .{},
9696
97emit_h: ?Compilation.EmitLoc,
98
97pub const Export = struct {99pub const Export = struct {
98 options: std.builtin.ExportOptions,100 options: std.builtin.ExportOptions,
99 /// Byte offset into the file that contains the export directive.101 /// Byte offset into the file that contains the export directive.
...@@ -1943,14 +1945,14 @@ fn allocateNewDecl(...@@ -1943,14 +1945,14 @@ fn allocateNewDecl(
1943 .coff => .{ .coff = link.File.Coff.TextBlock.empty },1945 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
1944 .elf => .{ .elf = link.File.Elf.TextBlock.empty },1946 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
1945 .macho => .{ .macho = link.File.MachO.TextBlock.empty },1947 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
1946 .c => .{ .c = {} },1948 .c => .{ .c = link.File.C.DeclBlock.empty },
1947 .wasm => .{ .wasm = {} },1949 .wasm => .{ .wasm = {} },
1948 },1950 },
1949 .fn_link = switch (self.comp.bin_file.tag) {1951 .fn_link = switch (self.comp.bin_file.tag) {
1950 .coff => .{ .coff = {} },1952 .coff => .{ .coff = {} },
1951 .elf => .{ .elf = link.File.Elf.SrcFn.empty },1953 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
1952 .macho => .{ .macho = link.File.MachO.SrcFn.empty },1954 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
1953 .c => .{ .c = {} },1955 .c => .{ .c = link.File.C.FnBlock.empty },
1954 .wasm => .{ .wasm = null },1956 .wasm => .{ .wasm = null },
1955 },1957 },
1956 .generation = 0,1958 .generation = 0,
src/codegen/c.zig+491-459
...@@ -1,495 +1,526 @@...@@ -1,495 +1,526 @@
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;
10
11const C = link.File.C;12const C = link.File.C;
12const Decl = Module.Decl;13const Decl = Module.Decl;
13const mem = std.mem;14const trace = @import("../tracy.zig").trace;
14const log = std.log.scoped(.c);
1515
16const Writer = std.ArrayList(u8).Writer;16const Mutability = enum { Const, Mut };
1717
18/// Maps a name from Zig source to C. Currently, this will always give the same18pub const CValue = union(enum) {
19/// output for any given input, sometimes resulting in broken identifiers.19 none: void,
20fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {20 /// Index into local_names
21 return allocator.dupe(u8, name);21 local: usize,
22}22 /// Index into local_names, but take the address.
23 local_ref: usize,
24 /// A constant instruction, to be rendered inline.
25 constant: *Inst,
26 /// Index into the parameters
27 arg: usize,
28 /// By-value
29 decl: *Decl,
2330
24const Mutability = enum { Const, Mut };31 pub fn printed(value: CValue, object: *Object) Printed {
32 return .{
33 .value = value,
34 .object = object,
35 };
36 }
37
38 pub const Printed = struct {
39 value: CValue,
40 object: *Object,
41
42 /// TODO this got unwieldly, I want to remove the ability to print this way
43 pub fn format(
44 self: Printed,
45 comptime fmt: []const u8,
46 options: std.fmt.FormatOptions,
47 writer: anytype,
48 ) error{OutOfMemory}!void {
49 if (fmt.len != 0) @compileError("Unknown format string: '" ++ fmt ++ "'");
50 switch (self.value) {
51 .none => unreachable,
52 .local => |i| return std.fmt.format(writer, "t{d}", .{i}),
53 .local_ref => |i| return std.fmt.format(writer, "&t{d}", .{i}),
54 .constant => |inst| {
55 const o = self.object;
56 o.dg.renderValue(writer, inst.ty, inst.value().?) catch |err| switch (err) {
57 error.OutOfMemory => return error.OutOfMemory,
58 error.AnalysisFail => return,
59 };
60 },
61 .arg => |i| return std.fmt.format(writer, "a{d}", .{i}),
62 .decl => |decl| return writer.writeAll(mem.span(decl.name)),
63 }
64 }
65 };
66};
2567
26fn renderTypeAndName(68pub const CValueMap = std.AutoHashMap(*Inst, CValue);
27 ctx: *Context,69
28 writer: Writer,70/// This data is available when outputting .c code for a Module.
29 ty: Type,71/// It is not available when generating .h file.
30 name: []const u8,72pub const Object = struct {
31 mutability: Mutability,73 dg: DeclGen,
32) error{ OutOfMemory, AnalysisFail }!void {74 gpa: *mem.Allocator,
33 var suffix = std.ArrayList(u8).init(&ctx.arena.allocator);75 code: std.ArrayList(u8),
3476 value_map: CValueMap,
35 var render_ty = ty;77 next_arg_index: usize = 0,
36 while (render_ty.zigTypeTag() == .Array) {78 next_local_index: usize = 0,
37 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);79
38 const c_len = render_ty.arrayLen() + sentinel_bit;80 fn resolveInst(o: *Object, inst: *Inst) !CValue {
39 try suffix.writer().print("[{d}]", .{c_len});81 if (inst.value()) |_| {
40 render_ty = render_ty.elemType();82 return CValue{ .constant = inst };
83 }
84 return o.value_map.get(inst).?; // Instruction does not dominate all uses!
41 }85 }
4286
43 try renderType(ctx, writer, render_ty);87 fn allocLocalValue(o: *Object) CValue {
88 const result = o.next_local_index;
89 o.next_local_index += 1;
90 return .{ .local = result };
91 }
4492
45 const const_prefix = switch (mutability) {93 fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue {
46 .Const => "const ",94 const local_value = o.allocLocalValue();
47 .Mut => "",95 try o.renderTypeAndName(o.code.writer(), ty, local_value, mutability);
48 };96 return local_value;
49 try writer.print(" {s}{s}{s}", .{ const_prefix, name, suffix.items });97 }
50}
5198
52fn renderType(99 fn indent(o: *Object) !void {
53 ctx: *Context,100 const indent_size = 4;
54 writer: Writer,101 const indent_level = 1;
55 t: Type,102 const indent_amt = indent_size * indent_level;
56) error{ OutOfMemory, AnalysisFail }!void {103 try o.code.writer().writeByteNTimes(' ', indent_amt);
57 switch (t.zigTypeTag()) {104 }
58 .NoReturn => {105
59 try writer.writeAll("zig_noreturn void");106 fn renderTypeAndName(
60 },107 o: *Object,
61 .Void => try writer.writeAll("void"),108 writer: Writer,
62 .Bool => try writer.writeAll("bool"),109 ty: Type,
63 .Int => {110 name: CValue,
64 switch (t.tag()) {111 mutability: Mutability,
65 .u8 => try writer.writeAll("uint8_t"),112 ) error{ OutOfMemory, AnalysisFail }!void {
66 .i8 => try writer.writeAll("int8_t"),113 var suffix = std.ArrayList(u8).init(o.gpa);
67 .u16 => try writer.writeAll("uint16_t"),114 defer suffix.deinit();
68 .i16 => try writer.writeAll("int16_t"),115
69 .u32 => try writer.writeAll("uint32_t"),116 var render_ty = ty;
70 .i32 => try writer.writeAll("int32_t"),117 while (render_ty.zigTypeTag() == .Array) {
71 .u64 => try writer.writeAll("uint64_t"),118 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
72 .i64 => try writer.writeAll("int64_t"),119 const c_len = render_ty.arrayLen() + sentinel_bit;
73 .usize => try writer.writeAll("uintptr_t"),120 try suffix.writer().print("[{d}]", .{c_len});
74 .isize => try writer.writeAll("intptr_t"),121 render_ty = render_ty.elemType();
75 .c_short => try writer.writeAll("short"),122 }
76 .c_ushort => try writer.writeAll("unsigned short"),123
77 .c_int => try writer.writeAll("int"),124 try o.dg.renderType(writer, render_ty);
78 .c_uint => try writer.writeAll("unsigned int"),125
79 .c_long => try writer.writeAll("long"),126 const const_prefix = switch (mutability) {
80 .c_ulong => try writer.writeAll("unsigned long"),127 .Const => "const ",
81 .c_longlong => try writer.writeAll("long long"),128 .Mut => "",
82 .c_ulonglong => try writer.writeAll("unsigned long long"),129 };
83 .int_signed, .int_unsigned => {130 try writer.print(" {s}{}{s}", .{ const_prefix, name.printed(o), suffix.items });
84 const info = t.intInfo(ctx.target);131 }
85 const sign_prefix = switch (info.signedness) {132};
86 .signed => "i",133
87 .unsigned => "",134/// This data is available both when outputting .c code and when outputting an .h file.
88 };135const DeclGen = struct {
89 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {136 module: *Module,
90 if (info.bits <= nbits) {137 decl: *Decl,
91 try writer.print("{s}int{d}_t", .{ sign_prefix, nbits });138 fwd_decl: std.ArrayList(u8),
92 break;139 error_msg: ?*Compilation.ErrorMsg,
93 }140
141 fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
142 dg.error_msg = try Compilation.ErrorMsg.create(dg.module.gpa, src, format, args);
143 return error.AnalysisFail;
144 }
145
146 fn renderValue(
147 dg: *DeclGen,
148 writer: Writer,
149 t: Type,
150 val: Value,
151 ) error{ OutOfMemory, AnalysisFail }!void {
152 switch (t.zigTypeTag()) {
153 .Int => {
154 if (t.isSignedInt())
155 return writer.print("{d}", .{val.toSignedInt()});
156 return writer.print("{d}", .{val.toUnsignedInt()});
157 },
158 .Pointer => switch (val.tag()) {
159 .undef, .zero => try writer.writeAll("0"),
160 .one => try writer.writeAll("1"),
161 .decl_ref => {
162 const decl = val.castTag(.decl_ref).?.data;
163
164 // Determine if we must pointer cast.
165 const decl_tv = decl.typed_value.most_recent.typed_value;
166 if (t.eql(decl_tv.ty)) {
167 try writer.print("&{s}", .{decl.name});
94 } else {168 } else {
95 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});169 try writer.writeAll("(");
170 try dg.renderType(writer, t);
171 try writer.print(")&{s}", .{decl.name});
96 }172 }
97 },173 },
174 .function => {
175 const func = val.castTag(.function).?.data;
176 try writer.print("{s}", .{func.owner_decl.name});
177 },
178 .extern_fn => {
179 const decl = val.castTag(.extern_fn).?.data;
180 try writer.print("{s}", .{decl.name});
181 },
182 else => |e| return dg.fail(
183 dg.decl.src(),
184 "TODO: C backend: implement Pointer value {s}",
185 .{@tagName(e)},
186 ),
187 },
188 .Array => {
189 // First try specific tag representations for more efficiency.
190 switch (val.tag()) {
191 .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
192 .bytes => {
193 const bytes = val.castTag(.bytes).?.data;
194 // TODO: make our own C string escape instead of using {Z}
195 try writer.print("\"{Z}\"", .{bytes});
196 },
197 else => {
198 // Fall back to generic implementation.
199 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
200 defer arena.deinit();
201
202 try writer.writeAll("{");
203 var index: usize = 0;
204 const len = t.arrayLen();
205 const elem_ty = t.elemType();
206 while (index < len) : (index += 1) {
207 if (index != 0) try writer.writeAll(",");
208 const elem_val = try val.elemValue(&arena.allocator, index);
209 try dg.renderValue(writer, elem_ty, elem_val);
210 }
211 if (t.sentinel()) |sentinel_val| {
212 if (index != 0) try writer.writeAll(",");
213 try dg.renderValue(writer, elem_ty, sentinel_val);
214 }
215 try writer.writeAll("}");
216 },
217 }
218 },
219 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
220 @tagName(e),
221 }),
222 }
223 }
224
225 fn renderFunctionSignature(dg: *DeclGen, w: Writer) !void {
226 const tv = dg.decl.typed_value.most_recent.typed_value;
227 // Determine whether the function is globally visible.
228 const is_global = blk: {
229 switch (tv.val.tag()) {
230 .extern_fn => break :blk true,
231 .function => {
232 const func = tv.val.castTag(.function).?.data;
233 break :blk dg.module.decl_exports.contains(func.owner_decl);
234 },
98 else => unreachable,235 else => unreachable,
99 }236 }
100 },237 };
101 .Pointer => {238 if (!is_global) {
102 if (t.isSlice()) {239 try w.writeAll("static ");
103 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});240 }
104 } else {241 try dg.renderType(w, tv.ty.fnReturnType());
105 try renderType(ctx, writer, t.elemType());242 const decl_name = mem.span(dg.decl.name);
106 try writer.writeAll(" *");243 try w.print(" {s}(", .{decl_name});
107 if (t.isConstPtr()) {244 var param_len = tv.ty.fnParamLen();
108 try writer.writeAll("const ");245 if (param_len == 0)
109 }246 try w.writeAll("void")
110 if (t.isVolatilePtr()) {247 else {
111 try writer.writeAll("volatile ");248 var index: usize = 0;
249 while (index < param_len) : (index += 1) {
250 if (index > 0) {
251 try w.writeAll(", ");
112 }252 }
253 try dg.renderType(w, tv.ty.fnParamType(index));
254 try w.print(" a{d}", .{index});
113 }255 }
114 },256 }
115 .Array => {257 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 }258 }
123}
124259
125fn renderValue(260 fn renderType(dg: *DeclGen, w: Writer, t: Type) error{ OutOfMemory, AnalysisFail }!void {
126 ctx: *Context,261 switch (t.zigTypeTag()) {
127 writer: Writer,262 .NoReturn => {
128 t: Type,263 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 },264 },
153 .function => {265 .Void => try w.writeAll("void"),
154 const func = val.castTag(.function).?.data;266 .Bool => try w.writeAll("bool"),
155 try writer.print("{s}", .{func.owner_decl.name});267 .Int => {
156 },268 switch (t.tag()) {
157 .extern_fn => {269 .u8 => try w.writeAll("uint8_t"),
158 const decl = val.castTag(.extern_fn).?.data;270 .i8 => try w.writeAll("int8_t"),
159 try writer.print("{s}", .{decl.name});271 .u16 => try w.writeAll("uint16_t"),
272 .i16 => try w.writeAll("int16_t"),
273 .u32 => try w.writeAll("uint32_t"),
274 .i32 => try w.writeAll("int32_t"),
275 .u64 => try w.writeAll("uint64_t"),
276 .i64 => try w.writeAll("int64_t"),
277 .usize => try w.writeAll("uintptr_t"),
278 .isize => try w.writeAll("intptr_t"),
279 .c_short => try w.writeAll("short"),
280 .c_ushort => try w.writeAll("unsigned short"),
281 .c_int => try w.writeAll("int"),
282 .c_uint => try w.writeAll("unsigned int"),
283 .c_long => try w.writeAll("long"),
284 .c_ulong => try w.writeAll("unsigned long"),
285 .c_longlong => try w.writeAll("long long"),
286 .c_ulonglong => try w.writeAll("unsigned long long"),
287 .int_signed, .int_unsigned => {
288 const info = t.intInfo(dg.module.getTarget());
289 const sign_prefix = switch (info.signedness) {
290 .signed => "i",
291 .unsigned => "",
292 };
293 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
294 if (info.bits <= nbits) {
295 try w.print("{s}int{d}_t", .{ sign_prefix, nbits });
296 break;
297 }
298 } else {
299 return dg.fail(dg.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
300 }
301 },
302 else => unreachable,
303 }
160 },304 },
161 else => |e| return ctx.fail(305 .Pointer => {
162 ctx.decl.src(),306 if (t.isSlice()) {
163 "TODO: C backend: implement Pointer value {s}",307 return dg.fail(dg.decl.src(), "TODO: C backend: implement slices", .{});
164 .{@tagName(e)},308 } else {
165 ),309 try dg.renderType(w, t.elemType());
166 },310 try w.writeAll(" *");
167 .Array => {311 if (t.isConstPtr()) {
168 // First try specific tag representations for more efficiency.312 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 }313 }
187 if (t.sentinel()) |sentinel_val| {314 if (t.isVolatilePtr()) {
188 if (index != 0) try writer.writeAll(",");315 try w.writeAll("volatile ");
189 try renderValue(ctx, writer, elem_ty, sentinel_val);
190 }316 }
191 try writer.writeAll("}");317 }
192 },
193 }
194 },
195 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{
196 @tagName(e),
197 }),
198 }
199}
200
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: {
209 switch (tv.val.tag()) {
210 .extern_fn => break :blk true,
211 .function => {
212 const func = tv.val.castTag(.function).?.data;
213 break :blk ctx.module.decl_exports.contains(func.owner_decl);
214 },318 },
215 else => unreachable,319 .Array => {
216 }320 try dg.renderType(w, t.elemType());
217 };321 try w.writeAll(" *");
218 if (!is_global) {322 },
219 try writer.writeAll("static ");323 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
220 }324 @tagName(e),
221 try renderType(ctx, writer, tv.ty.fnReturnType());325 }),
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 }326 }
240 }327 }
241 try writer.writeByte(')');328};
242}
243329
244fn indent(file: *C) !void {330pub fn genDecl(o: *Object) !void {
245 const indent_size = 4;331 const tracy = trace(@src());
246 const indent_level = 1;332 defer tracy.end();
247 const indent_amt = indent_size * indent_level;
248 try file.main.writer().writeByteNTimes(' ', indent_amt);
249}
250333
251pub fn generate(file: *C, module: *Module, decl: *Decl) !void {334 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 }
270335
271 if (tv.val.castTag(.function)) |func_payload| {336 if (tv.val.castTag(.function)) |func_payload| {
272 const writer = file.main.writer();337 const fwd_decl_writer = o.dg.fwd_decl.writer();
273 try renderFunctionSignature(&ctx, writer, decl);338 try o.dg.renderFunctionSignature(fwd_decl_writer);
274339 try fwd_decl_writer.writeAll(";\n");
275 try writer.writeAll(" {");
276340
277 const func: *Module.Fn = func_payload.data;341 const func: *Module.Fn = func_payload.data;
278 const instructions = func.body.instructions;342 const instructions = func.body.instructions;
279 if (instructions.len > 0) {343 const writer = o.code.writer();
280 try writer.writeAll("\n");344 try o.dg.renderFunctionSignature(writer);
281 for (instructions) |inst| {345 if (instructions.len == 0) {
282 if (switch (inst.tag) {346 try writer.writeAll(" {}\n\n");
283 .add => try genBinOp(&ctx, file, inst.castTag(.add).?, "+"),347 return;
284 .alloc => try genAlloc(&ctx, file, inst.castTag(.alloc).?),348 }
285 .arg => try genArg(&ctx),349
286 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),350 try writer.writeAll(" {");
287 .block => try genBlock(&ctx, file, inst.castTag(.block).?),351
288 .bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),352 try writer.writeAll("\n");
289 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),353 for (instructions) |inst| {
290 .call => try genCall(&ctx, file, inst.castTag(.call).?),354 const result_value = switch (inst.tag) {
291 .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),355 .add => try genBinOp(o, inst.castTag(.add).?, "+"),
292 .cmp_gt => try genBinOp(&ctx, file, inst.castTag(.cmp_gt).?, ">"),356 .alloc => try genAlloc(o, inst.castTag(.alloc).?),
293 .cmp_gte => try genBinOp(&ctx, file, inst.castTag(.cmp_gte).?, ">="),357 .arg => genArg(o),
294 .cmp_lt => try genBinOp(&ctx, file, inst.castTag(.cmp_lt).?, "<"),358 .assembly => try genAsm(o, inst.castTag(.assembly).?),
295 .cmp_lte => try genBinOp(&ctx, file, inst.castTag(.cmp_lte).?, "<="),359 .block => try genBlock(o, inst.castTag(.block).?),
296 .cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),360 .bitcast => try genBitcast(o, inst.castTag(.bitcast).?),
297 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),361 .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?),
298 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),362 .call => try genCall(o, inst.castTag(.call).?),
299 .load => try genLoad(&ctx, file, inst.castTag(.load).?),363 .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, "=="),
300 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),364 .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, ">"),
301 .retvoid => try genRetVoid(file),365 .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, ">="),
302 .store => try genStore(&ctx, file, inst.castTag(.store).?),366 .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, "<"),
303 .sub => try genBinOp(&ctx, file, inst.castTag(.sub).?, "-"),367 .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, "<="),
304 .unreach => try genUnreach(file, inst.castTag(.unreach).?),368 .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, "!="),
305 else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),369 .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?),
306 }) |name| {370 .intcast => try genIntCast(o, inst.castTag(.intcast).?),
307 try ctx.inst_map.putNoClobber(inst, name);371 .load => try genLoad(o, inst.castTag(.load).?),
308 }372 .ret => try genRet(o, inst.castTag(.ret).?),
373 .retvoid => try genRetVoid(o),
374 .store => try genStore(o, inst.castTag(.store).?),
375 .sub => try genBinOp(o, inst.castTag(.sub).?, "-"),
376 .unreach => try genUnreach(o, inst.castTag(.unreach).?),
377 else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
378 };
379 switch (result_value) {
380 .none => {},
381 else => try o.value_map.putNoClobber(inst, result_value),
309 }382 }
310 }383 }
311384
312 try writer.writeAll("}\n\n");385 try writer.writeAll("}\n\n");
313 } else if (tv.val.tag() == .extern_fn) {386 } else if (tv.val.tag() == .extern_fn) {
314 return; // handled when referenced387 const writer = o.code.writer();
388 try o.dg.renderFunctionSignature(writer);
389 try writer.writeAll(";\n");
315 } else {390 } else {
316 const writer = file.constants.writer();391 const writer = o.code.writer();
317 try writer.writeAll("static ");392 try writer.writeAll("static ");
318393
319 // TODO ask the Decl if it is const394 // TODO ask the Decl if it is const
320 // https://github.com/ziglang/zig/issues/7582395 // https://github.com/ziglang/zig/issues/7582
321396
322 try renderTypeAndName(&ctx, writer, tv.ty, mem.span(decl.name), .Mut);397 const decl_c_value: CValue = .{ .decl = o.dg.decl };
398 try o.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut);
323399
324 try writer.writeAll(" = ");400 try writer.writeAll(" = ");
325 try renderValue(&ctx, writer, tv.ty, tv.val);401 try o.dg.renderValue(writer, tv.ty, tv.val);
326 try writer.writeAll(";\n");402 try writer.writeAll(";\n");
327 }403 }
328}404}
329405
330pub fn generateHeader(406pub fn genHeader(comp: *Compilation, dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
331 comp: *Compilation,407 const tracy = trace(@src());
332 module: *Module,408 defer tracy.end();
333 header: *C.Header,409
334 decl: *Decl,
335) error{ AnalysisFail, OutOfMemory }!void {
336 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {410 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
337 .Fn => {411 .Fn => {
338 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);412 dg.renderFunctionSignature() catch |err| switch (err) {
339 defer inst_map.deinit();413 error.AnalysisFail => {
340414 try dg.module.failed_decls.put(dg.module.gpa, decl, dg.error_msg.?);
341 var arena = std.heap.ArenaAllocator.init(comp.gpa);415 dg.error_msg = null;
342 defer arena.deinit();416 return error.AnalysisFail;
343417 },
344 var ctx = Context{418 else => |e| return e,
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 };419 };
359 try writer.writeAll(";\n");420 try dg.fwd_decl.appendSlice(";\n");
360 },421 },
361 else => {},422 else => {},
362 }423 }
363}424}
364425
365const Context = struct {426fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {
366 decl: *Decl,427 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();
403428
404 // First line: the variable used as data storage.429 // First line: the variable used as data storage.
405 try indent(file);430 try o.indent();
406 const local_name = try ctx.name();
407 const elem_type = alloc.base.ty.elemType();431 const elem_type = alloc.base.ty.elemType();
408 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;432 const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut;
409 try renderTypeAndName(ctx, writer, elem_type, local_name, mutability);433 const local = try o.allocLocal(elem_type, mutability);
410 try writer.writeAll(";\n");434 try writer.writeAll(";\n");
411435
412 // Second line: a pointer to it so that we can refer to it as the allocation.436 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}437}
421438
422fn genArg(ctx: *Context) !?[]u8 {439fn genArg(o: *Object) CValue {
423 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex});440 const i = o.next_arg_index;
424 ctx.argdex += 1;441 o.next_arg_index += 1;
425 return name;442 return .{ .arg = i };
426}443}
427444
428fn genRetVoid(file: *C) !?[]u8 {445fn genRetVoid(o: *Object) !CValue {
429 try indent(file);446 try o.indent();
430 try file.main.writer().print("return;\n", .{});447 try o.code.writer().print("return;\n", .{});
431 return null;448 return CValue.none;
432}449}
433450
434fn genLoad(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {451fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
435 const operand = try ctx.resolveInst(inst.operand);452 const operand = try o.resolveInst(inst.operand);
436 const writer = file.main.writer();453 const writer = o.code.writer();
437 try indent(file);454 try o.indent();
438 const local_name = try ctx.name();455 const local = try o.allocLocal(inst.base.ty, .Const);
439 try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);456 switch (operand) {
440 try writer.print(" = *{s};\n", .{operand});457 .local_ref => |i| {
441 return local_name;458 const wrapped: CValue = .{ .local = i };
459 try writer.print(" = {};\n", .{wrapped.printed(o)});
460 },
461 else => {
462 try writer.print(" = *{};\n", .{operand.printed(o)});
463 },
464 }
465 return local;
442}466}
443467
444fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {468fn genRet(o: *Object, inst: *Inst.UnOp) !CValue {
445 try indent(file);469 const operand = try o.resolveInst(inst.operand);
446 const writer = file.main.writer();470 try o.indent();
447 try writer.print("return {s};\n", .{try ctx.resolveInst(inst.operand)});471 try o.code.writer().print("return {};\n", .{operand.printed(o)});
448 return null;472 return CValue.none;
449}473}
450474
451fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {475fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue {
452 if (inst.base.isUnused())476 if (inst.base.isUnused())
453 return null;477 return CValue.none;
454 try indent(file);
455 const writer = file.main.writer();
456 const name = try ctx.name();
457 const from = try ctx.resolveInst(inst.operand);
458478
459 try renderTypeAndName(ctx, writer, inst.base.ty, name, .Const);479 const from = try o.resolveInst(inst.operand);
480
481 try o.indent();
482 const writer = o.code.writer();
483 const local = try o.allocLocal(inst.base.ty, .Const);
460 try writer.writeAll(" = (");484 try writer.writeAll(" = (");
461 try renderType(ctx, writer, inst.base.ty);485 try o.dg.renderType(writer, inst.base.ty);
462 try writer.print("){s};\n", .{from});486 try writer.print("){};\n", .{from.printed(o)});
463 return name;487 return local;
464}488}
465489
466fn genStore(ctx: *Context, file: *C, inst: *Inst.BinOp) !?[]u8 {490fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
467 // *a = b;491 // *a = b;
468 try indent(file);492 const dest_ptr = try o.resolveInst(inst.lhs);
469 const writer = file.main.writer();493 const src_val = try o.resolveInst(inst.rhs);
470 const dest_ptr_name = try ctx.resolveInst(inst.lhs);494
471 const src_val_name = try ctx.resolveInst(inst.rhs);495 try o.indent();
472 try writer.print("*{s} = {s};\n", .{ dest_ptr_name, src_val_name });496 const writer = o.code.writer();
473 return null;497 switch (dest_ptr) {
498 .local_ref => |i| {
499 const dest: CValue = .{ .local = i };
500 try writer.print("{} = {};\n", .{ dest.printed(o), src_val.printed(o) });
501 },
502 else => {
503 try writer.print("*{} = {};\n", .{ dest_ptr.printed(o), src_val.printed(o) });
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 try writer.print(" = {} {s} {};\n", .{ lhs.printed(o), operator, rhs.printed(o) });
520 return local;
487}521}
488522
489fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {523fn 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| {524 if (inst.func.castTag(.constant)) |func_inst| {
494 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|525 const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn|
495 extern_fn.data526 extern_fn.data
...@@ -501,23 +532,19 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {...@@ -501,23 +532,19 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
501 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;532 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
502 const ret_ty = fn_ty.fnReturnType();533 const ret_ty = fn_ty.fnReturnType();
503 const unused_result = inst.base.isUnused();534 const unused_result = inst.base.isUnused();
504 var result_name: ?[]u8 = null;535 var result_local: CValue = .none;
536
537 try o.indent();
538 const writer = o.code.writer();
505 if (unused_result) {539 if (unused_result) {
506 if (ret_ty.hasCodeGenBits()) {540 if (ret_ty.hasCodeGenBits()) {
507 try writer.print("(void)", .{});541 try writer.print("(void)", .{});
508 }542 }
509 } else {543 } else {
510 const local_name = try ctx.name();544 result_local = try o.allocLocal(ret_ty, .Const);
511 try renderTypeAndName(ctx, writer, ret_ty, local_name, .Const);
512 try writer.writeAll(" = ");545 try writer.writeAll(" = ");
513 result_name = local_name;
514 }546 }
515 const fn_name = mem.spanZ(fn_decl.name);547 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});548 try writer.print("{s}(", .{fn_name});
522 if (inst.args.len != 0) {549 if (inst.args.len != 0) {
523 for (inst.args) |arg, i| {550 for (inst.args) |arg, i| {
...@@ -525,87 +552,88 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {...@@ -525,87 +552,88 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
525 try writer.writeAll(", ");552 try writer.writeAll(", ");
526 }553 }
527 if (arg.value()) |val| {554 if (arg.value()) |val| {
528 try renderValue(ctx, writer, arg.ty, val);555 try o.dg.renderValue(writer, arg.ty, val);
529 } else {556 } else {
530 const val = try ctx.resolveInst(arg);557 const val = try o.resolveInst(arg);
531 try writer.print("{s}", .{val});558 try writer.print("{}", .{val.printed(o)});
532 }559 }
533 }560 }
534 }561 }
535 try writer.writeAll(");\n");562 try writer.writeAll(");\n");
536 return result_name;563 return result_local;
537 } else {564 } else {
538 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});565 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement function pointers", .{});
539 }566 }
540}567}
541568
542fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {569fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue {
543 // TODO emit #line directive here with line number and filename570 // TODO emit #line directive here with line number and filename
544 return null;571 return CValue.none;
545}572}
546573
547fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {574fn genBlock(o: *Object, inst: *Inst.Block) !CValue {
548 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});575 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement blocks", .{});
549}576}
550577
551fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {578fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue {
552 const writer = file.main.writer();579 const operand = try o.resolveInst(inst.operand);
553 try indent(file);580
554 const local_name = try ctx.name();581 const writer = o.code.writer();
555 const operand = try ctx.resolveInst(inst.operand);582 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) {583 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
584 const local = try o.allocLocal(inst.base.ty, .Const);
558 try writer.writeAll(" = (");585 try writer.writeAll(" = (");
559 try renderType(ctx, writer, inst.base.ty);586 try o.dg.renderType(writer, inst.base.ty);
560 try writer.print("){s};\n", .{operand});587 try writer.print("){};\n", .{operand.printed(o)});
561 } else {588 return local;
562 try writer.writeAll(";\n");
563 try indent(file);
564 try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name });
565 }589 }
566 return local_name;590
591 const local = try o.allocLocal(inst.base.ty, .Mut);
592 try writer.writeAll(";\n");
593 try o.indent();
594 try writer.print("memcpy(&{}, &{}, sizeof {});\n", .{
595 local.printed(o), operand.printed(o), local.printed(o),
596 });
597 return local;
567}598}
568599
569fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {600fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue {
570 try indent(file);601 try o.indent();
571 try file.main.writer().writeAll("zig_breakpoint();\n");602 try o.code.writer().writeAll("zig_breakpoint();\n");
572 return null;603 return CValue.none;
573}604}
574605
575fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {606fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue {
576 try indent(file);607 try o.indent();
577 try file.main.writer().writeAll("zig_unreachable();\n");608 try o.code.writer().writeAll("zig_unreachable();\n");
578 return null;609 return CValue.none;
579}610}
580611
581fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {612fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
582 try indent(file);613 if (as.base.isUnused() and !as.is_volatile)
583 const writer = file.main.writer();614 return CValue.none;
615
616 const writer = o.code.writer();
584 for (as.inputs) |i, index| {617 for (as.inputs) |i, index| {
585 if (i[0] == '{' and i[i.len - 1] == '}') {618 if (i[0] == '{' and i[i.len - 1] == '}') {
586 const reg = i[1 .. i.len - 1];619 const reg = i[1 .. i.len - 1];
587 const arg = as.args[index];620 const arg = as.args[index];
621 const arg_c_value = try o.resolveInst(arg);
622 try o.indent();
588 try writer.writeAll("register ");623 try writer.writeAll("register ");
589 try renderType(ctx, writer, arg.ty);624 try o.dg.renderType(writer, arg.ty);
590 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });625 try writer.print(" {s}_constant __asm__(\"{s}\") = {};\n", .{
591 // TODO merge constant handling into inst_map as well626 reg, reg, arg_c_value.printed(o),
592 if (arg.castTag(.constant)) |c| {627 });
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 {628 } else {
603 return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});629 return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{});
604 }630 }
605 }631 }
606 try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });632 try o.indent();
607 if (as.output) |o| {633 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
608 return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{});634 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
635 if (as.output) |_| {
636 return o.dg.fail(o.dg.decl.src(), "TODO inline asm output", .{});
609 }637 }
610 if (as.inputs.len > 0) {638 if (as.inputs.len > 0) {
611 if (as.output == null) {639 if (as.output == null) {
...@@ -627,5 +655,9 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {...@@ -627,5 +655,9 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
627 }655 }
628 }656 }
629 try writer.writeAll(");\n");657 try writer.writeAll(");\n");
630 return null;658
659 if (as.base.isUnused())
660 return CValue.none;
661
662 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
631}663}
src/link.zig+11-8
...@@ -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
...@@ -291,7 +291,7 @@ pub const File = struct {...@@ -291,7 +291,7 @@ pub const File = struct {
291 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),291 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
292 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),292 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
293 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),293 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
294 .c => {},294 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
295 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),295 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
296 }296 }
297 }297 }
...@@ -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 => {},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+120-75
...@@ -11,45 +11,28 @@ const trace = @import("../tracy.zig").trace;...@@ -11,45 +11,28 @@ const trace = @import("../tracy.zig").trace;
11const C = @This();11const C = @This();
1212
13pub const base_tag: link.File.Tag = .c;13pub const base_tag: link.File.Tag = .c;
14pub const zig_h = @embedFile("C/zig.h");
1415
15pub const Header = struct {16base: link.File,
16 buf: std.ArrayList(u8),
17 emit_loc: ?Compilation.EmitLoc,
18
19 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
20 return .{
21 .buf = std.ArrayList(u8).init(allocator),
22 .emit_loc = emit_loc,
23 };
24 }
25
26 pub fn flush(self: *const Header, writer: anytype) !void {
27 const tracy = trace(@src());
28 defer tracy.end();
2917
30 try writer.writeAll(@embedFile("cbe.h"));18/// Per-declaration data. For functions this is the body, and
31 if (self.buf.items.len > 0) {19/// the forward declaration is stored in the FnBlock.
32 try writer.print("{s}", .{self.buf.items});20pub const DeclBlock = struct {
33 }21 code: std.ArrayListUnmanaged(u8),
34 }
3522
36 pub fn deinit(self: *Header) void {23 pub const empty: DeclBlock = .{
37 self.buf.deinit();24 .code = .{},
38 self.* = undefined;25 };
39 }
40};26};
4127
42base: link.File,28/// Per-function data.
4329pub const FnBlock = struct {
44path: []const u8,30 fwd_decl: std.ArrayListUnmanaged(u8),
4531
46// These are only valid during a flush()!32 pub const empty: FnBlock = .{
47header: Header,33 .fwd_decl = .{},
48constants: std.ArrayList(u8),34 };
49main: std.ArrayList(u8),35};
50called: std.StringHashMap(void),
51
52error_msg: *Compilation.ErrorMsg = undefined,
5336
54pub 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 {
55 assert(options.object_format == .c);38 assert(options.object_format == .c);
...@@ -57,6 +40,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -57,6 +40,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
57 if (options.use_llvm) return error.LLVMHasNoCBackend;40 if (options.use_llvm) return error.LLVMHasNoCBackend;
58 if (options.use_lld) return error.LLDHasNoCBackend;41 if (options.use_lld) return error.LLDHasNoCBackend;
5942
43 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
44 .truncate = true,
45 .mode = link.determineMode(options),
46 });
47 errdefer file.close();
48
49 try file.writeAll(zig_h);
50
60 var c_file = try allocator.create(C);51 var c_file = try allocator.create(C);
61 errdefer allocator.destroy(c_file);52 errdefer allocator.destroy(c_file);
6253
...@@ -64,25 +55,75 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -64,25 +55,75 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
64 .base = .{55 .base = .{
65 .tag = .c,56 .tag = .c,
66 .options = options,57 .options = options,
67 .file = null,58 .file = file,
68 .allocator = allocator,59 .allocator = allocator,
69 },60 },
70 .main = undefined,
71 .header = undefined,
72 .constants = undefined,
73 .called = undefined,
74 .path = sub_path,
75 };61 };
7662
77 return c_file;63 return c_file;
78}64}
7965
80pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {66pub fn deinit(self: *C) void {
81 self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);67 const module = self.base.options.module orelse return;
82 return error.AnalysisFail;68 for (module.decl_table.items()) |entry| {
69 self.freeDecl(entry.value);
70 }
71}
72
73pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
74
75pub fn freeDecl(self: *C, decl: *Module.Decl) void {
76 decl.link.c.code.deinit(self.base.allocator);
77 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);
78}
79
80pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
81 const tracy = trace(@src());
82 defer tracy.end();
83
84 const fwd_decl = &decl.fn_link.c.fwd_decl;
85 const code = &decl.link.c.code;
86 fwd_decl.shrinkRetainingCapacity(0);
87 code.shrinkRetainingCapacity(0);
88
89 var object: codegen.Object = .{
90 .dg = .{
91 .module = module,
92 .error_msg = null,
93 .decl = decl,
94 .fwd_decl = fwd_decl.toManaged(module.gpa),
95 },
96 .gpa = module.gpa,
97 .code = code.toManaged(module.gpa),
98 .value_map = codegen.CValueMap.init(module.gpa),
99 };
100 defer object.value_map.deinit();
101 defer object.code.deinit();
102 defer object.dg.fwd_decl.deinit();
103
104 codegen.genDecl(&object) catch |err| switch (err) {
105 error.AnalysisFail => {},
106 else => |e| return e,
107 };
108 // The code may populate this error without returning error.AnalysisFail.
109 if (object.dg.error_msg) |msg| {
110 try module.failed_decls.put(module.gpa, decl, msg);
111 return;
112 }
113
114 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
115 code.* = object.code.moveToUnmanaged();
116
117 // Free excess allocated memory for this Decl.
118 fwd_decl.shrink(module.gpa, fwd_decl.items.len);
119 code.shrink(module.gpa, code.items.len);
83}120}
84121
85pub fn deinit(self: *C) void {}122pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
123 // The C backend does not have the ability to fix line numbers without re-generating
124 // the entire Decl.
125 return self.updateDecl(module, decl);
126}
86127
87pub fn flush(self: *C, comp: *Compilation) !void {128pub fn flush(self: *C, comp: *Compilation) !void {
88 return self.flushModule(comp);129 return self.flushModule(comp);
...@@ -92,41 +133,45 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -92,41 +133,45 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
92 const tracy = trace(@src());133 const tracy = trace(@src());
93 defer tracy.end();134 defer tracy.end();
94135
95 self.main = std.ArrayList(u8).init(self.base.allocator);136 const file = self.base.file.?;
96 self.header = Header.init(self.base.allocator, null);
97 self.constants = std.ArrayList(u8).init(self.base.allocator);
98 self.called = std.StringHashMap(void).init(self.base.allocator);
99 defer self.main.deinit();
100 defer self.header.deinit();
101 defer self.constants.deinit();
102 defer self.called.deinit();
103
104 const module = self.base.options.module.?;
105 for (self.base.options.module.?.decl_table.entries.items) |kv| {
106 codegen.generate(self, module, kv.value) catch |err| {
107 if (err == error.AnalysisFail) {
108 try module.failed_decls.put(module.gpa, kv.value, self.error_msg);
109 }
110 return err;
111 };
112 }
113137
114 const file = try self.base.options.emit.?.directory.handle.createFile(self.path, .{ .truncate = true, .read = true, .mode = link.determineMode(self.base.options) });138 // The header is written upon opening; here we truncate and seek to after the header.
115 defer file.close();139 // TODO: use writev
140 try file.seekTo(zig_h.len);
141 try file.setEndPos(zig_h.len);
116142
117 const writer = file.writer();143 var buffered_writer = std.io.bufferedWriter(file.writer());
118 try self.header.flush(writer);144 const writer = buffered_writer.writer();
119 if (self.header.buf.items.len > 0) {145
120 try writer.writeByte('\n');146 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
121 }147
122 if (self.constants.items.len > 0) {148 // Forward decls and non-functions first.
123 try writer.print("{s}\n", .{self.constants.items});149 // TODO: use writev
150 for (module.decl_table.items()) |kv| {
151 const decl = kv.value;
152 const decl_tv = decl.typed_value.most_recent.typed_value;
153 if (decl_tv.val.castTag(.function)) |_| {
154 try writer.writeAll(decl.fn_link.c.fwd_decl.items);
155 } else {
156 try writer.writeAll(decl.link.c.code.items);
157 }
124 }158 }
125 if (self.main.items.len > 1) {159
126 const last_two = self.main.items[self.main.items.len - 2 ..];160 // Now the function bodies.
127 if (std.mem.eql(u8, last_two, "\n\n")) {161 for (module.decl_table.items()) |kv| {
128 self.main.items.len -= 1;162 const decl = kv.value;
163 const decl_tv = decl.typed_value.most_recent.typed_value;
164 if (decl_tv.val.castTag(.function)) |_| {
165 try writer.writeAll(decl.link.c.code.items);
129 }166 }
130 }167 }
131 try writer.writeAll(self.main.items);168
169 try buffered_writer.flush();
132}170}
171
172pub fn updateDeclExports(
173 self: *C,
174 module: *Module,
175 decl: *Module.Decl,
176 exports: []const *Module.Export,
177) !void {}
src/link/C/zig.h created+45
...@@ -0,0 +1,45 @@
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>
45
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-2
...@@ -22,8 +22,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -22,8 +22,6 @@ 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 updates
26 // https://github.com/ziglang/zig/issues/7589
27 case.addCompareOutput(25 case.addCompareOutput(
28 \\extern fn puts(s: [*:0]const u8) c_int;26 \\extern fn puts(s: [*:0]const u8) c_int;
29 \\export fn main() c_int {27 \\export fn main() c_int {