Skip to content

Commit 9cd3c0b

Browse files
authored
[HLSL] Codegen for handling global resource array initialization (llvm#198891)
When a global resource array is accessed - whether it is declared at a global scope or as part of a global struct instance - all of its resource elements should be initialized from binding into a temporary local resource array. This change intercepts the Clang codegen at the relevant places to allow `CGHLSLRuntime` handle this special global resource array initialization. Fixes llvm#187087 Fixes llvm#198888
1 parent 48a1ee7 commit 9cd3c0b

8 files changed

Lines changed: 445 additions & 56 deletions

File tree

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3460,6 +3460,16 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
34603460
return CGF.MakeAddrLValue(Addr, T, AlignmentSource::Decl);
34613461
}
34623462

3463+
// Global HLSL resource arrays initialized on access; create a temporary with
3464+
// the initialized global resource array.
3465+
if (CGF.getLangOpts().HLSL && VD->getType()->isHLSLResourceRecordArray()) {
3466+
std::optional<LValue> LV =
3467+
CGF.CGM.getHLSLRuntime().emitGlobalResourceArrayAsLValue(
3468+
CGF, VD, E->getExprLoc());
3469+
if (LV.has_value())
3470+
return LV.value();
3471+
}
3472+
34633473
llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
34643474

34653475
if (VD->getTLSKind() != VarDecl::TLS_None)
@@ -6767,9 +6777,14 @@ LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
67676777

67686778
// If the RHS is a global resource array, copy all individual resources
67696779
// into LHS.
6770-
if (E->getRHS()->getType()->isHLSLResourceRecordArray())
6771-
if (CGM.getHLSLRuntime().emitResourceArrayCopy(LHS, E->getRHS(), *this))
6780+
if (E->getRHS()->getType()->isHLSLResourceRecordArray()) {
6781+
AggValueSlot Slot = AggValueSlot::forAddr(
6782+
LHS.getAddress(), Qualifiers(), AggValueSlot::IsDestructed_t(true),
6783+
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
6784+
AggValueSlot::DoesNotOverlap);
6785+
if (CGM.getHLSLRuntime().emitGlobalResourceArray(*this, E->getRHS(), Slot))
67726786
return LHS;
6787+
}
67736788

67746789
// In C the RHS of an assignment operator is an RValue.
67756790
// EmitAggregateAssign takes an LValue for the RHS. Instead we can call

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,10 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
972972
[[fallthrough]];
973973

