|  | //===--- SPIRVUtils.cpp ---- SPIR-V Utility Functions -----------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file contains miscellaneous utility functions. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "SPIRVUtils.h" | 
|  | #include "MCTargetDesc/SPIRVBaseInfo.h" | 
|  | #include "SPIRV.h" | 
|  | #include "SPIRVInstrInfo.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | 
|  | #include "llvm/CodeGen/MachineInstr.h" | 
|  | #include "llvm/CodeGen/MachineInstrBuilder.h" | 
|  | #include "llvm/Demangle/Demangle.h" | 
|  | #include "llvm/IR/IntrinsicsSPIRV.h" | 
|  |  | 
|  | namespace llvm { | 
|  |  | 
|  | // The following functions are used to add these string literals as a series of | 
|  | // 32-bit integer operands with the correct format, and unpack them if necessary | 
|  | // when making string comparisons in compiler passes. | 
|  | // SPIR-V requires null-terminated UTF-8 strings padded to 32-bit alignment. | 
|  | static uint32_t convertCharsToWord(const StringRef &Str, unsigned i) { | 
|  | uint32_t Word = 0u; // Build up this 32-bit word from 4 8-bit chars. | 
|  | for (unsigned WordIndex = 0; WordIndex < 4; ++WordIndex) { | 
|  | unsigned StrIndex = i + WordIndex; | 
|  | uint8_t CharToAdd = 0;       // Initilize char as padding/null. | 
|  | if (StrIndex < Str.size()) { // If it's within the string, get a real char. | 
|  | CharToAdd = Str[StrIndex]; | 
|  | } | 
|  | Word |= (CharToAdd << (WordIndex * 8)); | 
|  | } | 
|  | return Word; | 
|  | } | 
|  |  | 
|  | // Get length including padding and null terminator. | 
|  | static size_t getPaddedLen(const StringRef &Str) { | 
|  | const size_t Len = Str.size() + 1; | 
|  | return (Len % 4 == 0) ? Len : Len + (4 - (Len % 4)); | 
|  | } | 
|  |  | 
|  | void addStringImm(const StringRef &Str, MCInst &Inst) { | 
|  | const size_t PaddedLen = getPaddedLen(Str); | 
|  | for (unsigned i = 0; i < PaddedLen; i += 4) { | 
|  | // Add an operand for the 32-bits of chars or padding. | 
|  | Inst.addOperand(MCOperand::createImm(convertCharsToWord(Str, i))); | 
|  | } | 
|  | } | 
|  |  | 
|  | void addStringImm(const StringRef &Str, MachineInstrBuilder &MIB) { | 
|  | const size_t PaddedLen = getPaddedLen(Str); | 
|  | for (unsigned i = 0; i < PaddedLen; i += 4) { | 
|  | // Add an operand for the 32-bits of chars or padding. | 
|  | MIB.addImm(convertCharsToWord(Str, i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void addStringImm(const StringRef &Str, IRBuilder<> &B, | 
|  | std::vector<Value *> &Args) { | 
|  | const size_t PaddedLen = getPaddedLen(Str); | 
|  | for (unsigned i = 0; i < PaddedLen; i += 4) { | 
|  | // Add a vector element for the 32-bits of chars or padding. | 
|  | Args.push_back(B.getInt32(convertCharsToWord(Str, i))); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) { | 
|  | return getSPIRVStringOperand(MI, StartIndex); | 
|  | } | 
|  |  | 
|  | void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) { | 
|  | const auto Bitwidth = Imm.getBitWidth(); | 
|  | if (Bitwidth == 1) | 
|  | return; // Already handled | 
|  | else if (Bitwidth <= 32) { | 
|  | MIB.addImm(Imm.getZExtValue()); | 
|  | return; | 
|  | } else if (Bitwidth <= 64) { | 
|  | uint64_t FullImm = Imm.getZExtValue(); | 
|  | uint32_t LowBits = FullImm & 0xffffffff; | 
|  | uint32_t HighBits = (FullImm >> 32) & 0xffffffff; | 
|  | MIB.addImm(LowBits).addImm(HighBits); | 
|  | return; | 
|  | } | 
|  | report_fatal_error("Unsupported constant bitwidth"); | 
|  | } | 
|  |  | 
|  | void buildOpName(Register Target, const StringRef &Name, | 
|  | MachineIRBuilder &MIRBuilder) { | 
|  | if (!Name.empty()) { | 
|  | auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target); | 
|  | addStringImm(Name, MIB); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void finishBuildOpDecorate(MachineInstrBuilder &MIB, | 
|  | const std::vector<uint32_t> &DecArgs, | 
|  | StringRef StrImm) { | 
|  | if (!StrImm.empty()) | 
|  | addStringImm(StrImm, MIB); | 
|  | for (const auto &DecArg : DecArgs) | 
|  | MIB.addImm(DecArg); | 
|  | } | 
|  |  | 
|  | void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, | 
|  | SPIRV::Decoration::Decoration Dec, | 
|  | const std::vector<uint32_t> &DecArgs, StringRef StrImm) { | 
|  | auto MIB = MIRBuilder.buildInstr(SPIRV::OpDecorate) | 
|  | .addUse(Reg) | 
|  | .addImm(static_cast<uint32_t>(Dec)); | 
|  | finishBuildOpDecorate(MIB, DecArgs, StrImm); | 
|  | } | 
|  |  | 
|  | void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, | 
|  | SPIRV::Decoration::Decoration Dec, | 
|  | const std::vector<uint32_t> &DecArgs, StringRef StrImm) { | 
|  | MachineBasicBlock &MBB = *I.getParent(); | 
|  | auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpDecorate)) | 
|  | .addUse(Reg) | 
|  | .addImm(static_cast<uint32_t>(Dec)); | 
|  | finishBuildOpDecorate(MIB, DecArgs, StrImm); | 
|  | } | 
|  |  | 
|  | // TODO: maybe the following two functions should be handled in the subtarget | 
|  | // to allow for different OpenCL vs Vulkan handling. | 
|  | unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) { | 
|  | switch (SC) { | 
|  | case SPIRV::StorageClass::Function: | 
|  | return 0; | 
|  | case SPIRV::StorageClass::CrossWorkgroup: | 
|  | return 1; | 
|  | case SPIRV::StorageClass::UniformConstant: | 
|  | return 2; | 
|  | case SPIRV::StorageClass::Workgroup: | 
|  | return 3; | 
|  | case SPIRV::StorageClass::Generic: | 
|  | return 4; | 
|  | case SPIRV::StorageClass::Input: | 
|  | return 7; | 
|  | default: | 
|  | llvm_unreachable("Unable to get address space id"); | 
|  | } | 
|  | } | 
|  |  | 
|  | SPIRV::StorageClass::StorageClass | 
|  | addressSpaceToStorageClass(unsigned AddrSpace) { | 
|  | switch (AddrSpace) { | 
|  | case 0: | 
|  | return SPIRV::StorageClass::Function; | 
|  | case 1: | 
|  | return SPIRV::StorageClass::CrossWorkgroup; | 
|  | case 2: | 
|  | return SPIRV::StorageClass::UniformConstant; | 
|  | case 3: | 
|  | return SPIRV::StorageClass::Workgroup; | 
|  | case 4: | 
|  | return SPIRV::StorageClass::Generic; | 
|  | case 7: | 
|  | return SPIRV::StorageClass::Input; | 
|  | default: | 
|  | llvm_unreachable("Unknown address space"); | 
|  | } | 
|  | } | 
|  |  | 
|  | SPIRV::MemorySemantics::MemorySemantics | 
|  | getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC) { | 
|  | switch (SC) { | 
|  | case SPIRV::StorageClass::StorageBuffer: | 
|  | case SPIRV::StorageClass::Uniform: | 
|  | return SPIRV::MemorySemantics::UniformMemory; | 
|  | case SPIRV::StorageClass::Workgroup: | 
|  | return SPIRV::MemorySemantics::WorkgroupMemory; | 
|  | case SPIRV::StorageClass::CrossWorkgroup: | 
|  | return SPIRV::MemorySemantics::CrossWorkgroupMemory; | 
|  | case SPIRV::StorageClass::AtomicCounter: | 
|  | return SPIRV::MemorySemantics::AtomicCounterMemory; | 
|  | case SPIRV::StorageClass::Image: | 
|  | return SPIRV::MemorySemantics::ImageMemory; | 
|  | default: | 
|  | return SPIRV::MemorySemantics::None; | 
|  | } | 
|  | } | 
|  |  | 
|  | SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord) { | 
|  | switch (Ord) { | 
|  | case AtomicOrdering::Acquire: | 
|  | return SPIRV::MemorySemantics::Acquire; | 
|  | case AtomicOrdering::Release: | 
|  | return SPIRV::MemorySemantics::Release; | 
|  | case AtomicOrdering::AcquireRelease: | 
|  | return SPIRV::MemorySemantics::AcquireRelease; | 
|  | case AtomicOrdering::SequentiallyConsistent: | 
|  | return SPIRV::MemorySemantics::SequentiallyConsistent; | 
|  | case AtomicOrdering::Unordered: | 
|  | case AtomicOrdering::Monotonic: | 
|  | case AtomicOrdering::NotAtomic: | 
|  | return SPIRV::MemorySemantics::None; | 
|  | } | 
|  | llvm_unreachable(nullptr); | 
|  | } | 
|  |  | 
|  | MachineInstr *getDefInstrMaybeConstant(Register &ConstReg, | 
|  | const MachineRegisterInfo *MRI) { | 
|  | MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg); | 
|  | if (ConstInstr->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && | 
|  | ConstInstr->getIntrinsicID() == Intrinsic::spv_track_constant) { | 
|  | ConstReg = ConstInstr->getOperand(2).getReg(); | 
|  | ConstInstr = MRI->getVRegDef(ConstReg); | 
|  | } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) { | 
|  | ConstReg = ConstInstr->getOperand(1).getReg(); | 
|  | ConstInstr = MRI->getVRegDef(ConstReg); | 
|  | } | 
|  | return ConstInstr; | 
|  | } | 
|  |  | 
|  | uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) { | 
|  | const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI); | 
|  | assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT); | 
|  | return MI->getOperand(1).getCImm()->getValue().getZExtValue(); | 
|  | } | 
|  |  | 
|  | bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) { | 
|  | return MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && | 
|  | MI.getIntrinsicID() == IntrinsicID; | 
|  | } | 
|  |  | 
|  | Type *getMDOperandAsType(const MDNode *N, unsigned I) { | 
|  | return cast<ValueAsMetadata>(N->getOperand(I))->getType(); | 
|  | } | 
|  |  | 
|  | // The set of names is borrowed from the SPIR-V translator. | 
|  | // TODO: may be implemented in SPIRVBuiltins.td. | 
|  | static bool isPipeOrAddressSpaceCastBI(const StringRef MangledName) { | 
|  | return MangledName == "write_pipe_2" || MangledName == "read_pipe_2" || | 
|  | MangledName == "write_pipe_2_bl" || MangledName == "read_pipe_2_bl" || | 
|  | MangledName == "write_pipe_4" || MangledName == "read_pipe_4" || | 
|  | MangledName == "reserve_write_pipe" || | 
|  | MangledName == "reserve_read_pipe" || | 
|  | MangledName == "commit_write_pipe" || | 
|  | MangledName == "commit_read_pipe" || | 
|  | MangledName == "work_group_reserve_write_pipe" || | 
|  | MangledName == "work_group_reserve_read_pipe" || | 
|  | MangledName == "work_group_commit_write_pipe" || | 
|  | MangledName == "work_group_commit_read_pipe" || | 
|  | MangledName == "get_pipe_num_packets_ro" || | 
|  | MangledName == "get_pipe_max_packets_ro" || | 
|  | MangledName == "get_pipe_num_packets_wo" || | 
|  | MangledName == "get_pipe_max_packets_wo" || | 
|  | MangledName == "sub_group_reserve_write_pipe" || | 
|  | MangledName == "sub_group_reserve_read_pipe" || | 
|  | MangledName == "sub_group_commit_write_pipe" || | 
|  | MangledName == "sub_group_commit_read_pipe" || | 
|  | MangledName == "to_global" || MangledName == "to_local" || | 
|  | MangledName == "to_private"; | 
|  | } | 
|  |  | 
|  | static bool isEnqueueKernelBI(const StringRef MangledName) { | 
|  | return MangledName == "__enqueue_kernel_basic" || | 
|  | MangledName == "__enqueue_kernel_basic_events" || | 
|  | MangledName == "__enqueue_kernel_varargs" || | 
|  | MangledName == "__enqueue_kernel_events_varargs"; | 
|  | } | 
|  |  | 
|  | static bool isKernelQueryBI(const StringRef MangledName) { | 
|  | return MangledName == "__get_kernel_work_group_size_impl" || | 
|  | MangledName == "__get_kernel_sub_group_count_for_ndrange_impl" || | 
|  | MangledName == "__get_kernel_max_sub_group_size_for_ndrange_impl" || | 
|  | MangledName == "__get_kernel_preferred_work_group_size_multiple_impl"; | 
|  | } | 
|  |  | 
|  | static bool isNonMangledOCLBuiltin(StringRef Name) { | 
|  | if (!Name.startswith("__")) | 
|  | return false; | 
|  |  | 
|  | return isEnqueueKernelBI(Name) || isKernelQueryBI(Name) || | 
|  | isPipeOrAddressSpaceCastBI(Name.drop_front(2)) || | 
|  | Name == "__translate_sampler_initializer"; | 
|  | } | 
|  |  | 
|  | std::string getOclOrSpirvBuiltinDemangledName(StringRef Name) { | 
|  | bool IsNonMangledOCL = isNonMangledOCLBuiltin(Name); | 
|  | bool IsNonMangledSPIRV = Name.startswith("__spirv_"); | 
|  | bool IsMangled = Name.startswith("_Z"); | 
|  |  | 
|  | if (!IsNonMangledOCL && !IsNonMangledSPIRV && !IsMangled) | 
|  | return std::string(); | 
|  |  | 
|  | // Try to use the itanium demangler. | 
|  | if (char *DemangledName = itaniumDemangle(Name.data())) { | 
|  | std::string Result = DemangledName; | 
|  | free(DemangledName); | 
|  | return Result; | 
|  | } | 
|  | // Otherwise use simple demangling to return the function name. | 
|  | if (IsNonMangledOCL || IsNonMangledSPIRV) | 
|  | return Name.str(); | 
|  |  | 
|  | // Autocheck C++, maybe need to do explicit check of the source language. | 
|  | // OpenCL C++ built-ins are declared in cl namespace. | 
|  | // TODO: consider using 'St' abbriviation for cl namespace mangling. | 
|  | // Similar to ::std:: in C++. | 
|  | size_t Start, Len = 0; | 
|  | size_t DemangledNameLenStart = 2; | 
|  | if (Name.startswith("_ZN")) { | 
|  | // Skip CV and ref qualifiers. | 
|  | size_t NameSpaceStart = Name.find_first_not_of("rVKRO", 3); | 
|  | // All built-ins are in the ::cl:: namespace. | 
|  | if (Name.substr(NameSpaceStart, 11) != "2cl7__spirv") | 
|  | return std::string(); | 
|  | DemangledNameLenStart = NameSpaceStart + 11; | 
|  | } | 
|  | Start = Name.find_first_not_of("0123456789", DemangledNameLenStart); | 
|  | Name.substr(DemangledNameLenStart, Start - DemangledNameLenStart) | 
|  | .getAsInteger(10, Len); | 
|  | return Name.substr(Start, Len).str(); | 
|  | } | 
|  |  | 
|  | const Type *getTypedPtrEltType(const Type *Ty) { | 
|  | auto PType = dyn_cast<PointerType>(Ty); | 
|  | if (!PType || PType->isOpaque()) | 
|  | return Ty; | 
|  | return PType->getNonOpaquePointerElementType(); | 
|  | } | 
|  |  | 
|  | static bool hasBuiltinTypePrefix(StringRef Name) { | 
|  | if (Name.starts_with("opencl.") || Name.starts_with("spirv.")) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool isSpecialOpaqueType(const Type *Ty) { | 
|  | const StructType *SType = dyn_cast<StructType>(getTypedPtrEltType(Ty)); | 
|  | if (SType && SType->hasName()) | 
|  | return hasBuiltinTypePrefix(SType->getName()); | 
|  |  | 
|  | if (const TargetExtType *EType = | 
|  | dyn_cast<TargetExtType>(getTypedPtrEltType(Ty))) | 
|  | return hasBuiltinTypePrefix(EType->getName()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } // namespace llvm |