//===--- SILArgument.cpp - Arguments for high-level SIL code --------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/STLExtras.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"

using namespace swift;

//===----------------------------------------------------------------------===//
// SILArgument Implementation
//===----------------------------------------------------------------------===//

SILArgument::SILArgument(ValueKind ChildKind, SILBasicBlock *ParentBB,
                         SILType Ty, ValueOwnershipKind OwnershipKind,
                         const ValueDecl *D)
    : ValueBase(ChildKind, Ty), ParentBB(ParentBB), Decl(D),
      OwnershipKind(OwnershipKind) {
  ParentBB->insertArgument(ParentBB->args_end(), this);
}

SILArgument::SILArgument(ValueKind ChildKind, SILBasicBlock *ParentBB,
                         SILBasicBlock::arg_iterator Pos, SILType Ty,
                         ValueOwnershipKind OwnershipKind,
                         const ValueDecl *D)
    : ValueBase(ChildKind, Ty), ParentBB(ParentBB), Decl(D), OwnershipKind(OwnershipKind) {
  // Function arguments need to have a decl.
  assert(
    !ParentBB->getParent()->isBare() &&
    ParentBB->getParent()->size() == 1
          ? D != nullptr
          : true);
  ParentBB->insertArgument(Pos, this);
}


SILFunction *SILArgument::getFunction() {
  return getParent()->getParent();
}

const SILFunction *SILArgument::getFunction() const {
  return getParent()->getParent();
}

SILModule &SILArgument::getModule() const {
  return getFunction()->getModule();
}

//===----------------------------------------------------------------------===//
//                              SILBlockArgument
//===----------------------------------------------------------------------===//

static SILValue getIncomingValueForPred(const SILBasicBlock *BB,
                                        const SILBasicBlock *Pred,
                                        unsigned Index) {
  const TermInst *TI = Pred->getTerminator();

  switch (TI->getTermKind()) {
  // TODO: This list is conservative. I think we can probably handle more of
  // these.
  case TermKind::UnreachableInst:
  case TermKind::ReturnInst:
  case TermKind::ThrowInst:
    llvm_unreachable("Have terminator that implies no successors?!");
  case TermKind::TryApplyInst:
  case TermKind::SwitchValueInst:
  case TermKind::SwitchEnumAddrInst:
  case TermKind::CheckedCastAddrBranchInst:
  case TermKind::DynamicMethodBranchInst:
    return SILValue();
  case TermKind::BranchInst:
    return cast<const BranchInst>(TI)->getArg(Index);
  case TermKind::CondBranchInst:
    return cast<const CondBranchInst>(TI)->getArgForDestBB(BB, Index);
  case TermKind::CheckedCastBranchInst:
    return cast<const CheckedCastBranchInst>(TI)->getOperand();
  case TermKind::CheckedCastValueBranchInst:
    return cast<const CheckedCastValueBranchInst>(TI)->getOperand();
  case TermKind::SwitchEnumInst:
    return cast<const SwitchEnumInst>(TI)->getOperand();
  }
  llvm_unreachable("Unhandled TermKind?!");
}

SILValue SILPHIArgument::getSingleIncomingValue() const {
  const SILBasicBlock *Parent = getParent();
  const SILBasicBlock *PredBB = Parent->getSinglePredecessorBlock();
  if (!PredBB)
    return SILValue();
  return getIncomingValueForPred(Parent, PredBB, getIndex());
}

bool SILPHIArgument::getIncomingValues(
    llvm::SmallVectorImpl<SILValue> &OutArray) {
  SILBasicBlock *Parent = getParent();

  if (Parent->pred_empty())
    return false;

  unsigned Index = getIndex();
  for (SILBasicBlock *Pred : getParent()->getPredecessorBlocks()) {
    SILValue Value = getIncomingValueForPred(Parent, Pred, Index);
    if (!Value)
      return false;
    OutArray.push_back(Value);
  }

  return true;
}

bool SILPHIArgument::getIncomingValues(
    llvm::SmallVectorImpl<std::pair<SILBasicBlock *, SILValue>> &OutArray) {
  SILBasicBlock *Parent = getParent();

  if (Parent->pred_empty())
    return false;

  unsigned Index = getIndex();
  for (SILBasicBlock *Pred : getParent()->getPredecessorBlocks()) {
    SILValue Value = getIncomingValueForPred(Parent, Pred, Index);
    if (!Value)
      return false;
    OutArray.push_back({Pred, Value});
  }

  return true;
}

SILValue SILPHIArgument::getIncomingValue(unsigned BBIndex) {
  SILBasicBlock *Parent = getParent();

  if (Parent->pred_empty())
    return SILValue();

  unsigned Index = getIndex();

  // We could do an early check if the size of the pred list is <= BBIndex, but
  // that would involve walking the linked list anyways, so we just iterate once
  // over the loop.

  // We use this funky loop since predecessors are stored in a linked list but
  // we want array like semantics.
  unsigned BBCount = 0;
  for (SILBasicBlock *Pred : Parent->getPredecessorBlocks()) {
    // If BBCount is not BBIndex, continue.
    if (BBCount < BBIndex) {
      BBCount++;
      continue;
    }

    // This will return an empty SILValue if we found something we do not
    // understand.
    return getIncomingValueForPred(Parent, Pred, Index);
  }

  return SILValue();
}

SILValue SILPHIArgument::getIncomingValue(SILBasicBlock *BB) {
  SILBasicBlock *Parent = getParent();

  assert(!Parent->pred_empty() && "Passed in non-predecessor BB!");
  unsigned Index = getIndex();

  // We could do an early check if the size of the pred list is <= BBIndex, but
  // that would involve walking the linked list anyways, so we just iterate once
  // over the loop.

  auto Target = std::find(Parent->pred_begin(), Parent->pred_end(), BB);
  if (Target == Parent->pred_end())
    return SILValue();
  return getIncomingValueForPred(Parent, BB, Index);
}

//===----------------------------------------------------------------------===//
//                            SILFunctionArgument
//===----------------------------------------------------------------------===//

bool SILFunctionArgument::isSelf() const {
  // Return true if we are the last argument of our BB and that our parent
  // function has a call signature with self.
  return getFunction()->hasSelfParam() &&
         getParent()->getArguments().back() == this;
}
