From ef4aca2dc4224d0de1ab15112097fd48930476db Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 28 Feb 2022 16:12:01 +0200 Subject: [PATCH 1/3] stage2: implement `@extern` --- src/Module.zig | 1 + src/Sema.zig | 100 ++++++++++++++++++++++++++++++++-- src/codegen/llvm.zig | 18 +++++- src/codegen/llvm/bindings.zig | 11 ++++ 4 files changed, 124 insertions(+), 6 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 847e78f1f2..c122a2e3cd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1529,6 +1529,7 @@ pub const Var = struct { is_extern: bool, is_mutable: bool, is_threadlocal: bool, + is_weak_linkage: bool, pub fn deinit(variable: *Var, gpa: Allocator) void { if (variable.lib_name) |lib_name| { diff --git a/src/Sema.zig b/src/Sema.zig index 93dd3c4557..9805899335 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13384,11 +13384,11 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const args = sema.resolveInst(extra.data.args); const modifier: std.builtin.CallOptions.Modifier = modifier: { - const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions"); - const coerced_options = try sema.coerce(block, export_options_ty, options, options_src); + const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions"); + const coerced_options = try sema.coerce(block, call_options_ty, options, options_src); const options_val = try sema.resolveConstValue(block, options_src, coerced_options); const fields = options_val.castTag(.@"struct").?.data; - const struct_obj = export_options_ty.castTag(.@"struct").?.data; + const struct_obj = call_options_ty.castTag(.@"struct").?.data; const modifier_index = struct_obj.fields.getIndex("modifier").?; const stack_index = struct_obj.fields.getIndex("stack").?; if (!fields[stack_index].isNull()) { @@ -13743,6 +13743,7 @@ fn zirVarExtended( .is_extern = small.is_extern, .is_mutable = true, // TODO get rid of this unused field .is_threadlocal = small.is_threadlocal, + .is_weak_linkage = false, .lib_name = null, }; @@ -13937,7 +13938,98 @@ fn zirBuiltinExtern( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; - return sema.fail(block, src, "TODO: implement Sema.zirBuiltinExtern", .{}); + const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; + + var ty = try sema.resolveType(block, ty_src, extra.lhs); + const options_inst = sema.resolveInst(extra.rhs); + + const options = options: { + const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions"); + const coerced_options = try sema.coerce(block, extern_options_ty, options_inst, options_src); + const options_val = try sema.resolveConstValue(block, options_src, coerced_options); + const fields = options_val.castTag(.@"struct").?.data; + const struct_obj = extern_options_ty.castTag(.@"struct").?.data; + const name_index = struct_obj.fields.getIndex("name").?; + const library_name_index = struct_obj.fields.getIndex("library_name").?; + const linkage_index = struct_obj.fields.getIndex("linkage").?; + const is_thread_local_index = struct_obj.fields.getIndex("is_thread_local").?; + + var library_name: ?[]const u8 = null; + if (!fields[library_name_index].isNull()) { + const payload = fields[library_name_index].castTag(.opt_payload).?.data; + library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena); + } + + break :options std.builtin.ExternOptions{ + .name = try fields[name_index].toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena), + .library_name = library_name, + .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), + .is_thread_local = fields[is_thread_local_index].toBool(), + }; + }; + + if (!ty.isPtrAtRuntime()) { + return sema.fail(block, options_src, "expected (optional) pointer", .{}); + } + + if (options.name.len == 0) { + return sema.fail(block, options_src, "extern symbol name cannot be empty", .{}); + } + + if (options.linkage != .Weak and options.linkage != .Strong) { + return sema.fail(block, options_src, "extern symbol must use strong or weak linkage", .{}); + } + + if (options.linkage == .Weak and !ty.ptrAllowsZero()) { + ty = try Type.optional(sema.arena, ty); + } + + // TODO check duplicate extern + + 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); + errdefer new_decl.destroy(sema.mod); + + var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); + errdefer new_decl_arena.deinit(); + const new_decl_arena_allocator = new_decl_arena.allocator(); + + const new_var = try new_decl_arena_allocator.create(Module.Var); + errdefer new_decl_arena_allocator.destroy(new_var); + + new_var.* = .{ + .owner_decl = sema.owner_decl, + .init = Value.initTag(.unreachable_value), + .is_extern = true, + .is_mutable = false, // TODO get rid of this unused field + .is_threadlocal = options.is_thread_local, + .is_weak_linkage = options.linkage == .Weak, + .lib_name = null, + }; + + if (options.library_name) |library_name| { + if (library_name.len == 0) { + return sema.fail(block, options_src, "library name name cannot be empty", .{}); + } + new_var.lib_name = try sema.handleExternLibName(block, options_src, library_name); + } + + new_decl.src_line = sema.owner_decl.src_line; + new_decl.ty = try ty.copy(new_decl_arena_allocator); + new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); + new_decl.align_val = Value.@"null"; + new_decl.linksection_val = Value.@"null"; + new_decl.has_tv = true; + new_decl.analysis = .complete; + new_decl.generation = sema.mod.generation; + + const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State); + arena_state.* = new_decl_arena.state; + new_decl.value_arena = arena_state; + + const ref = try sema.analyzeDeclRef(new_decl); + try sema.requireRuntimeBlock(block, src); + return block.addBitCast(ty, ref); } fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fa29a1aae3..5ef62aadcb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -548,6 +548,10 @@ pub const Object = struct { llvm_global.setValueName(decl.name); llvm_global.setUnnamedAddr(.False); llvm_global.setLinkage(.External); + if (decl.val.castTag(.variable)) |variable| { + if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); + if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak); + } } else if (exports.len != 0) { const exp_name = exports[0].options.name; llvm_global.setValueName2(exp_name.ptr, exp_name.len); @@ -558,6 +562,9 @@ pub const Object = struct { .Weak => llvm_global.setLinkage(.WeakODR), .LinkOnce => llvm_global.setLinkage(.LinkOnceODR), } + if (decl.val.castTag(.variable)) |variable| { + if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); + } // If a Decl is exported more than one time (which is rare), // we add aliases for all but the first export. // TODO LLVM C API does not support deleting aliases. We need to @@ -788,8 +795,15 @@ pub const DeclGen = struct { const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace); gop.value_ptr.* = llvm_global; - const is_extern = decl.val.tag() == .unreachable_value; - if (!is_extern) { + if (decl.isExtern()) { + llvm_global.setValueName(decl.name); + llvm_global.setUnnamedAddr(.False); + llvm_global.setLinkage(.External); + if (decl.val.castTag(.variable)) |variable| { + if (variable.data.is_threadlocal) llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); + if (variable.data.is_weak_linkage) llvm_global.setLinkage(.ExternalWeak); + } + } else { llvm_global.setLinkage(.Internal); llvm_global.setUnnamedAddr(.True); } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index c505a7d045..a3f7ff9561 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -120,6 +120,9 @@ pub const Value = opaque { pub const setUnnamedAddr = LLVMSetUnnamedAddr; extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void; + pub const setThreadLocalMode = LLVMSetThreadLocalMode; + extern fn LLVMSetThreadLocalMode(Global: *const Value, Mode: ThreadLocalMode) void; + pub const deleteGlobal = LLVMDeleteGlobal; extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void; @@ -1230,6 +1233,14 @@ pub const Linkage = enum(c_uint) { LinkerPrivateWeak, }; +pub const ThreadLocalMode = enum(c_uint) { + NotThreadLocal, + GeneralDynamicTLSModel, + LocalDynamicTLSModel, + InitialExecTLSModel, + LocalExecTLSModel, +}; + pub const AtomicOrdering = enum(c_uint) { NotAtomic = 0, Unordered = 1, From 58530c17364d2525655c90e97fd2cdd2937e5c19 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 2 Mar 2022 12:25:44 +0200 Subject: [PATCH 2/3] stage2: resolve options values --- src/Sema.zig | 71 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9805899335..6496adf96d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13019,20 +13019,24 @@ fn resolveExportOptions( ) CompileError!std.builtin.ExportOptions { const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions"); const air_ref = sema.resolveInst(zir_ref); - const coerced = try sema.coerce(block, export_options_ty, air_ref, src); - const val = try sema.resolveConstValue(block, src, coerced); - const fields = val.castTag(.@"struct").?.data; - const struct_obj = export_options_ty.castTag(.@"struct").?.data; - const name_index = struct_obj.fields.getIndex("name").?; - const linkage_index = struct_obj.fields.getIndex("linkage").?; - const section_index = struct_obj.fields.getIndex("section").?; - if (!fields[section_index].isNull()) { + const options = try sema.coerce(block, export_options_ty, air_ref, src); + + const name = try sema.fieldVal(block, src, options, "name", src); + const name_val = try sema.resolveConstValue(block, src, name); + + const linkage = try sema.fieldVal(block, src, options, "linkage", src); + const linkage_val = try sema.resolveConstValue(block, src, linkage); + + const section = try sema.fieldVal(block, src, options, "section", src); + const section_val = try sema.resolveConstValue(block, src, section); + + if (!section_val.isNull()) { return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); } const name_ty = Type.initTag(.const_slice_u8); return std.builtin.ExportOptions{ - .name = try fields[name_index].toAllocatedBytes(name_ty, sema.arena), - .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), + .name = try name_val.toAllocatedBytes(name_ty, sema.arena), + .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .section = null, // TODO }; } @@ -13386,15 +13390,17 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const modifier: std.builtin.CallOptions.Modifier = modifier: { const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions"); const coerced_options = try sema.coerce(block, call_options_ty, options, options_src); - const options_val = try sema.resolveConstValue(block, options_src, coerced_options); - const fields = options_val.castTag(.@"struct").?.data; - const struct_obj = call_options_ty.castTag(.@"struct").?.data; - const modifier_index = struct_obj.fields.getIndex("modifier").?; - const stack_index = struct_obj.fields.getIndex("stack").?; - if (!fields[stack_index].isNull()) { + + const modifier = try sema.fieldVal(block, options_src, coerced_options, "modifier", options_src); + const modifier_val = try sema.resolveConstValue(block, options_src, modifier); + + const stack = try sema.fieldVal(block, options_src, coerced_options, "stack", options_src); + const stack_val = try sema.resolveConstValue(block, options_src, stack); + + if (!stack_val.isNull()) { return sema.fail(block, options_src, "TODO: implement @call with stack", .{}); } - break :modifier fields[modifier_index].toEnum(std.builtin.CallOptions.Modifier); + break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier); }; const args_ty = sema.typeOf(args); @@ -13946,26 +13952,31 @@ fn zirBuiltinExtern( const options = options: { const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions"); - const coerced_options = try sema.coerce(block, extern_options_ty, options_inst, options_src); - const options_val = try sema.resolveConstValue(block, options_src, coerced_options); - const fields = options_val.castTag(.@"struct").?.data; - const struct_obj = extern_options_ty.castTag(.@"struct").?.data; - const name_index = struct_obj.fields.getIndex("name").?; - const library_name_index = struct_obj.fields.getIndex("library_name").?; - const linkage_index = struct_obj.fields.getIndex("linkage").?; - const is_thread_local_index = struct_obj.fields.getIndex("is_thread_local").?; + const options = try sema.coerce(block, extern_options_ty, options_inst, options_src); + + const name = try sema.fieldVal(block, options_src, options, "name", options_src); + const name_val = try sema.resolveConstValue(block, options_src, name); + + const library_name_inst = try sema.fieldVal(block, options_src, options, "library_name", options_src); + const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst); + + const linkage = try sema.fieldVal(block, options_src, options, "linkage", options_src); + const linkage_val = try sema.resolveConstValue(block, options_src, linkage); + + const is_thread_local = try sema.fieldVal(block, options_src, options, "is_thread_local", options_src); + const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local); var library_name: ?[]const u8 = null; - if (!fields[library_name_index].isNull()) { - const payload = fields[library_name_index].castTag(.opt_payload).?.data; + if (!library_name_val.isNull()) { + const payload = library_name_val.castTag(.opt_payload).?.data; library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena); } break :options std.builtin.ExternOptions{ - .name = try fields[name_index].toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena), + .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena), .library_name = library_name, - .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), - .is_thread_local = fields[is_thread_local_index].toBool(), + .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), + .is_thread_local = is_thread_local_val.toBool(), }; }; From 403a1fe5d767e801b58fc706eaa54f1171ae27de Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 1 Mar 2022 21:57:06 +0200 Subject: [PATCH 3/3] stage2: add cast from ?*T to ?*anyopaque --- lib/std/special/test_runner.zig | 5 ++--- src/Sema.zig | 14 +++++++++++--- src/type.zig | 17 ++++++++++++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 0d90bbfeaa..58a9d78974 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -46,10 +46,9 @@ pub fn main() void { var leaks: usize = 0; for (test_fn_list) |test_fn, i| { - const gpa_works = builtin.zig_backend == .stage1 or builtin.os.tag != .macos; - if (gpa_works) std.testing.allocator_instance = .{}; + std.testing.allocator_instance = .{}; defer { - if (gpa_works and std.testing.allocator_instance.deinit()) { + if (std.testing.allocator_instance.deinit()) { leaks += 1; } } diff --git a/src/Sema.zig b/src/Sema.zig index 6496adf96d..d96acfea24 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12371,7 +12371,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const type_res = try sema.resolveType(block, src, extra.lhs); try sema.checkPtrType(block, type_src, type_res); - _ = try sema.resolveTypeLayout(block, src, type_res.childType()); + try sema.resolveTypeLayout(block, src, type_res.elemType2()); const ptr_align = type_res.ptrAlignment(sema.mod.getTarget()); if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { @@ -15512,6 +15512,14 @@ fn coerce( return sema.addConstant(dest_ty, Value.@"null"); } + // cast from ?*T and ?[*]T to ?*anyopaque + // but don't do it if the source type is a double pointer + if (dest_ty.isPtrLikeOptional() and dest_ty.elemType2().tag() == .anyopaque and + inst_ty.isPtrLikeOptional() and inst_ty.elemType2().zigTypeTag() != .Pointer) + { + return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); + } + // T to ?T const child_type = try dest_ty.optionalChildAlloc(sema.arena); const intermediate = try sema.coerce(block, child_type, inst, inst_src); @@ -16791,6 +16799,7 @@ fn coerceCompatiblePtrs( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) !Air.Inst.Ref { + // TODO check const/volatile/alignment if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { // The comptime Value representation is compatible with both types. return sema.addConstant(dest_ty, val); @@ -18506,6 +18515,7 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE if (inferred_error_set.is_resolved) { return; } + inferred_error_set.is_resolved = true; var it = inferred_error_set.inferred_error_sets.keyIterator(); while (it.next()) |other_error_set_ptr| { @@ -18526,8 +18536,6 @@ fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredE if (other_error_set_ptr.*.is_anyerror) inferred_error_set.is_anyerror = true; } - - inferred_error_set.is_resolved = true; } fn resolveInferredErrorSetTy(sema: *Sema, ty: Type) CompileError!void { diff --git a/src/type.zig b/src/type.zig index 6e58f7a42f..3c08ed5b63 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2168,7 +2168,14 @@ pub const Type = extern union { .mut_slice, .optional_single_const_pointer, .optional_single_mut_pointer, - => return self.cast(Payload.ElemType).?.data.abiAlignment(target), + => { + const child_type = self.cast(Payload.ElemType).?.data; + if (child_type.zigTypeTag() == .Opaque) { + return 1; + } else { + return child_type.abiAlignment(target); + } + }, .manyptr_u8, .manyptr_const_u8, @@ -2181,10 +2188,13 @@ pub const Type = extern union { const ptr_info = self.castTag(.pointer).?.data; if (ptr_info.@"align" != 0) { return ptr_info.@"align"; + } else if (ptr_info.pointee_type.zigTypeTag() == .Opaque) { + return 1; } else { return ptr_info.pointee_type.abiAlignment(target); } }, + .optional => return self.castTag(.optional).?.data.ptrAlignment(target), else => unreachable, } @@ -3235,8 +3245,9 @@ pub const Type = extern union { return child_ty; } }, - - // TODO handle optionals + .optional => ty.castTag(.optional).?.data.childType(), + .optional_single_mut_pointer => ty.castPointer().?.data, + .optional_single_const_pointer => ty.castPointer().?.data, else => unreachable, };