//===--- SerializeSILPass.cpp ---------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "serialize-sil" #include "swift/Strings.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILFunction.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" using namespace swift; namespace { /// In place map opaque archetypes to their underlying type in a function. /// This needs to happen when a function changes from serializable to not /// serializable. class MapOpaqueArchetypes : public SILCloner { using SuperTy = SILCloner; SILBasicBlock *origEntryBlock; SILBasicBlock *clonedEntryBlock; public: friend class SILCloner; friend class SILCloner; friend class SILInstructionVisitor; MapOpaqueArchetypes(SILFunction &fun) : SuperTy(fun) { origEntryBlock = fun.getEntryBlock(); clonedEntryBlock = fun.createBasicBlock(); } bool shouldSubstOpaqueArchetypes() const { return true; } void replace(); }; } // namespace void MapOpaqueArchetypes::replace() { // Map the function arguments. SmallVector entryArgs; entryArgs.reserve(origEntryBlock->getArguments().size()); for (auto &origArg : origEntryBlock->getArguments()) { SILType mappedType = remapType(origArg->getType()); auto *NewArg = clonedEntryBlock->createFunctionArgument( mappedType, origArg->getDecl(), true); NewArg->copyFlags(cast(origArg)); entryArgs.push_back(NewArg); } getBuilder().setInsertionPoint(clonedEntryBlock); auto &fn = getBuilder().getFunction(); cloneFunctionBody(&fn, clonedEntryBlock, entryArgs, true /*replaceOriginalFunctionInPlace*/); // Insert the new entry block at the beginning. fn.moveBlockBefore(clonedEntryBlock, fn.begin()); removeUnreachableBlocks(fn); } static bool opaqueArchetypeWouldChange(TypeExpansionContext context, CanType ty) { if (!ty->hasOpaqueArchetype()) return false; return ty.findIf([=](Type type) -> bool { if (auto opaqueTy = type->getAs()) { auto opaque = opaqueTy->getDecl(); auto module = context.getContext()->getParentModule(); OpaqueSubstitutionKind subKind = ReplaceOpaqueTypesWithUnderlyingTypes::shouldPerformSubstitution( opaque, module, context.getResilienceExpansion()); return subKind != OpaqueSubstitutionKind::DontSubstitute; } return false; }); } static bool hasOpaqueArchetypeOperand(TypeExpansionContext context, SILInstruction &inst) { // Check the operands for opaque types. for (auto &opd : inst.getAllOperands()) if (opaqueArchetypeWouldChange(context, opd.get()->getType().getASTType())) return true; return false; } static bool hasOpaqueArchetypeResult(TypeExpansionContext context, SILInstruction &inst) { // Check the results for opaque types. for (SILValue res : inst.getResults()) if (opaqueArchetypeWouldChange(context, res->getType().getASTType())) return true; return false; } static bool hasOpaqueArchetype(TypeExpansionContext context, SILInstruction &inst) { // Check operands and results. if (hasOpaqueArchetypeOperand(context, inst)) return true; if (hasOpaqueArchetypeResult(context, inst)) return true; // Check substitution maps. switch (inst.getKind()) { case SILInstructionKind::AllocStackInst: case SILInstructionKind::AllocPackInst: case SILInstructionKind::AllocPackMetadataInst: case SILInstructionKind::AllocRefInst: case SILInstructionKind::AllocRefDynamicInst: case SILInstructionKind::AllocBoxInst: case SILInstructionKind::AllocExistentialBoxInst: case SILInstructionKind::IndexAddrInst: case SILInstructionKind::TailAddrInst: case SILInstructionKind::IndexRawPointerInst: case SILInstructionKind::FunctionRefInst: case SILInstructionKind::DynamicFunctionRefInst: case SILInstructionKind::PreviousDynamicFunctionRefInst: case SILInstructionKind::GlobalAddrInst: case SILInstructionKind::GlobalValueInst: case SILInstructionKind::BaseAddrForOffsetInst: case SILInstructionKind::IntegerLiteralInst: case SILInstructionKind::FloatLiteralInst: case SILInstructionKind::StringLiteralInst: case SILInstructionKind::ClassMethodInst: case SILInstructionKind::SuperMethodInst: case SILInstructionKind::ObjCMethodInst: case SILInstructionKind::ObjCSuperMethodInst: case SILInstructionKind::WitnessMethodInst: case SILInstructionKind::UpcastInst: case SILInstructionKind::AddressToPointerInst: case SILInstructionKind::PointerToAddressInst: case SILInstructionKind::UncheckedRefCastInst: case SILInstructionKind::UncheckedAddrCastInst: case SILInstructionKind::UncheckedTrivialBitCastInst: case SILInstructionKind::UncheckedBitwiseCastInst: case SILInstructionKind::UncheckedValueCastInst: case SILInstructionKind::RefToRawPointerInst: case SILInstructionKind::RawPointerToRefInst: #define LOADABLE_REF_STORAGE(Name, ...) \ case SILInstructionKind::RefTo##Name##Inst: \ case SILInstructionKind::Name##ToRefInst: #include "swift/AST/ReferenceStorage.def" #undef LOADABLE_REF_STORAGE_HELPER case SILInstructionKind::ConvertFunctionInst: case SILInstructionKind::ConvertEscapeToNoEscapeInst: case SILInstructionKind::RefToBridgeObjectInst: case SILInstructionKind::BridgeObjectToRefInst: case SILInstructionKind::BridgeObjectToWordInst: case SILInstructionKind::ThinToThickFunctionInst: case SILInstructionKind::ThickToObjCMetatypeInst: case SILInstructionKind::ObjCToThickMetatypeInst: case SILInstructionKind::ObjCMetatypeToObjectInst: case SILInstructionKind::ObjCExistentialMetatypeToObjectInst: case SILInstructionKind::UnconditionalCheckedCastInst: case SILInstructionKind::ClassifyBridgeObjectInst: case SILInstructionKind::ValueToBridgeObjectInst: case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::MarkDependenceAddrInst: case SILInstructionKind::MergeIsolationRegionInst: case SILInstructionKind::CopyBlockInst: case SILInstructionKind::CopyBlockWithoutEscapingInst: case SILInstructionKind::CopyValueInst: case SILInstructionKind::ExplicitCopyValueInst: case SILInstructionKind::MoveValueInst: case SILInstructionKind::DropDeinitInst: case SILInstructionKind::MarkUnresolvedNonCopyableValueInst: case SILInstructionKind::MarkUnresolvedReferenceBindingInst: case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst: case SILInstructionKind::MoveOnlyWrapperToCopyableValueInst: case SILInstructionKind::UnownedCopyValueInst: case SILInstructionKind::WeakCopyValueInst: #define REF_STORAGE(Name, ...) \ case SILInstructionKind::StrongCopy##Name##ValueInst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::UncheckedOwnershipConversionInst: case SILInstructionKind::IsUniqueInst: case SILInstructionKind::DestroyNotEscapedClosureInst: case SILInstructionKind::LoadInst: case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::BeginBorrowInst: case SILInstructionKind::BorrowedFromInst: case SILInstructionKind::StoreBorrowInst: case SILInstructionKind::BeginAccessInst: case SILInstructionKind::MoveOnlyWrapperToCopyableAddrInst: case SILInstructionKind::MoveOnlyWrapperToCopyableBoxInst: case SILInstructionKind::CopyableToMoveOnlyWrapperAddrInst: #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ case SILInstructionKind::Load##Name##Inst: #include "swift/AST/ReferenceStorage.def" #undef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE case SILInstructionKind::MarkUninitializedInst: case SILInstructionKind::ProjectBoxInst: case SILInstructionKind::ProjectExistentialBoxInst: case SILInstructionKind::BuiltinInst: case SILInstructionKind::MetatypeInst: case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::ObjCProtocolInst: case SILInstructionKind::ObjectInst: case SILInstructionKind::VectorInst: case SILInstructionKind::VectorBaseAddrInst: case SILInstructionKind::TupleInst: case SILInstructionKind::TupleAddrConstructorInst: case SILInstructionKind::TupleExtractInst: case SILInstructionKind::TuplePackExtractInst: case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::StructInst: case SILInstructionKind::StructExtractInst: case SILInstructionKind::StructElementAddrInst: case SILInstructionKind::RefElementAddrInst: case SILInstructionKind::RefTailAddrInst: case SILInstructionKind::EnumInst: case SILInstructionKind::UncheckedEnumDataInst: case SILInstructionKind::InitEnumDataAddrInst: case SILInstructionKind::UncheckedTakeEnumDataAddrInst: case SILInstructionKind::SelectEnumInst: case SILInstructionKind::SelectEnumAddrInst: case SILInstructionKind::InitExistentialAddrInst: case SILInstructionKind::InitExistentialValueInst: case SILInstructionKind::OpenExistentialAddrInst: case SILInstructionKind::InitExistentialRefInst: case SILInstructionKind::OpenExistentialRefInst: case SILInstructionKind::InitExistentialMetatypeInst: case SILInstructionKind::OpenExistentialMetatypeInst: case SILInstructionKind::OpenExistentialBoxInst: case SILInstructionKind::OpenExistentialValueInst: case SILInstructionKind::OpenExistentialBoxValueInst: case SILInstructionKind::ProjectBlockStorageInst: case SILInstructionKind::InitBlockStorageHeaderInst: case SILInstructionKind::KeyPathInst: case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: case SILInstructionKind::ThrowInst: case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::YieldInst: case SILInstructionKind::UnwindInst: case SILInstructionKind::BranchInst: case SILInstructionKind::CondBranchInst: case SILInstructionKind::SwitchValueInst: case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::SwitchEnumAddrInst: case SILInstructionKind::DynamicMethodBranchInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::CheckedCastAddrBranchInst: case SILInstructionKind::DeallocStackInst: case SILInstructionKind::DeallocStackRefInst: case SILInstructionKind::DeallocPackInst: case SILInstructionKind::DeallocPackMetadataInst: case SILInstructionKind::DeallocRefInst: case SILInstructionKind::DeallocPartialRefInst: case SILInstructionKind::DeallocBoxInst: case SILInstructionKind::DeallocExistentialBoxInst: case SILInstructionKind::StrongRetainInst: case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::UnmanagedRetainValueInst: case SILInstructionKind::UnmanagedReleaseValueInst: case SILInstructionKind::UnmanagedAutoreleaseValueInst: #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::StrongRetain##Name##Inst: \ case SILInstructionKind::Name##RetainInst: \ case SILInstructionKind::Name##ReleaseInst: #include "swift/AST/ReferenceStorage.def" #undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE case SILInstructionKind::RetainValueInst: case SILInstructionKind::RetainValueAddrInst: case SILInstructionKind::ReleaseValueInst: case SILInstructionKind::ReleaseValueAddrInst: case SILInstructionKind::BeginDeallocRefInst: case SILInstructionKind::EndInitLetRefInst: case SILInstructionKind::AutoreleaseValueInst: case SILInstructionKind::BindMemoryInst: case SILInstructionKind::RebindMemoryInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::DestroyValueInst: case SILInstructionKind::EndBorrowInst: case SILInstructionKind::EndAccessInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::EndUnpairedAccessInst: case SILInstructionKind::StoreInst: case SILInstructionKind::AssignInst: case SILInstructionKind::AssignOrInitInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::DebugValueInst: case SILInstructionKind::DebugStepInst: case SILInstructionKind::SpecifyTestInst: #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::CopyAddrInst: case SILInstructionKind::ExplicitCopyAddrInst: case SILInstructionKind::MarkUnresolvedMoveAddrInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::ExtendLifetimeInst: case SILInstructionKind::InjectEnumAddrInst: case SILInstructionKind::DeinitExistentialAddrInst: case SILInstructionKind::DeinitExistentialValueInst: case SILInstructionKind::UnconditionalCheckedCastAddrInst: case SILInstructionKind::UncheckedRefCastAddrInst: case SILInstructionKind::AllocGlobalInst: case SILInstructionKind::EndApplyInst: case SILInstructionKind::AbortApplyInst: case SILInstructionKind::CondFailInst: case SILInstructionKind::DestructureStructInst: case SILInstructionKind::DestructureTupleInst: case SILInstructionKind::DifferentiableFunctionInst: case SILInstructionKind::DifferentiableFunctionExtractInst: case SILInstructionKind::LinearFunctionInst: case SILInstructionKind::LinearFunctionExtractInst: case SILInstructionKind::DifferentiabilityWitnessFunctionInst: case SILInstructionKind::BeginCOWMutationInst: case SILInstructionKind::EndCOWMutationInst: case SILInstructionKind::EndCOWMutationAddrInst: case SILInstructionKind::IncrementProfilerCounterInst: case SILInstructionKind::GetAsyncContinuationInst: case SILInstructionKind::GetAsyncContinuationAddrInst: case SILInstructionKind::AwaitAsyncContinuationInst: case SILInstructionKind::HopToExecutorInst: case SILInstructionKind::ExtractExecutorInst: case SILInstructionKind::FunctionExtractIsolationInst: case SILInstructionKind::HasSymbolInst: case SILInstructionKind::PackElementGetInst: case SILInstructionKind::PackElementSetInst: case SILInstructionKind::TuplePackElementAddrInst: case SILInstructionKind::TypeValueInst: case SILInstructionKind::IgnoredUseInst: // Handle by operand and result check. break; case SILInstructionKind::PackLengthInst: // We reduce the pack shape, so it would very odd if this pack had // opaque archetypes in it, but there's no harm in doing it right. return opaqueArchetypeWouldChange(context, cast(&inst)->getPackType()); case SILInstructionKind::DynamicPackIndexInst: case SILInstructionKind::PackPackIndexInst: case SILInstructionKind::ScalarPackIndexInst: return opaqueArchetypeWouldChange(context, cast(&inst)->getIndexedPackType()); case SILInstructionKind::OpenPackElementInst: { auto open = cast(&inst); bool wouldChange = false; open->getOpenedGenericEnvironment() ->forEachPackElementBinding([&](ElementArchetypeType *elementType, PackType *packSubstitution) { if (!wouldChange) { if (opaqueArchetypeWouldChange(context, packSubstitution->getCanonicalType())) wouldChange = true; } }); return wouldChange; } case SILInstructionKind::ThunkInst: { auto subs = cast(&inst)->getSubstitutionMap(); for (auto ty : subs.getReplacementTypes()) { if (opaqueArchetypeWouldChange(context, ty->getCanonicalType())) return true; } break; } case SILInstructionKind::ApplyInst: case SILInstructionKind::PartialApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: // Check substitution map. auto apply = ApplySite(&inst); auto subs = apply.getSubstitutionMap(); for (auto ty: subs.getReplacementTypes()) { if (opaqueArchetypeWouldChange(context, ty->getCanonicalType())) return true; } break; } return false; } static bool hasOpaqueArchetypeArgument(TypeExpansionContext context, SILBasicBlock &BB) { for (auto *arg : BB.getArguments()) { if (opaqueArchetypeWouldChange(context, arg->getType().getASTType())) return true; } return false; } static bool hasAnyOpaqueArchetype(SILFunction &F) { bool foundOpaqueArchetype = false; auto context = F.getTypeExpansionContext(); for (auto &BB : F) { // Check basic block argument types. if (hasOpaqueArchetypeArgument(context, BB)) { foundOpaqueArchetype = true; break; } // Check instruction results and operands. for (auto &inst : BB) { if (hasOpaqueArchetype(context, inst)) { foundOpaqueArchetype = true; break; } } if (foundOpaqueArchetype) break; } return foundOpaqueArchetype; } void updateOpaqueArchetypes(SILFunction &F) { // Only map if there are opaque archetypes that could change. if (!hasAnyOpaqueArchetype(F)) return; MapOpaqueArchetypes(F).replace(); } /// A utility pass to serialize a SILModule at any place inside the optimization /// pipeline. class SerializeSILPass : public SILModuleTransform { /// Removes [serialized] from all functions. This allows for more /// optimizations and for a better dead function elimination. void removeSerializedFlagFromAllFunctions(SILModule &M) { for (auto &F : M) { bool wasSerialized = F.isAnySerialized(); F.setSerializedKind(IsNotSerialized); // We are removing [serialized] from the function. This will change how // opaque archetypes are lowered in SIL - they might lower to their // underlying type. Update the function's opaque archetypes. if (wasSerialized && F.isDefinition()) { updateOpaqueArchetypes(F); invalidateAnalysis(&F, SILAnalysis::InvalidationKind::FunctionBody); } // After serialization we don't need to keep @alwaysEmitIntoClient // functions alive, i.e. we don't need to treat them as public functions. if (M.isWholeModule()) { switch (F.getLinkage()) { case SILLinkage::PublicNonABI: case SILLinkage::PackageNonABI: F.setLinkage(SILLinkage::Shared); break; case SILLinkage::Public: case SILLinkage::Package: case SILLinkage::Hidden: case SILLinkage::Shared: case SILLinkage::Private: case SILLinkage::PublicExternal: case SILLinkage::PackageExternal: case SILLinkage::HiddenExternal: break; } } } for (auto &WT : M.getWitnessTables()) { WT.setSerializedKind(IsNotSerialized); } for (auto &VT : M.getVTables()) { VT->setSerializedKind(IsNotSerialized); } for (auto &Deinit : M.getMoveOnlyDeinits()) { Deinit->setSerializedKind(IsNotSerialized); } } public: SerializeSILPass() {} void run() override { auto &M = *getModule(); // Nothing to do if the module was serialized already. if (M.isSerialized()) return; LLVM_DEBUG(llvm::dbgs() << "Serializing SILModule in SerializeSILPass\n"); M.serialize(); removeSerializedFlagFromAllFunctions(M); } }; SILTransform *swift::createSerializeSILPass() { return new SerializeSILPass(); }