/// \brief Populate the body of the cloned closure, modifying instructions as /// necessary. This is where we create the actual specialized BB Arguments. void ClosureSpecCloner::populateCloned() { SILFunction *Cloned = getCloned(); SILFunction *ClosureUser = CallSiteDesc.getApplyCallee(); // Create arguments for the entry block. SILBasicBlock *ClosureUserEntryBB = &*ClosureUser->begin(); SILBasicBlock *ClonedEntryBB = Cloned->createBasicBlock(); // Remove the closure argument. SILArgument *ClosureArg = nullptr; for (size_t i = 0, e = ClosureUserEntryBB->args_size(); i != e; ++i) { SILArgument *Arg = ClosureUserEntryBB->getArgument(i); if (i == CallSiteDesc.getClosureIndex()) { ClosureArg = Arg; continue; } // Otherwise, create a new argument which copies the original argument SILValue MappedValue = ClonedEntryBB->createFunctionArgument(Arg->getType(), Arg->getDecl()); ValueMap.insert(std::make_pair(Arg, MappedValue)); } // Next we need to add in any arguments that are not captured as arguments to // the cloned function. // // We do not insert the new mapped arguments into the value map since there by // definition is nothing in the partial apply user function that references // such arguments. After this pass is done the only thing that will reference // the arguments is the partial apply that we will create. SILFunction *ClosedOverFun = CallSiteDesc.getClosureCallee(); auto ClosedOverFunConv = ClosedOverFun->getConventions(); unsigned NumTotalParams = ClosedOverFunConv.getNumParameters(); unsigned NumNotCaptured = NumTotalParams - CallSiteDesc.getNumArguments(); llvm::SmallVector<SILValue, 4> NewPAIArgs; for (auto &PInfo : ClosedOverFunConv.getParameters().slice(NumNotCaptured)) { auto paramTy = ClosedOverFunConv.getSILType(PInfo); SILValue MappedValue = ClonedEntryBB->createFunctionArgument(paramTy); NewPAIArgs.push_back(MappedValue); } SILBuilder &Builder = getBuilder(); Builder.setInsertionPoint(ClonedEntryBB); // Clone FRI and PAI, and replace usage of the removed closure argument // with result of cloned PAI. SILValue FnVal = Builder.createFunctionRef(CallSiteDesc.getLoc(), ClosedOverFun); auto *NewClosure = CallSiteDesc.createNewClosure(Builder, FnVal, NewPAIArgs); // Clone a chain of ConvertFunctionInsts. SILValue ConvertedCallee = cloneCalleeConversion( CallSiteDesc.getClosureCallerArg(), NewClosure, Builder); ValueMap.insert(std::make_pair(ClosureArg, ConvertedCallee)); BBMap.insert(std::make_pair(ClosureUserEntryBB, ClonedEntryBB)); // Recursively visit original BBs in depth-first preorder, starting with the // entry block, cloning all instructions other than terminators. visitSILBasicBlock(ClosureUserEntryBB); // Now iterate over the BBs and fix up the terminators. for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) { Builder.setInsertionPoint(BI->second); visit(BI->first->getTerminator()); } // Then insert a release in all non failure exit BBs if our partial apply was // guaranteed. This is b/c it was passed at +0 originally and we need to // balance the initial increment of the newly created closure. if ((CallSiteDesc.isClosureGuaranteed() || CallSiteDesc.isTrivialNoEscapeParameter()) && CallSiteDesc.closureHasRefSemanticContext()) { for (SILBasicBlock *BB : CallSiteDesc.getNonFailureExitBBs()) { SILBasicBlock *OpBB = BBMap[BB]; TermInst *TI = OpBB->getTerminator(); auto Loc = CleanupLocation::get(NewClosure->getLoc()); // If we have a return/throw, we place the release right before it so we know // that it will be executed at the end of the epilogue. if (isa<ReturnInst>(TI)) { Builder.setInsertionPoint(TI); Builder.createReleaseValue(Loc, SILValue(NewClosure), Builder.getDefaultAtomicity()); continue; } else if (isa<ThrowInst>(TI)) { Builder.setInsertionPoint(TI); Builder.createReleaseValue(Loc, SILValue(NewClosure), Builder.getDefaultAtomicity()); continue; } // We use casts where findAllNonFailureExitBBs should have made sure that // this is true. This will ensure that the code is updated when we hit the // cast failure in debug builds. auto *Unreachable = cast<UnreachableInst>(TI); auto PrevIter = std::prev(SILBasicBlock::iterator(Unreachable)); auto NoReturnApply = FullApplySite::isa(&*PrevIter); // We insert the release value right before the no return apply so that if // the partial apply is passed into the no-return function as an @owned // value, we will retain the partial apply before we release it and // potentially eliminate it. Builder.setInsertionPoint(NoReturnApply.getInstruction()); Builder.createReleaseValue(Loc, SILValue(NewClosure), Builder.getDefaultAtomicity()); } } }
static bool matchSwitch(SwitchInfo &SI, SILInstruction *Inst, SILValue SwitchOperand) { auto *SwitchEnum = dyn_cast<SwitchEnumInst>(Inst); if (!SwitchEnum || SwitchEnum->getNumCases() != 2 || SwitchEnum->getOperand() != SwitchOperand) return false; auto *SwitchBB = SwitchEnum->getParent(); SILBasicBlock *SomeBB = SwitchEnum->getCase(0).second; SILBasicBlock *NoneBB = SwitchEnum->getCase(1).second; if (NoneBB->getSinglePredecessorBlock() != SwitchBB) return false; if (SomeBB->getSinglePredecessorBlock() != SwitchBB) return false; if (NoneBB->args_size() == 1) std::swap(NoneBB, SomeBB); if (SomeBB->args_size() != 1 || NoneBB->args_size() != 0) return false; // bb9: // %43 = enum $Optional<String>, #Optional.none!enumelt auto It = NoneBB->begin(); auto *NoneEnum = dyn_cast<EnumInst>(It); if (!NoneEnum || NoneEnum->hasOperand() || !NoneEnum->hasOneUse()) return false; // br bb10(%43 : $Optional<String>) ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Br1 = dyn_cast<BranchInst>(It); if (!Br1 || Br1->getNumArgs() != 1 || Br1->getArg(0) != NoneEnum) return false; auto *MergeBB = Br1->getDestBB(); // bb8(%36 : $NSString): It = SomeBB->begin(); auto *SomeBBArg = SomeBB->getArgument(0); if (!SomeBBArg->hasOneUse()) return false; // %37 = function_ref @$SSS10FoundationE36_unconditionallyBridgeFromObjectiveCSSSo8NSStringCSgFZ : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String auto *FunRef = dyn_cast<FunctionRefInst>(It); if (!FunRef || !FunRef->hasOneUse()) return false; // %38 = enum $Optional<NSString>, #Optional.some!enumelt.1, %36 : $NSString ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *SomeEnum = dyn_cast<EnumInst>(It); if (!SomeEnum || !SomeEnum->hasOperand() || SomeEnum->getOperand() != SomeBBArg) return false; size_t numSomeEnumUses = std::distance(SomeEnum->use_begin(), SomeEnum->use_end()); if (numSomeEnumUses > 2) return false; // %39 = metatype $@thin String.Type ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Metatype = dyn_cast<MetatypeInst>(It); if (!Metatype || !Metatype->hasOneUse()) return false; // %40 = apply %37(%38, %39) : $@convention(method) (@owned Optional<NSString>, @thin String.Type) -> @owned String ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Apply = dyn_cast<ApplyInst>(It); if (!Apply || !Apply->hasOneUse() || Apply->getCallee() != FunRef || Apply->getNumArguments() != 2 || Apply->getArgument(0) != SomeEnum || Apply->getArgument(1) != Metatype || Apply->getSubstCalleeType()->getNumResults() != 1) return false; if (Apply->getSubstCalleeType()->getSingleResult().getConvention() != ResultConvention::Owned) return false; // Check that we call the _unconditionallyBridgeFromObjectiveC witness. auto NativeType = Apply->getType().getASTType(); auto *BridgeFun = FunRef->getReferencedFunction(); auto *SwiftModule = BridgeFun->getModule().getSwiftModule(); auto bridgeWitness = getBridgeFromObjectiveC(NativeType, SwiftModule); if (BridgeFun->getName() != bridgeWitness.mangle()) return false; // %41 = enum $Optional<String>, #Optional.some!enumelt.1, %40 : $String ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Enum3 = dyn_cast<EnumInst>(It); if (!Enum3 || !Enum3->hasOneUse() || !Enum3->hasOperand() || Enum3->getOperand() != Apply) return false; if (numSomeEnumUses == 2) { // release_value %38 : $Optional<NSString> ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *RVI = dyn_cast<ReleaseValueInst>(It); if (!RVI || RVI->getOperand() != SomeEnum) return false; } // br bb10(%41 : $Optional<String>) ADVANCE_ITERATOR_OR_RETURN_FALSE(It); auto *Br = dyn_cast<BranchInst>(It); if (!Br || Br->getDestBB() != MergeBB || Br->getNumArgs() != 1 || Br->getArg(0) != Enum3) return false; SI.SwitchEnum = SwitchEnum; SI.SomeBB = SomeBB; SI.NoneBB = NoneBB; SI.Br = Br; return true; }