/// Perform the apply{partial_apply(x,y)}(z) -> apply(z,x,y) peephole
/// by iterating over all uses of the partial_apply and searching
/// for the pattern to transform.
SILInstruction *PartialApplyCombiner::combine() {
    if (!canCombinePartialApply(PAI))
        return nullptr;

    // Only process partial apply if the callee is a known function.
    FRI = dyn_cast<FunctionRefInst>(PAI->getCallee());

    // Iterate over all uses of the partial_apply
    // and look for applies that use it as a callee.
    for (auto UI = PAI->use_begin(), UE = PAI->use_end(); UI != UE; ) {
        auto Use = *UI;
        ++UI;
        auto User = Use->getUser();
        // If this use of a partial_apply is not
        // an apply which uses it as a callee, bail.
        auto AI = FullApplySite::isa(User);
        if (!AI)
            continue;

        if (AI.getCallee() != PAI)
            continue;

        // We cannot handle generic apply yet. Bail.
        if (AI.hasSubstitutions())
            continue;

        if (!processSingleApply(AI))
            return nullptr;
    }

    // release/destroy and deallocate introduced temporaries.
    if (!Tmps.empty()) {
        releaseTemporaries();
        deallocateTemporaries();
    }

    return nullptr;
}
/// Returns true on success.
bool PartialApplyCombiner::allocateTemporaries() {
    // Copy the original arguments of the partial_apply into
    // newly created temporaries and use these temporaries instead of
    // the original arguments afterwards.
    // This is done to "extend" the life-time of original partial_apply
    // arguments, as they may be destroyed/deallocated before the last
    // use by one of the apply instructions.
    // TODO:
    // Copy arguments of the partial_apply into new temporaries
    // only if the lifetime of arguments ends before their uses
    // by apply instructions.
    bool needsReleases = false;
    CanSILFunctionType PAITy =
        dyn_cast<SILFunctionType>(PAI->getCallee()->getType().getSwiftType());

    // Emit a destroy value for each captured closure argument.
    ArrayRef<SILParameterInfo> Params = PAITy->getParameters();
    auto Args = PAI->getArguments();
    unsigned Delta = Params.size() - Args.size();

    llvm::SmallVector<std::pair<SILValue, unsigned>, 8> ArgsToHandle;
    for (unsigned AI = 0, AE = Args.size(); AI != AE; ++AI) {
        SILValue Arg = Args[AI];
        SILParameterInfo Param = Params[AI + Delta];
        if (Param.isIndirectMutating())
            continue;
        // Create a temporary and copy the argument into it, if:
        // - the argument stems from an alloc_stack
        // - the argument is consumed by the callee and is indirect
        //   (e.g. it is an @in argument)
        if (isa<AllocStackInst>(Arg) ||
                (Param.isConsumed() && Param.isIndirect())) {
            // If the temporary is non-trivial, we need to release it later.
            if (!Arg->getType().isTrivial(PAI->getModule()))
                needsReleases = true;
            ArgsToHandle.push_back(std::make_pair(Arg, AI));
        }
    }

    if (needsReleases) {
        // Compute the set of endpoints, which will be used to insert releases of
        // temporaries. This may fail if the frontier is located on a critical edge
        // which we may not split (no CFG changes in SILCombine).
        ValueLifetimeAnalysis VLA(PAI);
        if (!VLA.computeFrontier(PAFrontier, ValueLifetimeAnalysis::DontModifyCFG))
            return false;
    }

    for (auto ArgWithIdx : ArgsToHandle) {
        SILValue Arg = ArgWithIdx.first;
        Builder.setInsertionPoint(PAI->getFunction()->begin()->begin());
        // Create a new temporary at the beginning of a function.
        auto *Tmp = Builder.createAllocStack(PAI->getLoc(), Arg->getType(),
        {/*Constant*/ true, ArgWithIdx.second});
        Builder.setInsertionPoint(PAI);
        // Copy argument into this temporary.
        Builder.createCopyAddr(PAI->getLoc(), Arg, Tmp,
                               IsTake_t::IsNotTake,
                               IsInitialization_t::IsInitialization);

        Tmps.push_back(Tmp);
        ArgToTmp.insert(std::make_pair(Arg, Tmp));
    }
    return true;
}
Esempio n. 3
0
static void checkNoEscapePartialApplyUse(Operand *oper, FollowUse followUses) {
  SILInstruction *user = oper->getUser();

  // Ignore uses that are totally uninteresting.
  if (isIncidentalUse(user) || onlyAffectsRefCount(user))
    return;

  // Before checking conversions in general below (getSingleValueCopyOrCast),
  // check for convert_function to [without_actually_escaping]. Assume such
  // conversion are not actually escaping without following their uses.
  if (auto *CFI = dyn_cast<ConvertFunctionInst>(user)) {
    if (CFI->withoutActuallyEscaping())
      return;
  }

  // Look through copies, borrows, and conversions.
  //
  // Note: This handles ConversionInst, which already includes everything in
  // swift::stripConvertFunctions.
  if (SingleValueInstruction *copy = getSingleValueCopyOrCast(user)) {
    // Only follow the copied operand. Other operands are incidental,
    // as in the second operand of mark_dependence.
    if (oper->getOperandNumber() == 0)
      followUses(copy);

    return;
  }

  switch (user->getKind()) {
  default:
    break;

  // Look through Optionals.
  case SILInstructionKind::EnumInst:
    // @noescape block storage can be passed as an Optional (Nullable).
    followUses(cast<EnumInst>(user));
    return;

  // Look through Phis.
  case SILInstructionKind::BranchInst: {
    const SILPhiArgument *arg = cast<BranchInst>(user)->getArgForOperand(oper);
    followUses(arg);
    return;
  }
  case SILInstructionKind::CondBranchInst: {
    const SILPhiArgument *arg =
        cast<CondBranchInst>(user)->getArgForOperand(oper);
    if (arg) // If the use isn't the branch condition, follow it.
      followUses(arg);
    return;
  }
  // Look through ObjC closures.
  case SILInstructionKind::StoreInst:
    if (oper->getOperandNumber() == StoreInst::Src) {
      if (auto *PBSI = dyn_cast<ProjectBlockStorageInst>(
            cast<StoreInst>(user)->getDest())) {
        SILValue storageAddr = PBSI->getOperand();
        // The closure is stored to block storage. Recursively visit all
        // uses of any initialized block storage values derived from this
        // storage address..
        for (Operand *oper : storageAddr->getUses()) {
          if (auto *IBS = dyn_cast<InitBlockStorageHeaderInst>(oper->getUser()))
            followUses(IBS);
        }
        return;
      }
    }
    break;

  case SILInstructionKind::IsEscapingClosureInst:
    // May be generated by withoutActuallyEscaping.
    return;

  case SILInstructionKind::PartialApplyInst: {
    // Recurse through partial_apply to handle special cases before handling
    // ApplySites in general below.
    PartialApplyInst *PAI = cast<PartialApplyInst>(user);
    // Use the same logic as checkForViolationAtApply applied to a def-use
    // traversal.
    //
    // checkForViolationAtApply recurses through partial_apply chains.
    if (oper->get() == PAI->getCallee()) {
      followUses(PAI);
      return;
    }
    // checkForViolationAtApply also uses findClosuresForAppliedArg which in
    // turn checks isPartialApplyOfReabstractionThunk.
    //
    // A closure with @inout_aliasable arguments may be applied to a
    // thunk as "escaping", but as long as the thunk is only used as a
    // '@noescape" type then it is safe.
    if (isPartialApplyOfReabstractionThunk(PAI)) {
      // Don't follow thunks that were generated by withoutActuallyEscaping.
      SILFunction *thunkDef = PAI->getReferencedFunction();
      if (!thunkDef->isWithoutActuallyEscapingThunk())
        followUses(PAI);
      return;
    }
    // Handle this use like a normal applied argument.
    break;
  }
  };

  // Handle ApplySites in general after checking PartialApply above.
  if (isa<ApplySite>(user)) {
    SILValue arg = oper->get();
    auto argumentFnType = getSILFunctionTypeForValue(arg);
    if (argumentFnType && argumentFnType->isNoEscape()) {
      // Verify that the inverse operation, finding a partial_apply from a
      // @noescape argument, is consistent.
      TinyPtrVector<PartialApplyInst *> partialApplies;
      findClosuresForFunctionValue(arg, partialApplies);
      assert(!partialApplies.empty()
             && "cannot find partial_apply from @noescape function argument");
      return;
    }
    llvm::dbgs() << "Applied argument must be @noescape function type: " << *arg;
  }
  else
    llvm::dbgs() << "Unexpected partial_apply use: " << *user;

  llvm_unreachable("A partial_apply with @inout_aliasable may only be "
                   "used as a @noescape function type argument.");
}