Skip to content

Commit fa98ab9

Browse files
committed
[SPIRV] Expand RWBuffer load and store from HLSL
The code pattern that clang will generate for HLSL has changed from the original plan. This allows the SPIR-V backend to generate code for the current code generation. It looks for patterns of the form: ``` %1 = @llvm.spv.resource.handlefrombinding %2 = @llvm.spv.resource.getpointer(%1, index) load/store %2 ``` These three llvm-ir instruction are treated as a single unit that will 1. Generate or find the global variable identified by the call to `resource.handlefrombinding`. 2. Generate an OpLoad of the variable to get the handle to the image. 3. Generate an OpImageRead or OpImageWrite using that handle with the given index. This will generate the OpLoad in the same BB as the read/write. Note: Now that `resource.handlefrombinding` is not processed on its own, many existing tests had to be removed. We do not have intrinsics that are able to use handles to sampled images, input attachments, etc., so we cannot generate the load of the handle. These tests are removed for now, and will be added when those resource types are fully implemented.
1 parent 7c84a9a commit fa98ab9

23 files changed

+241
-580
lines changed

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,14 @@ bool expectIgnoredInIRTranslation(const Instruction *I) {
264264
const auto *II = dyn_cast<IntrinsicInst>(I);
265265
if (!II)
266266
return false;
267-
return II->getIntrinsicID() == Intrinsic::invariant_start;
267+
switch (II->getIntrinsicID()) {
268+
case Intrinsic::invariant_start:
269+
case Intrinsic::spv_resource_handlefrombinding:
270+
case Intrinsic::spv_resource_getpointer:
271+
return true;
272+
default:
273+
return false;
274+
}
268275
}
269276

