//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// \file
// This file implements a TargetTransformInfo analysis pass specific to the
// JS target machine. It uses the target's detailed information to provide
// more precise answers to certain TTI queries, while letting the target
// independent and default TTI implementations handle the rest.
//
//===----------------------------------------------------------------------===//

#include "JSTargetTransformInfo.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/BasicTTIImpl.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/CostTable.h"
#include "llvm/Target/TargetLowering.h"
using namespace llvm;

#define DEBUG_TYPE "JStti"

void JSTTIImpl::getUnrollingPreferences(Loop *L,
                                            TTI::UnrollingPreferences &UP) {
  // We generally don't want a lot of unrolling.
  UP.Partial = false;
  UP.Runtime = false;
}

unsigned JSTTIImpl::getNumberOfRegisters(bool Vector) {
  if (Vector) return 16; // like NEON, x86_64, etc.

  return 8; // like x86, thumb, etc.
}

unsigned JSTTIImpl::getRegisterBitWidth(bool Vector) {
  if (Vector) {
    return 128;
  }

  return 32;
}

static const unsigned Nope = 65536;

// Certain types are fine, but some vector types must be avoided at all Costs.
static bool isOkType(Type *Ty) {
  if (VectorType *VTy = dyn_cast<VectorType>(Ty)) {
    if (VTy->getNumElements() != 4 || !(VTy->getElementType()->isIntegerTy(1) ||
                                        VTy->getElementType()->isIntegerTy(32) ||
                                        VTy->getElementType()->isFloatTy())) {
      return false;
    }
  }
  return true;
}

unsigned JSTTIImpl::getArithmeticInstrCost(
    unsigned Opcode, Type *Ty, TTI::OperandValueKind Opd1Info,
    TTI::OperandValueKind Opd2Info, TTI::OperandValueProperties Opd1PropInfo,
    TTI::OperandValueProperties Opd2PropInfo,
    ArrayRef<const Value*> Args) {

  unsigned Cost = BasicTTIImplBase<JSTTIImpl>::getArithmeticInstrCost(Opcode, Ty, Opd1Info,
                                                                      Opd2Info, Opd1PropInfo,
                                                                      Opd2PropInfo, Args);

  if (!isOkType(Ty))
    return Nope;

  if (VectorType *VTy = dyn_cast<VectorType>(Ty)) {
    switch (Opcode) {
      case Instruction::LShr:
      case Instruction::AShr:
      case Instruction::Shl:
        // SIMD.js' shifts are currently only ByScalar.
        if (Opd2Info != TTI::OK_UniformValue && Opd2Info != TTI::OK_UniformConstantValue)
          Cost = Cost * VTy->getNumElements() + 100;
        break;
    }
  }
  return Cost;
}

unsigned JSTTIImpl::getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) {
  if (!isOkType(Val))
    return Nope;

  unsigned Cost = BasicTTIImplBase::getVectorInstrCost(Opcode, Val, Index);

  // SIMD.js' insert/extract currently only take constant indices.
  if (Index == -1u)
    return Cost + 100;

  return Cost;
}


unsigned JSTTIImpl::getMemoryOpCost(unsigned Opcode, Type *Src, unsigned Alignment,
                                    unsigned AddressSpace) {
  if (!isOkType(Src))
    return Nope;

  return BasicTTIImplBase::getMemoryOpCost(Opcode, Src, Alignment, AddressSpace);
}

unsigned JSTTIImpl::getCastInstrCost(unsigned Opcode, Type *Dst, Type *Src) {
  if (!isOkType(Src) || !isOkType(Dst))
    return Nope;

  return BasicTTIImplBase::getCastInstrCost(Opcode, Dst, Src);
}

