diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 52 | ||||
| -rw-r--r-- | src/Sema.zig | 144 | ||||
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/codegen/c.zig | 8 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 4 | ||||
| -rw-r--r-- | src/value.zig | 19 |
8 files changed, 177 insertions, 78 deletions
diff --git a/src/Module.zig b/src/Module.zig index eeed6b2dc9..bc806cfb9c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -501,11 +501,16 @@ 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); } if (decl.getVariable()) |variable| { + variable.deinit(gpa); gpa.destroy(variable); } if (decl.value_arena) |arena_state| { @@ -690,6 +695,17 @@ pub const Decl = struct { return func; } + /// If the Decl has a value and it is an extern function, returns it, + /// otherwise null. + 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; + } + + /// If the Decl has a value and it is a variable, returns it, + /// otherwise null. pub fn getVariable(decl: *Decl) ?*Var { if (!decl.owns_tv) return null; const variable = (decl.val.castTag(.variable) orelse return null).data; @@ -1320,9 +1336,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, @@ -1441,9 +1474,20 @@ pub const Var = struct { init: Value, owner_decl: *Decl, + /// Library name if specified. + /// For example `extern "c" var stderrp = ...` would have 'c' as library name. + /// Allocated with Module's allocator; outlives the ZIR code. + lib_name: ?[*:0]const u8, + is_extern: bool, is_mutable: bool, is_threadlocal: bool, + + pub fn deinit(variable: *Var, gpa: Allocator) void { + if (variable.lib_name) |lib_name| { + gpa.free(mem.sliceTo(lib_name, 0)); + } + } }; /// The container that structs, enums, unions, and opaques have. @@ -3768,8 +3812,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; diff --git a/src/Sema.zig b/src/Sema.zig index 4e8853d7ce..1dba136a48 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5431,6 +5431,69 @@ fn zirFunc( ); } +/// Given a library name, examines if the library name should end up in +/// `link.File.Options.system_libs` table (for example, libc is always +/// specified via dedicated flag `link.File.Options.link_libc` instead), +/// and puts it there if it doesn't exist. +/// It also dupes the library name which can then be saved as part of the +/// respective `Decl` (either `ExternFn` or `Var`). +/// The liveness of the duped library name is tied to liveness of `Module`. +/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). +fn handleExternLibName( + sema: *Sema, + block: *Block, + src_loc: LazySrcLoc, + lib_name: []const u8, +) CompileError![:0]u8 { + blk: { + const mod = sema.mod; + const target = mod.getTarget(); + log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); + if (target_util.is_libc_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libc) { + return sema.fail( + block, + src_loc, + "dependency on libc must be explicitly specified in the build command", + .{}, + ); + } + mod.comp.bin_file.options.link_libc = true; + break :blk; + } + if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libcpp) { + return sema.fail( + block, + src_loc, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); + } + mod.comp.bin_file.options.link_libcpp = true; + break :blk; + } + if (mem.eql(u8, lib_name, "unwind")) { + mod.comp.bin_file.options.link_libunwind = true; + break :blk; + } + if (!target.isWasm() and !mod.comp.bin_file.options.pic) { + return sema.fail( + block, + src_loc, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ); + } + mod.comp.stage1AddLinkLib(lib_name) catch |err| { + return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ + lib_name, @errorName(err), + }); + }; + } + return sema.gpa.dupeZ(u8, lib_name); +} + fn funcCommon( sema: *Sema, block: *Block, @@ -5568,57 +5631,27 @@ fn funcCommon( }); }; - if (opt_lib_name) |lib_name| blk: { - const lib_name_src: LazySrcLoc = .{ .node_offset_lib_name = src_node_offset }; - log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); - if (target_util.is_libc_lib_name(target, lib_name)) { - if (!mod.comp.bin_file.options.link_libc) { - return sema.fail( - block, - lib_name_src, - "dependency on libc must be explicitly specified in the build command", - .{}, - ); - } - mod.comp.bin_file.options.link_libc = true; - break :blk; - } - if (target_util.is_libcpp_lib_name(target, lib_name)) { - if (!mod.comp.bin_file.options.link_libcpp) { - return sema.fail( - block, - lib_name_src, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } - mod.comp.bin_file.options.link_libcpp = true; - break :blk; - } - if (mem.eql(u8, lib_name, "unwind")) { - mod.comp.bin_file.options.link_libunwind = true; - break :blk; - } - if (!target.isWasm() and !mod.comp.bin_file.options.pic) { - return sema.fail( - block, - lib_name_src, - "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", - .{ lib_name, lib_name }, - ); - } - mod.comp.stage1AddLinkLib(lib_name) catch |err| { - return sema.fail(block, lib_name_src, "unable to add link lib '{s}': {s}", .{ - lib_name, @errorName(err), - }); + if (is_extern) { + const new_extern_fn = try sema.gpa.create(Module.ExternFn); + errdefer sema.gpa.destroy(new_extern_fn); + + new_extern_fn.* = Module.ExternFn{ + .owner_decl = sema.owner_decl, + .lib_name = null, }; - } - if (is_extern) { - return sema.addConstant( - fn_ty, - try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl), - ); + if (opt_lib_name) |lib_name| { + new_extern_fn.lib_name = try sema.handleExternLibName(block, .{ + .node_offset_lib_name = src_node_offset, + }, 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) { @@ -12444,13 +12477,8 @@ fn zirVarExtended( try sema.validateVarType(block, mut_src, var_ty, small.is_extern); - if (lib_name != null) { - // Look at the sema code for functions which has this logic, it just needs to - // be extracted and shared by both var and func - return sema.fail(block, src, "TODO: handle var with lib_name in Sema", .{}); - } - const new_var = try sema.gpa.create(Module.Var); + errdefer sema.gpa.destroy(new_var); log.debug("created variable {*} owner_decl: {*} ({s})", .{ new_var, sema.owner_decl, sema.owner_decl.name, @@ -12462,7 +12490,13 @@ fn zirVarExtended( .is_extern = small.is_extern, .is_mutable = true, // TODO get rid of this unused field .is_threadlocal = small.is_threadlocal, + .lib_name = null, }; + + if (lib_name) |lname| { + new_var.lib_name = try sema.handleExternLibName(block, ty_src, lname); + } + const result = try sema.addConstant( var_ty, try Value.Tag.variable.create(sema.arena, new_var), diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 2b8c5e62d4..6e8f88a2a7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1581,8 +1581,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, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 67aa9a6c88..8e0ffac76b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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; } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 535e65000a..a60b8c78f0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2566,8 +2566,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, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 299581cd48..44b904f031 100644 --- a/src/codegen/c.zig +++ b/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, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 08fc3879a9..abf371c85b 100644 --- a/src/codegen/llvm.zig +++ b/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, }; diff --git a/src/value.zig b/src/value.zig index 1a7f51ecd5..cc6827b0cc 100644 --- a/src/value.zig +++ b/src/value.zig @@ -263,9 +263,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, @@ -477,7 +477,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), @@ -1842,9 +1842,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, }; } @@ -1911,9 +1912,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, @@ -3301,6 +3303,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, |
