blob: e7f8551bcb87a7c26f31b898b807b57966de8df7 [file] [edit]
// Copyright 2022 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/Instructions.h"
#include "llvm/IR/Module.h"
#include <map>
#include <set>
#include "Builtins.h"
#include "Constants.h"
#include "PushConstant.h"
#include "SamplerUtils.h"
#include "SetImageMetadataPass.h"
#include "Types.h"
#include "clspv/Option.h"
#include "clspv/Sampler.h"
using namespace llvm;
#define DEBUG_TYPE "setimagemetadata"
namespace {
using ImageMdMap = std::map<std::pair<unsigned, unsigned>, std::set<Value *>>;
using MetadataVector = SmallVector<Metadata *, 3>;
unsigned getOrdinal(Value *Val) {
auto *call = dyn_cast<CallInst>(Val);
assert(call != nullptr);
assert(clspv::Builtins::Lookup(call->getCalledFunction()).getType() ==
clspv::Builtins::kClspvResource);
return dyn_cast<ConstantInt>(
call->getOperand(clspv::ClspvOperand::kResourceArgIndex))
->getZExtValue();
}
void concatWithFunctionMetadata(Function *F, MetadataVector &MDs) {
auto fct_md = F->getMetadata(clspv::PushConstantsMetadataImageChannelName());
if (fct_md != nullptr) {
for (unsigned i = 0; i < fct_md->getNumOperands(); i++) {
MDs.push_back(fct_md->getOperand(i));
}
}
}
void setImageSamplerMetadata(Module &M, Function *F, unsigned ordinal,
unsigned offset, unsigned pc,
std::set<Value *> &samplers) {
DenseMap<Value *, Type *> cache;
auto i32 = IntegerType::get(M.getContext(), 32);
MetadataVector MDs = {ConstantAsMetadata::get(ConstantInt::get(i32, ordinal)),
ConstantAsMetadata::get(ConstantInt::get(i32, offset))};
concatWithFunctionMetadata(F, MDs);
// Set metadata for the function to be able to generate the appropriate
// reflection from it
F->setMetadata(clspv::PushConstantMetadataSamplerMaskName(),
MDNode::get(M.getContext(), MDs));
auto call_md = MDNode::get(
M.getContext(), {ConstantAsMetadata::get(ConstantInt::get(i32, offset))});
for (auto sampler : samplers) {
for (auto *U : sampler->users()) {
assert(isa<CallInst>(U));
auto call = cast<CallInst>(U);
IRBuilder<> B(call);
Value *Coord = call->getOperand(2);
Value *Img = call->getOperand(0);
Type *ImgTy = clspv::InferType(Img, M.getContext(), &cache);
Value *ImgDimFP = clspv::GetImageDimFP(M, B, Img, ImgTy);
Value *NormCoordNearest =
clspv::NormalizedCoordinate(M, B, Coord, ImgDimFP, true);
Value *NormCoordLinear =
clspv::NormalizedCoordinate(M, B, Coord, ImgDimFP, false);
auto getSamplerNormFct =
M.getOrInsertFunction("clspv.get_normalized_sampler_mask",
FunctionType::get(B.getInt32Ty(), {}, false));
auto SamplerNorm = B.CreateCall(getSamplerNormFct, {});
SamplerNorm->setMetadata(clspv::SamplerMaskPushConstantOffsetName(),
call_md);
auto FilterMask =
B.CreateAnd(SamplerNorm, B.getInt32(clspv::kSamplerFilterMask));
auto FilterCond =
B.CreateICmpEQ(FilterMask, B.getInt32(clspv::CLK_FILTER_NEAREST));
if (clspv::Option::SpvVersion() <=
clspv::Option::SPIRVVersion::SPIRV_1_3) {
FilterCond = B.CreateVectorSplat(4, FilterCond);
}
auto NormCoord =
B.CreateSelect(FilterCond, NormCoordNearest, NormCoordLinear);
auto NormMask = B.CreateAnd(
SamplerNorm, B.getInt32(clspv::kSamplerNormalizedCoordsMask));
auto NormCond = B.CreateICmpEQ(
NormMask, B.getInt32(clspv::CLK_NORMALIZED_COORDS_TRUE));
if (clspv::Option::SpvVersion() <=
clspv::Option::SPIRVVersion::SPIRV_1_3) {
NormCond = B.CreateVectorSplat(4, NormCond);
}
auto Select = B.CreateSelect(NormCond, Coord, NormCoord);
call->setOperand(2, Select);
}
}
}
void setImageChannelMetadata(Module &M, Function *F, unsigned ordinal,
unsigned offset, unsigned pc,
std::set<Value *> &calls) {
auto i32 = IntegerType::get(M.getContext(), 32);
MetadataVector MDs = {ConstantAsMetadata::get(ConstantInt::get(i32, ordinal)),
ConstantAsMetadata::get(ConstantInt::get(i32, offset)),
ConstantAsMetadata::get(ConstantInt::get(i32, pc))};
concatWithFunctionMetadata(F, MDs);
// Set metadata for the function to be able to generate the appropriate
// reflection from it
F->setMetadata(clspv::PushConstantsMetadataImageChannelName(),
MDNode::get(M.getContext(), MDs));
auto call_md = MDNode::get(
M.getContext(), {ConstantAsMetadata::get(ConstantInt::get(i32, offset))});
for (auto call : calls) {
assert(isa<CallInst>(call));
// Set metadata for the call to be able to generate the appropriate gep
// with the correct offset from it
cast<CallInst>(call)->setMetadata(
clspv::ImageGetterPushConstantOffsetName(), call_md);
}
}
unsigned setMetadata(Module &M, Function *F, ImageMdMap &map) {
unsigned int count = 0;
for (const auto &elem : map) {
auto pc = elem.first.first;
auto ordinal = elem.first.second;
auto calls = elem.second;
unsigned offset = count++;
if (pc == (unsigned)clspv::ImageMetadata::NormalizedSamplerMask) {
setImageSamplerMetadata(M, F, ordinal, offset, pc, calls);
} else {
setImageChannelMetadata(M, F, ordinal, offset, pc, calls);
}
}
return count;
}
void updatePushConstant(Module &M, unsigned max_elements) {
// Create and return the structure that will contains the needed values
std::vector<Type *> orderTypes(max_elements,
IntegerType::get(M.getContext(), 32));
StructType *Ty = StructType::get(M.getContext(), orderTypes);
clspv::RedeclareGlobalPushConstants(M, Ty,
(int)clspv::PushConstant::ImageMetadata);
}
} // namespace
PreservedAnalyses clspv::SetImageMetadataPass::run(Module &M,
ModuleAnalysisManager &) {
PreservedAnalyses PA;
unsigned max_elements = 0;
// Go through function and instruction to look for image metadata getter
// function
for (Function &F : M) {
if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL) {
continue;
}
ImageMdMap Map;
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
if (auto call = dyn_cast<CallInst>(&I)) {
auto Name = call->getCalledFunction()->getName();
if (Name.contains("get_image_channel_order")) {
unsigned ordinal = getOrdinal(call->getArgOperand(0));
Map[std::make_pair((unsigned)clspv::ImageMetadata::ChannelOrder,
ordinal)]
.insert(call);
} else if (Name.contains("get_image_channel_data_type")) {
unsigned ordinal = getOrdinal(call->getArgOperand(0));
Map[std::make_pair((unsigned)clspv::ImageMetadata::ChannelDataType,
ordinal)]
.insert(call);
} else if (isReadImage3DWithNonLiteralSampler(call)) {
auto sampler = call->getArgOperand(1);
unsigned ordinal = getOrdinal(sampler);
Map[std::make_pair(
(unsigned)clspv::ImageMetadata::NormalizedSamplerMask,
ordinal)]
.insert(sampler);
}
}
}
}
max_elements = std::max(max_elements, setMetadata(M, &F, Map));
}
if (max_elements > 0)
updatePushConstant(M, max_elements);
return PA;
}