blob: 7cf83dd606205afc8bee98f55ececbee4a2edfbd [file] [edit]
// 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;
}