SILInstruction * SILCombiner:: visitAllocRefDynamicInst(AllocRefDynamicInst *ARDI) { SmallVector<SILValue, 4> Counts; auto getCounts = [&] (AllocRefDynamicInst *AI) -> ArrayRef<SILValue> { for (Operand &Op : AI->getTailAllocatedCounts()) { Counts.push_back(Op.get()); } return Counts; }; // %1 = metatype $X.Type // %2 = alloc_ref_dynamic %1 : $X.Type, Y // -> // alloc_ref X Builder.setCurrentDebugScope(ARDI->getDebugScope()); SILValue MDVal = ARDI->getMetatypeOperand(); if (auto *UC = dyn_cast<UpcastInst>(MDVal)) MDVal = UC->getOperand(); SingleValueInstruction *NewInst = nullptr; if (auto *MI = dyn_cast<MetatypeInst>(MDVal)) { auto &Mod = ARDI->getModule(); auto SILInstanceTy = MI->getType().getMetatypeInstanceType(Mod); NewInst = Builder.createAllocRef(ARDI->getLoc(), SILInstanceTy, ARDI->isObjC(), false, ARDI->getTailAllocatedTypes(), getCounts(ARDI)); } else if (isa<SILArgument>(MDVal)) { // checked_cast_br [exact] $Y.Type to $X.Type, bbSuccess, bbFailure // ... // bbSuccess(%T: $X.Type) // alloc_ref_dynamic %T : $X.Type, $X // -> // alloc_ref $X auto *PredBB = ARDI->getParent()->getSinglePredecessorBlock(); if (!PredBB) return nullptr; auto *CCBI = dyn_cast<CheckedCastBranchInst>(PredBB->getTerminator()); if (CCBI && CCBI->isExact() && ARDI->getParent() == CCBI->getSuccessBB()) { auto &Mod = ARDI->getModule(); auto SILInstanceTy = CCBI->getCastType().getMetatypeInstanceType(Mod); NewInst = Builder.createAllocRef(ARDI->getLoc(), SILInstanceTy, ARDI->isObjC(), false, ARDI->getTailAllocatedTypes(), getCounts(ARDI)); } } if (NewInst && NewInst->getType() != ARDI->getType()) { // In case the argument was an upcast of the metatype, we have to upcast the // resulting reference. NewInst = Builder.createUpcast(ARDI->getLoc(), NewInst, ARDI->getType()); } return NewInst; }
bool SILLoop::canDuplicate(SILInstruction *I) const { // The deallocation of a stack allocation must be in the loop, otherwise the // deallocation will be fed by a phi node of two allocations. if (I->isAllocatingStack()) { for (auto *UI : cast<SingleValueInstruction>(I)->getUses()) { if (UI->getUser()->isDeallocatingStack()) { if (!contains(UI->getUser()->getParent())) return false; } } return true; } // CodeGen can't build ssa for objc methods. if (auto *Method = dyn_cast<MethodInst>(I)) { if (Method->getMember().isForeign) { for (auto *UI : Method->getUses()) { if (!contains(UI->getUser())) return false; } } return true; } // We can't have a phi of two openexistential instructions of different UUID. if (isa<OpenExistentialAddrInst>(I) || isa<OpenExistentialRefInst>(I) || isa<OpenExistentialMetatypeInst>(I) || isa<OpenExistentialValueInst>(I) || isa<OpenExistentialBoxInst>(I) || isa<OpenExistentialBoxValueInst>(I)) { SingleValueInstruction *OI = cast<SingleValueInstruction>(I); for (auto *UI : OI->getUses()) if (!contains(UI->getUser())) return false; return true; } if (auto *Dealloc = dyn_cast<DeallocStackInst>(I)) { // The matching alloc_stack must be in the loop. if (auto *Alloc = dyn_cast<AllocStackInst>(Dealloc->getOperand())) return contains(Alloc->getParent()); return false; } if (isa<ThrowInst>(I)) return false; assert(I->isTriviallyDuplicatable() && "Code here must match isTriviallyDuplicatable in SILInstruction"); return true; }
/// Update the callsite to pass in the correct arguments. static void rewriteApplyInst(const CallSiteDescriptor &CSDesc, SILFunction *NewF) { FullApplySite AI = CSDesc.getApplyInst(); SingleValueInstruction *Closure = CSDesc.getClosure(); SILBuilderWithScope Builder(Closure); FunctionRefInst *FRI = Builder.createFunctionRef(AI.getLoc(), NewF); // Create the args for the new apply by removing the closure argument... llvm::SmallVector<SILValue, 8> NewArgs; unsigned Index = 0; for (auto Arg : AI.getArguments()) { if (Index != CSDesc.getClosureIndex()) NewArgs.push_back(Arg); Index++; } // ... and appending the captured arguments. We also insert retains here at // the location of the original closure. This is needed to balance the // implicit release of all captured arguments that occurs when the partial // apply is destroyed. SILModule &M = NewF->getModule(); auto ClosureCalleeConv = CSDesc.getClosureCallee()->getConventions(); unsigned ClosureArgIdx = ClosureCalleeConv.getNumSILArguments() - CSDesc.getNumArguments(); for (auto Arg : CSDesc.getArguments()) { SILType ArgTy = Arg->getType(); // If our argument is of trivial type, continue... if (ArgTy.isTrivial(M)) { NewArgs.push_back(Arg); ++ClosureArgIdx; continue; } auto ArgConvention = ClosureCalleeConv.getSILArgumentConvention(ClosureArgIdx); // Non-inout indirect arguments are not supported yet. assert(ArgTy.isObject() || !isNonInoutIndirectSILArgument(Arg, ArgConvention)); // If argument is not an object and it is an inout parameter, // continue... if (!ArgTy.isObject() && !isNonInoutIndirectSILArgument(Arg, ArgConvention)) { NewArgs.push_back(Arg); ++ClosureArgIdx; continue; } // TODO: When we support address types, this code path will need to be // updated. // We need to balance the consumed argument of the new partial_apply in the // specialized callee by a retain. If both the original partial_apply and // the apply of the callee are in the same basic block we can assume they // are executed the same number of times. Therefore it is sufficient to just // retain the argument at the site of the original partial_apply. // // %closure = partial_apply (%arg) // = apply %callee(%closure) // => // retain %arg // %closure = partial_apply (%arg) // apply %specialized_callee(..., %arg) // // However, if they are not in the same basic block the callee might be // executed more frequently than the closure (for example, if the closure is // created in a loop preheader and the callee taking the closure is executed // in the loop). In such a case we must keep the argument live across the // call site of the callee and emit a matching retain for every invocation // of the callee. // // %closure = partial_apply (%arg) // // while () { // = %callee(%closure) // } // => // retain %arg // %closure = partial_apply (%arg) // // while () { // retain %arg // apply %specialized_callee(.., %arg) // } // release %arg // if (AI.getParent() != Closure->getParent()) { // Emit the retain and release that keeps the argument life across the // callee using the closure. CSDesc.extendArgumentLifetime(Arg, ArgConvention); // Emit the retain that matches the captured argument by the // partial_apply // in the callee that is consumed by the partial_apply. Builder.setInsertionPoint(AI.getInstruction()); Builder.createRetainValue(Closure->getLoc(), Arg, Builder.getDefaultAtomicity()); } else { Builder.createRetainValue(Closure->getLoc(), Arg, Builder.getDefaultAtomicity()); } NewArgs.push_back(Arg); ++ClosureArgIdx; } Builder.setInsertionPoint(AI.getInstruction()); FullApplySite NewAI; switch (AI.getKind()) { case FullApplySiteKind::TryApplyInst: { auto *TAI = cast<TryApplyInst>(AI); NewAI = Builder.createTryApply(AI.getLoc(), FRI, SubstitutionMap(), NewArgs, TAI->getNormalBB(), TAI->getErrorBB()); // If we passed in the original closure as @owned, then insert a release // right after NewAI. This is to balance the +1 from being an @owned // argument to AI. if (!CSDesc.isClosureConsumed() || !CSDesc.closureHasRefSemanticContext()) { break; } Builder.setInsertionPoint(TAI->getNormalBB()->begin()); Builder.createReleaseValue(Closure->getLoc(), Closure, Builder.getDefaultAtomicity()); Builder.setInsertionPoint(TAI->getErrorBB()->begin()); Builder.createReleaseValue(Closure->getLoc(), Closure, Builder.getDefaultAtomicity()); Builder.setInsertionPoint(AI.getInstruction()); break; } case FullApplySiteKind::ApplyInst: { auto oldApply = cast<ApplyInst>(AI); auto newApply = Builder.createApply(oldApply->getLoc(), FRI, SubstitutionMap(), NewArgs, oldApply->isNonThrowing()); // If we passed in the original closure as @owned, then insert a release // right after NewAI. This is to balance the +1 from being an @owned // argument to AI. if (CSDesc.isClosureConsumed() && CSDesc.closureHasRefSemanticContext()) Builder.createReleaseValue(Closure->getLoc(), Closure, Builder.getDefaultAtomicity()); // Replace all uses of the old apply with the new apply. oldApply->replaceAllUsesWith(newApply); break; } case FullApplySiteKind::BeginApplyInst: llvm_unreachable("Unhandled case"); } // Erase the old apply. AI.getInstruction()->eraseFromParent(); // TODO: Maybe include invalidation code for CallSiteDescriptor after we erase // AI from parent? }
bool SILLoop::canDuplicate(SILInstruction *I) const { // The deallocation of a stack allocation must be in the loop, otherwise the // deallocation will be fed by a phi node of two allocations. if (I->isAllocatingStack()) { for (auto *UI : cast<SingleValueInstruction>(I)->getUses()) { if (UI->getUser()->isDeallocatingStack()) { if (!contains(UI->getUser()->getParent())) return false; } } return true; } // CodeGen can't build ssa for objc methods. if (auto *Method = dyn_cast<MethodInst>(I)) { if (Method->getMember().isForeign) { for (auto *UI : Method->getUses()) { if (!contains(UI->getUser())) return false; } } return true; } // We can't have a phi of two openexistential instructions of different UUID. if (isa<OpenExistentialAddrInst>(I) || isa<OpenExistentialRefInst>(I) || isa<OpenExistentialMetatypeInst>(I) || isa<OpenExistentialValueInst>(I) || isa<OpenExistentialBoxInst>(I) || isa<OpenExistentialBoxValueInst>(I)) { SingleValueInstruction *OI = cast<SingleValueInstruction>(I); for (auto *UI : OI->getUses()) if (!contains(UI->getUser())) return false; return true; } if (auto *Dealloc = dyn_cast<DeallocStackInst>(I)) { // The matching alloc_stack must be in the loop. if (auto *Alloc = dyn_cast<AllocStackInst>(Dealloc->getOperand())) return contains(Alloc->getParent()); return false; } // The entire coroutine execution must be within the loop. // Note that we don't have to worry about the reverse --- a loop which // contains an end_apply or abort_apply of an external begin_apply --- // because that wouldn't be structurally valid in the first place. if (auto BAI = dyn_cast<BeginApplyInst>(I)) { for (auto UI : BAI->getTokenResult()->getUses()) { auto User = UI->getUser(); assert(isa<EndApplyInst>(User) || isa<AbortApplyInst>(User)); if (!contains(User)) return false; } return true; } if (isa<ThrowInst>(I)) return false; if (isa<BeginAccessInst>(I)) return false; if (isa<DynamicMethodBranchInst>(I)) return false; if (auto *PA = dyn_cast<PartialApplyInst>(I)) return !PA->isOnStack(); assert(I->isTriviallyDuplicatable() && "Code here must match isTriviallyDuplicatable in SILInstruction"); return true; }
void SelectEnforcement::updateCapture(AddressCapture capture) { auto captureIfEscaped = [&](SILInstruction *user) { if (hasPotentiallyEscapedAt(user)) dynamicCaptures.recordCapture(capture); }; SingleValueInstruction *PAIUser = dyn_cast<PartialApplyInst>(capture.site); if (!PAIUser) { // This is a full apply site. Immediately record the capture and return. captureIfEscaped(capture.site.getInstruction()); return; } // For partial applies, check all use points of the closure. llvm::SmallSetVector<SingleValueInstruction *, 8> worklist; auto visitUse = [&](Operand *oper) { auto *user = oper->getUser(); if (FullApplySite::isa(user)) { // A call is considered a closure access regardless of whether it calls // the closure or accepts the closure as an argument. captureIfEscaped(user); return; } switch (user->getKind()) { case SILInstructionKind::ConvertEscapeToNoEscapeInst: case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::ConvertFunctionInst: case SILInstructionKind::BeginBorrowInst: case SILInstructionKind::CopyValueInst: case SILInstructionKind::EnumInst: case SILInstructionKind::StructInst: case SILInstructionKind::TupleInst: case SILInstructionKind::PartialApplyInst: // Propagate the closure. worklist.insert(cast<SingleValueInstruction>(user)); return; case SILInstructionKind::StrongRetainInst: case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::DebugValueInst: case SILInstructionKind::DestroyValueInst: case SILInstructionKind::RetainValueInst: case SILInstructionKind::ReleaseValueInst: case SILInstructionKind::EndBorrowInst: // Benign use. return; case SILInstructionKind::TupleExtractInst: case SILInstructionKind::StructExtractInst: case SILInstructionKind::AssignInst: case SILInstructionKind::BranchInst: case SILInstructionKind::CondBranchInst: case SILInstructionKind::ReturnInst: case SILInstructionKind::StoreInst: // These are all valid partial_apply users, however we don't expect them // to occur with non-escaping closures. Handle them conservatively just in // case they occur. LLVM_FALLTHROUGH; default: LLVM_DEBUG(llvm::dbgs() << " Unrecognized partial_apply user: " << *user); // Handle unknown uses conservatively by assuming a capture. captureIfEscaped(user); } }; while (true) { for (auto *oper : PAIUser->getUses()) visitUse(oper); if (worklist.empty()) break; PAIUser = worklist.pop_back_val(); } }