@@ -249,6 +249,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
249249
250250 bool selectUnmergeValues (MachineInstr &I) const ;
251251
252+ // Utilities
252253 Register buildI32Constant (uint32_t Val, MachineInstr &I,
253254 const SPIRVType *ResType = nullptr ) const ;
254255
@@ -260,6 +261,14 @@ class SPIRVInstructionSelector : public InstructionSelector {
260261
261262 bool wrapIntoSpecConstantOp (MachineInstr &I,
262263 SmallVector<Register> &CompositeArgs) const ;
264+
265+ Register getUcharPtrTypeReg (MachineInstr &I,
266+ SPIRV::StorageClass::StorageClass SC) const ;
267+ MachineInstrBuilder buildSpecConstantOp (MachineInstr &I, Register Dest,
268+ Register Src, Register DestType,
269+ uint32_t Opcode) const ;
270+ MachineInstrBuilder buildConstGenericPtr (MachineInstr &I, Register SrcPtr,
271+ SPIRVType *SrcPtrTy) const ;
263272};
264273
265274} // end anonymous namespace
@@ -1244,6 +1253,58 @@ static bool isUSMStorageClass(SPIRV::StorageClass::StorageClass SC) {
12441253 }
12451254}
12461255
1256+ // Returns true ResVReg is referred only from global vars and OpName's.
1257+ static bool isASCastInGVar (MachineRegisterInfo *MRI, Register ResVReg) {
1258+ bool IsGRef = false ;
1259+ bool IsAllowedRefs =
1260+ std::all_of (MRI->use_instr_begin (ResVReg), MRI->use_instr_end (),
1261+ [&IsGRef](auto const &It) {
1262+ unsigned Opcode = It.getOpcode ();
1263+ if (Opcode == SPIRV::OpConstantComposite ||
1264+ Opcode == SPIRV::OpVariable ||
1265+ isSpvIntrinsic (It, Intrinsic::spv_init_global))
1266+ return IsGRef = true ;
1267+ return Opcode == SPIRV::OpName;
1268+ });
1269+ return IsAllowedRefs && IsGRef;
1270+ }
1271+
1272+ Register SPIRVInstructionSelector::getUcharPtrTypeReg (
1273+ MachineInstr &I, SPIRV::StorageClass::StorageClass SC) const {
1274+ return GR.getSPIRVTypeID (GR.getOrCreateSPIRVPointerType (
1275+ GR.getOrCreateSPIRVIntegerType (8 , I, TII), I, TII, SC));
1276+ }
1277+
1278+ MachineInstrBuilder
1279+ SPIRVInstructionSelector::buildSpecConstantOp (MachineInstr &I, Register Dest,
1280+ Register Src, Register DestType,
1281+ uint32_t Opcode) const {
1282+ return BuildMI (*I.getParent (), I, I.getDebugLoc (),
1283+ TII.get (SPIRV::OpSpecConstantOp))
1284+ .addDef (Dest)
1285+ .addUse (DestType)
1286+ .addImm (Opcode)
1287+ .addUse (Src);
1288+ }
1289+
1290+ MachineInstrBuilder
1291+ SPIRVInstructionSelector::buildConstGenericPtr (MachineInstr &I, Register SrcPtr,
1292+ SPIRVType *SrcPtrTy) const {
1293+ SPIRVType *GenericPtrTy = GR.getOrCreateSPIRVPointerType (
1294+ GR.getPointeeType (SrcPtrTy), I, TII, SPIRV::StorageClass::Generic);
1295+ Register Tmp = MRI->createVirtualRegister (&SPIRV::pIDRegClass);
1296+ MRI->setType (Tmp, LLT::pointer (storageClassToAddressSpace (
1297+ SPIRV::StorageClass::Generic),
1298+ GR.getPointerSize ()));
1299+ MachineFunction *MF = I.getParent ()->getParent ();
1300+ GR.assignSPIRVTypeToVReg (GenericPtrTy, Tmp, *MF);
1301+ MachineInstrBuilder MIB = buildSpecConstantOp (
1302+ I, Tmp, SrcPtr, GR.getSPIRVTypeID (GenericPtrTy),
1303+ static_cast <uint32_t >(SPIRV::Opcode::PtrCastToGeneric));
1304+ GR.add (MIB.getInstr (), MF, Tmp);
1305+ return MIB;
1306+ }
1307+
12471308// In SPIR-V address space casting can only happen to and from the Generic
12481309// storage class. We can also only cast Workgroup, CrossWorkgroup, or Function
12491310// pointers to and from Generic pointers. As such, we can convert e.g. from
@@ -1252,36 +1313,57 @@ static bool isUSMStorageClass(SPIRV::StorageClass::StorageClass SC) {
12521313bool SPIRVInstructionSelector::selectAddrSpaceCast (Register ResVReg,
12531314 const SPIRVType *ResType,
12541315 MachineInstr &I) const {
1255- // If the AddrSpaceCast user is single and in OpConstantComposite or
1256- // OpVariable, we should select OpSpecConstantOp.
1257- auto UIs = MRI->use_instructions (ResVReg);
1258- if (!UIs.empty () && ++UIs.begin () == UIs.end () &&
1259- (UIs.begin ()->getOpcode () == SPIRV::OpConstantComposite ||
1260- UIs.begin ()->getOpcode () == SPIRV::OpVariable ||
1261- isSpvIntrinsic (*UIs.begin (), Intrinsic::spv_init_global))) {
1262- Register NewReg = I.getOperand (1 ).getReg ();
1263- MachineBasicBlock &BB = *I.getParent ();
1264- SPIRVType *SpvBaseTy = GR.getOrCreateSPIRVIntegerType (8 , I, TII);
1265- ResType = GR.getOrCreateSPIRVPointerType (SpvBaseTy, I, TII,
1266- SPIRV::StorageClass::Generic);
1267- bool Result =
1268- BuildMI (BB, I, I.getDebugLoc (), TII.get (SPIRV::OpSpecConstantOp))
1269- .addDef (ResVReg)
1270- .addUse (GR.getSPIRVTypeID (ResType))
1271- .addImm (static_cast <uint32_t >(SPIRV::Opcode::PtrCastToGeneric))
1272- .addUse (NewReg)
1273- .constrainAllUses (TII, TRI, RBI);
1274- return Result;
1275- }
1316+ MachineBasicBlock &BB = *I.getParent ();
1317+ const DebugLoc &DL = I.getDebugLoc ();
1318+
12761319 Register SrcPtr = I.getOperand (1 ).getReg ();
12771320 SPIRVType *SrcPtrTy = GR.getSPIRVTypeForVReg (SrcPtr);
1278- SPIRV::StorageClass::StorageClass SrcSC = GR.getPointerStorageClass (SrcPtr);
1279- SPIRV::StorageClass::StorageClass DstSC = GR.getPointerStorageClass (ResVReg);
1321+
1322+ // don't generate a cast for a null that may be represented by OpTypeInt
1323+ if (SrcPtrTy->getOpcode () != SPIRV::OpTypePointer ||
1324+ ResType->getOpcode () != SPIRV::OpTypePointer)
1325+ return BuildMI (BB, I, DL, TII.get (TargetOpcode::COPY))
1326+ .addDef (ResVReg)
1327+ .addUse (SrcPtr)
1328+ .constrainAllUses (TII, TRI, RBI);
1329+
1330+ SPIRV::StorageClass::StorageClass SrcSC = GR.getPointerStorageClass (SrcPtrTy);
1331+ SPIRV::StorageClass::StorageClass DstSC = GR.getPointerStorageClass (ResType);
1332+
1333+ if (isASCastInGVar (MRI, ResVReg)) {
1334+ // AddrSpaceCast uses within OpVariable and OpConstantComposite instructions
1335+ // are expressed by OpSpecConstantOp with an Opcode.
1336+ // TODO: maybe insert a check whether the Kernel capability was declared and
1337+ // so PtrCastToGeneric/GenericCastToPtr are available.
1338+ unsigned SpecOpcode =
1339+ DstSC == SPIRV::StorageClass::Generic && isGenericCastablePtr (SrcSC)
1340+ ? static_cast <uint32_t >(SPIRV::Opcode::PtrCastToGeneric)
1341+ : (SrcSC == SPIRV::StorageClass::Generic &&
1342+ isGenericCastablePtr (DstSC)
1343+ ? static_cast <uint32_t >(SPIRV::Opcode::GenericCastToPtr)
1344+ : 0 );
1345+ // TODO: OpConstantComposite expects i8*, so we are forced to forget a
1346+ // correct value of ResType and use general i8* instead. Maybe this should
1347+ // be addressed in the emit-intrinsic step to infer a correct
1348+ // OpConstantComposite type.
1349+ if (SpecOpcode) {
1350+ return buildSpecConstantOp (I, ResVReg, SrcPtr,
1351+ getUcharPtrTypeReg (I, DstSC), SpecOpcode)
1352+ .constrainAllUses (TII, TRI, RBI);
1353+ } else if (isGenericCastablePtr (SrcSC) && isGenericCastablePtr (DstSC)) {
1354+ MachineInstrBuilder MIB = buildConstGenericPtr (I, SrcPtr, SrcPtrTy);
1355+ return MIB.constrainAllUses (TII, TRI, RBI) &&
1356+ buildSpecConstantOp (
1357+ I, ResVReg, MIB->getOperand (0 ).getReg (),
1358+ getUcharPtrTypeReg (I, DstSC),
1359+ static_cast <uint32_t >(SPIRV::Opcode::GenericCastToPtr))
1360+ .constrainAllUses (TII, TRI, RBI);
1361+ }
1362+ }
12801363
12811364 // don't generate a cast between identical storage classes
12821365 if (SrcSC == DstSC)
1283- return BuildMI (*I.getParent (), I, I.getDebugLoc (),
1284- TII.get (TargetOpcode::COPY))
1366+ return BuildMI (BB, I, DL, TII.get (TargetOpcode::COPY))
12851367 .addDef (ResVReg)
12861368 .addUse (SrcPtr)
12871369 .constrainAllUses (TII, TRI, RBI);
@@ -1297,8 +1379,6 @@ bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg,
12971379 Register Tmp = MRI->createVirtualRegister (&SPIRV::iIDRegClass);
12981380 SPIRVType *GenericPtrTy = GR.getOrCreateSPIRVPointerType (
12991381 GR.getPointeeType (SrcPtrTy), I, TII, SPIRV::StorageClass::Generic);
1300- MachineBasicBlock &BB = *I.getParent ();
1301- const DebugLoc &DL = I.getDebugLoc ();
13021382 bool Success = BuildMI (BB, I, DL, TII.get (SPIRV::OpPtrCastToGeneric))
13031383 .addDef (Tmp)
13041384 .addUse (GR.getSPIRVTypeID (GenericPtrTy))
0 commit comments