//===--- Local.cpp - Functions that perform local SIL transformations. ----===// // // 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 // //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/Utils/Local.h" #include "swift/SILOptimizer/Utils/CFG.h" #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/SubstitutionMap.h" #include "swift/SIL/DynamicCasts.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/BasicBlockUtils.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/IR/Intrinsics.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include using namespace swift; static llvm::cl::opt EnableExpandAll("enable-expand-all", llvm::cl::init(false)); /// Creates an increment on \p Ptr before insertion point \p InsertPt that /// creates a strong_retain if \p Ptr has reference semantics itself or a /// retain_value if \p Ptr is a non-trivial value without reference-semantics. NullablePtr swift::createIncrementBefore(SILValue Ptr, SILInstruction *InsertPt) { // If we have a trivial type, just bail, there is no work to do. if (Ptr->getType().isTrivial(InsertPt->getModule())) return nullptr; // Set up the builder we use to insert at our insertion point. SILBuilder B(InsertPt); auto Loc = RegularLocation(SourceLoc()); // If Ptr is refcounted itself, create the strong_retain and // return. if (Ptr->getType().isReferenceCounted(B.getModule())) return B.createStrongRetain(Loc, Ptr, B.getDefaultAtomicity()); // Otherwise, create the retain_value. return B.createRetainValue(Loc, Ptr, B.getDefaultAtomicity()); } /// Creates a decrement on \p Ptr before insertion point \p InsertPt that /// creates a strong_release if \p Ptr has reference semantics itself or /// a release_value if \p Ptr is a non-trivial value without reference-semantics. NullablePtr swift::createDecrementBefore(SILValue Ptr, SILInstruction *InsertPt) { if (Ptr->getType().isTrivial(InsertPt->getModule())) return nullptr; // Setup the builder we will use to insert at our insertion point. SILBuilder B(InsertPt); auto Loc = RegularLocation(SourceLoc()); // If Ptr has reference semantics itself, create a strong_release. if (Ptr->getType().isReferenceCounted(B.getModule())) return B.createStrongRelease(Loc, Ptr, B.getDefaultAtomicity()); // Otherwise create a release value. return B.createReleaseValue(Loc, Ptr, B.getDefaultAtomicity()); } /// \brief Perform a fast local check to see if the instruction is dead. /// /// This routine only examines the state of the instruction at hand. bool swift::isInstructionTriviallyDead(SILInstruction *I) { // At Onone, consider all uses, including the debug_info. // This way, debug_info is preserved at Onone. if (I->hasUsesOfAnyResult() && I->getFunction()->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization) return false; if (!onlyHaveDebugUsesOfAllResults(I) || isa(I)) return false; if (auto *BI = dyn_cast(I)) { // Although the onFastPath builtin has no side-effects we don't want to // remove it. if (BI->getBuiltinInfo().ID == BuiltinValueKind::OnFastPath) return false; return !BI->mayHaveSideEffects(); } // condfail instructions that obviously can't fail are dead. if (auto *CFI = dyn_cast(I)) if (auto *ILI = dyn_cast(CFI->getOperand())) if (!ILI->getValue()) return true; // mark_uninitialized is never dead. if (isa(I)) return false; if (isa(I)) return false; if (isa(I) || isa(I)) return false; // These invalidate enums so "write" memory, but that is not an essential // operation so we can remove these if they are trivially dead. if (isa(I)) return true; if (!I->mayHaveSideEffects()) return true; return false; } /// \brief Return true if this is a release instruction and the released value /// is a part of a guaranteed parameter. bool swift::isIntermediateRelease(SILInstruction *I, EpilogueARCFunctionInfo *EAFI) { // Check whether this is a release instruction. if (!isa(I) && !isa(I)) return false; // OK. we have a release instruction. // Check whether this is a release on part of a guaranteed function argument. SILValue Op = stripValueProjections(I->getOperand(0)); auto *Arg = dyn_cast(Op); if (!Arg) return false; // This is a release on a guaranteed parameter. Its not the final release. if (Arg->hasConvention(SILArgumentConvention::Direct_Guaranteed)) return true; // This is a release on an owned parameter and its not the epilogue release. // Its not the final release. auto Rel = EAFI->computeEpilogueARCInstructions( EpilogueARCContext::EpilogueARCKind::Release, Arg); if (Rel.size() && !Rel.count(I)) return true; // Failed to prove anything. return false; } namespace { using CallbackTy = std::function; } // end anonymous namespace void swift:: recursivelyDeleteTriviallyDeadInstructions(ArrayRef IA, bool Force, CallbackTy Callback) { // Delete these instruction and others that become dead after it's deleted. llvm::SmallPtrSet DeadInsts; for (auto I : IA) { // If the instruction is not dead and force is false, do nothing. if (Force || isInstructionTriviallyDead(I)) DeadInsts.insert(I); } llvm::SmallPtrSet NextInsts; while (!DeadInsts.empty()) { for (auto I : DeadInsts) { // Call the callback before we mutate the to be deleted instruction in any // way. Callback(I); // Check if any of the operands will become dead as well. MutableArrayRef Ops = I->getAllOperands(); for (Operand &Op : Ops) { SILValue OpVal = Op.get(); if (!OpVal) continue; // Remove the reference from the instruction being deleted to this // operand. Op.drop(); // If the operand is an instruction that is only used by the instruction // being deleted, delete it. if (auto *OpValInst = OpVal->getDefiningInstruction()) if (!DeadInsts.count(OpValInst) && isInstructionTriviallyDead(OpValInst)) NextInsts.insert(OpValInst); } // If we have a function ref inst, we need to especially drop its function // argument so that it gets a proper ref decrement. auto *FRI = dyn_cast(I); if (FRI && FRI->getReferencedFunction()) FRI->dropReferencedFunction(); } for (auto I : DeadInsts) { // This will remove this instruction and all its uses. eraseFromParentWithDebugInsts(I); } NextInsts.swap(DeadInsts); NextInsts.clear(); } } /// \brief If the given instruction is dead, delete it along with its dead /// operands. /// /// \param I The instruction to be deleted. /// \param Force If Force is set, don't check if the top level instruction is /// considered dead - delete it regardless. void swift::recursivelyDeleteTriviallyDeadInstructions(SILInstruction *I, bool Force, CallbackTy Callback) { ArrayRef AI = ArrayRef(I); recursivelyDeleteTriviallyDeadInstructions(AI, Force, Callback); } void swift::eraseUsesOfInstruction(SILInstruction *Inst, CallbackTy Callback) { for (auto result : Inst->getResults()) { while (!result->use_empty()) { auto UI = result->use_begin(); auto *User = UI->getUser(); assert(User && "User should never be NULL!"); // If the instruction itself has any uses, recursively zap them so that // nothing uses this instruction. eraseUsesOfInstruction(User, Callback); // Walk through the operand list and delete any random instructions that // will become trivially dead when this instruction is removed. for (auto &Op : User->getAllOperands()) { if (auto *OpI = Op.get()->getDefiningInstruction()) { // Don't recursively delete the instruction we're working on. // FIXME: what if we're being recursively invoked? if (OpI != Inst) { Op.drop(); recursivelyDeleteTriviallyDeadInstructions(OpI, false, Callback); } } } Callback(User); User->eraseFromParent(); } } } void swift:: collectUsesOfValue(SILValue V, llvm::SmallPtrSetImpl &Insts) { for (auto UI = V->use_begin(), E = V->use_end(); UI != E; UI++) { auto *User = UI->getUser(); // Instruction has been processed. if (!Insts.insert(User).second) continue; // Collect the users of this instruction. for (auto result : User->getResults()) collectUsesOfValue(result, Insts); } } void swift::eraseUsesOfValue(SILValue V) { llvm::SmallPtrSet Insts; // Collect the uses. collectUsesOfValue(V, Insts); // Erase the uses, we can have instructions that become dead because // of the removal of these instructions, leave to DCE to cleanup. // Its not safe to do recursively delete here as some of the SILInstruction // maybe tracked by this set. for (auto I : Insts) { I->replaceAllUsesOfAllResultsWithUndef(); I->eraseFromParent(); } } // Devirtualization of functions with covariant return types produces // a result that is not an apply, but takes an apply as an // argument. Attempt to dig the apply out from this result. FullApplySite swift::findApplyFromDevirtualizedResult(SILValue V) { if (auto Apply = FullApplySite::isa(V)) return Apply; if (isa(V) || isa(V) || isa(V)) return findApplyFromDevirtualizedResult( cast(V)->getOperand(0)); return FullApplySite(); } SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { if (PAI->getNumArguments() != 1) return SILValue(); auto *Fun = PAI->getReferencedFunction(); if (!Fun) return SILValue(); // Make sure we have a reabstraction thunk. if (Fun->isThunk() != IsReabstractionThunk) return SILValue(); // The argument should be a closure. auto Arg = PAI->getArgument(0); if (!Arg->getType().is() || !Arg->getType().isReferenceCounted(PAI->getFunction()->getModule())) return SILValue(); return Arg; } // Replace a dead apply with a new instruction that computes the same // value, and delete the old apply. void swift::replaceDeadApply(ApplySite Old, ValueBase *New) { auto *OldApply = Old.getInstruction(); if (!isa(OldApply)) cast(OldApply)->replaceAllUsesWith(New); recursivelyDeleteTriviallyDeadInstructions(OldApply, true); } bool swift::hasArchetypes(SubstitutionList Subs) { // Check whether any of the substitutions are dependent. return llvm::any_of(Subs, [](const Substitution &S) { return S.getReplacement()->hasArchetype(); }); } bool swift::mayBindDynamicSelf(SILFunction *F) { if (!F->hasSelfMetadataParam()) return false; SILValue MDArg = F->getSelfMetadataArgument(); for (Operand *MDUse : F->getSelfMetadataArgument()->getUses()) { SILInstruction *MDUser = MDUse->getUser(); for (Operand &TypeDepOp : MDUser->getTypeDependentOperands()) { if (TypeDepOp.get() == MDArg) return true; } } return false; } /// Find a new position for an ApplyInst's FuncRef so that it dominates its /// use. Not that FunctionRefInsts may be shared by multiple ApplyInsts. void swift::placeFuncRef(ApplyInst *AI, DominanceInfo *DT) { FunctionRefInst *FuncRef = cast(AI->getCallee()); SILBasicBlock *DomBB = DT->findNearestCommonDominator(AI->getParent(), FuncRef->getParent()); if (DomBB == AI->getParent() && DomBB != FuncRef->getParent()) // Prefer to place the FuncRef immediately before the call. Since we're // moving FuncRef up, this must be the only call to it in the block. FuncRef->moveBefore(AI); else // Otherwise, conservatively stick it at the beginning of the block. FuncRef->moveBefore(&*DomBB->begin()); } /// \brief Add an argument, \p val, to the branch-edge that is pointing into /// block \p Dest. Return a new instruction and do not erase the old /// instruction. TermInst *swift::addArgumentToBranch(SILValue Val, SILBasicBlock *Dest, TermInst *Branch) { SILBuilderWithScope Builder(Branch); if (auto *CBI = dyn_cast(Branch)) { SmallVector TrueArgs; SmallVector FalseArgs; for (auto A : CBI->getTrueArgs()) TrueArgs.push_back(A); for (auto A : CBI->getFalseArgs()) FalseArgs.push_back(A); if (Dest == CBI->getTrueBB()) { TrueArgs.push_back(Val); assert(TrueArgs.size() == Dest->getNumArguments()); } else { FalseArgs.push_back(Val); assert(FalseArgs.size() == Dest->getNumArguments()); } return Builder.createCondBranch(CBI->getLoc(), CBI->getCondition(), CBI->getTrueBB(), TrueArgs, CBI->getFalseBB(), FalseArgs, CBI->getTrueBBCount(), CBI->getFalseBBCount()); } if (auto *BI = dyn_cast(Branch)) { SmallVector Args; for (auto A : BI->getArgs()) Args.push_back(A); Args.push_back(Val); assert(Args.size() == Dest->getNumArguments()); return Builder.createBranch(BI->getLoc(), BI->getDestBB(), Args); } llvm_unreachable("unsupported terminator"); } SILLinkage swift::getSpecializedLinkage(SILFunction *F, SILLinkage L) { if (hasPrivateVisibility(L) && !F->isSerialized()) { // Specializations of private symbols should remain so, unless // they were serialized, which can only happen when specializing // definitions from a standard library built with -sil-serialize-all. return SILLinkage::Private; } return SILLinkage::Shared; } /// Remove all instructions in the body of \p BB in safe manner by using /// undef. void swift::clearBlockBody(SILBasicBlock *BB) { // Instructions in the dead block may be used by other dead blocks. Replace // any uses of them with undef values. while (!BB->empty()) { // Grab the last instruction in the BB. auto *Inst = &BB->back(); // Replace any still-remaining uses with undef values and erase. Inst->replaceAllUsesOfAllResultsWithUndef(); Inst->eraseFromParent(); } } // Handle the mechanical aspects of removing an unreachable block. void swift::removeDeadBlock(SILBasicBlock *BB) { // Clear the body of BB. clearBlockBody(BB); // Now that the BB is empty, eliminate it. BB->eraseFromParent(); } /// Cast a value into the expected, ABI compatible type if necessary. /// This may happen e.g. when: /// - a type of the return value is a subclass of the expected return type. /// - actual return type and expected return type differ in optionality. /// - both types are tuple-types and some of the elements need to be casted. /// /// If CheckOnly flag is set, then this function only checks if the /// required casting is possible. If it is not possible, then None /// is returned. /// /// If CheckOnly is not set, then a casting code is generated and the final /// casted value is returned. /// /// NOTE: We intentionally combine the checking of the cast's handling possibility /// and the transformation performing the cast in the same function, to avoid /// any divergence between the check and the implementation in the future. /// /// NOTE: The implementation of this function is very closely related to the /// rules checked by SILVerifier::requireABICompatibleFunctionTypes. SILValue swift::castValueToABICompatibleType(SILBuilder *B, SILLocation Loc, SILValue Value, SILType SrcTy, SILType DestTy) { // No cast is required if types are the same. if (SrcTy == DestTy) return Value; assert(SrcTy.isAddress() == DestTy.isAddress() && "Addresses aren't compatible with values"); if (SrcTy.isAddress() && DestTy.isAddress()) { // Cast between two addresses and that's it. return B->createUncheckedAddrCast(Loc, Value, DestTy); } // If both types are classes and dest is the superclass of src, // simply perform an upcast. if (DestTy.isExactSuperclassOf(SrcTy)) { return B->createUpcast(Loc, Value, DestTy); } if (SrcTy.isHeapObjectReferenceType() && DestTy.isHeapObjectReferenceType()) { return B->createUncheckedRefCast(Loc, Value, DestTy); } if (auto mt1 = SrcTy.getAs()) { if (auto mt2 = DestTy.getAs()) { if (mt1->getRepresentation() == mt2->getRepresentation()) { // If B.Type needs to be casted to A.Type and // A is a superclass of B, then it can be done by means // of a simple upcast. if (mt2.getInstanceType()->isExactSuperclassOf( mt1.getInstanceType())) { return B->createUpcast(Loc, Value, DestTy); } // Cast between two metatypes and that's it. return B->createUncheckedBitCast(Loc, Value, DestTy); } } } // Check if src and dest types are optional. auto OptionalSrcTy = SrcTy.getAnyOptionalObjectType(); auto OptionalDestTy = DestTy.getAnyOptionalObjectType(); // Both types are optional. if (OptionalDestTy && OptionalSrcTy) { // If both wrapped types are classes and dest is the superclass of src, // simply perform an upcast. if (OptionalDestTy.isExactSuperclassOf(OptionalSrcTy)) { // Insert upcast. return B->createUpcast(Loc, Value, DestTy); } // Unwrap the original optional value. auto *SomeDecl = B->getASTContext().getOptionalSomeDecl(); auto *NoneBB = B->getFunction().createBasicBlock(); auto *SomeBB = B->getFunction().createBasicBlock(); auto *CurBB = B->getInsertionPoint()->getParent(); auto *ContBB = CurBB->split(B->getInsertionPoint()); ContBB->createPHIArgument(DestTy, ValueOwnershipKind::Owned); SmallVector, 1> CaseBBs; CaseBBs.push_back(std::make_pair(SomeDecl, SomeBB)); B->setInsertionPoint(CurBB); B->createSwitchEnum(Loc, Value, NoneBB, CaseBBs); // Handle the Some case. B->setInsertionPoint(SomeBB); SILValue UnwrappedValue = B->createUncheckedEnumData(Loc, Value, SomeDecl); // Cast the unwrapped value. auto CastedUnwrappedValue = castValueToABICompatibleType(B, Loc, UnwrappedValue, OptionalSrcTy, OptionalDestTy); // Wrap into optional. auto CastedValue = B->createOptionalSome(Loc, CastedUnwrappedValue, DestTy); B->createBranch(Loc, ContBB, {CastedValue}); // Handle the None case. B->setInsertionPoint(NoneBB); CastedValue = B->createOptionalNone(Loc, DestTy); B->createBranch(Loc, ContBB, {CastedValue}); B->setInsertionPoint(ContBB->begin()); return ContBB->getArgument(0); } // Src is not optional, but dest is optional. if (!OptionalSrcTy && OptionalDestTy) { auto OptionalSrcCanTy = OptionalType::get(SrcTy.getSwiftRValueType()) ->getCanonicalType(); auto LoweredOptionalSrcType = SILType::getPrimitiveObjectType( OptionalSrcCanTy); // Wrap the source value into an optional first. SILValue WrappedValue = B->createOptionalSome(Loc, Value, LoweredOptionalSrcType); // Cast the wrapped value. return castValueToABICompatibleType(B, Loc, WrappedValue, WrappedValue->getType(), DestTy); } // Handle tuple types. // Extract elements, cast each of them, create a new tuple. if (auto SrcTupleTy = SrcTy.getAs()) { SmallVector ExpectedTuple; for (unsigned i = 0, e = SrcTupleTy->getNumElements(); i < e; i++) { SILValue Element = B->createTupleExtract(Loc, Value, i); // Cast the value if necessary. Element = castValueToABICompatibleType(B, Loc, Element, SrcTy.getTupleElementType(i), DestTy.getTupleElementType(i)); ExpectedTuple.push_back(Element); } return B->createTuple(Loc, DestTy, ExpectedTuple); } // Function types are interchangeable if they're also ABI-compatible. if (SrcTy.is()) { if (DestTy.is()) { // Insert convert_function. return B->createConvertFunction(Loc, Value, DestTy); } } llvm::errs() << "Source type: " << SrcTy << "\n"; llvm::errs() << "Destination type: " << DestTy << "\n"; llvm_unreachable("Unknown combination of types for casting"); } ProjectBoxInst *swift::getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index){ SILBasicBlock::iterator Iter(ABI); Iter++; assert(Iter != ABI->getParent()->end() && "alloc_box cannot be the last instruction of a block"); SILInstruction *NextInst = &*Iter; if (auto *PBI = dyn_cast(NextInst)) { if (PBI->getOperand() == ABI && PBI->getFieldIndex() == Index) return PBI; } SILBuilder B(NextInst); return B.createProjectBox(ABI->getLoc(), ABI, Index); } //===----------------------------------------------------------------------===// // String Concatenation Optimizer //===----------------------------------------------------------------------===// namespace { /// This is a helper class that performs optimization of string literals /// concatenation. class StringConcatenationOptimizer { /// Apply instruction being optimized. ApplyInst *AI; /// Builder to be used for creation of new instructions. SILBuilder &Builder; /// Left string literal operand of a string concatenation. StringLiteralInst *SLILeft = nullptr; /// Right string literal operand of a string concatenation. StringLiteralInst *SLIRight = nullptr; /// Function used to construct the left string literal. FunctionRefInst *FRILeft = nullptr; /// Function used to construct the right string literal. FunctionRefInst *FRIRight = nullptr; /// Apply instructions used to construct left string literal. ApplyInst *AILeft = nullptr; /// Apply instructions used to construct right string literal. ApplyInst *AIRight = nullptr; /// String literal conversion function to be used. FunctionRefInst *FRIConvertFromBuiltin = nullptr; /// Result type of a function producing the concatenated string literal. SILValue FuncResultType; /// Internal helper methods bool extractStringConcatOperands(); void adjustEncodings(); APInt getConcatenatedLength(); bool isAscii() const; public: StringConcatenationOptimizer(ApplyInst *AI, SILBuilder &Builder) : AI(AI), Builder(Builder) {} /// Tries to optimize a given apply instruction if it is a /// concatenation of string literals. /// /// Returns a new instruction if optimization was possible. SingleValueInstruction *optimize(); }; } // end anonymous namespace /// Checks operands of a string concatenation operation to see if /// optimization is applicable. /// /// Returns false if optimization is not possible. /// Returns true and initializes internal fields if optimization is possible. bool StringConcatenationOptimizer::extractStringConcatOperands() { auto *Fn = AI->getReferencedFunction(); if (!Fn) return false; if (AI->getNumArguments() != 3 || !Fn->hasSemanticsAttr("string.concat")) return false; assert(Fn->getRepresentation() == SILFunctionTypeRepresentation::Method); // Left and right operands of a string concatenation operation. AILeft = dyn_cast(AI->getOperand(1)); AIRight = dyn_cast(AI->getOperand(2)); if (!AILeft || !AIRight) return false; FRILeft = dyn_cast(AILeft->getCallee()); FRIRight = dyn_cast(AIRight->getCallee()); if (!FRILeft || !FRIRight) return false; auto *FRILeftFun = FRILeft->getReferencedFunction(); auto *FRIRightFun = FRIRight->getReferencedFunction(); if (FRILeftFun->getEffectsKind() >= EffectsKind::ReadWrite || FRIRightFun->getEffectsKind() >= EffectsKind::ReadWrite) return false; if (!FRILeftFun->hasSemanticsAttrs() || !FRIRightFun->hasSemanticsAttrs()) return false; auto AILeftOperandsNum = AILeft->getNumOperands(); auto AIRightOperandsNum = AIRight->getNumOperands(); // makeUTF16 should have following parameters: // (start: RawPointer, utf16CodeUnitCount: Word) // makeUTF8 should have following parameters: // (start: RawPointer, utf8CodeUnitCount: Word, isASCII: Int1) if (!((FRILeftFun->hasSemanticsAttr("string.makeUTF16") && AILeftOperandsNum == 4) || (FRILeftFun->hasSemanticsAttr("string.makeUTF8") && AILeftOperandsNum == 5) || (FRIRightFun->hasSemanticsAttr("string.makeUTF16") && AIRightOperandsNum == 4) || (FRIRightFun->hasSemanticsAttr("string.makeUTF8") && AIRightOperandsNum == 5))) return false; assert(FRILeftFun->getRepresentation() == SILFunctionTypeRepresentation::Method); assert(FRIRightFun->getRepresentation() == SILFunctionTypeRepresentation::Method); SLILeft = dyn_cast(AILeft->getOperand(1)); SLIRight = dyn_cast(AIRight->getOperand(1)); if (!SLILeft || !SLIRight) return false; // Only UTF-8 and UTF-16 encoded string literals are supported by this // optimization. if (SLILeft->getEncoding() != StringLiteralInst::Encoding::UTF8 && SLILeft->getEncoding() != StringLiteralInst::Encoding::UTF16) return false; if (SLIRight->getEncoding() != StringLiteralInst::Encoding::UTF8 && SLIRight->getEncoding() != StringLiteralInst::Encoding::UTF16) return false; return true; } /// Ensures that both string literals to be concatenated use the same /// UTF encoding. Converts UTF-8 into UTF-16 if required. void StringConcatenationOptimizer::adjustEncodings() { if (SLILeft->getEncoding() == SLIRight->getEncoding()) { FRIConvertFromBuiltin = FRILeft; if (SLILeft->getEncoding() == StringLiteralInst::Encoding::UTF8) { FuncResultType = AILeft->getOperand(4); } else { FuncResultType = AILeft->getOperand(3); } return; } Builder.setCurrentDebugScope(AI->getDebugScope()); // If one of the string literals is UTF8 and another one is UTF16, // convert the UTF8-encoded string literal into UTF16-encoding first. if (SLILeft->getEncoding() == StringLiteralInst::Encoding::UTF8 && SLIRight->getEncoding() == StringLiteralInst::Encoding::UTF16) { FuncResultType = AIRight->getOperand(3); FRIConvertFromBuiltin = FRIRight; // Convert UTF8 representation into UTF16. SLILeft = Builder.createStringLiteral(AI->getLoc(), SLILeft->getValue(), StringLiteralInst::Encoding::UTF16); } if (SLIRight->getEncoding() == StringLiteralInst::Encoding::UTF8 && SLILeft->getEncoding() == StringLiteralInst::Encoding::UTF16) { FuncResultType = AILeft->getOperand(3); FRIConvertFromBuiltin = FRILeft; // Convert UTF8 representation into UTF16. SLIRight = Builder.createStringLiteral(AI->getLoc(), SLIRight->getValue(), StringLiteralInst::Encoding::UTF16); } // It should be impossible to have two operands with different // encodings at this point. assert(SLILeft->getEncoding() == SLIRight->getEncoding() && "Both operands of string concatenation should have the same encoding"); } /// Computes the length of a concatenated string literal. APInt StringConcatenationOptimizer::getConcatenatedLength() { // Real length of string literals computed based on its contents. // Length is in code units. auto SLILenLeft = SLILeft->getCodeUnitCount(); (void) SLILenLeft; auto SLILenRight = SLIRight->getCodeUnitCount(); (void) SLILenRight; // Length of string literals as reported by string.make functions. auto *LenLeft = dyn_cast(AILeft->getOperand(2)); auto *LenRight = dyn_cast(AIRight->getOperand(2)); // Real and reported length should be the same. assert(SLILenLeft == LenLeft->getValue() && "Size of string literal in @_semantics(string.make) is wrong"); assert(SLILenRight == LenRight->getValue() && "Size of string literal in @_semantics(string.make) is wrong"); // Compute length of the concatenated literal. return LenLeft->getValue() + LenRight->getValue(); } /// Computes the isAscii flag of a concatenated UTF8-encoded string literal. bool StringConcatenationOptimizer::isAscii() const{ // Add the isASCII argument in case of UTF8. // IsASCII is true only if IsASCII of both literals is true. auto *AsciiLeft = dyn_cast(AILeft->getOperand(3)); auto *AsciiRight = dyn_cast(AIRight->getOperand(3)); auto IsAsciiLeft = AsciiLeft->getValue() == 1; auto IsAsciiRight = AsciiRight->getValue() == 1; return IsAsciiLeft && IsAsciiRight; } SingleValueInstruction *StringConcatenationOptimizer::optimize() { // Bail out if string literals concatenation optimization is // not possible. if (!extractStringConcatOperands()) return nullptr; // Perform string literal encodings adjustments if needed. adjustEncodings(); // Arguments of the new StringLiteralInst to be created. SmallVector Arguments; // Encoding to be used for the concatenated string literal. auto Encoding = SLILeft->getEncoding(); // Create a concatenated string literal. Builder.setCurrentDebugScope(AI->getDebugScope()); auto LV = SLILeft->getValue(); auto RV = SLIRight->getValue(); auto *NewSLI = Builder.createStringLiteral(AI->getLoc(), LV + Twine(RV), Encoding); Arguments.push_back(NewSLI); // Length of the concatenated literal according to its encoding. auto *Len = Builder.createIntegerLiteral( AI->getLoc(), AILeft->getOperand(2)->getType(), getConcatenatedLength()); Arguments.push_back(Len); // isAscii flag for UTF8-encoded string literals. if (Encoding == StringLiteralInst::Encoding::UTF8) { bool IsAscii = isAscii(); auto ILType = AILeft->getOperand(3)->getType(); auto *Ascii = Builder.createIntegerLiteral(AI->getLoc(), ILType, intmax_t(IsAscii)); Arguments.push_back(Ascii); } // Type. Arguments.push_back(FuncResultType); return Builder.createApply(AI->getLoc(), FRIConvertFromBuiltin, SubstitutionList(), Arguments, false); } /// Top level entry point SingleValueInstruction * swift::tryToConcatenateStrings(ApplyInst *AI, SILBuilder &B) { return StringConcatenationOptimizer(AI, B).optimize(); } //===----------------------------------------------------------------------===// // Closure Deletion //===----------------------------------------------------------------------===// static bool useDoesNotKeepClosureAlive(const SILInstruction *I) { switch (I->getKind()) { case SILInstructionKind::StrongRetainInst: case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::CopyValueInst: case SILInstructionKind::DestroyValueInst: case SILInstructionKind::RetainValueInst: case SILInstructionKind::ReleaseValueInst: case SILInstructionKind::DebugValueInst: return true; default: return false; } } static bool useHasTransitiveOwnership(const SILInstruction *I) { // convert_function is used to add the @noescape attribute. It does not change // ownership of the function value. return isa(I); } static SILValue createLifetimeExtendedAllocStack( SILBuilder &Builder, SILLocation Loc, SILValue Arg, ArrayRef ExitingBlocks, InstModCallbacks Callbacks) { AllocStackInst *ASI = nullptr; { // Save our insert point and create a new alloc_stack in the initial BB and // dealloc_stack in all exit blocks. auto *OldInsertPt = &*Builder.getInsertionPoint(); Builder.setInsertionPoint(Builder.getFunction().begin()->begin()); ASI = Builder.createAllocStack(Loc, Arg->getType()); Callbacks.CreatedNewInst(ASI); for (auto *BB : ExitingBlocks) { Builder.setInsertionPoint(BB->getTerminator()); Callbacks.CreatedNewInst(Builder.createDeallocStack(Loc, ASI)); } Builder.setInsertionPoint(OldInsertPt); } assert(ASI != nullptr); // Then perform a copy_addr [take] [init] right after the partial_apply from // the original address argument to the new alloc_stack that we have // created. Callbacks.CreatedNewInst( Builder.createCopyAddr(Loc, Arg, ASI, IsTake, IsInitialization)); // Return the new alloc_stack inst that has the appropriate live range to // destroy said values. return ASI; } static bool shouldDestroyPartialApplyCapturedArg(SILValue Arg, SILParameterInfo PInfo, SILModule &M) { // If we have a non-trivial type and the argument is passed in @inout, we do // not need to destroy it here. This is something that is implicit in the // partial_apply design that will be revisited when partial_apply is // redesigned. if (PInfo.isIndirectMutating()) return false; // If we have a trivial type, we do not need to put in any extra releases. if (Arg->getType().isTrivial(M)) return false; // We handle all other cases. return true; } // *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is // printed with the convention of the closed over function upon it, all // non-inout arguments to a partial_apply are passed at +1. This includes // arguments that will eventually be passed as guaranteed or in_guaranteed to // the closed over function. This is because the partial apply is building up a // boxed aggregate to send off to the closed over function. Of course when you // call the function, the proper conventions will be used. void swift::releasePartialApplyCapturedArg(SILBuilder &Builder, SILLocation Loc, SILValue Arg, SILParameterInfo PInfo, InstModCallbacks Callbacks) { if (!shouldDestroyPartialApplyCapturedArg(Arg, PInfo, Builder.getModule())) return; // Otherwise, we need to destroy the argument. If we have an address, we // insert a destroy_addr and return. Any live range issues must have been // dealt with by our caller. if (Arg->getType().isAddress()) { // Then emit the destroy_addr for this arg SILInstruction *NewInst = Builder.emitDestroyAddrAndFold(Loc, Arg); Callbacks.CreatedNewInst(NewInst); return; } // Otherwise, we have an object. We emit the most optimized form of release // possible for that value. // If we have qualified ownership, we should just emit a destroy value. if (Arg->getFunction()->hasQualifiedOwnership()) { Callbacks.CreatedNewInst(Builder.createDestroyValue(Loc, Arg)); return; } if (Arg->getType().hasReferenceSemantics()) { auto U = Builder.emitStrongRelease(Loc, Arg); if (U.isNull()) return; if (auto *SRI = U.dyn_cast()) { Callbacks.DeleteInst(SRI); return; } Callbacks.CreatedNewInst(U.get()); return; } auto U = Builder.emitReleaseValue(Loc, Arg); if (U.isNull()) return; if (auto *RVI = U.dyn_cast()) { Callbacks.DeleteInst(RVI); return; } Callbacks.CreatedNewInst(U.get()); } /// For each captured argument of PAI, decrement the ref count of the captured /// argument as appropriate at each of the post dominated release locations /// found by Tracker. static bool releaseCapturedArgsOfDeadPartialApply(PartialApplyInst *PAI, ReleaseTracker &Tracker, InstModCallbacks Callbacks) { SILBuilderWithScope Builder(PAI); SILLocation Loc = PAI->getLoc(); CanSILFunctionType PAITy = PAI->getCallee()->getType().getAs(); ArrayRef Params = PAITy->getParameters(); llvm::SmallVector Args; for (SILValue v : PAI->getArguments()) { // If any of our arguments contain open existentials, bail. We do not // support this for now so that we can avoid having to re-order stack // locations (a larger change). if (v->getType().hasOpenedExistential()) return false; Args.emplace_back(v); } unsigned Delta = Params.size() - Args.size(); assert(Delta <= Params.size() && "Error, more Args to partial apply than " "params in its interface."); Params = Params.drop_front(Delta); llvm::SmallVector ExitingBlocks; PAI->getFunction()->findExitingBlocks(ExitingBlocks); // Go through our argument list and create new alloc_stacks for each // non-trivial address value. This ensures that the memory location that we // are cleaning up has the same live range as the partial_apply. Otherwise, we // may be inserting destroy_addr of alloc_stack that have already been passed // to a dealloc_stack. for (unsigned i : reversed(indices(Args))) { SILValue Arg = Args[i]; SILParameterInfo PInfo = Params[i]; // If we are not going to destroy this partial_apply, continue. if (!shouldDestroyPartialApplyCapturedArg(Arg, PInfo, Builder.getModule())) continue; // If we have an object, we will not have live range issues, just continue. if (Arg->getType().isObject()) continue; // Now that we know that we have a non-argument address, perform a take-init // of Arg into a lifetime extended alloc_stack Args[i] = createLifetimeExtendedAllocStack(Builder, Loc, Arg, ExitingBlocks, Callbacks); } // Emit a destroy for each captured closure argument at each final release // point. for (auto *FinalRelease : Tracker.getFinalReleases()) { Builder.setInsertionPoint(FinalRelease); for (unsigned i : indices(Args)) { SILValue Arg = Args[i]; SILParameterInfo Param = Params[i]; releasePartialApplyCapturedArg(Builder, Loc, Arg, Param, Callbacks); } } return true; } /// TODO: Generalize this to general objects. bool swift::tryDeleteDeadClosure(SingleValueInstruction *Closure, InstModCallbacks Callbacks) { // We currently only handle locally identified values that do not escape. We // also assume that the partial apply does not capture any addresses. if (!isa(Closure) && !isa(Closure)) return false; // We only accept a user if it is an ARC object that can be removed if the // object is dead. This should be expanded in the future. This also ensures // that we are locally identified and non-escaping since we only allow for // specific ARC users. ReleaseTracker Tracker(useDoesNotKeepClosureAlive, useHasTransitiveOwnership); // Find the ARC Users and the final retain, release. if (!getFinalReleasesForValue(SILValue(Closure), Tracker)) return false; // If we have a partial_apply, release each captured argument at each one of // the final release locations of the partial apply. if (auto *PAI = dyn_cast(Closure)) { // If we can not decrement the ref counts of the dead partial apply for any // reason, bail. if (!releaseCapturedArgsOfDeadPartialApply(PAI, Tracker, Callbacks)) return false; } // Then delete all user instructions in reverse so that leaf uses are deleted // first. for (auto *User : reverse(Tracker.getTrackedUsers())) { assert(User->getResults().empty() || useHasTransitiveOwnership(User) && "We expect only ARC operations without " "results. This is true b/c of " "isARCOperationRemovableIfObjectIsDead"); Callbacks.DeleteInst(User); } // Finally delete the closure. Callbacks.DeleteInst(Closure); return true; } //===----------------------------------------------------------------------===// // Value Lifetime //===----------------------------------------------------------------------===// void ValueLifetimeAnalysis::propagateLiveness() { assert(LiveBlocks.empty() && "frontier computed twice"); auto DefBB = DefValue->getParentBlock(); llvm::SmallVector Worklist; int NumUsersBeforeDef = 0; // Find the initial set of blocks where the value is live, because // it is used in those blocks. for (SILInstruction *User : UserSet) { SILBasicBlock *UserBlock = User->getParent(); if (LiveBlocks.insert(UserBlock)) Worklist.push_back(UserBlock); // A user in the DefBB could potentially be located before the DefValue. if (UserBlock == DefBB) NumUsersBeforeDef++; } // Don't count any users in the DefBB which are actually located _after_ // the DefValue. auto InstIter = DefValue->getIterator(); while (NumUsersBeforeDef > 0 && ++InstIter != DefBB->end()) { if (UserSet.count(&*InstIter)) NumUsersBeforeDef--; } // Now propagate liveness backwards until we hit the block that defines the // value. while (!Worklist.empty()) { auto *BB = Worklist.pop_back_val(); // Don't go beyond the definition. if (BB == DefBB && NumUsersBeforeDef == 0) continue; for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) { // If it's already in the set, then we've already queued and/or // processed the predecessors. if (LiveBlocks.insert(Pred)) Worklist.push_back(Pred); } } } SILInstruction *ValueLifetimeAnalysis:: findLastUserInBlock(SILBasicBlock *BB) { // Walk backwards in BB looking for last use of the value. for (auto II = BB->rbegin(); II != BB->rend(); ++II) { assert(DefValue != &*II && "Found def before finding use!"); if (UserSet.count(&*II)) return &*II; } llvm_unreachable("Expected to find use of value in block!"); } bool ValueLifetimeAnalysis::computeFrontier(Frontier &Fr, Mode mode, DeadEndBlocks *DEBlocks) { assert(!isAliveAtBeginOfBlock(DefValue->getFunction()->getEntryBlock()) && "Can't compute frontier for def which does not dominate all uses"); bool NoCriticalEdges = true; // Exit-blocks from the lifetime region. The value is live at the end of // a predecessor block but not in the frontier block itself. llvm::SmallSetVector FrontierBlocks; // Blocks where the value is live at the end of the block and which have // a frontier block as successor. llvm::SmallSetVector LiveOutBlocks; /// The lifetime ends if we have a live block and a not-live successor. for (SILBasicBlock *BB : LiveBlocks) { if (DEBlocks && DEBlocks->isDeadEnd(BB)) continue; bool LiveInSucc = false; bool DeadInSucc = false; for (const SILSuccessor &Succ : BB->getSuccessors()) { if (isAliveAtBeginOfBlock(Succ)) { LiveInSucc = true; } else if (!DEBlocks || !DEBlocks->isDeadEnd(Succ)) { DeadInSucc = true; } } if (!LiveInSucc) { // The value is not live in any of the successor blocks. This means the // block contains a last use of the value. The next instruction after // the last use is part of the frontier. SILInstruction *LastUser = findLastUserInBlock(BB); if (!isa(LastUser)) { Fr.push_back(&*std::next(LastUser->getIterator())); continue; } // In case the last user is a TermInst we add all successor blocks to the // frontier (see below). assert(DeadInSucc && "The final using TermInst must have successors"); } if (DeadInSucc) { if (mode == UsersMustPostDomDef) return false; // The value is not live in some of the successor blocks. LiveOutBlocks.insert(BB); for (const SILSuccessor &Succ : BB->getSuccessors()) { if (!isAliveAtBeginOfBlock(Succ)) { // It's an "exit" edge from the lifetime region. FrontierBlocks.insert(Succ); } } } } // Handle "exit" edges from the lifetime region. llvm::SmallPtrSet UnhandledFrontierBlocks; for (SILBasicBlock *FrontierBB: FrontierBlocks) { assert(mode != UsersMustPostDomDef); bool needSplit = false; // If the value is live only in part of the predecessor blocks we have to // split those predecessor edges. for (SILBasicBlock *Pred : FrontierBB->getPredecessorBlocks()) { if (!LiveOutBlocks.count(Pred)) { needSplit = true; break; } } if (needSplit) { if (mode == DontModifyCFG) return false; // We need to split the critical edge to create a frontier instruction. UnhandledFrontierBlocks.insert(FrontierBB); } else { // The first instruction of the exit-block is part of the frontier. Fr.push_back(&*FrontierBB->begin()); } } // Split critical edges from the lifetime region to not yet handled frontier // blocks. for (SILBasicBlock *FrontierPred : LiveOutBlocks) { assert(mode != UsersMustPostDomDef); auto *T = FrontierPred->getTerminator(); // Cache the successor blocks because splitting critical edges invalidates // the successor list iterator of T. llvm::SmallVector SuccBlocks; for (const SILSuccessor &Succ : T->getSuccessors()) SuccBlocks.push_back(Succ); for (unsigned i = 0, e = SuccBlocks.size(); i != e; ++i) { if (UnhandledFrontierBlocks.count(SuccBlocks[i])) { assert(mode == AllowToModifyCFG); assert(isCriticalEdge(T, i) && "actually not a critical edge?"); SILBasicBlock *NewBlock = splitEdge(T, i); // The single terminator instruction is part of the frontier. Fr.push_back(&*NewBlock->begin()); NoCriticalEdges = false; } } } return NoCriticalEdges; } bool ValueLifetimeAnalysis::isWithinLifetime(SILInstruction *Inst) { SILBasicBlock *BB = Inst->getParent(); // Check if the value is not live anywhere in Inst's block. if (!LiveBlocks.count(BB)) return false; for (const SILSuccessor &Succ : BB->getSuccessors()) { // If the value is live at the beginning of any successor block it is also // live at the end of BB and therefore Inst is definitely in the lifetime // region (Note that we don't check in upward direction against the value's // definition). if (isAliveAtBeginOfBlock(Succ)) return true; } // The value is live in the block but not at the end of the block. Check if // Inst is located before (or at) the last use. for (auto II = BB->rbegin(); II != BB->rend(); ++II) { if (UserSet.count(&*II)) { return true; } if (Inst == &*II) return false; } llvm_unreachable("Expected to find use of value in block!"); } void ValueLifetimeAnalysis::dump() const { llvm::errs() << "lifetime of def: " << *DefValue; for (SILInstruction *Use : UserSet) { llvm::errs() << " use: " << *Use; } llvm::errs() << " live blocks:"; for (SILBasicBlock *BB : LiveBlocks) { llvm::errs() << ' ' << BB->getDebugID(); } llvm::errs() << '\n'; } //===----------------------------------------------------------------------===// // Casts Optimization and Simplification //===----------------------------------------------------------------------===// /// Check if is a bridging cast, i.e. one of the sides is /// a bridged type. static bool isBridgingCast(CanType SourceType, CanType TargetType) { // Bridging casts cannot be further simplified. auto TargetIsBridgeable = TargetType->isBridgeableObjectType(); auto SourceIsBridgeable = SourceType->isBridgeableObjectType(); if (TargetIsBridgeable != SourceIsBridgeable) return true; return false; } /// If target is a Swift type bridging to an ObjC type, /// return the ObjC type it bridges to. /// If target is an ObjC type, return this type. static Type getCastFromObjC(SILModule &M, CanType source, CanType target) { return M.getASTContext().getBridgedToObjC(M.getSwiftModule(), target); } /// Create a call of _forceBridgeFromObjectiveC_bridgeable or /// _conditionallyBridgeFromObjectiveC_bridgeable which converts an ObjC /// instance into a corresponding Swift type, conforming to /// _ObjectiveCBridgeable. SILInstruction * CastOptimizer:: optimizeBridgedObjCToSwiftCast(SILInstruction *Inst, bool isConditional, SILValue Src, SILValue Dest, CanType Source, CanType Target, Type BridgedSourceTy, Type BridgedTargetTy, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) { auto &M = Inst->getModule(); auto Loc = Inst->getLoc(); // The conformance to _BridgedToObjectiveC is statically known. // Retrieve the bridging operation to be used if a static conformance // to _BridgedToObjectiveC can be proven. FuncDecl *BridgeFuncDecl = isConditional ? M.getASTContext().getConditionallyBridgeFromObjectiveCBridgeable(nullptr) : M.getASTContext().getForceBridgeFromObjectiveCBridgeable(nullptr); assert(BridgeFuncDecl && "_forceBridgeFromObjectiveC should exist"); SILDeclRef FuncDeclRef(BridgeFuncDecl, SILDeclRef::Kind::Func); // Lookup a function from the stdlib. SILFunction *BridgedFunc = M.getOrCreateFunction( Loc, FuncDeclRef, ForDefinition_t::NotForDefinition); if (!BridgedFunc) return nullptr; CanType CanBridgedTy = BridgedTargetTy->getCanonicalType(); SILType SILBridgedTy = SILType::getPrimitiveObjectType(CanBridgedTy); SILBuilderWithScope Builder(Inst); SILValue SrcOp; SILInstruction *NewI = nullptr; assert(Src->getType().isAddress() && "Source should have an address type"); assert(Dest->getType().isAddress() && "Source should have an address type"); // AnyHashable is a special case - it does not conform to NSObject - // If AnyHashable - Bail out of the optimization if (auto DT = Target.getNominalOrBoundGenericNominal()) { if (DT == M.getASTContext().getAnyHashableDecl()) { return nullptr; } } // If this is a conditional cast: // We need a new fail BB in order to add a dealloc_stack to it SILBasicBlock *ConvFailBB = nullptr; if (isConditional) { auto CurrInsPoint = Builder.getInsertionPoint(); ConvFailBB = splitBasicBlockAndBranch(Builder, &(*FailureBB->begin()), nullptr, nullptr); Builder.setInsertionPoint(CurrInsPoint); } if (SILBridgedTy != Src->getType()) { // Check if we can simplify a cast into: // - ObjCTy to _ObjectiveCBridgeable._ObjectiveCType. // - then convert _ObjectiveCBridgeable._ObjectiveCType to // a Swift type using _forceBridgeFromObjectiveC. if (!Src->getType().isLoadable(M)) { // This code path is never reached in current test cases // If reached, we'd have to convert from an ObjC Any* to a loadable type // Should use check_addr / make a source we can actually load return nullptr; } // Generate a load for the source argument. auto *Load = Builder.createLoad(Loc, Src, LoadOwnershipQualifier::Unqualified); // Try to convert the source into the expected ObjC type first. if (Load->getType() == SILBridgedTy) { // If type of the source and the expected ObjC type are // equal, there is no need to generate the conversion // from ObjCTy to _ObjectiveCBridgeable._ObjectiveCType. if (isConditional) { SILBasicBlock *CastSuccessBB = Inst->getFunction()->createBasicBlock(); CastSuccessBB->createPHIArgument(SILBridgedTy, ValueOwnershipKind::Owned); Builder.createBranch(Loc, CastSuccessBB, SILValue(Load)); Builder.setInsertionPoint(CastSuccessBB); SrcOp = CastSuccessBB->getArgument(0); } else { SrcOp = Load; } } else if (isConditional) { SILBasicBlock *CastSuccessBB = Inst->getFunction()->createBasicBlock(); CastSuccessBB->createPHIArgument(SILBridgedTy, ValueOwnershipKind::Owned); NewI = Builder.createCheckedCastBranch(Loc, false, Load, SILBridgedTy, CastSuccessBB, ConvFailBB); Builder.setInsertionPoint(CastSuccessBB); SrcOp = CastSuccessBB->getArgument(0); } else { auto cast = Builder.createUnconditionalCheckedCast(Loc, Load, SILBridgedTy); NewI = cast; SrcOp = cast; } } else { SrcOp = Src; } // Now emit the a cast from the casted ObjC object into a target type. // This is done by means of calling _forceBridgeFromObjectiveC or // _conditionallyBridgeFromObjectiveC_bridgeable from the Target type. // Lookup the required function in the Target type. // Lookup the _ObjectiveCBridgeable protocol. auto BridgedProto = M.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable); auto Conf = *M.getSwiftModule()->lookupConformance(Target, BridgedProto); auto ParamTypes = BridgedFunc->getLoweredFunctionType()->getParameters(); auto *FuncRef = Builder.createFunctionRef(Loc, BridgedFunc); auto MetaTy = MetatypeType::get(Target, MetatypeRepresentation::Thick); auto SILMetaTy = M.Types.getTypeLowering(MetaTy).getLoweredType(); auto *MetaTyVal = Builder.createMetatype(Loc, SILMetaTy); SmallVector Args; // Add substitutions auto SubMap = SubstitutionMap::getProtocolSubstitutions( Conf.getRequirement(), Target, Conf); auto SILFnTy = FuncRef->getType(); SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap); SILFunctionConventions substConv(SubstFnTy.castTo(), M); // Temporary to hold the intermediate result. AllocStackInst *Tmp = nullptr; CanType OptionalTy; OptionalTypeKind OTK; SILValue InOutOptionalParam; if (isConditional) { // Create a temporary OptionalTy = OptionalType::get(Dest->getType().getSwiftRValueType()) ->getImplementationType() ->getCanonicalType(); OptionalTy.getAnyOptionalObjectType(OTK); Tmp = Builder.createAllocStack(Loc, SILType::getPrimitiveObjectType(OptionalTy)); InOutOptionalParam = Tmp; } else { InOutOptionalParam = Dest; } (void) ParamTypes; assert(ParamTypes[0].getConvention() == ParameterConvention::Direct_Owned && "Parameter should be @owned"); // Emit a retain. Builder.createRetainValue(Loc, SrcOp, Builder.getDefaultAtomicity()); Args.push_back(InOutOptionalParam); Args.push_back(SrcOp); Args.push_back(MetaTyVal); SmallVector Subs; Conf.getRequirement()->getGenericSignature()->getSubstitutions(SubMap, Subs); auto *AI = Builder.createApply(Loc, FuncRef, Subs, Args, false); // If the source of a cast should be destroyed, emit a release. if (isa(Inst)) { Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity()); } if (auto *CCABI = dyn_cast(Inst)) { if (CCABI->getConsumptionKind() == CastConsumptionKind::TakeAlways) { Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity()); } else if (CCABI->getConsumptionKind() == CastConsumptionKind::TakeOnSuccess) { // Insert a release in the success BB. Builder.setInsertionPoint(SuccessBB->begin()); Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity()); } } // Results should be checked in case we process a conditional // case. E.g. casts from NSArray into [SwiftType] may fail, i.e. return .None. if (isConditional) { // Copy the temporary into Dest. // Load from the optional. auto *SomeDecl = Builder.getASTContext().getOptionalSomeDecl(OTK); SILBasicBlock *ConvSuccessBB = Inst->getFunction()->createBasicBlock(); SmallVector, 1> CaseBBs; CaseBBs.push_back(std::make_pair(M.getASTContext().getOptionalNoneDecl(), FailureBB)); Builder.createSwitchEnumAddr(Loc, InOutOptionalParam, ConvSuccessBB, CaseBBs); Builder.setInsertionPoint(FailureBB->begin()); Builder.createDeallocStack(Loc, Tmp); Builder.setInsertionPoint(ConvSuccessBB); auto Addr = Builder.createUncheckedTakeEnumDataAddr(Loc, InOutOptionalParam, SomeDecl); Builder.createCopyAddr(Loc, Addr, Dest, IsTake, IsInitialization); Builder.createDeallocStack(Loc, Tmp); SmallVector SuccessBBArgs; Builder.createBranch(Loc, SuccessBB, SuccessBBArgs); } EraseInstAction(Inst); return (NewI) ? NewI : AI; } static bool canOptimizeCast(const swift::Type &BridgedTargetTy, swift::SILModule &M, swift::SILFunctionConventions &substConv) { // DestTy is the type which we want to convert to SILType DestTy = SILType::getPrimitiveObjectType(BridgedTargetTy->getCanonicalType()); // ConvTy is the return type of the _bridgeToObjectiveCImpl() auto ConvTy = substConv.getSILResultType().getObjectType(); if (ConvTy == DestTy) { // Destination is the same type return true; } // Check if a superclass/subclass of the source operand if (DestTy.isExactSuperclassOf(ConvTy)) { return true; } if (ConvTy.isExactSuperclassOf(DestTy)) { return true; } // check if it is a bridgeable CF type if (ConvTy.getSwiftRValueType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), DestTy.getSwiftRValueType())) { return true; } if (DestTy.getSwiftRValueType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), ConvTy.getSwiftRValueType())) { return true; } // All else failed - can't optimize this case return false; } /// Create a call of _bridgeToObjectiveC which converts an _ObjectiveCBridgeable /// instance into a bridged ObjC type. SILInstruction * CastOptimizer:: optimizeBridgedSwiftToObjCCast(SILInstruction *Inst, CastConsumptionKind ConsumptionKind, bool isConditional, SILValue Src, SILValue Dest, CanType Source, CanType Target, Type BridgedSourceTy, Type BridgedTargetTy, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) { auto &M = Inst->getModule(); auto Loc = Inst->getLoc(); bool AddressOnlyType = false; if (!Src->getType().isLoadable(M) || !Dest->getType().isLoadable(M)) { AddressOnlyType = true; } // Find the _BridgedToObjectiveC protocol. auto BridgedProto = M.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable); auto Conf = M.getSwiftModule()->lookupConformance(Source, BridgedProto); assert(Conf && "_ObjectiveCBridgeable conformance should exist"); (void) Conf; // Generate code to invoke _bridgeToObjectiveC SILBuilderWithScope Builder(Inst); auto *NTD = Source.getNominalOrBoundGenericNominal(); assert(NTD); SmallVector FoundMembers; ArrayRef Members; Members = NTD->lookupDirect(M.getASTContext().Id_bridgeToObjectiveC); if (Members.empty()) { if (NTD->getDeclContext()->lookupQualified( NTD->getDeclaredType(), M.getASTContext().Id_bridgeToObjectiveC, NLOptions::NL_ProtocolMembers, nullptr, FoundMembers)) { Members = FoundMembers; // Returned members are starting with the most specialized ones. // Thus, the first element is what we are looking for. Members = Members.take_front(1); } } // There should be exactly one implementation of _bridgeToObjectiveC. if (Members.size() != 1) return nullptr; auto BridgeFuncDecl = Members.front(); auto BridgeFuncDeclRef = SILDeclRef(BridgeFuncDecl); ModuleDecl *Mod = M.getASTContext().getLoadedModule( M.getASTContext().Id_Foundation); if (!Mod) return nullptr; SmallVector Results; Mod->lookupMember(Results, Source.getNominalOrBoundGenericNominal(), M.getASTContext().Id_bridgeToObjectiveC, Identifier()); ArrayRef ResultsRef(Results); if (ResultsRef.empty()) { M.getSwiftModule()->lookupMember(Results, Source.getNominalOrBoundGenericNominal(), M.getASTContext().Id_bridgeToObjectiveC, Identifier()); ResultsRef = Results; } if (ResultsRef.size() != 1) return nullptr; auto *resultDecl = Results.front(); auto MemberDeclRef = SILDeclRef(resultDecl); auto *BridgedFunc = M.getOrCreateFunction(Loc, MemberDeclRef, ForDefinition_t::NotForDefinition); // Implementation of _bridgeToObjectiveC could not be found. if (!BridgedFunc) return nullptr; if (Inst->getFunction()->isSerialized() && !BridgedFunc->hasValidLinkageForFragileRef()) return nullptr; auto ParamTypes = BridgedFunc->getLoweredFunctionType()->getParameters(); auto SILFnTy = SILType::getPrimitiveObjectType( M.Types.getConstantFunctionType(BridgeFuncDeclRef)); // TODO: Handle return from witness function. if (BridgedFunc->getLoweredFunctionType() ->getSingleResult() .isFormalIndirect()) return nullptr; // Get substitutions, if source is a bound generic type. auto SubMap = Source->getContextSubstitutionMap(M.getSwiftModule(), BridgeFuncDecl->getDeclContext()); SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap); SILFunctionConventions substConv(SubstFnTy.castTo(), M); // check that we can go through with the optimization if (!canOptimizeCast(BridgedTargetTy, M, substConv)) { return nullptr; } auto FnRef = Builder.createFunctionRef(Loc, BridgedFunc); if (Src->getType().isAddress() && !substConv.isSILIndirect(ParamTypes[0])) { // Create load Src = Builder.createLoad(Loc, Src, LoadOwnershipQualifier::Unqualified); } // Compensate different owning conventions of the replaced cast instruction // and the inserted conversion function. bool needRetainBeforeCall = false; bool needReleaseAfterCall = false; bool needReleaseInSuccess = false; switch (ParamTypes[0].getConvention()) { case ParameterConvention::Direct_Guaranteed: case ParameterConvention::Indirect_In_Guaranteed: switch (ConsumptionKind) { case CastConsumptionKind::TakeAlways: needReleaseAfterCall = true; break; case CastConsumptionKind::TakeOnSuccess: needReleaseInSuccess = true; break; case CastConsumptionKind::CopyOnSuccess: // Conservatively insert a retain/release pair around the conversion // function because the conversion function could decrement the // (global) reference count of the source object. // // %src = load %global_var // apply %conversion_func(@guaranteed %src) // // sil conversion_func { // %old_value = load %global_var // store %something_else, %global_var // strong_release %old_value // } needRetainBeforeCall = true; needReleaseAfterCall = true; break; } break; case ParameterConvention::Direct_Owned: case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: // Currently this // cannot appear, because the _bridgeToObjectiveC protocol witness method // always receives the this pointer (= the source) as guaranteed. // If it became possible (perhaps with the advent of ownership and // explicit +1 annotations), the implementation should look something // like this: /* switch (ConsumptionKind) { case CastConsumptionKind::TakeAlways: break; case CastConsumptionKind::TakeOnSuccess: needRetainBeforeCall = true; needReleaseInSuccess = true; break; case CastConsumptionKind::CopyOnSuccess: needRetainBeforeCall = true; break; } break; */ llvm_unreachable("this should never happen so is currently untestable"); case ParameterConvention::Direct_Unowned: assert(!AddressOnlyType && "AddressOnlyType with Direct_Unowned is not supported"); break; case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: // TODO handle remaining indirect argument types return nullptr; } bool needStackAllocatedTemporary = false; if (needRetainBeforeCall) { if (AddressOnlyType) { needStackAllocatedTemporary = true; auto NewSrc = Builder.createAllocStack(Loc, Src->getType()); Builder.createCopyAddr(Loc, Src, NewSrc, IsNotTake, IsInitialization); Src = NewSrc; } else { Builder.createRetainValue(Loc, Src, Builder.getDefaultAtomicity()); } } SmallVector Subs; if (auto *Sig = Source->getAnyNominal()->getGenericSignature()) Sig->getSubstitutions(SubMap, Subs); // Generate a code to invoke the bridging function. auto *NewAI = Builder.createApply(Loc, FnRef, Subs, Src, false); auto releaseSrc = [&](SILBuilder &Builder) { if (AddressOnlyType) { Builder.createDestroyAddr(Loc, Src); } else { Builder.createReleaseValue(Loc, Src, Builder.getDefaultAtomicity()); } }; Optional SuccBuilder; if (needReleaseInSuccess || needStackAllocatedTemporary) SuccBuilder.emplace(SuccessBB->begin()); if (needReleaseAfterCall) { releaseSrc(Builder); } else if (needReleaseInSuccess) { if (SuccessBB) { releaseSrc(*SuccBuilder); } else { // For an unconditional cast, success is the only defined path releaseSrc(Builder); } } // Pop the temporary stack slot for a copied temporary. if (needStackAllocatedTemporary) { assert((bool)SuccessBB == (bool)FailureBB); if (SuccessBB) { SuccBuilder->createDeallocStack(Loc, Src); SILBuilder FailBuilder(FailureBB->begin()); FailBuilder.createDeallocStack(Loc, Src); } else { Builder.createDeallocStack(Loc, Src); } } SILInstruction *NewI = NewAI; if (Dest) { // If it is addr cast then store the result. auto ConvTy = NewAI->getType(); auto DestTy = Dest->getType().getObjectType(); assert(DestTy == SILType::getPrimitiveObjectType( BridgedTargetTy->getCanonicalType()) && "Expected Dest Type to be the same as BridgedTargetTy"); SILValue CastedValue; if (ConvTy == DestTy) { CastedValue = NewAI; } else if (DestTy.isExactSuperclassOf(ConvTy)) { CastedValue = Builder.createUpcast(Loc, NewAI, DestTy); } else if (ConvTy.isExactSuperclassOf(DestTy)) { // The downcast from a base class to derived class may fail. if (isConditional) { // In case of a conditional cast, we should handle it gracefully. auto CondBrSuccessBB = NewAI->getFunction()->createBasicBlock(NewAI->getParent()); CondBrSuccessBB->createPHIArgument(DestTy, ValueOwnershipKind::Owned, nullptr); Builder.createCheckedCastBranch(Loc, /* isExact*/ false, NewAI, DestTy, CondBrSuccessBB, FailureBB); Builder.setInsertionPoint(CondBrSuccessBB, CondBrSuccessBB->begin()); CastedValue = CondBrSuccessBB->getArgument(0); } else { CastedValue = SILValue( Builder.createUnconditionalCheckedCast(Loc, NewAI, DestTy)); } } else if (ConvTy.getSwiftRValueType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), DestTy.getSwiftRValueType()) || DestTy.getSwiftRValueType() == getNSBridgedClassOfCFClass(M.getSwiftModule(), ConvTy.getSwiftRValueType())) { // Handle NS <-> CF toll-free bridging here. CastedValue = SILValue(Builder.createUncheckedRefCast(Loc, NewAI, DestTy)); } else { llvm_unreachable("optimizeBridgedSwiftToObjCCast: should never reach " "this condition: if the Destination does not have the " "same type, is not a bridgeable CF type and isn't a " "superclass/subclass of the source operand we should " "have bailed earlier"); } NewI = Builder.createStore(Loc, CastedValue, Dest, StoreOwnershipQualifier::Unqualified); if (isConditional && NewI->getParent() != NewAI->getParent()) { Builder.createBranch(Loc, SuccessBB); } } if (Dest) { EraseInstAction(Inst); } return NewI; } /// Make use of the fact that some of these casts cannot fail. /// For example, if the ObjC type is exactly the expected /// _ObjectiveCType type, then it would always succeed for /// NSString, NSNumber, etc. /// Casts from NSArray, NSDictionary and NSSet may fail. /// /// If ObjC class is not exactly _ObjectiveCType, then /// its conversion to a required _ObjectiveCType may fail. SILInstruction * CastOptimizer:: optimizeBridgedCasts(SILInstruction *Inst, CastConsumptionKind ConsumptionKind, bool isConditional, SILValue Src, SILValue Dest, CanType source, CanType target, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) { auto &M = Inst->getModule(); // To apply the bridged optimizations, we should ensure that types are not // existential (and keep in mind that generic parameters can be existentials), // and that one of the types is a class and another one is a struct. if (source.isAnyExistentialType() || target.isAnyExistentialType() || source->is() || target->is() || (source.getClassOrBoundGenericClass() && !target.getStructOrBoundGenericStruct()) || (target.getClassOrBoundGenericClass() && !source.getStructOrBoundGenericStruct())) return nullptr; // Casts involving non-bound generic types cannot be optimized. if (source->hasArchetype() || target->hasArchetype()) return nullptr; auto BridgedTargetTy = getCastFromObjC(M, source, target); if (!BridgedTargetTy) return nullptr; auto BridgedSourceTy = getCastFromObjC(M, target, source); if (!BridgedSourceTy) return nullptr; CanType CanBridgedTargetTy = BridgedTargetTy->getCanonicalType(); CanType CanBridgedSourceTy = BridgedSourceTy->getCanonicalType(); if (CanBridgedSourceTy == source && CanBridgedTargetTy == target) { // Both source and target type are ObjC types. return nullptr; } if (CanBridgedSourceTy != source && CanBridgedTargetTy != target) { // Both source and target type are Swift types. return nullptr; } if ((CanBridgedSourceTy && CanBridgedSourceTy->getAnyNominal() == M.getASTContext().getNSErrorDecl()) || (CanBridgedTargetTy && CanBridgedSourceTy->getAnyNominal() == M.getASTContext().getNSErrorDecl())) { // FIXME: Can't optimize bridging with NSError. return nullptr; } if (CanBridgedSourceTy || CanBridgedTargetTy) { // Check what kind of conversion it is? ObjC->Swift or Swift-ObjC? if (CanBridgedTargetTy != target) { // This is an ObjC to Swift cast. return optimizeBridgedObjCToSwiftCast(Inst, isConditional, Src, Dest, source, target, BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB); } else { // This is a Swift to ObjC cast return optimizeBridgedSwiftToObjCCast(Inst, ConsumptionKind, isConditional, Src, Dest, source, target, BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB); } } llvm_unreachable("Unknown kind of bridging"); } SILInstruction * CastOptimizer:: simplifyCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst) { if (auto *I = optimizeCheckedCastAddrBranchInst(Inst)) Inst = dyn_cast(I); if (!Inst) return nullptr; auto Loc = Inst->getLoc(); auto Src = Inst->getSrc(); auto Dest = Inst->getDest(); auto SourceType = Inst->getSourceType(); auto TargetType = Inst->getTargetType(); auto *SuccessBB = Inst->getSuccessBB(); auto *FailureBB = Inst->getFailureBB(); auto &Mod = Inst->getModule(); SILBuilderWithScope Builder(Inst); // Try to determine the outcome of the cast from a known type // to a protocol type at compile-time. bool isSourceTypeExact = isa(Inst->getSrc()); // Check if we can statically predict the outcome of the cast. auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType, TargetType, isSourceTypeExact, Mod.isWholeModule()); if (Feasibility == DynamicCastFeasibility::WillFail) { if (shouldDestroyOnFailure(Inst->getConsumptionKind())) { auto &srcTL = Builder.getModule().getTypeLowering(Src->getType()); srcTL.emitDestroyAddress(Builder, Loc, Src); } auto NewI = Builder.createBranch(Loc, FailureBB); EraseInstAction(Inst); WillFailAction(); return NewI; } bool ResultNotUsed = isa(Dest); if (ResultNotUsed) { for (auto Use : Dest->getUses()) { auto *User = Use->getUser(); if (isa(User) || User == Inst) continue; ResultNotUsed = false; break; } } auto *BB = Inst->getParent(); SILInstruction *BridgedI = nullptr; // To apply the bridged optimizations, we should // ensure that types are not existential, // and that not both types are classes. BridgedI = optimizeBridgedCasts( Inst, Inst->getConsumptionKind(), /* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed, Src, Dest, SourceType, TargetType, SuccessBB, FailureBB); if (!BridgedI) { // If the cast may succeed or fail, and it can't be optimized into a // bridging operation, then let it be. if (Feasibility == DynamicCastFeasibility::MaySucceed) { return nullptr; } assert(Feasibility == DynamicCastFeasibility::WillSucceed); // Replace by unconditional_addr_cast, followed by a branch. // The unconditional_addr_cast can be skipped, if the result of a cast // is not used afterwards. if (ResultNotUsed) { if (shouldTakeOnSuccess(Inst->getConsumptionKind())) { auto &srcTL = Builder.getModule().getTypeLowering(Src->getType()); srcTL.emitDestroyAddress(Builder, Loc, Src); } EraseInstAction(Inst); Builder.setInsertionPoint(BB); auto *NewI = Builder.createBranch(Loc, SuccessBB); WillSucceedAction(); return NewI; } // Since it is an addr cast, only address types are handled here. if (!Src->getType().isAddress() || !Dest->getType().isAddress()) { return nullptr; } // For CopyOnSuccess casts, we could insert an explicit copy here, but this // case does not happen in practice. // Both TakeOnSuccess and TakeAlways can be reduced to an // UnconditionalCheckedCast, since the failure path is irrelevant. if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess) return nullptr; if (!emitSuccessfulIndirectUnconditionalCast( Builder, Mod.getSwiftModule(), Loc, Src, SourceType, Dest, TargetType, Inst)) { // No optimization was possible. return nullptr; } EraseInstAction(Inst); } SILInstruction *NewI = &BB->back(); if (!isa(NewI)) { Builder.setInsertionPoint(BB); NewI = Builder.createBranch(Loc, SuccessBB); } WillSucceedAction(); return NewI; } SILInstruction * CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) { if (Inst->isExact()) { auto *ARI = dyn_cast(stripUpCasts(Inst->getOperand())); if (!ARI) return nullptr; // We know the dynamic type of the operand. SILBuilderWithScope Builder(Inst); auto Loc = Inst->getLoc(); auto *SuccessBB = Inst->getSuccessBB(); auto *FailureBB = Inst->getFailureBB(); if (ARI->getType() == Inst->getCastType()) { // This exact cast will succeed. SmallVector Args; Args.push_back(ARI); auto *NewI = Builder.createBranch(Loc, SuccessBB, Args); EraseInstAction(Inst); WillSucceedAction(); return NewI; } // This exact cast will fail. auto *NewI = Builder.createBranch(Loc, FailureBB); EraseInstAction(Inst); WillFailAction(); return NewI; } if (auto *I = optimizeCheckedCastBranchInst(Inst)) Inst = dyn_cast(I); if (!Inst) return nullptr; auto LoweredTargetType = Inst->getCastType(); auto SourceType = Inst->getSourceType(); auto TargetType = Inst->getTargetType(); auto Loc = Inst->getLoc(); auto *SuccessBB = Inst->getSuccessBB(); auto *FailureBB = Inst->getFailureBB(); auto Op = Inst->getOperand(); auto &Mod = Inst->getModule(); bool isSourceTypeExact = isa(Op); // Check if we can statically predict the outcome of the cast. auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType, TargetType, isSourceTypeExact); SILBuilderWithScope Builder(Inst); if (Feasibility == DynamicCastFeasibility::WillFail) { auto *NewI = Builder.createBranch(Loc, FailureBB); EraseInstAction(Inst); WillFailAction(); return NewI; } bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty(); SILValue CastedValue; if (Op->getType() != LoweredTargetType) { auto Src = Inst->getOperand(); auto Dest = SILValue(); // Apply the bridged cast optimizations. // TODO: Bridged casts cannot be expressed by checked_cast_br yet. // Should we ever support it, please review this code. auto BridgedI = optimizeBridgedCasts( Inst, CastConsumptionKind::CopyOnSuccess, /* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed, Src, Dest, SourceType, TargetType, nullptr, nullptr); if (BridgedI) { llvm_unreachable( "Bridged casts cannot be expressed by checked_cast_br yet"); } else { // If the cast may succeed or fail and can't be turned into a bridging // call, then let it be. if (Feasibility == DynamicCastFeasibility::MaySucceed) { return nullptr; } assert(Feasibility == DynamicCastFeasibility::WillSucceed); // Replace by unconditional_cast, followed by a branch. // The unconditional_cast can be skipped, if the result of a cast // is not used afterwards. if (!ResultNotUsed) { if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType)) return nullptr; CastedValue = emitSuccessfulScalarUnconditionalCast( Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType, SourceType, TargetType, Inst); } else { CastedValue = SILUndef::get(LoweredTargetType, Mod); } if (!CastedValue) CastedValue = Builder.createUnconditionalCheckedCast(Loc, Op, LoweredTargetType); } } else { // No need to cast. CastedValue = Op; } auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue); EraseInstAction(Inst); WillSucceedAction(); return NewI; } SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst( CheckedCastValueBranchInst *Inst) { if (auto *I = optimizeCheckedCastValueBranchInst(Inst)) Inst = dyn_cast(I); if (!Inst) return nullptr; auto LoweredTargetType = Inst->getCastType(); auto SourceType = Inst->getSourceType(); auto TargetType = Inst->getTargetType(); auto Loc = Inst->getLoc(); auto *SuccessBB = Inst->getSuccessBB(); auto *FailureBB = Inst->getFailureBB(); auto Op = Inst->getOperand(); auto &Mod = Inst->getModule(); bool isSourceTypeExact = isa(Op); // Check if we can statically predict the outcome of the cast. auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType, TargetType, isSourceTypeExact); SILBuilderWithScope Builder(Inst); if (Feasibility == DynamicCastFeasibility::WillFail) { auto *NewI = Builder.createBranch(Loc, FailureBB); EraseInstAction(Inst); WillFailAction(); return NewI; } // Casting will succeed. bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty(); SILValue CastedValue; if (Op->getType() != LoweredTargetType) { auto Src = Inst->getOperand(); auto Dest = SILValue(); // Apply the bridged cast optimizations. // TODO: Bridged casts cannot be expressed by checked_cast_value_br yet. // Once the support for opaque values has landed, please review this // code. auto BridgedI = optimizeBridgedCasts( Inst, CastConsumptionKind::CopyOnSuccess, /* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed, Src, Dest, SourceType, TargetType, nullptr, nullptr); if (BridgedI) { llvm_unreachable( "Bridged casts cannot be expressed by checked_cast_value_br yet"); } else { // If the cast may succeed or fail and can't be turned into a bridging // call, then let it be. if (Feasibility == DynamicCastFeasibility::MaySucceed) { return nullptr; } assert(Feasibility == DynamicCastFeasibility::WillSucceed); // Replace by unconditional_cast, followed by a branch. // The unconditional_cast can be skipped, if the result of a cast // is not used afterwards. if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType)) return nullptr; if (!ResultNotUsed) { CastedValue = emitSuccessfulScalarUnconditionalCast( Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType, SourceType, TargetType, Inst); } else { CastedValue = SILUndef::get(LoweredTargetType, Mod); } } if (!CastedValue) CastedValue = Builder.createUnconditionalCheckedCastValue( Loc, Op, LoweredTargetType); } else { // No need to cast. CastedValue = Op; } auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue); EraseInstAction(Inst); WillSucceedAction(); return NewI; } SILInstruction *CastOptimizer::optimizeCheckedCastAddrBranchInst( CheckedCastAddrBranchInst *Inst) { auto Loc = Inst->getLoc(); auto Src = Inst->getSrc(); auto Dest = Inst->getDest(); auto *SuccessBB = Inst->getSuccessBB(); auto *FailureBB = Inst->getFailureBB(); // If there is an unbound generic type involved in the cast, bail. if (Src->getType().hasArchetype() || Dest->getType().hasArchetype()) return nullptr; // %1 = metatype $A.Type // [%2 = init_existential_metatype %1 ...] // %3 = alloc_stack // store %1 to %3 or store %2 to %3 // checked_cast_addr_br %3 to ... // -> // %1 = metatype $A.Type // %c = checked_cast_br %1 to ... // store %c to %3 (if successful) if (auto *ASI = dyn_cast(Src)) { // Check if the value of this alloc_stack is set only once by a store // instruction, used only by CCABI and then deallocated. bool isLegal = true; StoreInst *Store = nullptr; for (auto Use : ASI->getUses()) { auto *User = Use->getUser(); if (isa(User) || User == Inst) continue; if (auto *SI = dyn_cast(User)) { if (!Store) { Store = SI; continue; } } isLegal = false; break; } if (isLegal && Store) { // Check what was the value stored in the allocated stack slot. auto Src = Store->getSrc(); MetatypeInst *MI = nullptr; if (auto *IEMI = dyn_cast(Src)) { MI = dyn_cast(IEMI->getOperand()); } if (!MI) MI = dyn_cast(Src); if (MI) { if (SuccessBB->getSinglePredecessorBlock() && canUseScalarCheckedCastInstructions( Inst->getModule(), MI->getType().getSwiftRValueType(), Inst->getTargetType())) { SILBuilderWithScope B(Inst); auto NewI = B.createCheckedCastBranch( Loc, false /*isExact*/, MI, Dest->getType().getObjectType(), SuccessBB, FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount()); SuccessBB->createPHIArgument(Dest->getType().getObjectType(), ValueOwnershipKind::Owned); B.setInsertionPoint(SuccessBB->begin()); // Store the result B.createStore(Loc, SuccessBB->getArgument(0), Dest, StoreOwnershipQualifier::Unqualified); EraseInstAction(Inst); return NewI; } } } } return nullptr; } SILInstruction *CastOptimizer::optimizeCheckedCastValueBranchInst( CheckedCastValueBranchInst *Inst) { // TODO return nullptr; } SILInstruction * CastOptimizer::optimizeCheckedCastBranchInst(CheckedCastBranchInst *Inst) { if (Inst->isExact()) return nullptr; auto LoweredTargetType = Inst->getCastType(); auto Loc = Inst->getLoc(); auto *SuccessBB = Inst->getSuccessBB(); auto *FailureBB = Inst->getFailureBB(); auto Op = Inst->getOperand(); // Try to simplify checked_cond_br instructions using existential // metatypes by propagating a concrete type whenever it can be // determined statically. // %0 = metatype $A.Type // %1 = init_existential_metatype ..., %0: $A // checked_cast_br %1, .... // -> // %0 = metatype $A.Type // checked_cast_br %0 to ... if (auto *IEMI = dyn_cast(Op)) { if (auto *MI = dyn_cast(IEMI->getOperand())) { SILBuilderWithScope B(Inst); auto *NewI = B.createCheckedCastBranch( Loc, /* isExact */ false, MI, LoweredTargetType, SuccessBB, FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount()); EraseInstAction(Inst); return NewI; } } if (auto *EMI = dyn_cast(Op)) { // Operand of the existential_metatype instruction. auto Op = EMI->getOperand(); auto EmiTy = EMI->getType(); // %0 = alloc_stack $T // %1 = init_existential_addr %0: $*T, $A // %2 = existential_metatype $T.Type, %0: $*T // checked_cast_br %2 to ... // -> // %1 = metatype $A.Type // checked_cast_br %1 to ... if (auto *ASI = dyn_cast(Op)) { // Should be in the same BB. if (ASI->getParent() != EMI->getParent()) return nullptr; // Check if this alloc_stack is only initialized once by means of // single init_existential_addr. bool isLegal = true; // init_existential instruction used to initialize this alloc_stack. InitExistentialAddrInst *FoundIEI = nullptr; for (auto Use: getNonDebugUses(ASI)) { auto *User = Use->getUser(); if (isa(User) || isa(User) || isa(User)) continue; if (auto *IEI = dyn_cast(User)) { if (!FoundIEI) { FoundIEI = IEI; continue; } } isLegal = false; break; } if (isLegal && FoundIEI) { // Should be in the same BB. if (FoundIEI->getParent() != EMI->getParent()) return nullptr; // Get the type used to initialize the existential. auto LoweredConcreteTy = FoundIEI->getLoweredConcreteType(); // We don't know enough at compile time about existential // and generic type parameters. if (LoweredConcreteTy.isAnyExistentialType() || LoweredConcreteTy.is()) return nullptr; // Get the metatype of this type. auto EMT = EmiTy.castTo(); auto *MetaTy = MetatypeType::get(LoweredConcreteTy.getSwiftRValueType(), EMT->getRepresentation()); auto CanMetaTy = CanTypeWrapper(MetaTy); auto SILMetaTy = SILType::getPrimitiveObjectType(CanMetaTy); SILBuilderWithScope B(Inst); B.getOpenedArchetypes().addOpenedArchetypeOperands( FoundIEI->getTypeDependentOperands()); auto *MI = B.createMetatype(FoundIEI->getLoc(), SILMetaTy); auto *NewI = B.createCheckedCastBranch( Loc, /* isExact */ false, MI, LoweredTargetType, SuccessBB, FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount()); EraseInstAction(Inst); return NewI; } } // %0 = alloc_ref $A // %1 = init_existential_ref %0: $A, $... // %2 = existential_metatype ..., %1 : ... // checked_cast_br %2, .... // -> // %1 = metatype $A.Type // checked_cast_br %1, .... if (auto *FoundIERI = dyn_cast(Op)) { auto *ASRI = dyn_cast(FoundIERI->getOperand()); if (!ASRI) return nullptr; // Should be in the same BB. if (ASRI->getParent() != EMI->getParent()) return nullptr; // Check if this alloc_stack is only initialized once by means of // a single init_existential_ref. bool isLegal = true; for (auto Use: getNonDebugUses(ASRI)) { auto *User = Use->getUser(); if (isa(User) || isa(User)) continue; if (auto *IERI = dyn_cast(User)) { if (IERI == FoundIERI) { continue; } } isLegal = false; break; } if (isLegal && FoundIERI) { // Should be in the same BB. if (FoundIERI->getParent() != EMI->getParent()) return nullptr; // Get the type used to initialize the existential. auto ConcreteTy = FoundIERI->getFormalConcreteType(); // We don't know enough at compile time about existential // and generic type parameters. if (ConcreteTy.isAnyExistentialType() || ConcreteTy->is()) return nullptr; // Get the SIL metatype of this type. auto EMT = EMI->getType().castTo(); auto *MetaTy = MetatypeType::get(ConcreteTy, EMT->getRepresentation()); auto CanMetaTy = CanTypeWrapper(MetaTy); auto SILMetaTy = SILType::getPrimitiveObjectType(CanMetaTy); SILBuilderWithScope B(Inst); B.getOpenedArchetypes().addOpenedArchetypeOperands( FoundIERI->getTypeDependentOperands()); auto *MI = B.createMetatype(FoundIERI->getLoc(), SILMetaTy); auto *NewI = B.createCheckedCastBranch( Loc, /* isExact */ false, MI, LoweredTargetType, SuccessBB, FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount()); EraseInstAction(Inst); return NewI; } } } return nullptr; } ValueBase * CastOptimizer:: optimizeUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *Inst) { auto LoweredSourceType = Inst->getOperand()->getType(); auto LoweredTargetType = Inst->getType(); auto Loc = Inst->getLoc(); auto Op = Inst->getOperand(); auto &Mod = Inst->getModule(); bool isSourceTypeExact = isa(Op); // Check if we can statically predict the outcome of the cast. auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), Inst->getSourceType(), Inst->getTargetType(), isSourceTypeExact); if (Feasibility == DynamicCastFeasibility::WillFail) { // Remove the cast and insert a trap, followed by an // unreachable instruction. SILBuilderWithScope Builder(Inst); auto *Trap = Builder.createBuiltinTrap(Loc); Inst->replaceAllUsesWithUndef(); EraseInstAction(Inst); Builder.setInsertionPoint(std::next(SILBasicBlock::iterator(Trap))); auto *UnreachableInst = Builder.createUnreachable(ArtificialUnreachableLocation()); // Delete everything after the unreachable except for dealloc_stack which we // move before the trap. deleteInstructionsAfterUnreachable(UnreachableInst, Trap); WillFailAction(); return Trap; } if (Feasibility == DynamicCastFeasibility::WillSucceed) { if (Inst->use_empty()) { EraseInstAction(Inst); WillSucceedAction(); return nullptr; } } SILBuilderWithScope Builder(Inst); // Try to apply the bridged casts optimizations auto SourceType = LoweredSourceType.getSwiftRValueType(); auto TargetType = LoweredTargetType.getSwiftRValueType(); auto Src = Inst->getOperand(); auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess, false, Src, SILValue(), SourceType, TargetType, nullptr, nullptr); if (NewI) { // FIXME: I'm not sure why this is true! auto newValue = cast(NewI); ReplaceInstUsesAction(Inst, newValue); EraseInstAction(Inst); WillSucceedAction(); return newValue; } // If the cast may succeed or fail and can't be optimized into a bridging // call, let it be. if (Feasibility == DynamicCastFeasibility::MaySucceed) { return nullptr; } assert(Feasibility == DynamicCastFeasibility::WillSucceed); if (isBridgingCast(SourceType, TargetType)) return nullptr; auto Result = emitSuccessfulScalarUnconditionalCast(Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType, LoweredSourceType.getSwiftRValueType(), LoweredTargetType.getSwiftRValueType(), Inst); if (!Result) { // No optimization was possible. return nullptr; } ReplaceInstUsesAction(Inst, Result); EraseInstAction(Inst); WillSucceedAction(); return Result; } /// Deletes all instructions after \p UnreachableInst except dealloc_stack /// instructions are moved before \p TrapInst. void CastOptimizer::deleteInstructionsAfterUnreachable( SILInstruction *UnreachableInst, SILInstruction *TrapInst) { auto UnreachableInstIt = std::next(SILBasicBlock::iterator(UnreachableInst)); auto *Block = TrapInst->getParent(); while (UnreachableInstIt != Block->end()) { SILInstruction *CurInst = &*UnreachableInstIt; ++UnreachableInstIt; if (auto *DeallocStack = dyn_cast(CurInst)) if (!isa(DeallocStack->getOperand())) { DeallocStack->moveBefore(TrapInst); continue; } CurInst->replaceAllUsesOfAllResultsWithUndef(); EraseInstAction(CurInst); } } /// TODO: Move to emitSuccessfulIndirectUnconditionalCast? /// /// Peephole to avoid runtime calls: /// unconditional_checked_cast_addr T in %0 : $*T to P in %1 : $*P /// -> /// %addr = init_existential_addr %1 : $*P, T /// copy_addr %0 to %addr /// /// where T is a type statically known to conform to P. /// /// In caase P is a class existential type, it generates: /// %val = load %0 : $*T /// %existential = init_existential_ref %val : $T, $T, P /// store %existential to %1 : $*P /// /// Returns true if the optimization was possible and false otherwise. static bool optimizeStaticallyKnownProtocolConformance( UnconditionalCheckedCastAddrInst *Inst) { auto Loc = Inst->getLoc(); auto Src = Inst->getSrc(); auto Dest = Inst->getDest(); auto SourceType = Inst->getSourceType(); auto TargetType = Inst->getTargetType(); auto &Mod = Inst->getModule(); if (TargetType->isAnyExistentialType() && !SourceType->isAnyExistentialType()) { auto &Ctx = Mod.getASTContext(); auto *SM = Mod.getSwiftModule(); auto Proto = dyn_cast(TargetType->getAnyNominal()); if (Proto) { auto Conformance = SM->lookupConformance(SourceType, Proto); if (Conformance.hasValue()) { // SourceType is a non-existential type conforming to a // protocol represented by the TargetType. SILBuilder B(Inst); SmallVector NewConformances; NewConformances.push_back(Conformance.getValue()); ArrayRef Conformances = Ctx.AllocateCopy(NewConformances); auto ExistentialRepr = Dest->getType().getPreferredExistentialRepresentation(Mod, SourceType); switch (ExistentialRepr) { default: return false; case ExistentialRepresentation::Opaque: { auto ExistentialAddr = B.createInitExistentialAddr( Loc, Dest, SourceType, Src->getType().getObjectType(), Conformances); B.createCopyAddr( Loc, Src, ExistentialAddr, IsTake_t::IsTake, IsInitialization_t::IsInitialization); break; } case ExistentialRepresentation::Class: { auto Value = B.createLoad(Loc, Src, swift::LoadOwnershipQualifier::Unqualified); auto Existential = B.createInitExistentialRef(Loc, Dest->getType().getObjectType(), SourceType, Value, Conformances); B.createStore(Loc, Existential, Dest, swift::StoreOwnershipQualifier::Unqualified); break; } case ExistentialRepresentation::Boxed: { auto AllocBox = B.createAllocExistentialBox(Loc, Dest->getType(), SourceType, Conformances); auto Projection = B.createProjectExistentialBox(Loc, Src->getType(), AllocBox); auto Value = B.createLoad(Loc, Src, swift::LoadOwnershipQualifier::Unqualified); B.createStore(Loc, Value, Projection, swift::StoreOwnershipQualifier::Unqualified); B.createStore(Loc, AllocBox, Dest, swift::StoreOwnershipQualifier::Unqualified); break; } }; return true; } } } return false; } SILInstruction * CastOptimizer:: optimizeUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *Inst) { auto Loc = Inst->getLoc(); auto Src = Inst->getSrc(); auto Dest = Inst->getDest(); auto SourceType = Inst->getSourceType(); auto TargetType = Inst->getTargetType(); auto &Mod = Inst->getModule(); bool isSourceTypeExact = isa(Src); // Check if we can statically predict the outcome of the cast. auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType, TargetType, isSourceTypeExact); if (Feasibility == DynamicCastFeasibility::MaySucceed) { // Forced bridged casts can be still simplified here. // If they fail, they fail inside the conversion function. if (!isBridgingCast(SourceType, TargetType)) return nullptr; } if (Feasibility == DynamicCastFeasibility::WillFail) { // Remove the cast and insert a trap, followed by an // unreachable instruction. SILBuilderWithScope Builder(Inst); // mem2reg's invariants get unhappy if we don't try to // initialize a loadable result. auto DestType = Dest->getType(); auto &resultTL = Mod.Types.getTypeLowering(DestType); if (!resultTL.isAddressOnly()) { auto undef = SILValue(SILUndef::get(DestType.getObjectType(), Builder.getModule())); Builder.createStore(Loc, undef, Dest, StoreOwnershipQualifier::Unqualified); } auto *TrapI = Builder.createBuiltinTrap(Loc); EraseInstAction(Inst); Builder.setInsertionPoint(std::next(SILBasicBlock::iterator(TrapI))); auto *UnreachableInst = Builder.createUnreachable(ArtificialUnreachableLocation()); // Delete everything after the unreachable except for dealloc_stack which we // move before the trap. deleteInstructionsAfterUnreachable(UnreachableInst, TrapI); WillFailAction(); } if (Feasibility == DynamicCastFeasibility::WillSucceed || Feasibility == DynamicCastFeasibility::MaySucceed) { // Check if a result of a cast is unused. If this is the case, the cast can // be removed even if the cast may fail at runtime. // Swift optimizer does not claim to be crash-preserving. bool ResultNotUsed = isa(Dest); DestroyAddrInst *DestroyDestInst = nullptr; if (ResultNotUsed) { for (auto Use : Dest->getUses()) { auto *User = Use->getUser(); if (isa(User) || User == Inst) continue; if (isa(User) && !DestroyDestInst) { DestroyDestInst = cast(User); continue; } ResultNotUsed = false; DestroyDestInst = nullptr; break; } } if (ResultNotUsed) { SILBuilder B(Inst); B.createDestroyAddr(Inst->getLoc(), Inst->getSrc()); if (DestroyDestInst) EraseInstAction(DestroyDestInst); EraseInstAction(Inst); WillSucceedAction(); return nullptr; } // Try to apply the bridged casts optimizations. auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::TakeAlways, false, Src, Dest, SourceType, TargetType, nullptr, nullptr); if (NewI) { WillSucceedAction(); return nullptr; } if (Feasibility == DynamicCastFeasibility::MaySucceed) return nullptr; assert(Feasibility == DynamicCastFeasibility::WillSucceed); if (optimizeStaticallyKnownProtocolConformance(Inst)) { EraseInstAction(Inst); WillSucceedAction(); return nullptr; } if (isBridgingCast(SourceType, TargetType)) return nullptr; SILBuilderWithScope Builder(Inst); if (!emitSuccessfulIndirectUnconditionalCast(Builder, Mod.getSwiftModule(), Loc, Src, SourceType, Dest, TargetType, Inst)) { // No optimization was possible. return nullptr; } EraseInstAction(Inst); WillSucceedAction(); } return nullptr; } bool swift::simplifyUsers(SingleValueInstruction *I) { bool Changed = false; for (auto UI = I->use_begin(), UE = I->use_end(); UI != UE; ) { SILInstruction *User = UI->getUser(); ++UI; auto SVI = dyn_cast(User); if (!SVI) continue; SILValue S = simplifyInstruction(SVI); if (!S) continue; SVI->replaceAllUsesWith(S); SVI->eraseFromParent(); Changed = true; } return Changed; } /// True if a type can be expanded /// without a significant increase to code size. bool swift::shouldExpand(SILModule &Module, SILType Ty) { if (EnableExpandAll) { return true; } if (Ty.isAddressOnly(Module)) { return false; } unsigned numFields = Module.Types.countNumberOfFields(Ty); if (numFields > 6) { return false; } return true; } /// Some support functions for the global-opt and let-properties-opts /// Check if a given type is a simple type, i.e. a builtin /// integer or floating point type or a struct/tuple whose members /// are of simple types. /// TODO: Cache the "simple" flag for types to avoid repeating checks. bool swift::isSimpleType(SILType SILTy, SILModule& Module) { // Classes can never be initialized statically at compile-time. if (SILTy.getClassOrBoundGenericClass()) { return false; } if (!SILTy.isTrivial(Module)) return false; return true; } /// Check if the value of V is computed by means of a simple initialization. /// Store the actual SILValue into Val and the reversed list of instructions /// initializing it in Insns. /// The check is performed by recursively walking the computation of the /// SIL value being analyzed. /// TODO: Move into utils. bool swift::analyzeStaticInitializer(SILValue V, SmallVectorImpl &Insns) { // Save every instruction we see. // TODO: MultiValueInstruction? if (auto I = dyn_cast(V)) Insns.push_back(I); if (auto *SI = dyn_cast(V)) { // If it is not a struct which is a simple type, bail. if (!isSimpleType(SI->getType(), SI->getModule())) return false; for (auto &Op: SI->getAllOperands()) { // If one of the struct instruction operands is not // a simple initializer, bail. if (!analyzeStaticInitializer(Op.get(), Insns)) return false; } return true; } if (auto *TI = dyn_cast(V)) { // If it is not a tuple which is a simple type, bail. if (!isSimpleType(TI->getType(), TI->getModule())) return false; for (auto &Op: TI->getAllOperands()) { // If one of the struct instruction operands is not // a simple initializer, bail. if (!analyzeStaticInitializer(Op.get(), Insns)) return false; } return true; } if (auto *bi = dyn_cast(V)) { switch (bi->getBuiltinInfo().ID) { case BuiltinValueKind::FPTrunc: if (auto *LI = dyn_cast(bi->getArguments()[0])) { return analyzeStaticInitializer(LI, Insns); } return false; default: return false; } } if (isa(V) || isa(V) || isa(V)) { return true; } return false; } /// Replace load sequence which may contain /// a chain of struct_element_addr followed by a load. /// The sequence is traversed inside out, i.e. /// starting with the innermost struct_element_addr /// Move into utils. void swift::replaceLoadSequence(SILInstruction *I, SILValue Value, SILBuilder &B) { if (auto *LI = dyn_cast(I)) { LI->replaceAllUsesWith(Value); return; } // It is a series of struct_element_addr followed by load. if (auto *SEAI = dyn_cast(I)) { auto *SEI = B.createStructExtract(SEAI->getLoc(), Value, SEAI->getField()); for (auto SEAIUse : SEAI->getUses()) { replaceLoadSequence(SEAIUse->getUser(), SEI, B); } return; } if (auto *TEAI = dyn_cast(I)) { auto *TEI = B.createTupleExtract(TEAI->getLoc(), Value, TEAI->getFieldNo()); for (auto TEAIUse : TEAI->getUses()) { replaceLoadSequence(TEAIUse->getUser(), TEI, B); } return; } llvm_unreachable("Unknown instruction sequence for reading from a global"); } /// Are the callees that could be called through Decl statically /// knowable based on the Decl and the compilation mode? bool swift::calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl) { if (Decl.isForeign) return false; const DeclContext *AssocDC = M.getAssociatedContext(); if (!AssocDC) return false; auto *AFD = Decl.getAbstractFunctionDecl(); assert(AFD && "Expected abstract function decl!"); // Only handle members defined within the SILModule's associated context. if (!AFD->isChildContextOf(AssocDC)) return false; if (AFD->isDynamic()) return false; if (!AFD->hasAccess()) return false; // Only consider 'private' members, unless we are in whole-module compilation. switch (AFD->getEffectiveAccess()) { case AccessLevel::Open: return false; case AccessLevel::Public: if (isa(AFD)) { // Constructors are special: a derived class in another module can // "override" a constructor if its class is "open", although the // constructor itself is not open. auto *ND = AFD->getDeclContext() ->getAsNominalTypeOrNominalTypeExtensionContext(); if (ND->getEffectiveAccess() == AccessLevel::Open) return false; } LLVM_FALLTHROUGH; case AccessLevel::Internal: return M.isWholeModule(); case AccessLevel::FilePrivate: case AccessLevel::Private: return true; } llvm_unreachable("Unhandled access level in switch."); } void swift::hoistAddressProjections(Operand &Op, SILInstruction *InsertBefore, DominanceInfo *DomTree) { SILValue V = Op.get(); SILInstruction *Prev = nullptr; auto *InsertPt = InsertBefore; while (true) { SILValue Incoming = stripSinglePredecessorArgs(V); // Forward the incoming arg from a single predecessor. if (V != Incoming) { if (V == Op.get()) { // If we are the operand itself set the operand to the incoming // argument. Op.set(Incoming); V = Incoming; } else { // Otherwise, set the previous projections operand to the incoming // argument. assert(Prev && "Must have seen a projection"); Prev->setOperand(0, Incoming); V = Incoming; } } switch (V->getKind()) { case ValueKind::StructElementAddrInst: case ValueKind::TupleElementAddrInst: case ValueKind::RefElementAddrInst: case ValueKind::RefTailAddrInst: case ValueKind::UncheckedTakeEnumDataAddrInst: { auto *Inst = cast(V); // We are done once the current projection dominates the insert point. if (DomTree->dominates(Inst->getParent(), InsertBefore->getParent())) return; // Move the current projection and memorize it for the next iteration. Prev = Inst; Inst->moveBefore(InsertPt); InsertPt = Inst; V = Inst->getOperand(0); continue; } default: assert(DomTree->dominates(V->getParentBlock(), InsertBefore->getParent()) && "The projected value must dominate the insertion point"); return; } } }