/// Return true if it's both legal and a good idea to explode this argument.
static bool shouldExplode(ArgumentDescriptor &argDesc,
                          ConsumedArgToEpilogueReleaseMatcher &ERM) {
  // We cannot optimize the argument.
  if (!argDesc.canOptimizeLiveArg())
    return false;

  // See if the projection tree consists of potentially multiple levels of
  // structs containing one field. In such a case, there is no point in
  // exploding the argument.
  //
  // Also, in case of a type can not be exploded, e.g an enum, we treat it
  // as a singleton.
  if (argDesc.ProjTree.isSingleton())
    return false;

  auto *arg = argDesc.Arg;
  if (!shouldExpand(arg->getModule(), arg->getType().getObjectType())) {
    return false;
  }

  // If this argument is @owned and we can not find all the releases for it
  // try to explode it, maybe we can find some of the releases and O2G some
  // of its components.
  //
  // This is a potentially a very profitable optimization. Ignore other
  // heuristics.
  if (arg->hasConvention(SILArgumentConvention::Direct_Owned) &&
      ERM.hasSomeReleasesForArgument(arg))
    return true;

  unsigned explosionSize = argDesc.ProjTree.getLiveLeafCount();
  return explosionSize >= 1 && explosionSize <= 3;
}
/// Compute what the function interface will look like based on the
/// optimization we are doing on the given argument descriptor. Default
/// implementation simply passes it through.
void
FunctionSignatureTransform::
computeOptimizedArgInterface(ArgumentDescriptor &AD, SILParameterInfoList &Out) {
  // If this argument is live, but we cannot optimize it.
  if (!AD.canOptimizeLiveArg()) {
    Out.push_back(AD.PInfo);
    return;
  }

  // If we have a dead argument, bail.
  if (AD.IsEntirelyDead) {
    ++NumDeadArgsEliminated;
    return;
  }

  // Explode the argument or not ?
  if (AD.Explode) {
    ++NumSROAArguments;
    llvm::SmallVector<const ProjectionTreeNode*, 8> LeafNodes;
    AD.ProjTree.getLeafNodes(LeafNodes);
    for (auto Node : LeafNodes) {
      SILType Ty = Node->getType();
      DEBUG(llvm::dbgs() << "                " << Ty << "\n");
      // If Ty is trivial, just pass it directly.
      if (Ty.isTrivial(AD.Arg->getModule())) {
        SILParameterInfo NewInfo(Ty.getSwiftRValueType(),
                                 ParameterConvention::Direct_Unowned);
        Out.push_back(NewInfo);
        continue;
      }

      // Ty is not trivial, pass it through as the original calling convention.
      SILParameterInfo NewInfo(Ty.getSwiftRValueType(), AD.OwnedToGuaranteed ? 
                               ParameterConvention::Direct_Guaranteed : 
                               AD.PInfo.getConvention());
      Out.push_back(NewInfo);
    }
    return;
  }

  // If we cannot explode this value, handle callee release and return.
  // If we found releases in the callee in the last BB on an @owned
  // parameter, change the parameter to @guaranteed and continue...
  if (AD.OwnedToGuaranteed) {
    ++NumOwnedConvertedToGuaranteed;
    SILParameterInfo NewInfo(AD.PInfo.getType(),
                             ParameterConvention::Direct_Guaranteed);
    Out.push_back(NewInfo);
    return;
  }

  // Otherwise just propagate through the parameter info.
  Out.push_back(AD.PInfo);
}