diff options
author | Dmitry Chestnykh <dm.chestnykh@gmail.com> | 2024-07-24 11:20:36 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-24 11:20:36 +0300 |
commit | ddf5725ef180692b60962ae56e352a7af6fc5919 (patch) | |
tree | 0ffdf6b4681920c31fdda6d34cedd5590c65280b /llvm/lib | |
parent | 3993a47bb58f7b6da9940d084e62e54a821e81fc (diff) | |
download | llvm-ddf5725ef180692b60962ae56e352a7af6fc5919.zip llvm-ddf5725ef180692b60962ae56e352a7af6fc5919.tar.gz llvm-ddf5725ef180692b60962ae56e352a7af6fc5919.tar.bz2 |
[nsan] Emit calls to optimized functions (#98900)
As previously noted in nsan.cpp we can implement
optimized variants of `__nsan_copy_values` and
`__nsan_set_value_unknown` if a memory operation
size is known.
Now the instrumentation creates calls to optimized functions if there is
4, 8 or 16-byte memory operation like
`memset(X, value, 4/8/16)` or `memcpy(dst, src, 4/8/16)`
nsan.cpp provides definitions of the optimized functions.
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/Transforms/Instrumentation/NumericalStabilitySanitizer.cpp | 120 |
1 files changed, 102 insertions, 18 deletions
diff --git a/llvm/lib/Transforms/Instrumentation/NumericalStabilitySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/NumericalStabilitySanitizer.cpp index 99b1c77..0b54f57 100644 --- a/llvm/lib/Transforms/Instrumentation/NumericalStabilitySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/NumericalStabilitySanitizer.cpp @@ -493,6 +493,60 @@ private: DenseMap<Value *, Value *> Map; }; +class NsanMemOpFn { +public: + NsanMemOpFn(Module &M, ArrayRef<StringRef> Sized, StringRef Fallback, + size_t NumArgs); + FunctionCallee getFunctionFor(uint64_t MemOpSize) const; + FunctionCallee getFallback() const; + +private: + SmallVector<FunctionCallee> Funcs; + size_t NumSizedFuncs; +}; + +NsanMemOpFn::NsanMemOpFn(Module &M, ArrayRef<StringRef> Sized, + StringRef Fallback, size_t NumArgs) { + LLVMContext &Ctx = M.getContext(); + AttributeList Attr; + Attr = Attr.addFnAttribute(Ctx, Attribute::NoUnwind); + Type *PtrTy = PointerType::getUnqual(Ctx); + Type *VoidTy = Type::getVoidTy(Ctx); + IntegerType *IntptrTy = M.getDataLayout().getIntPtrType(Ctx); + FunctionType *SizedFnTy = nullptr; + + NumSizedFuncs = Sized.size(); + + // First entry is fallback function + if (NumArgs == 3) { + Funcs.push_back( + M.getOrInsertFunction(Fallback, Attr, VoidTy, PtrTy, PtrTy, IntptrTy)); + SizedFnTy = FunctionType::get(VoidTy, {PtrTy, PtrTy}, false); + } else if (NumArgs == 2) { + Funcs.push_back( + M.getOrInsertFunction(Fallback, Attr, VoidTy, PtrTy, IntptrTy)); + SizedFnTy = FunctionType::get(VoidTy, {PtrTy}, false); + } else { + assert(!"Unexpected value of sized functions arguments"); + } + + for (size_t i = 0; i < NumSizedFuncs; ++i) + Funcs.push_back(M.getOrInsertFunction(Sized[i], SizedFnTy, Attr)); +} + +FunctionCallee NsanMemOpFn::getFunctionFor(uint64_t MemOpSize) const { + // Now `getFunctionFor` operates on `Funcs` of size 4 (at least) and the + // following code assumes that the number of functions in `Func` is sufficient + assert(NumSizedFuncs >= 3 && "Unexpected number of sized functions"); + + size_t Idx = + MemOpSize == 4 ? 1 : (MemOpSize == 8 ? 2 : (MemOpSize == 16 ? 3 : 0)); + + return Funcs[Idx]; +} + +FunctionCallee NsanMemOpFn::getFallback() const { return Funcs[0]; } + /// Instantiating NumericalStabilitySanitizer inserts the nsan runtime library /// API function declarations into the module if they don't exist already. /// Instantiating ensures the __nsan_init function is in the list of global @@ -550,12 +604,16 @@ private: LLVMContext &Context; MappingConfig Config; IntegerType *IntptrTy = nullptr; + + // TODO: Use std::array instead? FunctionCallee NsanGetShadowPtrForStore[FTValueType::kNumValueTypes] = {}; FunctionCallee NsanGetShadowPtrForLoad[FTValueType::kNumValueTypes] = {}; FunctionCallee NsanCheckValue[FTValueType::kNumValueTypes] = {}; FunctionCallee NsanFCmpFail[FTValueType::kNumValueTypes] = {}; - FunctionCallee NsanCopyValues; - FunctionCallee NsanSetValueUnknown; + + NsanMemOpFn NsanCopyFns; + NsanMemOpFn NsanSetUnknownFns; + FunctionCallee NsanGetRawShadowTypePtr; FunctionCallee NsanGetRawShadowPtr; GlobalValue *NsanShadowRetTag = nullptr; @@ -598,7 +656,14 @@ static GlobalValue *createThreadLocalGV(const char *Name, Module &M, Type *Ty) { } NumericalStabilitySanitizer::NumericalStabilitySanitizer(Module &M) - : DL(M.getDataLayout()), Context(M.getContext()), Config(Context) { + : DL(M.getDataLayout()), Context(M.getContext()), Config(Context), + NsanCopyFns(M, {"__nsan_copy_4", "__nsan_copy_8", "__nsan_copy_16"}, + "__nsan_copy_values", /*NumArgs=*/3), + NsanSetUnknownFns(M, + {"__nsan_set_value_unknown_4", + "__nsan_set_value_unknown_8", + "__nsan_set_value_unknown_16"}, + "__nsan_set_value_unknown", /*NumArgs=*/2) { IntptrTy = DL.getIntPtrType(Context); Type *PtrTy = PointerType::getUnqual(Context); Type *Int32Ty = Type::getInt32Ty(Context); @@ -634,11 +699,6 @@ NumericalStabilitySanitizer::NumericalStabilitySanitizer(Module &M) Attr, VoidTy, VTTy, VTTy, ShadowTy, ShadowTy, Int32Ty, Int1Ty, Int1Ty); } - NsanCopyValues = M.getOrInsertFunction("__nsan_copy_values", Attr, VoidTy, - PtrTy, PtrTy, IntptrTy); - NsanSetValueUnknown = M.getOrInsertFunction("__nsan_set_value_unknown", Attr, - VoidTy, PtrTy, IntptrTy); - // TODO: Add attributes nofree, nosync, readnone, readonly, NsanGetRawShadowTypePtr = M.getOrInsertFunction( "__nsan_internal_get_raw_shadow_type_ptr", Attr, PtrTy, PtrTy); @@ -1880,7 +1940,7 @@ void NumericalStabilitySanitizer::propagateNonFTStore( } } // All other stores just reset the shadow value to unknown. - Builder.CreateCall(NsanSetValueUnknown, {Dst, ValueSize}); + Builder.CreateCall(NsanSetUnknownFns.getFallback(), {Dst, ValueSize}); } void NumericalStabilitySanitizer::propagateShadowValues( @@ -2123,21 +2183,45 @@ bool NumericalStabilitySanitizer::sanitizeFunction( return !ValueToShadow.empty(); } +static uint64_t GetMemOpSize(Value *V) { + uint64_t OpSize = 0; + if (Constant *C = dyn_cast<Constant>(V)) { + auto *CInt = dyn_cast<ConstantInt>(C); + if (CInt && CInt->getValue().getBitWidth() <= 64) + OpSize = CInt->getValue().getZExtValue(); + } + + return OpSize; +} + // Instrument the memory intrinsics so that they properly modify the shadow // memory. bool NumericalStabilitySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) { IRBuilder<> Builder(MI); if (auto *M = dyn_cast<MemSetInst>(MI)) { - Builder.CreateCall( - NsanSetValueUnknown, - {/*Address=*/M->getArgOperand(0), - /*Size=*/Builder.CreateIntCast(M->getArgOperand(2), IntptrTy, false)}); + FunctionCallee SetUnknownFn = + NsanSetUnknownFns.getFunctionFor(GetMemOpSize(M->getArgOperand(2))); + if (SetUnknownFn.getFunctionType()->getNumParams() == 1) + Builder.CreateCall(SetUnknownFn, {/*Address=*/M->getArgOperand(0)}); + else + Builder.CreateCall(SetUnknownFn, + {/*Address=*/M->getArgOperand(0), + /*Size=*/Builder.CreateIntCast(M->getArgOperand(2), + IntptrTy, false)}); + } else if (auto *M = dyn_cast<MemTransferInst>(MI)) { - Builder.CreateCall( - NsanCopyValues, - {/*Destination=*/M->getArgOperand(0), - /*Source=*/M->getArgOperand(1), - /*Size=*/Builder.CreateIntCast(M->getArgOperand(2), IntptrTy, false)}); + FunctionCallee CopyFn = + NsanCopyFns.getFunctionFor(GetMemOpSize(M->getArgOperand(2))); + + if (CopyFn.getFunctionType()->getNumParams() == 2) + Builder.CreateCall(CopyFn, {/*Destination=*/M->getArgOperand(0), + /*Source=*/M->getArgOperand(1)}); + else + Builder.CreateCall(CopyFn, {/*Destination=*/M->getArgOperand(0), + /*Source=*/M->getArgOperand(1), + /*Size=*/ + Builder.CreateIntCast(M->getArgOperand(2), + IntptrTy, false)}); } return false; } |