llvm.org GIT mirror llvm / ce26290
[AddressSanitizer] Add support for (constant-)masked loads and stores. This patch adds support for instrumenting masked loads and stores under ASan, if they have a constant mask. isInterestingMemoryAccess now supports returning a mask to be applied to the loads, and instrumentMop will use it to generate additional checks. Added tests for v4i32 v8i32, and v4p0i32 (~v4i64) for both loads and stores (as well as a test to verify we don't add checks to non-constant masks). Differential Revision: https://reviews.llvm.org/D26230 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@287047 91177308-0d34-0410-b5e6-96231b3b80d8 Filipe Cabecinhas 4 years ago
2 changed file(s) with 212 addition(s) and 14 deletion(s). Raw diff Collapse all Expand all
502502
503503 /// If it is an interesting memory access, return the PointerOperand
504504 /// and set IsWrite/Alignment. Otherwise return nullptr.
505 /// MaybeMask is an output parameter for the mask Value, if we're looking at a
506 /// masked load/store.
505507 Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite,
506 uint64_t *TypeSize, unsigned *Alignment);
508 uint64_t *TypeSize, unsigned *Alignment,
509 Value **MaybeMask = nullptr);
507510 void instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis, Instruction *I,
508511 bool UseCalls, const DataLayout &DL);
509512 void instrumentPointerComparisonOrSubtraction(Instruction *I);
9971000 return IsInteresting;
9981001 }
9991002
1000 /// If I is an interesting memory access, return the PointerOperand
1001 /// and set IsWrite/Alignment. Otherwise return nullptr.
10021003 Value *AddressSanitizer::isInterestingMemoryAccess(Instruction *I,
10031004 bool *IsWrite,
10041005 uint64_t *TypeSize,
1005 unsigned *Alignment) {
1006 unsigned *Alignment,
1007 Value **MaybeMask) {
10061008 // Skip memory accesses inserted by another instrumentation.
10071009 if (I->getMetadata("nosanitize")) return nullptr;
10081010
10361038 *TypeSize = DL.getTypeStoreSizeInBits(XCHG->getCompareOperand()->getType());
10371039 *Alignment = 0;
10381040 PtrOperand = XCHG->getPointerOperand();
1041 } else if (auto CI = dyn_cast(I)) {
1042 auto *F = dyn_cast(CI->getCalledValue());
1043 if (F && (F->getName().startswith("llvm.masked.load.") ||
1044 F->getName().startswith("llvm.masked.store."))) {
1045 unsigned OpOffset = 0;
1046 if (F->getName().startswith("llvm.masked.store.")) {
1047 // Masked store has an initial operand for the value.
1048 OpOffset = 1;
1049 *IsWrite = true;
1050 } else {
1051 *IsWrite = false;
1052 }
1053 // Only instrument if the mask is constant for now.
1054 if (isa(CI->getOperand(2 + OpOffset))) {
1055 auto BasePtr = CI->getOperand(0 + OpOffset);
1056 auto Ty = cast(BasePtr->getType())->getElementType();
1057 *TypeSize = DL.getTypeStoreSizeInBits(Ty);
1058 if (auto AlignmentConstant =
1059 dyn_cast(CI->getOperand(1 + OpOffset)))
1060 *Alignment = (unsigned)AlignmentConstant->getZExtValue();
1061 else
1062 *Alignment = 1; // No alignment guarantees. We probably got Undef
1063 if (MaybeMask)
1064 *MaybeMask = CI->getOperand(2 + OpOffset);
1065 PtrOperand = BasePtr;
1066 }
1067 }
10391068 }
10401069
10411070 // Do not instrument acesses from different address spaces; we cannot deal
10941123 IRB.CreateCall(F, Param);
10951124 }
10961125
1126 static void doInstrumentAddress(AddressSanitizer *Pass, Instruction *I,
1127 Value *Addr, unsigned Alignment,
1128 unsigned Granularity, uint32_t TypeSize,
1129 bool IsWrite, Value *SizeArgument,
1130 bool UseCalls, uint32_t Exp) {
1131 // Instrument a 1-, 2-, 4-, 8-, or 16- byte access with one check
1132 // if the data is properly aligned.
1133 if ((TypeSize == 8 || TypeSize == 16 || TypeSize == 32 || TypeSize == 64 ||
1134 TypeSize == 128) &&
1135 (Alignment >= Granularity || Alignment == 0 || Alignment >= TypeSize / 8))
1136 return Pass->instrumentAddress(I, I, Addr, TypeSize, IsWrite, nullptr,
1137 UseCalls, Exp);
1138 Pass->instrumentUnusualSizeOrAlignment(I, Addr, TypeSize, IsWrite, nullptr,
1139 UseCalls, Exp);
1140 }
1141
1142 static void instrumentMaskedLoadOrStore(AddressSanitizer *Pass,
1143 const DataLayout &DL, Type *IntptrTy,
1144 ConstantVector *Mask, Instruction *I,
1145 Value *Addr, unsigned Alignment,
1146 unsigned Granularity, uint32_t TypeSize,
1147 bool IsWrite, Value *SizeArgument,
1148 bool UseCalls, uint32_t Exp) {
1149 auto *VTy = cast(Addr->getType())->getElementType();
1150 uint64_t ElemTypeSize = DL.getTypeStoreSizeInBits(VTy->getScalarType());
1151 unsigned Num = VTy->getVectorNumElements();
1152 auto Zero = ConstantInt::get(IntptrTy, 0);
1153 for (unsigned Idx = 0; Idx < Num; ++Idx) {
1154 // dyn_cast as we might get UndefValue
1155 auto Masked = dyn_cast(Mask->getOperand(Idx));
1156 if (Masked && Masked->isAllOnesValue()) {
1157 IRBuilder<> IRB(I);
1158 auto InstrumentedAddress =
1159 IRB.CreateGEP(Addr, {Zero, ConstantInt::get(IntptrTy, Idx)});
1160 doInstrumentAddress(Pass, I, InstrumentedAddress, Alignment, Granularity,
1161 ElemTypeSize, IsWrite, SizeArgument, UseCalls, Exp);
1162 }
1163 }
1164 }
1165
10971166 void AddressSanitizer::instrumentMop(ObjectSizeOffsetVisitor &ObjSizeVis,
10981167 Instruction *I, bool UseCalls,
10991168 const DataLayout &DL) {
11001169 bool IsWrite = false;
11011170 unsigned Alignment = 0;
11021171 uint64_t TypeSize = 0;
1103 Value *Addr = isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment);
1172 Value *MaybeMask = nullptr;
1173 Value *Addr =
1174 isInterestingMemoryAccess(I, &IsWrite, &TypeSize, &Alignment, &MaybeMask);
11041175 assert(Addr);
11051176
11061177 // Optimization experiments.
11421213 NumInstrumentedReads++;
11431214
11441215 unsigned Granularity = 1 << Mapping.Scale;
1145 // Instrument a 1-, 2-, 4-, 8-, or 16- byte access with one check
1146 // if the data is properly aligned.
1147 if ((TypeSize == 8 || TypeSize == 16 || TypeSize == 32 || TypeSize == 64 ||
1148 TypeSize == 128) &&
1149 (Alignment >= Granularity || Alignment == 0 || Alignment >= TypeSize / 8))
1150 return instrumentAddress(I, I, Addr, TypeSize, IsWrite, nullptr, UseCalls,
1151 Exp);
1152 instrumentUnusualSizeOrAlignment(I, Addr, TypeSize, IsWrite, nullptr,
1153 UseCalls, Exp);
1216 if (MaybeMask) {
1217 auto Mask = cast(MaybeMask);
1218 instrumentMaskedLoadOrStore(this, DL, IntptrTy, Mask, I, Addr, Alignment,
1219 Granularity, TypeSize, IsWrite, nullptr,
1220 UseCalls, Exp);
1221 } else {
1222 doInstrumentAddress(this, I, Addr, Alignment, Granularity, TypeSize,
1223 IsWrite, nullptr, UseCalls, Exp);
1224 }
11541225 }
11551226
11561227 Instruction *AddressSanitizer::generateCrashCode(Instruction *InsertBefore,
0 ; RUN: opt < %s -asan -asan-instrumentation-with-call-threshold=0 -S | FileCheck %s
1 ; Support ASan instrumentation for constant-mask llvm.masked.{load,store}
2
3 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
4
5 @v4f32 = global <4 x float>* zeroinitializer, align 8
6 @v8i32 = global <8 x i32>* zeroinitializer, align 8
7 @v4i64 = global <4 x i32*>* zeroinitializer, align 8
8
9 ;;;;;;;;;;;;;;;; STORE
10 declare void @llvm.masked.store.v4f32.p0v4f32(<4 x float>, <4 x float>*, i32, <4 x i1>) argmemonly nounwind
11 declare void @llvm.masked.store.v8i32.p0v8i32(<8 x i32>, <8 x i32>*, i32, <8 x i1>) argmemonly nounwind
12 declare void @llvm.masked.store.v4p0i32.p0v4p0i32(<4 x i32*>, <4 x i32*>*, i32, <4 x i1>) argmemonly nounwind
13
14 define void @store.v4f32.1110(<4 x float> %arg) sanitize_address {
15 ; CHECK-LABEL: @store.v4f32.1110
16 %p = load <4 x float>*, <4 x float>** @v4f32, align 8
17 ; CHECK: [[GEP0:%[0-9A-Za-z]+]] = getelementptr <4 x float>, <4 x float>* %p, i64 0, i64 0
18 ; CHECK: [[PGEP0:%[0-9A-Za-z]+]] = ptrtoint float* [[GEP0]] to i64
19 ; CHECK: call void @__asan_store4(i64 [[PGEP0]])
20 ; CHECK: [[GEP1:%[0-9A-Za-z]+]] = getelementptr <4 x float>, <4 x float>* %p, i64 0, i64 1
21 ; CHECK: [[PGEP1:%[0-9A-Za-z]+]] = ptrtoint float* [[GEP1]] to i64
22 ; CHECK: call void @__asan_store4(i64 [[PGEP1]])
23 ; CHECK: [[GEP2:%[0-9A-Za-z]+]] = getelementptr <4 x float>, <4 x float>* %p, i64 0, i64 2
24 ; CHECK: [[PGEP2:%[0-9A-Za-z]+]] = ptrtoint float* [[GEP2]] to i64
25 ; CHECK: call void @__asan_store4(i64 [[PGEP2]])
26 ; CHECK: tail call void @llvm.masked.store.v4f32.p0v4f32(<4 x float> %arg, <4 x float>* %p, i32 4, <4 x i1> )
27 tail call void @llvm.masked.store.v4f32.p0v4f32(<4 x float> %arg, <4 x float>* %p, i32 4, <4 x i1> )
28 ret void
29 }
30
31 define void @store.v8i32.10010110(<8 x i32> %arg) sanitize_address {
32 ; CHECK-LABEL: @store.v8i32.10010110
33 %p = load <8 x i32>*, <8 x i32>** @v8i32, align 8
34 ; CHECK: [[GEP0:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 0
35 ; CHECK: [[PGEP0:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP0]] to i64
36 ; CHECK: call void @__asan_store4(i64 [[PGEP0]])
37 ; CHECK: [[GEP3:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 3
38 ; CHECK: [[PGEP3:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP3]] to i64
39 ; CHECK: call void @__asan_store4(i64 [[PGEP3]])
40 ; CHECK: [[GEP5:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 5
41 ; CHECK: [[PGEP5:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP5]] to i64
42 ; CHECK: call void @__asan_store4(i64 [[PGEP5]])
43 ; CHECK: [[GEP6:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 6
44 ; CHECK: [[PGEP6:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP6]] to i64
45 ; CHECK: call void @__asan_store4(i64 [[PGEP6]])
46 ; CHECK: tail call void @llvm.masked.store.v8i32.p0v8i32(<8 x i32> %arg, <8 x i32>* %p, i32 8, <8 x i1> )
47 tail call void @llvm.masked.store.v8i32.p0v8i32(<8 x i32> %arg, <8 x i32>* %p, i32 8, <8 x i1> )
48 ret void
49 }
50
51 define void @store.v4i64.0001(<4 x i32*> %arg) sanitize_address {
52 ; CHECK-LABEL: @store.v4i64.0001
53 %p = load <4 x i32*>*, <4 x i32*>** @v4i64, align 8
54 ; CHECK: [[GEP3:%[0-9A-Za-z]+]] = getelementptr <4 x i32*>, <4 x i32*>* %p, i64 0, i64 3
55 ; CHECK: [[PGEP3:%[0-9A-Za-z]+]] = ptrtoint i32** [[GEP3]] to i64
56 ; CHECK: call void @__asan_store8(i64 [[PGEP3]])
57 ; CHECK: tail call void @llvm.masked.store.v4p0i32.p0v4p0i32(<4 x i32*> %arg, <4 x i32*>* %p, i32 8, <4 x i1> )
58 tail call void @llvm.masked.store.v4p0i32.p0v4p0i32(<4 x i32*> %arg, <4 x i32*>* %p, i32 8, <4 x i1> )
59 ret void
60 }
61
62 define void @store.v4f32.variable(<4 x float> %arg, <4 x i1> %mask) sanitize_address {
63 ; CHECK-LABEL: @store.v4f32.variable
64 %p = load <4 x float>*, <4 x float>** @v4f32, align 8
65 ; CHECK-NOT: call void @__asan_store
66 tail call void @llvm.masked.store.v4f32.p0v4f32(<4 x float> %arg, <4 x float>* %p, i32 4, <4 x i1> %mask)
67 ret void
68 }
69
70 ;;;;;;;;;;;;;;;; LOAD
71 declare <4 x float> @llvm.masked.load.v4f32.p0v4f32(<4 x float>*, i32, <4 x i1>, <4 x float>) argmemonly nounwind
72 declare <8 x i32> @llvm.masked.load.v8i32.p0v8i32(<8 x i32>*, i32, <8 x i1>, <8 x i32>) argmemonly nounwind
73 declare <4 x i32*> @llvm.masked.load.v4p0i32.p0v4p0i32(<4 x i32*>*, i32, <4 x i1>, <4 x i32*>) argmemonly nounwind
74
75 define <8 x i32> @load.v8i32.11100001(<8 x i32> %arg) sanitize_address {
76 ; CHECK-LABEL: @load.v8i32.11100001
77 %p = load <8 x i32>*, <8 x i32>** @v8i32, align 8
78 ; CHECK: [[GEP0:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 0
79 ; CHECK: [[PGEP0:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP0]] to i64
80 ; CHECK: call void @__asan_load4(i64 [[PGEP0]])
81 ; CHECK: [[GEP1:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 1
82 ; CHECK: [[PGEP1:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP1]] to i64
83 ; CHECK: call void @__asan_load4(i64 [[PGEP1]])
84 ; CHECK: [[GEP2:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 2
85 ; CHECK: [[PGEP2:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP2]] to i64
86 ; CHECK: call void @__asan_load4(i64 [[PGEP2]])
87 ; CHECK: [[GEP7:%[0-9A-Za-z]+]] = getelementptr <8 x i32>, <8 x i32>* %p, i64 0, i64 7
88 ; CHECK: [[PGEP7:%[0-9A-Za-z]+]] = ptrtoint i32* [[GEP7]] to i64
89 ; CHECK: call void @__asan_load4(i64 [[PGEP7]])
90 ; CHECK: tail call <8 x i32> @llvm.masked.load.v8i32.p0v8i32(<8 x i32>* %p, i32 8, <8 x i1> , <8 x i32> %arg)
91 %res = tail call <8 x i32> @llvm.masked.load.v8i32.p0v8i32(<8 x i32>* %p, i32 8, <8 x i1> , <8 x i32> %arg)
92 ret <8 x i32> %res
93 }
94
95 define <4 x float> @load.v4f32.1001(<4 x float> %arg) sanitize_address {
96 ; CHECK-LABEL: @load.v4f32.1001
97 %p = load <4 x float>*, <4 x float>** @v4f32, align 8
98 ; CHECK: [[GEP0:%[0-9A-Za-z]+]] = getelementptr <4 x float>, <4 x float>* %p, i64 0, i64 0
99 ; CHECK: [[PGEP0:%[0-9A-Za-z]+]] = ptrtoint float* [[GEP0]] to i64
100 ; CHECK: call void @__asan_load4(i64 [[PGEP0]])
101 ; CHECK: [[GEP3:%[0-9A-Za-z]+]] = getelementptr <4 x float>, <4 x float>* %p, i64 0, i64 3
102 ; CHECK: [[PGEP3:%[0-9A-Za-z]+]] = ptrtoint float* [[GEP3]] to i64
103 ; CHECK: call void @__asan_load4(i64 [[PGEP3]])
104 ; CHECK: tail call <4 x float> @llvm.masked.load.v4f32.p0v4f32(<4 x float>* %p, i32 4, <4 x i1> , <4 x float> %arg)
105 %res = tail call <4 x float> @llvm.masked.load.v4f32.p0v4f32(<4 x float>* %p, i32 4, <4 x i1> , <4 x float> %arg)
106 ret <4 x float> %res
107 }
108
109 define <4 x i32*> @load.v4i64.0001(<4 x i32*> %arg) sanitize_address {
110 ; CHECK-LABEL: @load.v4i64.0001
111 %p = load <4 x i32*>*, <4 x i32*>** @v4i64, align 8
112 ; CHECK: [[GEP3:%[0-9A-Za-z]+]] = getelementptr <4 x i32*>, <4 x i32*>* %p, i64 0, i64 3
113 ; CHECK: [[PGEP3:%[0-9A-Za-z]+]] = ptrtoint i32** [[GEP3]] to i64
114 ; CHECK: call void @__asan_load8(i64 [[PGEP3]])
115 ; CHECK: tail call <4 x i32*> @llvm.masked.load.v4p0i32.p0v4p0i32(<4 x i32*>* %p, i32 8, <4 x i1> , <4 x i32*> %arg)
116 %res = tail call <4 x i32*> @llvm.masked.load.v4p0i32.p0v4p0i32(<4 x i32*>* %p, i32 8, <4 x i1> , <4 x i32*> %arg)
117 ret <4 x i32*> %res
118 }
119
120 define <4 x float> @load.v4f32.variable(<4 x float> %arg, <4 x i1> %mask) sanitize_address {
121 ; CHECK-LABEL: @load.v4f32.variable
122 %p = load <4 x float>*, <4 x float>** @v4f32, align 8
123 ; CHECK-NOT: call void @__asan_load
124 %res = tail call <4 x float> @llvm.masked.load.v4f32.p0v4f32(<4 x float>* %p, i32 4, <4 x i1> %mask, <4 x float> %arg)
125 ret <4 x float> %res
126 }