//===- InstrumentMemoryAccesses.cpp - Insert load/store checks ------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass instruments loads, stores, and other memory intrinsics with
// load/store checks by inserting the relevant __loadcheck and/or
// __storecheck calls before the them.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "instrument-memory-accesses"

#include "CommonMemorySafetyPasses.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Pass.h"
#include "llvm/InstVisitor.h"
#include "llvm/Transforms/Instrumentation.h"

using namespace llvm;

STATISTIC(LoadsInstrumented, "Loads instrumented");
STATISTIC(StoresInstrumented, "Stores instrumented");
STATISTIC(AtomicsInstrumented, "Atomic memory intrinsics instrumented");
STATISTIC(IntrinsicsInstrumented, "Block memory intrinsics instrumented");

namespace {
  class InstrumentMemoryAccesses : public FunctionPass,
                                   public InstVisitor<InstrumentMemoryAccesses> {
    const DataLayout *TD;
    IRBuilder<> *Builder;

    PointerType *VoidPtrTy;
    IntegerType *SizeTy;

    Function *LoadCheckFunction;
    Function *StoreCheckFunction;

    void instrument(Value *Pointer, Value *AccessSize, Function *Check,
                    Instruction &I);

  public:
    static char ID;
    InstrumentMemoryAccesses(): FunctionPass(ID) { }
    virtual bool doInitialization(Module &M);
    virtual bool runOnFunction(Function &F);

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.addRequired<DataLayout>();
      AU.setPreservesCFG();
    }

    virtual const char *getPassName() const {
      return "InstrumentMemoryAccesses";
    }

    // Visitor methods
    void visitLoadInst(LoadInst &LI);
    void visitStoreInst(StoreInst &SI);
    void visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
    void visitAtomicRMWInst(AtomicRMWInst &I);
    void visitMemIntrinsic(MemIntrinsic &MI);
  };
} // end anon namespace

char InstrumentMemoryAccesses::ID = 0;

INITIALIZE_PASS(InstrumentMemoryAccesses, "instrument-memory-accesses",
                "Instrument memory accesses", false, false)

FunctionPass *llvm::createInstrumentMemoryAccessesPass() {
  return new InstrumentMemoryAccesses();
}

bool InstrumentMemoryAccesses::doInitialization(Module &M) {
  Type *VoidTy = Type::getVoidTy(M.getContext());
  VoidPtrTy = Type::getInt8PtrTy(M.getContext());
  SizeTy = IntegerType::getInt64Ty(M.getContext());

  // Create function prototypes
  M.getOrInsertFunction("__loadcheck", VoidTy, VoidPtrTy, SizeTy, NULL);
  M.getOrInsertFunction("__storecheck", VoidTy, VoidPtrTy, SizeTy, NULL);
  return true;
}

bool InstrumentMemoryAccesses::runOnFunction(Function &F) {
  // Check that the load and store check functions are declared.
  LoadCheckFunction = F.getParent()->getFunction("__loadcheck");
  assert(LoadCheckFunction && "__loadcheck function has disappeared!\n");

  StoreCheckFunction = F.getParent()->getFunction("__storecheck");
  assert(StoreCheckFunction && "__storecheck function has disappeared!\n");

  TD = &getAnalysis<DataLayout>();
  IRBuilder<> TheBuilder(F.getContext());
  Builder = &TheBuilder;

  // Visit all of the instructions in the function.
  visit(F);
  return true;
}

void InstrumentMemoryAccesses::instrument(Value *Pointer, Value *AccessSize,
                                          Function *Check, Instruction &I) {
  Builder->SetInsertPoint(&I);
  Value *VoidPointer = Builder->CreatePointerCast(Pointer, VoidPtrTy);
  CallInst *CI = Builder->CreateCall2(Check, VoidPointer, AccessSize);

  // Copy debug information if it is present.
  if (MDNode *MD = I.getMetadata("dbg"))
    CI->setMetadata("dbg", MD);
}

void InstrumentMemoryAccesses::visitLoadInst(LoadInst &LI) {
  // Instrument a load instruction with a load check.
  Value *AccessSize = ConstantInt::get(SizeTy,
                                       TD->getTypeStoreSize(LI.getType()));
  instrument(LI.getPointerOperand(), AccessSize, LoadCheckFunction, LI);
  ++LoadsInstrumented;
}

void InstrumentMemoryAccesses::visitStoreInst(StoreInst &SI) {
  // Instrument a store instruction with a store check.
  uint64_t Bytes = TD->getTypeStoreSize(SI.getValueOperand()->getType());
  Value *AccessSize = ConstantInt::get(SizeTy, Bytes);
  instrument(SI.getPointerOperand(), AccessSize, StoreCheckFunction, SI);
  ++StoresInstrumented;
}

void InstrumentMemoryAccesses::visitAtomicRMWInst(AtomicRMWInst &I) {
  // Instrument an AtomicRMW instruction with a store check.
  Value *AccessSize = ConstantInt::get(SizeTy,
                                       TD->getTypeStoreSize(I.getType()));
  instrument(I.getPointerOperand(), AccessSize, StoreCheckFunction, I);
  ++AtomicsInstrumented;
}

void InstrumentMemoryAccesses::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
  // Instrument an AtomicCmpXchg instruction with a store check.
  Value *AccessSize = ConstantInt::get(SizeTy,
                                       TD->getTypeStoreSize(I.getType()));
  instrument(I.getPointerOperand(), AccessSize, StoreCheckFunction, I);
  ++AtomicsInstrumented;
}

void InstrumentMemoryAccesses::visitMemIntrinsic(MemIntrinsic &MI) {
  // Instrument llvm.mem[set|cpy|move].* calls with load/store checks.
  Builder->SetInsertPoint(&MI);
  Value *AccessSize = Builder->CreateIntCast(MI.getLength(), SizeTy,
                                             /*isSigned=*/false);

  // memcpy and memmove have a source memory area but memset doesn't
  if (MemTransferInst *MTI = dyn_cast<MemTransferInst>(&MI))
    instrument(MTI->getSource(), AccessSize, LoadCheckFunction, MI);
  instrument(MI.getDest(), AccessSize, StoreCheckFunction, MI);
  ++IntrinsicsInstrumented;
}
