| 1 | //===-- SPIRVGlobalRegistry.h - SPIR-V Global Registry ----------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // SPIRVGlobalRegistry is used to maintain rich type information required for |
| 10 | // SPIR-V even after lowering from LLVM IR to GMIR. It can convert an llvm::Type |
| 11 | // into an OpTypeXXX instruction, and map it to a virtual register. Also it |
| 12 | // builds and supports consistency of constants and global variables. |
| 13 | // |
| 14 | //===----------------------------------------------------------------------===// |
| 15 | |
| 16 | #ifndef LLVM_LIB_TARGET_SPIRV_SPIRVGLOBALREGISTRY_H |
| 17 | #define LLVM_LIB_TARGET_SPIRV_SPIRVGLOBALREGISTRY_H |
| 18 | |
| 19 | #include "MCTargetDesc/SPIRVBaseInfo.h" |
| 20 | #include "SPIRVIRMapping.h" |
| 21 | #include "SPIRVInstrInfo.h" |
| 22 | #include "SPIRVTypeInst.h" |
| 23 | #include "llvm/ADT/DenseSet.h" |
| 24 | #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" |
| 25 | #include "llvm/IR/Constant.h" |
| 26 | #include "llvm/IR/TypedPointerType.h" |
| 27 | |
| 28 | namespace llvm { |
| 29 | class SPIRVSubtarget; |
| 30 | |
| 31 | using StructOffsetDecorator = std::function<void(Register)>; |
| 32 | |
| 33 | class SPIRVGlobalRegistry : public SPIRVIRMapping { |
| 34 | // Registers holding values which have types associated with them. |
| 35 | // Initialized upon VReg definition in IRTranslator. |
| 36 | // Do not confuse this with DuplicatesTracker as DT maps Type* to <MF, Reg> |
| 37 | // where Reg = OpType... |
| 38 | // while VRegToTypeMap tracks SPIR-V type assigned to other regs (i.e. not |
| 39 | // type-declaring ones). |
| 40 | DenseMap<const MachineFunction *, DenseMap<Register, SPIRVTypeInst>> |
| 41 | VRegToTypeMap; |
| 42 | |
| 43 | DenseMap<SPIRVTypeInst, const Type *> SPIRVToLLVMType; |
| 44 | |
| 45 | // map a Function to its definition (as a machine instruction operand) |
| 46 | DenseMap<const Function *, const MachineOperand *> FunctionToInstr; |
| 47 | DenseMap<const MachineInstr *, const Function *> FunctionToInstrRev; |
| 48 | // map function pointer (as a machine instruction operand) to the used |
| 49 | // Function |
| 50 | DenseMap<const MachineOperand *, const Function *> InstrToFunction; |
| 51 | // Maps Functions to their calls (in a form of the machine instruction, |
| 52 | // OpFunctionCall) that happened before the definition is available |
| 53 | DenseMap<const Function *, SmallPtrSet<MachineInstr *, 8>> ForwardCalls; |
| 54 | // map a Function to its original return type before the clone function was |
| 55 | // created during substitution of aggregate arguments |
| 56 | // (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`) |
| 57 | DenseMap<Value *, Type *> MutatedAggRet; |
| 58 | // map an instruction to its value's attributes (type, name) |
| 59 | DenseMap<MachineInstr *, std::pair<Type *, std::string>> ValueAttrs; |
| 60 | |
| 61 | SmallPtrSet<const Type *, 4> TypesInProcessing; |
| 62 | DenseMap<const Type *, SPIRVTypeInst> ForwardPointerTypes; |
| 63 | |
| 64 | // Struct types decorated with Block, recorded at the point the decoration |
| 65 | // is emitted. |
| 66 | DenseSet<SPIRVTypeInst> BlockDecoratedTypes; |
| 67 | |
| 68 | // Stores for each function the last inserted SPIR-V Type. |
| 69 | // See: SPIRVGlobalRegistry::createOpType. |
| 70 | DenseMap<const MachineFunction *, MachineInstr *> LastInsertedTypeMap; |
| 71 | |
| 72 | // if a function returns a pointer, this is to map it into TypedPointerType |
| 73 | DenseMap<const Function *, TypedPointerType *> FunResPointerTypes; |
| 74 | |
| 75 | // Current target's datalayout. |
| 76 | DataLayout DL; |
| 77 | |
| 78 | // Holds the maximum ID we have in the module. |
| 79 | unsigned Bound; |
| 80 | |
| 81 | // Maps values associated with untyped pointers into deduced element types of |
| 82 | // untyped pointers. |
| 83 | DenseMap<Value *, Type *> DeducedElTys; |
| 84 | // Maps composite values to deduced types where untyped pointers are replaced |
| 85 | // with typed ones. |
| 86 | DenseMap<Value *, Type *> DeducedNestedTys; |
| 87 | |
| 88 | // Element type for each untyped-pointer register, which |
| 89 | // OpTypeUntypedPointerKHR omits but OpUntypedVariableKHR needs as a Data |
| 90 | // Type. |
| 91 | DenseMap<std::pair<const MachineFunction *, Register>, SPIRVTypeInst> |
| 92 | UntypedPointerElementTypes; |
| 93 | // Maps values to "assign type" calls, thus being a registry of created |
| 94 | // Intrinsic::spv_assign_ptr_type instructions. |
| 95 | DenseMap<Value *, CallInst *> AssignPtrTypeInstr; |
| 96 | |
| 97 | // Maps OpVariable and OpFunction-related v-regs to its LLVM IR definition. |
| 98 | DenseMap<std::pair<const MachineFunction *, Register>, const Value *> Reg2GO; |
| 99 | |
| 100 | // map of aliasing decorations to aliasing metadata |
| 101 | DenseMap<const MDNode *, MachineInstr *> AliasInstMDMap; |
| 102 | |
| 103 | // Add a new OpTypeXXX instruction without checking for duplicates. |
| 104 | SPIRVTypeInst createSPIRVType(const Type *Type, MachineIRBuilder &MIRBuilder, |
| 105 | SPIRV::AccessQualifier::AccessQualifier AQ, |
| 106 | bool ExplicitLayoutRequired, bool EmitIR); |
| 107 | SPIRVTypeInst |
| 108 | findSPIRVType(const Type *Ty, MachineIRBuilder &MIRBuilder, |
| 109 | SPIRV::AccessQualifier::AccessQualifier accessQual, |
| 110 | bool ExplicitLayoutRequired, bool EmitIR); |
| 111 | SPIRVTypeInst |
| 112 | restOfCreateSPIRVType(const Type *Type, MachineIRBuilder &MIRBuilder, |
| 113 | SPIRV::AccessQualifier::AccessQualifier AccessQual, |
| 114 | bool ExplicitLayoutRequired, bool EmitIR); |
| 115 | |
| 116 | // Internal function creating the Types/Constants at the correct position |
| 117 | // in the function by tweaking the passed "MIRBuilder" insertion point and |
| 118 | // restoring it to the correct position. "Op" should be the function creating |
| 119 | // the specific operation you need, and should return the newly created |
| 120 | // instruction. |
| 121 | const MachineInstr *createConstOrTypeAtFunctionEntry( |
| 122 | MachineIRBuilder &MIRBuilder, |
| 123 | std::function<MachineInstr *(MachineIRBuilder &)> Op); |
| 124 | |
| 125 | public: |
| 126 | SPIRVGlobalRegistry(DataLayout DL); |
| 127 | |
| 128 | MachineFunction *CurMF; |
| 129 | |
| 130 | void setBound(unsigned V) { Bound = V; } |
| 131 | unsigned getBound() { return Bound; } |
| 132 | |
| 133 | void addGlobalObject(const Value *V, const MachineFunction *MF, Register R) { |
| 134 | Reg2GO[std::make_pair(x&: MF, y&: R)] = V; |
| 135 | } |
| 136 | const Value *getGlobalObject(const MachineFunction *MF, Register R) { |
| 137 | auto It = Reg2GO.find(Val: std::make_pair(x&: MF, y&: R)); |
| 138 | return It == Reg2GO.end() ? nullptr : It->second; |
| 139 | } |
| 140 | |
| 141 | // Add a record to the map of function return pointer types. |
| 142 | void addReturnType(const Function *ArgF, TypedPointerType *DerivedTy) { |
| 143 | FunResPointerTypes[ArgF] = DerivedTy; |
| 144 | } |
| 145 | // Find a record in the map of function return pointer types. |
| 146 | const TypedPointerType *findReturnType(const Function *ArgF) { |
| 147 | auto It = FunResPointerTypes.find(Val: ArgF); |
| 148 | return It == FunResPointerTypes.end() ? nullptr : It->second; |
| 149 | } |
| 150 | |
| 151 | // A registry of "assign type" records: |
| 152 | // - Add a record. |
| 153 | void addAssignPtrTypeInstr(Value *Val, CallInst *AssignPtrTyCI) { |
| 154 | AssignPtrTypeInstr[Val] = AssignPtrTyCI; |
| 155 | } |
| 156 | // - Find a record. |
| 157 | CallInst *findAssignPtrTypeInstr(const Value *Val) { |
| 158 | auto It = AssignPtrTypeInstr.find(Val); |
| 159 | return It == AssignPtrTypeInstr.end() ? nullptr : It->second; |
| 160 | } |
| 161 | // - Find a record and update its key or add a new record, if found. |
| 162 | void updateIfExistAssignPtrTypeInstr(Value *OldVal, Value *NewVal, |
| 163 | bool DeleteOld) { |
| 164 | if (CallInst *CI = findAssignPtrTypeInstr(Val: OldVal)) { |
| 165 | if (DeleteOld) |
| 166 | AssignPtrTypeInstr.erase(Val: OldVal); |
| 167 | AssignPtrTypeInstr[NewVal] = CI; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | // A registry of mutated values |
| 172 | // (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`): |
| 173 | // - Add a record. |
| 174 | void addMutated(Value *Val, Type *Ty) { MutatedAggRet[Val] = Ty; } |
| 175 | // - Find a record. |
| 176 | Type *findMutated(const Value *Val) { |
| 177 | auto It = MutatedAggRet.find(Val); |
| 178 | return It == MutatedAggRet.end() ? nullptr : It->second; |
| 179 | } |
| 180 | |
| 181 | // A registry of value's attributes (type, name) |
| 182 | // - Add a record. |
| 183 | void addValueAttrs(MachineInstr *Key, std::pair<Type *, std::string> Val) { |
| 184 | ValueAttrs[Key] = Val; |
| 185 | } |
| 186 | // - Find a record. |
| 187 | bool findValueAttrs(const MachineInstr *Key, Type *&Ty, StringRef &Name) { |
| 188 | auto It = ValueAttrs.find(Val: Key); |
| 189 | if (It == ValueAttrs.end()) |
| 190 | return false; |
| 191 | Ty = It->second.first; |
| 192 | Name = It->second.second; |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | // Deduced element types of untyped pointers and composites: |
| 197 | // - Add a record to the map of deduced element types. |
| 198 | void addDeducedElementType(Value *Val, Type *Ty) { DeducedElTys[Val] = Ty; } |
| 199 | // - Find a record in the map of deduced element types. |
| 200 | Type *findDeducedElementType(const Value *Val) { |
| 201 | auto It = DeducedElTys.find(Val); |
| 202 | return It == DeducedElTys.end() ? nullptr : It->second; |
| 203 | } |
| 204 | // - Find a record and update its key or add a new record, if found. |
| 205 | void updateIfExistDeducedElementType(Value *OldVal, Value *NewVal, |
| 206 | bool DeleteOld) { |
| 207 | if (Type *Ty = findDeducedElementType(Val: OldVal)) { |
| 208 | if (DeleteOld) |
| 209 | DeducedElTys.erase(Val: OldVal); |
| 210 | DeducedElTys[NewVal] = Ty; |
| 211 | } |
| 212 | } |
| 213 | // - Add a record to the map of deduced composite types. |
| 214 | void addDeducedCompositeType(Value *Val, Type *Ty) { |
| 215 | DeducedNestedTys[Val] = Ty; |
| 216 | } |
| 217 | // - Find a record in the map of deduced composite types. |
| 218 | Type *findDeducedCompositeType(const Value *Val) { |
| 219 | auto It = DeducedNestedTys.find(Val); |
| 220 | return It == DeducedNestedTys.end() ? nullptr : It->second; |
| 221 | } |
| 222 | |
| 223 | // Store the element type associated with an untyped-pointer register. |
| 224 | void setUntypedPtrElementType(Register Reg, SPIRVTypeInst ElemType) { |
| 225 | UntypedPointerElementTypes[{CurMF, Reg}] = ElemType; |
| 226 | } |
| 227 | // Get the element type associated with an untyped-pointer register. |
| 228 | SPIRVTypeInst getUntypedPtrElementType(Register Reg) const { |
| 229 | auto It = UntypedPointerElementTypes.find(Val: {CurMF, Reg}); |
| 230 | return It == UntypedPointerElementTypes.end() ? nullptr : It->second; |
| 231 | } |
| 232 | // - Find a type of the given Global value |
| 233 | Type *getDeducedGlobalValueType(const GlobalValue *Global) { |
| 234 | // we may know element type if it was deduced earlier |
| 235 | Type *ElementTy = findDeducedElementType(Val: Global); |
| 236 | if (!ElementTy) { |
| 237 | // or we may know element type if it's associated with a composite |
| 238 | // value |
| 239 | if (Value *GlobalElem = |
| 240 | Global->getNumOperands() > 0 ? Global->getOperand(i: 0) : nullptr) |
| 241 | ElementTy = findDeducedCompositeType(Val: GlobalElem); |
| 242 | else if (const Function *Fn = dyn_cast<Function>(Val: Global)) |
| 243 | ElementTy = SPIRV::getOriginalFunctionType(F: *Fn); |
| 244 | } |
| 245 | return ElementTy ? ElementTy : Global->getValueType(); |
| 246 | } |
| 247 | |
| 248 | // Map a machine operand that represents a use of a function via function |
| 249 | // pointer to a machine operand that represents the function definition. |
| 250 | // Return either the register or invalid value, because we have no context for |
| 251 | // a good diagnostic message in case of unexpectedly missing references. |
| 252 | const MachineOperand *getFunctionDefinitionByUse(const MachineOperand *Use) { |
| 253 | auto ResF = InstrToFunction.find(Val: Use); |
| 254 | if (ResF == InstrToFunction.end()) |
| 255 | return nullptr; |
| 256 | auto ResReg = FunctionToInstr.find(Val: ResF->second); |
| 257 | return ResReg == FunctionToInstr.end() ? nullptr : ResReg->second; |
| 258 | } |
| 259 | |
| 260 | // Map a Function to a machine instruction that represents the function |
| 261 | // definition. |
| 262 | const MachineInstr *getFunctionDefinition(const Function *F) { |
| 263 | if (!F) |
| 264 | return nullptr; |
| 265 | auto MOIt = FunctionToInstr.find(Val: F); |
| 266 | return MOIt == FunctionToInstr.end() ? nullptr : MOIt->second->getParent(); |
| 267 | } |
| 268 | |
| 269 | // Map a Function to a machine instruction that represents the function |
| 270 | // definition. |
| 271 | const Function *getFunctionByDefinition(const MachineInstr *MI) { |
| 272 | if (!MI) |
| 273 | return nullptr; |
| 274 | auto FIt = FunctionToInstrRev.find(Val: MI); |
| 275 | return FIt == FunctionToInstrRev.end() ? nullptr : FIt->second; |
| 276 | } |
| 277 | |
| 278 | // map function pointer (as a machine instruction operand) to the used |
| 279 | // Function |
| 280 | void recordFunctionPointer(const MachineOperand *MO, const Function *F) { |
| 281 | InstrToFunction[MO] = F; |
| 282 | } |
| 283 | |
| 284 | // map a Function to its definition (as a machine instruction) |
| 285 | void recordFunctionDefinition(const Function *F, const MachineOperand *MO) { |
| 286 | FunctionToInstr[F] = MO; |
| 287 | FunctionToInstrRev[MO->getParent()] = F; |
| 288 | } |
| 289 | |
| 290 | // Return true if any OpConstantFunctionPointerINTEL were generated |
| 291 | bool hasConstFunPtr() { return !InstrToFunction.empty(); } |
| 292 | |
| 293 | // Add a record about forward function call. |
| 294 | void addForwardCall(const Function *F, MachineInstr *MI) { |
| 295 | ForwardCalls[F].insert(Ptr: MI); |
| 296 | } |
| 297 | |
| 298 | // Map a Function to the vector of machine instructions that represents |
| 299 | // forward function calls or to nullptr if not found. |
| 300 | SmallPtrSet<MachineInstr *, 8> *getForwardCalls(const Function *F) { |
| 301 | auto It = ForwardCalls.find(Val: F); |
| 302 | return It == ForwardCalls.end() ? nullptr : &It->second; |
| 303 | } |
| 304 | |
| 305 | // Get or create a SPIR-V type corresponding the given LLVM IR type, |
| 306 | // and map it to the given VReg. |
| 307 | SPIRVTypeInst assignTypeToVReg(const Type *Type, Register VReg, |
| 308 | MachineIRBuilder &MIRBuilder, |
| 309 | SPIRV::AccessQualifier::AccessQualifier AQ, |
| 310 | bool EmitIR); |
| 311 | SPIRVTypeInst assignIntTypeToVReg(unsigned BitWidth, Register VReg, |
| 312 | MachineInstr &I, const SPIRVInstrInfo &TII); |
| 313 | |
| 314 | // In cases where the SPIR-V type is already known, this function can be |
| 315 | // used to map it to the given VReg. |
| 316 | void assignSPIRVTypeToVReg(SPIRVTypeInst Type, Register VReg, |
| 317 | const MachineFunction &MF); |
| 318 | |
| 319 | // Either generate a new OpTypeXXX instruction or return an existing one |
| 320 | // corresponding to the given LLVM IR type. |
| 321 | // EmitIR controls if we emit GMIR or SPV constants (e.g. for array sizes) |
| 322 | // because this method may be called from InstructionSelector and we don't |
| 323 | // want to emit extra IR instructions there. |
| 324 | SPIRVTypeInst getOrCreateSPIRVType(const Type *Type, MachineInstr &I, |
| 325 | SPIRV::AccessQualifier::AccessQualifier AQ, |
| 326 | bool EmitIR) { |
| 327 | MachineIRBuilder MIRBuilder(I); |
| 328 | return getOrCreateSPIRVType(Type, MIRBuilder, AQ, EmitIR); |
| 329 | } |
| 330 | |
| 331 | SPIRVTypeInst getOrCreateSPIRVType(const Type *Type, |
| 332 | MachineIRBuilder &MIRBuilder, |
| 333 | SPIRV::AccessQualifier::AccessQualifier AQ, |
| 334 | bool EmitIR) { |
| 335 | return getOrCreateSPIRVType(Type, MIRBuilder, AQ, ExplicitLayoutRequired: false, EmitIR); |
| 336 | } |
| 337 | |
| 338 | const Type *getTypeForSPIRVType(SPIRVTypeInst Ty) const { |
| 339 | auto Res = SPIRVToLLVMType.find(Val: Ty); |
| 340 | assert(Res != SPIRVToLLVMType.end()); |
| 341 | return Res->second; |
| 342 | } |
| 343 | |
| 344 | // Return a pointee's type, or nullptr otherwise. |
| 345 | SPIRVTypeInst getPointeeType(SPIRVTypeInst PtrType); |
| 346 | |
| 347 | // Either generate a new OpTypeXXX instruction or return an existing one |
| 348 | // corresponding to the given string containing the name of the builtin type. |
| 349 | // Return nullptr if unable to recognize SPIRV type name from `TypeStr`. |
| 350 | SPIRVTypeInst getOrCreateSPIRVTypeByName( |
| 351 | StringRef TypeStr, MachineIRBuilder &MIRBuilder, bool EmitIR, |
| 352 | SPIRV::StorageClass::StorageClass SC = SPIRV::StorageClass::Function, |
| 353 | SPIRV::AccessQualifier::AccessQualifier AQ = |
| 354 | SPIRV::AccessQualifier::ReadWrite); |
| 355 | |
| 356 | // Return the SPIR-V type instruction corresponding to the given VReg, or |
| 357 | // nullptr if no such type instruction exists. The second argument MF |
| 358 | // allows to search for the association in a context of the machine functions |
| 359 | // than the current one, without switching between different "current" machine |
| 360 | // functions. |
| 361 | SPIRVTypeInst getSPIRVTypeForVReg(Register VReg, |
| 362 | const MachineFunction *MF = nullptr) const; |
| 363 | |
| 364 | // Return the result type of the instruction defining the register. |
| 365 | SPIRVTypeInst getResultType(Register VReg, MachineFunction *MF = nullptr); |
| 366 | |
| 367 | // Return the VReg holding the result of the given OpTypeXXX instruction. |
| 368 | Register getSPIRVTypeID(SPIRVTypeInst SpirvType) const; |
| 369 | |
| 370 | // Return previous value of the current machine function |
| 371 | MachineFunction *setCurrentFunc(MachineFunction &MF) { |
| 372 | MachineFunction *Ret = CurMF; |
| 373 | CurMF = &MF; |
| 374 | return Ret; |
| 375 | } |
| 376 | |
| 377 | // Return true if the type is an aggregate type. |
| 378 | bool isAggregateType(SPIRVTypeInst Type) const { |
| 379 | return Type && (Type->getOpcode() == SPIRV::OpTypeStruct || |
| 380 | Type->getOpcode() == SPIRV::OpTypeArray); |
| 381 | } |
| 382 | |
| 383 | // Whether the given VReg has an OpTypeXXX instruction mapped to it with the |
| 384 | // given opcode (e.g. OpTypeFloat). |
| 385 | bool isScalarOfType(Register VReg, unsigned TypeOpcode) const; |
| 386 | |
| 387 | // Return true if the given VReg's assigned SPIR-V type is either a scalar |
| 388 | // matching the given opcode, or a vector with an element type matching that |
| 389 | // opcode (e.g. OpTypeBool, or OpTypeVector %x 4, where %x is OpTypeBool). |
| 390 | bool isScalarOrVectorOfType(Register VReg, unsigned TypeOpcode) const; |
| 391 | |
| 392 | // Returns true if `Type` is a resource type. This could be an image type |
| 393 | // or a struct for a buffer decorated with the block decoration. |
| 394 | bool isResourceType(SPIRVTypeInst Type) const; |
| 395 | |
| 396 | // Return number of elements in a vector if the argument is associated with |
| 397 | // a vector type. Return 1 for a scalar type, and 0 for a missing type. |
| 398 | unsigned getScalarOrVectorComponentCount(Register VReg) const; |
| 399 | unsigned getScalarOrVectorComponentCount(SPIRVTypeInst Type) const; |
| 400 | |
| 401 | // Return the component type in a vector if the argument is associated with |
| 402 | // a vector type. Returns the argument itself for other types, and nullptr |
| 403 | // for a missing type. |
| 404 | SPIRVTypeInst getScalarOrVectorComponentType(SPIRVTypeInst Type) const; |
| 405 | |
| 406 | // For vectors or scalars of booleans, integers and floats, return the scalar |
| 407 | // type's bitwidth. Otherwise calls llvm_unreachable(). |
| 408 | unsigned getScalarOrVectorBitWidth(SPIRVTypeInst Type) const; |
| 409 | |
| 410 | // For vectors or scalars of integers and floats, return total bitwidth of the |
| 411 | // argument. Otherwise returns 0. |
| 412 | unsigned getNumScalarOrVectorTotalBitWidth(SPIRVTypeInst Type) const; |
| 413 | |
| 414 | // True if a pointer to this element type must stay typed rather than become |
| 415 | // OpTypeUntypedPointerKHR. Such an element type is either a function type, |
| 416 | // which an untyped pointer cannot express, or an opaque builtin type such as |
| 417 | // an image or a sampler. |
| 418 | bool shouldKeepTypedPtrType(SPIRVTypeInst ElemType) const; |
| 419 | |
| 420 | // True if a pointer to this element type should be emitted as |
| 421 | // OpTypeUntypedPointerKHR rather than OpTypePointer. |
| 422 | bool shouldUseUntypedPointer(SPIRVTypeInst ElemType, |
| 423 | const SPIRVSubtarget &ST) const; |
| 424 | |
| 425 | // Byte size of a pointer value's IR-deduced element type, or 0 if unknown. |
| 426 | // Array indexing and copy strides work in terms of the alloc size, so this |
| 427 | // reports the size a value of that type occupies in an array. For OpenCL |
| 428 | // that means a 3-component vector is as large as a 4-component one. |
| 429 | unsigned getDeducedPointeeByteSize(const Value *PtrVal) { |
| 430 | if (Type *ElemTy = findDeducedElementType(Val: PtrVal)) |
| 431 | return DL.getTypeAllocSize(Ty: ElemTy).getFixedValue(); |
| 432 | return 0; |
| 433 | } |
| 434 | |
| 435 | // Returns either pointer to integer type, that may be a type of vector |
| 436 | // elements or an original type, or nullptr if the argument is niether |
| 437 | // an integer scalar, nor an integer vector |
| 438 | SPIRVTypeInst retrieveScalarOrVectorIntType(SPIRVTypeInst Type) const; |
| 439 | |
| 440 | // For integer vectors or scalars, return whether the integers are signed. |
| 441 | bool isScalarOrVectorSigned(SPIRVTypeInst Type) const; |
| 442 | |
| 443 | // Gets the storage class of the pointer type assigned to this vreg. |
| 444 | SPIRV::StorageClass::StorageClass getPointerStorageClass(Register VReg) const; |
| 445 | SPIRV::StorageClass::StorageClass |
| 446 | getPointerStorageClass(SPIRVTypeInst Type) const; |
| 447 | |
| 448 | // Return the number of bits SPIR-V pointers and size_t variables require. |
| 449 | unsigned getPointerSize() const { |
| 450 | return DL.getPointerSizeInBits(/* AS = */ AS: 0); |
| 451 | } |
| 452 | |
| 453 | // Returns true if two types are defined and are compatible in a sense of |
| 454 | // OpBitcast instruction |
| 455 | bool isBitcastCompatible(SPIRVTypeInst Type1, SPIRVTypeInst Type2) const; |
| 456 | |
| 457 | // Informs about removal of the machine instruction and invalidates data |
| 458 | // structures referring this instruction. |
| 459 | void invalidateMachineInstr(MachineInstr *MI); |
| 460 | |
| 461 | private: |
| 462 | SPIRVTypeInst getOpTypeBool(MachineIRBuilder &MIRBuilder); |
| 463 | |
| 464 | const Type *adjustIntTypeByWidth(const Type *Ty) const; |
| 465 | unsigned adjustOpTypeIntWidth(unsigned Width) const; |
| 466 | |
| 467 | SPIRVTypeInst getOrCreateSPIRVType(const Type *Type, |
| 468 | MachineIRBuilder &MIRBuilder, |
| 469 | SPIRV::AccessQualifier::AccessQualifier AQ, |
| 470 | bool ExplicitLayoutRequired, bool EmitIR); |
| 471 | |
| 472 | SPIRVTypeInst getOpTypeInt(unsigned Width, MachineIRBuilder &MIRBuilder, |
| 473 | bool IsSigned = false); |
| 474 | |
| 475 | SPIRVTypeInst getOpTypeFloat(uint32_t Width, MachineIRBuilder &MIRBuilder); |
| 476 | |
| 477 | SPIRVTypeInst getOpTypeFloat(uint32_t Width, MachineIRBuilder &MIRBuilder, |
| 478 | SPIRV::FPEncoding::FPEncoding FPEncode); |
| 479 | |
| 480 | SPIRVTypeInst getOpTypeVectorImpl(uint32_t NumElems, SPIRVTypeInst ElemType, |
| 481 | MachineIRBuilder &MIRBuilder, |
| 482 | bool IsLongVectorEXT = false); |
| 483 | |
| 484 | SPIRVTypeInst getOpTypeVector(uint32_t NumElems, SPIRVTypeInst ElemType, |
| 485 | MachineIRBuilder &MIRBuilder); |
| 486 | |
| 487 | SPIRVTypeInst getOpTypeVectorIdEXT(uint32_t NumElems, SPIRVTypeInst ElemType, |
| 488 | MachineIRBuilder &MIRBuilder); |
| 489 | |
| 490 | SPIRVTypeInst getOpTypeArray(uint32_t NumElems, SPIRVTypeInst ElemType, |
| 491 | MachineIRBuilder &MIRBuilder, |
| 492 | bool ExplicitLayoutRequired, bool EmitIR); |
| 493 | |
| 494 | SPIRVTypeInst getOpTypeOpaque(const StructType *Ty, |
| 495 | MachineIRBuilder &MIRBuilder); |
| 496 | |
| 497 | SPIRVTypeInst getOpTypeStruct(const StructType *Ty, |
| 498 | MachineIRBuilder &MIRBuilder, |
| 499 | SPIRV::AccessQualifier::AccessQualifier AccQual, |
| 500 | StructOffsetDecorator Decorator, bool EmitIR); |
| 501 | |
| 502 | SPIRVTypeInst getOpTypePointer(SPIRV::StorageClass::StorageClass SC, |
| 503 | SPIRVTypeInst ElemType, |
| 504 | MachineIRBuilder &MIRBuilder, Register Reg); |
| 505 | |
| 506 | SPIRVTypeInst |
| 507 | getOpTypeFunction(const FunctionType *Ty, SPIRVTypeInst RetType, |
| 508 | const SmallVectorImpl<SPIRVTypeInst> &ArgTypes, |
| 509 | MachineIRBuilder &MIRBuilder); |
| 510 | |
| 511 | SPIRVTypeInst |
| 512 | getOrCreateSpecialType(const Type *Ty, MachineIRBuilder &MIRBuilder, |
| 513 | SPIRV::AccessQualifier::AccessQualifier AccQual); |
| 514 | |
| 515 | SPIRVTypeInst finishCreatingSPIRVType(const Type *LLVMTy, |
| 516 | SPIRVTypeInst SpirvType); |
| 517 | Register getOrCreateBaseRegister(Constant *Val, MachineInstr &I, |
| 518 | SPIRVTypeInst SpvType, |
| 519 | const SPIRVInstrInfo &TII, unsigned BitWidth, |
| 520 | bool ZeroAsNull); |
| 521 | Register getOrCreateCompositeOrNull(Constant *Val, MachineInstr &I, |
| 522 | SPIRVTypeInst SpvType, |
| 523 | const SPIRVInstrInfo &TII, Constant *CA, |
| 524 | unsigned BitWidth, unsigned ElemCnt, |
| 525 | bool ZeroAsNull = true); |
| 526 | |
| 527 | Register getOrCreateIntCompositeOrNull(uint64_t Val, |
| 528 | MachineIRBuilder &MIRBuilder, |
| 529 | SPIRVTypeInst SpvType, bool EmitIR, |
| 530 | Constant *CA, unsigned BitWidth, |
| 531 | unsigned ElemCnt); |
| 532 | |
| 533 | // Returns a pointer to a SPIR-V pointer type with the given base type and |
| 534 | // storage class. It is the responsibility of the caller to make sure the |
| 535 | // decorations on the base type are valid for the given storage class. For |
| 536 | // example, it has the correct offset and stride decorations. |
| 537 | // ForceTyped keeps an OpTypePointer even when untyped pointers are available, |
| 538 | // for cases where the pointee type must be preserved (e.g. a byval/byref/sret |
| 539 | // aggregate argument). |
| 540 | SPIRVTypeInst getOrCreateSPIRVPointerTypeInternal( |
| 541 | SPIRVTypeInst BaseType, MachineIRBuilder &MIRBuilder, |
| 542 | SPIRV::StorageClass::StorageClass SC, bool ForceTyped = false); |
| 543 | |
| 544 | void addStructOffsetDecorations(Register Reg, StructType *Ty, |
| 545 | MachineIRBuilder &MIRBuilder); |
| 546 | void addArrayStrideDecorations(Register Reg, Type *ElementType, |
| 547 | MachineIRBuilder &MIRBuilder); |
| 548 | |
| 549 | void constrainSelectedInstRegOperands(MachineInstrBuilder &MIB) const; |
| 550 | |
| 551 | SPIRVTypeInst |
| 552 | getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, |
| 553 | SPIRVTypeInst SampledType, SPIRV::Dim::Dim Dim, |
| 554 | uint32_t Depth, uint32_t Arrayed, |
| 555 | uint32_t Multisampled, uint32_t Sampled, |
| 556 | SPIRV::ImageFormat::ImageFormat ImageFormat, |
| 557 | SPIRV::AccessQualifier::AccessQualifier AccQual); |
| 558 | |
| 559 | public: |
| 560 | Register buildConstantInt(uint64_t Val, MachineIRBuilder &MIRBuilder, |
| 561 | SPIRVTypeInst SpvType, bool EmitIR, |
| 562 | bool ZeroAsNull = true); |
| 563 | Register getOrCreateConstInt(uint64_t Val, MachineInstr &I, |
| 564 | SPIRVTypeInst SpvType, const SPIRVInstrInfo &TII, |
| 565 | bool ZeroAsNull = true); |
| 566 | Register getOrCreateConstInt(const APInt &Val, MachineInstr &I, |
| 567 | SPIRVTypeInst SpvType, const SPIRVInstrInfo &TII, |
| 568 | bool ZeroAsNull = true); |
| 569 | Register createConstInt(const ConstantInt *CI, MachineInstr &I, |
| 570 | SPIRVTypeInst SpvType, const SPIRVInstrInfo &TII, |
| 571 | bool ZeroAsNull); |
| 572 | Register getOrCreateConstFP(APFloat Val, MachineInstr &I, |
| 573 | SPIRVTypeInst SpvType, const SPIRVInstrInfo &TII, |
| 574 | bool ZeroAsNull = true); |
| 575 | Register createConstFP(const ConstantFP *CF, MachineInstr &I, |
| 576 | SPIRVTypeInst SpvType, const SPIRVInstrInfo &TII, |
| 577 | bool ZeroAsNull); |
| 578 | Register buildConstantFP(APFloat Val, MachineIRBuilder &MIRBuilder, |
| 579 | SPIRVTypeInst SpvType = nullptr); |
| 580 | |
| 581 | Register getOrCreateConstVector(uint64_t Val, MachineInstr &I, |
| 582 | SPIRVTypeInst SpvType, |
| 583 | const SPIRVInstrInfo &TII, |
| 584 | bool ZeroAsNull = true); |
| 585 | Register getOrCreateConstVector(const APInt &Val, MachineInstr &I, |
| 586 | SPIRVTypeInst SpvType, |
| 587 | const SPIRVInstrInfo &TII, |
| 588 | bool ZeroAsNull = true); |
| 589 | Register getOrCreateConstVector(APFloat Val, MachineInstr &I, |
| 590 | SPIRVTypeInst SpvType, |
| 591 | const SPIRVInstrInfo &TII, |
| 592 | bool ZeroAsNull = true); |
| 593 | Register getOrCreateConstIntArray(uint64_t Val, size_t Num, MachineInstr &I, |
| 594 | SPIRVTypeInst SpvType, |
| 595 | const SPIRVInstrInfo &TII); |
| 596 | Register getOrCreateConsIntVector(uint64_t Val, MachineIRBuilder &MIRBuilder, |
| 597 | SPIRVTypeInst SpvType, bool EmitIR); |
| 598 | Register getOrCreateConstNullPtr(MachineIRBuilder &MIRBuilder, |
| 599 | SPIRVTypeInst SpvType); |
| 600 | Register buildConstantSampler(Register Res, unsigned AddrMode, unsigned Param, |
| 601 | unsigned FilerMode, |
| 602 | MachineIRBuilder &MIRBuilder); |
| 603 | Register getOrCreateUndef(MachineInstr &I, SPIRVTypeInst SpvType, |
| 604 | const SPIRVInstrInfo &TII); |
| 605 | Register buildGlobalVariable( |
| 606 | Register Reg, SPIRVTypeInst BaseType, StringRef Name, |
| 607 | const GlobalValue *GV, SPIRV::StorageClass::StorageClass Storage, |
| 608 | const MachineInstr *Init, bool IsConst, |
| 609 | const std::optional<SPIRV::LinkageType::LinkageType> &LinkageType, |
| 610 | MachineIRBuilder &MIRBuilder, bool IsInstSelector); |
| 611 | Register getOrCreateGlobalVariableWithBinding(SPIRVTypeInst VarType, |
| 612 | uint32_t Set, uint32_t Binding, |
| 613 | StringRef Name, |
| 614 | MachineIRBuilder &MIRBuilder); |
| 615 | |
| 616 | // Convenient helpers for getting types with check for duplicates. |
| 617 | SPIRVTypeInst getOrCreateSPIRVIntegerType(unsigned BitWidth, |
| 618 | MachineIRBuilder &MIRBuilder); |
| 619 | SPIRVTypeInst getOrCreateSPIRVIntegerType(unsigned BitWidth, MachineInstr &I, |
| 620 | const SPIRVInstrInfo &TII); |
| 621 | SPIRVTypeInst getOrCreateSPIRVType(unsigned BitWidth, MachineInstr &I, |
| 622 | const SPIRVInstrInfo &TII, |
| 623 | unsigned SPIRVOPcode, Type *LLVMTy); |
| 624 | SPIRVTypeInst getOrCreateSPIRVFloatType(unsigned BitWidth, MachineInstr &I, |
| 625 | const SPIRVInstrInfo &TII); |
| 626 | SPIRVTypeInst getOrCreateSPIRVBoolType(MachineIRBuilder &MIRBuilder, |
| 627 | bool EmitIR); |
| 628 | SPIRVTypeInst getOrCreateSPIRVBoolType(MachineInstr &I, |
| 629 | const SPIRVInstrInfo &TII); |
| 630 | SPIRVTypeInst getOrCreateSPIRVVectorType(SPIRVTypeInst BaseType, |
| 631 | unsigned NumElements, |
| 632 | MachineIRBuilder &MIRBuilder, |
| 633 | bool EmitIR); |
| 634 | SPIRVTypeInst getOrCreateSPIRVVectorType(SPIRVTypeInst BaseType, |
| 635 | unsigned NumElements, |
| 636 | MachineInstr &I, |
| 637 | const SPIRVInstrInfo &TII); |
| 638 | SPIRVTypeInst getOpTypeVoid(MachineIRBuilder &MIRBuilder); |
| 639 | |
| 640 | // Returns a pointer to a SPIR-V pointer type with the given base type and |
| 641 | // storage class. The base type will be translated to a SPIR-V type, and the |
| 642 | // appropriate layout decorations will be added to the base type. |
| 643 | // See getOrCreateSPIRVPointerTypeInternal for ForceTyped. |
| 644 | SPIRVTypeInst getOrCreateSPIRVPointerType( |
| 645 | const Type *BaseType, MachineIRBuilder &MIRBuilder, |
| 646 | SPIRV::StorageClass::StorageClass SC, bool ForceTyped = false); |
| 647 | SPIRVTypeInst |
| 648 | getOrCreateSPIRVPointerType(const Type *BaseType, MachineInstr &I, |
| 649 | SPIRV::StorageClass::StorageClass SC, |
| 650 | bool ForceTyped = false); |
| 651 | |
| 652 | // Like getOrCreateSPIRVPointerType, but always returns an OpTypePointer even |
| 653 | // when untyped pointers are available. Use this when the pointee type must be |
| 654 | // preserved (e.g. a byval/byref/sret aggregate argument). |
| 655 | SPIRVTypeInst |
| 656 | getOrCreateSPIRVTypedPointerType(const Type *BaseType, |
| 657 | MachineIRBuilder &MIRBuilder, |
| 658 | SPIRV::StorageClass::StorageClass SC) { |
| 659 | return getOrCreateSPIRVPointerType(BaseType, MIRBuilder, SC, |
| 660 | /*ForceTyped=*/ForceTyped: true); |
| 661 | } |
| 662 | |
| 663 | // Returns a pointer to a SPIR-V pointer type with the given base type and |
| 664 | // storage class. It is the responsibility of the caller to make sure the |
| 665 | // decorations on the base type are valid for the given storage class. For |
| 666 | // example, it has the correct offset and stride decorations. |
| 667 | SPIRVTypeInst |
| 668 | getOrCreateSPIRVPointerType(SPIRVTypeInst BaseType, |
| 669 | MachineIRBuilder &MIRBuilder, |
| 670 | SPIRV::StorageClass::StorageClass SC); |
| 671 | |
| 672 | // Returns a pointer to a SPIR-V pointer type that is the same as `PtrType` |
| 673 | // except the stroage class has been changed to `SC`. It is the responsibility |
| 674 | // of the caller to be sure that the original and new storage class have the |
| 675 | // same layout requirements. |
| 676 | SPIRVTypeInst changePointerStorageClass(SPIRVTypeInst PtrType, |
| 677 | SPIRV::StorageClass::StorageClass SC, |
| 678 | MachineInstr &I); |
| 679 | |
| 680 | // Returns OpTypeUntypedPointerKHR for the given storage class. |
| 681 | SPIRVTypeInst |
| 682 | getOrCreateSPIRVUntypedPointerType(SPIRV::StorageClass::StorageClass SC, |
| 683 | MachineIRBuilder &MIRBuilder); |
| 684 | |
| 685 | SPIRVTypeInst |
| 686 | getOrCreateVulkanBufferType(MachineIRBuilder &MIRBuilder, Type *ElemType, |
| 687 | SPIRV::StorageClass::StorageClass SC, |
| 688 | bool IsWritable, bool EmitIr = false); |
| 689 | |
| 690 | SPIRVTypeInst getOrCreatePaddingType(MachineIRBuilder &MIRBuilder); |
| 691 | |
| 692 | SPIRVTypeInst getOrCreateVulkanPushConstantType(MachineIRBuilder &MIRBuilder, |
| 693 | Type *ElemType); |
| 694 | |
| 695 | SPIRVTypeInst getOrCreateLayoutType(MachineIRBuilder &MIRBuilder, |
| 696 | const TargetExtType *T, |
| 697 | bool EmitIr = false); |
| 698 | |
| 699 | SPIRVTypeInst |
| 700 | getImageType(const TargetExtType *ExtensionType, |
| 701 | const SPIRV::AccessQualifier::AccessQualifier Qualifier, |
| 702 | MachineIRBuilder &MIRBuilder); |
| 703 | |
| 704 | SPIRVTypeInst getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder); |
| 705 | |
| 706 | SPIRVTypeInst getOrCreateOpTypeSampledImage(SPIRVTypeInst ImageType, |
| 707 | MachineIRBuilder &MIRBuilder); |
| 708 | SPIRVTypeInst getOrCreateOpTypeCoopMatr(MachineIRBuilder &MIRBuilder, |
| 709 | const TargetExtType *ExtensionType, |
| 710 | SPIRVTypeInst ElemType, |
| 711 | uint32_t Scope, uint32_t Rows, |
| 712 | uint32_t Columns, uint32_t Use, |
| 713 | bool EmitIR); |
| 714 | SPIRVTypeInst |
| 715 | getOrCreateOpTypePipe(MachineIRBuilder &MIRBuilder, |
| 716 | SPIRV::AccessQualifier::AccessQualifier AccQual); |
| 717 | SPIRVTypeInst getOrCreateOpTypeDeviceEvent(MachineIRBuilder &MIRBuilder); |
| 718 | SPIRVTypeInst getOrCreateOpTypeFunctionWithArgs( |
| 719 | const Type *Ty, SPIRVTypeInst RetType, |
| 720 | const SmallVectorImpl<SPIRVTypeInst> &ArgTypes, |
| 721 | MachineIRBuilder &MIRBuilder); |
| 722 | SPIRVTypeInst getOrCreateOpTypeByOpcode(const Type *Ty, |
| 723 | MachineIRBuilder &MIRBuilder, |
| 724 | unsigned Opcode); |
| 725 | |
| 726 | SPIRVTypeInst getOrCreateUnknownType(const Type *Ty, |
| 727 | MachineIRBuilder &MIRBuilder, |
| 728 | unsigned Opcode, |
| 729 | const ArrayRef<MCOperand> Operands); |
| 730 | |
| 731 | const TargetRegisterClass *getRegClass(SPIRVTypeInst SpvType) const; |
| 732 | LLT getRegType(SPIRVTypeInst SpvType) const; |
| 733 | |
| 734 | MachineInstr *getOrAddMemAliasingINTELInst(MachineIRBuilder &MIRBuilder, |
| 735 | const MDNode *AliasingListMD); |
| 736 | void buildMemAliasingOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, |
| 737 | uint32_t Dec, const MDNode *GVarMD); |
| 738 | // Replace all uses of a |Old| with |New| updates the global registry type |
| 739 | // mappings. |
| 740 | void replaceAllUsesWith(Value *Old, Value *New, bool DeleteOld = true); |
| 741 | |
| 742 | void buildAssignType(IRBuilder<> &B, Type *Ty, Value *Arg, |
| 743 | bool CanUseAnyVectorRank); |
| 744 | void buildAssignPtr(IRBuilder<> &B, Type *ElemTy, Value *Arg); |
| 745 | void updateAssignType(CallInst *AssignCI, Value *Arg, Value *OfType); |
| 746 | }; |
| 747 | } // end namespace llvm |
| 748 | #endif // LLVM_LIB_TARGET_SPIRV_SPIRVGLOBALREGISTRY_H |
| 749 | |