diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 8c1e38ea09..3b0a24d3f5 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -68,6 +68,14 @@ pub const GlobalLinkage = enum { LinkOnce, }; +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const GlobalVisibility = enum { + default, + hidden, + protected, +}; + /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const AtomicOrder = enum { @@ -655,6 +663,7 @@ pub const ExportOptions = struct { name: []const u8, linkage: GlobalLinkage = .Strong, section: ?[]const u8 = null, + visibility: GlobalVisibility = .default, }; /// This data structure is used by the Zig language code generation and diff --git a/src/Sema.zig b/src/Sema.zig index 2e7e5c9293..6de4e3704c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4348,6 +4348,7 @@ pub fn analyzeExport( .name = symbol_name, .linkage = borrowed_options.linkage, .section = section, + .visibility = borrowed_options.visibility, }, .src = src, .link = switch (mod.comp.bin_file.tag) { @@ -15007,6 +15008,9 @@ fn resolveExportOptions( const section = try sema.fieldVal(block, src, options, "section", src); const section_val = try sema.resolveConstValue(block, src, section); + const visibility = try sema.fieldVal(block, src, options, "visibility", src); + const visibility_val = try sema.resolveConstValue(block, src, visibility); + if (!section_val.isNull()) { return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); } @@ -15015,6 +15019,7 @@ fn resolveExportOptions( .name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod), .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .section = null, // TODO + .visibility = visibility_val.toEnum(std.builtin.GlobalVisibility), }; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 57bcbe9338..5e70710046 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -808,6 +808,11 @@ pub const Object = struct { .Weak => llvm_global.setLinkage(.WeakODR), .LinkOnce => llvm_global.setLinkage(.LinkOnceODR), } + switch (exports[0].options.visibility) { + .default => llvm_global.setVisibility(.Default), + .hidden => llvm_global.setVisibility(.Hidden), + .protected => llvm_global.setVisibility(.Protected), + } if (decl.val.castTag(.variable)) |variable| { if (variable.data.is_threadlocal) { llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index c70a7d1553..f3987a8665 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -117,6 +117,9 @@ pub const Value = opaque { pub const setLinkage = LLVMSetLinkage; extern fn LLVMSetLinkage(Global: *const Value, Linkage: Linkage) void; + pub const setVisibility = LLVMSetVisibility; + extern fn LLVMSetVisibility(Global: *const Value, Linkage: Visibility) void; + pub const setUnnamedAddr = LLVMSetUnnamedAddr; extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void; @@ -1324,6 +1327,12 @@ pub const Linkage = enum(c_uint) { LinkerPrivateWeak, }; +pub const Visibility = enum(c_uint) { + Default, + Hidden, + Protected, +}; + pub const ThreadLocalMode = enum(c_uint) { NotThreadLocal, GeneralDynamicTLSModel, diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index ba5df49c59..f2395efdcb 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -571,6 +571,12 @@ enum GlobalLinkageId { GlobalLinkageIdLinkOnce, }; +enum GlobalVisibilityId { + GlobalVisibilityIdDefault, + GlobalVisibilityIdHidden, + GlobalVisibilityIdProtected, +}; + enum TldId { TldIdVar, TldIdFn, @@ -1654,6 +1660,7 @@ enum FnAnalState { struct GlobalExport { Buf name; GlobalLinkageId linkage; + GlobalVisibilityId visibility; }; struct ZigFn { diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 8faf3c0fc9..0a5468cbc3 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3717,14 +3717,15 @@ ZigType *get_test_fn_type(CodeGen *g) { return g->test_fn_type; } -void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage) { +void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility) { GlobalExport *global_export = var->export_list.add_one(); memset(global_export, 0, sizeof(GlobalExport)); buf_init_from_str(&global_export->name, symbol_name); global_export->linkage = linkage; + global_export->visibility = visibility; } -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) { +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility, CallingConvention cc) { CallingConvention winapi_cc = g->zig_target->arch == ZigLLVM_x86 ? CallingConventionStdcall : CallingConventionC; @@ -3749,6 +3750,7 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, G memset(fn_export, 0, sizeof(GlobalExport)); buf_init_from_str(&fn_export->name, symbol_name); fn_export->linkage = linkage; + fn_export->visibility = visibility; } static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { @@ -3852,13 +3854,13 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { case CallingConventionWin64: case CallingConventionPtxKernel: add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), - GlobalLinkageIdStrong, fn_cc); + GlobalLinkageIdStrong, GlobalVisibilityIdDefault, fn_cc); break; case CallingConventionUnspecified: // An exported function without a specific calling // convention defaults to C add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), - GlobalLinkageIdStrong, CallingConventionC); + GlobalLinkageIdStrong, GlobalVisibilityIdDefault, CallingConventionC); break; } } @@ -4321,7 +4323,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { if (is_export) { validate_export_var_type(g, type, source_node); - add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong); + add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong, GlobalVisibilityIdDefault); } if (is_extern) { diff --git a/src/stage1/analyze.hpp b/src/stage1/analyze.hpp index 64e0e199f8..d4985286d5 100644 --- a/src/stage1/analyze.hpp +++ b/src/stage1/analyze.hpp @@ -221,8 +221,8 @@ ZigType *get_align_amt_type(CodeGen *g); ZigPackage *new_anonymous_package(void); Buf *const_value_to_buffer(ZigValue *const_val); -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc); -void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage); +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility, CallingConvention cc); +void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility); ZigValue *get_builtin_value(CodeGen *codegen, const char *name); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index d101030c33..dece8e7348 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -242,6 +242,18 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id, bool is_extern) { zig_unreachable(); } +static LLVMVisibility to_llvm_visibility(GlobalVisibilityId id) { + switch (id) { + case GlobalVisibilityIdDefault: + return LLVMDefaultVisibility; + case GlobalVisibilityIdHidden: + return LLVMHiddenVisibility; + case GlobalVisibilityIdProtected: + return LLVMProtectedVisibility; + } + zig_unreachable(); +} + struct CalcLLVMFieldIndex { uint32_t offset; uint32_t field_index; @@ -400,6 +412,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { const char *unmangled_name = buf_ptr(&fn->symbol_name); const char *symbol_name; GlobalLinkageId linkage; + GlobalVisibilityId visibility = GlobalVisibilityIdDefault; if (fn->body_node == nullptr) { symbol_name = unmangled_name; linkage = GlobalLinkageIdStrong; @@ -410,6 +423,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { GlobalExport *fn_export = &fn->export_list.items[0]; symbol_name = buf_ptr(&fn_export->name); linkage = fn_export->linkage; + visibility = fn_export->visibility; } CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc; @@ -532,6 +546,8 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { LLVMSetUnnamedAddr(llvm_fn, true); } + LLVMSetVisibility(llvm_fn, to_llvm_visibility(visibility)); + ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; if (return_type->id == ZigTypeIdUnreachable) { addLLVMFnAttr(llvm_fn, "noreturn"); @@ -8951,6 +8967,7 @@ static void do_code_gen(CodeGen *g) { assert(var->decl_node); GlobalLinkageId linkage; + GlobalVisibilityId visibility = GlobalVisibilityIdDefault; const char *unmangled_name = var->name; const char *symbol_name; if (var->export_list.length == 0) { @@ -8965,6 +8982,7 @@ static void do_code_gen(CodeGen *g) { GlobalExport *global_export = &var->export_list.items[0]; symbol_name = buf_ptr(&global_export->name); linkage = global_export->linkage; + visibility = global_export->visibility; } LLVMValueRef global_value; @@ -9010,6 +9028,8 @@ static void do_code_gen(CodeGen *g) { set_global_tls(g, var, global_value); } + LLVMSetVisibility(global_value, to_llvm_visibility(visibility)); + var->value_ref = global_value; for (size_t export_i = 1; export_i < var->export_list.length; export_i += 1) { diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 62834e564d..02b9dbd48b 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -8635,6 +8635,25 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, Stage1AirInst *value, Glob return true; } +static bool ir_resolve_global_visibility(IrAnalyze *ira, Stage1AirInst *value, GlobalVisibilityId *out) { + if (type_is_invalid(value->value->type)) + return false; + + ZigType *global_visibility_type = get_builtin_type(ira->codegen, "GlobalVisibility"); + + Stage1AirInst *casted_value = ir_implicit_cast(ira, value, global_visibility_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (GlobalVisibilityId)bigint_as_u32(&const_val->data.x_enum_tag); + return true; +} + + static bool ir_resolve_float_mode(IrAnalyze *ira, Stage1AirInst *value, FloatMode *out) { if (type_is_invalid(value->value->type)) return false; @@ -11661,6 +11680,12 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (type_is_invalid(section_inst->value->type)) return ira->codegen->invalid_inst_gen; + TypeStructField *visibility_field = find_struct_type_field(options_type, buf_create_from_str("visibility")); + src_assert(visibility_field != nullptr, instruction->base.source_node); + Stage1AirInst *visibility_inst = ir_analyze_struct_value_field_value(ira, instruction->base.scope, instruction->base.source_node, options, visibility_field); + if (type_is_invalid(visibility_inst->value->type)) + return ira->codegen->invalid_inst_gen; + // The `section` field is optional, we have to unwrap it first Stage1AirInst *non_null_check = ir_analyze_test_non_null(ira, instruction->base.scope, instruction->base.source_node, section_inst); bool is_non_null; @@ -11689,6 +11714,10 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) return ira->codegen->invalid_inst_gen; + GlobalVisibilityId global_visibility_id; + if (!ir_resolve_global_visibility(ira, visibility_inst, &global_visibility_id)) + return ira->codegen->invalid_inst_gen; + Buf *section_name = nullptr; if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) return ira->codegen->invalid_inst_gen; @@ -11751,7 +11780,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns case CallingConventionSysV: case CallingConventionWin64: case CallingConventionPtxKernel: - add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); + add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, global_visibility_id, cc); fn_entry->section_name = section_name; break; } @@ -11898,7 +11927,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (load_ptr->ptr->id == Stage1AirInstIdVarPtr) { Stage1AirInstVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); ZigVar *var = var_ptr->var; - add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); + add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id, global_visibility_id); var->section_name = section_name; } }