已关闭
feat: disable import lto reflection #601
duan创建于 8月17日关闭于 16 天前
feat: disable import lto reflection #601
已关闭
共 5 个文件变更+187-0
| @@ -261,6 +261,17 @@ struct CJFillMetadata : public PassInfoMixin<CJFillMetadata> { | |||
| 261 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; | 261 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; |
| 262 | }; | 262 | }; |
| 263 | 263 | ||
| 264 | +// CJDisableImportLibReflection strips import-lib reflection at LTO post-link | ||
| 265 | +// under --disable-reflection: clears the TF_REFLECTION flag on each | ||
| 266 | +// TypeInfo/TypeTemplate, rewrites oversized reflect structs to a small .dbg | ||
| 267 | +// global (enum keeps ctorInfo/modifier/ctorCnt, class keeps fieldNames), keeps | ||
| 268 | +// already-minimal/unsupported reflect operands unchanged, and erases the | ||
| 269 | +// now-unreferenced reflect globals. | ||
| 270 | +struct CJDisableImportLibReflection | ||
| 271 | + : public PassInfoMixin<CJDisableImportLibReflection> { | ||
| 272 | + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; | ||
| 273 | +}; | ||
| 274 | + | ||
| 264 | constexpr uint32_t ArrayHeadSize = 16; | 275 | constexpr uint32_t ArrayHeadSize = 16; |
| 265 | constexpr uint32_t ObjectHeadSize = 8; | 276 | constexpr uint32_t ObjectHeadSize = 8; |
| 266 | constexpr uint32_t SyncSize = 168; | 277 | constexpr uint32_t SyncSize = 168; |
| @@ -327,6 +327,10 @@ cl::opt<bool> | |||
| 327 | EnableCJPtrAuthBackwardCFI("cj-ptrauth-backward-cfi", | 327 | EnableCJPtrAuthBackwardCFI("cj-ptrauth-backward-cfi", |
| 328 | cl::desc("Cangjie PtrAuth-based backward CFI"), | 328 | cl::desc("Cangjie PtrAuth-based backward CFI"), |
| 329 | cl::NotHidden, cl::init(false)); | 329 | cl::NotHidden, cl::init(false)); |
| 330 | +cl::opt<bool> DisableCJLTOReflection("cj-disable-lto-reflection", cl::Hidden, | ||
| 331 | + cl::init(false), | ||
| 332 | + cl::desc("Disable reflection metadata in" | ||
| 333 | + " cangjie LTO pipelines")); | ||
| 330 | 334 | ||
| 331 | namespace llvm { | 335 | namespace llvm { |
| 332 | cl::opt<bool> PrintPipelinePasses( | 336 | cl::opt<bool> PrintPipelinePasses( |
| @@ -203,6 +203,7 @@ extern cl::opt<int> MaxRecursionInl; | |||
| 203 | extern cl::opt<int> CountedLoopTripWidth; | 203 | extern cl::opt<int> CountedLoopTripWidth; |
| 204 | extern cl::opt<bool> CangjieLTOPreOpt; | 204 | extern cl::opt<bool> CangjieLTOPreOpt; |
| 205 | extern cl::opt<bool> EnableCJPtrAuthBackwardCFI; | 205 | extern cl::opt<bool> EnableCJPtrAuthBackwardCFI; |
| 206 | +extern cl::opt<bool> DisableCJLTOReflection; | ||
| 206 | 207 | ||
| 207 | PipelineTuningOptions::PipelineTuningOptions() { | 208 | PipelineTuningOptions::PipelineTuningOptions() { |
| 208 | LoopInterleaving = true; | 209 | LoopInterleaving = true; |
| @@ -1498,6 +1499,8 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level, | |||
| 1498 | else if (LTOPreLink) | 1499 | else if (LTOPreLink) |
| 1499 | LTOPhase = ThinOrFullLTOPhase::FullLTOPreLink; | 1500 | LTOPhase = ThinOrFullLTOPhase::FullLTOPreLink; |
| 1500 | 1501 | ||
| 1502 | + // Import-lib reflection is trimmed at post-link by CJDisableImportLibReflection; | ||
| 1503 | + // the main package already has TF_REFLECTION=0 under --disable-reflection. | ||
| 1501 | // Add the core simplification pipeline. | 1504 | // Add the core simplification pipeline. |
| 1502 | MPM.addPass(buildModuleSimplificationPipeline(Level, LTOPhase)); | 1505 | MPM.addPass(buildModuleSimplificationPipeline(Level, LTOPhase)); |
| 1503 | 1506 | ||
| @@ -1613,6 +1616,8 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline( | |||
| 1613 | // with ThinLTO in order to avoid leaving undefined references to dead | 1616 | // with ThinLTO in order to avoid leaving undefined references to dead |
| 1614 | // globals in the object file. | 1617 | // globals in the object file. |
| 1615 | MPM.addPass(EliminateAvailableExternallyPass()); | 1618 | MPM.addPass(EliminateAvailableExternallyPass()); |
| 1619 | + if (CJPipeline && DisableCJLTOReflection) | ||
| 1620 | + MPM.addPass(CJDisableImportLibReflection()); | ||
| 1616 | MPM.addPass(GlobalDCEPass()); | 1621 | MPM.addPass(GlobalDCEPass()); |
| 1617 | return MPM; | 1622 | return MPM; |
| 1618 | } | 1623 | } |
| @@ -1620,6 +1625,10 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline( | |||
| 1620 | // Force any function attributes we want the rest of the pipeline to observe. | 1625 | // Force any function attributes we want the rest of the pipeline to observe. |
| 1621 | MPM.addPass(ForceFunctionAttrsPass()); | 1626 | MPM.addPass(ForceFunctionAttrsPass()); |
| 1622 | 1627 | ||
| 1628 | + if (CJPipeline && DisableCJLTOReflection) { | ||
| 1629 | + MPM.addPass(CJDisableImportLibReflection()); | ||
| 1630 | + } | ||
| 1631 | + | ||
| 1623 | // Add the core simplification pipeline. | 1632 | // Add the core simplification pipeline. |
| 1624 | MPM.addPass(buildModuleSimplificationPipeline( | 1633 | MPM.addPass(buildModuleSimplificationPipeline( |
| 1625 | Level, ThinOrFullLTOPhase::ThinLTOPostLink)); | 1634 | Level, ThinOrFullLTOPhase::ThinLTOPostLink)); |
| @@ -1687,6 +1696,11 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, | |||
| 1687 | // Emit annotation remarks. | 1696 | // Emit annotation remarks. |
| 1688 | addAnnotationRemarksPass(MPM); | 1697 | addAnnotationRemarksPass(MPM); |
| 1689 | 1698 | ||
| 1699 | + if (CJPipeline && DisableCJLTOReflection) { | ||
| 1700 | + MPM.addPass(CJDisableImportLibReflection()); | ||
| 1701 | + // No GlobalDCE at -O0; the pass self-erases dead reflection globals. | ||
| 1702 | + } | ||
| 1703 | + | ||
| 1690 | return MPM; | 1704 | return MPM; |
| 1691 | } | 1705 | } |
| 1692 | 1706 | ||
| @@ -1701,6 +1715,10 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, | |||
| 1701 | ThinOrFullLTOPhase::FullLTOPostLink); | 1715 | ThinOrFullLTOPhase::FullLTOPostLink); |
| 1702 | } | 1716 | } |
| 1703 | 1717 | ||
| 1718 | + if (CJPipeline && DisableCJLTOReflection) { | ||
| 1719 | + MPM.addPass(CJDisableImportLibReflection()); | ||
| 1720 | + } | ||
| 1721 | + | ||
| 1704 | if (PGOOpt && PGOOpt->Action == PGOOptions::SampleUse) { | 1722 | if (PGOOpt && PGOOpt->Action == PGOOptions::SampleUse) { |
| 1705 | // Load sample profile before running the LTO optimization pipeline. | 1723 | // Load sample profile before running the LTO optimization pipeline. |
| 1706 | MPM.addPass(SampleProfileLoaderPass(PGOOpt->ProfileFile, | 1724 | MPM.addPass(SampleProfileLoaderPass(PGOOpt->ProfileFile, |
| @@ -132,6 +132,7 @@ MODULE_PASS("place-safepoints", PlaceSafepoints()) | |||
| 132 | MODULE_PASS("cj-allocation-size-check", CJAllocationSizeCheck()) | 132 | MODULE_PASS("cj-allocation-size-check", CJAllocationSizeCheck()) |
| 133 | MODULE_PASS("cj-ir-verifier", CJIRVerifier()) | 133 | MODULE_PASS("cj-ir-verifier", CJIRVerifier()) |
| 134 | MODULE_PASS("cj-fill-metadata", CJFillMetadata()) | 134 | MODULE_PASS("cj-fill-metadata", CJFillMetadata()) |
| 135 | +MODULE_PASS("cj-disable-import-lib-reflection", CJDisableImportLibReflection()) | ||
| 135 | MODULE_PASS("cj-barrier-opt", CJBarrierOpt()) | 136 | MODULE_PASS("cj-barrier-opt", CJBarrierOpt()) |
| 136 | MODULE_PASS("cj-runtime-lowering", CJRuntimeLowering()) | 137 | MODULE_PASS("cj-runtime-lowering", CJRuntimeLowering()) |
| 137 | MODULE_PASS("cj-specific-opt", CJSpecificOpt()) | 138 | MODULE_PASS("cj-specific-opt", CJSpecificOpt()) |
| @@ -1625,6 +1625,159 @@ PreservedAnalyses CJFillMetadata::run(Module &M, ModuleAnalysisManager &) const | |||
| 1625 | return PreservedAnalyses::all(); | 1625 | return PreservedAnalyses::all(); |
| 1626 | } | 1626 | } |
| 1627 | 1627 | ||
| 1628 | +namespace { | ||
| 1629 | + | ||
| 1630 | +// Copy the debug fields of a reflect struct into a small .dbg global. | ||
| 1631 | +// Enum reflects keep operands [0..2] (ctorInfo/modifier/ctorCnt), non-enum | ||
| 1632 | +// reflects keep operand [0] only. We only rewrite when that compact form would | ||
| 1633 | +// actually shrink the payload; otherwise the caller keeps the original | ||
| 1634 | +// reflection operand unchanged. | ||
| 1635 | +// IsEnum comes from the owner's type byte, not operand count — enum reflects | ||
| 1636 | +// can grow to 15+ operands, so field count is not a reliable discriminator. | ||
| 1637 | +GlobalVariable *downgradeReflectGlobal(GlobalVariable &ReflectGV, bool IsEnum) { | ||
| 1638 | + if (!ReflectGV.hasInitializer()) | ||
| 1639 | + return nullptr; | ||
| 1640 | + | ||
| 1641 | + auto *Init = dyn_cast<ConstantStruct>(ReflectGV.getInitializer()); | ||
| 1642 | + if (!Init) | ||
| 1643 | + return nullptr; | ||
| 1644 | + | ||
| 1645 | + unsigned NumOps = Init->getNumOperands(); | ||
| 1646 | + unsigned CopiedOps = IsEnum ? 3 : 1; | ||
| 1647 | + if (NumOps <= CopiedOps) | ||
| 1648 | + return nullptr; | ||
| 1649 | + | ||
| 1650 | + SmallVector<Type *, 4> DebugTypes; | ||
| 1651 | + SmallVector<Constant *, 4> DebugOps; | ||
| 1652 | + for (unsigned I = 0; I < CopiedOps; ++I) { | ||
| 1653 | + DebugTypes.push_back(Init->getOperand(I)->getType()); | ||
| 1654 | + DebugOps.push_back(Init->getOperand(I)); | ||
| 1655 | + } | ||
| 1656 | + | ||
| 1657 | + auto *DebugST = StructType::get(ReflectGV.getContext(), DebugTypes); | ||
| 1658 | + auto *DebugGV = new GlobalVariable( | ||
| 1659 | + *ReflectGV.getParent(), DebugST, false, GlobalValue::InternalLinkage, | ||
| 1660 | + ConstantStruct::get(DebugST, DebugOps), ReflectGV.getName() + ".dbg"); | ||
| 1661 | + DebugGV->copyAttributesFrom(&ReflectGV); | ||
| 1662 | + return DebugGV; | ||
| 1663 | +} | ||
| 1664 | + | ||
| 1665 | +bool rewriteReflectionOperand(GlobalVariable &GV, unsigned FlagIdx, | ||
| 1666 | + unsigned ReflectionIdx) { | ||
| 1667 | + if (!GV.hasInitializer()) | ||
| 1668 | + return false; | ||
| 1669 | + | ||
| 1670 | + auto *Init = dyn_cast<ConstantStruct>(GV.getInitializer()); | ||
| 1671 | + if (Init == nullptr || Init->getNumOperands() <= ReflectionIdx) | ||
| 1672 | + return false; | ||
| 1673 | + | ||
| 1674 | + auto *Flag = cast<ConstantInt>(Init->getOperand(FlagIdx)); | ||
| 1675 | + uint64_t NewFlag = Flag->getZExtValue() & ~TF_REFLECTION; | ||
| 1676 | + | ||
| 1677 | + if (NewFlag == Flag->getZExtValue()) | ||
| 1678 | + return false; | ||
| 1679 | + | ||
| 1680 | + SmallVector<Constant *, 0> Ops; | ||
| 1681 | + Ops.reserve(Init->getNumOperands()); | ||
| 1682 | + for (unsigned I = 0; I < Init->getNumOperands(); ++I) | ||
| 1683 | + Ops.push_back(cast<Constant>(Init->getOperand(I))); | ||
| 1684 | + | ||
| 1685 | + Ops[FlagIdx] = ConstantInt::get(Flag->getType(), NewFlag); | ||
| 1686 | + | ||
| 1687 | + auto *ReflectOp = Init->getOperand(ReflectionIdx); | ||
| 1688 | + auto *CE = dyn_cast<ConstantExpr>(ReflectOp); | ||
| 1689 | + if (CE && CE->getOpcode() == Instruction::BitCast) { | ||
| 1690 | + bool IsEnum = false; | ||
| 1691 | + unsigned TypeIdx = FlagIdx == CIT_FLAG ? CIT_TYPE : TT_TYPE; | ||
| 1692 | + if (auto *TypeCI = dyn_cast<ConstantInt>(Init->getOperand(TypeIdx))) { | ||
| 1693 | + int64_t Kind = TypeCI->getSExtValue(); | ||
| 1694 | + IsEnum = (Kind == TK_ENUM || Kind == TK_TEMP_ENUM); | ||
| 1695 | + } | ||
| 1696 | + | ||
| 1697 | + if (auto *ReflectGV = dyn_cast<GlobalVariable>(CE->getOperand(0))) { | ||
| 1698 | + if (auto *DebugGV = downgradeReflectGlobal(*ReflectGV, IsEnum)) { | ||
| 1699 | + Ops[ReflectionIdx] = ConstantExpr::getBitCast(DebugGV, CE->getType()); | ||
| 1700 | + } | ||
| 1701 | + } | ||
| 1702 | + } | ||
| 1703 | + | ||
| 1704 | + GV.setInitializer(ConstantStruct::get(Init->getType(), Ops)); | ||
| 1705 | + return true; | ||
| 1706 | +} | ||
| 1707 | + | ||
| 1708 | +bool rewritePackageInfoPayload(GlobalVariable &GV) { | ||
| 1709 | + if (!GV.hasInitializer()) | ||
| 1710 | + return false; | ||
| 1711 | + | ||
| 1712 | + auto *Init = dyn_cast<ConstantStruct>(GV.getInitializer()); | ||
| 1713 | + if (Init == nullptr || Init->getNumOperands() < FPT_PTRS) | ||
| 1714 | + return false; | ||
| 1715 | + | ||
| 1716 | + // Clear only FPT_FLAG (PackageInfo::isVaild) to make package lookup fail; zeroing the counts | ||
| 1717 | + // would misalign the runtime's GetPackageSize() stride walk over the packageInfos. | ||
| 1718 | + auto *Flag = dyn_cast<ConstantInt>(Init->getOperand(FPT_FLAG)); | ||
| 1719 | + if (Flag == nullptr || Flag->isZero()) | ||
| 1720 | + return false; | ||
| 1721 | + | ||
| 1722 | + SmallVector<Constant *, 0> Ops; | ||
| 1723 | + Ops.reserve(Init->getNumOperands()); | ||
| 1724 | + for (unsigned I = 0; I < Init->getNumOperands(); ++I) | ||
| 1725 | + Ops.push_back(cast<Constant>(Init->getOperand(I))); | ||
| 1726 | + | ||
| 1727 | + Ops[FPT_FLAG] = ConstantInt::get(Flag->getType(), 0); | ||
| 1728 | + GV.setInitializer(ConstantStruct::get(Init->getType(), Ops)); | ||
| 1729 | + return true; | ||
| 1730 | +} | ||
| 1731 | + | ||
| 1732 | +// Erase reflect globals left unreferenced by the rewrite (needed at FullLTO | ||
| 1733 | +// -O0, where no GlobalDCE follows). Scope: internal + isCJReflectGV() + no | ||
| 1734 | +// uses; fixpoint collects chains. .dbg/fieldNames stay referenced and never | ||
| 1735 | +// touched. | ||
| 1736 | +void eraseReflectGlobals(Module &M) { | ||
| 1737 | + bool RoundChanged = true; | ||
| 1738 | + while (RoundChanged) { | ||
| 1739 | + RoundChanged = false; | ||
| 1740 | + SmallVector<GlobalVariable *, 16> ToErase; | ||
| 1741 | + for (auto &GV : M.globals()) { | ||
| 1742 | + if (GV.isDeclaration() || !GV.isCJReflectGV() || | ||
| 1743 | + !GlobalValue::isInternalLinkage(GV.getLinkage())) | ||
| 1744 | + continue; | ||
| 1745 | + // The rewrite detaches the old TI/TT initializer constant, whose nested | ||
| 1746 | + // constant exprs still count as uses; drop them so use_empty() is accurate. | ||
| 1747 | + GV.removeDeadConstantUsers(); | ||
| 1748 | + if (!GV.use_empty()) | ||
| 1749 | + continue; | ||
| 1750 | + ToErase.push_back(&GV); | ||
| 1751 | + } | ||
| 1752 | + for (auto *GV : ToErase) { | ||
| 1753 | + GV->eraseFromParent(); | ||
| 1754 | + RoundChanged = true; | ||
| 1755 | + } | ||
| 1756 | + } | ||
| 1757 | +} | ||
| 1758 | +} // namespace | ||
| 1759 | + | ||
| 1760 | +PreservedAnalyses | ||
| 1761 | +CJDisableImportLibReflection::run(Module &M, ModuleAnalysisManager &) const { | ||
| 1762 | + bool Changed = false; | ||
| 1763 | + for (auto &GV : M.globals()) { | ||
| 1764 | + if (GV.isCJTypeInfo()) { | ||
| 1765 | + Changed |= rewriteReflectionOperand(GV, CIT_FLAG, CIT_REFLECTION); | ||
| 1766 | + } else if (GV.isCJTypeTemplate()) { | ||
| 1767 | + Changed |= rewriteReflectionOperand(GV, TT_FLAG, TT_REFLECTION); | ||
| 1768 | + } else if (GV.isCJReflectPkgInfo()) { | ||
| 1769 | + Changed |= rewritePackageInfoPayload(GV); | ||
| 1770 | + } | ||
| 1771 | + } | ||
| 1772 | + // Self-erase dead reflection globals; harmless where GlobalDCE follows. | ||
| 1773 | + // Nothing becomes dead unless a rewrite happened, so skip the scan otherwise. | ||
| 1774 | + if (Changed) { | ||
| 1775 | + eraseReflectGlobals(M); | ||
| 1776 | + return PreservedAnalyses::none(); | ||
| 1777 | + } | ||
| 1778 | + return PreservedAnalyses::all(); | ||
| 1779 | +} | ||
| 1780 | + | ||
| 1628 | namespace { | 1781 | namespace { |
| 1629 | class CJFillMetadataLegacyPass : public ModulePass { | 1782 | class CJFillMetadataLegacyPass : public ModulePass { |
| 1630 | public: | 1783 | public: |
所以 这个pass 需要跑在 GlobalDCEPass 之前么