aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib
diff options
context:
space:
mode:
authorDmitry Chestnykh <dm.chestnykh@gmail.com>2024-07-24 11:20:36 +0300
committerGitHub <noreply@github.com>2024-07-24 11:20:36 +0300
commitddf5725ef180692b60962ae56e352a7af6fc5919 (patch)
tree0ffdf6b4681920c31fdda6d34cedd5590c65280b /llvm/lib
parent3993a47bb58f7b6da9940d084e62e54a821e81fc (diff)
downloadllvm-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.cpp120
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;
}