From: weiyu Date: Fri, 14 Jun 2019 00:41:34 +0000 (-0700) Subject: refactor codes in CDSPass.cpp and also add support for function-like atomic operation... X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11llvm.git;a=commitdiff_plain;h=b8ee0fcbdf6e8d37dc8bf48da920241dd50fe503;ds=sidebyside refactor codes in CDSPass.cpp and also add support for function-like atomic operations syntax --- diff --git a/CDSPass.cpp b/CDSPass.cpp index 4483c22..605a028 100644 --- a/CDSPass.cpp +++ b/CDSPass.cpp @@ -33,6 +33,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/DebugLoc.h" #include "llvm/Pass.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/raw_ostream.h" @@ -42,9 +43,7 @@ #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include #include -// #include "llvm/Support/MathExtras.h" #define DEBUG_TYPE "CDS" using namespace llvm; @@ -78,10 +77,12 @@ 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[FUNCARRAYSIZE]; +Constant * CDSAtomicCAS_V1[FUNCARRAYSIZE]; +Constant * CDSAtomicCAS_V2[FUNCARRAYSIZE]; Constant * CDSAtomicThreadFence; bool start = false; @@ -107,13 +108,13 @@ int getAtomicOrderIndex(AtomicOrdering order){ } int getTypeSize(Type* type) { - if (type==Int32PtrTy) { - return sizeof(int)*8; - } else if (type==Int8PtrTy) { + if (type == Int8PtrTy) { return sizeof(char)*8; - } else if (type==Int16PtrTy) { + } else if (type == Int16PtrTy) { return sizeof(short)*8; - } else if (type==Int64PtrTy) { + } else if (type == Int32PtrTy) { + return sizeof(int)*8; + } else if (type == Int64PtrTy) { return sizeof(long long int)*8; } else { return sizeof(void*)*8; @@ -141,91 +142,26 @@ namespace { private: void initializeCallbacks(Module &M); bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL); - bool instrumentAtomic(Instruction *I); + bool instrumentAtomic(Instruction *I, const DataLayout &DL); + bool instrumentAtomicCall(CallInst *CI, const DataLayout &DL); void chooseInstructionsToInstrument(SmallVectorImpl &Local, SmallVectorImpl &All, const DataLayout &DL); bool addrPointsToConstantData(Value *Addr); + int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL); }; } -void CDSPass::initializeCallbacks(Module &M) { - LLVMContext &Ctx = M.getContext(); - - Int8Ty = Type::getInt8Ty(Ctx); - Int16Ty = Type::getInt16Ty(Ctx); - Int32Ty = Type::getInt32Ty(Ctx); - Int64Ty = Type::getInt64Ty(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 < FUNCARRAYSIZE; i++) { - const unsigned ByteSize = 1U << i; - const unsigned BitSize = ByteSize * 8; -// errs() << BitSize << "\n"; - 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> 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); - CDSAtomicLoad[i] = M.getOrInsertFunction(AtomicLoadName, Ty, PtrTy, OrdTy); - CDSAtomicStore[i] = M.getOrInsertFunction(AtomicStoreName, VoidTy, PtrTy, OrdTy, Ty); - - 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, OrdTy, Ty); - } - - // only supportes strong version - SmallString<32> AtomicCASName("cds_atomic_compare_exchange" + BitSizeStr); - CDSAtomicCAS[i] = M.getOrInsertFunction(AtomicCASName, Ty, PtrTy, Ty, Ty, OrdTy, OrdTy); - } - - CDSAtomicThreadFence = M.getOrInsertFunction("cds_atomic_thread_fence", VoidTy, OrdTy); -} - static bool isVtableAccess(Instruction *I) { if (MDNode *Tag = I->getMetadata(LLVMContext::MD_tbaa)) return Tag->isTBAAVtableAccess(); return false; } +#include "initializeCallbacks.hpp" +#include "isAtomicCall.hpp" +#include "instrumentAtomicCall.hpp" + static bool shouldInstrumentReadWriteFromAddress(const Module *M, Value *Addr) { // Peel off GEPs and BitCasts. Addr = Addr->stripInBoundsOffsets(); @@ -281,11 +217,10 @@ bool CDSPass::addrPointsToConstantData(Value *Addr) { bool CDSPass::runOnFunction(Function &F) { if (F.getName() == "main") { F.setName("user_main"); -// errs() << "main replaced by user_main\n"; + errs() << "main replaced by user_main\n"; } if (true) { - initializeCallbacks( *F.getParent() ); SmallVector AllLoadsAndStores; @@ -296,17 +231,29 @@ bool CDSPass::runOnFunction(Function &F) { bool Res = false; const DataLayout &DL = F.getParent()->getDataLayout(); - + + errs() << "--- " << F.getName() << "---\n"; + for (auto &B : F) { for (auto &I : B) { - if ( (&I)->isAtomic() ) { + if ( (&I)->isAtomic() || isAtomicCall(&I) ) { AtomicAccesses.push_back(&I); + + const llvm::DebugLoc & debug_location = I.getDebugLoc(); + std::string position_string; + { + llvm::raw_string_ostream position_stream (position_string); + debug_location . print (position_stream); + } + + errs() << I << "\n" << (position_string) << "\n\n"; } else if (isa(I) || isa(I)) { LocalLoadsAndStores.push_back(&I); } else if (isa(I) || isa(I)) { // not implemented yet } } + chooseInstructionsToInstrument(LocalLoadsAndStores, AllLoadsAndStores, DL); } @@ -316,17 +263,15 @@ bool CDSPass::runOnFunction(Function &F) { } for (auto Inst : AtomicAccesses) { - Res |= instrumentAtomic(Inst); + Res |= instrumentAtomic(Inst, DL); + } + + if (F.getName() == "user_main") { + // F.dump(); } - } -/* - if (Res) { - errs() << F.getName(); - errs() << " has above instructions replaced\n"; } -*/ - + return false; } @@ -436,18 +381,22 @@ bool CDSPass::instrumentLoadOrStore(Instruction *I, return true; } - -bool CDSPass::instrumentAtomic(Instruction * I) { +// todo: replace getTypeSize with the getMemoryAccessFuncIndex +bool CDSPass::instrumentAtomic(Instruction * I, const DataLayout &DL) { IRBuilder<> IRB(I); // LLVMContext &Ctx = IRB.getContext(); + if (auto *CI = dyn_cast(I)) { + return instrumentAtomicCall(CI, DL); + } + if (StoreInst *SI = dyn_cast(I)) { 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, order, val}; + Value *args[] = {ptr, val, order}; int size=getTypeSize(ptr->getType()); int index=sizetoindex(size); @@ -474,7 +423,7 @@ bool CDSPass::instrumentAtomic(Instruction * I) { Value *val = RMWI->getValOperand(); Value *ptr = RMWI->getPointerOperand(); Value *order = ConstantInt::get(OrdTy, atomic_order_index); - Value *args[] = {ptr, order, val}; + Value *args[] = {ptr, val, order}; int size = getTypeSize(ptr->getType()); int index = sizetoindex(size); @@ -507,7 +456,7 @@ bool CDSPass::instrumentAtomic(Instruction * I) { CmpOperand, NewOperand, order_succ, order_fail}; - CallInst *funcInst = IRB.CreateCall(CDSAtomicCAS[index], Args); + CallInst *funcInst = IRB.CreateCall(CDSAtomicCAS_V1[index], Args); Value *Success = IRB.CreateICmpEQ(funcInst, CmpOperand); Value *OldVal = funcInst; @@ -535,16 +484,31 @@ bool CDSPass::instrumentAtomic(Instruction * I) { return true; } +int CDSPass::getMemoryAccessFuncIndex(Value *Addr, + const DataLayout &DL) { + Type *OrigPtrTy = Addr->getType(); + Type *OrigTy = cast(OrigPtrTy)->getElementType(); + assert(OrigTy->isSized()); + uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy); + if (TypeSize != 8 && TypeSize != 16 && + TypeSize != 32 && TypeSize != 64 && TypeSize != 128) { + // NumAccessesWithBadSize++; + // Ignore all unusual sizes. + return -1; + } + size_t Idx = countTrailingZeros(TypeSize / 8); + // assert(Idx < FUNCARRAYSIZE); + return Idx; +} char CDSPass::ID = 0; // Automatically enable the pass. -// http://adriansampson.net/blog/clangpass.html static void registerCDSPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { PM.add(new CDSPass()); } static RegisterStandardPasses - RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, + RegisterMyPass(PassManagerBuilder::EP_OptimizerLast, registerCDSPass); diff --git a/initializeCallbacks.hpp b/initializeCallbacks.hpp new file mode 100644 index 0000000..df128c0 --- /dev/null +++ b/initializeCallbacks.hpp @@ -0,0 +1,77 @@ +void CDSPass::initializeCallbacks(Module &M) { + LLVMContext &Ctx = M.getContext(); + + Type * Int1Ty = Type::getInt1Ty(Ctx); + Int8Ty = Type::getInt8Ty(Ctx); + Int16Ty = Type::getInt16Ty(Ctx); + Int32Ty = Type::getInt32Ty(Ctx); + Int64Ty = Type::getInt64Ty(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 < FUNCARRAYSIZE; 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); + CDSAtomicLoad[i] = M.getOrInsertFunction(AtomicLoadName, Ty, PtrTy, OrdTy); + CDSAtomicStore[i] = M.getOrInsertFunction(AtomicStoreName, VoidTy, PtrTy, Ty, OrdTy); + + 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); + } + + // 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); + CDSAtomicCAS_V2[i] = M.getOrInsertFunction(AtomicCASName_V2, + Int1Ty, PtrTy, PtrTy, Ty, OrdTy, OrdTy); + } + + CDSAtomicThreadFence = M.getOrInsertFunction("cds_atomic_thread_fence", VoidTy, OrdTy); +} \ No newline at end of file diff --git a/instrumentAtomicCall.hpp b/instrumentAtomicCall.hpp new file mode 100644 index 0000000..0c96df8 --- /dev/null +++ b/instrumentAtomicCall.hpp @@ -0,0 +1,152 @@ +bool containsStr() { + +} + +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); + } + + // 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}; + + 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}; + + Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args,""); + ReplaceInstWithInst(CI, funcInst); + + 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}; + + 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}; + + Instruction* funcInst=CallInst::Create(CDSAtomicRMW[op][Idx], args,""); + ReplaceInstWithInst(CI, funcInst); + + return true; + } + + /* 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}; + + Instruction* funcInst=CallInst::Create(CDSAtomicCAS_V2[Idx], args,""); + ReplaceInstWithInst(CI, funcInst); + + return true; + } + + return false; +} diff --git a/isAtomicCall.hpp b/isAtomicCall.hpp new file mode 100644 index 0000000..419312c --- /dev/null +++ b/isAtomicCall.hpp @@ -0,0 +1,39 @@ +#pragma once + +void printArgs(CallInst *); + +bool isAtomicCall(Instruction *I) +{ + if ( auto *CI = dyn_cast(I) ) { + Function *fun = CI->getCalledFunction(); + StringRef funName = fun->getName(); + + if ( (CI->isTailCall() && funName.contains("atomic_")) || + funName.contains("atomic_compare_exchange_") ) { + printArgs(CI); + 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.find("atomic_") != -1) { + 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"; + } + } + +}