Index: llvm/trunk/include/llvm/Transforms/Utils/Local.h =================================================================== --- llvm/trunk/include/llvm/Transforms/Utils/Local.h +++ llvm/trunk/include/llvm/Transforms/Utils/Local.h @@ -30,6 +30,7 @@ class Function; class BranchInst; class Instruction; +class CallInst; class DbgDeclareInst; class StoreInst; class LoadInst; @@ -354,6 +355,17 @@ Instruction *I, bool MatchBSwaps, bool MatchBitReversals, SmallVectorImpl &InsertedInsts); +//===----------------------------------------------------------------------===// +// Sanitizer utilities +// + +/// Given a CallInst, check if it calls a string function known to CodeGen, +/// and mark it with NoBuiltin if so. To be used by sanitizers that intend +/// to intercept string functions and want to avoid converting them to target +/// specific instructions. +void maybeMarkSanitizerLibraryCallNoBuiltin(CallInst *CI, + const TargetLibraryInfo *TLI); + } // End llvm namespace #endif Index: llvm/trunk/lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- llvm/trunk/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ llvm/trunk/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1765,6 +1765,8 @@ bool IsWrite; unsigned Alignment; uint64_t TypeSize; + const TargetLibraryInfo *TLI = + &getAnalysis().getTLI(); // Fill the set of memory operations to instrument. for (auto &BB : F) { @@ -1793,6 +1795,8 @@ TempsToInstrument.clear(); if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction()); } + if (CallInst *CI = dyn_cast(&Inst)) + maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); continue; } ToInstrument.push_back(&Inst); @@ -1805,8 +1809,6 @@ CompileKernel || (ClInstrumentationWithCallsThreshold >= 0 && ToInstrument.size() > (unsigned)ClInstrumentationWithCallsThreshold); - const TargetLibraryInfo *TLI = - &getAnalysis().getTLI(); const DataLayout &DL = F.getParent()->getDataLayout(); ObjectSizeOffsetVisitor ObjSizeVis(DL, TLI, F.getContext(), /*RoundToAlign=*/true); Index: llvm/trunk/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp =================================================================== --- llvm/trunk/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp +++ llvm/trunk/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" @@ -32,6 +33,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ModuleUtils.h" using namespace llvm; @@ -149,6 +151,7 @@ const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions()) : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {} const char *getPassName() const override; + void getAnalysisUsage(AnalysisUsage &AU) const override; bool runOnModule(Module &M) override; static char ID; @@ -199,13 +202,22 @@ } // namespace char EfficiencySanitizer::ID = 0; -INITIALIZE_PASS(EfficiencySanitizer, "esan", - "EfficiencySanitizer: finds performance issues.", false, false) +INITIALIZE_PASS_BEGIN( + EfficiencySanitizer, "esan", + "EfficiencySanitizer: finds performance issues.", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END( + EfficiencySanitizer, "esan", + "EfficiencySanitizer: finds performance issues.", false, false) const char *EfficiencySanitizer::getPassName() const { return "EfficiencySanitizer"; } +void EfficiencySanitizer::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); +} + ModulePass * llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) { return new EfficiencySanitizer(Options); @@ -544,6 +556,8 @@ SmallVector GetElementPtrs; bool Res = false; const DataLayout &DL = M.getDataLayout(); + const TargetLibraryInfo *TLI = + &getAnalysis().getTLI(); for (auto &BB : F) { for (auto &Inst : BB) { @@ -555,6 +569,8 @@ MemIntrinCalls.push_back(&Inst); else if (isa(Inst)) GetElementPtrs.push_back(&Inst); + else if (CallInst *CI = dyn_cast(&Inst)) + maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); } } Index: llvm/trunk/lib/Transforms/Instrumentation/MemorySanitizer.cpp =================================================================== --- llvm/trunk/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ llvm/trunk/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -317,6 +317,9 @@ TrackOrigins(std::max(TrackOrigins, (int)ClTrackOrigins)), WarningFn(nullptr) {} const char *getPassName() const override { return "MemorySanitizer"; } + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + } bool runOnFunction(Function &F) override; bool doInitialization(Module &M) override; static char ID; // Pass identification, replacement for typeid. @@ -384,9 +387,13 @@ } // anonymous namespace char MemorySanitizer::ID = 0; -INITIALIZE_PASS(MemorySanitizer, "msan", - "MemorySanitizer: detects uninitialized reads.", - false, false) +INITIALIZE_PASS_BEGIN( + MemorySanitizer, "msan", + "MemorySanitizer: detects uninitialized reads.", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END( + MemorySanitizer, "msan", + "MemorySanitizer: detects uninitialized reads.", false, false) FunctionPass *llvm::createMemorySanitizerPass(int TrackOrigins) { return new MemorySanitizer(TrackOrigins); @@ -618,6 +625,7 @@ SmallVector ShadowPHINodes, OriginPHINodes; ValueMap ShadowMap, OriginMap; std::unique_ptr VAHelper; + const TargetLibraryInfo *TLI; // The following flags disable parts of MSan instrumentation based on // blacklist contents and command-line options. @@ -647,6 +655,7 @@ // FIXME: Consider using SpecialCaseList to specify a list of functions that // must always return fully initialized values. For now, we hardcode "main". CheckReturnValue = SanitizeFunction && (F.getName() == "main"); + TLI = &MS.getAnalysis().getTLI(); DEBUG(if (!InsertChecks) dbgs() << "MemorySanitizer is not inserting checks into '" @@ -2529,6 +2538,8 @@ AttributeSet::FunctionIndex, B)); } + + maybeMarkSanitizerLibraryCallNoBuiltin(Call, TLI); } IRBuilder<> IRB(&I); Index: llvm/trunk/lib/Transforms/Instrumentation/ThreadSanitizer.cpp =================================================================== --- llvm/trunk/lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ llvm/trunk/lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" @@ -42,6 +43,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ModuleUtils.h" using namespace llvm; @@ -82,6 +84,7 @@ struct ThreadSanitizer : public FunctionPass { ThreadSanitizer() : FunctionPass(ID) {} const char *getPassName() const override; + void getAnalysisUsage(AnalysisUsage &AU) const override; bool runOnFunction(Function &F) override; bool doInitialization(Module &M) override; static char ID; // Pass identification, replacement for typeid. @@ -122,7 +125,13 @@ } // namespace char ThreadSanitizer::ID = 0; -INITIALIZE_PASS(ThreadSanitizer, "tsan", +INITIALIZE_PASS_BEGIN( + ThreadSanitizer, "tsan", + "ThreadSanitizer: detects data races.", + false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END( + ThreadSanitizer, "tsan", "ThreadSanitizer: detects data races.", false, false) @@ -130,6 +139,10 @@ return "ThreadSanitizer"; } +void ThreadSanitizer::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); +} + FunctionPass *llvm::createThreadSanitizerPass() { return new ThreadSanitizer(); } @@ -368,6 +381,8 @@ bool HasCalls = false; bool SanitizeFunction = F.hasFnAttribute(Attribute::SanitizeThread); const DataLayout &DL = F.getParent()->getDataLayout(); + const TargetLibraryInfo *TLI = + &getAnalysis().getTLI(); // Traverse all instructions, collect loads/stores/returns, check for calls. for (auto &BB : F) { @@ -379,6 +394,8 @@ else if (isa(Inst)) RetVec.push_back(&Inst); else if (isa(Inst) || isa(Inst)) { + if (CallInst *CI = dyn_cast(&Inst)) + maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); if (isa(Inst)) MemIntrinCalls.push_back(&Inst); HasCalls = true; Index: llvm/trunk/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/Local.cpp +++ llvm/trunk/lib/Transforms/Utils/Local.cpp @@ -1942,3 +1942,29 @@ InsertedInsts.push_back(CallInst::Create(F, Res->Provider, "rev", I)); return true; } + +// CodeGen has special handling for some string functions that may replace +// them with target-specific intrinsics. Since that'd skip our interceptors +// in ASan/MSan/TSan/DFSan, and thus make us miss some memory accesses, +// we mark affected calls as NoBuiltin, which will disable optimization +// in CodeGen. +void llvm::maybeMarkSanitizerLibraryCallNoBuiltin(CallInst *CI, + const TargetLibraryInfo *TLI) { + Function *F = CI->getCalledFunction(); + LibFunc::Func Func; + if (!F || F->hasLocalLinkage() || !F->hasName() || + !TLI->getLibFunc(F->getName(), Func)) + return; + switch (Func) { + default: break; + case LibFunc::memcmp: + case LibFunc::memchr: + case LibFunc::strcpy: + case LibFunc::stpcpy: + case LibFunc::strcmp: + case LibFunc::strlen: + case LibFunc::strnlen: + CI->addAttribute(AttributeSet::FunctionIndex, Attribute::NoBuiltin); + break; + } +} Index: llvm/trunk/test/Instrumentation/AddressSanitizer/str-nobuiltin.ll =================================================================== --- llvm/trunk/test/Instrumentation/AddressSanitizer/str-nobuiltin.ll +++ llvm/trunk/test/Instrumentation/AddressSanitizer/str-nobuiltin.ll @@ -0,0 +1,33 @@ +; Test marking string functions as nobuiltin in address sanitizer. +; +; RUN: opt < %s -asan -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +declare i8* @memchr(i8* %a, i32 %b, i64 %c) +declare i32 @memcmp(i8* %a, i8* %b, i64 %c) +declare i32 @strcmp(i8* %a, i8* %b) +declare i8* @strcpy(i8* %a, i8* %b) +declare i8* @stpcpy(i8* %a, i8* %b) +declare i64 @strlen(i8* %a) +declare i64 @strnlen(i8* %a, i64 %b) + +; CHECK: call{{.*}}@memchr{{.*}} #[[ATTR:[0-9]+]] +; CHECK: call{{.*}}@memcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@stpcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strlen{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strnlen{{.*}} #[[ATTR]] +; attributes #[[ATTR]] = { nobuiltin } + +define void @f1(i8* %a, i8* %b) nounwind uwtable sanitize_address { + tail call i8* @memchr(i8* %a, i32 1, i64 12) + tail call i32 @memcmp(i8* %a, i8* %b, i64 12) + tail call i32 @strcmp(i8* %a, i8* %b) + tail call i8* @strcpy(i8* %a, i8* %b) + tail call i8* @stpcpy(i8* %a, i8* %b) + tail call i64 @strlen(i8* %a) + tail call i64 @strnlen(i8* %a, i64 12) + ret void +} Index: llvm/trunk/test/Instrumentation/EfficiencySanitizer/str-nobuiltin.ll =================================================================== --- llvm/trunk/test/Instrumentation/EfficiencySanitizer/str-nobuiltin.ll +++ llvm/trunk/test/Instrumentation/EfficiencySanitizer/str-nobuiltin.ll @@ -0,0 +1,33 @@ +; Test marking string functions as nobuiltin in efficiency sanitizer. +; +; RUN: opt < %s -esan -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +declare i8* @memchr(i8* %a, i32 %b, i64 %c) +declare i32 @memcmp(i8* %a, i8* %b, i64 %c) +declare i32 @strcmp(i8* %a, i8* %b) +declare i8* @strcpy(i8* %a, i8* %b) +declare i8* @stpcpy(i8* %a, i8* %b) +declare i64 @strlen(i8* %a) +declare i64 @strnlen(i8* %a, i64 %b) + +; CHECK: call{{.*}}@memchr{{.*}} #[[ATTR:[0-9]+]] +; CHECK: call{{.*}}@memcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@stpcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strlen{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strnlen{{.*}} #[[ATTR]] +; attributes #[[ATTR]] = { nobuiltin } + +define void @f1(i8* %a, i8* %b) nounwind uwtable { + tail call i8* @memchr(i8* %a, i32 1, i64 12) + tail call i32 @memcmp(i8* %a, i8* %b, i64 12) + tail call i32 @strcmp(i8* %a, i8* %b) + tail call i8* @strcpy(i8* %a, i8* %b) + tail call i8* @stpcpy(i8* %a, i8* %b) + tail call i64 @strlen(i8* %a) + tail call i64 @strnlen(i8* %a, i64 12) + ret void +} Index: llvm/trunk/test/Instrumentation/MemorySanitizer/str-nobuiltin.ll =================================================================== --- llvm/trunk/test/Instrumentation/MemorySanitizer/str-nobuiltin.ll +++ llvm/trunk/test/Instrumentation/MemorySanitizer/str-nobuiltin.ll @@ -0,0 +1,33 @@ +; Test marking string functions as nobuiltin in memory sanitizer. +; +; RUN: opt < %s -msan -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +declare i8* @memchr(i8* %a, i32 %b, i64 %c) +declare i32 @memcmp(i8* %a, i8* %b, i64 %c) +declare i32 @strcmp(i8* %a, i8* %b) +declare i8* @strcpy(i8* %a, i8* %b) +declare i8* @stpcpy(i8* %a, i8* %b) +declare i64 @strlen(i8* %a) +declare i64 @strnlen(i8* %a, i64 %b) + +; CHECK: call{{.*}}@memchr{{.*}} #[[ATTR:[0-9]+]] +; CHECK: call{{.*}}@memcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@stpcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strlen{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strnlen{{.*}} #[[ATTR]] +; attributes #[[ATTR]] = { nobuiltin } + +define void @f1(i8* %a, i8* %b) nounwind uwtable sanitize_memory { + tail call i8* @memchr(i8* %a, i32 1, i64 12) + tail call i32 @memcmp(i8* %a, i8* %b, i64 12) + tail call i32 @strcmp(i8* %a, i8* %b) + tail call i8* @strcpy(i8* %a, i8* %b) + tail call i8* @stpcpy(i8* %a, i8* %b) + tail call i64 @strlen(i8* %a) + tail call i64 @strnlen(i8* %a, i64 12) + ret void +} Index: llvm/trunk/test/Instrumentation/ThreadSanitizer/str-nobuiltin.ll =================================================================== --- llvm/trunk/test/Instrumentation/ThreadSanitizer/str-nobuiltin.ll +++ llvm/trunk/test/Instrumentation/ThreadSanitizer/str-nobuiltin.ll @@ -0,0 +1,33 @@ +; Test marking string functions as nobuiltin in thread sanitizer. +; +; RUN: opt < %s -tsan -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +declare i8* @memchr(i8* %a, i32 %b, i64 %c) +declare i32 @memcmp(i8* %a, i8* %b, i64 %c) +declare i32 @strcmp(i8* %a, i8* %b) +declare i8* @strcpy(i8* %a, i8* %b) +declare i8* @stpcpy(i8* %a, i8* %b) +declare i64 @strlen(i8* %a) +declare i64 @strnlen(i8* %a, i64 %b) + +; CHECK: call{{.*}}@memchr{{.*}} #[[ATTR:[0-9]+]] +; CHECK: call{{.*}}@memcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@stpcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strlen{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strnlen{{.*}} #[[ATTR]] +; attributes #[[ATTR]] = { nobuiltin } + +define void @f1(i8* %a, i8* %b) nounwind uwtable sanitize_thread { + tail call i8* @memchr(i8* %a, i32 1, i64 12) + tail call i32 @memcmp(i8* %a, i8* %b, i64 12) + tail call i32 @strcmp(i8* %a, i8* %b) + tail call i8* @strcpy(i8* %a, i8* %b) + tail call i8* @stpcpy(i8* %a, i8* %b) + tail call i64 @strlen(i8* %a) + tail call i64 @strnlen(i8* %a, i64 12) + ret void +}