已关闭
feat: disable import lto reflection #601
feat: disable import lto reflection #601
已关闭
duan创建于 8月17日关闭于 16 天前
共 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+ 
264constexpr uint32_t ArrayHeadSize = 16;275constexpr uint32_t ArrayHeadSize = 16;
265constexpr uint32_t ObjectHeadSize = 8;276constexpr uint32_t ObjectHeadSize = 8;
266constexpr uint32_t SyncSize = 168;277constexpr 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 
331namespace llvm {335namespace llvm {
332cl::opt<bool> PrintPipelinePasses(336cl::opt<bool> PrintPipelinePasses(
@@ -203,6 +203,7 @@ extern cl::opt<int> MaxRecursionInl;
203extern cl::opt<int> CountedLoopTripWidth;203extern cl::opt<int> CountedLoopTripWidth;
204extern cl::opt<bool> CangjieLTOPreOpt;204extern cl::opt<bool> CangjieLTOPreOpt;
205extern cl::opt<bool> EnableCJPtrAuthBackwardCFI;205extern cl::opt<bool> EnableCJPtrAuthBackwardCFI;
206+extern cl::opt<bool> DisableCJLTOReflection;
206 207 
207PipelineTuningOptions::PipelineTuningOptions() {208PipelineTuningOptions::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 dead1616 // 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());
睡觉对我很重要

所以 这个pass 需要跑在 GlobalDCEPass 之前么

likedislike
duan
duan
22 天前 评论:
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())
132MODULE_PASS("cj-allocation-size-check", CJAllocationSizeCheck())132MODULE_PASS("cj-allocation-size-check", CJAllocationSizeCheck())
133MODULE_PASS("cj-ir-verifier", CJIRVerifier())133MODULE_PASS("cj-ir-verifier", CJIRVerifier())
134MODULE_PASS("cj-fill-metadata", CJFillMetadata())134MODULE_PASS("cj-fill-metadata", CJFillMetadata())
135+MODULE_PASS("cj-disable-import-lib-reflection", CJDisableImportLibReflection())
135MODULE_PASS("cj-barrier-opt", CJBarrierOpt())136MODULE_PASS("cj-barrier-opt", CJBarrierOpt())
136MODULE_PASS("cj-runtime-lowering", CJRuntimeLowering())137MODULE_PASS("cj-runtime-lowering", CJRuntimeLowering())
137MODULE_PASS("cj-specific-opt", CJSpecificOpt())138MODULE_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+ 
1628namespace {1781namespace {
1629class CJFillMetadataLegacyPass : public ModulePass {1782class CJFillMetadataLegacyPass : public ModulePass {
1630public:1783public: