#include "AliasHelper.hpp"
#include "Utility.hpp"

#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Function.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Support/Debug.h"

#include <vector>
#include <fstream>
#include <numeric>
#include <algorithm>

using namespace llvm;

AliasHelper::AliasHelper(AliasAnalysis &AA)
:_AA(AA) {}

bool AliasHelper::must_alias(const Value* V1, size_t V1Size,
                           const Value* V2, size_t V2Size)
{
  assert(!isa<const PHINode>(V1));
  assert(!isa<const PHINode>(V2));
  
  // All incoming values (if this is a phi node) must alias
  // and all must be pointers
  if(!isa<PointerType>(V1->getType()) ||
     !isa<PointerType>(V2->getType()))
    return false;
 
  if(_AA.alias(V1, V1Size, V2, V2Size) !=
     AliasAnalysis::MustAlias)
    return false;

  return true;
}

bool AliasHelper::may_alias(const Value* V1, size_t V1Size,
                          const Value* V2, size_t V2Size)
{
  assert(!isa<const PHINode>(V1));
  assert(!isa<const PHINode>(V2));

  // True if at least one incoming value can alias
  if(!isa<PointerType>(V1->getType()) ||
     !isa<PointerType>(V2->getType()))
    return false;

  if(_AA.alias(V1, V1Size, V2, V2Size) ==
     AliasAnalysis::MayAlias)
    return true;

  return false;
}

bool AliasHelper::no_alias(const Value* V1, size_t V1Size,
                         const Value* V2, size_t V2Size)
{
  assert(!isa<const PHINode>(V1));
  assert(!isa<const PHINode>(V2));

  DOUT << "no_alias\n\n";
  DOUT << "sV1: ";
  DEBUG(V1->dump());
  DOUT << "sV2: ";
  DEBUG(V2->dump());
 

  if(V1->getType()->getTypeID() != Type::PointerTyID ||
     V2->getType()->getTypeID() != Type::PointerTyID)
    return true;

  if(V1 == V2) return false;

 
  if(_AA.alias(V1, V1Size, V2, V2Size) ==
     AliasAnalysis::NoAlias)
    return true;
      
  return false;
}

bool AliasHelper::alias(const llvm::Value * v1,
                      const llvm::Value * v2,
                      AliasType t)
{
  if(v1 == v2) return t != NoAlias;
  
  if(!isa<PointerType>(v1->getType()) ||
     !isa<PointerType>(v2->getType()))
    return t == NoAlias;
  
  v1 = normalizeValue(v1);
  v2 = normalizeValue(v2);

  // If both v1 and v2 are GEP instructions with
  // identical indices, compare their pointer
  // operands in the alias check, rather than their
  // results (after renormalize) .  Otherwise, return != noalias
  const GetElementPtrInst * gep1 = dyn_cast<const GetElementPtrInst>(v1);
  const GetElementPtrInst * gep2 = dyn_cast<const GetElementPtrInst>(v2);
  if(gep1 && gep2 && (gep1->getNumIndices() == gep2->getNumIndices()))
  {
    for(User::const_op_iterator it1 = gep1->idx_begin(),
          it2 = gep2->idx_begin(),
          ed1 = gep1->idx_end(), ed2 = gep2->idx_end();
        it1 != ed1 && it2 != ed2; ++it1, ++it2)
    {
      if(it1 != it2)
        return t != NoAlias;
    }

    v1 = normalizeValue(gep1->getPointerOperand());
    v2 = normalizeValue(gep2->getPointerOperand());
  }
  // FIXME: This might be too draconian - it will
  // deny pointers aliasing fields.
  else if(gep1 || gep2)
  {
    return t != NoAlias;
  }
  
  switch(t)
  {
  case NoAlias:
    return no_alias(v1, 0, v2, 0);
  case MayAlias:
    return may_alias(v1, 0, v2, 0);
  case MustAlias:
    return must_alias(v1, 0, v2, 0);
  }

  assert(0 && "Unmatched case");
  return false;
}

bool AliasHelper::alias(const llvm::Value * v,
                      const value_set & vs,
                      AliasType t)
{
  bool ret;
  if(t == MayAlias)
    ret = false;
  else
    ret = true;
  
  for(value_set::const_iterator it = vs.begin(), ed = vs.end();
      it != ed; ++it)
  {
    if(t == MayAlias)
      ret = ret || alias(*it, v, t);
    else
      ret = ret && alias(*it, v, t);
  }

  return ret;
}

bool AliasHelper::alias(const value_set & vs,
                      const llvm::Value * v,
                      AliasType t)
{
  return alias(v, vs, t);
}

bool AliasHelper::alias(const value_set & vs1,
                      const value_set & vs2,
                      AliasType t)
{
  bool ret;
  
  if(t == MayAlias)
    ret = false;
  else
    ret = true;
  
  for(value_set::const_iterator it = vs1.begin(), ed = vs1.end();
      it != ed; ++it)
  {
    if(t == MayAlias)
      ret = ret || alias(*it, vs2, t);
    else
      ret = ret && alias(*it, vs2, t);
  }

  return ret;
}
  
