aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig52
-rw-r--r--src/Sema.zig144
-rw-r--r--src/arch/aarch64/CodeGen.zig11
-rw-r--r--src/arch/wasm/CodeGen.zig6
-rw-r--r--src/arch/x86_64/CodeGen.zig11
-rw-r--r--src/codegen/c.zig8
-rw-r--r--src/codegen/llvm.zig4
-rw-r--r--src/value.zig19
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,