bool MemorySafetyChecker::runOnModule(Module& m) {
  DataLayout* dataLayout = new DataLayout(&m);
  Function* memorySafetyFunction = m.getFunction(Naming::MEMORY_SAFETY_FUNCTION);
  assert(memorySafetyFunction != NULL && "Couldn't find memory safety function");
  for (auto& F : m) {
    if (!Naming::isSmackName(F.getName())) {
      for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
        Value* pointer = NULL;
        if (LoadInst* li = dyn_cast<LoadInst>(&*I)) {
          pointer = li->getPointerOperand();
        } else if (StoreInst* si = dyn_cast<StoreInst>(&*I)) {
          pointer = si->getPointerOperand();
        }

        if (pointer) {
          // Finding the exact type of the second argument to our memory safety function
          Type* sizeType = memorySafetyFunction->getFunctionType()->getParamType(1);
          PointerType* pointerType = cast<PointerType>(pointer->getType());
          uint64_t storeSize = dataLayout->getTypeStoreSize(pointerType->getPointerElementType());
          Value* size = ConstantInt::get(sizeType, storeSize);
          Type *voidPtrTy = PointerType::getUnqual(IntegerType::getInt8Ty(F.getContext()));
          CastInst* castPointer = CastInst::Create(Instruction::BitCast, pointer, voidPtrTy, "", &*I);
          Value* args[] = {castPointer, size};
          CallInst::Create(memorySafetyFunction, ArrayRef<Value*>(args, 2), "", &*I);
        }
      }
    }
  }
  return true;
}
bool AMDGPURewriteOutArguments::isOutArgumentCandidate(Argument &Arg) const {
  const unsigned MaxOutArgSizeBytes = 4 * MaxNumRetRegs;
  PointerType *ArgTy = dyn_cast<PointerType>(Arg.getType());

  // TODO: It might be useful for any out arguments, not just privates.
  if (!ArgTy || (ArgTy->getAddressSpace() != DL->getAllocaAddrSpace() &&
                 !AnyAddressSpace) ||
      Arg.hasByValAttr() || Arg.hasStructRetAttr() ||
      DL->getTypeStoreSize(ArgTy->getPointerElementType()) > MaxOutArgSizeBytes) {
    return false;
  }

  return checkArgumentUses(Arg);
}
// Arguments: Out-of-Bound
// ConstantPointer: Not exist normally unless for NULL, NULL is OoB
// Global Values: OoB for simplicity
void PointerAnalysis::contextFreeAnalysis(Function *funct) 
{
	// transitive is the (inbound) pointers with constant, static bound
	InstSet inbounds, transitives;
	BoundMap bounds;
	bool changed = false;
	llvm::DataLayout DL(module);

	do {
	changed = false;

	for(inst_iterator i = inst_begin(funct), e = inst_end(funct); i != e; ++i) {
		Instruction* inst = &*i;
		if (isaPointer(inst)) {
			// handle a Pointer
			PointerType *pty = dyn_cast<PointerType>(inst->getType());
			Type* deRefTy = pty->getPointerElementType();
			outs() << *inst << *deRefTy << "\n";
			uint64_t deBound = sizeOf(deRefTy, DL);
			outs() << "...\n";

			bool isAlloca = isAllocation(inst);
			uint64_t bound = getConstantAllocSize(inst);


			if (isAlloca && bound) {
				bool insertB = testAndInsert(inst, inbounds);
				bool insertT = testAndInsert(inst, transitives);
				bounds[inst] = bound;
				// outs() << "alloca: " << inst->getName() << ", b=" << bound << "\n";
				changed = changed | insertB | insertT;
			} else if (isAlloca) {
				bool insertB = testAndInsert(inst, inbounds);
				// outs() << "dyn alloca: " << inst->getName() << "\n";
				changed = changed | insertB;
			}

			if ( isa<PHINode>(inst) ) {
				// possibly an inbound, but never transitive
				const PHINode *phi = dyn_cast<PHINode>(inst);
				bool allInbound = true;
				for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) {
					Value *iv = phi->getIncomingValue(i);
					if (!test(iv, inbounds)) {
						allInbound = false;
						break;
					}
				}
				if (allInbound) {
					bool insertB = testAndInsert(inst, inbounds);
					changed = changed | insertB;
				}
				
			}

			if ( isa<GetElementPtrInst>(inst) ) {
				// inbound && transitive && offset in range => inbound && transitive
				GetElementPtrInst *gep_inst = dyn_cast<GetElementPtrInst>(inst);
				Value *basePtr = gep_inst->getPointerOperand();
				uint64_t origBound = bounds[basePtr];
				APInt offset(64, false);
				bool testB = test(basePtr, inbounds);
				bool testT = test(basePtr, transitives);
				bool testC = gep_inst->accumulateConstantOffset(DL, offset);
				// should I convert to byte count?
				uint64_t intOff = offset.getLimitedValue();
				// outs() << "getelementptr: " << inst->getName() << ", offset=" << intOff << "\n";
				bool testR = testC && (origBound > 0) && (intOff + deBound <= origBound);
				if ( testB && testT && testR ) {
					bool insertB = testAndInsert(inst, inbounds);
					bool insertT = testAndInsert(inst, transitives);
					bounds[inst] = bounds[basePtr] - (intOff);
					changed = changed | insertB | insertT;
				}
			}

			if ( isa<CastInst>(inst) ) {
				// inbound && transitive && bound decreased => inbound && transitive
				CastInst *cast_inst = dyn_cast<CastInst>(inst);
				if (cast_inst->getSrcTy()->isPointerTy()) {
					Value *srcPtr = cast_inst->getOperand(0);
					bool testB = test(srcPtr, inbounds);
					bool testT = test(srcPtr, transitives);
					uint64_t origBound = bounds[srcPtr];
					bool testR = (origBound > 0) && (deBound <= origBound);
					if ( testB && testT && testR ) {
						bool insertB = testAndInsert(inst, inbounds);
						bool insertT = testAndInsert(inst, transitives);
						bounds[inst] = origBound;
						changed = changed | insertB | insertT;
					}
				}
			
			}

			// TODO: handle load/store
			// default case: nothing change
		}
	}

	} while (changed);

	outs() << funct->getName() << ":\t";
	printX(inbounds);
	contextFree[funct] = inbounds;
}