1 //===AMDGPUAsanInstrumentation.cpp - ASAN related helper functions===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===-------------------------------------------------------------===//
9 #include "AMDGPUAsanInstrumentation.h"
11 #define DEBUG_TYPE "amdgpu-asan-instrumentation"
18 static uint64_t getRedzoneSizeForScale(int AsanScale
) {
19 // Redzone used for stack and globals is at least 32 bytes.
20 // For scales 6 and 7, the redzone has to be 64 and 128 bytes respectively.
21 return std::max(32U, 1U << AsanScale
);
24 static uint64_t getMinRedzoneSizeForGlobal(int AsanScale
) {
25 return getRedzoneSizeForScale(AsanScale
);
28 uint64_t getRedzoneSizeForGlobal(int AsanScale
, uint64_t SizeInBytes
) {
29 constexpr uint64_t kMaxRZ
= 1 << 18;
30 const uint64_t MinRZ
= getMinRedzoneSizeForGlobal(AsanScale
);
33 if (SizeInBytes
<= MinRZ
/ 2) {
34 // Reduce redzone size for small size objects, e.g. int, char[1]. MinRZ is
35 // at least 32 bytes, optimize when SizeInBytes is less than or equal to
37 RZ
= MinRZ
- SizeInBytes
;
39 // Calculate RZ, where MinRZ <= RZ <= MaxRZ, and RZ ~ 1/4 * SizeInBytes.
40 RZ
= std::clamp((SizeInBytes
/ MinRZ
/ 4) * MinRZ
, MinRZ
, kMaxRZ
);
42 // Round up to multiple of MinRZ.
43 if (SizeInBytes
% MinRZ
)
44 RZ
+= MinRZ
- (SizeInBytes
% MinRZ
);
47 assert((RZ
+ SizeInBytes
) % MinRZ
== 0);
52 static size_t TypeStoreSizeToSizeIndex(uint32_t TypeSize
) {
53 size_t Res
= llvm::countr_zero(TypeSize
/ 8);
57 static Instruction
*genAMDGPUReportBlock(Module
&M
, IRBuilder
<> &IRB
,
58 Value
*Cond
, bool Recover
) {
59 Value
*ReportCond
= Cond
;
62 IRB
.CreateIntrinsic(Intrinsic::amdgcn_ballot
, IRB
.getInt64Ty(), {Cond
});
63 ReportCond
= IRB
.CreateIsNotNull(Ballot
);
66 auto *Trm
= SplitBlockAndInsertIfThen(
67 ReportCond
, &*IRB
.GetInsertPoint(), false,
68 MDBuilder(M
.getContext()).createUnlikelyBranchWeights());
69 Trm
->getParent()->setName("asan.report");
74 Trm
= SplitBlockAndInsertIfThen(Cond
, Trm
, false);
75 IRB
.SetInsertPoint(Trm
);
76 return IRB
.CreateIntrinsic(Intrinsic::amdgcn_unreachable
, {}, {});
79 static Value
*createSlowPathCmp(Module
&M
, IRBuilder
<> &IRB
, Type
*IntptrTy
,
80 Value
*AddrLong
, Value
*ShadowValue
,
81 uint32_t TypeStoreSize
, int AsanScale
) {
82 uint64_t Granularity
= static_cast<uint64_t>(1) << AsanScale
;
83 // Addr & (Granularity - 1)
84 Value
*LastAccessedByte
=
85 IRB
.CreateAnd(AddrLong
, ConstantInt::get(IntptrTy
, Granularity
- 1));
86 // (Addr & (Granularity - 1)) + size - 1
87 if (TypeStoreSize
/ 8 > 1)
88 LastAccessedByte
= IRB
.CreateAdd(
89 LastAccessedByte
, ConstantInt::get(IntptrTy
, TypeStoreSize
/ 8 - 1));
90 // (uint8_t) ((Addr & (Granularity-1)) + size - 1)
92 IRB
.CreateIntCast(LastAccessedByte
, ShadowValue
->getType(), false);
93 // ((uint8_t) ((Addr & (Granularity-1)) + size - 1)) >= ShadowValue
94 return IRB
.CreateICmpSGE(LastAccessedByte
, ShadowValue
);
97 static Instruction
*generateCrashCode(Module
&M
, IRBuilder
<> &IRB
,
98 Type
*IntptrTy
, Instruction
*InsertBefore
,
99 Value
*Addr
, bool IsWrite
,
100 size_t AccessSizeIndex
,
101 Value
*SizeArgument
, bool Recover
) {
102 IRB
.SetInsertPoint(InsertBefore
);
103 CallInst
*Call
= nullptr;
104 SmallString
<128> kAsanReportErrorTemplate
{"__asan_report_"};
105 SmallString
<64> TypeStr
{IsWrite
? "store" : "load"};
106 SmallString
<64> EndingStr
{Recover
? "_noabort" : ""};
108 SmallString
<128> AsanErrorCallbackSizedString
;
109 raw_svector_ostream
AsanErrorCallbackSizedOS(AsanErrorCallbackSizedString
);
110 AsanErrorCallbackSizedOS
<< kAsanReportErrorTemplate
<< TypeStr
<< "_n"
113 SmallVector
<Type
*, 3> Args2
= {IntptrTy
, IntptrTy
};
115 FunctionCallee AsanErrorCallbackSized
= M
.getOrInsertFunction(
116 AsanErrorCallbackSizedOS
.str(),
117 FunctionType::get(IRB
.getVoidTy(), Args2
, false), AL2
);
118 SmallVector
<Type
*, 2> Args1
{1, IntptrTy
};
121 SmallString
<128> AsanErrorCallbackString
;
122 raw_svector_ostream
AsanErrorCallbackOS(AsanErrorCallbackString
);
123 AsanErrorCallbackOS
<< kAsanReportErrorTemplate
<< TypeStr
124 << (1ULL << AccessSizeIndex
) << EndingStr
;
126 FunctionCallee AsanErrorCallback
= M
.getOrInsertFunction(
127 AsanErrorCallbackOS
.str(),
128 FunctionType::get(IRB
.getVoidTy(), Args1
, false), AL1
);
130 Call
= IRB
.CreateCall(AsanErrorCallbackSized
, {Addr
, SizeArgument
});
132 Call
= IRB
.CreateCall(AsanErrorCallback
, Addr
);
135 Call
->setCannotMerge();
139 static Value
*memToShadow(Module
&M
, IRBuilder
<> &IRB
, Type
*IntptrTy
,
140 Value
*Shadow
, int AsanScale
, uint32_t AsanOffset
) {
142 Shadow
= IRB
.CreateLShr(Shadow
, AsanScale
);
145 // (Shadow >> scale) | offset
146 Value
*ShadowBase
= ConstantInt::get(IntptrTy
, AsanOffset
);
147 return IRB
.CreateAdd(Shadow
, ShadowBase
);
150 static void instrumentAddressImpl(Module
&M
, IRBuilder
<> &IRB
,
151 Instruction
*OrigIns
,
152 Instruction
*InsertBefore
, Value
*Addr
,
153 Align Alignment
, uint32_t TypeStoreSize
,
154 bool IsWrite
, Value
*SizeArgument
,
155 bool UseCalls
, bool Recover
, int AsanScale
,
157 Type
*AddrTy
= Addr
->getType();
158 Type
*IntptrTy
= M
.getDataLayout().getIntPtrType(
159 M
.getContext(), AddrTy
->getPointerAddressSpace());
160 IRB
.SetInsertPoint(InsertBefore
);
161 size_t AccessSizeIndex
= TypeStoreSizeToSizeIndex(TypeStoreSize
);
162 Type
*ShadowTy
= IntegerType::get(M
.getContext(),
163 std::max(8U, TypeStoreSize
>> AsanScale
));
164 Type
*ShadowPtrTy
= PointerType::get(ShadowTy
, 0);
165 Value
*AddrLong
= IRB
.CreatePtrToInt(Addr
, IntptrTy
);
167 memToShadow(M
, IRB
, IntptrTy
, AddrLong
, AsanScale
, AsanOffset
);
168 const uint64_t ShadowAlign
=
169 std::max
<uint64_t>(Alignment
.value() >> AsanScale
, 1);
170 Value
*ShadowValue
= IRB
.CreateAlignedLoad(
171 ShadowTy
, IRB
.CreateIntToPtr(ShadowPtr
, ShadowPtrTy
), Align(ShadowAlign
));
172 Value
*Cmp
= IRB
.CreateIsNotNull(ShadowValue
);
173 auto *Cmp2
= createSlowPathCmp(M
, IRB
, IntptrTy
, AddrLong
, ShadowValue
,
174 TypeStoreSize
, AsanScale
);
175 Cmp
= IRB
.CreateAnd(Cmp
, Cmp2
);
176 Instruction
*CrashTerm
= genAMDGPUReportBlock(M
, IRB
, Cmp
, Recover
);
178 generateCrashCode(M
, IRB
, IntptrTy
, CrashTerm
, AddrLong
, IsWrite
,
179 AccessSizeIndex
, SizeArgument
, Recover
);
180 Crash
->setDebugLoc(OrigIns
->getDebugLoc());
183 void instrumentAddress(Module
&M
, IRBuilder
<> &IRB
, Instruction
*OrigIns
,
184 Instruction
*InsertBefore
, Value
*Addr
, Align Alignment
,
185 TypeSize TypeStoreSize
, bool IsWrite
,
186 Value
*SizeArgument
, bool UseCalls
, bool Recover
,
187 int AsanScale
, int AsanOffset
) {
188 if (!TypeStoreSize
.isScalable()) {
189 unsigned Granularity
= 1 << AsanScale
;
190 const auto FixedSize
= TypeStoreSize
.getFixedValue();
197 if (Alignment
.value() >= Granularity
||
198 Alignment
.value() >= FixedSize
/ 8)
199 return instrumentAddressImpl(
200 M
, IRB
, OrigIns
, InsertBefore
, Addr
, Alignment
, FixedSize
, IsWrite
,
201 SizeArgument
, UseCalls
, Recover
, AsanScale
, AsanOffset
);
204 // Instrument unusual size or unusual alignment.
205 IRB
.SetInsertPoint(InsertBefore
);
206 Type
*AddrTy
= Addr
->getType();
207 Type
*IntptrTy
= M
.getDataLayout().getIntPtrType(AddrTy
);
208 Value
*NumBits
= IRB
.CreateTypeSize(IntptrTy
, TypeStoreSize
);
209 Value
*Size
= IRB
.CreateLShr(NumBits
, ConstantInt::get(IntptrTy
, 3));
210 Value
*AddrLong
= IRB
.CreatePtrToInt(Addr
, IntptrTy
);
211 Value
*SizeMinusOne
= IRB
.CreateAdd(Size
, ConstantInt::get(IntptrTy
, -1));
213 IRB
.CreateIntToPtr(IRB
.CreateAdd(AddrLong
, SizeMinusOne
), AddrTy
);
214 instrumentAddressImpl(M
, IRB
, OrigIns
, InsertBefore
, Addr
, {}, 8, IsWrite
,
215 SizeArgument
, UseCalls
, Recover
, AsanScale
, AsanOffset
);
216 instrumentAddressImpl(M
, IRB
, OrigIns
, InsertBefore
, LastByte
, {}, 8, IsWrite
,
217 SizeArgument
, UseCalls
, Recover
, AsanScale
, AsanOffset
);
220 void getInterestingMemoryOperands(
221 Module
&M
, Instruction
*I
,
222 SmallVectorImpl
<InterestingMemoryOperand
> &Interesting
) {
223 const DataLayout
&DL
= M
.getDataLayout();
224 if (LoadInst
*LI
= dyn_cast
<LoadInst
>(I
)) {
225 Interesting
.emplace_back(I
, LI
->getPointerOperandIndex(), false,
226 LI
->getType(), LI
->getAlign());
227 } else if (StoreInst
*SI
= dyn_cast
<StoreInst
>(I
)) {
228 Interesting
.emplace_back(I
, SI
->getPointerOperandIndex(), true,
229 SI
->getValueOperand()->getType(), SI
->getAlign());
230 } else if (AtomicRMWInst
*RMW
= dyn_cast
<AtomicRMWInst
>(I
)) {
231 Interesting
.emplace_back(I
, RMW
->getPointerOperandIndex(), true,
232 RMW
->getValOperand()->getType(), std::nullopt
);
233 } else if (AtomicCmpXchgInst
*XCHG
= dyn_cast
<AtomicCmpXchgInst
>(I
)) {
234 Interesting
.emplace_back(I
, XCHG
->getPointerOperandIndex(), true,
235 XCHG
->getCompareOperand()->getType(),
237 } else if (auto *CI
= dyn_cast
<CallInst
>(I
)) {
238 switch (CI
->getIntrinsicID()) {
239 case Intrinsic::masked_load
:
240 case Intrinsic::masked_store
:
241 case Intrinsic::masked_gather
:
242 case Intrinsic::masked_scatter
: {
243 bool IsWrite
= CI
->getType()->isVoidTy();
244 // Masked store has an initial operand for the value.
245 unsigned OpOffset
= IsWrite
? 1 : 0;
246 Type
*Ty
= IsWrite
? CI
->getArgOperand(0)->getType() : CI
->getType();
247 MaybeAlign Alignment
= Align(1);
248 // Otherwise no alignment guarantees. We probably got Undef.
249 if (auto *Op
= dyn_cast
<ConstantInt
>(CI
->getOperand(1 + OpOffset
)))
250 Alignment
= Op
->getMaybeAlignValue();
251 Value
*Mask
= CI
->getOperand(2 + OpOffset
);
252 Interesting
.emplace_back(I
, OpOffset
, IsWrite
, Ty
, Alignment
, Mask
);
255 case Intrinsic::masked_expandload
:
256 case Intrinsic::masked_compressstore
: {
257 bool IsWrite
= CI
->getIntrinsicID() == Intrinsic::masked_compressstore
;
258 unsigned OpOffset
= IsWrite
? 1 : 0;
259 auto *BasePtr
= CI
->getOperand(OpOffset
);
260 MaybeAlign Alignment
= BasePtr
->getPointerAlignment(DL
);
261 Type
*Ty
= IsWrite
? CI
->getArgOperand(0)->getType() : CI
->getType();
263 Value
*Mask
= CI
->getOperand(1 + OpOffset
);
264 Type
*IntptrTy
= M
.getDataLayout().getIntPtrType(
265 M
.getContext(), BasePtr
->getType()->getPointerAddressSpace());
266 // Use the popcount of Mask as the effective vector length.
267 Type
*ExtTy
= VectorType::get(IntptrTy
, cast
<VectorType
>(Ty
));
268 Value
*ExtMask
= IB
.CreateZExt(Mask
, ExtTy
);
269 Value
*EVL
= IB
.CreateAddReduce(ExtMask
);
270 Value
*TrueMask
= ConstantInt::get(Mask
->getType(), 1);
271 Interesting
.emplace_back(I
, OpOffset
, IsWrite
, Ty
, Alignment
, TrueMask
,
275 case Intrinsic::vp_load
:
276 case Intrinsic::vp_store
:
277 case Intrinsic::experimental_vp_strided_load
:
278 case Intrinsic::experimental_vp_strided_store
: {
279 auto *VPI
= cast
<VPIntrinsic
>(CI
);
280 unsigned IID
= CI
->getIntrinsicID();
281 bool IsWrite
= CI
->getType()->isVoidTy();
282 unsigned PtrOpNo
= *VPI
->getMemoryPointerParamPos(IID
);
283 Type
*Ty
= IsWrite
? CI
->getArgOperand(0)->getType() : CI
->getType();
284 MaybeAlign Alignment
= VPI
->getOperand(PtrOpNo
)->getPointerAlignment(DL
);
285 Value
*Stride
= nullptr;
286 if (IID
== Intrinsic::experimental_vp_strided_store
||
287 IID
== Intrinsic::experimental_vp_strided_load
) {
288 Stride
= VPI
->getOperand(PtrOpNo
+ 1);
289 // Use the pointer alignment as the element alignment if the stride is a
290 // mutiple of the pointer alignment. Otherwise, the element alignment
291 // should be Align(1).
292 unsigned PointerAlign
= Alignment
.valueOrOne().value();
293 if (!isa
<ConstantInt
>(Stride
) ||
294 cast
<ConstantInt
>(Stride
)->getZExtValue() % PointerAlign
!= 0)
295 Alignment
= Align(1);
297 Interesting
.emplace_back(I
, PtrOpNo
, IsWrite
, Ty
, Alignment
,
298 VPI
->getMaskParam(), VPI
->getVectorLengthParam(),
302 case Intrinsic::vp_gather
:
303 case Intrinsic::vp_scatter
: {
304 auto *VPI
= cast
<VPIntrinsic
>(CI
);
305 unsigned IID
= CI
->getIntrinsicID();
306 bool IsWrite
= IID
== Intrinsic::vp_scatter
;
307 unsigned PtrOpNo
= *VPI
->getMemoryPointerParamPos(IID
);
308 Type
*Ty
= IsWrite
? CI
->getArgOperand(0)->getType() : CI
->getType();
309 MaybeAlign Alignment
= VPI
->getPointerAlignment();
310 Interesting
.emplace_back(I
, PtrOpNo
, IsWrite
, Ty
, Alignment
,
312 VPI
->getVectorLengthParam());
315 case Intrinsic::amdgcn_raw_buffer_load
:
316 case Intrinsic::amdgcn_raw_ptr_buffer_load
:
317 case Intrinsic::amdgcn_raw_buffer_load_format
:
318 case Intrinsic::amdgcn_raw_ptr_buffer_load_format
:
319 case Intrinsic::amdgcn_raw_tbuffer_load
:
320 case Intrinsic::amdgcn_raw_ptr_tbuffer_load
:
321 case Intrinsic::amdgcn_struct_buffer_load
:
322 case Intrinsic::amdgcn_struct_ptr_buffer_load
:
323 case Intrinsic::amdgcn_struct_buffer_load_format
:
324 case Intrinsic::amdgcn_struct_ptr_buffer_load_format
:
325 case Intrinsic::amdgcn_struct_tbuffer_load
:
326 case Intrinsic::amdgcn_struct_ptr_tbuffer_load
:
327 case Intrinsic::amdgcn_s_buffer_load
:
328 case Intrinsic::amdgcn_global_load_tr_b64
:
329 case Intrinsic::amdgcn_global_load_tr_b128
: {
330 unsigned PtrOpNo
= 0;
331 bool IsWrite
= false;
332 Type
*Ty
= CI
->getType();
333 Value
*Ptr
= CI
->getArgOperand(PtrOpNo
);
334 MaybeAlign Alignment
= Ptr
->getPointerAlignment(DL
);
335 Interesting
.emplace_back(I
, PtrOpNo
, IsWrite
, Ty
, Alignment
);
338 case Intrinsic::amdgcn_raw_tbuffer_store
:
339 case Intrinsic::amdgcn_raw_ptr_tbuffer_store
:
340 case Intrinsic::amdgcn_raw_buffer_store
:
341 case Intrinsic::amdgcn_raw_ptr_buffer_store
:
342 case Intrinsic::amdgcn_raw_buffer_store_format
:
343 case Intrinsic::amdgcn_raw_ptr_buffer_store_format
:
344 case Intrinsic::amdgcn_struct_buffer_store
:
345 case Intrinsic::amdgcn_struct_ptr_buffer_store
:
346 case Intrinsic::amdgcn_struct_buffer_store_format
:
347 case Intrinsic::amdgcn_struct_ptr_buffer_store_format
:
348 case Intrinsic::amdgcn_struct_tbuffer_store
:
349 case Intrinsic::amdgcn_struct_ptr_tbuffer_store
: {
350 unsigned PtrOpNo
= 1;
352 Value
*Ptr
= CI
->getArgOperand(PtrOpNo
);
353 Type
*Ty
= Ptr
->getType();
354 MaybeAlign Alignment
= Ptr
->getPointerAlignment(DL
);
355 Interesting
.emplace_back(I
, PtrOpNo
, IsWrite
, Ty
, Alignment
);
359 for (unsigned ArgNo
= 0; ArgNo
< CI
->arg_size(); ArgNo
++) {
360 if (Type
*Ty
= CI
->getParamByRefType(ArgNo
)) {
361 Interesting
.emplace_back(I
, ArgNo
, false, Ty
, Align(1));
362 } else if (Type
*Ty
= CI
->getParamByValType(ArgNo
)) {
363 Interesting
.emplace_back(I
, ArgNo
, false, Ty
, Align(1));
369 } // end namespace AMDGPU
370 } // end namespace llvm