270277
bool allowEmitFakeUse(const Value *Arg) {
@@ -725,6 +732,14 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
725732
if (Ty)
726733
break;
727734
}
735+
} else if (auto *II = dyn_cast<IntrinsicInst>(I)) {
736+
if (II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
737+
auto *ImageType = cast<TargetExtType>(II->getOperand(0)->getType());
738+
assert(ImageType->getTargetExtName() == "spirv.Image");
739+
Ty = ImageType->getTypeParameter(0);
740+
// TODO: Need to look at the use to see if it needs to be a vector of the
741+
// type.
742+
}
728743
} else if (auto *CI = dyn_cast<CallInst>(I)) {
729744
static StringMap<unsigned> ResTypeByArg = {
730745
{"to_global", 0},

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,9 +1114,12 @@ SPIRVGlobalRegistry::getSPIRVTypeForVReg(Register VReg,
11141114
return nullptr;
11151115
}
11161116

1117-
SPIRVType *SPIRVGlobalRegistry::getResultType(Register VReg) {
1118-
MachineInstr *Instr = getVRegDef(CurMF->getRegInfo(), VReg);
1119-
return getSPIRVTypeForVReg(Instr->getOperand(1).getReg());
1117+
SPIRVType *SPIRVGlobalRegistry::getResultType(Register VReg,
1118+
MachineFunction *MF) {
1119+
if (!MF)
1120+
MF = CurMF;
1121+
MachineInstr *Instr = getVRegDef(MF->getRegInfo(), VReg);
1122+
return getSPIRVTypeForVReg(Instr->getOperand(1).getReg(), MF);
11201123
}
11211124

11221125
SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ class SPIRVGlobalRegistry {
377377
const MachineFunction *MF = nullptr) const;
378378

379379
// Return the result type of the instruction defining the register.
380-
SPIRVType *getResultType(Register VReg);
380+
SPIRVType *getResultType(Register VReg, MachineFunction *MF = nullptr);
381381

382382
// Whether the given VReg has a SPIR-V type mapped to it yet.
383383
bool hasSPIRVTypeForVReg(Register VReg) const {

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
276276

277277
bool selectReadImageIntrinsic(Register &ResVReg, const SPIRVType *ResType,
278278
MachineInstr &I) const;
279-
280279
bool selectImageWriteIntrinsic(MachineInstr &I) const;
280+
bool selectResourceGetPointer(Register &ResVReg, const SPIRVType *ResType,
281+
MachineInstr &I) const;
281282

282283
// Utilities
283284
std::pair<Register, bool>
@@ -307,10 +308,15 @@ class SPIRVInstructionSelector : public InstructionSelector {
307308
SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const;
308309
bool extractSubvector(Register &ResVReg, const SPIRVType *ResType,
309310
Register &ReadReg, MachineInstr &InsertionPoint) const;
311+
bool generateImageRead(Register &ResVReg, const SPIRVType *ResType,
312+
Register ImageReg, Register IdxReg, DebugLoc Loc,
313+
MachineInstr &Pos) const;
310314
bool BuildCOPY(Register DestReg, Register SrcReg, MachineInstr &I) const;
311315
bool loadVec3BuiltinInputID(SPIRV::BuiltIn::BuiltIn BuiltInValue,
312316
Register ResVReg, const SPIRVType *ResType,
313317
MachineInstr &I) const;
318+
bool loadHandleBeforePosition(Register &HandleReg, const SPIRVType *ResType,
319+
GIntrinsic &HandleDef, MachineInstr &Pos) const;
314320
};
315321

316322
} // end anonymous namespace
@@ -1018,6 +1024,25 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
10181024
MachineInstr &I) const {
10191025
unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0;
10201026
Register Ptr = I.getOperand(1 + OpOffset).getReg();
1027+
1028+
auto *PtrDef = getVRegDef(*MRI, Ptr);
1029+
auto *IntPtrDef = dyn_cast<GIntrinsic>(PtrDef);
1030+
if (IntPtrDef &&
1031+
IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
1032+
Register ImageReg = IntPtrDef->getOperand(2).getReg();
1033+
Register NewImageReg =
1034+
MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
1035+
auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
1036+
if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
1037+
*ImageDef, I)) {
1038+
return false;
1039+
}
1040+
1041+
Register IdxReg = IntPtrDef->getOperand(3).getReg();
1042+
return generateImageRead(ResVReg, ResType, NewImageReg, IdxReg,
1043+
I.getDebugLoc(), I);
1044+
}
1045+
10211046
auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
10221047
.addDef(ResVReg)
10231048
.addUse(GR.getSPIRVTypeID(ResType))
@@ -1037,6 +1062,29 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
10371062
unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0;
10381063
Register StoreVal = I.getOperand(0 + OpOffset).getReg();
10391064
Register Ptr = I.getOperand(1 + OpOffset).getReg();
1065+
1066+
auto *PtrDef = getVRegDef(*MRI, Ptr);
1067+
auto *IntPtrDef = dyn_cast<GIntrinsic>(PtrDef);
1068+
if (IntPtrDef &&
1069+
IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
1070+
Register ImageReg = IntPtrDef->getOperand(2).getReg();
1071+
Register NewImageReg =
1072+
MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
1073+
auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
1074+
if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
1075+
*ImageDef, I)) {
1076+
return false;
1077+
}
1078+
1079+
Register IdxReg = IntPtrDef->getOperand(3).getReg();
1080+
return BuildMI(*I.getParent(), I, I.getDebugLoc(),
1081+
TII.get(SPIRV::OpImageWrite))
1082+
.addUse(NewImageReg)
1083+
.addUse(IdxReg)
1084+
.addUse(StoreVal)
1085+
.constrainAllUses(TII, TRI, RBI);
1086+
}
1087+
10401088
MachineBasicBlock &BB = *I.getParent();
10411089
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpStore))
10421090
.addUse(Ptr)
@@ -3007,6 +3055,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
30073055
case Intrinsic::spv_resource_load_typedbuffer: {
30083056
return selectReadImageIntrinsic(ResVReg, ResType, I);
30093057
}
3058+
case Intrinsic::spv_resource_getpointer: {
3059+
return selectResourceGetPointer(ResVReg, ResType, I);
3060+
}
30103061
case Intrinsic::spv_discard: {
30113062
return selectDiscard(ResVReg, ResType, I);
30123063
}
@@ -3024,27 +3075,7 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
30243075
bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
30253076
const SPIRVType *ResType,
30263077
MachineInstr &I) const {
3027-
3028-
uint32_t Set = foldImm(I.getOperand(2), MRI);
3029-
uint32_t Binding = foldImm(I.getOperand(3), MRI);
3030-
uint32_t ArraySize = foldImm(I.getOperand(4), MRI);
3031-
Register IndexReg = I.getOperand(5).getReg();
3032-
bool IsNonUniform = ArraySize > 1 && foldImm(I.getOperand(6), MRI);
3033-
3034-
MachineIRBuilder MIRBuilder(I);
3035-
Register VarReg = buildPointerToResource(ResType, Set, Binding, ArraySize,
3036-
IndexReg, IsNonUniform, MIRBuilder);
3037-
3038-
if (IsNonUniform)
3039-
buildOpDecorate(ResVReg, I, TII, SPIRV::Decoration::NonUniformEXT, {});
3040-
3041-
// TODO: For now we assume the resource is an image, which needs to be
3042-
// loaded to get the handle. That will not be true for storage buffers.
3043-
return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
3044-
.addDef(ResVReg)
3045-
.addUse(GR.getSPIRVTypeID(ResType))
3046-
.addUse(VarReg)
3047-
.constrainAllUses(TII, TRI, RBI);
3078+
return true;
30483079
}
30493080

30503081
bool SPIRVInstructionSelector::selectReadImageIntrinsic(
@@ -3057,42 +3088,75 @@ bool SPIRVInstructionSelector::selectReadImageIntrinsic(
30573088
// We will do that when we can, but for now trying to move forward with other
30583089
// issues.
30593090
Register ImageReg = I.getOperand(2).getReg();
3060-
assert(MRI->getVRegDef(ImageReg)->getParent() == I.getParent() &&
3061-
"The image must be loaded in the same basic block as its use.");
3091+
auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
3092+
Register NewImageReg = MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
3093+
if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
3094+
*ImageDef, I)) {
3095+
return false;
3096+
}
3097+
3098+
Register IdxReg = I.getOperand(3).getReg();
3099+
DebugLoc Loc = I.getDebugLoc();
3100+
MachineInstr &Pos = I;
30623101

3102+
return generateImageRead(ResVReg, ResType, NewImageReg, IdxReg, Loc, Pos);
3103+
}
3104+
3105+
bool SPIRVInstructionSelector::generateImageRead(Register &ResVReg,
3106+
const SPIRVType *ResType,
3107+
Register ImageReg,
3108+
Register IdxReg, DebugLoc Loc,
3109+
MachineInstr &Pos) const {
30633110
uint64_t ResultSize = GR.getScalarOrVectorComponentCount(ResType);
30643111
if (ResultSize == 4) {
3065-
return BuildMI(*I.getParent(), I, I.getDebugLoc(),
3066-
TII.get(SPIRV::OpImageRead))
3112+
return BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpImageRead))
30673113
.addDef(ResVReg)
30683114
.addUse(GR.getSPIRVTypeID(ResType))
30693115
.addUse(ImageReg)
3070-
.addUse(I.getOperand(3).getReg())
3116+
.addUse(IdxReg)
30713117
.constrainAllUses(TII, TRI, RBI);
30723118
}
30733119

3074-
SPIRVType *ReadType = widenTypeToVec4(ResType, I);
3120+
SPIRVType *ReadType = widenTypeToVec4(ResType, Pos);
30753121
Register ReadReg = MRI->createVirtualRegister(GR.getRegClass(ReadType));
30763122
bool Succeed =
3077-
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpImageRead))
3123+
BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpImageRead))
30783124
.addDef(ReadReg)
30793125
.addUse(GR.getSPIRVTypeID(ReadType))
30803126
.addUse(ImageReg)
3081-
.addUse(I.getOperand(3).getReg())
3127+
.addUse(IdxReg)
30823128
.constrainAllUses(TII, TRI, RBI);
30833129
if (!Succeed)
30843130
return false;
30853131

30863132
if (ResultSize == 1) {
3087-
return BuildMI(*I.getParent(), I, I.getDebugLoc(),
3133+
return BuildMI(*Pos.getParent(), Pos, Loc,
30883134
TII.get(SPIRV::OpCompositeExtract))
30893135
.addDef(ResVReg)
30903136
.addUse(GR.getSPIRVTypeID(ResType))
30913137
.addUse(ReadReg)
30923138
.addImm(0)
30933139
.constrainAllUses(TII, TRI, RBI);
30943140
}
3095-
return extractSubvector(ResVReg, ResType, ReadReg, I);
3141+
return extractSubvector(ResVReg, ResType, ReadReg, Pos);
3142+
}
3143+
3144+
bool SPIRVInstructionSelector::selectResourceGetPointer(
3145+
Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
3146+
#ifdef ASSERT
3147+
// For now, the operand is an image. This will change once we start handling
3148+
// more resource types.
3149+
Register ResourcePtr = I.getOperand(2).getReg();
3150+
SPIRVType *RegType = GR.getResultType(ResourcePtr);
3151+
assert(RegType->getOpcode() == SPIRV::OpTypeImage &&
3152+
"Can only handle texel buffers for now.");
3153+
#endif
3154+
3155+
// For texel buffers, the index into the image is part of the OpImageRead or
3156+
// OpImageWrite instructions. So we will do nothing in this case. This
3157+
// intrinsic will be combined with the load or store when selecting the load
3158+
// or store.
3159+
return true;
30963160
}
30973161

30983162
bool SPIRVInstructionSelector::extractSubvector(
@@ -3144,15 +3208,20 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
31443208
// We will do that when we can, but for now trying to move forward with other
31453209
// issues.
31463210
Register ImageReg = I.getOperand(1).getReg();
3147-
assert(MRI->getVRegDef(ImageReg)->getParent() == I.getParent() &&
3148-
"The image must be loaded in the same basic block as its use.");
3211+
auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
3212+
Register NewImageReg = MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
3213+
if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
3214+
*ImageDef, I)) {
3215+
return false;
3216+
}
3217+
31493218
Register CoordinateReg = I.getOperand(2).getReg();
31503219
Register DataReg = I.getOperand(3).getReg();
31513220
assert(GR.getResultType(DataReg)->getOpcode() == SPIRV::OpTypeVector);
31523221
assert(GR.getScalarOrVectorComponentCount(GR.getResultType(DataReg)) == 4);
31533222
return BuildMI(*I.getParent(), I, I.getDebugLoc(),
31543223
TII.get(SPIRV::OpImageWrite))
3155-
.addUse(ImageReg)
3224+
.addUse(NewImageReg)
31563225
.addUse(CoordinateReg)
31573226
.addUse(DataReg)
31583227
.constrainAllUses(TII, TRI, RBI);
@@ -3677,6 +3746,36 @@ SPIRVType *SPIRVInstructionSelector::widenTypeToVec4(const SPIRVType *Type,
36773746
return GR.getOrCreateSPIRVVectorType(ScalarType, 4, MIRBuilder);
36783747
}
36793748

3749+
bool SPIRVInstructionSelector::loadHandleBeforePosition(
3750+
Register &HandleReg, const SPIRVType *ResType, GIntrinsic &HandleDef,
3751+
MachineInstr &Pos) const {
3752+
3753+
assert(HandleDef.getIntrinsicID() ==
3754+
Intrinsic::spv_resource_handlefrombinding);
3755+
uint32_t Set = foldImm(HandleDef.getOperand(2), MRI);
3756+
uint32_t Binding = foldImm(HandleDef.getOperand(3), MRI);
3757+
uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI);
3758+
Register IndexReg = HandleDef.getOperand(5).getReg();
3759+
bool IsNonUniform = ArraySize > 1 && foldImm(HandleDef.getOperand(6), MRI);
3760+
3761+
MachineIRBuilder MIRBuilder(HandleDef);
3762+
Register VarReg = buildPointerToResource(ResType, Set, Binding, ArraySize,
3763+
IndexReg, IsNonUniform, MIRBuilder);
3764+
3765+
if (IsNonUniform)
3766+
buildOpDecorate(HandleReg, HandleDef, TII, SPIRV::Decoration::NonUniformEXT,
3767+
{});
3768+
3769+
// TODO: For now we assume the resource is an image, which needs to be
3770+
// loaded to get the handle. That will not be true for storage buffers.
3771+
return BuildMI(*Pos.getParent(), Pos, HandleDef.getDebugLoc(),
3772+
TII.get(SPIRV::OpLoad))
3773+
.addDef(HandleReg)
3774+
.addUse(GR.getSPIRVTypeID(ResType))
3775+
.addUse(VarReg)
3776+
.constrainAllUses(TII, TRI, RBI);
3777+
}
3778+
36803779
namespace llvm {
36813780
InstructionSelector *
36823781
createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,14 +1696,16 @@ void addInstrRequirements(const MachineInstr &MI,
16961696
break;
16971697
case SPIRV::OpImageRead: {
16981698
Register ImageReg = MI.getOperand(2).getReg();
1699-
SPIRVType *TypeDef = ST.getSPIRVGlobalRegistry()->getResultType(ImageReg);
1699+
SPIRVType *TypeDef = ST.getSPIRVGlobalRegistry()->getResultType(
1700+
ImageReg, const_cast<MachineFunction *>(MI.getMF()));
17001701
if (isImageTypeWithUnknownFormat(TypeDef))
17011702
Reqs.addCapability(SPIRV::Capability::StorageImageReadWithoutFormat);
17021703
break;
17031704
}
17041705
case SPIRV::OpImageWrite: {
17051706
Register ImageReg = MI.getOperand(0).getReg();
1706-
SPIRVType *TypeDef = ST.getSPIRVGlobalRegistry()->getResultType(ImageReg);
1707+
SPIRVType *TypeDef = ST.getSPIRVGlobalRegistry()->getResultType(
1708+
ImageReg, const_cast<MachineFunction *>(MI.getMF()));
17071709
if (isImageTypeWithUnknownFormat(TypeDef))
17081710
Reqs.addCapability(SPIRV::Capability::StorageImageWriteWithoutFormat);
17091711
break;

0 commit comments

Comments
 (0)