authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-28 17:15:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-28 17:15:29-07:00
log87c6341b61aa54301aa98fea1a449fff40ba25af
treea9c9c60c20bb600314cebb2dce25579dca94ee5f
parent2df2f0020f4ddc41b3b914cd17efcb403cf0f6ad

stage2: add extern functions

and improve the C backend enough to support Hello World (almost)

7 files changed, 452 insertions(+), 163 deletions(-)

src/Compilation.zig+1-4
......@@ -1431,9 +1431,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14311431 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
14321432 defer c_comp_progress_node.end();
14331433
1434 var arena = std.heap.ArenaAllocator.init(self.gpa);
1435 defer arena.deinit();
1436
14371434 self.work_queue_wait_group.reset();
14381435 defer self.work_queue_wait_group.wait();
14391436
......@@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
15021499 };
15031500
15041501 if (self.c_header) |*header| {
1505 c_codegen.generateHeader(&arena, module, &header.*, decl) catch |err| switch (err) {
1502 c_codegen.generateHeader(self, module, header, decl) catch |err| switch (err) {
15061503 error.OutOfMemory => return error.OutOfMemory,
15071504 error.AnalysisFail => {
15081505 decl.analysis = .dependency_failure;
src/Module.zig+86-11
......@@ -277,6 +277,8 @@ pub const Decl = struct {
277277};
278278
279279/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
280/// Extern functions do not have this data structure; they are represented by
281/// the `Decl` only, with a `Value` tag of `extern_fn`.
280282pub const Fn = struct {
281283 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
282284 analysis: union(enum) {
......@@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10101012 defer fn_type_scope.instructions.deinit(self.gpa);
10111013
10121014 decl.is_pub = fn_proto.getVisibToken() != null;
1013 const body_node = fn_proto.getBodyNode() orelse
1014 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
10151015
10161016 const param_decls = fn_proto.params();
10171017 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
......@@ -1083,6 +1083,36 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10831083 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
10841084 .instructions = fn_type_scope.instructions.items,
10851085 });
1086 const body_node = fn_proto.getBodyNode() orelse {
1087 // Extern function.
1088 var type_changed = true;
1089 if (decl.typedValueManaged()) |tvm| {
1090 type_changed = !tvm.typed_value.ty.eql(fn_type);
1091
1092 tvm.deinit(self.gpa);
1093 }
1094 const value_payload = try decl_arena.allocator.create(Value.Payload.ExternFn);
1095 value_payload.* = .{ .decl = decl };
1096
1097 decl_arena_state.* = decl_arena.state;
1098 decl.typed_value = .{
1099 .most_recent = .{
1100 .typed_value = .{
1101 .ty = fn_type,
1102 .val = Value.initPayload(&value_payload.base),
1103 },
1104 .arena = decl_arena_state,
1105 },
1106 };
1107 decl.analysis = .complete;
1108 decl.generation = self.generation;
1109
1110 try self.comp.bin_file.allocateDeclIndexes(decl);
1111 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1112
1113 return type_changed;
1114 };
1115
10861116 const new_func = try decl_arena.allocator.create(Fn);
10871117 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
10881118
......@@ -1899,7 +1929,13 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
18991929 return null;
19001930}
19011931
1902pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
1932pub fn analyzeExport(
1933 self: *Module,
1934 scope: *Scope,
1935 src: usize,
1936 borrowed_symbol_name: []const u8,
1937 exported_decl: *Decl,
1938) !void {
19031939 try self.ensureDeclAnalyzed(exported_decl);
19041940 const typed_value = exported_decl.typed_value.most_recent.typed_value;
19051941 switch (typed_value.ty.zigTypeTag()) {
......@@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
28012837 }
28022838 }
28032839
2804 // *[N]T to []T
2805 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
2806 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
2807 {
2840 // Coercions where the source is a single pointer to an array.
2841 src_array_ptr: {
2842 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
28082843 const array_type = inst.ty.elemType();
2844 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
2845 const array_elem_type = array_type.elemType();
2846 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
2847 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
2848
28092849 const dst_elem_type = dest_type.elemType();
2810 if (array_type.zigTypeTag() == .Array and
2811 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
2812 {
2813 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
2850 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
2851 .ok => {},
2852 .no_match => break :src_array_ptr,
2853 }
2854
2855 switch (dest_type.ptrSize()) {
2856 .Slice => {
2857 // *[N]T to []T
2858 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
2859 },
2860 .C => {
2861 // *[N]T to [*c]T
2862 return self.coerceArrayPtrToMany(scope, dest_type, inst);
2863 },
2864 .Many => {
2865 // *[N]T to [*]T
2866 // *[N:s]T to [*:s]T
2867 const src_sentinel = array_type.sentinel();
2868 const dst_sentinel = dest_type.sentinel();
2869 if (src_sentinel == null and dst_sentinel == null)
2870 return self.coerceArrayPtrToMany(scope, dest_type, inst);
2871
2872 if (src_sentinel) |src_s| {
2873 if (dst_sentinel) |dst_s| {
2874 if (src_s.eql(dst_s)) {
2875 return self.coerceArrayPtrToMany(scope, dest_type, inst);
2876 }
2877 }
2878 }
2879 },
2880 .One => {},
28142881 }
28152882 }
28162883
......@@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
29182985 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
29192986}
29202987
2988fn coerceArrayPtrToMany(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2989 if (inst.value()) |val| {
2990 // The comptime Value representation is compatible with both types.
2991 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2992 }
2993 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
2994}
2995
29212996pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
29222997 @setCold(true);
29232998 const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
src/codegen/c.zig+245-132
......@@ -12,7 +12,7 @@ const C = link.File.C;
1212const Decl = Module.Decl;
1313const mem = std.mem;
1414
15const indentation = " ";
15const Writer = std.ArrayList(u8).Writer;
1616
1717/// Maps a name from Zig source to C. Currently, this will always give the same
1818/// output for any given input, sometimes resulting in broken identifiers.
......@@ -20,43 +20,145 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
2020 return allocator.dupe(u8, name);
2121}
2222
23fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void {
24 switch (T.zigTypeTag()) {
23fn renderType(
24 ctx: *Context,
25 header: *C.Header,
26 writer: Writer,
27 t: Type,
28) error{ OutOfMemory, AnalysisFail }!void {
29 switch (t.zigTypeTag()) {
2530 .NoReturn => {
2631 try writer.writeAll("zig_noreturn void");
2732 },
2833 .Void => try writer.writeAll("void"),
2934 .Bool => try writer.writeAll("bool"),
3035 .Int => {
31 if (T.tag() == .u8) {
32 header.need_stdint = true;
33 try writer.writeAll("uint8_t");
34 } else if (T.tag() == .u32) {
35 header.need_stdint = true;
36 try writer.writeAll("uint32_t");
37 } else if (T.tag() == .usize) {
38 header.need_stddef = true;
39 try writer.writeAll("size_t");
36 switch (t.tag()) {
37 .u8 => try writer.writeAll("uint8_t"),
38 .i8 => try writer.writeAll("int8_t"),
39 .u16 => try writer.writeAll("uint16_t"),
40 .i16 => try writer.writeAll("int16_t"),
41 .u32 => try writer.writeAll("uint32_t"),
42 .i32 => try writer.writeAll("int32_t"),
43 .u64 => try writer.writeAll("uint64_t"),
44 .i64 => try writer.writeAll("int64_t"),
45 .usize => try writer.writeAll("uintptr_t"),
46 .isize => try writer.writeAll("intptr_t"),
47 .c_short => try writer.writeAll("short"),
48 .c_ushort => try writer.writeAll("unsigned short"),
49 .c_int => try writer.writeAll("int"),
50 .c_uint => try writer.writeAll("unsigned int"),
51 .c_long => try writer.writeAll("long"),
52 .c_ulong => try writer.writeAll("unsigned long"),
53 .c_longlong => try writer.writeAll("long long"),
54 .c_ulonglong => try writer.writeAll("unsigned long long"),
55 .int_signed, .int_unsigned => {
56 const info = t.intInfo(ctx.target);
57 const sign_prefix = switch (info.signedness) {
58 .signed => "i",
59 .unsigned => "",
60 };
61 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
62 if (info.bits <= nbits) {
63 try writer.print("{s}int{d}_t", .{ sign_prefix, nbits });
64 break;
65 }
66 } else {
67 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
68 }
69 },
70 else => unreachable,
71 }
72 },
73 .Pointer => {
74 if (t.isSlice()) {
75 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});
4076 } else {
41 return ctx.fail(ctx.decl.src(), "TODO implement int type {}", .{T});
77 if (t.isConstPtr()) {
78 try writer.writeAll("const ");
79 }
80 if (t.isVolatilePtr()) {
81 try writer.writeAll("volatile ");
82 }
83 try renderType(ctx, header, writer, t.elemType());
84 try writer.writeAll(" *");
4285 }
4386 },
44 else => |e| return ctx.fail(ctx.decl.src(), "TODO implement type {}", .{e}),
87 .Array => {
88 try renderType(ctx, header, writer, t.elemType());
89 const sentinel_bit = @boolToInt(t.sentinel() != null);
90 const c_len = t.arrayLen() + sentinel_bit;
91 try writer.print("[{d}]", .{c_len});
92 },
93 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement type {s}", .{
94 @tagName(e),
95 }),
4596 }
4697}
4798
48fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {
49 switch (T.zigTypeTag()) {
99fn renderValue(
100 ctx: *Context,
101 writer: Writer,
102 t: Type,
103 val: Value,
104) error{ OutOfMemory, AnalysisFail }!void {
105 switch (t.zigTypeTag()) {
50106 .Int => {
51 if (T.isSignedInt())
52 return writer.print("{}", .{val.toSignedInt()});
53 return writer.print("{}", .{val.toUnsignedInt()});
107 if (t.isSignedInt())
108 return writer.print("{d}", .{val.toSignedInt()});
109 return writer.print("{d}", .{val.toUnsignedInt()});
110 },
111 .Pointer => switch (val.tag()) {
112 .undef, .zero => try writer.writeAll("0"),
113 .one => try writer.writeAll("1"),
114 .decl_ref => {
115 const decl_ref_payload = val.cast(Value.Payload.DeclRef).?;
116 try writer.print("&{s}", .{decl_ref_payload.decl.name});
117 },
118 .function => {
119 const payload = val.cast(Value.Payload.Function).?;
120 try writer.print("{s}", .{payload.func.owner_decl.name});
121 },
122 .extern_fn => {
123 const payload = val.cast(Value.Payload.ExternFn).?;
124 try writer.print("{s}", .{payload.decl.name});
125 },
126 else => |e| return ctx.fail(
127 ctx.decl.src(),
128 "TODO: C backend: implement Pointer value {s}",
129 .{@tagName(e)},
130 ),
131 },
132 .Array => {
133 // TODO first try specific tag representations for more efficiency
134 // Fall back to inefficient generic implementation.
135 try writer.writeAll("{");
136 var index: usize = 0;
137 const len = t.arrayLen();
138 const elem_ty = t.elemType();
139 while (index < len) : (index += 1) {
140 if (index != 0) try writer.writeAll(",");
141 const elem_val = try val.elemValue(&ctx.arena.allocator, index);
142 try renderValue(ctx, writer, elem_ty, elem_val);
143 }
144 if (t.sentinel()) |sentinel_val| {
145 if (index != 0) try writer.writeAll(",");
146 try renderValue(ctx, writer, elem_ty, sentinel_val);
147 }
148 try writer.writeAll("}");
54149 },
55 else => |e| return ctx.fail(ctx.decl.src(), "TODO implement value {}", .{e}),
150 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{
151 @tagName(e),
152 }),
56153 }
57154}
58155
59fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
156fn renderFunctionSignature(
157 ctx: *Context,
158 header: *C.Header,
159 writer: Writer,
160 decl: *Decl,
161) !void {
60162 const tv = decl.typed_value.most_recent.typed_value;
61163 try renderType(ctx, header, writer, tv.ty.fnReturnType());
62164 // Use the child allocator directly, as we know the name can be freed before
......@@ -81,27 +183,92 @@ fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayLi
81183}
82184
83185pub fn generate(file: *C, decl: *Decl) !void {
84 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
85 .Fn => try genFn(file, decl),
86 .Array => try genArray(file, decl),
87 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
186 const tv = decl.typed_value.most_recent.typed_value;
187
188 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
189 defer arena.deinit();
190 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
191 defer inst_map.deinit();
192 var ctx = Context{
193 .decl = decl,
194 .arena = &arena,
195 .inst_map = &inst_map,
196 .target = file.base.options.target,
197 };
198 defer {
199 file.error_msg = ctx.error_msg;
200 ctx.deinit();
201 }
202
203 if (tv.val.cast(Value.Payload.Function)) |func_payload| {
204 const writer = file.main.writer();
205 try renderFunctionSignature(&ctx, &file.header, writer, decl);
206
207 try writer.writeAll(" {");
208
209 const func: *Module.Fn = func_payload.func;
210 const instructions = func.analysis.success.instructions;
211 if (instructions.len > 0) {
212 try writer.writeAll("\n");
213 for (instructions) |inst| {
214 const indent_size = 4;
215 const indent_level = 1;
216 try writer.writeByteNTimes(' ', indent_size * indent_level);
217 if (switch (inst.tag) {
218 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
219 .call => try genCall(&ctx, file, inst.castTag(.call).?),
220 .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"),
221 .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"),
222 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),
223 .retvoid => try genRetVoid(file),
224 .arg => try genArg(&ctx),
225 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
226 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
227 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
228 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
229 else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
230 }) |name| {
231 try ctx.inst_map.putNoClobber(inst, name);
232 }
233 }
234 }
235
236 try writer.writeAll("}\n\n");
237 } else if (tv.val.tag() == .extern_fn) {
238 return; // handled when referenced
239 } else {
240 const writer = file.constants.writer();
241 try writer.writeAll("static ");
242
243 // TODO ask the Decl if it is const
244 // https://github.com/ziglang/zig/issues/7582
245
246 try renderType(&ctx, &file.header, writer, tv.ty);
247 try writer.print(" {s} = ", .{decl.name});
248 try renderValue(&ctx, writer, tv.ty, tv.val);
249 try writer.writeAll(";\n");
88250 }
89251}
90252
91253pub fn generateHeader(
92 arena: *std.heap.ArenaAllocator,
254 comp: *Compilation,
93255 module: *Module,
94256 header: *C.Header,
95257 decl: *Decl,
96258) error{ AnalysisFail, OutOfMemory }!void {
97259 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
98260 .Fn => {
99 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
261 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);
100262 defer inst_map.deinit();
263
264 var arena = std.heap.ArenaAllocator.init(comp.gpa);
265 defer arena.deinit();
266
101267 var ctx = Context{
102268 .decl = decl,
103 .arena = arena,
269 .arena = &arena,
104270 .inst_map = &inst_map,
271 .target = comp.getTarget(),
105272 };
106273 const writer = header.buf.writer();
107274 renderFunctionSignature(&ctx, header, writer, decl) catch |err| {
......@@ -116,24 +283,6 @@ pub fn generateHeader(
116283 }
117284}
118285
119fn genArray(file: *C, decl: *Decl) !void {
120 const tv = decl.typed_value.most_recent.typed_value;
121 // TODO: prevent inline asm constants from being emitted
122 const name = try map(file.base.allocator, mem.span(decl.name));
123 defer file.base.allocator.free(name);
124 if (tv.val.cast(Value.Payload.Bytes)) |payload|
125 if (tv.ty.sentinel()) |sentinel|
126 if (sentinel.toUnsignedInt() == 0)
127 // TODO: static by default
128 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
129 else
130 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
131 else
132 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
133 else
134 return file.fail(decl.src(), "TODO non-byte arrays", .{});
135}
136
137286const Context = struct {
138287 decl: *Decl,
139288 inst_map: *std.AutoHashMap(*Inst, []u8),
......@@ -141,6 +290,7 @@ const Context = struct {
141290 argdex: usize = 0,
142291 unnamed_index: usize = 0,
143292 error_msg: *Compilation.ErrorMsg = undefined,
293 target: std.Target,
144294
145295 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
146296 if (inst.cast(Inst.Constant)) |const_inst| {
......@@ -170,55 +320,6 @@ const Context = struct {
170320 }
171321};
172322
173fn genFn(file: *C, decl: *Decl) !void {
174 const writer = file.main.writer();
175 const tv = decl.typed_value.most_recent.typed_value;
176
177 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
178 defer arena.deinit();
179 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
180 defer inst_map.deinit();
181 var ctx = Context{
182 .decl = decl,
183 .arena = &arena,
184 .inst_map = &inst_map,
185 };
186 defer {
187 file.error_msg = ctx.error_msg;
188 ctx.deinit();
189 }
190
191 try renderFunctionSignature(&ctx, &file.header, writer, decl);
192
193 try writer.writeAll(" {");
194
195 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
196 const instructions = func.analysis.success.instructions;
197 if (instructions.len > 0) {
198 try writer.writeAll("\n");
199 for (instructions) |inst| {
200 if (switch (inst.tag) {
201 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
202 .call => try genCall(&ctx, file, inst.castTag(.call).?),
203 .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"),
204 .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"),
205 .ret => try genRet(&ctx, inst.castTag(.ret).?),
206 .retvoid => try genRetVoid(file),
207 .arg => try genArg(&ctx),
208 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
209 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
210 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
211 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
212 else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
213 }) |name| {
214 try ctx.inst_map.putNoClobber(inst, name);
215 }
216 }
217 }
218
219 try writer.writeAll("}\n\n");
220}
221
222323fn genArg(ctx: *Context) !?[]u8 {
223324 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});
224325 ctx.argdex += 1;
......@@ -226,12 +327,24 @@ fn genArg(ctx: *Context) !?[]u8 {
226327}
227328
228329fn genRetVoid(file: *C) !?[]u8 {
229 try file.main.writer().print(indentation ++ "return;\n", .{});
330 try file.main.writer().print("return;\n", .{});
230331 return null;
231332}
232333
233fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
234 return ctx.fail(ctx.decl.src(), "TODO return", .{});
334fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
335 const writer = file.main.writer();
336 try writer.writeAll("return ");
337 try genValue(ctx, writer, inst.operand);
338 try writer.writeAll(";\n");
339 return null;
340}
341
342fn genValue(ctx: *Context, writer: Writer, inst: *Inst) !void {
343 if (inst.value()) |val| {
344 try renderValue(ctx, writer, inst.ty, val);
345 return;
346 }
347 return ctx.fail(ctx.decl.src(), "TODO: C backend: genValue for non-constant value", .{});
235348}
236349
237350fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
......@@ -241,7 +354,7 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
241354 const writer = file.main.writer();
242355 const name = try ctx.name();
243356 const from = try ctx.resolveInst(inst.operand);
244 try writer.writeAll(indentation ++ "const ");
357 try writer.writeAll("const ");
245358 try renderType(ctx, &file.header, writer, inst.base.ty);
246359 try writer.print(" {} = (", .{name});
247360 try renderType(ctx, &file.header, writer, inst.base.ty);
......@@ -256,7 +369,7 @@ fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []con
256369 const rhs = ctx.resolveInst(inst.rhs);
257370 const writer = file.main.writer();
258371 const name = try ctx.name();
259 try writer.writeAll(indentation ++ "const ");
372 try writer.writeAll("const ");
260373 try renderType(ctx, &file.header, writer, inst.base.ty);
261374 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
262375 return name;
......@@ -265,41 +378,42 @@ fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []con
265378fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
266379 const writer = file.main.writer();
267380 const header = file.header.buf.writer();
268 try writer.writeAll(indentation);
269381 if (inst.func.castTag(.constant)) |func_inst| {
270 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
271 const target = func_val.func.owner_decl;
272 const target_ty = target.typed_value.most_recent.typed_value.ty;
273 const ret_ty = target_ty.fnReturnType().tag();
274 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
275 try writer.print("(void)", .{});
276 }
277 const tname = mem.spanZ(target.name);
278 if (file.called.get(tname) == null) {
279 try file.called.put(tname, void{});
280 try renderFunctionSignature(ctx, &file.header, header, target);
281 try header.writeAll(";\n");
282 }
283 try writer.print("{}(", .{tname});
284 if (inst.args.len != 0) {
285 for (inst.args) |arg, i| {
286 if (i > 0) {
287 try writer.writeAll(", ");
288 }
289 if (arg.cast(Inst.Constant)) |con| {
290 try renderValue(ctx, writer, arg.ty, con.val);
291 } else {
292 const val = try ctx.resolveInst(arg);
293 try writer.print("{}", .{val});
294 }
382 const fn_decl = if (func_inst.val.cast(Value.Payload.ExternFn)) |extern_fn|
383 extern_fn.decl
384 else if (func_inst.val.cast(Value.Payload.Function)) |func_val|
385 func_val.func.owner_decl
386 else
387 unreachable;
388
389 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
390 const ret_ty = fn_ty.fnReturnType().tag();
391 if (fn_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
392 try writer.print("(void)", .{});
393 }
394 const fn_name = mem.spanZ(fn_decl.name);
395 if (file.called.get(fn_name) == null) {
396 try file.called.put(fn_name, void{});
397 try renderFunctionSignature(ctx, &file.header, header, fn_decl);
398 try header.writeAll(";\n");
399 }
400 try writer.print("{s}(", .{fn_name});
401 if (inst.args.len != 0) {
402 for (inst.args) |arg, i| {
403 if (i > 0) {
404 try writer.writeAll(", ");
405 }
406 if (arg.cast(Inst.Constant)) |con| {
407 try renderValue(ctx, writer, arg.ty, con.val);
408 } else {
409 const val = try ctx.resolveInst(arg);
410 try writer.print("{}", .{val});
295411 }
296412 }
297 try writer.writeAll(");\n");
298 } else {
299 return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{});
300413 }
414 try writer.writeAll(");\n");
301415 } else {
302 return ctx.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});
416 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});
303417 }
304418 return null;
305419}
......@@ -315,13 +429,12 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
315429}
316430
317431fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {
318 try file.main.writer().writeAll(indentation ++ "zig_unreachable();\n");
432 try file.main.writer().writeAll("zig_unreachable();\n");
319433 return null;
320434}
321435
322436fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
323437 const writer = file.main.writer();
324 try writer.writeAll(indentation);
325438 for (as.inputs) |i, index| {
326439 if (i[0] == '{' and i[i.len - 1] == '}') {
327440 const reg = i[1 .. i.len - 1];
src/link/C.zig+1-15
......@@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c;
1515
1616pub const Header = struct {
1717 buf: std.ArrayList(u8),
18 need_stddef: bool = false,
19 need_stdint: bool = false,
2018 emit_loc: ?Compilation.EmitLoc,
2119
2220 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
......@@ -31,20 +29,8 @@ pub const Header = struct {
3129 defer tracy.end();
3230
3331 try writer.writeAll(@embedFile("cbe.h"));
34 var includes = false;
35 if (self.need_stddef) {
36 try writer.writeAll("#include <stddef.h>\n");
37 includes = true;
38 }
39 if (self.need_stdint) {
40 try writer.writeAll("#include <stdint.h>\n");
41 includes = true;
42 }
43 if (includes) {
44 try writer.writeByte('\n');
45 }
4632 if (self.buf.items.len > 0) {
47 try writer.print("{}", .{self.buf.items});
33 try writer.print("{s}", .{self.buf.items});
4834 }
4935 }
5036
src/link/cbe.h+4
......@@ -23,3 +23,7 @@
2323#define zig_unreachable()
2424#endif
2525
26#include <stdint.h>
27#define int128_t __int128
28#define uint128_t unsigned __int128
29
src/type.zig+91-1
......@@ -172,7 +172,15 @@ pub const Type = extern union {
172172 const is_slice_b = isSlice(b);
173173 if (is_slice_a != is_slice_b)
174174 return false;
175 @panic("TODO implement more pointer Type equality comparison");
175
176 const ptr_size_a = ptrSize(a);
177 const ptr_size_b = ptrSize(b);
178 if (ptr_size_a != ptr_size_b)
179 return false;
180
181 std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{
182 a, b,
183 });
176184 },
177185 .Int => {
178186 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
......@@ -1128,6 +1136,88 @@ pub const Type = extern union {
11281136 };
11291137 }
11301138
1139 /// Asserts the `Type` is a pointer.
1140 pub fn ptrSize(self: Type) std.builtin.TypeInfo.Pointer.Size {
1141 return switch (self.tag()) {
1142 .u8,
1143 .i8,
1144 .u16,
1145 .i16,
1146 .u32,
1147 .i32,
1148 .u64,
1149 .i64,
1150 .usize,
1151 .isize,
1152 .c_short,
1153 .c_ushort,
1154 .c_int,
1155 .c_uint,
1156 .c_long,
1157 .c_ulong,
1158 .c_longlong,
1159 .c_ulonglong,
1160 .c_longdouble,
1161 .f16,
1162 .f32,
1163 .f64,
1164 .f128,
1165 .c_void,
1166 .bool,
1167 .void,
1168 .type,
1169 .anyerror,
1170 .comptime_int,
1171 .comptime_float,
1172 .noreturn,
1173 .@"null",
1174 .@"undefined",
1175 .array,
1176 .array_sentinel,
1177 .array_u8,
1178 .array_u8_sentinel_0,
1179 .fn_noreturn_no_args,
1180 .fn_void_no_args,
1181 .fn_naked_noreturn_no_args,
1182 .fn_ccc_void_no_args,
1183 .function,
1184 .int_unsigned,
1185 .int_signed,
1186 .optional,
1187 .optional_single_mut_pointer,
1188 .optional_single_const_pointer,
1189 .enum_literal,
1190 .error_union,
1191 .@"anyframe",
1192 .anyframe_T,
1193 .anyerror_void_error_union,
1194 .error_set,
1195 .error_set_single,
1196 .empty_struct,
1197 => unreachable,
1198
1199 .const_slice,
1200 .mut_slice,
1201 .const_slice_u8,
1202 => .Slice,
1203
1204 .many_const_pointer,
1205 .many_mut_pointer,
1206 => .Many,
1207
1208 .c_const_pointer,
1209 .c_mut_pointer,
1210 => .C,
1211
1212 .single_const_pointer,
1213 .single_mut_pointer,
1214 .single_const_pointer_to_comptime_int,
1215 => .One,
1216
1217 .pointer => self.cast(Payload.Pointer).?.size,
1218 };
1219 }
1220
11311221 pub fn isSlice(self: Type) bool {
11321222 return switch (self.tag()) {
11331223 .u8,
src/value.zig+24
......@@ -82,6 +82,7 @@ pub const Value = extern union {
8282 int_big_positive,
8383 int_big_negative,
8484 function,
85 extern_fn,
8586 variable,
8687 ref_val,
8788 decl_ref,
......@@ -205,6 +206,7 @@ pub const Value = extern union {
205206 @panic("TODO implement copying of big ints");
206207 },
207208 .function => return self.copyPayloadShallow(allocator, Payload.Function),
209 .extern_fn => return self.copyPayloadShallow(allocator, Payload.ExternFn),
208210 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
209211 .ref_val => {
210212 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
......@@ -337,6 +339,7 @@ pub const Value = extern union {
337339 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
338340 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
339341 .function => return out_stream.writeAll("(function)"),
342 .extern_fn => return out_stream.writeAll("(extern function)"),
340343 .variable => return out_stream.writeAll("(variable)"),
341344 .ref_val => {
342345 const ref_val = val.cast(Payload.RefVal).?;
......@@ -468,6 +471,7 @@ pub const Value = extern union {
468471 .int_big_positive,
469472 .int_big_negative,
470473 .function,
474 .extern_fn,
471475 .variable,
472476 .ref_val,
473477 .decl_ref,
......@@ -533,6 +537,7 @@ pub const Value = extern union {
533537 .anyframe_type,
534538 .null_value,
535539 .function,
540 .extern_fn,
536541 .variable,
537542 .ref_val,
538543 .decl_ref,
......@@ -617,6 +622,7 @@ pub const Value = extern union {
617622 .anyframe_type,
618623 .null_value,
619624 .function,
625 .extern_fn,
620626 .variable,
621627 .ref_val,
622628 .decl_ref,
......@@ -701,6 +707,7 @@ pub const Value = extern union {
701707 .anyframe_type,
702708 .null_value,
703709 .function,
710 .extern_fn,
704711 .variable,
705712 .ref_val,
706713 .decl_ref,
......@@ -812,6 +819,7 @@ pub const Value = extern union {
812819 .anyframe_type,
813820 .null_value,
814821 .function,
822 .extern_fn,
815823 .variable,
816824 .ref_val,
817825 .decl_ref,
......@@ -901,6 +909,7 @@ pub const Value = extern union {
901909 .anyframe_type,
902910 .null_value,
903911 .function,
912 .extern_fn,
904913 .variable,
905914 .ref_val,
906915 .decl_ref,
......@@ -1071,6 +1080,7 @@ pub const Value = extern union {
10711080 .bool_false,
10721081 .null_value,
10731082 .function,
1083 .extern_fn,
10741084 .variable,
10751085 .ref_val,
10761086 .decl_ref,
......@@ -1150,6 +1160,7 @@ pub const Value = extern union {
11501160 .anyframe_type,
11511161 .null_value,
11521162 .function,
1163 .extern_fn,
11531164 .variable,
11541165 .ref_val,
11551166 .decl_ref,
......@@ -1383,6 +1394,10 @@ pub const Value = extern union {
13831394 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
13841395 std.hash.autoHash(&hasher, payload.func);
13851396 },
1397 .extern_fn => {
1398 const payload = @fieldParentPtr(Payload.ExternFn, "base", self.ptr_otherwise);
1399 std.hash.autoHash(&hasher, payload.decl);
1400 },
13861401 .variable => {
13871402 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);
13881403 std.hash.autoHash(&hasher, payload.variable);
......@@ -1449,6 +1464,7 @@ pub const Value = extern union {
14491464 .bool_false,
14501465 .null_value,
14511466 .function,
1467 .extern_fn,
14521468 .variable,
14531469 .int_u64,
14541470 .int_i64,
......@@ -1533,6 +1549,7 @@ pub const Value = extern union {
15331549 .bool_false,
15341550 .null_value,
15351551 .function,
1552 .extern_fn,
15361553 .variable,
15371554 .int_u64,
15381555 .int_i64,
......@@ -1634,6 +1651,7 @@ pub const Value = extern union {
16341651 .bool_true,
16351652 .bool_false,
16361653 .function,
1654 .extern_fn,
16371655 .variable,
16381656 .int_u64,
16391657 .int_i64,
......@@ -1730,6 +1748,7 @@ pub const Value = extern union {
17301748 .bool_true,
17311749 .bool_false,
17321750 .function,
1751 .extern_fn,
17331752 .variable,
17341753 .int_u64,
17351754 .int_i64,
......@@ -1793,6 +1812,11 @@ pub const Value = extern union {
17931812 func: *Module.Fn,
17941813 };
17951814
1815 pub const ExternFn = struct {
1816 base: Payload = Payload{ .tag = .extern_fn },
1817 decl: *Module.Decl,
1818 };
1819
17961820 pub const Variable = struct {
17971821 base: Payload = Payload{ .tag = .variable },
17981822 variable: *Module.Var,