974974
case CK_HLSLArrayRValue:
975+
if (CGF.getLangOpts().HLSL &&
976+
E->getSubExpr()->getType()->isHLSLResourceRecordArray())
977+
if (CGF.CGM.getHLSLRuntime().emitGlobalResourceArray(CGF, E, Dest))
978+
break;
975979
Visit(E->getSubExpr());
976980
break;
977981
case CK_HLSLAggregateSplatCast: {

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ static const ValueDecl *getArrayDecl(ASTContext &AST, const Expr *E) {
194194
E = E->IgnoreImpCasts();
195195
if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(E))
196196
return DRE->getDecl();
197+
if (auto *OVE = dyn_cast<OpaqueValueExpr>(E))
198+
E = OVE->getSourceExpr()->IgnoreImpCasts();
197199
if (isa<MemberExpr>(E))
198200
return findAssociatedResourceDeclForStruct(AST, cast<MemberExpr>(E));
199201
return nullptr;
@@ -296,12 +298,13 @@ static void callResourceInitMethod(CodeGenFunction &CGF,
296298
CGF.EmitCall(FnInfo, Callee, ReturnValue, Args, nullptr);
297299
}
298300

299-
// Initializes local resource array variable. For multi-dimensional arrays it
300-
// calls itself recursively to initialize its sub-arrays. The Index used in the
301-
// resource constructor calls will begin at StartIndex and will be incremented
302-
// for each array element. The last used resource Index is returned to the
303-
// caller. If the function returns std::nullopt, it indicates an error.
304-
static std::optional<llvm::Value *> initializeLocalResourceArray(
301+
// Initializes local resource array variable with global resource array
302+
// elements. For multi-dimensional arrays it calls itself recursively to
303+
// initialize its sub-arrays. The Index used in the resource constructor calls
304+
// will begin at StartIndex and will be incremented for each array element. The
305+
// last used resource Index is returned to the caller. If the function returns
306+
// std::nullopt, it indicates an error.
307+
static std::optional<llvm::Value *> initializeResourceArrayFromGlobal(
305308
CodeGenFunction &CGF, CXXRecordDecl *ResourceDecl,
306309
const ConstantArrayType *ArrayTy, AggValueSlot &ValueSlot,
307310
llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName,
@@ -329,9 +332,10 @@ static std::optional<llvm::Value *> initializeLocalResourceArray(
329332
Index = CGF.Builder.CreateAdd(Index, One);
330333
GEPIndices.back() = llvm::ConstantInt::get(IntTy, I);
331334
}
332-
std::optional<llvm::Value *> MaybeIndex = initializeLocalResourceArray(
333-
CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index, ResourceName,
334-
Binding, GEPIndices, ArraySubsExprLoc);
335+
std::optional<llvm::Value *> MaybeIndex =
336+
initializeResourceArrayFromGlobal(
337+
CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index,
338+
ResourceName, Binding, GEPIndices, ArraySubsExprLoc);
335339
if (!MaybeIndex)
336340
return std::nullopt;
337341
Index = *MaybeIndex;
@@ -1443,7 +1447,7 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
14431447
// needs to be initialized.
14441448
const ConstantArrayType *ArrayTy =
14451449
cast<ConstantArrayType>(ResultTy.getTypePtr());
1446-
std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray(
1450+
std::optional<llvm::Value *> EndIndex = initializeResourceArrayFromGlobal(
14471451
CGF, ResourceTy->getAsCXXRecordDecl(), ArrayTy, ValueSlot, Range, Index,
14481452
ArrayDecl->getName(), Binding, {llvm::ConstantInt::get(CGM.IntTy, 0)},
14491453
ArraySubsExpr->getExprLoc());
@@ -1453,20 +1457,15 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
14531457
return CGF.MakeAddrLValue(TmpVar, ResultTy, AlignmentSource::Decl);
14541458
}
14551459

1456-
// If RHSExpr is a global resource array, initialize all of its resources and
1457-
// set them into LHS. Returns false if no copy has been performed and the
1458-
// array copy should be handled by Clang codegen.
1459-
bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
1460-
CodeGenFunction &CGF) {
1461-
QualType ResultTy = RHSExpr->getType();
1462-
assert(ResultTy->isHLSLResourceRecordArray() && "expected resource array");
1463-
1464-
// Let Clang codegen handle local and static resource array copies.
1465-
const VarDecl *ArrayDecl =
1466-
dyn_cast_or_null<VarDecl>(getArrayDecl(CGF.CGM.getContext(), RHSExpr));
1467-
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
1468-
ArrayDecl->getStorageClass() == SC_Static)
1469-
return false;
1460+
// Initialize all resources of a global resource array into provided slot.
1461+
bool CGHLSLRuntime::initializeGlobalResourceArray(CodeGenFunction &CGF,
1462+
const VarDecl *ArrayDecl,
1463+
SourceLocation Loc,
1464+
AggValueSlot &DestSlot) {
1465+
assert(ArrayDecl->getType()->isHLSLResourceRecordArray() &&
1466+
ArrayDecl->hasGlobalStorage() &&
1467+
ArrayDecl->getStorageClass() != SC_Static &&
1468+
"expected global non-static resource array");
14701469

14711470
// Find binding info for the resource array. For implicit binding
14721471
// the HLSLResourceBindingAttr should have been added by SemaHLSL.
@@ -1476,27 +1475,63 @@ bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
14761475

14771476
// Find the individual resource type.
14781477
ASTContext &AST = ArrayDecl->getASTContext();
1479-
QualType ResTy = AST.getBaseElementType(ResultTy);
1480-
const auto *ResArrayTy = cast<ConstantArrayType>(ResultTy.getTypePtr());
1481-
1482-
// Use the provided LHS for the result.
1483-
AggValueSlot ValueSlot = AggValueSlot::forAddr(
1484-
LHS.getAddress(), Qualifiers(), AggValueSlot::IsDestructed_t(true),
1485-
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
1486-
AggValueSlot::DoesNotOverlap);
1478+
QualType ResTy = AST.getBaseElementType(ArrayDecl->getType());
1479+
const auto *ResArrayTy =
1480+
cast<ConstantArrayType>(ArrayDecl->getType().getTypePtr());
14871481

14881482
// Create Value for index and total array size (= range size).
14891483
int Size = getTotalArraySize(AST, ResArrayTy);
14901484
llvm::Value *Zero = llvm::ConstantInt::get(CGM.IntTy, 0);
14911485
llvm::Value *Range = llvm::ConstantInt::get(CGM.IntTy, Size);
14921486

1493-
// Initialize individual resources in the array into LHS.
1494-
std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray(
1495-
CGF, ResTy->getAsCXXRecordDecl(), ResArrayTy, ValueSlot, Range, Zero,
1496-
ArrayDecl->getName(), Binding, {Zero}, RHSExpr->getExprLoc());
1487+
// Initialize individual resources in the array into DestSlot.
1488+
std::optional<llvm::Value *> EndIndex = initializeResourceArrayFromGlobal(
1489+
CGF, ResTy->getAsCXXRecordDecl(), ResArrayTy, DestSlot, Range, Zero,
1490+
ArrayDecl->getName(), Binding, {Zero}, Loc);
14971491
return EndIndex.has_value();
14981492
}
14991493

1494+
// If the expression is a global resource array, initialize all of its resources
1495+
// into Dest. Returns false if no initialization has been performed and the
1496+
// array copy should be handled by the default codegen.
1497+
bool CGHLSLRuntime::emitGlobalResourceArray(CodeGenFunction &CGF, const Expr *E,
1498+
AggValueSlot &DestSlot) {
1499+
assert(E->getType()->isHLSLResourceRecordArray() &&
1500+
"expected resource array");
1501+
1502+
// Find the array declaration for the expression. Fallback to the default
1503+
// handling if it's not a global resource array.
1504+
const VarDecl *ArrayDecl =
1505+
dyn_cast_or_null<VarDecl>(getArrayDecl(CGF.CGM.getContext(), E));
1506+
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
1507+
ArrayDecl->getStorageClass() == SC_Static)
1508+
return false;
1509+
1510+
return initializeGlobalResourceArray(CGF, ArrayDecl, E->getExprLoc(),
1511+
DestSlot);
1512+
}
1513+
1514+
// If the expression is a global resource array, create a temporary and
1515+
// initialize all of its resources, and return it as an LValue. Returns nullopt
1516+
// if no initialization has been performed and the handling should follow the
1517+
// default path.
1518+
std::optional<LValue> CGHLSLRuntime::emitGlobalResourceArrayAsLValue(
1519+
CodeGenFunction &CGF, const VarDecl *ArrayDecl, SourceLocation Loc) {
1520+
assert(ArrayDecl->getType()->isHLSLResourceRecordArray() &&
1521+
"expected resource array declaration");
1522+
1523+
if (!ArrayDecl->hasGlobalStorage() ||
1524+
ArrayDecl->getStorageClass() == SC_Static)
1525+
return std::nullopt;
1526+
1527+
AggValueSlot TmpArraySlot =
1528+
CGF.CreateAggTemp(ArrayDecl->getType(), "tmpResArray");
1529+
if (initializeGlobalResourceArray(CGF, ArrayDecl, Loc, TmpArraySlot))
1530+
return CGF.MakeAddrLValue(TmpArraySlot.getAddress(), ArrayDecl->getType(),
1531+
AlignmentSource::Decl);
1532+
return std::nullopt;
1533+
}
1534+
15001535
RawAddress CGHLSLRuntime::createBufferMatrixTempAddress(const LValue &LV,
15011536
SourceLocation Loc,
15021537
CodeGenFunction &CGF) {
@@ -1588,18 +1623,16 @@ CGHLSLRuntime::emitResourceMemberExpr(CodeGenFunction &CGF,
15881623
ME->getType()->isHLSLResourceRecordArray()) &&
15891624
"expected resource member expression");
15901625

1591-
if (ME->getType()->isHLSLResourceRecordArray()) {
1592-
// FIXME: Handle member access of the whole array of resources
1593-
// (llvm/llvm-project#187087). Access to individual resource array elements
1594-
// is already handled in emitResourceArraySubscriptExpr.
1595-
return std::nullopt;
1596-
}
1597-
15981626
const VarDecl *ResourceVD =
15991627
findAssociatedResourceDeclForStruct(CGF.CGM.getContext(), ME);
16001628
if (!ResourceVD)
16011629
return std::nullopt;
16021630

1631+
// Handle member of resource array type.
1632+
if (ResourceVD->getType()->isHLSLResourceRecordArray())
1633+
return emitGlobalResourceArrayAsLValue(CGF, ResourceVD, ME->getExprLoc());
1634+
1635+
// Handle member that is an individual resource.
16031636
GlobalVariable *ResGV =
16041637
cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(ResourceVD));
16051638
const DataLayout &DL = CGM.getDataLayout();

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace CodeGen {
7979
class CodeGenModule;
8080
class CodeGenFunction;
8181
class LValue;
82+
class AggValueSlot;
8283

8384
class CGHLSLOffsetInfo {
8485
SmallVector<uint32_t> Offsets;
@@ -299,7 +300,12 @@ class CGHLSLRuntime {
299300
std::optional<LValue>
300301
emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
301302
CodeGenFunction &CGF);
302-
bool emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, CodeGenFunction &CGF);
303+
304+
bool emitGlobalResourceArray(CodeGenFunction &CGF, const Expr *E,
305+
AggValueSlot &DestSlot);
306+
std::optional<LValue>
307+
emitGlobalResourceArrayAsLValue(CodeGenFunction &CGF,
308+
const VarDecl *ArrayDecl, SourceLocation Loc);
303309

304310
std::optional<LValue> emitBufferArraySubscriptExpr(
305311
const ArraySubscriptExpr *E, CodeGenFunction &CGF,
@@ -349,6 +355,11 @@ class CGHLSLRuntime {
349355
HLSLAppliedSemanticAttr *Semantic,
350356
std::optional<unsigned> Index);
351357

358+
bool initializeGlobalResourceArray(CodeGenFunction &CGF,
359+
const VarDecl *ArrayDecl,
360+
SourceLocation Loc,
361+
AggValueSlot &DestSlot);
362+
352363
llvm::Triple::ArchType getArch();
353364

354365
llvm::DenseMap<const clang::RecordType *, llvm::StructType *> LayoutTypes;

0 commit comments

Comments
 (0)