X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11llvm.git;a=blobdiff_plain;f=CDSPass.cpp;h=0cebdf01ac647d65fcaf9d437ef4e6e01ce4358b;hp=886621ff7f1e6ed51a15804201f9e141a1d778b0;hb=9afe4502822f002bd97b90c2795ee7d6624e48ec;hpb=c9efd55388808675480a2f3f476959dd7e5de4be;ds=sidebyside diff --git a/CDSPass.cpp b/CDSPass.cpp index 886621f..0cebdf0 100644 --- a/CDSPass.cpp +++ b/CDSPass.cpp @@ -1,7 +1,8 @@ //===-- CDSPass.cpp - xxx -------------------------------===// // -// The LLVM Compiler Infrastructure -// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // @@ -43,15 +44,26 @@ #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include -#define DEBUG_TYPE "CDS" using namespace llvm; -#include "getPosition.hpp" - -#define FUNCARRAYSIZE 4 +#define DEBUG_TYPE "CDS" +#include + +Value *getPosition( Instruction * I, IRBuilder <> IRB) +{ + const DebugLoc & debug_location = I->getDebugLoc (); + std::string position_string; + { + llvm::raw_string_ostream position_stream (position_string); + debug_location . print (position_stream); + } + + return IRB . CreateGlobalStringPtr (position_string); +} STATISTIC(NumInstrumentedReads, "Number of instrumented reads"); STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); +STATISTIC(NumAccessesWithBadSize, "Number of accesses with bad size"); // STATISTIC(NumInstrumentedVtableWrites, "Number of vtable ptr writes"); // STATISTIC(NumInstrumentedVtableReads, "Number of vtable ptr reads"); @@ -62,10 +74,6 @@ STATISTIC(NumOmittedReadsFromConstantGlobals, STATISTIC(NumOmittedReadsFromVtable, "Number of vtable reads"); STATISTIC(NumOmittedNonCaptured, "Number of accesses ignored due to capturing"); -Type * Int8Ty; -Type * Int16Ty; -Type * Int32Ty; -Type * Int64Ty; Type * OrdTy; Type * Int8PtrTy; @@ -75,18 +83,17 @@ Type * Int64PtrTy; Type * VoidTy; -Constant * CDSLoad[FUNCARRAYSIZE]; -Constant * CDSStore[FUNCARRAYSIZE]; -Constant * CDSAtomicInit[FUNCARRAYSIZE]; -Constant * CDSAtomicLoad[FUNCARRAYSIZE]; -Constant * CDSAtomicStore[FUNCARRAYSIZE]; -Constant * CDSAtomicRMW[AtomicRMWInst::LAST_BINOP + 1][FUNCARRAYSIZE]; -Constant * CDSAtomicCAS_V1[FUNCARRAYSIZE]; -Constant * CDSAtomicCAS_V2[FUNCARRAYSIZE]; +static const size_t kNumberOfAccessSizes = 4; +Constant * CDSLoad[kNumberOfAccessSizes]; +Constant * CDSStore[kNumberOfAccessSizes]; +Constant * CDSAtomicInit[kNumberOfAccessSizes]; +Constant * CDSAtomicLoad[kNumberOfAccessSizes]; +Constant * CDSAtomicStore[kNumberOfAccessSizes]; +Constant * CDSAtomicRMW[AtomicRMWInst::LAST_BINOP + 1][kNumberOfAccessSizes]; +Constant * CDSAtomicCAS_V1[kNumberOfAccessSizes]; +Constant * CDSAtomicCAS_V2[kNumberOfAccessSizes]; Constant * CDSAtomicThreadFence; -bool start = false; - int getAtomicOrderIndex(AtomicOrdering order){ switch (order) { case AtomicOrdering::Monotonic: @@ -107,32 +114,6 @@ int getAtomicOrderIndex(AtomicOrdering order){ } } -int getTypeSize(Type* type) { - if (type == Int8PtrTy) { - return sizeof(char)*8; - } else if (type == Int16PtrTy) { - return sizeof(short)*8; - } else if (type == Int32PtrTy) { - return sizeof(int)*8; - } else if (type == Int64PtrTy) { - return sizeof(long long int)*8; - } else { - return sizeof(void*)*8; - } - - return -1; -} - -static int sizetoindex(int size) { - switch(size) { - case 8: return 0; - case 16: return 1; - case 32: return 2; - case 64: return 3; - } - return -1; -} - namespace { struct CDSPass : public FunctionPass { static char ID; @@ -158,9 +139,331 @@ static bool isVtableAccess(Instruction *I) { return false; } -#include "initializeCallbacks.hpp" -#include "isAtomicCall.hpp" -#include "instrumentAtomicCall.hpp" +void CDSPass::initializeCallbacks(Module &M) { + LLVMContext &Ctx = M.getContext(); + + Type * Int1Ty = Type::getInt1Ty(Ctx); + OrdTy = Type::getInt32Ty(Ctx); + + Int8PtrTy = Type::getInt8PtrTy(Ctx); + Int16PtrTy = Type::getInt16PtrTy(Ctx); + Int32PtrTy = Type::getInt32PtrTy(Ctx); + Int64PtrTy = Type::getInt64PtrTy(Ctx); + + VoidTy = Type::getVoidTy(Ctx); + + // Get the function to call from our untime library. + for (unsigned i = 0; i < kNumberOfAccessSizes; i++) { + const unsigned ByteSize = 1U << i; + const unsigned BitSize = ByteSize * 8; + + std::string ByteSizeStr = utostr(ByteSize); + std::string BitSizeStr = utostr(BitSize); + + Type *Ty = Type::getIntNTy(Ctx, BitSize); + Type *PtrTy = Ty->getPointerTo(); + + // uint8_t cds_atomic_load8 (void * obj, int atomic_index) + // void cds_atomic_store8 (void * obj, int atomic_index, uint8_t val) + SmallString<32> LoadName("cds_load" + BitSizeStr); + SmallString<32> StoreName("cds_store" + BitSizeStr); + SmallString<32> AtomicInitName("cds_atomic_init" + BitSizeStr); + SmallString<32> AtomicLoadName("cds_atomic_load" + BitSizeStr); + SmallString<32> AtomicStoreName("cds_atomic_store" + BitSizeStr); + + CDSLoad[i] = M.getOrInsertFunction(LoadName, VoidTy, PtrTy); + CDSStore[i] = M.getOrInsertFunction(StoreName, VoidTy, PtrTy); + CDSAtomicInit[i] = M.getOrInsertFunction(AtomicInitName, + VoidTy, PtrTy, Ty, Int8PtrTy); + CDSAtomicLoad[i] = M.getOrInsertFunction(AtomicLoadName, + Ty, PtrTy, OrdTy, Int8PtrTy); + CDSAtomicStore[i] = M.getOrInsertFunction(AtomicStoreName, + VoidTy, PtrTy, Ty, OrdTy, Int8PtrTy); + + for (int op = AtomicRMWInst::FIRST_BINOP; + op <= AtomicRMWInst::LAST_BINOP; ++op) { + CDSAtomicRMW[op][i] = nullptr; + std::string NamePart; + + if (op == AtomicRMWInst::Xchg) + NamePart = "_exchange"; + else if (op == AtomicRMWInst::Add) + NamePart = "_fetch_add"; + else if (op == AtomicRMWInst::Sub) + NamePart = "_fetch_sub"; + else if (op == AtomicRMWInst::And) + NamePart = "_fetch_and"; + else if (op == AtomicRMWInst::Or) + NamePart = "_fetch_or"; + else if (op == AtomicRMWInst::Xor) + NamePart = "_fetch_xor"; + else + continue; + + SmallString<32> AtomicRMWName("cds_atomic" + NamePart + BitSizeStr); + CDSAtomicRMW[op][i] = M.getOrInsertFunction(AtomicRMWName, + Ty, PtrTy, Ty, OrdTy, Int8PtrTy); + } + + // only supportes strong version + SmallString<32> AtomicCASName_V1("cds_atomic_compare_exchange" + BitSizeStr + "_v1"); + SmallString<32> AtomicCASName_V2("cds_atomic_compare_exchange" + BitSizeStr + "_v2"); + CDSAtomicCAS_V1[i] = M.getOrInsertFunction(AtomicCASName_V1, + Ty, PtrTy, Ty, Ty, OrdTy, OrdTy, Int8PtrTy); + CDSAtomicCAS_V2[i] = M.getOrInsertFunction(AtomicCASName_V2, + Int1Ty, PtrTy, PtrTy, Ty, OrdTy, OrdTy, Int8PtrTy); + } + + CDSAtomicThreadFence = M.getOrInsertFunction("cds_atomic_thread_fence", + VoidTy, OrdTy, Int8PtrTy); +} + +void printArgs(CallInst *); + +bool isAtomicCall(Instruction *I) { + if ( auto *CI = dyn_cast(I) ) { + Function *fun = CI->getCalledFunction(); + if (fun == NULL) + return false; + + StringRef funName = fun->getName(); + // todo: come up with better rules for function name checking + if ( funName.contains("atomic_") ) { + return true; + } else if (funName.contains("atomic") ) { + return true; + } + } + + return false; +} + +void printArgs (CallInst *CI) { + Function *fun = CI->getCalledFunction(); + StringRef funName = fun->getName(); + + User::op_iterator begin = CI->arg_begin(); + User::op_iterator end = CI->arg_end(); + + if ( funName.contains("atomic_") ) { + std::vector parameters; + + for (User::op_iterator it = begin; it != end; ++it) { + Value *param = *it; + parameters.push_back(param); + errs() << *param << " type: " << *param->getType() << "\n"; + } + } + +} + +bool CDSPass::instrumentAtomicCall(CallInst *CI, const DataLayout &DL) { + IRBuilder<> IRB(CI); + Function *fun = CI->getCalledFunction(); + StringRef funName = fun->getName(); + std::vector parameters; + + User::op_iterator begin = CI->arg_begin(); + User::op_iterator end = CI->arg_end(); + for (User::op_iterator it = begin; it != end; ++it) { + Value *param = *it; + parameters.push_back(param); + } + + // obtain source line number of the CallInst + Value *position = getPosition(CI, IRB); + + // the pointer to the address is always the first argument + Value *OrigPtr = parameters[0]; + int Idx = getMemoryAccessFuncIndex(OrigPtr, DL); + if (Idx < 0) + return false; + + const unsigned ByteSize = 1U << Idx; + const unsigned BitSize = ByteSize * 8; + Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + + // atomic_init; args = {obj, order} + if (funName.contains("atomic_init")) { + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreateBitOrPointerCast(parameters[1], Ty); + Value *args[] = {ptr, val, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicInit[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } + + // atomic_load; args = {obj, order} + if (funName.contains("atomic_load")) { + bool isExplicit = funName.contains("atomic_load_explicit"); + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *order; + if (isExplicit) + order = IRB.CreateBitOrPointerCast(parameters[1], OrdTy); + else + order = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + Value *args[] = {ptr, order, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } else if (funName.contains("atomic") && + funName.contains("load")) { + // does this version of call always have an atomic order as an argument? + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *order = IRB.CreateBitOrPointerCast(parameters[1], OrdTy); + Value *args[] = {ptr, order, position}; + + //Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args); + CallInst *funcInst = IRB.CreateCall(CDSAtomicLoad[Idx], args); + Value *RetVal = IRB.CreateIntToPtr(funcInst, CI->getType()); + + CI->replaceAllUsesWith(RetVal); + CI->eraseFromParent(); + + return true; + } + + // atomic_store; args = {obj, val, order} + if (funName.contains("atomic_store")) { + bool isExplicit = funName.contains("atomic_store_explicit"); + Value *OrigVal = parameters[1]; + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreatePointerCast(OrigVal, Ty); + Value *order; + if (isExplicit) + order = IRB.CreateBitOrPointerCast(parameters[2], OrdTy); + else + order = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + Value *args[] = {ptr, val, order, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicStore[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } else if (funName.contains("atomic") && + funName.contains("EEEE5store")) { + // does this version of call always have an atomic order as an argument? + Value *OrigVal = parameters[1]; + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreatePointerCast(OrigVal, Ty); + Value *order = IRB.CreateBitOrPointerCast(parameters[1], OrdTy); + Value *args[] = {ptr, val, order, position}; + + Instruction* funcInst = CallInst::Create(CDSAtomicStore[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } + + + // atomic_fetch_*; args = {obj, val, order} + if (funName.contains("atomic_fetch_") || + funName.contains("atomic_exchange") ) { + bool isExplicit = funName.contains("_explicit"); + Value *OrigVal = parameters[1]; + + int op; + if ( funName.contains("_fetch_add") ) + op = AtomicRMWInst::Add; + else if ( funName.contains("_fetch_sub") ) + op = AtomicRMWInst::Sub; + else if ( funName.contains("_fetch_and") ) + op = AtomicRMWInst::And; + else if ( funName.contains("_fetch_or") ) + op = AtomicRMWInst::Or; + else if ( funName.contains("_fetch_xor") ) + op = AtomicRMWInst::Xor; + else if ( funName.contains("atomic_exchange") ) + op = AtomicRMWInst::Xchg; + else { + errs() << "Unknown atomic read modify write operation\n"; + return false; + } + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreatePointerCast(OrigVal, Ty); + Value *order; + if (isExplicit) + order = IRB.CreateBitOrPointerCast(parameters[2], OrdTy); + else + order = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + Value *args[] = {ptr, val, order, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicRMW[op][Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } else if (funName.contains("fetch")) { + errs() << "atomic exchange captured. Not implemented yet. "; + errs() << "See source file :"; + getPositionPrint(CI, IRB); + } else if (funName.contains("exchange") && + !funName.contains("compare_exchange") ) { + errs() << "atomic exchange captured. Not implemented yet. "; + errs() << "See source file :"; + getPositionPrint(CI, IRB); + } + + /* atomic_compare_exchange_*; + args = {obj, expected, new value, order1, order2} + */ + if ( funName.contains("atomic_compare_exchange_") ) { + bool isExplicit = funName.contains("_explicit"); + + Value *Addr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *CmpOperand = IRB.CreatePointerCast(parameters[1], PtrTy); + Value *NewOperand = IRB.CreateBitOrPointerCast(parameters[2], Ty); + + Value *order_succ, *order_fail; + if (isExplicit) { + order_succ = IRB.CreateBitOrPointerCast(parameters[3], OrdTy); + order_fail = IRB.CreateBitOrPointerCast(parameters[4], OrdTy); + } else { + order_succ = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + order_fail = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + } + + Value *args[] = {Addr, CmpOperand, NewOperand, + order_succ, order_fail, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicCAS_V2[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } else if ( funName.contains("compare_exchange_strong") || + funName.contains("compare_exchange_weak") ) { + Value *Addr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *CmpOperand = IRB.CreatePointerCast(parameters[1], PtrTy); + Value *NewOperand = IRB.CreateBitOrPointerCast(parameters[2], Ty); + + Value *order_succ, *order_fail; + order_succ = IRB.CreateBitOrPointerCast(parameters[3], OrdTy); + order_fail = IRB.CreateBitOrPointerCast(parameters[4], OrdTy); + + Value *args[] = {Addr, CmpOperand, NewOperand, + order_succ, order_fail, position}; + Instruction* funcInst=CallInst::Create(CDSAtomicCAS_V2[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } + + + return false; +} static bool shouldInstrumentReadWriteFromAddress(const Module *M, Value *Addr) { // Peel off GEPs and BitCasts. @@ -323,8 +626,8 @@ bool CDSPass::instrumentLoadOrStore(Instruction *I, if (Addr->isSwiftError()) return false; - int size = getTypeSize(Addr->getType()); - int index = sizetoindex(size); + int Idx = getMemoryAccessFuncIndex(Addr, DL); + // not supported by CDS yet /* if (IsWrite && isVtableAccess(I)) { @@ -355,7 +658,7 @@ bool CDSPass::instrumentLoadOrStore(Instruction *I, */ Value *OnAccessFunc = nullptr; - OnAccessFunc = IsWrite ? CDSStore[index] : CDSLoad[index]; + OnAccessFunc = IsWrite ? CDSStore[Idx] : CDSLoad[Idx]; Type *ArgType = IRB.CreatePointerCast(Addr, Addr->getType())->getType(); @@ -372,7 +675,6 @@ bool CDSPass::instrumentLoadOrStore(Instruction *I, return true; } -// todo: replace getTypeSize with the getMemoryAccessFuncIndex bool CDSPass::instrumentAtomic(Instruction * I, const DataLayout &DL) { IRBuilder<> IRB(I); // LLVMContext &Ctx = IRB.getContext(); @@ -383,56 +685,39 @@ bool CDSPass::instrumentAtomic(Instruction * I, const DataLayout &DL) { Value *position = getPosition(I, IRB); - if (StoreInst *SI = dyn_cast(I)) { + if (LoadInst *LI = dyn_cast(I)) { + Value *Addr = LI->getPointerOperand(); + int Idx=getMemoryAccessFuncIndex(Addr, DL); + int atomic_order_index = getAtomicOrderIndex(LI->getOrdering()); + Value *order = ConstantInt::get(OrdTy, atomic_order_index); + Value *args[] = {Addr, order, position}; + Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args); + ReplaceInstWithInst(LI, funcInst); + } else if (StoreInst *SI = dyn_cast(I)) { + Value *Addr = SI->getPointerOperand(); + int Idx=getMemoryAccessFuncIndex(Addr, DL); int atomic_order_index = getAtomicOrderIndex(SI->getOrdering()); - Value *val = SI->getValueOperand(); - Value *ptr = SI->getPointerOperand(); Value *order = ConstantInt::get(OrdTy, atomic_order_index); - Value *args[] = {ptr, val, order, position}; - - int size=getTypeSize(ptr->getType()); - int index=sizetoindex(size); - - Instruction* funcInst=CallInst::Create(CDSAtomicStore[index], args); + Value *args[] = {Addr, val, order, position}; + Instruction* funcInst=CallInst::Create(CDSAtomicStore[Idx], args); ReplaceInstWithInst(SI, funcInst); -// errs() << "Store replaced\n"; - } else if (LoadInst *LI = dyn_cast(I)) { - int atomic_order_index = getAtomicOrderIndex(LI->getOrdering()); - - Value *ptr = LI->getPointerOperand(); - Value *order = ConstantInt::get(OrdTy, atomic_order_index); - Value *args[] = {ptr, order, position}; - - int size=getTypeSize(ptr->getType()); - int index=sizetoindex(size); - - Instruction* funcInst=CallInst::Create(CDSAtomicLoad[index], args); - ReplaceInstWithInst(LI, funcInst); -// errs() << "Load Replaced\n"; } else if (AtomicRMWInst *RMWI = dyn_cast(I)) { + Value *Addr = RMWI->getPointerOperand(); + int Idx=getMemoryAccessFuncIndex(Addr, DL); int atomic_order_index = getAtomicOrderIndex(RMWI->getOrdering()); - Value *val = RMWI->getValOperand(); - Value *ptr = RMWI->getPointerOperand(); Value *order = ConstantInt::get(OrdTy, atomic_order_index); - Value *args[] = {ptr, val, order, position}; - - int size = getTypeSize(ptr->getType()); - int index = sizetoindex(size); - - Instruction* funcInst = CallInst::Create(CDSAtomicRMW[RMWI->getOperation()][index], args); + Value *args[] = {Addr, val, order, position}; + Instruction* funcInst = CallInst::Create(CDSAtomicRMW[RMWI->getOperation()][Idx], args); ReplaceInstWithInst(RMWI, funcInst); -// errs() << RMWI->getOperationName(RMWI->getOperation()); -// errs() << " replaced\n"; } else if (AtomicCmpXchgInst *CASI = dyn_cast(I)) { IRBuilder<> IRB(CASI); Value *Addr = CASI->getPointerOperand(); + int Idx=getMemoryAccessFuncIndex(Addr, DL); - int size = getTypeSize(Addr->getType()); - int index = sizetoindex(size); - const unsigned ByteSize = 1U << index; + const unsigned ByteSize = 1U << Idx; const unsigned BitSize = ByteSize * 8; Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); Type *PtrTy = Ty->getPointerTo(); @@ -449,7 +734,7 @@ bool CDSPass::instrumentAtomic(Instruction * I, const DataLayout &DL) { CmpOperand, NewOperand, order_succ, order_fail, position}; - CallInst *funcInst = IRB.CreateCall(CDSAtomicCAS_V1[index], Args); + CallInst *funcInst = IRB.CreateCall(CDSAtomicCAS_V1[Idx], Args); Value *Success = IRB.CreateICmpEQ(funcInst, CmpOperand); Value *OldVal = funcInst; @@ -485,12 +770,12 @@ int CDSPass::getMemoryAccessFuncIndex(Value *Addr, uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy); if (TypeSize != 8 && TypeSize != 16 && TypeSize != 32 && TypeSize != 64 && TypeSize != 128) { - // NumAccessesWithBadSize++; + NumAccessesWithBadSize++; // Ignore all unusual sizes. return -1; } size_t Idx = countTrailingZeros(TypeSize / 8); - // assert(Idx < FUNCARRAYSIZE); + assert(Idx < kNumberOfAccessSizes); return Idx; }