Commit 9654a54d4a

Takeshi Yoneda <takeshi@tetrate.io>
2021-09-24 15:14:53
Add Visibility field to ExportOptions.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
1 parent 67c4b16
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
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,
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);
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 {
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) {
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);
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) {
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<Stage1AirInstVarPtr *>(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;
         }
     }
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),
     };
 }