//===-- AMDILInliner.cpp --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "amdilinline"
#include "AMDIL.h"
#include "AMDILCompilerErrors.h"
#include "AMDILMachineFunctionInfo.h"
#include "AMDILSubtarget.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Function.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionAnalysis.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"

using namespace llvm;

namespace
{
class LLVM_LIBRARY_VISIBILITY AMDILInlinePass : public FunctionPass

{
public:
  TargetMachine &TM;
  static char ID;
  AMDILInlinePass(TargetMachine &tm, CodeGenOpt::Level OL);
  ~AMDILInlinePass();
  virtual const char* getPassName() const;
  virtual bool runOnFunction(Function &F);
  bool doInitialization(Module &M);
  bool doFinalization(Module &M);
  virtual void getAnalysisUsage(AnalysisUsage &AU) const;
private:
  typedef DenseMap<const ArrayType*, SmallVector<AllocaInst*,
                                                 DEFAULT_VEC_SLOTS> >
  InlinedArrayAllocasTy;
  bool
  AMDILInlineCallIfPossible(CallSite CS,
                            const TargetData *TD,
                            InlinedArrayAllocasTy &InlinedArrayAllocas);
  CodeGenOpt::Level OptLevel;
};
char AMDILInlinePass::ID = 0;
} // anonymouse namespace

namespace llvm
{
FunctionPass*
createAMDILInlinePass(TargetMachine &tm, CodeGenOpt::Level OL)
{
  return new AMDILInlinePass(tm, OL);
}
} // llvm namespace

