authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-28 16:12:01+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-02 12:15:50+02:00
logef4aca2dc4224d0de1ab15112097fd48930476db
treedabd0a2f1f63db29e97a052b514cc992bbfb01cb
parent7cfc3f0cfa626abe25c8318a7852977cbc1c723b

stage2: implement `@extern`


4 files changed, 124 insertions(+), 6 deletions(-)

src/Module.zig+1
......@@ -1529,6 +1529,7 @@ pub const Var = struct {
15291529 is_extern: bool,
15301530 is_mutable: bool,
15311531 is_threadlocal: bool,
1532 is_weak_linkage: bool,
15321533
15331534 pub fn deinit(variable: *Var, gpa: Allocator) void {
15341535 if (variable.lib_name) |lib_name| {
src/Sema.zig+96-4
......@@ -13384,11 +13384,11 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1338413384 const args = sema.resolveInst(extra.data.args);
1338513385
1338613386 const modifier: std.builtin.CallOptions.Modifier = modifier: {
13387 const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
13388 const coerced_options = try sema.coerce(block, export_options_ty, options, options_src);
13387 const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
13388 const coerced_options = try sema.coerce(block, call_options_ty, options, options_src);
1338913389 const options_val = try sema.resolveConstValue(block, options_src, coerced_options);
1339013390 const fields = options_val.castTag(.@"struct").?.data;
13391 const struct_obj = export_options_ty.castTag(.@"struct").?.data;
13391 const struct_obj = call_options_ty.castTag(.@"struct").?.data;
1339213392 const modifier_index = struct_obj.fields.getIndex("modifier").?;
1339313393 const stack_index = struct_obj.fields.getIndex("stack").?;
1339413394 if (!fields[stack_index].isNull()) {
......@@ -13743,6 +13743,7 @@ fn zirVarExtended(
1374313743 .is_extern = small.is_extern,
1374413744 .is_mutable = true, // TODO get rid of this unused field
1374513745 .is_threadlocal = small.is_threadlocal,
13746 .is_weak_linkage = false,
1374613747 .lib_name = null,
1374713748 };
1374813749
......@@ -13937,7 +13938,98 @@ fn zirBuiltinExtern(
1393713938) CompileError!Air.Inst.Ref {
1393813939 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
1393913940 const src: LazySrcLoc = .{ .node_offset = extra.node };
13940 return sema.fail(block, src, "TODO: implement Sema.zirBuiltinExtern", .{});
13941 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
13942 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
13943
13944 var ty = try sema.resolveType(block, ty_src, extra.lhs);
13945 const options_inst = sema.resolveInst(extra.rhs);
13946
13947 const options = options: {
13948 const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions");
13949 const coerced_options = try sema.coerce(block, extern_options_ty, options_inst, options_src);
13950 const options_val = try sema.resolveConstValue(block, options_src, coerced_options);
13951 const fields = options_val.castTag(.@"struct").?.data;
13952 const struct_obj = extern_options_ty.castTag(.@"struct").?.data;
13953 const name_index = struct_obj.fields.getIndex("name").?;
13954 const library_name_index = struct_obj.fields.getIndex("library_name").?;
13955 const linkage_index = struct_obj.fields.getIndex("linkage").?;
13956 const is_thread_local_index = struct_obj.fields.getIndex("is_thread_local").?;
13957
13958 var library_name: ?[]const u8 = null;
13959 if (!fields[library_name_index].isNull()) {
13960 const payload = fields[library_name_index].castTag(.opt_payload).?.data;
13961 library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena);
13962 }
13963
13964 break :options std.builtin.ExternOptions{
13965 .name = try fields[name_index].toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena),
13966 .library_name = library_name,
13967 .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage),
13968 .is_thread_local = fields[is_thread_local_index].toBool(),
13969 };
13970 };
13971
13972 if (!ty.isPtrAtRuntime()) {
13973 return sema.fail(block, options_src, "expected (optional) pointer", .{});
13974 }
13975
13976 if (options.name.len == 0) {
13977 return sema.fail(block, options_src, "extern symbol name cannot be empty", .{});
13978 }
13979
13980 if (options.linkage != .Weak and options.linkage != .Strong) {
13981 return sema.fail(block, options_src, "extern symbol must use strong or weak linkage", .{});
13982 }
13983
13984 if (options.linkage == .Weak and !ty.ptrAllowsZero()) {
13985 ty = try Type.optional(sema.arena, ty);
13986 }
13987
13988 // TODO check duplicate extern
13989
13990 const new_decl = try sema.mod.allocateNewDecl(try sema.gpa.dupeZ(u8, options.name), sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
13991 errdefer new_decl.destroy(sema.mod);
13992
13993 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
13994 errdefer new_decl_arena.deinit();
13995 const new_decl_arena_allocator = new_decl_arena.allocator();
13996
13997 const new_var = try new_decl_arena_allocator.create(Module.Var);
13998 errdefer new_decl_arena_allocator.destroy(new_var);
13999
14000 new_var.* = .{
14001 .owner_decl = sema.owner_decl,
14002 .init = Value.initTag(.unreachable_value),
14003 .is_extern = true,
14004 .is_mutable = false, // TODO get rid of this unused field
14005 .is_threadlocal = options.is_thread_local,
14006 .is_weak_linkage = options.linkage == .Weak,
14007 .lib_name = null,
14008 };
14009
14010 if (options.library_name) |library_name| {
14011 if (library_name.len == 0) {
14012 return sema.fail(block, options_src, "library name name cannot be empty", .{});
14013 }
14014 new_var.lib_name = try sema.handleExternLibName(block, options_src, library_name);
14015 }
14016
14017 new_decl.src_line = sema.owner_decl.src_line;
14018 new_decl.ty = try ty.copy(new_decl_arena_allocator);
14019 new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
14020 new_decl.align_val = Value.@"null";
14021 new_decl.linksection_val = Value.@"null";
14022 new_decl.has_tv = true;
14023 new_decl.analysis = .complete;
14024 new_decl.generation = sema.mod.generation;
14025
14026 const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
14027 arena_state.* = new_decl_arena.state;
14028 new_decl.value_arena = arena_state;
14029
14030 const ref = try sema.analyzeDeclRef(new_decl);
14031 try sema.requireRuntimeBlock(block, src);
14032 return block.addBitCast(ty, ref);
1394114033}
1394214034
1394314035fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
src/codegen/llvm.zig+16-2
......@@ -548,6 +548,10 @@ pub const Object = struct {
548548 llvm_global.setValueName(decl.name);
549549 llvm_global.setUnnamedAddr(.False);
550550 llvm_global.setLinkage(.External);
551 if (decl.val.castTag(.variable)) |variable| {
552 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
553 if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
554 }
551555 } else if (exports.len != 0) {
552556 const exp_name = exports[0].options.name;
553557 llvm_global.setValueName2(exp_name.ptr, exp_name.len);
......@@ -558,6 +562,9 @@ pub const Object = struct {
558562 .Weak => llvm_global.setLinkage(.WeakODR),
559563 .LinkOnce => llvm_global.setLinkage(.LinkOnceODR),
560564 }
565 if (decl.val.castTag(.variable)) |variable| {
566 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
567 }
561568 // If a Decl is exported more than one time (which is rare),
562569 // we add aliases for all but the first export.
563570 // TODO LLVM C API does not support deleting aliases. We need to
......@@ -788,8 +795,15 @@ pub const DeclGen = struct {
788795 const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);
789796 gop.value_ptr.* = llvm_global;
790797
791 const is_extern = decl.val.tag() == .unreachable_value;
792 if (!is_extern) {
798 if (decl.isExtern()) {
799 llvm_global.setValueName(decl.name);
800 llvm_global.setUnnamedAddr(.False);
801 llvm_global.setLinkage(.External);
802 if (decl.val.castTag(.variable)) |variable| {
803 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
804 if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak);
805 }
806 } else {
793807 llvm_global.setLinkage(.Internal);
794808 llvm_global.setUnnamedAddr(.True);
795809 }
src/codegen/llvm/bindings.zig+11
......@@ -120,6 +120,9 @@ pub const Value = opaque {
120120 pub const setUnnamedAddr = LLVMSetUnnamedAddr;
121121 extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void;
122122
123 pub const setThreadLocalMode = LLVMSetThreadLocalMode;
124 extern fn LLVMSetThreadLocalMode(Global: *const Value, Mode: ThreadLocalMode) void;
125
123126 pub const deleteGlobal = LLVMDeleteGlobal;
124127 extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void;
125128
......@@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) {
12301233 LinkerPrivateWeak,
12311234};
12321235
1236pub const ThreadLocalMode = enum(c_uint) {
1237 NotThreadLocal,
1238 GeneralDynamicTLSModel,
1239 LocalDynamicTLSModel,
1240 InitialExecTLSModel,
1241 LocalExecTLSModel,
1242};
1243
12331244pub const AtomicOrdering = enum(c_uint) {
12341245 NotAtomic = 0,
12351246 Unordered = 1,