blob: 3c8d5e046596c6759e813d3f8a40bf31a64fec8f [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/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "Builtins.h"
#include "FixupBuiltinsPass.h"
#include "Types.h"
#include "clspv/Option.h"
#include <cmath>
using namespace clspv;
using namespace llvm;
namespace clspv_local {
static double rsqrt(double input) { return 1.0 / sqrt(input); }
} // namespace clspv_local
PreservedAnalyses FixupBuiltinsPass::run(Module &M, ModuleAnalysisManager &) {
PreservedAnalyses PA;
for (auto &F : M) {
runOnFunction(F);
}
return PA;
}
bool FixupBuiltinsPass::runOnFunction(Function &F) {
auto &FI = Builtins::Lookup(&F);
switch (FI.getType()) {
case Builtins::kSqrt:
return fixupSqrt(F, sqrt);
case Builtins::kRsqrt:
return fixupSqrt(F, clspv_local::rsqrt);
case Builtins::kReadImagef:
case Builtins::kReadImagei:
case Builtins::kReadImageui:
if (clspv::Option::HackImage1dBufferBGRA() &&
!FI.getParameter(1).isSampler()) {
return fixupReadImage(F);
} else {
return false;
}
default:
return false;
}
}
bool FixupBuiltinsPass::fixupSqrt(Function &F, double (*fct)(double)) {
// We only want to perform this transformation on the native sqrt/rsqrt
// implementation.
if (!F.isDeclaration()) {
return false;
}
bool modified = false;
SmallVector<CallInst *> worklist;
for (auto &U : F.uses()) {
if (auto CI = dyn_cast<CallInst>(U.getUser())) {
worklist.push_back(CI);
}
}
for (auto CI : worklist) {
IRBuilder<> builder(CI);
auto nan = ConstantFP::getNaN(CI->getType());
auto zero = ConstantFP::getZero(CI->getType());
if (auto cst = dyn_cast<ConstantFP>(CI->getOperand(0))) {
CI->replaceAllUsesWith(ConstantFP::get(
CI->getType(), fct(cst->getValue().convertToDouble())));
CI->eraseFromParent();
} else if (auto vec_cst = dyn_cast<ConstantDataVector>(CI->getOperand(0))) {
Value *Res = UndefValue::get(vec_cst->getType());
for (unsigned int i = 0; i < vec_cst->getNumElements(); i++) {
auto fp = cast<ConstantFP>(vec_cst->getElementAsConstant(i))
->getValue()
.convertToDouble();
Res = builder.CreateInsertElement(
Res, ConstantFP::get(CI->getType()->getScalarType(), fct(fp)), i);
}
CI->replaceAllUsesWith(Res);
} else {
auto op_is_positive = builder.CreateFCmpOGE(CI->getOperand(0), zero);
builder.SetInsertPoint(CI->getNextNode());
SelectInst *select =
cast<SelectInst>(builder.CreateSelect(op_is_positive, zero, nan));
CI->replaceAllUsesWith(select);
select->setTrueValue(CI);
}
modified = true;
}
return modified;
}
bool FixupBuiltinsPass::fixupReadImage(Function &F) {
const uint32_t CL_BGRA = 0x10B6;
DenseMap<Value *, Type *> cache;
bool changed = false;
for (auto &U : F.uses()) {
if (auto CI = dyn_cast<CallInst>(U.getUser())) {
auto Img = CI->getOperand(0);
auto *image_ty = InferType(Img, F.getContext(), &cache);
if (clspv::ImageDimensionality(image_ty) == spv::DimBuffer) {
IRBuilder<> B(CI);
B.SetInsertPoint(*CI->getInsertionPointAfterDef());
auto shuffle =
cast<ShuffleVectorInst>(B.CreateShuffleVector(CI, {2, 1, 0, 3}));
auto channel_order_fct = F.getParent()->getOrInsertFunction(
"_Z23get_image_channel_order21ocl_image1d_buffer_ro",
FunctionType::get(B.getInt32Ty(), {image_ty}, false));
auto channel_order = B.CreateCall(channel_order_fct, {Img});
auto cmp = B.CreateICmpNE(channel_order, B.getInt32(CL_BGRA));
SelectInst *select = cast<SelectInst>(B.CreateSelect(cmp, CI, shuffle));
// Do not use tmp before because llvm can optimize the node and not
// create it. But we need to use tmp to be able to replace all uses of
// CI without having a circular dependency.
auto tmp = UndefValue::get(CI->getType());
select->setTrueValue(tmp);
shuffle->setOperand(0, tmp);
CI->replaceAllUsesWith(select);
// Put the right argument at the proper places.
select->setTrueValue(CI);
shuffle->setOperand(0, CI);
changed = true;
}
}
}
return changed;
}