//===-- Int2PtrCmp.cpp - Merge inttoptr/ptrtoint --------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Remove unnecessary inttoptr casts
// Specially ones used in just compares
// Most cases derived from InstCombine
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "int2ptr-cmp"

#include "assistDS/Int2PtrCmp.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Debug.h"
#include "llvm/IR/PatternMatch.h"

#include <set>
#include <map>
#include <vector>

using namespace llvm;
using namespace PatternMatch;

//
// Method: runOnModule()
//
// Description:
//  Entry point for this LLVM pass.
//  Remove unnecessary inttoptr instructions.
//
// Inputs:
//  M - A reference to the LLVM module to transform
//
// Outputs:
//  M - The transformed LLVM module.
//
// Return value:
//  true  - The module was modified.
//  false - The module was not modified.
//
bool Int2PtrCmp::runOnModule(Module& M) {
  const DataLayout &TD = M.getDataLayout();
  for (Module::iterator F = M.begin(); F != M.end(); ++F) {
    for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) {      
      for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) {
        // ptrtoint(inttoptr ty Y) to ty -> Y 
        if(PtrToIntInst *P2I = dyn_cast<PtrToIntInst>(I++)) {
          if(IntToPtrInst *I2P = dyn_cast<IntToPtrInst>(P2I->getOperand(0))) {
            if(I2P->getSrcTy() == P2I->getDestTy()){
              P2I->replaceAllUsesWith(I2P->getOperand(0));
              P2I->eraseFromParent();
              if(I2P->use_empty()) {
                // If this is the only use of the cast delete it.
                I2P->eraseFromParent();
              }
            }
          }
        }
      }
      for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) {
        //icmp pred inttoptr(X), null  -> icmp pred X 0
        if(ICmpInst *CI = dyn_cast<ICmpInst>(I++)) {
          Value *Op0 = CI->getOperand(0);
          Value *Op1 = CI->getOperand(1);
          if (Constant *RHSC = dyn_cast<Constant>(Op1)) {
            if (Instruction *LHSI = dyn_cast<Instruction>(Op0)){
              if(LHSI->getOpcode() == Instruction::IntToPtr) {
                if (RHSC->isNullValue() &&
                    TD.getIntPtrType(RHSC->getContext(), 0) ==
                    LHSI->getOperand(0)->getType()){
                  ICmpInst *CI_new = new ICmpInst(CI, CI->getPredicate(), LHSI->getOperand(0),
                                                  Constant::getNullValue(LHSI->getOperand(0)->getType()));

                  CI->replaceAllUsesWith(CI_new);
                  CI->eraseFromParent();
                  if(LHSI->use_empty()) {
                    // If this is the only use of the cast delete it.
                    LHSI->eraseFromParent();
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  for (Module::iterator F = M.begin(); F != M.end(); ++F) {
    for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) {      
      for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) {
        if(ICmpInst *ICI = dyn_cast<ICmpInst>(I++)) {
          Value *Op0 = ICI->getOperand(0);
          Value *Op1 = ICI->getOperand(1);
          if (ConstantInt *CI = dyn_cast<ConstantInt>(Op1)) {
            // Since the RHS is a ConstantInt (CI), if the left hand side is an
            // instruction, see if that instruction also has constants so that the
            // instruction can be folded into the icmp
            if (Instruction *LHSI = dyn_cast<Instruction>(Op0)){
              if(LHSI->getOpcode() == Instruction::Or) {
                if (!ICI->isEquality() || !CI->isNullValue() || !LHSI->hasOneUse())
                  break;
                Value *P, *Q, *R;
                if (match(LHSI, m_Or(m_PtrToInt(m_Value(P)), m_PtrToInt(m_Value(Q))))) {
                  // Simplify icmp eq (or (ptrtoint P), (ptrtoint Q)), 0
                  // -> and (icmp eq P, null), (icmp eq Q, null).
                  Value *ICIP = new ICmpInst(ICI, ICI->getPredicate(), P,
                                             Constant::getNullValue(P->getType()));
                  Value *ICIQ = new ICmpInst(ICI, ICI->getPredicate(), Q,
                                             Constant::getNullValue(Q->getType()));
                  Instruction *Op;
                  if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
                    Op = BinaryOperator::CreateAnd(ICIP, ICIQ,"",ICI);
                  else
                    Op = BinaryOperator::CreateOr(ICIP, ICIQ, "", ICI);
                  ICI->replaceAllUsesWith(Op);

                } else if(match(LHSI, m_Or(m_Or(m_PtrToInt(m_Value(P)), 
                                                m_PtrToInt(m_Value(Q))), 
                                           m_PtrToInt(m_Value(R))))) {
                  // Simplify icmp eq (or (or (ptrtoint P), (ptrtoint Q)), ptrtoint(R)), 0
                  // -> and (and (icmp eq P, null), (icmp eq Q, null)), (icmp eq R, null).
                  Value *ICIP = new ICmpInst(ICI, ICI->getPredicate(), P,
                                             Constant::getNullValue(P->getType()));
                  Value *ICIQ = new ICmpInst(ICI, ICI->getPredicate(), Q,
                                             Constant::getNullValue(Q->getType()));
                  Value *ICIR = new ICmpInst(ICI, ICI->getPredicate(), R,
                                             Constant::getNullValue(R->getType()));
                  Instruction *Op;
                  if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
                    Op = BinaryOperator::CreateAnd(ICIP, ICIQ,"",ICI);
                  else
                    Op = BinaryOperator::CreateOr(ICIP, ICIQ, "", ICI);

                  if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
                    Op = BinaryOperator::CreateAnd(Op, ICIR,"",ICI);
                  else
                    Op = BinaryOperator::CreateOr(Op, ICIR, "", ICI);
                  ICI->replaceAllUsesWith(Op);

                } else if(match(LHSI, m_Or(m_PtrToInt(m_Value(Q)), m_Or(
                        m_PtrToInt(m_Value(P)), m_PtrToInt(m_Value(R)))))) {
                  // Simplify icmp eq (or  (ptrtoint P), or((ptrtoint Q), ptrtoint(R))), 0
                  // -> and (icmp eq P, null), (and (icmp eq Q, null), (icmp eq R, null)).
                  Value *ICIP = new ICmpInst(ICI, ICI->getPredicate(), P,
                                             Constant::getNullValue(P->getType()));
                  Value *ICIQ = new ICmpInst(ICI, ICI->getPredicate(), Q,
                                             Constant::getNullValue(Q->getType()));
                  Value *ICIR = new ICmpInst(ICI, ICI->getPredicate(), R,
                                             Constant::getNullValue(R->getType()));
                  Instruction *Op;
                  if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
                    Op = BinaryOperator::CreateAnd(ICIP, ICIQ,"",ICI);
                  else
                    Op = BinaryOperator::CreateOr(ICIP, ICIQ, "", ICI);

                  if (ICI->getPredicate() == ICmpInst::ICMP_EQ)
                    Op = BinaryOperator::CreateAnd(Op, ICIR,"",ICI);
                  else
                    Op = BinaryOperator::CreateOr(Op, ICIR, "", ICI);
                  ICI->replaceAllUsesWith(Op);
                }
              }
            }
          }
        }
      }
    }
  }
  return true;
}

// Pass ID variable
char Int2PtrCmp::ID = 0;

// Register the pass
static RegisterPass<Int2PtrCmp>
X("int2ptrcmp", "Simplify inttoptr/ptrtoint insts");
