已开启
feat: refine iOS LTO staticlib #605
wangyang594创建于 17 天前
feat: refine iOS LTO staticlib #605
已开启
共 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>; | ||
| 125 | def dylinker : Flag<["-"], "dylinker">, | 128 | def 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>; | ||
| 955 | def cache_path_lto : Separate<["-"], "cache_path_lto">, | 955 | def 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 | |||
| 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 write | 266 | /// This is a convenience function that configures this Config object to write |
| 256 | /// temporary files named after the given OutputFileName for each of the LTO | 267 | /// 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 module | 164 | // 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)); |
GUID :特指为GlobalValue(如函数、全局变量等)生成的一个64位哈希值,用于在跨编译单元优化(如LTO、ThinLTO) 中唯一标识一个全局符号