blob: 895d818273cb55d2a2925acdcf9322656743137f [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 "PhysicalPointerArgsPass.h"
#include "llvm/IR/AttributeMask.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "clspv/AddressSpace.h"
#include "clspv/Option.h"
#include "Constants.h"
#include "NormalizeGlobalVariable.h"
#include "Types.h"
using namespace llvm;
PreservedAnalyses clspv::PhysicalPointerArgsPass::run(Module &M,
ModuleAnalysisManager &) {
PreservedAnalyses PA;
DenseMap<Value *, Type *> TypeCache;
SmallVector<Function *, 8> FuncsToDelete;
for (auto &F : M) {
if (F.getCallingConv() != CallingConv::SPIR_KERNEL)
continue;
// Check if the kernel has any pointer arguments that require transformation
// If so, we want to create a wrapper function that takes an i64 instead of
// each pointer
SmallVector<Type *, 8> NewParamTypes;
auto *PtrIntTy = IntegerType::getInt64Ty(M.getContext());
bool UpdateNeeded = false;
for (auto &Arg : F.args()) {
if (auto *PtrTy = dyn_cast<PointerType>(Arg.getType())) {
auto *ArgTy = clspv::InferType(&Arg, M.getContext(), &TypeCache);
// If we cannot infer the type, that Arg should not be used. Then just
// use the most common type for it. Removing the arg would be annoying
// when go through the arg indexes of the `NewFunc` later in the current
// function.
if (ArgTy == nullptr) {
ArgTy = Type::getInt32Ty(M.getContext());
}
bool IsBuiltinStructTy = false;
if (auto *ArgStructTy = dyn_cast<StructType>(ArgTy)) {
IsBuiltinStructTy = clspv::IsImageType(ArgStructTy) ||
clspv::IsSamplerType(ArgStructTy);
}
if ((PtrTy->getAddressSpace() == clspv::AddressSpace::Global ||
PtrTy->getAddressSpace() == clspv::AddressSpace::Constant) &&
!IsBuiltinStructTy) {
NewParamTypes.push_back(PtrIntTy);
UpdateNeeded = true;
continue;
}
}
NewParamTypes.push_back(Arg.getType());
}
if (!UpdateNeeded)
continue;
auto *NewFuncTy =
FunctionType::get(F.getReturnType(), NewParamTypes, false);
auto NewFunc = Function::Create(NewFuncTy, F.getLinkage());
// The new function adopts the real name so that linkage to the outside
// world remains the same.
NewFunc->setName(F.getName());
F.setName(NewFunc->getName().str() + ".inner");
NewFunc->setCallingConv(F.getCallingConv());
NewFunc->copyAttributesFrom(&F);
// Remove all the pointer related parameter attributes for any parameter
// converted to an integer.
AttributeMask mask;
mask.addAttribute(Attribute::AttrKind::Alignment);
mask.addAttribute(Attribute::AttrKind::NoAlias);
mask.addAttribute(Attribute::AttrKind::ByVal);
mask.addAttribute(Attribute::AttrKind::ByRef);
mask.addAttribute(Attribute::AttrKind::StructRet);
mask.addAttribute(Attribute::AttrKind::ElementType);
mask.addAttribute(Attribute::AttrKind::InAlloca);
mask.addAttribute(Attribute::AttrKind::Preallocated);
mask.addAttribute(
Attribute::getWithCaptureInfo(M.getContext(), CaptureInfo::none()));
mask.addAttribute(Attribute::AttrKind::NoFree);
mask.addAttribute(Attribute::AttrKind::Nest);
mask.addAttribute(Attribute::AttrKind::NonNull);
mask.addAttribute(Attribute::AttrKind::Dereferenceable);
mask.addAttribute(Attribute::AttrKind::DereferenceableOrNull);
mask.addAttribute(Attribute::AttrKind::ReadOnly);
mask.addAttribute(Attribute::AttrKind::ReadNone);
mask.addAttribute(Attribute::AttrKind::WriteOnly);
for (unsigned i = 0; i < NewParamTypes.size(); i++) {
if (F.getArg(i)->getType()->isPointerTy() &&
!NewParamTypes[i]->isPointerTy()) {
NewFunc->removeParamAttrs(i, mask);
}
}
F.setCallingConv(CallingConv::SPIR_FUNC);
for (auto &U : F.uses()) {
if (auto CI = dyn_cast<CallInst>(U.getUser())) {
CI->setCallingConv(CallingConv::SPIR_FUNC);
}
}
NewFunc->copyMetadata(&F, 0);
// Create the function definition. It calls the wrapped function, bitcasting
// any global pointers from the i64 arguments in the wrapper
IRBuilder<> Builder(BasicBlock::Create(M.getContext(), "entry", NewFunc));
SmallVector<Value *, 8> WrappedArgs;
for (unsigned ArgNum = 0; ArgNum < F.arg_size(); ArgNum++) {
auto *OriginalArgTy = F.getArg(ArgNum)->getType();
auto *NewArg = NewFunc->getArg(ArgNum);
if (OriginalArgTy != NewArg->getType()) {
auto *IntAsPtr = Builder.CreateIntToPtr(NewArg, OriginalArgTy);
WrappedArgs.push_back(IntAsPtr);
// We can't attach metadata to arguments directly, so add to this
// use instead. Subsequent passes can determine whether the POD
// contains a pointer by checking the users of the argument.
if (auto *InstAsPtrInstr = dyn_cast<Instruction>(IntAsPtr)) {
auto *EmptyMD = MDNode::get(F.getContext(), {});
InstAsPtrInstr->setMetadata(clspv::PointerPodArgMetadataName(),
EmptyMD);
continue;
}
llvm_unreachable("IntToPtr is not an instruction!");
}
WrappedArgs.push_back(NewArg);
}
auto *CallInst = Builder.CreateCall(&F, WrappedArgs);
CallInst->setCallingConv(F.getCallingConv());
Builder.CreateRetVoid();
// Insert the function after the original, to preserve ordering
// in the module as much as possible.
auto &FunctionList = M.getFunctionList();
for (auto Iter = FunctionList.begin(), IterEnd = FunctionList.end();
Iter != IterEnd; ++Iter) {
if (&*Iter == &F) {
FunctionList.insertAfter(Iter, NewFunc);
break;
}
}
// Inline the function into the wrapper
InlineFunctionInfo info;
InlineFunction(*CallInst, info);
FuncsToDelete.push_back(&F);
}
for (auto *F : FuncsToDelete) {
if (F->getNumUses() == 0) {
F->eraseFromParent();
}
}
clspv::NormalizeGlobalVariables(M);
return PA;
}