1*0fca6ea1SDimitry Andric //===- NVVMIntrRange.cpp - Set range attributes for NVVM intrinsics -------===// 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 // 9*0fca6ea1SDimitry Andric // This pass adds appropriate range attributes for calls to NVVM 100b57cec5SDimitry Andric // intrinsics that return a limited range of values. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "NVPTX.h" 15*0fca6ea1SDimitry Andric #include "NVPTXUtilities.h" 160b57cec5SDimitry Andric #include "llvm/IR/InstIterator.h" 170b57cec5SDimitry Andric #include "llvm/IR/Instructions.h" 18*0fca6ea1SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 190b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h" 20480093f4SDimitry Andric #include "llvm/IR/IntrinsicsNVPTX.h" 21e8d8bef9SDimitry Andric #include "llvm/IR/PassManager.h" 22480093f4SDimitry Andric #include "llvm/Support/CommandLine.h" 23*0fca6ea1SDimitry Andric #include <cstdint> 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric using namespace llvm; 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric #define DEBUG_TYPE "nvvm-intr-range" 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric namespace llvm { void initializeNVVMIntrRangePass(PassRegistry &); } 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric namespace { 320b57cec5SDimitry Andric class NVVMIntrRange : public FunctionPass { 330b57cec5SDimitry Andric public: 340b57cec5SDimitry Andric static char ID; 35*0fca6ea1SDimitry Andric NVVMIntrRange() : FunctionPass(ID) { 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric initializeNVVMIntrRangePass(*PassRegistry::getPassRegistry()); 380b57cec5SDimitry Andric } 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric bool runOnFunction(Function &) override; 410b57cec5SDimitry Andric }; 42*0fca6ea1SDimitry Andric } // namespace 430b57cec5SDimitry Andric 44*0fca6ea1SDimitry Andric FunctionPass *llvm::createNVVMIntrRangePass() { return new NVVMIntrRange(); } 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric char NVVMIntrRange::ID = 0; 470b57cec5SDimitry Andric INITIALIZE_PASS(NVVMIntrRange, "nvvm-intr-range", 480b57cec5SDimitry Andric "Add !range metadata to NVVM intrinsics.", false, false) 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric // Adds the passed-in [Low,High) range information as metadata to the 510b57cec5SDimitry Andric // passed-in call instruction. 52*0fca6ea1SDimitry Andric static bool addRangeAttr(uint64_t Low, uint64_t High, IntrinsicInst *II) { 53*0fca6ea1SDimitry Andric if (II->getMetadata(LLVMContext::MD_range)) 540b57cec5SDimitry Andric return false; 550b57cec5SDimitry Andric 56*0fca6ea1SDimitry Andric const uint64_t BitWidth = II->getType()->getIntegerBitWidth(); 57*0fca6ea1SDimitry Andric ConstantRange Range(APInt(BitWidth, Low), APInt(BitWidth, High)); 58*0fca6ea1SDimitry Andric 59*0fca6ea1SDimitry Andric if (auto CurrentRange = II->getRange()) 60*0fca6ea1SDimitry Andric Range = Range.intersectWith(CurrentRange.value()); 61*0fca6ea1SDimitry Andric 62*0fca6ea1SDimitry Andric II->addRangeRetAttr(Range); 630b57cec5SDimitry Andric return true; 640b57cec5SDimitry Andric } 650b57cec5SDimitry Andric 66*0fca6ea1SDimitry Andric static bool runNVVMIntrRange(Function &F) { 67e8d8bef9SDimitry Andric struct { 68e8d8bef9SDimitry Andric unsigned x, y, z; 69e8d8bef9SDimitry Andric } MaxBlockSize, MaxGridSize; 70e8d8bef9SDimitry Andric 71*0fca6ea1SDimitry Andric const unsigned MetadataNTID = getReqNTID(F).value_or( 72*0fca6ea1SDimitry Andric getMaxNTID(F).value_or(std::numeric_limits<unsigned>::max())); 73*0fca6ea1SDimitry Andric 74*0fca6ea1SDimitry Andric MaxBlockSize.x = std::min(1024u, MetadataNTID); 75*0fca6ea1SDimitry Andric MaxBlockSize.y = std::min(1024u, MetadataNTID); 76*0fca6ea1SDimitry Andric MaxBlockSize.z = std::min(64u, MetadataNTID); 77*0fca6ea1SDimitry Andric 78*0fca6ea1SDimitry Andric MaxGridSize.x = 0x7fffffff; 79e8d8bef9SDimitry Andric MaxGridSize.y = 0xffff; 80e8d8bef9SDimitry Andric MaxGridSize.z = 0xffff; 81e8d8bef9SDimitry Andric 820b57cec5SDimitry Andric // Go through the calls in this function. 830b57cec5SDimitry Andric bool Changed = false; 840b57cec5SDimitry Andric for (Instruction &I : instructions(F)) { 85*0fca6ea1SDimitry Andric IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I); 86*0fca6ea1SDimitry Andric if (!II) 870b57cec5SDimitry Andric continue; 880b57cec5SDimitry Andric 89*0fca6ea1SDimitry Andric switch (II->getIntrinsicID()) { 900b57cec5SDimitry Andric // Index within block 910b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_tid_x: 92*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, MaxBlockSize.x, II); 930b57cec5SDimitry Andric break; 940b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_tid_y: 95*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, MaxBlockSize.y, II); 960b57cec5SDimitry Andric break; 970b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_tid_z: 98*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, MaxBlockSize.z, II); 990b57cec5SDimitry Andric break; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric // Block size 1020b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_ntid_x: 103*0fca6ea1SDimitry Andric Changed |= addRangeAttr(1, MaxBlockSize.x + 1, II); 1040b57cec5SDimitry Andric break; 1050b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_ntid_y: 106*0fca6ea1SDimitry Andric Changed |= addRangeAttr(1, MaxBlockSize.y + 1, II); 1070b57cec5SDimitry Andric break; 1080b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_ntid_z: 109*0fca6ea1SDimitry Andric Changed |= addRangeAttr(1, MaxBlockSize.z + 1, II); 1100b57cec5SDimitry Andric break; 1110b57cec5SDimitry Andric 1120b57cec5SDimitry Andric // Index within grid 1130b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_ctaid_x: 114*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, MaxGridSize.x, II); 1150b57cec5SDimitry Andric break; 1160b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_ctaid_y: 117*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, MaxGridSize.y, II); 1180b57cec5SDimitry Andric break; 1190b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_ctaid_z: 120*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, MaxGridSize.z, II); 1210b57cec5SDimitry Andric break; 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric // Grid size 1240b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_nctaid_x: 125*0fca6ea1SDimitry Andric Changed |= addRangeAttr(1, MaxGridSize.x + 1, II); 1260b57cec5SDimitry Andric break; 1270b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_nctaid_y: 128*0fca6ea1SDimitry Andric Changed |= addRangeAttr(1, MaxGridSize.y + 1, II); 1290b57cec5SDimitry Andric break; 1300b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_nctaid_z: 131*0fca6ea1SDimitry Andric Changed |= addRangeAttr(1, MaxGridSize.z + 1, II); 1320b57cec5SDimitry Andric break; 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric // warp size is constant 32. 1350b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_warpsize: 136*0fca6ea1SDimitry Andric Changed |= addRangeAttr(32, 32 + 1, II); 1370b57cec5SDimitry Andric break; 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric // Lane ID is [0..warpsize) 1400b57cec5SDimitry Andric case Intrinsic::nvvm_read_ptx_sreg_laneid: 141*0fca6ea1SDimitry Andric Changed |= addRangeAttr(0, 32, II); 1420b57cec5SDimitry Andric break; 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric default: 1450b57cec5SDimitry Andric break; 1460b57cec5SDimitry Andric } 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric return Changed; 1500b57cec5SDimitry Andric } 151e8d8bef9SDimitry Andric 152*0fca6ea1SDimitry Andric bool NVVMIntrRange::runOnFunction(Function &F) { return runNVVMIntrRange(F); } 153e8d8bef9SDimitry Andric 154e8d8bef9SDimitry Andric PreservedAnalyses NVVMIntrRangePass::run(Function &F, 155e8d8bef9SDimitry Andric FunctionAnalysisManager &AM) { 156*0fca6ea1SDimitry Andric return runNVVMIntrRange(F) ? PreservedAnalyses::none() 157e8d8bef9SDimitry Andric : PreservedAnalyses::all(); 158e8d8bef9SDimitry Andric } 159