1010#include " DirectX.h"
1111#include " llvm/ADT/SetVector.h"
1212#include " llvm/Analysis/DXILResource.h"
13+ #include " llvm/Frontend/HLSL/HLSLResource.h"
1314#include " llvm/IR/BasicBlock.h"
1415#include " llvm/IR/Dominators.h"
1516#include " llvm/IR/IRBuilder.h"
2021#include " llvm/IR/IntrinsicsDirectX.h"
2122#include " llvm/IR/User.h"
2223#include " llvm/InitializePasses.h"
24+ #include " llvm/Support/FormatVariadic.h"
2325#include " llvm/Transforms/Utils/ValueMapper.h"
2426
2527#define DEBUG_TYPE " dxil-resource-access"
@@ -44,16 +46,28 @@ static Value *calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset,
4446 APInt ConstantOffset (DL.getIndexTypeSizeInBits (GEP->getType ()), 0 );
4547 if (GEP->accumulateConstantOffset (DL, ConstantOffset)) {
4648 APInt Scaled = ConstantOffset.udiv (ScalarSize);
47- return ConstantInt::get (Type::getInt32Ty (GEP->getContext ()), Scaled);
49+ return ConstantInt::get (DL. getIndexType (GEP->getType ()), Scaled);
4850 }
4951
50- auto IndexIt = GEP->idx_begin ();
51- assert (cast<ConstantInt>(IndexIt)->getZExtValue () == 0 &&
52- " GEP is not indexing through pointer" );
53- ++IndexIt;
54- Value *Offset = *IndexIt;
55- assert (++IndexIt == GEP->idx_end () && " Too many indices in GEP" );
56- return Offset;
52+ unsigned NumIndices = GEP->getNumIndices ();
53+
54+ // If we have a single index we're indexing into a top level array. This
55+ // generally only happens with cbuffers.
56+ if (NumIndices == 1 )
57+ return *GEP->idx_begin ();
58+
59+ // If we have two indices, this should be a simple access through a pointer.
60+ if (NumIndices == 2 ) {
61+ auto IndexIt = GEP->idx_begin ();
62+ assert (cast<ConstantInt>(IndexIt)->getZExtValue () == 0 &&
63+ " GEP is not indexing through pointer" );
64+ ++IndexIt;
65+ Value *Offset = *IndexIt;
66+ assert (++IndexIt == GEP->idx_end () && " Too many indices in GEP" );
67+ return Offset;
68+ }
69+
70+ llvm_unreachable (" Unhandled GEP structure for resource access" );
5771}
5872
5973static void createTypedBufferStore (IntrinsicInst *II, StoreInst *SI,
@@ -171,6 +185,127 @@ static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset) {
171185 LI->replaceAllUsesWith (V);
172186}
173187
188+ namespace {
189+ // / Helper for building a `load.cbufferrow` intrinsic given a simple type.
190+ struct CBufferRowIntrin {
191+ Intrinsic::ID IID;
192+ Type *RetTy;
193+ unsigned int EltSize;
194+ unsigned int NumElts;
195+
196+ CBufferRowIntrin (const DataLayout &DL, Type *Ty) {
197+ assert (Ty == Ty->getScalarType () && " Expected scalar type" );
198+
199+ switch (DL.getTypeSizeInBits (Ty)) {
200+ case 16 :
201+ IID = Intrinsic::dx_resource_load_cbufferrow_8;
202+ RetTy = StructType::get (Ty, Ty, Ty, Ty, Ty, Ty, Ty, Ty);
203+ EltSize = 2 ;
204+ NumElts = 8 ;
205+ break ;
206+ case 32 :
207+ IID = Intrinsic::dx_resource_load_cbufferrow_4;
208+ RetTy = StructType::get (Ty, Ty, Ty, Ty);
209+ EltSize = 4 ;
210+ NumElts = 4 ;
211+ break ;
212+ case 64 :
213+ IID = Intrinsic::dx_resource_load_cbufferrow_2;
214+ RetTy = StructType::get (Ty, Ty);
215+ EltSize = 8 ;
216+ NumElts = 2 ;
217+ break ;
218+ default :
219+ llvm_unreachable (" Only 16, 32, and 64 bit types supported" );
220+ }
221+ }
222+ };
223+ } // namespace
224+
225+ static void createCBufferLoad (IntrinsicInst *II, LoadInst *LI, Value *Offset,
226+ dxil::ResourceTypeInfo &RTI) {
227+ const DataLayout &DL = LI->getDataLayout ();
228+
229+ Type *Ty = LI->getType ();
230+ assert (!isa<StructType>(Ty) && " Structs not handled yet" );
231+ CBufferRowIntrin Intrin (DL, Ty->getScalarType ());
232+
233+ StringRef Name = LI->getName ();
234+ Value *Handle = II->getOperand (0 );
235+
236+ IRBuilder<> Builder (LI);
237+
238+ ConstantInt *GlobalOffset = dyn_cast<ConstantInt>(II->getOperand (1 ));
239+ assert (GlobalOffset && " CBuffer getpointer index must be constant" );
240+
241+ unsigned int FixedOffset = GlobalOffset->getZExtValue ();
242+ // If we have a further constant offset we can just fold it in to the fixed
243+ // offset.
244+ if (auto *ConstOffset = dyn_cast_if_present<ConstantInt>(Offset)) {
245+ FixedOffset += ConstOffset->getZExtValue ();
246+ Offset = nullptr ;
247+ }
248+
249+ Value *CurrentRow = ConstantInt::get (
250+ Builder.getInt32Ty (), FixedOffset / hlsl::CBufferRowSizeInBytes);
251+ unsigned int CurrentIndex =
252+ (FixedOffset % hlsl::CBufferRowSizeInBytes) / Intrin.EltSize ;
253+
254+ assert (!(CurrentIndex && Offset) &&
255+ " Dynamic indexing into elements of cbuffer rows is not supported" );
256+ // At this point if we have a non-constant offset it has to be an array
257+ // offset, so we can assume that it's a multiple of the row size.
258+ if (Offset)
259+ CurrentRow = FixedOffset ? Builder.CreateAdd (CurrentRow, Offset) : Offset;
260+
261+ auto *CBufLoad = Builder.CreateIntrinsic (
262+ Intrin.RetTy , Intrin.IID , {Handle, CurrentRow}, nullptr , Name + " .load" );
263+ auto *Elt =
264+ Builder.CreateExtractValue (CBufLoad, {CurrentIndex++}, Name + " .extract" );
265+
266+ // At this point we've loaded the first scalar of our result, but our original
267+ // type may have been a vector.
268+ unsigned int Remaining =
269+ ((DL.getTypeSizeInBits (Ty) / 8 ) / Intrin.EltSize ) - 1 ;
270+ if (Remaining == 0 ) {
271+ // We only have a single element, so we're done.
272+ Value *Result = Elt;
273+
274+ // However, if we loaded a <1 x T>, then we need to adjust the type.
275+ if (auto *VT = dyn_cast<FixedVectorType>(Ty)) {
276+ assert (VT->getNumElements () == 1 && " Can't have multiple elements here" );
277+ Result = Builder.CreateInsertElement (PoisonValue::get (VT), Result,
278+ Builder.getInt32 (0 ), Name);
279+ }
280+ LI->replaceAllUsesWith (Result);
281+ return ;
282+ }
283+
284+ // Walk each element and extract it, wrapping to new rows as needed.
285+ SmallVector<Value *> Extracts{Elt};
286+ while (Remaining--) {
287+ CurrentIndex %= Intrin.NumElts ;
288+
289+ if (CurrentIndex == 0 ) {
290+ CurrentRow = Builder.CreateAdd (CurrentRow,
291+ ConstantInt::get (Builder.getInt32Ty (), 1 ));
292+ CBufLoad = Builder.CreateIntrinsic (Intrin.RetTy , Intrin.IID ,
293+ {Handle, CurrentRow}, nullptr ,
294+ Name + " .load" );
295+ }
296+
297+ Extracts.push_back (Builder.CreateExtractValue (CBufLoad, {CurrentIndex++},
298+ Name + " .extract" ));
299+ }
300+
301+ // Finally, we build up the original loaded value.
302+ Value *Result = PoisonValue::get (Ty);
303+ for (int I = 0 , E = Extracts.size (); I < E; ++I)
304+ Result = Builder.CreateInsertElement (
305+ Result, Extracts[I], Builder.getInt32 (I), Name + formatv (" .upto{}" , I));
306+ LI->replaceAllUsesWith (Result);
307+ }
308+
174309static void createLoadIntrinsic (IntrinsicInst *II, LoadInst *LI, Value *Offset,
175310 dxil::ResourceTypeInfo &RTI) {
176311 switch (RTI.getResourceKind ()) {
@@ -179,6 +314,8 @@ static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
179314 case dxil::ResourceKind::RawBuffer:
180315 case dxil::ResourceKind::StructuredBuffer:
181316 return createRawLoad (II, LI, Offset);
317+ case dxil::ResourceKind::CBuffer:
318+ return createCBufferLoad (II, LI, Offset, RTI);
182319 case dxil::ResourceKind::Texture1D:
183320 case dxil::ResourceKind::Texture2D:
184321 case dxil::ResourceKind::Texture2DMS:
@@ -190,9 +327,8 @@ static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
190327 case dxil::ResourceKind::TextureCubeArray:
191328 case dxil::ResourceKind::FeedbackTexture2D:
192329 case dxil::ResourceKind::FeedbackTexture2DArray:
193- case dxil::ResourceKind::CBuffer:
194330 case dxil::ResourceKind::TBuffer:
195- // TODO: handle these
331+ reportFatalUsageError ( " Load not yet implemented for resource type " );
196332 return ;
197333 case dxil::ResourceKind::Sampler:
198334 case dxil::ResourceKind::RTAccelerationStructure:
0 commit comments