已开启
feat: refine iOS LTO staticlib #605
feat: refine iOS LTO staticlib #605
已开启
wangyang594创建于 17 天前
7 个文件变更+114-10
@@ -153,7 +153,7 @@ struct Configuration {
153 llvm::StringRef thinLTOJobs;153 llvm::StringRef thinLTOJobs;
154 llvm::StringRef umbrella;154 llvm::StringRef umbrella;
155 uint32_t ltoo = 2;155 uint32_t ltoo = 2;
156- bool ltoEmitObjOnly = false;156+ bool staticlib = false;
157 llvm::CachePruningPolicy thinLTOCachePolicy;157 llvm::CachePruningPolicy thinLTOCachePolicy;
158 llvm::StringRef thinLTOCacheDir;158 llvm::StringRef thinLTOCacheDir;
159 bool deadStripDylibs = false;159 bool deadStripDylibs = false;
@@ -1383,7 +1383,7 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
1383 config->umbrella = arg->getValue();1383 config->umbrella = arg->getValue();
1384 }1384 }
1385 config->ltoObjPath = args.getLastArgValue(OPT_object_path_lto);1385 config->ltoObjPath = args.getLastArgValue(OPT_object_path_lto);
1386- config->ltoEmitObjOnly = args.hasArg(OPT_lto_emit_obj_only);1386+ config->staticlib = args.hasArg(OPT_staticlib);
1387 config->ltoo = args::getInteger(args, OPT_lto_O, 2);1387 config->ltoo = args::getInteger(args, OPT_lto_O, 2);
1388 if (config->ltoo > 3)1388 if (config->ltoo > 3)
1389 error("--lto-O: invalid optimization level: " + Twine(config->ltoo));1389 error("--lto-O: invalid optimization level: " + Twine(config->ltoo));
@@ -1665,7 +1665,7 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
1665 parseClangOption(arg->getValue(), arg->getSpelling());1665 parseClangOption(arg->getValue(), arg->getSpelling());
1666 1666 
1667 compileBitcodeFiles();1667 compileBitcodeFiles();
1668- if (config->ltoEmitObjOnly)1668+ if (config->staticlib)
1669 return !errorCount();1669 return !errorCount();
1670 replaceCommonSymbols();1670 replaceCommonSymbols();
1671 1671 
@@ -71,6 +71,16 @@ static lto::Config createConfig() {
71 c.OptLevel = config->ltoo;71 c.OptLevel = config->ltoo;
72 c.CGOptLevel = args::getCGOptLevel(config->ltoo);72 c.CGOptLevel = args::getCGOptLevel(config->ltoo);
73 c.OpaquePointers =false;73 c.OpaquePointers =false;
74+ // Cangjie: enable package-visibility lowering when the package-visibility
75+ // path engaged: --hide-all-visible-pkgs forces it on, otherwise at least one
76+ // bitcode module matched a listed package (hasMatchedVisiblePkg). Without it,
77+ // HiddenGUIDs stays empty and ordinary LTO links (iOS executables, dylibs,
78+ // archives without --visible-pkgs) hide nothing. Which symbols get hidden is
79+ // decided separately by the ExportDynamic resolution in BitcodeCompiler::add
80+ // (visible-package symbols get ExportDynamic=true and never enter
81+ // HiddenGUIDs).
82+ c.EnablePackageVisibility =
83+ config->hideAllVisiblePkgs || config->hasMatchedVisiblePkg;
74 if (config->saveTemps)84 if (config->saveTemps)
75 checkError(c.addSaveTemps(config->outputFile.str() + ".",85 checkError(c.addSaveTemps(config->outputFile.str() + ".",
76 /*UseInputModulePath=*/true));86 /*UseInputModulePath=*/true));
@@ -91,7 +101,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
91 101 
92 // Provide a resolution to the LTO API for each symbol.102 // Provide a resolution to the LTO API for each symbol.
93 bool defaultExportDynamic =103 bool defaultExportDynamic =
94- config->outputType != MH_EXECUTE || config->exportDynamic;104+ config->outputType != MH_EXECUTE || config->exportDynamic || config->staticlib;
95 bool exportDynamic = defaultExportDynamic && exportForPkg;105 bool exportDynamic = defaultExportDynamic && exportForPkg;
96 auto symIt = f.symbols.begin();106 auto symIt = f.symbols.begin();
97 for (const lto::InputFile::Symbol &objSym : objSyms) {107 for (const lto::InputFile::Symbol &objSym : objSyms) {
@@ -107,8 +117,13 @@ void BitcodeCompiler::add(BitcodeFile &f) {
107 r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;117 r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
108 118 
109 if (const auto *defined = dyn_cast<Defined>(sym)) {119 if (const auto *defined = dyn_cast<Defined>(sym)) {
110- r.ExportDynamic =120+ // Only the file that actually defines this symbol may mark it export
111- defined->isExternal() && !defined->privateExtern && exportDynamic;121+ // dynamic. The merged lld Symbol below can be defined by a different
122+ // input file (e.g. lib1 referencing lib2's function); applying this
123+ // file's export policy to such a reference would un-hide a symbol whose
124+ // defining package is not visible (--visible-pkgs).
125+ r.ExportDynamic = !objSym.isUndefined() && defined->isExternal() &&
126+ !defined->privateExtern && exportDynamic;
112 r.FinalDefinitionInLinkageUnit =127 r.FinalDefinitionInLinkageUnit =
113 !defined->isExternalWeakDef() && !defined->interposable;128 !defined->isExternalWeakDef() && !defined->interposable;
114 } else if (const auto *common = dyn_cast<CommonSymbol>(sym)) {129 } else if (const auto *common = dyn_cast<CommonSymbol>(sym)) {
@@ -122,6 +122,9 @@ def r : Flag<["-"], "r">,
122 HelpText<"Merge multiple object files into one, retaining relocations">,122 HelpText<"Merge multiple object files into one, retaining relocations">,
123 Flags<[HelpHidden]>,123 Flags<[HelpHidden]>,
124 Group<grp_kind>;124 Group<grp_kind>;
125+def staticlib : Flag<["-"], "staticlib">,
126+ HelpText<"Produce a static library">,
127+ Group<grp_kind>;
125def dylinker : Flag<["-"], "dylinker">,128def dylinker : Flag<["-"], "dylinker">,
126 HelpText<"Produce a dylinker only used when building dyld">,129 HelpText<"Produce a dylinker only used when building dyld">,
127 Flags<[HelpHidden]>,130 Flags<[HelpHidden]>,
@@ -949,9 +952,6 @@ def object_path_lto : Separate<["-"], "object_path_lto">,
949 MetaVarName<"<path>">,952 MetaVarName<"<path>">,
950 HelpText<"Retain any temporary mach-o file in <path> that would otherwise be deleted during LTO">,953 HelpText<"Retain any temporary mach-o file in <path> that would otherwise be deleted during LTO">,
951 Group<grp_rare>;954 Group<grp_rare>;
952-def lto_emit_obj_only : Flag<["-"], "lto-emit-obj-only">,
953- HelpText<"Stop after emitting LTO native object files">,
954- Group<grp_rare>;
955def cache_path_lto : Separate<["-"], "cache_path_lto">,955def cache_path_lto : Separate<["-"], "cache_path_lto">,
956 MetaVarName<"<path>">,956 MetaVarName<"<path>">,
957 HelpText<"Use <path> as a directory for the incremental LTO cache">,957 HelpText<"Use <path> as a directory for the incremental LTO cache">,
@@ -252,6 +252,17 @@ struct Config {
252 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols)>;252 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols)>;
253 CombinedIndexHookFn CombinedIndexHook;253 CombinedIndexHookFn CombinedIndexHook;
254 254 
255+ /// Cangjie: symbols (by GUID) hidden in the thin backend output, mapped to
256+ /// N_PEXT (private extern) on Mach-O, to implement package-level symbol
257+ /// visibility. Only consulted by the thin backend.
258+ DenseSet<GlobalValue::GUID> HiddenGUIDs;
W
Wwangyang5946 天前

GUID :特指为GlobalValue(如函数、全局变量等)生成的一个64位哈希值,用于在跨编译单元优化(如LTO、ThinLTO) 中唯一标识一个全局符号

likedislike
259+ 
260+ /// Cangjie: master gate for package-visibility lowering. Defaults to false.
261+ /// When false, HiddenGUIDs is never populated, so non-visible-package symbols
262+ /// are NOT lowered to N_PEXT -- this keeps LTO links that are not static
263+ /// libraries (e.g. iOS executables) from hiding main/cross-package symbols.
264+ bool EnablePackageVisibility = false;
265+ 
255 /// This is a convenience function that configures this Config object to write266 /// This is a convenience function that configures this Config object to write
256 /// temporary files named after the given OutputFileName for each of the LTO267 /// temporary files named after the given OutputFileName for each of the LTO
257 /// phases to disk. A client can use this function to implement -save-temps.268 /// phases to disk. A client can use this function to implement -save-temps.
@@ -149,6 +149,18 @@ void llvm::computeLTOCacheKey(
149 AddString(Conf.DefaultTriple);149 AddString(Conf.DefaultTriple);
150 AddString(Conf.DwoDir);150 AddString(Conf.DwoDir);
151 151 
152+ // Cangjie: hash the hidden-symbol set so cached objects with different
153+ // package-visibility configurations are not mixed.
154+ {
155+ std::vector<uint64_t> SortedHiddenGUIDs;
156+ SortedHiddenGUIDs.reserve(Conf.HiddenGUIDs.size());
157+ for (uint64_t GUID : Conf.HiddenGUIDs)
158+ SortedHiddenGUIDs.push_back(GUID);
159+ llvm::sort(SortedHiddenGUIDs);
160+ for (uint64_t GUID : SortedHiddenGUIDs)
161+ AddUint64(GUID);
162+ }
163+ 
152 // Include the hash for the current module164 // Include the hash for the current module
153 auto ModHash = Index.getModuleHash(ModuleID);165 auto ModHash = Index.getModuleHash(ModuleID);
154 Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));166 Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
@@ -1066,6 +1078,10 @@ Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
1066 if (Res.second.ExportDynamic)1078 if (Res.second.ExportDynamic)
1067 DynamicExportSymbols.insert(GUID);1079 DynamicExportSymbols.insert(GUID);
1068 1080 
1081+ // Cangjie: prevailing symbols not marked ExportDynamic are from
1082+ // non-visible packages and are hidden in the thin backend output (see
1083+ // runThinLTO for the HiddenGUIDs population).
1084+ 
1069 GUIDPrevailingResolutions[GUID] =1085 GUIDPrevailingResolutions[GUID] =
1070 Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No;1086 Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No;
1071 }1087 }
@@ -1498,6 +1514,24 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
1498 if (ThinLTO.ModuleMap.empty())1514 if (ThinLTO.ModuleMap.empty())
1499 return Error::success();1515 return Error::success();
1500 1516 
1517+ // Cangjie: on the ThinLTO path, populate HiddenGUIDs with the prevailing
1518+ // symbols that are not marked ExportDynamic (non-visible packages). Visible
1519+ // packages are excluded because BitcodeCompiler::add sets ExportDynamic=true
1520+ // for them, so they stay external. Gated on EnablePackageVisibility, which
1521+ // lld sets only when --visible-pkgs / --hide-all-visible-pkgs actually
1522+ // matched a bitcode module; ordinary LTO links keep it false.
1523+ if (Conf.EnablePackageVisibility) {
1524+ for (auto &Res : GlobalResolutions) {
1525+ if (Res.second.IRName.empty())
1526+ continue;
1527+ if (Res.second.Prevailing && !Res.second.ExportDynamic) {
1528+ GlobalValue::GUID GUID = GlobalValue::getGUID(
1529+ GlobalValue::dropLLVMManglingEscape(Res.second.IRName));
1530+ Conf.HiddenGUIDs.insert(GUID);
1531+ }
1532+ }
1533+ }
1534+ 
1501 if (ThinLTO.ModulesToCompile && ThinLTO.ModulesToCompile->empty()) {1535 if (ThinLTO.ModulesToCompile && ThinLTO.ModulesToCompile->empty()) {
1502 llvm::errs() << "warning: [ThinLTO] No module compiled\n";1536 llvm::errs() << "warning: [ThinLTO] No module compiled\n";
1503 return Error::success();1537 return Error::success();
@@ -649,6 +649,14 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
649 if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(Task, Mod))649 if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(Task, Mod))
650 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));650 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
651 651 
652+ // Cangjie static-lib: the package-visibility semantics (external → N_PEXT,
653+ // local 't' unchanged, llvm.used unchanged) is implemented by the hide-loop
654+ // below, which sets HiddenVisibility on symbols listed in HiddenGUIDs.
655+ // Keeping External linkage + HiddenVisibility yields N_PEXT (private-extern):
656+ // linkable across partitions and archive members, but invisible outside the
657+ // library. llvm.used-pinned symbols are exempted by the hide-loop's UsedGVs
658+ // set (see below); local symbols are guarded by !isLocalLinkage() so they
659+ // stay 't'.
652 auto OptimizeAndCodegen =660 auto OptimizeAndCodegen =
653 [&](Module &Mod, TargetMachine *TM,661 [&](Module &Mod, TargetMachine *TM,
654 std::unique_ptr<ToolOutputFile> DiagnosticOutputFile) {662 std::unique_ptr<ToolOutputFile> DiagnosticOutputFile) {
@@ -656,7 +664,6 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
656 /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex,664 /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex,
657 CmdArgs))665 CmdArgs))
658 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));666 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
659- 
660 ::codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex);667 ::codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex);
661 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));668 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
662 };669 };
@@ -682,6 +689,43 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
682 if (!DefinedGlobals.empty())689 if (!DefinedGlobals.empty())
683 thinLTOInternalizeModule(Mod, DefinedGlobals);690 thinLTOInternalizeModule(Mod, DefinedGlobals);
684 691 
692+ // Cangjie static-lib: hide non-visible-package symbols. This hide-loop is the
693+ // visibility rewriter for package-visibility semantics:
694+ // - hidden prevailing symbols (in HiddenGUIDs) → HiddenVisibility, which
695+ // on Mach-O yields N_PEXT (private-extern): linkable across archive
696+ // members and partitions, invisible outside the library;
697+ // - llvm.used/llvm.compiler.used-pinned symbols are exempt (UsedGVs) so
698+ // they keep their original visibility (e.g. package init entries
699+ // __CJ_..._init must remain externally callable);
700+ // - already-local symbols (InternalLinkage / PrivateLinkage, i.e. Mach-O
701+ // 't'/'non-external') are guarded by !isLocalLinkage(getLinkage()) so
702+ // they are never touched -- 't' stays 't'.
703+ // Note: this runs BEFORE opt(), so collectUsedGlobalVariables reads the
704+ // intact @llvm.used array; GlobalDCE inside opt() still honors @llvm.used as
705+ // a strong IR-level root, so pinned symbols survive.
706+ // Note: the standard library participates in LTO only when the archive is a
707+ // final product (--lto-keep-pkg-visibility delegates interop/final exports to
708+ // explicitly listed packages), so std.core is hidden on the same terms as
709+ // any other non-visible package.
710+ if (!Conf.HiddenGUIDs.empty()) {
711+ SmallVector<GlobalValue *, 8> UsedVec;
712+ collectUsedGlobalVariables(Mod, UsedVec, /*CompilerUsed=*/false);
713+ collectUsedGlobalVariables(Mod, UsedVec, /*CompilerUsed=*/true);
714+ SmallPtrSet<GlobalValue *, 8> UsedGVs(UsedVec.begin(), UsedVec.end());
715+ // Symbols that must stay visible for runtime use are pinned in
716+ // @llvm.used / @llvm.compiler.used; symbols already at local linkage
717+ // would gain nothing from HiddenVisibility (they're already non-exported).
718+ auto MaybeHideGlobal = [&](GlobalValue &GV) {
719+ if (UsedGVs.count(&GV) || GlobalValue::isLocalLinkage(GV.getLinkage()))
720+ return;
721+ if (Conf.HiddenGUIDs.count(GlobalValue::getGUID(
722+ GlobalValue::dropLLVMManglingEscape(GV.getName()))))
723+ GV.setVisibility(GlobalValue::HiddenVisibility);
724+ };
725+ for (GlobalValue &GV : Mod.global_values())
726+ MaybeHideGlobal(GV);
727+ }
728+ 
685 if (Conf.PostInternalizeModuleHook &&729 if (Conf.PostInternalizeModuleHook &&
686 !Conf.PostInternalizeModuleHook(Task, Mod))730 !Conf.PostInternalizeModuleHook(Task, Mod))
687 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));731 return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));