10b57cec5SDimitry Andric //===-- AMDGPULowerKernelArguments.cpp ------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric /// \file This pass replaces accesses to kernel arguments with loads from 100b57cec5SDimitry Andric /// offsets from the kernarg base pointer. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "AMDGPU.h" 150b57cec5SDimitry Andric #include "AMDGPUSubtarget.h" 160b57cec5SDimitry Andric #include "AMDGPUTargetMachine.h" 170b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 180b57cec5SDimitry Andric #include "llvm/Analysis/Loads.h" 190b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h" 200b57cec5SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 210b57cec5SDimitry Andric #include "llvm/IR/Attributes.h" 220b57cec5SDimitry Andric #include "llvm/IR/BasicBlock.h" 230b57cec5SDimitry Andric #include "llvm/IR/Constants.h" 240b57cec5SDimitry Andric #include "llvm/IR/DerivedTypes.h" 250b57cec5SDimitry Andric #include "llvm/IR/Function.h" 260b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h" 270b57cec5SDimitry Andric #include "llvm/IR/InstrTypes.h" 280b57cec5SDimitry Andric #include "llvm/IR/Instruction.h" 290b57cec5SDimitry Andric #include "llvm/IR/Instructions.h" 300b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h" 310b57cec5SDimitry Andric #include "llvm/IR/MDBuilder.h" 320b57cec5SDimitry Andric #include "llvm/IR/Metadata.h" 330b57cec5SDimitry Andric #include "llvm/IR/Operator.h" 340b57cec5SDimitry Andric #include "llvm/IR/Type.h" 350b57cec5SDimitry Andric #include "llvm/IR/Value.h" 360b57cec5SDimitry Andric #include "llvm/Pass.h" 370b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric #define DEBUG_TYPE "amdgpu-lower-kernel-arguments" 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric using namespace llvm; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric namespace { 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric class AMDGPULowerKernelArguments : public FunctionPass{ 460b57cec5SDimitry Andric public: 470b57cec5SDimitry Andric static char ID; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric AMDGPULowerKernelArguments() : FunctionPass(ID) {} 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric bool runOnFunction(Function &F) override; 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 540b57cec5SDimitry Andric AU.addRequired<TargetPassConfig>(); 550b57cec5SDimitry Andric AU.setPreservesAll(); 560b57cec5SDimitry Andric } 570b57cec5SDimitry Andric }; 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric } // end anonymous namespace 600b57cec5SDimitry Andric 61*5ffd83dbSDimitry Andric // skip allocas 62*5ffd83dbSDimitry Andric static BasicBlock::iterator getInsertPt(BasicBlock &BB) { 63*5ffd83dbSDimitry Andric BasicBlock::iterator InsPt = BB.getFirstInsertionPt(); 64*5ffd83dbSDimitry Andric for (BasicBlock::iterator E = BB.end(); InsPt != E; ++InsPt) { 65*5ffd83dbSDimitry Andric AllocaInst *AI = dyn_cast<AllocaInst>(&*InsPt); 66*5ffd83dbSDimitry Andric 67*5ffd83dbSDimitry Andric // If this is a dynamic alloca, the value may depend on the loaded kernargs, 68*5ffd83dbSDimitry Andric // so loads will need to be inserted before it. 69*5ffd83dbSDimitry Andric if (!AI || !AI->isStaticAlloca()) 70*5ffd83dbSDimitry Andric break; 71*5ffd83dbSDimitry Andric } 72*5ffd83dbSDimitry Andric 73*5ffd83dbSDimitry Andric return InsPt; 74*5ffd83dbSDimitry Andric } 75*5ffd83dbSDimitry Andric 760b57cec5SDimitry Andric bool AMDGPULowerKernelArguments::runOnFunction(Function &F) { 770b57cec5SDimitry Andric CallingConv::ID CC = F.getCallingConv(); 780b57cec5SDimitry Andric if (CC != CallingConv::AMDGPU_KERNEL || F.arg_empty()) 790b57cec5SDimitry Andric return false; 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric auto &TPC = getAnalysis<TargetPassConfig>(); 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric const TargetMachine &TM = TPC.getTM<TargetMachine>(); 840b57cec5SDimitry Andric const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(F); 850b57cec5SDimitry Andric LLVMContext &Ctx = F.getParent()->getContext(); 860b57cec5SDimitry Andric const DataLayout &DL = F.getParent()->getDataLayout(); 870b57cec5SDimitry Andric BasicBlock &EntryBlock = *F.begin(); 88*5ffd83dbSDimitry Andric IRBuilder<> Builder(&*getInsertPt(EntryBlock)); 890b57cec5SDimitry Andric 908bcb0991SDimitry Andric const Align KernArgBaseAlign(16); // FIXME: Increase if necessary 910b57cec5SDimitry Andric const uint64_t BaseOffset = ST.getExplicitKernelArgOffset(F); 920b57cec5SDimitry Andric 938bcb0991SDimitry Andric Align MaxAlign; 940b57cec5SDimitry Andric // FIXME: Alignment is broken broken with explicit arg offset.; 950b57cec5SDimitry Andric const uint64_t TotalKernArgSize = ST.getKernArgSegmentSize(F, MaxAlign); 960b57cec5SDimitry Andric if (TotalKernArgSize == 0) 970b57cec5SDimitry Andric return false; 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric CallInst *KernArgSegment = 1000b57cec5SDimitry Andric Builder.CreateIntrinsic(Intrinsic::amdgcn_kernarg_segment_ptr, {}, {}, 1010b57cec5SDimitry Andric nullptr, F.getName() + ".kernarg.segment"); 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric KernArgSegment->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull); 1040b57cec5SDimitry Andric KernArgSegment->addAttribute(AttributeList::ReturnIndex, 1050b57cec5SDimitry Andric Attribute::getWithDereferenceableBytes(Ctx, TotalKernArgSize)); 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric unsigned AS = KernArgSegment->getType()->getPointerAddressSpace(); 1080b57cec5SDimitry Andric uint64_t ExplicitArgOffset = 0; 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric for (Argument &Arg : F.args()) { 1110b57cec5SDimitry Andric Type *ArgTy = Arg.getType(); 112*5ffd83dbSDimitry Andric Align ABITypeAlign = DL.getABITypeAlign(ArgTy); 1130b57cec5SDimitry Andric unsigned Size = DL.getTypeSizeInBits(ArgTy); 1140b57cec5SDimitry Andric unsigned AllocSize = DL.getTypeAllocSize(ArgTy); 1150b57cec5SDimitry Andric 1168bcb0991SDimitry Andric uint64_t EltOffset = alignTo(ExplicitArgOffset, ABITypeAlign) + BaseOffset; 1178bcb0991SDimitry Andric ExplicitArgOffset = alignTo(ExplicitArgOffset, ABITypeAlign) + AllocSize; 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric if (Arg.use_empty()) 1200b57cec5SDimitry Andric continue; 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric if (PointerType *PT = dyn_cast<PointerType>(ArgTy)) { 1230b57cec5SDimitry Andric // FIXME: Hack. We rely on AssertZext to be able to fold DS addressing 1240b57cec5SDimitry Andric // modes on SI to know the high bits are 0 so pointer adds don't wrap. We 1250b57cec5SDimitry Andric // can't represent this with range metadata because it's only allowed for 1260b57cec5SDimitry Andric // integer types. 1270b57cec5SDimitry Andric if ((PT->getAddressSpace() == AMDGPUAS::LOCAL_ADDRESS || 1280b57cec5SDimitry Andric PT->getAddressSpace() == AMDGPUAS::REGION_ADDRESS) && 1290b57cec5SDimitry Andric !ST.hasUsableDSOffset()) 1300b57cec5SDimitry Andric continue; 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric // FIXME: We can replace this with equivalent alias.scope/noalias 1330b57cec5SDimitry Andric // metadata, but this appears to be a lot of work. 1340b57cec5SDimitry Andric if (Arg.hasNoAliasAttr()) 1350b57cec5SDimitry Andric continue; 1360b57cec5SDimitry Andric } 1370b57cec5SDimitry Andric 138*5ffd83dbSDimitry Andric auto *VT = dyn_cast<FixedVectorType>(ArgTy); 1390b57cec5SDimitry Andric bool IsV3 = VT && VT->getNumElements() == 3; 1400b57cec5SDimitry Andric bool DoShiftOpt = Size < 32 && !ArgTy->isAggregateType(); 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric VectorType *V4Ty = nullptr; 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric int64_t AlignDownOffset = alignDown(EltOffset, 4); 1450b57cec5SDimitry Andric int64_t OffsetDiff = EltOffset - AlignDownOffset; 1468bcb0991SDimitry Andric Align AdjustedAlign = commonAlignment( 1478bcb0991SDimitry Andric KernArgBaseAlign, DoShiftOpt ? AlignDownOffset : EltOffset); 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric Value *ArgPtr; 1500b57cec5SDimitry Andric Type *AdjustedArgTy; 1510b57cec5SDimitry Andric if (DoShiftOpt) { // FIXME: Handle aggregate types 1520b57cec5SDimitry Andric // Since we don't have sub-dword scalar loads, avoid doing an extload by 1530b57cec5SDimitry Andric // loading earlier than the argument address, and extracting the relevant 1540b57cec5SDimitry Andric // bits. 1550b57cec5SDimitry Andric // 1560b57cec5SDimitry Andric // Additionally widen any sub-dword load to i32 even if suitably aligned, 1570b57cec5SDimitry Andric // so that CSE between different argument loads works easily. 1580b57cec5SDimitry Andric ArgPtr = Builder.CreateConstInBoundsGEP1_64( 1590b57cec5SDimitry Andric Builder.getInt8Ty(), KernArgSegment, AlignDownOffset, 1600b57cec5SDimitry Andric Arg.getName() + ".kernarg.offset.align.down"); 1610b57cec5SDimitry Andric AdjustedArgTy = Builder.getInt32Ty(); 1620b57cec5SDimitry Andric } else { 1630b57cec5SDimitry Andric ArgPtr = Builder.CreateConstInBoundsGEP1_64( 1640b57cec5SDimitry Andric Builder.getInt8Ty(), KernArgSegment, EltOffset, 1650b57cec5SDimitry Andric Arg.getName() + ".kernarg.offset"); 1660b57cec5SDimitry Andric AdjustedArgTy = ArgTy; 1670b57cec5SDimitry Andric } 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric if (IsV3 && Size >= 32) { 170*5ffd83dbSDimitry Andric V4Ty = FixedVectorType::get(VT->getElementType(), 4); 1710b57cec5SDimitry Andric // Use the hack that clang uses to avoid SelectionDAG ruining v3 loads 1720b57cec5SDimitry Andric AdjustedArgTy = V4Ty; 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric ArgPtr = Builder.CreateBitCast(ArgPtr, AdjustedArgTy->getPointerTo(AS), 1760b57cec5SDimitry Andric ArgPtr->getName() + ".cast"); 1770b57cec5SDimitry Andric LoadInst *Load = 178*5ffd83dbSDimitry Andric Builder.CreateAlignedLoad(AdjustedArgTy, ArgPtr, AdjustedAlign); 1790b57cec5SDimitry Andric Load->setMetadata(LLVMContext::MD_invariant_load, MDNode::get(Ctx, {})); 1800b57cec5SDimitry Andric 1810b57cec5SDimitry Andric MDBuilder MDB(Ctx); 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric if (isa<PointerType>(ArgTy)) { 1840b57cec5SDimitry Andric if (Arg.hasNonNullAttr()) 1850b57cec5SDimitry Andric Load->setMetadata(LLVMContext::MD_nonnull, MDNode::get(Ctx, {})); 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric uint64_t DerefBytes = Arg.getDereferenceableBytes(); 1880b57cec5SDimitry Andric if (DerefBytes != 0) { 1890b57cec5SDimitry Andric Load->setMetadata( 1900b57cec5SDimitry Andric LLVMContext::MD_dereferenceable, 1910b57cec5SDimitry Andric MDNode::get(Ctx, 1920b57cec5SDimitry Andric MDB.createConstant( 1930b57cec5SDimitry Andric ConstantInt::get(Builder.getInt64Ty(), DerefBytes)))); 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric uint64_t DerefOrNullBytes = Arg.getDereferenceableOrNullBytes(); 1970b57cec5SDimitry Andric if (DerefOrNullBytes != 0) { 1980b57cec5SDimitry Andric Load->setMetadata( 1990b57cec5SDimitry Andric LLVMContext::MD_dereferenceable_or_null, 2000b57cec5SDimitry Andric MDNode::get(Ctx, 2010b57cec5SDimitry Andric MDB.createConstant(ConstantInt::get(Builder.getInt64Ty(), 2020b57cec5SDimitry Andric DerefOrNullBytes)))); 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric unsigned ParamAlign = Arg.getParamAlignment(); 2060b57cec5SDimitry Andric if (ParamAlign != 0) { 2070b57cec5SDimitry Andric Load->setMetadata( 2080b57cec5SDimitry Andric LLVMContext::MD_align, 2090b57cec5SDimitry Andric MDNode::get(Ctx, 2100b57cec5SDimitry Andric MDB.createConstant(ConstantInt::get(Builder.getInt64Ty(), 2110b57cec5SDimitry Andric ParamAlign)))); 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric } 2140b57cec5SDimitry Andric 2150b57cec5SDimitry Andric // TODO: Convert noalias arg to !noalias 2160b57cec5SDimitry Andric 2170b57cec5SDimitry Andric if (DoShiftOpt) { 2180b57cec5SDimitry Andric Value *ExtractBits = OffsetDiff == 0 ? 2190b57cec5SDimitry Andric Load : Builder.CreateLShr(Load, OffsetDiff * 8); 2200b57cec5SDimitry Andric 2210b57cec5SDimitry Andric IntegerType *ArgIntTy = Builder.getIntNTy(Size); 2220b57cec5SDimitry Andric Value *Trunc = Builder.CreateTrunc(ExtractBits, ArgIntTy); 2230b57cec5SDimitry Andric Value *NewVal = Builder.CreateBitCast(Trunc, ArgTy, 2240b57cec5SDimitry Andric Arg.getName() + ".load"); 2250b57cec5SDimitry Andric Arg.replaceAllUsesWith(NewVal); 2260b57cec5SDimitry Andric } else if (IsV3) { 2270b57cec5SDimitry Andric Value *Shuf = Builder.CreateShuffleVector(Load, UndefValue::get(V4Ty), 228*5ffd83dbSDimitry Andric ArrayRef<int>{0, 1, 2}, 2290b57cec5SDimitry Andric Arg.getName() + ".load"); 2300b57cec5SDimitry Andric Arg.replaceAllUsesWith(Shuf); 2310b57cec5SDimitry Andric } else { 2320b57cec5SDimitry Andric Load->setName(Arg.getName() + ".load"); 2330b57cec5SDimitry Andric Arg.replaceAllUsesWith(Load); 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric } 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric KernArgSegment->addAttribute( 2380b57cec5SDimitry Andric AttributeList::ReturnIndex, 2390b57cec5SDimitry Andric Attribute::getWithAlignment(Ctx, std::max(KernArgBaseAlign, MaxAlign))); 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric return true; 2420b57cec5SDimitry Andric } 2430b57cec5SDimitry Andric 2440b57cec5SDimitry Andric INITIALIZE_PASS_BEGIN(AMDGPULowerKernelArguments, DEBUG_TYPE, 2450b57cec5SDimitry Andric "AMDGPU Lower Kernel Arguments", false, false) 2460b57cec5SDimitry Andric INITIALIZE_PASS_END(AMDGPULowerKernelArguments, DEBUG_TYPE, "AMDGPU Lower Kernel Arguments", 2470b57cec5SDimitry Andric false, false) 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric char AMDGPULowerKernelArguments::ID = 0; 2500b57cec5SDimitry Andric 2510b57cec5SDimitry Andric FunctionPass *llvm::createAMDGPULowerKernelArgumentsPass() { 2520b57cec5SDimitry Andric return new AMDGPULowerKernelArguments(); 2530b57cec5SDimitry Andric } 254