/// Insert monomorphic inline caches for a specific class or metatype
/// type \p SubClassTy.
static FullApplySite speculateMonomorphicTarget(FullApplySite AI,
                                                SILType SubType,
                                                CheckedCastBranchInst *&CCBI) {
  CCBI = nullptr;
  // Bail if this class_method cannot be devirtualized.
  if (!canDevirtualizeClassMethod(AI, SubType))
    return FullApplySite();

  if (SubType.getSwiftRValueType()->hasDynamicSelfType())
    return FullApplySite();

  // Create a diamond shaped control flow and a checked_cast_branch
  // instruction that checks the exact type of the object.
  // This cast selects between two paths: one that calls the slow dynamic
  // dispatch and one that calls the specific method.
  auto It = AI.getInstruction()->getIterator();
  SILFunction *F = AI.getFunction();
  SILBasicBlock *Entry = AI.getParent();

  // Iden is the basic block containing the direct call.
  SILBasicBlock *Iden = F->createBasicBlock();
  // Virt is the block containing the slow virtual call.
  SILBasicBlock *Virt = F->createBasicBlock();
  Iden->createPHIArgument(SubType, ValueOwnershipKind::Owned);

  SILBasicBlock *Continue = Entry->split(It);

  SILBuilderWithScope Builder(Entry, AI.getInstruction());
  // Create the checked_cast_branch instruction that checks at runtime if the
  // class instance is identical to the SILType.

  ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee());

  CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true,
                                       CMI->getOperand(), SubType, Iden,
                                       Virt);
  It = CCBI->getIterator();

  SILBuilderWithScope VirtBuilder(Virt, AI.getInstruction());
  SILBuilderWithScope IdenBuilder(Iden, AI.getInstruction());
  // This is the class reference downcasted into subclass SubType.
  SILValue DownCastedClassInstance = Iden->getArgument(0);

  // Copy the two apply instructions into the two blocks.
  FullApplySite IdenAI = CloneApply(AI, IdenBuilder);
  FullApplySite VirtAI = CloneApply(AI, VirtBuilder);

  // See if Continue has a release on self as the instruction right after the
  // apply. If it exists, move it into position in the diamond.
  SILBasicBlock::iterator next =
      next_or_end(Continue->begin(), Continue->end());
  auto *Release =
      (next == Continue->end()) ? nullptr : dyn_cast<StrongReleaseInst>(next);
  if (Release && Release->getOperand() == CMI->getOperand()) {
    VirtBuilder.createStrongRelease(Release->getLoc(), CMI->getOperand(),
                                    Release->getAtomicity());
    IdenBuilder.createStrongRelease(Release->getLoc(), DownCastedClassInstance,
                                    Release->getAtomicity());
    Release->eraseFromParent();
  }

  // Create a PHInode for returning the return value from both apply
  // instructions.
  SILArgument *Arg =
      Continue->createPHIArgument(AI.getType(), ValueOwnershipKind::Owned);
  if (!isa<TryApplyInst>(AI)) {
    if (AI.getSubstCalleeType()->isNoReturnFunction()) {
      IdenBuilder.createUnreachable(AI.getLoc());
      VirtBuilder.createUnreachable(AI.getLoc());
    } else {
      IdenBuilder.createBranch(AI.getLoc(), Continue,
                               { cast<ApplyInst>(IdenAI) });
      VirtBuilder.createBranch(AI.getLoc(), Continue,
                               { cast<ApplyInst>(VirtAI) });
    }
  }

  // Remove the old Apply instruction.
  assert(AI.getInstruction() == &Continue->front() &&
         "AI should be the first instruction in the split Continue block");
  if (isa<TryApplyInst>(AI)) {
    AI.getInstruction()->eraseFromParent();
    assert(Continue->empty() &&
           "There should not be an instruction after try_apply");
    Continue->eraseFromParent();
  } else {
    auto apply = cast<ApplyInst>(AI);
    apply->replaceAllUsesWith(Arg);
    apply->eraseFromParent();
    assert(!Continue->empty() &&
           "There should be at least a terminator after AI");
  }

  // Update the stats.
  NumTargetsPredicted++;

  // Devirtualize the apply instruction on the identical path.
  auto NewInstPair = devirtualizeClassMethod(IdenAI, DownCastedClassInstance);
  assert(NewInstPair.first && "Expected to be able to devirtualize apply!");
  replaceDeadApply(IdenAI, NewInstPair.first);

  // Split critical edges resulting from VirtAI.
  if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) {
    auto *ErrorBB = TAI->getFunction()->createBasicBlock();
    ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(),
                               ValueOwnershipKind::Owned);
    Builder.setInsertionPoint(ErrorBB);
    Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(),
                         {ErrorBB->getArgument(0)});

    auto *NormalBB = TAI->getFunction()->createBasicBlock();
    NormalBB->createPHIArgument(TAI->getNormalBB()->getArgument(0)->getType(),
                                ValueOwnershipKind::Owned);
    Builder.setInsertionPoint(NormalBB);
    Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(),
                         {NormalBB->getArgument(0)});

    Builder.setInsertionPoint(VirtAI.getInstruction());
    SmallVector<SILValue, 4> Args;
    for (auto Arg : VirtAI.getArguments()) {
      Args.push_back(Arg);
    }
    FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(),
        VirtAI.getSubstitutions(),
        Args, NormalBB, ErrorBB);
    VirtAI.getInstruction()->eraseFromParent();
    VirtAI = NewVirtAI;
  }

  return VirtAI;
}
Exemplo n.º 2
0
// Start with the substitutions from the apply.
// Try to propagate them to find out the real substitutions required
// to invoke the method.
static void
getSubstitutionsForCallee(SILModule &M,
                          CanSILFunctionType baseCalleeType,
                          CanType derivedSelfType,
                          FullApplySite AI,
                          SmallVectorImpl<Substitution> &newSubs) {

  // If the base method is not polymorphic, no substitutions are required,
  // even if we originally had substitutions for calling the derived method.
  if (!baseCalleeType->isPolymorphic())
    return;

  auto derivedClass = derivedSelfType;
  if (auto metatypeType = dyn_cast<MetatypeType>(derivedClass))
    derivedClass = CanType(metatypeType->getInstanceType());

  SubstitutionMap subMap;

  if (auto origCalleeSig = AI.getOrigCalleeType()->getGenericSignature()) {
    auto calleeSelfType = AI.getSubstCalleeType()->getSelfParameter().getType();
    if (auto metatypeType = dyn_cast<MetatypeType>(calleeSelfType))
      calleeSelfType = CanType(metatypeType->getInstanceType());
    auto *calleeClassDecl = calleeSelfType->getClassOrBoundGenericClass();
    assert(calleeClassDecl && "self is not a class type");

    auto origSubs = AI.getSubstitutions();

    // Add generic parameters from the method itself, ignoring any generic
    // parameters from the derived class.
    unsigned minDepth = 0;
    if (auto derivedClassSig = calleeClassDecl->getGenericSignatureOfContext())
      minDepth = derivedClassSig->getGenericParams().back()->getDepth() + 1;

    for (auto depTy : origCalleeSig->getAllDependentTypes()) {
      // Grab the next substitution.
      auto sub = origSubs.front();
      origSubs = origSubs.slice(1);

      // If the dependent type doesn't contain any generic parameter with
      // a depth of at least the minimum, skip this type.
      auto canTy = depTy->getCanonicalType();
      auto hasInnerGenericParameter = [minDepth](Type type) -> bool {
        if (auto gp = type->getAs<GenericTypeParamType>()) {
          return gp->getDepth() >= minDepth;
        }
        return false;
      };

      if (!Type(canTy.getPointer()).findIf(hasInnerGenericParameter))
        continue;

      // Otherwise, record the replacement and conformances for the mapped
      // type.
      subMap.addSubstitution(canTy, sub.getReplacement());
      subMap.addConformances(canTy, sub.getConformances());
    }
    assert(origSubs.empty());
  }

  // Add any generic substitutions for the base class.
  auto baseSelfType = baseCalleeType->getSelfParameter().getType();
  if (auto metatypeType = dyn_cast<MetatypeType>(baseSelfType))
    baseSelfType = CanType(metatypeType->getInstanceType());

  auto *baseClassDecl = baseSelfType.getClassOrBoundGenericClass();
  assert(baseClassDecl && "not a class method");

  if (auto baseClassSig = baseClassDecl->getGenericSignatureOfContext()) {
    // Compute the type of the base class, starting from the
    // derived class type and the type of the method's self
    // parameter.
    auto baseClass = derivedClass->getSuperclassForDecl(baseClassDecl, nullptr)
        ->getCanonicalType();
    auto baseClassSubs = baseClass->gatherAllSubstitutions(
        M.getSwiftModule(), nullptr);

    // Decompose the base class substitutions, adding them to the same
    // substitution maps as above.
    baseClassSig->getSubstitutionMap(baseClassSubs, subMap);
  }

  // Build the new substitutions using the base method signature.
  auto baseCalleeSig = baseCalleeType->getGenericSignature();
  baseCalleeSig->getSubstitutions(subMap, newSubs);
}