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 {...@@ -1529,6 +1529,7 @@ pub const Var = struct {
1529 is_extern: bool,1529 is_extern: bool,
1530 is_mutable: bool,1530 is_mutable: bool,
1531 is_threadlocal: bool,1531 is_threadlocal: bool,
1532 is_weak_linkage: bool,
15321533
1533 pub fn deinit(variable: *Var, gpa: Allocator) void {1534 pub fn deinit(variable: *Var, gpa: Allocator) void {
1534 if (variable.lib_name) |lib_name| {1535 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...@@ -13384,11 +13384,11 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
13384 const args = sema.resolveInst(extra.data.args);13384 const args = sema.resolveInst(extra.data.args);
1338513385
13386 const modifier: std.builtin.CallOptions.Modifier = modifier: {13386 const modifier: std.builtin.CallOptions.Modifier = modifier: {
13387 const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");13387 const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
13388 const coerced_options = try sema.coerce(block, export_options_ty, options, options_src);13388 const coerced_options = try sema.coerce(block, call_options_ty, options, options_src);
13389 const options_val = try sema.resolveConstValue(block, options_src, coerced_options);13389 const options_val = try sema.resolveConstValue(block, options_src, coerced_options);
13390 const fields = options_val.castTag(.@"struct").?.data;13390 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;
13392 const modifier_index = struct_obj.fields.getIndex("modifier").?;13392 const modifier_index = struct_obj.fields.getIndex("modifier").?;
13393 const stack_index = struct_obj.fields.getIndex("stack").?;13393 const stack_index = struct_obj.fields.getIndex("stack").?;
13394 if (!fields[stack_index].isNull()) {13394 if (!fields[stack_index].isNull()) {
...@@ -13743,6 +13743,7 @@ fn zirVarExtended(...@@ -13743,6 +13743,7 @@ fn zirVarExtended(
13743 .is_extern = small.is_extern,13743 .is_extern = small.is_extern,
13744 .is_mutable = true, // TODO get rid of this unused field13744 .is_mutable = true, // TODO get rid of this unused field
13745 .is_threadlocal = small.is_threadlocal,13745 .is_threadlocal = small.is_threadlocal,
13746 .is_weak_linkage = false,
13746 .lib_name = null,13747 .lib_name = null,
13747 };13748 };
1374813749
...@@ -13937,7 +13938,98 @@ fn zirBuiltinExtern(...@@ -13937,7 +13938,98 @@ fn zirBuiltinExtern(
13937) CompileError!Air.Inst.Ref {13938) CompileError!Air.Inst.Ref {
13938 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;13939 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
13939 const src: LazySrcLoc = .{ .node_offset = extra.node };13940 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);
13941}14033}
1394214034
13943fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {14035fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
src/codegen/llvm.zig+16-2
...@@ -548,6 +548,10 @@ pub const Object = struct {...@@ -548,6 +548,10 @@ pub const Object = struct {
548 llvm_global.setValueName(decl.name);548 llvm_global.setValueName(decl.name);
549 llvm_global.setUnnamedAddr(.False);549 llvm_global.setUnnamedAddr(.False);
550 llvm_global.setLinkage(.External);550 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 }
551 } else if (exports.len != 0) {555 } else if (exports.len != 0) {
552 const exp_name = exports[0].options.name;556 const exp_name = exports[0].options.name;
553 llvm_global.setValueName2(exp_name.ptr, exp_name.len);557 llvm_global.setValueName2(exp_name.ptr, exp_name.len);
...@@ -558,6 +562,9 @@ pub const Object = struct {...@@ -558,6 +562,9 @@ pub const Object = struct {
558 .Weak => llvm_global.setLinkage(.WeakODR),562 .Weak => llvm_global.setLinkage(.WeakODR),
559 .LinkOnce => llvm_global.setLinkage(.LinkOnceODR),563 .LinkOnce => llvm_global.setLinkage(.LinkOnceODR),
560 }564 }
565 if (decl.val.castTag(.variable)) |variable| {
566 if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
567 }
561 // If a Decl is exported more than one time (which is rare),568 // If a Decl is exported more than one time (which is rare),
562 // we add aliases for all but the first export.569 // we add aliases for all but the first export.
563 // TODO LLVM C API does not support deleting aliases. We need to570 // TODO LLVM C API does not support deleting aliases. We need to
...@@ -788,8 +795,15 @@ pub const DeclGen = struct {...@@ -788,8 +795,15 @@ pub const DeclGen = struct {
788 const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);795 const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);
789 gop.value_ptr.* = llvm_global;796 gop.value_ptr.* = llvm_global;
790797
791 const is_extern = decl.val.tag() == .unreachable_value;798 if (decl.isExtern()) {
792 if (!is_extern) {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 {
793 llvm_global.setLinkage(.Internal);807 llvm_global.setLinkage(.Internal);
794 llvm_global.setUnnamedAddr(.True);808 llvm_global.setUnnamedAddr(.True);
795 }809 }
src/codegen/llvm/bindings.zig+11
...@@ -120,6 +120,9 @@ pub const Value = opaque {...@@ -120,6 +120,9 @@ pub const Value = opaque {
120 pub const setUnnamedAddr = LLVMSetUnnamedAddr;120 pub const setUnnamedAddr = LLVMSetUnnamedAddr;
121 extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void;121 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
123 pub const deleteGlobal = LLVMDeleteGlobal;126 pub const deleteGlobal = LLVMDeleteGlobal;
124 extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void;127 extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void;
125128
...@@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) {...@@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) {
1230 LinkerPrivateWeak,1233 LinkerPrivateWeak,
1231};1234};
12321235
1236pub const ThreadLocalMode = enum(c_uint) {
1237 NotThreadLocal,
1238 GeneralDynamicTLSModel,
1239 LocalDynamicTLSModel,
1240 InitialExecTLSModel,
1241 LocalExecTLSModel,
1242};
1243
1233pub const AtomicOrdering = enum(c_uint) {1244pub const AtomicOrdering = enum(c_uint) {
1234 NotAtomic = 0,1245 NotAtomic = 0,
1235 Unordered = 1,1246 Unordered = 1,