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