|
| 1 | +//===- SPIRVBuiltinHelper.cpp - Helpers for managing calls to builtins ----===// |
| 2 | +// |
| 3 | +// The LLVM/SPIR-V Translator |
| 4 | +// |
| 5 | +// This file is distributed under the University of Illinois Open Source |
| 6 | +// License. See LICENSE.TXT for details. |
| 7 | +// |
| 8 | +// Copyright (c) 2022 The Khronos Group Inc. |
| 9 | +// |
| 10 | +// Permission is hereby granted, free of charge, to any person obtaining a |
| 11 | +// copy of this software and associated documentation files (the "Software"), |
| 12 | +// to deal with the Software without restriction, including without limitation |
| 13 | +// the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 14 | +// and/or sell copies of the Software, and to permit persons to whom the |
| 15 | +// Software is furnished to do so, subject to the following conditions: |
| 16 | +// |
| 17 | +// Redistributions of source code must retain the above copyright notice, |
| 18 | +// this list of conditions and the following disclaimers. |
| 19 | +// Redistributions in binary form must reproduce the above copyright notice, |
| 20 | +// this list of conditions and the following disclaimers in the documentation |
| 21 | +// and/or other materials provided with the distribution. |
| 22 | +// Neither the names of The Khronos Group, nor the names of its |
| 23 | +// contributors may be used to endorse or promote products derived from this |
| 24 | +// Software without specific prior written permission. |
| 25 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 26 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 27 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 28 | +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 29 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 30 | +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH |
| 31 | +// THE SOFTWARE. |
| 32 | +// |
| 33 | +//===----------------------------------------------------------------------===// |
| 34 | +// |
| 35 | +// This file implements helper functions for adding calls to OpenCL or SPIR-V |
| 36 | +// builtin functions, or for rewriting calls to one into calls to the other. |
| 37 | +// |
| 38 | +//===----------------------------------------------------------------------===// |
| 39 | + |
| 40 | +#include "SPIRVBuiltinHelper.h" |
| 41 | + |
| 42 | +#include "OCLUtil.h" |
| 43 | +#include "SPIRVInternal.h" |
| 44 | + |
| 45 | +using namespace llvm; |
| 46 | +using namespace SPIRV; |
| 47 | + |
| 48 | +static std::unique_ptr<BuiltinFuncMangleInfo> makeMangler(CallBase *CB, |
| 49 | + ManglingRules Rules) { |
| 50 | + switch (Rules) { |
| 51 | + case ManglingRules::None: |
| 52 | + return nullptr; |
| 53 | + case ManglingRules::SPIRV: |
| 54 | + return std::make_unique<BuiltinFuncMangleInfo>(); |
| 55 | + case ManglingRules::OpenCL: |
| 56 | + return OCLUtil::makeMangler(*CB->getCalledFunction()); |
| 57 | + } |
| 58 | + llvm_unreachable("Unknown mangling rules to make a name mangler"); |
| 59 | +} |
| 60 | + |
| 61 | +BuiltinCallMutator::BuiltinCallMutator( |
| 62 | + CallInst *CI, std::string FuncName, ManglingRules Rules, |
| 63 | + std::function<std::string(StringRef)> NameMapFn) |
| 64 | + : CI(CI), FuncName(FuncName), |
| 65 | + Attrs(CI->getCalledFunction()->getAttributes()), ReturnTy(CI->getType()), |
| 66 | + Args(CI->args()), Rules(Rules), Builder(CI) { |
| 67 | + getParameterTypes(CI->getCalledFunction(), PointerTypes, |
| 68 | + std::move(NameMapFn)); |
| 69 | + PointerTypes.resize(Args.size(), nullptr); |
| 70 | +} |
| 71 | + |
| 72 | +BuiltinCallMutator::BuiltinCallMutator(BuiltinCallMutator &&Other) |
| 73 | + : CI(Other.CI), FuncName(std::move(Other.FuncName)), |
| 74 | + MutateRet(std::move(Other.MutateRet)), Attrs(Other.Attrs), |
| 75 | + ReturnTy(Other.ReturnTy), Args(std::move(Other.Args)), |
| 76 | + PointerTypes(std::move(Other.PointerTypes)), |
| 77 | + Rules(std::move(Other.Rules)), Builder(CI) { |
| 78 | + // Clear the other's CI instance so that it knows not to construct the actual |
| 79 | + // call. |
| 80 | + Other.CI = nullptr; |
| 81 | +} |
| 82 | + |
| 83 | +Value *BuiltinCallMutator::doConversion() { |
| 84 | + assert(CI && "Need to have a call instruction to do the conversion"); |
| 85 | + auto Mangler = makeMangler(CI, Rules); |
| 86 | + for (unsigned I = 0; I < Args.size(); I++) { |
| 87 | + Mangler->getTypeMangleInfo(I).PointerTy = PointerTypes[I]; |
| 88 | + } |
| 89 | + assert(Attrs.getNumAttrSets() <= Args.size() + 2 && "Too many attributes?"); |
| 90 | + CallInst *NewCall = |
| 91 | + Builder.Insert(addCallInst(CI->getModule(), FuncName, ReturnTy, Args, |
| 92 | + &Attrs, nullptr, Mangler.get())); |
| 93 | + Value *Result = MutateRet ? MutateRet(Builder, NewCall) : NewCall; |
| 94 | + Result->takeName(CI); |
| 95 | + if (!CI->getType()->isVoidTy()) |
| 96 | + CI->replaceAllUsesWith(Result); |
| 97 | + CI->dropAllReferences(); |
| 98 | + CI->eraseFromParent(); |
| 99 | + CI = nullptr; |
| 100 | + return Result; |
| 101 | +} |
| 102 | + |
| 103 | +BuiltinCallMutator &BuiltinCallMutator::setArgs(ArrayRef<Value *> NewArgs) { |
| 104 | + // Retain only the function attributes, not any parameter attributes. |
| 105 | + Attrs = AttributeList::get(CI->getContext(), Attrs.getFnAttrs(), |
| 106 | + Attrs.getRetAttrs(), {}); |
| 107 | + Args.clear(); |
| 108 | + PointerTypes.clear(); |
| 109 | + for (Value *Arg : NewArgs) { |
| 110 | + assert(!Arg->getType()->isPointerTy() && |
| 111 | + "Cannot use this signature with pointer types"); |
| 112 | + Args.push_back(Arg); |
| 113 | + PointerTypes.emplace_back(); |
| 114 | + } |
| 115 | + return *this; |
| 116 | +} |
| 117 | + |
| 118 | +// This is a helper method to handle splicing of the attribute lists, as |
| 119 | +// llvm::AttributeList doesn't have any helper methods for this sort of design. |
| 120 | +// (It's designed to be manually built-up, not adjusted to add/remove |
| 121 | +// arguments on the fly). |
| 122 | +static void moveAttributes(LLVMContext &Ctx, AttributeList &Attrs, |
| 123 | + unsigned Start, unsigned Len, unsigned Dest) { |
| 124 | + SmallVector<std::pair<unsigned, AttributeSet>, 6> NewAttrs; |
| 125 | + for (unsigned Index : Attrs.indexes()) { |
| 126 | + AttributeSet AttrSet = Attrs.getAttributes(Index); |
| 127 | + if (!AttrSet.hasAttributes()) |
| 128 | + continue; |
| 129 | + |
| 130 | + // If the attribute is a parameter index, check to see how its index should |
| 131 | + // be adjusted. |
| 132 | + if (Index > AttributeList::FirstArgIndex) { |
| 133 | + unsigned ParamIndex = Index - AttributeList::FirstArgIndex; |
| 134 | + if (ParamIndex >= Start && ParamIndex < Start + Len) |
| 135 | + // A parameter in this range needs to have its index adjusted to its |
| 136 | + // destination location. |
| 137 | + Index += Dest - Start; |
| 138 | + else if (ParamIndex >= Dest && ParamIndex < Dest + Len) |
| 139 | + // This parameter will be overwritten by one of the moved parameters, so |
| 140 | + // omit it entirely. |
| 141 | + continue; |
| 142 | + } |
| 143 | + |
| 144 | + // The array is usually going to be sorted, but because of the above |
| 145 | + // adjustment, we might end up out of order. This logic ensures that the |
| 146 | + // array always remains in sorted order. |
| 147 | + std::pair<unsigned, AttributeSet> ToInsert(Index, AttrSet); |
| 148 | + NewAttrs.insert(llvm::lower_bound(NewAttrs, ToInsert, llvm::less_first()), |
| 149 | + ToInsert); |
| 150 | + } |
| 151 | + Attrs = AttributeList::get(Ctx, NewAttrs); |
| 152 | +} |
| 153 | + |
| 154 | +// Convert a ValueTypePair to a TypedPointerType for storing in the PointerTypes |
| 155 | +// array. |
| 156 | +static TypedPointerType *toTPT(BuiltinCallMutator::ValueTypePair Pair) { |
| 157 | + if (!Pair.second) |
| 158 | + return nullptr; |
| 159 | + unsigned AS = 0; |
| 160 | + if (auto *TPT = dyn_cast<TypedPointerType>(Pair.first->getType())) |
| 161 | + AS = TPT->getAddressSpace(); |
| 162 | + else if (isa<PointerType>(Pair.first->getType())) |
| 163 | + AS = Pair.first->getType()->getPointerAddressSpace(); |
| 164 | + return TypedPointerType::get(Pair.second, AS); |
| 165 | +} |
| 166 | + |
| 167 | +BuiltinCallMutator &BuiltinCallMutator::insertArg(unsigned Index, |
| 168 | + ValueTypePair Arg) { |
| 169 | + Args.insert(Args.begin() + Index, Arg.first); |
| 170 | + PointerTypes.insert(PointerTypes.begin() + Index, toTPT(Arg)); |
| 171 | + moveAttributes(CI->getContext(), Attrs, Index, Args.size() - Index, |
| 172 | + Index + 1); |
| 173 | + return *this; |
| 174 | +} |
| 175 | + |
| 176 | +BuiltinCallMutator &BuiltinCallMutator::replaceArg(unsigned Index, |
| 177 | + ValueTypePair Arg) { |
| 178 | + Args[Index] = Arg.first; |
| 179 | + PointerTypes[Index] = toTPT(Arg); |
| 180 | + Attrs = Attrs.removeParamAttributes(CI->getContext(), Index); |
| 181 | + return *this; |
| 182 | +} |
| 183 | + |
| 184 | +BuiltinCallMutator &BuiltinCallMutator::removeArg(unsigned Index) { |
| 185 | + // If the argument being dropped is the last one, there is nothing to move, so |
| 186 | + // just remove the attributes. |
| 187 | + if (Index == Args.size() - 1) |
| 188 | + Attrs = Attrs.removeParamAttributes(CI->getContext(), Index); |
| 189 | + else |
| 190 | + moveAttributes(CI->getContext(), Attrs, Index + 1, Args.size() - Index - 1, |
| 191 | + Index); |
| 192 | + Args.erase(Args.begin() + Index); |
| 193 | + PointerTypes.erase(PointerTypes.begin() + Index); |
| 194 | + return *this; |
| 195 | +} |
| 196 | + |
| 197 | +BuiltinCallMutator & |
| 198 | +BuiltinCallMutator::changeReturnType(Type *NewReturnTy, |
| 199 | + MutateRetFuncTy MutateFunc) { |
| 200 | + ReturnTy = NewReturnTy; |
| 201 | + MutateRet = std::move(MutateFunc); |
| 202 | + return *this; |
| 203 | +} |
| 204 | + |
| 205 | +BuiltinCallMutator BuiltinCallHelper::mutateCallInst(CallInst *CI, |
| 206 | + spv::Op Opcode) { |
| 207 | + return mutateCallInst(CI, getSPIRVFuncName(Opcode)); |
| 208 | +} |
| 209 | + |
| 210 | +BuiltinCallMutator BuiltinCallHelper::mutateCallInst(CallInst *CI, |
| 211 | + std::string FuncName) { |
| 212 | + return BuiltinCallMutator(CI, std::move(FuncName), Rules, NameMapFn); |
| 213 | +} |
0 commit comments