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...@@ -1431,9 +1431,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1431 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);1431 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
1432 defer c_comp_progress_node.end();1432 defer c_comp_progress_node.end();
14331433
1434 var arena = std.heap.ArenaAllocator.init(self.gpa);
1435 defer arena.deinit();
1436
1437 self.work_queue_wait_group.reset();1434 self.work_queue_wait_group.reset();
1438 defer self.work_queue_wait_group.wait();1435 defer self.work_queue_wait_group.wait();
14391436
...@@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1502 };1499 };
15031500
1504 if (self.c_header) |*header| {1501 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) {
1506 error.OutOfMemory => return error.OutOfMemory,1503 error.OutOfMemory => return error.OutOfMemory,
1507 error.AnalysisFail => {1504 error.AnalysisFail => {
1508 decl.analysis = .dependency_failure;1505 decl.analysis = .dependency_failure;
src/Module.zig+86-11
...@@ -277,6 +277,8 @@ pub const Decl = struct {...@@ -277,6 +277,8 @@ pub const Decl = struct {
277};277};
278278
279/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.279/// 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`.
280pub const Fn = struct {282pub const Fn = struct {
281 /// This memory owned by the Decl's TypedValue.Managed arena allocator.283 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
282 analysis: union(enum) {284 analysis: union(enum) {
...@@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1010 defer fn_type_scope.instructions.deinit(self.gpa);1012 defer fn_type_scope.instructions.deinit(self.gpa);
10111013
1012 decl.is_pub = fn_proto.getVisibToken() != null;1014 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
1016 const param_decls = fn_proto.params();1016 const param_decls = fn_proto.params();
1017 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);1017 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 {...@@ -1083,6 +1083,36 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1083 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{1083 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
1084 .instructions = fn_type_scope.instructions.items,1084 .instructions = fn_type_scope.instructions.items,
1085 });1085 });
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
1086 const new_func = try decl_arena.allocator.create(Fn);1116 const new_func = try decl_arena.allocator.create(Fn);
1087 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);1117 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 {...@@ -1899,7 +1929,13 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1899 return null;1929 return null;
1900}1930}
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 {
1903 try self.ensureDeclAnalyzed(exported_decl);1939 try self.ensureDeclAnalyzed(exported_decl);
1904 const typed_value = exported_decl.typed_value.most_recent.typed_value;1940 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1905 switch (typed_value.ty.zigTypeTag()) {1941 switch (typed_value.ty.zigTypeTag()) {
...@@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2801 }2837 }
2802 }2838 }
28032839
2804 // *[N]T to []T2840 // Coercions where the source is a single pointer to an array.
2805 if (inst.ty.isSinglePointer() and dest_type.isSlice() and2841 src_array_ptr: {
2806 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))2842 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
2807 {
2808 const array_type = inst.ty.elemType();2843 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
2809 const dst_elem_type = dest_type.elemType();2849 const dst_elem_type = dest_type.elemType();
2810 if (array_type.zigTypeTag() == .Array and2850 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
2811 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)2851 .ok => {},
2812 {2852 .no_match => break :src_array_ptr,
2813 return self.coerceArrayPtrToSlice(scope, dest_type, inst);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 => {},
2814 }2881 }
2815 }2882 }
28162883
...@@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I...@@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
2918 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});2985 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2919}2986}
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
2921pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {2996pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
2922 @setCold(true);2997 @setCold(true);
2923 const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);2998 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;...@@ -12,7 +12,7 @@ const C = link.File.C;
12const Decl = Module.Decl;12const Decl = Module.Decl;
13const mem = std.mem;13const mem = std.mem;
1414
15const indentation = " ";15const Writer = std.ArrayList(u8).Writer;
1616
17/// Maps a name from Zig source to C. Currently, this will always give the same17/// Maps a name from Zig source to C. Currently, this will always give the same
18/// output for any given input, sometimes resulting in broken identifiers.18/// 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 {...@@ -20,43 +20,145 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
20 return allocator.dupe(u8, name);20 return allocator.dupe(u8, name);
21}21}
2222
23fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void {23fn renderType(
24 switch (T.zigTypeTag()) {24 ctx: *Context,
25 header: *C.Header,
26 writer: Writer,
27 t: Type,
28) error{ OutOfMemory, AnalysisFail }!void {
29 switch (t.zigTypeTag()) {
25 .NoReturn => {30 .NoReturn => {
26 try writer.writeAll("zig_noreturn void");31 try writer.writeAll("zig_noreturn void");
27 },32 },
28 .Void => try writer.writeAll("void"),33 .Void => try writer.writeAll("void"),
29 .Bool => try writer.writeAll("bool"),34 .Bool => try writer.writeAll("bool"),
30 .Int => {35 .Int => {
31 if (T.tag() == .u8) {36 switch (t.tag()) {
32 header.need_stdint = true;37 .u8 => try writer.writeAll("uint8_t"),
33 try writer.writeAll("uint8_t");38 .i8 => try writer.writeAll("int8_t"),
34 } else if (T.tag() == .u32) {39 .u16 => try writer.writeAll("uint16_t"),
35 header.need_stdint = true;40 .i16 => try writer.writeAll("int16_t"),
36 try writer.writeAll("uint32_t");41 .u32 => try writer.writeAll("uint32_t"),
37 } else if (T.tag() == .usize) {42 .i32 => try writer.writeAll("int32_t"),
38 header.need_stddef = true;43 .u64 => try writer.writeAll("uint64_t"),
39 try writer.writeAll("size_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", .{});
40 } else {76 } 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(" *");
42 }85 }
43 },86 },
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 }),
45 }96 }
46}97}
4798
48fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {99fn renderValue(
49 switch (T.zigTypeTag()) {100 ctx: *Context,
101 writer: Writer,
102 t: Type,
103 val: Value,
104) error{ OutOfMemory, AnalysisFail }!void {
105 switch (t.zigTypeTag()) {
50 .Int => {106 .Int => {
51 if (T.isSignedInt())107 if (t.isSignedInt())
52 return writer.print("{}", .{val.toSignedInt()});108 return writer.print("{d}", .{val.toSignedInt()});
53 return writer.print("{}", .{val.toUnsignedInt()});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("}");
54 },149 },
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 }),
56 }153 }
57}154}
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 {
60 const tv = decl.typed_value.most_recent.typed_value;162 const tv = decl.typed_value.most_recent.typed_value;
61 try renderType(ctx, header, writer, tv.ty.fnReturnType());163 try renderType(ctx, header, writer, tv.ty.fnReturnType());
62 // Use the child allocator directly, as we know the name can be freed before164 // 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...@@ -81,27 +183,92 @@ fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayLi
81}183}
82184
83pub fn generate(file: *C, decl: *Decl) !void {185pub fn generate(file: *C, decl: *Decl) !void {
84 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {186 const tv = decl.typed_value.most_recent.typed_value;
85 .Fn => try genFn(file, decl),187
86 .Array => try genArray(file, decl),188 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
87 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),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");
88 }250 }
89}251}
90252
91pub fn generateHeader(253pub fn generateHeader(
92 arena: *std.heap.ArenaAllocator,254 comp: *Compilation,
93 module: *Module,255 module: *Module,
94 header: *C.Header,256 header: *C.Header,
95 decl: *Decl,257 decl: *Decl,
96) error{ AnalysisFail, OutOfMemory }!void {258) error{ AnalysisFail, OutOfMemory }!void {
97 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {259 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
98 .Fn => {260 .Fn => {
99 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);261 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);
100 defer inst_map.deinit();262 defer inst_map.deinit();
263
264 var arena = std.heap.ArenaAllocator.init(comp.gpa);
265 defer arena.deinit();
266
101 var ctx = Context{267 var ctx = Context{
102 .decl = decl,268 .decl = decl,
103 .arena = arena,269 .arena = &arena,
104 .inst_map = &inst_map,270 .inst_map = &inst_map,
271 .target = comp.getTarget(),
105 };272 };
106 const writer = header.buf.writer();273 const writer = header.buf.writer();
107 renderFunctionSignature(&ctx, header, writer, decl) catch |err| {274 renderFunctionSignature(&ctx, header, writer, decl) catch |err| {
...@@ -116,24 +283,6 @@ pub fn generateHeader(...@@ -116,24 +283,6 @@ pub fn generateHeader(
116 }283 }
117}284}
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
137const Context = struct {286const Context = struct {
138 decl: *Decl,287 decl: *Decl,
139 inst_map: *std.AutoHashMap(*Inst, []u8),288 inst_map: *std.AutoHashMap(*Inst, []u8),
...@@ -141,6 +290,7 @@ const Context = struct {...@@ -141,6 +290,7 @@ const Context = struct {
141 argdex: usize = 0,290 argdex: usize = 0,
142 unnamed_index: usize = 0,291 unnamed_index: usize = 0,
143 error_msg: *Compilation.ErrorMsg = undefined,292 error_msg: *Compilation.ErrorMsg = undefined,
293 target: std.Target,
144294
145 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {295 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
146 if (inst.cast(Inst.Constant)) |const_inst| {296 if (inst.cast(Inst.Constant)) |const_inst| {
...@@ -170,55 +320,6 @@ const Context = struct {...@@ -170,55 +320,6 @@ const Context = struct {
170 }320 }
171};321};
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
222fn genArg(ctx: *Context) !?[]u8 {323fn genArg(ctx: *Context) !?[]u8 {
223 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});324 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});
224 ctx.argdex += 1;325 ctx.argdex += 1;
...@@ -226,12 +327,24 @@ fn genArg(ctx: *Context) !?[]u8 {...@@ -226,12 +327,24 @@ fn genArg(ctx: *Context) !?[]u8 {
226}327}
227328
228fn genRetVoid(file: *C) !?[]u8 {329fn genRetVoid(file: *C) !?[]u8 {
229 try file.main.writer().print(indentation ++ "return;\n", .{});330 try file.main.writer().print("return;\n", .{});
230 return null;331 return null;
231}332}
232333
233fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {334fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
234 return ctx.fail(ctx.decl.src(), "TODO return", .{});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", .{});
235}348}
236349
237fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {350fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
...@@ -241,7 +354,7 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {...@@ -241,7 +354,7 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
241 const writer = file.main.writer();354 const writer = file.main.writer();
242 const name = try ctx.name();355 const name = try ctx.name();
243 const from = try ctx.resolveInst(inst.operand);356 const from = try ctx.resolveInst(inst.operand);
244 try writer.writeAll(indentation ++ "const ");357 try writer.writeAll("const ");
245 try renderType(ctx, &file.header, writer, inst.base.ty);358 try renderType(ctx, &file.header, writer, inst.base.ty);
246 try writer.print(" {} = (", .{name});359 try writer.print(" {} = (", .{name});
247 try renderType(ctx, &file.header, writer, inst.base.ty);360 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...@@ -256,7 +369,7 @@ fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []con
256 const rhs = ctx.resolveInst(inst.rhs);369 const rhs = ctx.resolveInst(inst.rhs);
257 const writer = file.main.writer();370 const writer = file.main.writer();
258 const name = try ctx.name();371 const name = try ctx.name();
259 try writer.writeAll(indentation ++ "const ");372 try writer.writeAll("const ");
260 try renderType(ctx, &file.header, writer, inst.base.ty);373 try renderType(ctx, &file.header, writer, inst.base.ty);
261 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });374 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
262 return name;375 return name;
...@@ -265,41 +378,42 @@ fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []con...@@ -265,41 +378,42 @@ fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []con
265fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {378fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
266 const writer = file.main.writer();379 const writer = file.main.writer();
267 const header = file.header.buf.writer();380 const header = file.header.buf.writer();
268 try writer.writeAll(indentation);
269 if (inst.func.castTag(.constant)) |func_inst| {381 if (inst.func.castTag(.constant)) |func_inst| {
270 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {382 const fn_decl = if (func_inst.val.cast(Value.Payload.ExternFn)) |extern_fn|
271 const target = func_val.func.owner_decl;383 extern_fn.decl
272 const target_ty = target.typed_value.most_recent.typed_value.ty;384 else if (func_inst.val.cast(Value.Payload.Function)) |func_val|
273 const ret_ty = target_ty.fnReturnType().tag();385 func_val.func.owner_decl
274 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {386 else
275 try writer.print("(void)", .{});387 unreachable;
276 }388
277 const tname = mem.spanZ(target.name);389 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
278 if (file.called.get(tname) == null) {390 const ret_ty = fn_ty.fnReturnType().tag();
279 try file.called.put(tname, void{});391 if (fn_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
280 try renderFunctionSignature(ctx, &file.header, header, target);392 try writer.print("(void)", .{});
281 try header.writeAll(";\n");393 }
282 }394 const fn_name = mem.spanZ(fn_decl.name);
283 try writer.print("{}(", .{tname});395 if (file.called.get(fn_name) == null) {
284 if (inst.args.len != 0) {396 try file.called.put(fn_name, void{});
285 for (inst.args) |arg, i| {397 try renderFunctionSignature(ctx, &file.header, header, fn_decl);
286 if (i > 0) {398 try header.writeAll(";\n");
287 try writer.writeAll(", ");399 }
288 }400 try writer.print("{s}(", .{fn_name});
289 if (arg.cast(Inst.Constant)) |con| {401 if (inst.args.len != 0) {
290 try renderValue(ctx, writer, arg.ty, con.val);402 for (inst.args) |arg, i| {
291 } else {403 if (i > 0) {
292 const val = try ctx.resolveInst(arg);404 try writer.writeAll(", ");
293 try writer.print("{}", .{val});405 }
294 }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});
295 }411 }
296 }412 }
297 try writer.writeAll(");\n");
298 } else {
299 return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{});
300 }413 }
414 try writer.writeAll(");\n");
301 } else {415 } 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", .{});
303 }417 }
304 return null;418 return null;
305}419}
...@@ -315,13 +429,12 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {...@@ -315,13 +429,12 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
315}429}
316430
317fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {431fn 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");
319 return null;433 return null;
320}434}
321435
322fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {436fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
323 const writer = file.main.writer();437 const writer = file.main.writer();
324 try writer.writeAll(indentation);
325 for (as.inputs) |i, index| {438 for (as.inputs) |i, index| {
326 if (i[0] == '{' and i[i.len - 1] == '}') {439 if (i[0] == '{' and i[i.len - 1] == '}') {
327 const reg = i[1 .. i.len - 1];440 const reg = i[1 .. i.len - 1];
src/link/C.zig+1-15
...@@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c;...@@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c;
1515
16pub const Header = struct {16pub const Header = struct {
17 buf: std.ArrayList(u8),17 buf: std.ArrayList(u8),
18 need_stddef: bool = false,
19 need_stdint: bool = false,
20 emit_loc: ?Compilation.EmitLoc,18 emit_loc: ?Compilation.EmitLoc,
2119
22 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {20 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
...@@ -31,20 +29,8 @@ pub const Header = struct {...@@ -31,20 +29,8 @@ pub const Header = struct {
31 defer tracy.end();29 defer tracy.end();
3230
33 try writer.writeAll(@embedFile("cbe.h"));31 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 }
46 if (self.buf.items.len > 0) {32 if (self.buf.items.len > 0) {
47 try writer.print("{}", .{self.buf.items});33 try writer.print("{s}", .{self.buf.items});
48 }34 }
49 }35 }
5036
src/link/cbe.h+4
...@@ -23,3 +23,7 @@...@@ -23,3 +23,7 @@
23#define zig_unreachable()23#define zig_unreachable()
24#endif24#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 {...@@ -172,7 +172,15 @@ pub const Type = extern union {
172 const is_slice_b = isSlice(b);172 const is_slice_b = isSlice(b);
173 if (is_slice_a != is_slice_b)173 if (is_slice_a != is_slice_b)
174 return false;174 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 });
176 },184 },
177 .Int => {185 .Int => {
178 // Detect that e.g. u64 != usize, even if the bits match on a particular target.186 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
...@@ -1128,6 +1136,88 @@ pub const Type = extern union {...@@ -1128,6 +1136,88 @@ pub const Type = extern union {
1128 };1136 };
1129 }1137 }
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
1131 pub fn isSlice(self: Type) bool {1221 pub fn isSlice(self: Type) bool {
1132 return switch (self.tag()) {1222 return switch (self.tag()) {
1133 .u8,1223 .u8,
src/value.zig+24
...@@ -82,6 +82,7 @@ pub const Value = extern union {...@@ -82,6 +82,7 @@ pub const Value = extern union {
82 int_big_positive,82 int_big_positive,
83 int_big_negative,83 int_big_negative,
84 function,84 function,
85 extern_fn,
85 variable,86 variable,
86 ref_val,87 ref_val,
87 decl_ref,88 decl_ref,
...@@ -205,6 +206,7 @@ pub const Value = extern union {...@@ -205,6 +206,7 @@ pub const Value = extern union {
205 @panic("TODO implement copying of big ints");206 @panic("TODO implement copying of big ints");
206 },207 },
207 .function => return self.copyPayloadShallow(allocator, Payload.Function),208 .function => return self.copyPayloadShallow(allocator, Payload.Function),
209 .extern_fn => return self.copyPayloadShallow(allocator, Payload.ExternFn),
208 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),210 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
209 .ref_val => {211 .ref_val => {
210 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);212 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
...@@ -337,6 +339,7 @@ pub const Value = extern union {...@@ -337,6 +339,7 @@ pub const Value = extern union {
337 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),339 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
338 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),340 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
339 .function => return out_stream.writeAll("(function)"),341 .function => return out_stream.writeAll("(function)"),
342 .extern_fn => return out_stream.writeAll("(extern function)"),
340 .variable => return out_stream.writeAll("(variable)"),343 .variable => return out_stream.writeAll("(variable)"),
341 .ref_val => {344 .ref_val => {
342 const ref_val = val.cast(Payload.RefVal).?;345 const ref_val = val.cast(Payload.RefVal).?;
...@@ -468,6 +471,7 @@ pub const Value = extern union {...@@ -468,6 +471,7 @@ pub const Value = extern union {
468 .int_big_positive,471 .int_big_positive,
469 .int_big_negative,472 .int_big_negative,
470 .function,473 .function,
474 .extern_fn,
471 .variable,475 .variable,
472 .ref_val,476 .ref_val,
473 .decl_ref,477 .decl_ref,
...@@ -533,6 +537,7 @@ pub const Value = extern union {...@@ -533,6 +537,7 @@ pub const Value = extern union {
533 .anyframe_type,537 .anyframe_type,
534 .null_value,538 .null_value,
535 .function,539 .function,
540 .extern_fn,
536 .variable,541 .variable,
537 .ref_val,542 .ref_val,
538 .decl_ref,543 .decl_ref,
...@@ -617,6 +622,7 @@ pub const Value = extern union {...@@ -617,6 +622,7 @@ pub const Value = extern union {
617 .anyframe_type,622 .anyframe_type,
618 .null_value,623 .null_value,
619 .function,624 .function,
625 .extern_fn,
620 .variable,626 .variable,
621 .ref_val,627 .ref_val,
622 .decl_ref,628 .decl_ref,
...@@ -701,6 +707,7 @@ pub const Value = extern union {...@@ -701,6 +707,7 @@ pub const Value = extern union {
701 .anyframe_type,707 .anyframe_type,
702 .null_value,708 .null_value,
703 .function,709 .function,
710 .extern_fn,
704 .variable,711 .variable,
705 .ref_val,712 .ref_val,
706 .decl_ref,713 .decl_ref,
...@@ -812,6 +819,7 @@ pub const Value = extern union {...@@ -812,6 +819,7 @@ pub const Value = extern union {
812 .anyframe_type,819 .anyframe_type,
813 .null_value,820 .null_value,
814 .function,821 .function,
822 .extern_fn,
815 .variable,823 .variable,
816 .ref_val,824 .ref_val,
817 .decl_ref,825 .decl_ref,
...@@ -901,6 +909,7 @@ pub const Value = extern union {...@@ -901,6 +909,7 @@ pub const Value = extern union {
901 .anyframe_type,909 .anyframe_type,
902 .null_value,910 .null_value,
903 .function,911 .function,
912 .extern_fn,
904 .variable,913 .variable,
905 .ref_val,914 .ref_val,
906 .decl_ref,915 .decl_ref,
...@@ -1071,6 +1080,7 @@ pub const Value = extern union {...@@ -1071,6 +1080,7 @@ pub const Value = extern union {
1071 .bool_false,1080 .bool_false,
1072 .null_value,1081 .null_value,
1073 .function,1082 .function,
1083 .extern_fn,
1074 .variable,1084 .variable,
1075 .ref_val,1085 .ref_val,
1076 .decl_ref,1086 .decl_ref,
...@@ -1150,6 +1160,7 @@ pub const Value = extern union {...@@ -1150,6 +1160,7 @@ pub const Value = extern union {
1150 .anyframe_type,1160 .anyframe_type,
1151 .null_value,1161 .null_value,
1152 .function,1162 .function,
1163 .extern_fn,
1153 .variable,1164 .variable,
1154 .ref_val,1165 .ref_val,
1155 .decl_ref,1166 .decl_ref,
...@@ -1383,6 +1394,10 @@ pub const Value = extern union {...@@ -1383,6 +1394,10 @@ pub const Value = extern union {
1383 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);1394 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1384 std.hash.autoHash(&hasher, payload.func);1395 std.hash.autoHash(&hasher, payload.func);
1385 },1396 },
1397 .extern_fn => {
1398 const payload = @fieldParentPtr(Payload.ExternFn, "base", self.ptr_otherwise);
1399 std.hash.autoHash(&hasher, payload.decl);
1400 },
1386 .variable => {1401 .variable => {
1387 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);1402 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);
1388 std.hash.autoHash(&hasher, payload.variable);1403 std.hash.autoHash(&hasher, payload.variable);
...@@ -1449,6 +1464,7 @@ pub const Value = extern union {...@@ -1449,6 +1464,7 @@ pub const Value = extern union {
1449 .bool_false,1464 .bool_false,
1450 .null_value,1465 .null_value,
1451 .function,1466 .function,
1467 .extern_fn,
1452 .variable,1468 .variable,
1453 .int_u64,1469 .int_u64,
1454 .int_i64,1470 .int_i64,
...@@ -1533,6 +1549,7 @@ pub const Value = extern union {...@@ -1533,6 +1549,7 @@ pub const Value = extern union {
1533 .bool_false,1549 .bool_false,
1534 .null_value,1550 .null_value,
1535 .function,1551 .function,
1552 .extern_fn,
1536 .variable,1553 .variable,
1537 .int_u64,1554 .int_u64,
1538 .int_i64,1555 .int_i64,
...@@ -1634,6 +1651,7 @@ pub const Value = extern union {...@@ -1634,6 +1651,7 @@ pub const Value = extern union {
1634 .bool_true,1651 .bool_true,
1635 .bool_false,1652 .bool_false,
1636 .function,1653 .function,
1654 .extern_fn,
1637 .variable,1655 .variable,
1638 .int_u64,1656 .int_u64,
1639 .int_i64,1657 .int_i64,
...@@ -1730,6 +1748,7 @@ pub const Value = extern union {...@@ -1730,6 +1748,7 @@ pub const Value = extern union {
1730 .bool_true,1748 .bool_true,
1731 .bool_false,1749 .bool_false,
1732 .function,1750 .function,
1751 .extern_fn,
1733 .variable,1752 .variable,
1734 .int_u64,1753 .int_u64,
1735 .int_i64,1754 .int_i64,
...@@ -1793,6 +1812,11 @@ pub const Value = extern union {...@@ -1793,6 +1812,11 @@ pub const Value = extern union {
1793 func: *Module.Fn,1812 func: *Module.Fn,
1794 };1813 };
17951814
1815 pub const ExternFn = struct {
1816 base: Payload = Payload{ .tag = .extern_fn },
1817 decl: *Module.Decl,
1818 };
1819
1796 pub const Variable = struct {1820 pub const Variable = struct {
1797 base: Payload = Payload{ .tag = .variable },1821 base: Payload = Payload{ .tag = .variable },
1798 variable: *Module.Var,1822 variable: *Module.Var,