| // Copyright 2017 The Clspv Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/IRBuilder.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Pass.h" |
| |
| #include "clspv/AddressSpace.h" |
| #include "clspv/Option.h" |
| |
| #include "Constants.h" |
| #include "DefineOpenCLWorkItemBuiltinsPass.h" |
| #include "PushConstant.h" |
| #include "Types.h" |
| |
| using namespace llvm; |
| using namespace clspv; |
| |
| namespace { |
| constexpr auto enqueued_local_size_mangled_name = |
| "_Z23get_enqueued_local_sizej"; |
| constexpr auto enqueued_num_sub_groups_mangled_name = |
| "_Z27get_enqueued_num_sub_groupsv"; |
| constexpr auto global_id_mangled_name = "_Z13get_global_idj"; |
| constexpr auto global_linear_id_mangled_name = "_Z20get_global_linear_idv"; |
| constexpr auto global_offset_mangled_name = "_Z17get_global_offsetj"; |
| constexpr auto global_size_mangled_name = "_Z15get_global_sizej"; |
| constexpr auto group_id_mangled_name = "_Z12get_group_idj"; |
| constexpr auto local_id_mangled_name = "_Z12get_local_idj"; |
| constexpr auto local_linear_id_mangled_name = "_Z19get_local_linear_idv"; |
| constexpr auto local_size_mangled_name = "_Z14get_local_sizej"; |
| constexpr auto max_sub_group_size_mangled_name = "_Z22get_max_sub_group_sizev"; |
| constexpr auto num_groups_mangled_name = "_Z14get_num_groupsj"; |
| constexpr auto work_dim_mangled_name = "_Z12get_work_dimv"; |
| |
| Value *inBoundsDimensionCondition(IRBuilder<> &Builder, Value *Dim) { |
| // Vulkan has 3 dimensions for work-items, but the OpenCL API is written |
| // such that it could have more. We have to check whether the value provided |
| // was less than 3... |
| return Builder.CreateICmp(CmpInst::ICMP_ULT, Dim, Builder.getInt32(3)); |
| } |
| |
| Value *inBoundsDimensionIndex(IRBuilder<> &Builder, Value *Dim) { |
| auto Cond = inBoundsDimensionCondition(Builder, Dim); |
| // Select dimension 0 if the requested dimension was greater than |
| // 2, otherwise return the requested dimension. |
| return Builder.CreateSelect(Cond, Dim, Builder.getInt32(0)); |
| } |
| |
| Value *inBoundsDimensionOrDefaultValue(IRBuilder<> &Builder, Value *Dim, |
| Value *Val, int DefaultValue) { |
| auto Cond = inBoundsDimensionCondition(Builder, Dim); |
| return Builder.CreateSelect(Cond, Val, Builder.getInt32(DefaultValue)); |
| } |
| |
| // Retrieve the function if it's needed directly or by a dependent |
| Function *getFunctionIfNeeded(Module &M, StringRef Name, |
| ArrayRef<StringRef> Dependents, |
| FunctionType *FType) { |
| Function *F = M.getFunction(Name); |
| if (F) |
| return F; // function is used directly |
| |
| for (auto &Dependent : Dependents) { |
| auto D = M.getFunction(Dependent); |
| if (D) { |
| // function must be inserted for use by dependent |
| F = cast<Function>(M.getOrInsertFunction(Name, FType).getCallee()); |
| F->setCallingConv(CallingConv::SPIR_FUNC); |
| return F; |
| } |
| } |
| return nullptr; |
| } |
| } // namespace |
| |
| PreservedAnalyses |
| DefineOpenCLWorkItemBuiltinsPass::run(Module &M, ModuleAnalysisManager &) { |
| PreservedAnalyses PA; |
| |
| defineGlobalOffsetBuiltin(M); |
| defineGlobalIDBuiltin(M); |
| |
| defineMappedBuiltin( |
| M, local_size_mangled_name, clspv::WorkgroupSizeVariableName(), 1, |
| clspv::WorkgroupSizeAddressSpace(), {local_linear_id_mangled_name}); |
| defineMappedBuiltin( |
| M, local_id_mangled_name, clspv::LocalInvocationIdVariableName(), 0, |
| clspv::LocalInvocationIdAddressSpace(), {local_linear_id_mangled_name}); |
| defineNumGroupsBuiltin(M); |
| defineGroupIDBuiltin(M); |
| defineGlobalSizeBuiltin(M); |
| defineWorkDimBuiltin(M); |
| defineEnqueuedLocalSizeBuiltin(M); |
| defineMaxSubGroupSizeBuiltin(M); |
| defineEnqueuedNumSubGroupsBuiltin(M); |
| addWorkgroupSizeIfRequired(M); |
| |
| defineGlobalLinearIDBuiltin(M); |
| defineLocalLinearIDBuiltin(M); |
| |
| return PA; |
| } |
| |
| GlobalVariable *DefineOpenCLWorkItemBuiltinsPass::createGlobalVariable( |
| Module &M, StringRef GlobalVarName, Type *Ty, |
| AddressSpace::Type AddrSpace) { |
| auto GV = new GlobalVariable( |
| M, Ty, false, GlobalValue::ExternalLinkage, nullptr, GlobalVarName, |
| nullptr, GlobalValue::ThreadLocalMode::NotThreadLocal, AddrSpace); |
| |
| GV->setInitializer(Constant::getNullValue(Ty)); |
| |
| return GV; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineMappedBuiltin( |
| Module &M, StringRef FuncName, StringRef GlobalVarName, |
| unsigned DefaultValue, AddressSpace::Type AddrSpace, |
| ArrayRef<StringRef> dependents) { |
| |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| IntegerType *SizeT = |
| clspv::PointersAre64Bit(M) ? IntegerType::get(M.getContext(), 64) : IT; |
| auto FType = FunctionType::get(SizeT, IT, false); |
| Function *F = getFunctionIfNeeded(M, FuncName, dependents, FType); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| VectorType *VT = FixedVectorType::get(IT, 3); |
| |
| GlobalVariable *GV = createGlobalVariable(M, GlobalVarName, VT, AddrSpace); |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| |
| Value *Result = nullptr; |
| if (GlobalVarName == "__spirv_WorkgroupSize") { |
| // Ugly hack to work around implementation bugs. |
| // Load the whole vector and extract the result |
| Value *LoadVec = Builder.CreateLoad(GV->getValueType(), GV); |
| Result = Builder.CreateExtractElement(LoadVec, InBoundsDim); |
| } else { |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| Value *GEP = Builder.CreateGEP(VT, GV, Indices); |
| Result = Builder.CreateLoad(IT, GEP); |
| } |
| |
| Value *Select2 = |
| inBoundsDimensionOrDefaultValue(Builder, Dim, Result, DefaultValue); |
| |
| Select2 = Builder.CreateZExt(Select2, SizeT); |
| Builder.CreateRet(Select2); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineGlobalIDBuiltin(Module &M) { |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| IntegerType *I64T = IntegerType::get(M.getContext(), 64); |
| auto FType = |
| FunctionType::get(clspv::PointersAre64Bit(M) ? I64T : IT, IT, false); |
| |
| Function *F = getFunctionIfNeeded(M, global_id_mangled_name, |
| {global_linear_id_mangled_name}, FType); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| VectorType *VT = FixedVectorType::get(IT, 3); |
| |
| GlobalVariable *GV = createGlobalVariable(M, "__spirv_GlobalInvocationId", VT, |
| AddressSpace::Input); |
| |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| |
| Value *Result = nullptr; |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| Value *GEP = Builder.CreateGEP(GV->getValueType(), GV, Indices); |
| Result = Builder.CreateLoad(IT, GEP); |
| |
| auto GidBase = inBoundsDimensionOrDefaultValue(Builder, Dim, Result, 0); |
| |
| // The underlying GlobalInvocationId will always be 32-bit, but this needs |
| // to be promoted when size_t is 64-bit. |
| if (clspv::PointersAre64Bit(M)) { |
| GidBase = Builder.CreateZExt(GidBase, I64T); |
| } |
| |
| Value *Ret = GidBase; |
| if (clspv::Option::NonUniformNDRangeSupported()) { |
| auto Ptr = GetPushConstantPointer(BB, clspv::PushConstant::RegionOffset); |
| auto DimPtr = Builder.CreateInBoundsGEP(VT, Ptr, Indices); |
| auto Size = Builder.CreateLoad(IT, DimPtr); |
| auto RegOff = inBoundsDimensionOrDefaultValue(Builder, Dim, Size, 0); |
| if (clspv::PointersAre64Bit(M)) { |
| RegOff = Builder.CreateZExt(RegOff, I64T); |
| } |
| Ret = Builder.CreateAdd(Ret, RegOff); |
| } else { |
| // If we have a global offset we need to add it |
| if (clspv::Option::GlobalOffset() || |
| clspv::Option::GlobalOffsetPushConstant()) { |
| auto Goff = |
| Builder.CreateCall(M.getFunction(global_offset_mangled_name), Dim); |
| Goff->setCallingConv(CallingConv::SPIR_FUNC); |
| Ret = Builder.CreateAdd(Ret, Goff); |
| } |
| } |
| |
| Builder.CreateRet(Ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineGlobalSizeBuiltin(Module &M) { |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| IntegerType *SizeT = |
| clspv::PointersAre64Bit(M) ? IntegerType::get(M.getContext(), 64) : IT; |
| auto FType = FunctionType::get(SizeT, IT, false); |
| Function *F = getFunctionIfNeeded(M, global_size_mangled_name, |
| {global_linear_id_mangled_name}, FType); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| |
| Value *GlobalSize; |
| if (clspv::Option::NonUniformNDRangeSupported()) { |
| auto Ptr = GetPushConstantPointer(BB, clspv::PushConstant::GlobalSize); |
| auto Ty = GetPushConstantType(M, clspv::PushConstant::GlobalSize); |
| auto DimPtr = Builder.CreateInBoundsGEP(Ty, Ptr, Indices); |
| GlobalSize = Builder.CreateLoad(Ty->getScalarType(), DimPtr); |
| } else { |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| VectorType *VT = FixedVectorType::get(IT, 3); |
| |
| // Global size uses two builtin variables that might already have been |
| // created. |
| StringRef WorkgroupSize = "__spirv_WorkgroupSize"; |
| StringRef NumWorkgroups = "__spirv_NumWorkgroups"; |
| |
| GlobalVariable *WGS = M.getGlobalVariable(WorkgroupSize); |
| |
| // If the module does not already have workgroup size. |
| if (nullptr == WGS) { |
| WGS = createGlobalVariable(M, WorkgroupSize, VT, |
| AddressSpace::ModuleScopePrivate); |
| } |
| |
| GlobalVariable *NWG = M.getGlobalVariable(NumWorkgroups); |
| |
| // If the module does not already have num workgroups. |
| if (nullptr == NWG) { |
| NWG = createGlobalVariable(M, NumWorkgroups, VT, AddressSpace::Input); |
| } |
| |
| // Load the workgroup size. |
| Value *GEP = Builder.CreateGEP(VT, WGS, Indices); |
| Value *LoadWGS = Builder.CreateLoad(IT, GEP); |
| |
| // And the number of workgroups. |
| GEP = Builder.CreateGEP(VT, NWG, Indices); |
| Value *LoadNWG = Builder.CreateLoad(IT, GEP); |
| |
| // We multiply the workgroup size by the number of workgroups to calculate |
| // the global size. |
| GlobalSize = Builder.CreateMul(LoadWGS, LoadNWG); |
| } |
| |
| GlobalSize = inBoundsDimensionOrDefaultValue(Builder, Dim, GlobalSize, 1); |
| |
| if (clspv::PointersAre64Bit(M)) { |
| GlobalSize = |
| Builder.CreateZExt(GlobalSize, IntegerType::get(M.getContext(), 64)); |
| } |
| |
| Builder.CreateRet(GlobalSize); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineNumGroupsBuiltin(Module &M) { |
| |
| Function *F = M.getFunction(num_groups_mangled_name); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| Value *NumGroupsVarPtr; |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| VectorType *VT = FixedVectorType::get(IT, 3); |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| |
| if (clspv::Option::NonUniformNDRangeSupported()) { |
| NumGroupsVarPtr = |
| GetPushConstantPointer(BB, clspv::PushConstant::NumWorkgroups); |
| } else { |
| NumGroupsVarPtr = createGlobalVariable(M, "__spirv_NumWorkgroups", VT, |
| AddressSpace::Input); |
| } |
| |
| auto NumGroupsPtr = Builder.CreateInBoundsGEP(VT, NumGroupsVarPtr, Indices); |
| auto NumGroups = Builder.CreateLoad(IT, NumGroupsPtr); |
| auto Ret = inBoundsDimensionOrDefaultValue(Builder, Dim, NumGroups, 1); |
| |
| if (clspv::PointersAre64Bit(M)) { |
| Ret = Builder.CreateZExt(Ret, IntegerType::get(M.getContext(), 64)); |
| } |
| |
| Builder.CreateRet(Ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineGroupIDBuiltin(Module &M) { |
| |
| Function *F = M.getFunction(group_id_mangled_name); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| VectorType *VT = FixedVectorType::get(IT, 3); |
| auto RegionGroupIDVarPtr = |
| createGlobalVariable(M, "__spirv_WorkgroupId", VT, AddressSpace::Input); |
| |
| auto RegionGroupIDPtr = |
| Builder.CreateInBoundsGEP(VT, RegionGroupIDVarPtr, Indices); |
| auto RegionGroupID = Builder.CreateLoad(IT, RegionGroupIDPtr); |
| auto Ret = inBoundsDimensionOrDefaultValue(Builder, Dim, RegionGroupID, 0); |
| |
| if (clspv::Option::NonUniformNDRangeSupported()) { |
| auto RegionGroupOffsetVarPtr = |
| GetPushConstantPointer(BB, clspv::PushConstant::RegionGroupOffset); |
| auto RegionGroupOffsetPtr = |
| Builder.CreateInBoundsGEP(VT, RegionGroupOffsetVarPtr, Indices); |
| auto RegionGroupOffsetVal = Builder.CreateLoad(IT, RegionGroupOffsetPtr); |
| auto RegionGroupOffset = |
| inBoundsDimensionOrDefaultValue(Builder, Dim, RegionGroupOffsetVal, 0); |
| |
| Ret = Builder.CreateAdd(Ret, RegionGroupOffset); |
| } |
| |
| if (clspv::PointersAre64Bit(M)) { |
| Ret = Builder.CreateZExt(Ret, IntegerType::get(M.getContext(), 64)); |
| } |
| |
| Builder.CreateRet(Ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineGlobalOffsetBuiltin(Module &M) { |
| auto Int32Ty = IntegerType::get(M.getContext(), 32); |
| auto Int64Ty = IntegerType::get(M.getContext(), 64); |
| auto FRetType = clspv::PointersAre64Bit(M) ? Int64Ty : Int32Ty; |
| auto FType = FunctionType::get(FRetType, Int32Ty, false); |
| |
| Function *F = getFunctionIfNeeded( |
| M, global_offset_mangled_name, |
| {global_id_mangled_name, global_linear_id_mangled_name}, FType); |
| |
| // Only define get_global_offset when it is used or the option is enabled |
| // and get_global_id is used (since it is used in global ID calculations). |
| if (F == nullptr) { |
| return false; |
| } |
| |
| bool isSupportEnabled = clspv::Option::GlobalOffset() || |
| clspv::Option::GlobalOffsetPushConstant(); |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| Value *Ret; |
| if (isSupportEnabled) { |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| Value *gep = nullptr; |
| auto *VecTy = FixedVectorType::get(Int32Ty, 3); |
| const bool uses_push_constant = |
| clspv::ShouldDeclareGlobalOffsetPushConstant(M); |
| if (uses_push_constant) { |
| Value *Indices[] = {InBoundsDim}; |
| gep = GetPushConstantPointer(BB, clspv::PushConstant::GlobalOffset, |
| Indices); |
| } else { |
| StringRef name = "__spirv_GlobalOffset"; |
| auto offset_var = createGlobalVariable(M, name, VecTy, |
| AddressSpace::ModuleScopePrivate); |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| gep = Builder.CreateInBoundsGEP(VecTy, offset_var, Indices); |
| } |
| auto load = Builder.CreateLoad(Int32Ty, gep); |
| Ret = inBoundsDimensionOrDefaultValue(Builder, Dim, load, 0); |
| } else { |
| // Get global offset is easy for us as it only returns 0. |
| Ret = |
| clspv::PointersAre64Bit(M) ? Builder.getInt64(0) : Builder.getInt32(0); |
| } |
| |
| if (clspv::PointersAre64Bit(M)) { |
| Ret = Builder.CreateZExt(Ret, IntegerType::get(M.getContext(), 64)); |
| } |
| |
| Builder.CreateRet(Ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineGlobalLinearIDBuiltin(Module &M) { |
| Function *F = M.getFunction(global_linear_id_mangled_name); |
| |
| // Only define get_global_linear_id when it is used. |
| if (F == nullptr) { |
| return false; |
| } |
| |
| bool useGlobalOffset = clspv::Option::GlobalOffset() || |
| clspv::Option::GlobalOffsetPushConstant(); |
| auto GlobalOffsetFunc = |
| useGlobalOffset ? M.getFunction(global_offset_mangled_name) : nullptr; |
| assert(useGlobalOffset == (GlobalOffsetFunc != nullptr) && |
| "if useGlobalOffset is enabled it should have been created by now " |
| "(since global_linear_id is a dependent of get_global_offset)."); |
| |
| auto GlobalIdFunc = M.getFunction(global_id_mangled_name); |
| auto GlobalSizeFunc = M.getFunction(global_size_mangled_name); |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| llvm::ConstantInt *Zero = Builder.getInt32(0); |
| llvm::ConstantInt *One = Builder.getInt32(1); |
| llvm::ConstantInt *Two = Builder.getInt32(2); |
| |
| auto addCallingConv = [](llvm::CallInst *call) { |
| call->setCallingConv(CallingConv::SPIR_FUNC); |
| }; |
| |
| auto getDimSize = [&](llvm::ConstantInt *Dim) -> llvm::Value * { |
| auto GID = Builder.CreateCall(GlobalIdFunc, Dim); |
| addCallingConv(GID); |
| if (useGlobalOffset) { |
| auto Offset = Builder.CreateCall(GlobalOffsetFunc, Dim); |
| addCallingConv(Offset); |
| return Builder.CreateSub(GID, Offset); |
| } |
| return GID; |
| }; |
| |
| auto Dim0 = getDimSize(Zero); |
| auto Dim1 = getDimSize(One); |
| auto Dim2 = getDimSize(Two); |
| |
| auto GSize0 = Builder.CreateCall(GlobalSizeFunc, Zero); |
| addCallingConv(GSize0); |
| auto GSize1 = Builder.CreateCall(GlobalSizeFunc, One); |
| addCallingConv(GSize1); |
| |
| // both get_global_id and get_global_offset default to 0 when the dimension is |
| // >= get_work_dim(), so there is no need for branching here |
| auto SecondDim = Builder.CreateMul(Dim1, GSize0); |
| auto ThirdDim = Builder.CreateMul(Dim2, Builder.CreateMul(GSize0, GSize1)); |
| auto Sum = Builder.CreateAdd(ThirdDim, Builder.CreateAdd(SecondDim, Dim0)); |
| |
| Builder.CreateRet(Sum); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineLocalLinearIDBuiltin(Module &M) { |
| Function *F = M.getFunction(local_linear_id_mangled_name); |
| |
| // Only define get_local_linear_id when it is used. |
| if (F == nullptr) { |
| return false; |
| } |
| |
| auto LocalIdFunc = M.getFunction(local_id_mangled_name); |
| auto LocalSizeFunc = M.getFunction(local_size_mangled_name); |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| llvm::ConstantInt *Zero = Builder.getInt32(0); |
| llvm::ConstantInt *One = Builder.getInt32(1); |
| llvm::ConstantInt *Two = Builder.getInt32(2); |
| |
| auto addCallingConv = [](llvm::CallInst *call) { |
| call->setCallingConv(CallingConv::SPIR_FUNC); |
| }; |
| |
| auto LID0 = Builder.CreateCall(LocalIdFunc, Zero); |
| addCallingConv(LID0); |
| auto LID1 = Builder.CreateCall(LocalIdFunc, One); |
| addCallingConv(LID1); |
| auto LID2 = Builder.CreateCall(LocalIdFunc, Two); |
| addCallingConv(LID2); |
| |
| auto LSize0 = Builder.CreateCall(LocalSizeFunc, Zero); |
| addCallingConv(LSize0); |
| auto LSize1 = Builder.CreateCall(LocalSizeFunc, One); |
| addCallingConv(LSize1); |
| |
| // get_local_id defaults to 0 when the dimension is >= get_work_dim(), so |
| // there is no need for branching here |
| auto SecondDim = Builder.CreateMul(LID1, LSize0); |
| auto ThirdDim = Builder.CreateMul(LID2, Builder.CreateMul(LSize0, LSize1)); |
| auto Sum = Builder.CreateAdd(ThirdDim, Builder.CreateAdd(SecondDim, LID0)); |
| |
| Builder.CreateRet(Sum); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineWorkDimBuiltin(Module &M) { |
| Function *F = M.getFunction(work_dim_mangled_name); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| if (clspv::Option::WorkDim()) { |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| StringRef name = "__spirv_WorkDim"; |
| auto work_dim_var = |
| createGlobalVariable(M, name, IT, AddressSpace::ModuleScopePrivate); |
| auto load = Builder.CreateLoad(IT, work_dim_var); |
| Builder.CreateRet(load); |
| } else { |
| // Get work dim is easy for us as it only returns 3. |
| Builder.CreateRet(Builder.getInt32(3)); |
| } |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineEnqueuedLocalSizeBuiltin( |
| Module &M) { |
| |
| auto Int32Ty = IntegerType::get(M.getContext(), 32); |
| auto Int64Ty = IntegerType::get(M.getContext(), 64); |
| auto FRetTy = clspv::PointersAre64Bit(M) ? Int64Ty : Int32Ty; |
| auto FType = FunctionType::get(FRetTy, Int32Ty, false); |
| Function *F = |
| getFunctionIfNeeded(M, enqueued_local_size_mangled_name, |
| {enqueued_num_sub_groups_mangled_name}, FType); |
| |
| if (nullptr == F) |
| return false; |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| auto Dim = &*F->arg_begin(); |
| auto InBoundsDim = inBoundsDimensionIndex(Builder, Dim); |
| Value *Indices[] = {Builder.getInt32(0), InBoundsDim}; |
| auto Ptr = GetPushConstantPointer(BB, clspv::PushConstant::EnqueuedLocalSize); |
| auto *Ty = GetPushConstantType(M, clspv::PushConstant::EnqueuedLocalSize); |
| auto DimPtr = Builder.CreateInBoundsGEP(Ty, Ptr, Indices); |
| auto Size = Builder.CreateLoad(Ty->getScalarType(), DimPtr); |
| auto Ret = inBoundsDimensionOrDefaultValue(Builder, Dim, Size, 1); |
| |
| if (clspv::PointersAre64Bit(M)) { |
| Ret = Builder.CreateZExt(Ret, Int64Ty); |
| } |
| |
| Builder.CreateRet(Ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineMaxSubGroupSizeBuiltin(Module &M) { |
| |
| auto Int32Ty = IntegerType::get(M.getContext(), 32); |
| auto FType = FunctionType::get(Int32Ty, false); |
| Function *F = |
| getFunctionIfNeeded(M, max_sub_group_size_mangled_name, |
| {enqueued_num_sub_groups_mangled_name}, FType); |
| |
| if (nullptr == F) |
| return false; |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| StringRef name = "__spirv_SubgroupMaxSize"; |
| auto var = |
| createGlobalVariable(M, name, IT, AddressSpace::ModuleScopePrivate); |
| auto ret = Builder.CreateLoad(IT, var); |
| Builder.CreateRet(ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::defineEnqueuedNumSubGroupsBuiltin( |
| Module &M) { |
| |
| Function *F = M.getFunction(enqueued_num_sub_groups_mangled_name); |
| |
| // If the builtin was not used in the module, don't create it! |
| if (nullptr == F) { |
| return false; |
| } |
| |
| BasicBlock *BB = BasicBlock::Create(M.getContext(), "body", F); |
| IRBuilder<> Builder(BB); |
| |
| auto FELS = M.getFunction(enqueued_local_size_mangled_name); |
| |
| Value *ELS0 = Builder.CreateCall(FELS, Builder.getInt32(0)); |
| cast<CallInst>(ELS0)->setCallingConv(CallingConv::SPIR_FUNC); |
| Value *ELS1 = Builder.CreateCall(FELS, Builder.getInt32(1)); |
| cast<CallInst>(ELS1)->setCallingConv(CallingConv::SPIR_FUNC); |
| Value *ELS2 = Builder.CreateCall(FELS, Builder.getInt32(2)); |
| cast<CallInst>(ELS2)->setCallingConv(CallingConv::SPIR_FUNC); |
| |
| auto ELS = Builder.CreateMul(ELS0, ELS1); |
| ELS = Builder.CreateMul(ELS, ELS2); |
| |
| // get_enqueued_local size returns size_t but this builtin and |
| // get_max_sub_group_size return uint, so truncate if needed |
| if (clspv::PointersAre64Bit(M)) { |
| ELS = Builder.CreateTrunc(ELS, Builder.getInt32Ty()); |
| } |
| |
| auto MaxSubgroupSize = |
| Builder.CreateCall(M.getFunction(max_sub_group_size_mangled_name)); |
| MaxSubgroupSize->setCallingConv(CallingConv::SPIR_FUNC); |
| |
| auto ELSRoundedUp = Builder.CreateAdd(ELS, MaxSubgroupSize); |
| ELSRoundedUp = Builder.CreateSub(ELSRoundedUp, Builder.getInt32(1)); |
| |
| auto Ret = Builder.CreateUDiv(ELSRoundedUp, MaxSubgroupSize); |
| |
| Builder.CreateRet(Ret); |
| |
| return true; |
| } |
| |
| bool DefineOpenCLWorkItemBuiltinsPass::addWorkgroupSizeIfRequired(Module &M) { |
| StringRef WorkgroupSize = "__spirv_WorkgroupSize"; |
| |
| // If the module doesn't already have workgroup size. |
| if (nullptr == M.getGlobalVariable(WorkgroupSize)) { |
| for (auto &F : M) { |
| if (F.getCallingConv() != llvm::CallingConv::SPIR_KERNEL) { |
| continue; |
| } |
| |
| // If this kernel does not have the reqd_work_group_size metadata, we need |
| // to output the workgroup size variable. |
| if (nullptr == F.getMetadata("reqd_work_group_size") || |
| clspv::Option::NonUniformNDRangeSupported()) { |
| IntegerType *IT = IntegerType::get(M.getContext(), 32); |
| VectorType *VT = FixedVectorType::get(IT, 3); |
| createGlobalVariable(M, WorkgroupSize, VT, |
| AddressSpace::ModuleScopePrivate); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |