[llvm] [DirectX] Add support for typedBufferLoad and Store for RWBuffer<double2> and RWBuffer<double> (PR #139996)
Justin Bogner via llvm-commits
llvm-commits at lists.llvm.org
Tue May 27 13:53:35 PDT 2025
================
@@ -532,6 +541,89 @@ static Value *expandRadiansIntrinsic(CallInst *Orig) {
return Builder.CreateFMul(X, PiOver180);
}
+static Value *expandTypedBufferLoadIntrinsic(CallInst *Orig) {
+ IRBuilder<> Builder(Orig);
+
+ Type *BufferTy = Orig->getType()->getStructElementType(0);
+
+ unsigned ExtractNum = 2;
+ if (auto *VT = dyn_cast<FixedVectorType>(BufferTy)) {
+ assert(VT->getNumElements() == 2 &&
+ "TypedBufferLoad double vector has wrong size");
+ ExtractNum = 4;
+ }
+
+ Type *Ty = VectorType::get(Builder.getInt32Ty(), ExtractNum, false);
+
+ Type *LoadType = StructType::get(Ty, Builder.getInt1Ty());
+ CallInst *Load =
+ Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
+ {Orig->getOperand(0), Orig->getOperand(1)});
+
+ // extract the buffer load's result
+ Value *Extract = Builder.CreateExtractValue(Load, {0});
+
+ SmallVector<Value *> ExtractElements;
+ for (unsigned I = 0; I < ExtractNum; ++I)
+ ExtractElements.push_back(
+ Builder.CreateExtractElement(Extract, (uint64_t)I));
+
+ // combine into double(s)
+ Value *Result = PoisonValue::get(BufferTy);
+ for (unsigned I = 0; I < ExtractNum; I += 2) {
+ Value *Dbl =
+ Builder.CreateIntrinsic(Builder.getDoubleTy(), Intrinsic::dx_asdouble,
+ {ExtractElements[I], ExtractElements[I + 1]});
+ if (ExtractNum == 4)
+ Result = Builder.CreateInsertElement(Result, Dbl, (uint64_t)I / 2);
+ else
+ Result = Dbl;
+ }
+
+ Value *CheckBit = Builder.CreateExtractValue(Load, {1});
+
+ Value *Struct = PoisonValue::get(Orig->getType());
+ Struct = Builder.CreateInsertValue(Struct, Result, {0});
+ Struct = Builder.CreateInsertValue(Struct, CheckBit, {1});
----------------
bogner wrote:
Creating a `{double, i1}` or `{<2 x double>, i1}` temporary like this causes some issues. Notably, the validator gets upset about the anonymous struct type:
```
Function: main: error: Instructions must be of an allowed type.
note: at '%18 = insertvalue { double, i1 } %17, i1 %13, 1' in block 'entry' of function 'main'.
Function: main: error: ExtractValue should only be used on dxil struct types and cmpxchg.
note: at '%19 = extractvalue { double, i1 } %18, 0' in block 'entry' of function 'main'.
```
We also run into some trouble with unconditionally using the check bit:
```
error: Flags must match usage.
note: Flags declared=4, actual=4100
```
(Where the flag 0x1000 is "TiledResources", since uses of that bit imply we're using [CheckAccessFullyMapped](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/checkaccessfullymapped))
I think we'll need to actually walk the uses of the original call to typedBufferLoad and replace them individually, taking care to lazily create the use of the check bit. Something like this:
```c++
Value *CheckBit = nullptr;
for (User *U : make_early_inc_range(Orig->users())) {
auto *EVI = cast<ExtractValueInst>(U);
ArrayRef<unsigned> Indices = EVI->getIndices();
assert(Indices.size() == 1);
if (Indices[0] == 0) {
// Use of the value(s)
EVI->replaceAllUsesWith(Result);
} else {
// Use of the check bit
assert(Indices[0] == 1 && "Unexpected type for typedbufferload");
if (!CheckBit)
CheckBit = Builder.CreateExtractValue(Load, {1});
EVI->replaceAllUsesWith(CheckBit);
}
EVI->eraseFromParent();
}
```
This of course does make the `Value *` return of this function kind of awkward. It's probably okay to make this return a bool and do something like this in the switch in expandIntrinsic:
```c++
case Intrinsic::dx_resource_load_typedbuffer:
if (expandTypedBufferLoadIntrinsic(Orig))
return true;
break;
```
We may want to follow up with an NFC change that changes the interface of all of these `expandX` functions to just return a bool and do the replacement internally.
https://github.com/llvm/llvm-project/pull/139996
More information about the llvm-commits
mailing list