AMDILInlinePass::AMDILInlinePass(TargetMachine &tm, CodeGenOpt::Level OL)
  : FunctionPass(ID), TM(tm)
{
  OptLevel = OL;
}
AMDILInlinePass::~AMDILInlinePass()
{
}
bool
AMDILInlinePass::AMDILInlineCallIfPossible(
  CallSite CS,
  const TargetData *TD,
  InlinedArrayAllocasTy &
  InlinedArrayAllocas) {
  Function *Callee = CS.getCalledFunction();
  Function *Caller = CS.getCaller();

  // Try to inline the function.  Get the list of static allocas that were
  // inlined.
  SmallVector<AllocaInst*, 16> StaticAllocas;
  InlineFunctionInfo IFI;
  if (!InlineFunction(CS, IFI))
    return false;
  DEBUG(errs() << "<amdilinline> function " << Caller->getName()
               << ": inlined call to "<< Callee->getName() << "\n");

  // If the inlined function had a higher stack protection level than the
  // calling function, then bump up the caller's stack protection level.
  if (Callee->hasFnAttr(Attribute::StackProtectReq))
    Caller->addFnAttr(Attribute::StackProtectReq);
  else if (Callee->hasFnAttr(Attribute::StackProtect) &&
           !Caller->hasFnAttr(Attribute::StackProtectReq))
    Caller->addFnAttr(Attribute::StackProtect);

  // Look at all of the allocas that we inlined through this call site.  If we
  // have already inlined other allocas through other calls into this function,
  // then we know that they have disjoint lifetimes and that we can merge them.
  //
  // There are many heuristics possible for merging these allocas, and the
  // different options have different tradeoffs.  One thing that we *really*
  // don't want to hurt is SRoA: once inlining happens, often allocas are no
  // longer address taken and so they can be promoted.
  //
  // Our "solution" for that is to only merge allocas whose outermost type is an
  // array type.  These are usually not promoted because someone is using a
  // variable index into them.  These are also often the most important ones to
  // merge.
  //
  // A better solution would be to have real memory lifetime markers in the IR
  // and not have the inliner do any merging of allocas at all.  This would
  // allow the backend to do proper stack slot coloring of all allocas that
  // *actually make it to the backend*, which is really what we want.
  //
  // Because we don't have this information, we do this simple and useful hack.
  //
  SmallPtrSet<AllocaInst*, 16> UsedAllocas;

  // Loop over all the allocas we have so far and see if they can be merged with
  // a previously inlined alloca.  If not, remember that we had it.

  for (unsigned AllocaNo = 0,
       e = IFI.StaticAllocas.size();
       AllocaNo != e; ++AllocaNo) {
    AllocaInst *AI = IFI.StaticAllocas[AllocaNo];

    // Don't bother trying to merge array allocations (they will usually be
    // canonicalized to be an allocation *of* an array), or allocations whose
    // type is not itself an array (because we're afraid of pessimizing SRoA).
    const ArrayType *ATy = dyn_cast<ArrayType>(AI->getAllocatedType());
    if (ATy == 0 || AI->isArrayAllocation())
      continue;

    // Get the list of all available allocas for this array type.
    SmallVector<AllocaInst*, DEFAULT_VEC_SLOTS> &AllocasForType
      = InlinedArrayAllocas[ATy];

    // Loop over the allocas in AllocasForType to see if we can reuse one.  Note
    // that we have to be careful not to reuse the same "available" alloca for
    // multiple different allocas that we just inlined, we use the 'UsedAllocas'
    // set to keep track of which "available" allocas are being used by this
    // function.  Also, AllocasForType can be empty of course!
    bool MergedAwayAlloca = false;
    for (unsigned i = 0, e = AllocasForType.size(); i != e; ++i) {
      AllocaInst *AvailableAlloca = AllocasForType[i];

      // The available alloca has to be in the right function, not in some other
      // function in this SCC.
      if (AvailableAlloca->getParent() != AI->getParent())
        continue;

      // If the inlined function already uses this alloca then we can't reuse
      // it.
      if (!UsedAllocas.insert(AvailableAlloca))
        continue;

      // Otherwise, we *can* reuse it, RAUW AI into AvailableAlloca and declare
      // success!
      DEBUG(errs() << "    ***MERGED ALLOCA: " << *AI);

      AI->replaceAllUsesWith(AvailableAlloca);
      AI->eraseFromParent();
      MergedAwayAlloca = true;
      break;
    }

    // If we already nuked the alloca, we're done with it.
    if (MergedAwayAlloca)
      continue;

    // If we were unable to merge away the alloca either because there are no
    // allocas of the right type available or because we reused them all
    // already, remember that this alloca came from an inlined function and mark
    // it used so we don't reuse it for other allocas from this inline
    // operation.
    AllocasForType.push_back(AI);
    UsedAllocas.insert(AI);
  }

  return true;
}
bool
AMDILInlinePass::runOnFunction(Function &MF)
{
  Function *F = &MF;
  const AMDILSubtarget &STM = TM.getSubtarget<AMDILSubtarget>();
  if (STM.device()->isSupported(AMDILDeviceInfo::NoInline)) {
    return false;
  }
  const TargetData *TD = getAnalysisIfAvailable<TargetData>();
  SmallVector<CallSite, 16> CallSites;
  for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
    for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) {
      CallSite CS = CallSite(cast<Value>(I));
      // If this isn't a call, or it is a call to an intrinsic, it can
      // never be inlined.
      if (CS.getInstruction() == 0 || isa<IntrinsicInst>(I))
        continue;

      // If this is a direct call to an external function, we can never inline
      // it.  If it is an indirect call, inlining may resolve it to be a
      // direct call, so we keep it.
      if (CS.getCalledFunction() && CS.getCalledFunction()->isDeclaration())
        continue;

      // We don't want to inline if we are recursive.
      if (CS.getCalledFunction() && CS.getCalledFunction()->getName() ==
          MF.getName()) {
        AMDILMachineFunctionInfo *MFI =
          getAnalysis<MachineFunctionAnalysis>().getMF()
          .getInfo<AMDILMachineFunctionInfo>();
        MFI->addErrorMsg(amd::CompilerErrorMessage[RECURSIVE_FUNCTION]);
        continue;
      }

      CallSites.push_back(CS);
    }
  }

  InlinedArrayAllocasTy InlinedArrayAllocas;
  bool Changed = false;
  for (unsigned CSi = 0; CSi != CallSites.size(); ++CSi) {
    CallSite CS = CallSites[CSi];

    Function *Callee = CS.getCalledFunction();

    // We can only inline direct calls to non-declarations.
    if (Callee == 0 || Callee->isDeclaration()) continue;

    // Attempt to inline the function...
    if (!AMDILInlineCallIfPossible(CS, TD, InlinedArrayAllocas))
      continue;
    Changed = true;
  }
  return Changed;
}
const char*
AMDILInlinePass::getPassName() const
{
  return "AMDIL Inline Function Pass";
}
bool
AMDILInlinePass::doInitialization(Module &M)
{
  return false;
}
bool
AMDILInlinePass::doFinalization(Module &M)
{
  return false;
}
void
AMDILInlinePass::getAnalysisUsage(AnalysisUsage &AU) const
{
  AU.addRequired<MachineFunctionAnalysis>();
  FunctionPass::getAnalysisUsage(AU);
  AU.setPreservesAll();
}
