Commit 556f0ce5bf

Jakub Konka <kubkon@jakubkonka.com>
2022-02-05 17:41:03
stage2: add new Decl subtype, ExternFn
`ExternFn` will contain a maybe-lib-name if it was defined with the `extern` keyword like so ```zig extern "c" fn write(usize, usize, usize) usize; ``` `lib_name` will live as long as `ExternFn` decl does.
1 parent 04f379d
Changed files (8)
src/arch/aarch64/CodeGen.zig
@@ -1574,8 +1574,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
                     .data = .{ .reg = .x30 },
                 });
             } else if (func_value.castTag(.extern_fn)) |func_payload| {
-                const decl = func_payload.data;
-                const n_strx = try macho_file.addExternFn(mem.sliceTo(decl.name, 0));
+                const extern_fn = func_payload.data;
+                const decl_name = extern_fn.owner_decl.name;
+                if (extern_fn.lib_name) |lib_name| {
+                    log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
+                        decl_name,
+                        lib_name,
+                    });
+                }
+                const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0));
 
                 _ = try self.addInst(.{
                     .tag = .call_extern,
src/arch/wasm/CodeGen.zig
@@ -952,7 +952,7 @@ pub const DeclGen = struct {
             _ = func_payload;
             return self.fail("TODO wasm backend genDecl function pointer", .{});
         } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
-            const ext_decl = extern_fn.data;
+            const ext_decl = extern_fn.data.owner_decl;
             var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target());
             func_type.deinit(self.gpa);
             ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
@@ -978,7 +978,7 @@ pub const DeclGen = struct {
         switch (ty.zigTypeTag()) {
             .Fn => {
                 const fn_decl = switch (val.tag()) {
-                    .extern_fn => val.castTag(.extern_fn).?.data,
+                    .extern_fn => val.castTag(.extern_fn).?.data.owner_decl,
                     .function => val.castTag(.function).?.data.owner_decl,
                     else => unreachable,
                 };
@@ -1776,7 +1776,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
         if (func_val.castTag(.function)) |func| {
             break :blk func.data.owner_decl;
         } else if (func_val.castTag(.extern_fn)) |ext_fn| {
-            break :blk ext_fn.data;
+            break :blk ext_fn.data.owner_decl;
         } else if (func_val.castTag(.decl_ref)) |decl_ref| {
             break :blk decl_ref.data;
         }
src/arch/x86_64/CodeGen.zig
@@ -2516,8 +2516,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
                     .data = undefined,
                 });
             } else if (func_value.castTag(.extern_fn)) |func_payload| {
-                const decl = func_payload.data;
-                const n_strx = try macho_file.addExternFn(mem.sliceTo(decl.name, 0));
+                const extern_fn = func_payload.data;
+                const decl_name = extern_fn.owner_decl.name;
+                if (extern_fn.lib_name) |lib_name| {
+                    log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
+                        decl_name,
+                        lib_name,
+                    });
+                }
+                const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0));
                 _ = try self.addInst(.{
                     .tag = .call_extern,
                     .ops = undefined,
src/codegen/c.zig
@@ -542,8 +542,8 @@ pub const DeclGen = struct {
                     try dg.renderDeclName(func.owner_decl, writer);
                 },
                 .extern_fn => {
-                    const decl = val.castTag(.extern_fn).?.data;
-                    try dg.renderDeclName(decl, writer);
+                    const extern_fn = val.castTag(.extern_fn).?.data;
+                    try dg.renderDeclName(extern_fn.owner_decl, writer);
                 },
                 .int_u64, .one => {
                     try writer.writeAll("((");
@@ -681,7 +681,7 @@ pub const DeclGen = struct {
                     return dg.renderDeclValue(writer, ty, val, decl);
                 },
                 .extern_fn => {
-                    const decl = val.castTag(.extern_fn).?.data;
+                    const decl = val.castTag(.extern_fn).?.data.owner_decl;
                     return dg.renderDeclValue(writer, ty, val, decl);
                 },
                 else => unreachable,
@@ -2442,7 +2442,7 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
             const fn_decl = fn_decl: {
                 const callee_val = f.air.value(pl_op.operand) orelse break :known;
                 break :fn_decl switch (callee_val.tag()) {
-                    .extern_fn => callee_val.castTag(.extern_fn).?.data,
+                    .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl,
                     .function => callee_val.castTag(.function).?.data.owner_decl,
                     .decl_ref => callee_val.castTag(.decl_ref).?.data,
                     else => break :known,
src/codegen/llvm.zig
@@ -622,7 +622,7 @@ pub const DeclGen = struct {
             _ = func_payload;
             @panic("TODO llvm backend genDecl function pointer");
         } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
-            _ = try dg.resolveLlvmFunction(extern_fn.data);
+            _ = try dg.resolveLlvmFunction(extern_fn.data.owner_decl);
         } else {
             const target = dg.module.getTarget();
             const global = try dg.resolveGlobalDecl(decl);
@@ -1410,7 +1410,7 @@ pub const DeclGen = struct {
             },
             .Fn => {
                 const fn_decl = switch (tv.val.tag()) {
-                    .extern_fn => tv.val.castTag(.extern_fn).?.data,
+                    .extern_fn => tv.val.castTag(.extern_fn).?.data.owner_decl,
                     .function => tv.val.castTag(.function).?.data.owner_decl,
                     else => unreachable,
                 };
src/Module.zig
@@ -501,6 +501,10 @@ pub const Decl = struct {
     }
 
     pub fn clearValues(decl: *Decl, gpa: Allocator) void {
+        if (decl.getExternFn()) |extern_fn| {
+            extern_fn.deinit(gpa);
+            gpa.destroy(extern_fn);
+        }
         if (decl.getFunction()) |func| {
             func.deinit(gpa);
             gpa.destroy(func);
@@ -690,6 +694,13 @@ pub const Decl = struct {
         return func;
     }
 
+    pub fn getExternFn(decl: *const Decl) ?*ExternFn {
+        if (!decl.owns_tv) return null;
+        const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data;
+        assert(extern_fn.owner_decl == decl);
+        return extern_fn;
+    }
+
     pub fn getVariable(decl: *Decl) ?*Var {
         if (!decl.owns_tv) return null;
         const variable = (decl.val.castTag(.variable) orelse return null).data;
@@ -1320,9 +1331,26 @@ pub const Opaque = struct {
     }
 };
 
+/// Some extern function struct memory is owned by the Decl's TypedValue.Managed
+/// arena allocator.
+pub const ExternFn = struct {
+    /// The Decl that corresponds to the function itself.
+    owner_decl: *Decl,
+    /// Library name if specified.
+    /// For example `extern "c" fn write(...) usize` would have 'c' as library name.
+    /// Allocated with Module's allocator; outlives the ZIR code.
+    lib_name: ?[*:0]const u8,
+
+    pub fn deinit(extern_fn: *ExternFn, gpa: Allocator) void {
+        if (extern_fn.lib_name) |lib_name| {
+            gpa.free(mem.sliceTo(lib_name, 0));
+        }
+    }
+};
+
 /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
-/// Extern functions do not have this data structure; they are represented by
-/// the `Decl` only, with a `Value` tag of `extern_fn`.
+/// Extern functions do not have this data structure; they are represented by `ExternFn`
+/// instead.
 pub const Fn = struct {
     /// The Decl that corresponds to the function itself.
     owner_decl: *Decl,
@@ -3768,8 +3796,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
             }
         },
         .extern_fn => {
-            const owner_decl = decl_tv.val.castTag(.extern_fn).?.data;
-            if (decl == owner_decl) {
+            const extern_fn = decl_tv.val.castTag(.extern_fn).?.data;
+            if (extern_fn.owner_decl == decl) {
                 decl.owns_tv = true;
                 queue_linker_work = true;
                 is_extern = true;
src/Sema.zig
@@ -5614,10 +5614,24 @@ fn funcCommon(
     }
 
     if (is_extern) {
-        return sema.addConstant(
-            fn_ty,
-            try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl),
-        );
+        const new_extern_fn = try sema.gpa.create(Module.ExternFn);
+        errdefer sema.gpa.destroy(new_extern_fn);
+
+        const lib_name: ?[*:0]const u8 = if (opt_lib_name) |lib_name| blk: {
+            break :blk try sema.gpa.dupeZ(u8, lib_name);
+        } else null;
+
+        new_extern_fn.* = Module.ExternFn{
+            .owner_decl = sema.owner_decl,
+            .lib_name = lib_name,
+        };
+
+        const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn);
+        extern_fn_payload.* = .{
+            .base = .{ .tag = .extern_fn },
+            .data = new_extern_fn,
+        };
+        return sema.addConstant(fn_ty, Value.initPayload(&extern_fn_payload.base));
     }
 
     if (!has_body) {
src/value.zig
@@ -262,9 +262,9 @@ pub const Value = extern union {
                 .int_big_negative,
                 => Payload.BigInt,
 
-                .extern_fn,
-                .decl_ref,
-                => Payload.Decl,
+                .extern_fn => Payload.ExternFn,
+
+                .decl_ref => Payload.Decl,
 
                 .repeated,
                 .eu_payload,
@@ -475,7 +475,7 @@ pub const Value = extern union {
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
             .function => return self.copyPayloadShallow(arena, Payload.Function),
-            .extern_fn => return self.copyPayloadShallow(arena, Payload.Decl),
+            .extern_fn => return self.copyPayloadShallow(arena, Payload.ExternFn),
             .variable => return self.copyPayloadShallow(arena, Payload.Variable),
             .decl_ref => return self.copyPayloadShallow(arena, Payload.Decl),
             .decl_ref_mut => return self.copyPayloadShallow(arena, Payload.DeclRefMut),
@@ -1803,9 +1803,10 @@ pub const Value = extern union {
     pub fn pointerDecl(val: Value) ?*Module.Decl {
         return switch (val.tag()) {
             .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl,
-            .extern_fn, .decl_ref => val.cast(Payload.Decl).?.data,
+            .extern_fn => val.castTag(.extern_fn).?.data.owner_decl,
             .function => val.castTag(.function).?.data.owner_decl,
             .variable => val.castTag(.variable).?.data.owner_decl,
+            .decl_ref => val.cast(Payload.Decl).?.data,
             else => null,
         };
     }
@@ -1872,9 +1873,10 @@ pub const Value = extern union {
     pub fn markReferencedDeclsAlive(val: Value) void {
         switch (val.tag()) {
             .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(),
-            .extern_fn, .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(),
+            .extern_fn => return val.castTag(.extern_fn).?.data.owner_decl.markAlive(),
             .function => return val.castTag(.function).?.data.owner_decl.markAlive(),
             .variable => return val.castTag(.variable).?.data.owner_decl.markAlive(),
+            .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(),
 
             .repeated,
             .eu_payload,
@@ -3148,6 +3150,11 @@ pub const Value = extern union {
             data: *Module.Fn,
         };
 
+        pub const ExternFn = struct {
+            base: Payload,
+            data: *Module.ExternFn,
+        };
+
         pub const Decl = struct {
             base: Payload,
             data: *Module.Decl,