authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 15:35:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-23 15:35:54-07:00
log78f643c46d36d296d17b332b577c966fd0dd21bb
treedaa72a84c587ed47377d6db27bf9725f95d94cb2
parent6bf52b0505ad7317b5f0d6fa77b7c41318b9c73b
parent7edd69d8aade6da2339cb0d9a027c52b3015bc31
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21758 from kcbanner/dll_storage_class

Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI

12 files changed, 99 insertions(+), 19 deletions(-)

lib/std/builtin.zig+1
......@@ -969,6 +969,7 @@ pub const ExternOptions = struct {
969969 library_name: ?[]const u8 = null,
970970 linkage: GlobalLinkage = .strong,
971971 is_thread_local: bool = false,
972 is_dll_import: bool = false,
972973};
973974
974975/// This data structure is used by the Zig language code generation and
src/InternPool.zig+9-2
......@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
20542054 is_const: bool,
20552055 is_threadlocal: bool,
20562056 is_weak_linkage: bool,
2057 is_dll_import: bool,
20572058 alignment: Alignment,
20582059 @"addrspace": std.builtin.AddressSpace,
20592060 /// The ZIR instruction which created this extern; used only for source locations.
......@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
26752676 asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
26762677 asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
26772678 asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
2678 asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
2679 asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
2680 asBytes(&e.zir_index)),
26792681 };
26802682 }
26812683
......@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
27712773 a_info.is_const == b_info.is_const and
27722774 a_info.is_threadlocal == b_info.is_threadlocal and
27732775 a_info.is_weak_linkage == b_info.is_weak_linkage and
2776 a_info.is_dll_import == b_info.is_dll_import and
27742777 a_info.alignment == b_info.alignment and
27752778 a_info.@"addrspace" == b_info.@"addrspace" and
27762779 a_info.zir_index == b_info.zir_index;
......@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
53705373 is_const: bool,
53715374 is_threadlocal: bool,
53725375 is_weak_linkage: bool,
5373 _: u29 = 0,
5376 is_dll_import: bool,
5377 _: u28 = 0,
53745378 };
53755379 };
53765380
......@@ -6714,6 +6718,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
67146718 .is_const = extra.flags.is_const,
67156719 .is_threadlocal = extra.flags.is_threadlocal,
67166720 .is_weak_linkage = extra.flags.is_weak_linkage,
6721 .is_dll_import = extra.flags.is_dll_import,
67176722 .alignment = nav.status.resolved.alignment,
67186723 .@"addrspace" = nav.status.resolved.@"addrspace",
67196724 .zir_index = extra.zir_index,
......@@ -7380,6 +7385,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
73807385 .is_const = false,
73817386 .is_threadlocal = variable.is_threadlocal,
73827387 .is_weak_linkage = variable.is_weak_linkage,
7388 .is_dll_import = false,
73837389 },
73847390 }),
73857391 });
......@@ -8643,6 +8649,7 @@ pub fn getExtern(
86438649 .is_const = key.is_const,
86448650 .is_threadlocal = key.is_threadlocal,
86458651 .is_weak_linkage = key.is_weak_linkage,
8652 .is_dll_import = key.is_dll_import,
86468653 },
86478654 .zir_index = key.zir_index,
86488655 .owner_nav = owner_nav,
src/Sema.zig+22-1
......@@ -876,6 +876,7 @@ const InferredAlloc = struct {
876876
877877const NeededComptimeReason = struct {
878878 needed_comptime_reason: []const u8,
879 value_comptime_reason: ?[]const u8 = null,
879880 block_comptime_reason: ?*const Block.ComptimeReason = null,
880881};
881882
......@@ -2246,7 +2247,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
22462247 }
22472248 };
22482249 const val = Value.fromInterned(ip_index);
2249 if (val.isPtrToThreadLocal(pt.zcu)) return null;
2250 if (val.isPtrRuntimeValue(pt.zcu)) return null;
22502251 return val;
22512252}
22522253
......@@ -2272,8 +2273,14 @@ pub fn resolveFinalDeclValue(
22722273 const zcu = sema.pt.zcu;
22732274
22742275 const val = try sema.resolveValueAllowVariables(air_ref) orelse {
2276 const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
2277 "thread local and dll imported variables have runtime-known addresses"
2278 else
2279 null;
2280
22752281 return sema.failWithNeededComptime(block, src, .{
22762282 .needed_comptime_reason = "global variable initializer must be comptime-known",
2283 .value_comptime_reason = value_comptime_reason,
22772284 });
22782285 };
22792286 if (val.isGenericPoison()) return error.GenericPoison;
......@@ -2291,6 +2298,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
22912298 const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
22922299 errdefer msg.destroy(sema.gpa);
22932300 try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
2301 if (reason.value_comptime_reason) |value_comptime_reason| {
2302 try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
2303 }
22942304
22952305 if (reason.block_comptime_reason) |block_comptime_reason| {
22962306 try block_comptime_reason.explain(sema, msg);
......@@ -10023,6 +10033,7 @@ fn funcCommon(
1002310033 .is_const = true,
1002410034 .is_threadlocal = false,
1002510035 .is_weak_linkage = false,
10036 .is_dll_import = false,
1002610037 .alignment = alignment orelse .none,
1002710038 .@"addrspace" = address_space orelse .generic,
1002810039 .zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
......@@ -26577,6 +26588,7 @@ fn zirVarExtended(
2657726588 .is_const = small.is_const,
2657826589 .is_threadlocal = small.is_threadlocal,
2657926590 .is_weak_linkage = false,
26591 .is_dll_import = false,
2658026592 .alignment = alignment,
2658126593 .@"addrspace" = @"addrspace",
2658226594 .zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
......@@ -27030,6 +27042,7 @@ fn resolveExternOptions(
2703027042 library_name: InternPool.OptionalNullTerminatedString = .none,
2703127043 linkage: std.builtin.GlobalLinkage = .strong,
2703227044 is_thread_local: bool = false,
27045 is_dll_import: bool = false,
2703327046} {
2703427047 const pt = sema.pt;
2703527048 const zcu = pt.zcu;
......@@ -27043,6 +27056,7 @@ fn resolveExternOptions(
2704327056 const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2704427057 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2704527058 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
27059 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
2704627060
2704727061 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
2704827062 const name = try sema.toConstString(block, name_src, name_ref, .{
......@@ -27076,6 +27090,11 @@ fn resolveExternOptions(
2707627090 break :library_name library_name;
2707727091 } else null;
2707827092
27093 const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
27094 const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
27095 .needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
27096 });
27097
2707927098 if (name.len == 0) {
2708027099 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
2708127100 }
......@@ -27089,6 +27108,7 @@ fn resolveExternOptions(
2708927108 .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
2709027109 .linkage = linkage,
2709127110 .is_thread_local = is_thread_local_val.toBool(),
27111 .is_dll_import = is_dll_import_val.toBool(),
2709227112 };
2709327113}
2709427114
......@@ -27134,6 +27154,7 @@ fn zirBuiltinExtern(
2713427154 .is_const = ptr_info.flags.is_const,
2713527155 .is_threadlocal = options.is_thread_local,
2713627156 .is_weak_linkage = options.linkage == .weak,
27157 .is_dll_import = options.is_dll_import,
2713727158 .alignment = ptr_info.flags.alignment,
2713827159 .@"addrspace" = ptr_info.flags.address_space,
2713927160 // This instruction is just for source locations.
src/Value.zig+2-2
......@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
13401340 };
13411341}
13421342
1343pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
1343pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
13441344 const ip = &zcu.intern_pool;
13451345 const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
13461346 return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
1347 .@"extern" => |e| e.is_threadlocal,
1347 .@"extern" => |e| e.is_threadlocal or e.is_dll_import,
13481348 .variable => |v| v.is_threadlocal,
13491349 else => false,
13501350 };
src/Zcu.zig+3
......@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
15221522 .init_field_cache,
15231523 .init_field_library,
15241524 .init_field_thread_local,
1525 .init_field_dll_import,
15251526 => |builtin_call_node| {
15261527 const wanted = switch (src_loc.lazy) {
15271528 .init_field_name => "name",
......@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
15331534 .init_field_cache => "cache",
15341535 .init_field_library => "library",
15351536 .init_field_thread_local => "thread_local",
1537 .init_field_dll_import => "dll_import",
15361538 else => unreachable,
15371539 };
15381540 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
19591961 init_field_cache: i32,
19601962 init_field_library: i32,
19611963 init_field_thread_local: i32,
1964 init_field_dll_import: i32,
19621965 /// The source location points to the value of an item in a specific
19631966 /// case of a `switch`.
19641967 switch_case_item: SwitchItem,
src/Zcu/PerThread.zig+1
......@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
27632763 .is_const = e.is_const,
27642764 .is_threadlocal = e.is_threadlocal,
27652765 .is_weak_linkage = e.is_weak_linkage,
2766 .is_dll_import = e.is_dll_import,
27662767 .alignment = e.alignment,
27672768 .@"addrspace" = e.@"addrspace",
27682769 .zir_index = e.zir_index,
src/codegen/llvm.zig+13-9
......@@ -3260,10 +3260,10 @@ pub const Object = struct {
32603260 const ip = &zcu.intern_pool;
32613261 const nav = ip.getNav(nav_index);
32623262 const resolved = nav.status.resolved;
3263 const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
3264 .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
3265 .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
3266 else => .{ false, false, false },
3263 const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
3264 .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
3265 .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
3266 else => .{ false, false, false, false },
32673267 };
32683268
32693269 const variable_index = try o.builder.addVariable(
......@@ -3280,6 +3280,7 @@ pub const Object = struct {
32803280 if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
32813281 variable_index.setThreadLocal(.generaldynamic, &o.builder);
32823282 if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
3283 if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
32833284 } else {
32843285 variable_index.setLinkage(.internal, &o.builder);
32853286 variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
......@@ -4810,10 +4811,10 @@ pub const NavGen = struct {
48104811 const nav = ip.getNav(nav_index);
48114812 const resolved = nav.status.resolved;
48124813
4813 const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
4814 .variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
4815 .@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
4816 else => .{ false, .none, false, false, true, resolved.val, nav_index },
4814 const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
4815 .variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
4816 .@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
4817 else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
48174818 };
48184819 const ty = Type.fromInterned(nav.typeOf(ip));
48194820
......@@ -4888,8 +4889,11 @@ pub const NavGen = struct {
48884889 try global_index.rename(decl_name, &o.builder);
48894890 global_index.setLinkage(.external, &o.builder);
48904891 global_index.setUnnamedAddr(.default, &o.builder);
4891 if (zcu.comp.config.dll_export_fns)
4892 if (is_dll_import) {
4893 global_index.setDllStorageClass(.dllimport, &o.builder);
4894 } else if (zcu.comp.config.dll_export_fns) {
48924895 global_index.setDllStorageClass(.default, &o.builder);
4896 }
48934897
48944898 if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
48954899 }
src/codegen/llvm/Builder.zig+4
......@@ -2541,6 +2541,10 @@ pub const Variable = struct {
25412541 return self.ptrConst(builder).global.setLinkage(linkage, builder);
25422542 }
25432543
2544 pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
2545 return self.ptrConst(builder).global.setDllStorageClass(class, builder);
2546 }
2547
25442548 pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
25452549 return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
25462550 }
test/cases/compile_errors/builtin_extern_in_comptime_scope.zig created+18
......@@ -0,0 +1,18 @@
1const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
2const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
3pub export fn entry() void {
4 _ = foo_tl;
5}
6pub export fn entry2() void {
7 _ = foo_dll;
8}
9// error
10// backend=stage2
11// target=native
12//
13// :1:16: error: unable to resolve comptime value
14// :1:16: note: global variable initializer must be comptime-known
15// :1:16: note: thread local and dll imported variables have runtime-known addresses
16// :2:17: error: unable to resolve comptime value
17// :2:17: note: global variable initializer must be comptime-known
18// :2:17: note: thread local and dll imported variables have runtime-known addresses
test/standalone/extern/build.zig+15-4
......@@ -1,6 +1,9 @@
11const std = @import("std");
22
33pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
47 const optimize: std.builtin.OptimizeMode = .Debug;
58
69 const obj = b.addObject(.{
......@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
912 .target = b.graph.host,
1013 .optimize = optimize,
1114 });
12 const main = b.addTest(.{
15 const shared = b.addSharedLibrary(.{
16 .name = "shared",
17 .target = b.graph.host,
18 .optimize = optimize,
19 .link_libc = true,
20 });
21 if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
22 shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
23 const test_exe = b.addTest(.{
1324 .root_source_file = b.path("main.zig"),
1425 .optimize = optimize,
1526 });
16 main.addObject(obj);
27 test_exe.addObject(obj);
28 test_exe.linkLibrary(shared);
1729
18 const test_step = b.step("test", "Test it");
19 test_step.dependOn(&main.step);
30 test_step.dependOn(&b.addRunArtifact(test_exe).step);
2031}
test/standalone/extern/main.zig+6-1
......@@ -1,4 +1,5 @@
11const assert = @import("std").debug.assert;
2const testing = @import("std").testing;
23
34const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
45const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
......@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
89test {
910 const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
1011 const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
12 const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });
1113
1214 assert(getHidden() == 0);
1315 updateHidden(123);
1416 assert(getHidden() == 123);
15
1617 assert(mut_val_ptr.* == 1.23);
1718 mut_val_ptr.* = 10.0;
1819 assert(mut_val_ptr.* == 10.0);
1920
2021 assert(const_val_ptr.x == 42);
22
23 assert(shared_val_ptr.* == 1234);
24 shared_val_ptr.* = 1235;
25 assert(shared_val_ptr.* == 1235);
2126}
test/standalone/extern/shared.c created+5
......@@ -0,0 +1,5 @@
1#ifndef API
2#define API
3#endif
4
5API int shared_val = 1234;