Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -13159,6 +13159,9 @@ def err_hlsl_resource_range_overlap: Error<
"%select{All|Vertex|Hull|Domain|Geometry|Pixel|Amplification|Mesh}9">;
def note_hlsl_resource_range_here: Note<"overlapping resource range here">;

def err_hlsl_incomplete_resource_array_in_function_param: Error<
"incomplete resource array in a function parameter">;

// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5419,7 +5419,7 @@ bool Type::isHLSLResourceRecordArray() const {
const Type *Ty = getUnqualifiedDesugaredType();
if (!Ty->isArrayType())
return false;
while (isa<ConstantArrayType>(Ty))
while (isa<ArrayType>(Ty))
Ty = Ty->getArrayElementTypeNoTypeQual();
return Ty->isHLSLResourceRecord();
}
Expand All @@ -5432,7 +5432,7 @@ bool Type::isHLSLIntangibleType() const {
return Ty->isHLSLBuiltinIntangibleType();

// unwrap arrays
while (isa<ConstantArrayType>(Ty))
while (isa<ArrayType>(Ty))
Ty = Ty->getArrayElementTypeNoTypeQual();

const RecordType *RT =
Expand Down
134 changes: 106 additions & 28 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "CodeGenModule.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
Expand All @@ -36,6 +37,7 @@
#include "llvm/Support/Alignment.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>

using namespace clang;
using namespace CodeGen;
Expand Down Expand Up @@ -190,6 +192,71 @@ static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD,
Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst()));
}

// Initializes local resource array variable. For multi-dimensional arrays it
// calls itself recursively to initialize its sub-arrays. The Index used in the
// resource constructor calls will begin at StartIndex and will be incremented
// for each array element. The last last used resource Index is returned to the
// caller.
static Value *initializeLocalResourceArray(
CodeGenFunction &CGF, AggValueSlot &ValueSlot,
const ConstantArrayType *ArrayTy, CXXConstructorDecl *CD,
llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName,
HLSLResourceBindingAttr *RBA, HLSLVkBindingAttr *VkBinding,
ArrayRef<llvm::Value *> PrevGEPIndices, SourceLocation ArraySubsExprLoc) {

llvm::IntegerType *IntTy = CGF.CGM.IntTy;
llvm::Value *Index = StartIndex;
llvm::Value *One = llvm::ConstantInt::get(IntTy, 1);
uint64_t ArraySize = ArrayTy->getSExtSize();
QualType ElemType = ArrayTy->getElementType();
Address TmpArrayAddr = ValueSlot.getAddress();

// Add additional index to the getelementptr call indices.
// This index will be updated for each array element in the loops below.
SmallVector<llvm::Value *> GEPIndices(PrevGEPIndices);
GEPIndices.push_back(llvm::ConstantInt::get(IntTy, 0));

// array of arrays - recursively initialize the sub-arrays
if (ElemType->isArrayType()) {
const ConstantArrayType *SubArrayTy = cast<ConstantArrayType>(ElemType);
for (uint64_t I = 0; I < ArraySize; I++) {
if (I > 0) {
Index = CGF.Builder.CreateAdd(Index, One);
GEPIndices.back() = llvm::ConstantInt::get(IntTy, I);
}
// recursively initialize the sub-array
Index = initializeLocalResourceArray(
CGF, ValueSlot, SubArrayTy, CD, Range, Index, ResourceName, RBA,
VkBinding, GEPIndices, ArraySubsExprLoc);
}
return Index;
}

// array of resources - initialize each resource in the array
llvm::Type *Ty = CGF.ConvertTypeForMem(ElemType);
CharUnits ElemSize = CD->getASTContext().getTypeSizeInChars(ElemType);
CharUnits Align =
TmpArrayAddr.getAlignment().alignmentOfArrayElement(ElemSize);

for (uint64_t I = 0; I < ArraySize; I++) {
if (I > 0) {
Index = CGF.Builder.CreateAdd(Index, One);
GEPIndices.back() = llvm::ConstantInt::get(IntTy, I);
}
Address ThisAddress =
CGF.Builder.CreateGEP(TmpArrayAddr, GEPIndices, Ty, Align);
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(ThisAddress, ElemType);

CallArgList Args;
createResourceCtorArgs(CGF.CGM, CD, ThisPtr, Range, Index, ResourceName,
RBA, VkBinding, Args);
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress,
Args, ValueSlot.mayOverlap(), ArraySubsExprLoc,
ValueSlot.isSanitizerChecked());
}
return Index;
}

} // namespace

llvm::Type *
Expand Down Expand Up @@ -802,16 +869,14 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
ArraySubsExpr->getType()->isHLSLResourceRecordArray() &&
"expected resource array subscript expression");

// let clang codegen handle local resource array subscripts
const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr));
// Let clang codegen handle local resource array subscripts,
// or when the subscript references on opaque expression (as part of
// ArrayInitLoopExpr AST node).
const VarDecl *ArrayDecl =
dyn_cast_or_null<VarDecl>(getArrayDecl(ArraySubsExpr));
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
return std::nullopt;

if (ArraySubsExpr->getType()->isArrayType())
// FIXME: this is not yet implemented (llvm/llvm-project#145426)
llvm_unreachable(
"indexing of sub-arrays of multidimensional arrays not supported yet");

// get the resource array type
ASTContext &AST = ArrayDecl->getASTContext();
const Type *ResArrayTy = ArrayDecl->getType().getTypePtr();
Expand All @@ -832,26 +897,30 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
CGM.IntTy, AST.getConstantArrayElementCount(ArrayTy));
SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier);
}

Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex;
ASE = dyn_cast<ArraySubscriptExpr>(ASE->getBase()->IgnoreParenImpCasts());
}

// find binding info for the resource array (for implicit binding
// an HLSLResourceBindingAttr should have been added by SemaHLSL)
QualType ResourceTy = ArraySubsExpr->getType();
HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>();
HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
assert((VkBinding || RBA) && "resource array must have a binding attribute");

// Find the individual resource type
QualType ResultTy = ArraySubsExpr->getType();
QualType ResourceTy =
ResultTy->isArrayType() ? AST.getBaseElementType(ResultTy) : ResultTy;

// lookup the resource class constructor based on the resource type and
// binding
CXXConstructorDecl *CD = findResourceConstructorDecl(
AST, ResourceTy, VkBinding || RBA->hasRegisterSlot());

// create a temporary variable for the resource class instance (we need to
// create a temporary variable for the result, which is either going
// to be a single resource instance or a local array of resources (we need to
// return an LValue)
RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy);
RawAddress TmpVar = CGF.CreateMemTemp(ResultTy);
if (CGF.EmitLifetimeStart(TmpVar.getPointer()))
CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>(
NormalEHLifetimeMarker, TmpVar);
Expand All @@ -860,26 +929,35 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true),
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
AggValueSlot::DoesNotOverlap);

Address ThisAddress = ValueSlot.getAddress();
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
ThisAddress, CD->getThisType()->getPointeeType());
Address TmpVarAddress = ValueSlot.getAddress();

// get total array size (= range size)
llvm::Value *Range =
llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(AST, ResArrayTy));

// assemble the constructor parameters
CallArgList Args;
createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(),
RBA, VkBinding, Args);

// call the constructor
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, Args,
ValueSlot.mayOverlap(),
ArraySubsExpr->getExprLoc(),
ValueSlot.isSanitizerChecked());

return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(),
AlignmentSource::Decl);
// if the result of the subscript operation is a single resource - call the
// constructor
if (ResultTy == ResourceTy) {
QualType ThisType = CD->getThisType()->getPointeeType();
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(TmpVarAddress, ThisType);

// assemble the constructor parameters
CallArgList Args;
createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(),
RBA, VkBinding, Args);
// call the constructor
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, TmpVarAddress,
Args, ValueSlot.mayOverlap(),
ArraySubsExpr->getExprLoc(),
ValueSlot.isSanitizerChecked());
} else {
// result of the subscript operation is a local resource array
const ConstantArrayType *ArrayTy =
cast<ConstantArrayType>(ResultTy.getTypePtr());
initializeLocalResourceArray(CGF, ValueSlot, ArrayTy, CD, Range, Index,
ArrayDecl->getName(), RBA, VkBinding,
{llvm::ConstantInt::get(CGM.IntTy, 0)},
ArraySubsExpr->getExprLoc());
}
return CGF.MakeAddrLValue(TmpVar, ResultTy, AlignmentSource::Decl);
}
17 changes: 14 additions & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14380,9 +14380,12 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
return;
}

// Provide a specific diagnostic for uninitialized variable
// definitions with incomplete array type.
if (Type->isIncompleteArrayType()) {
// Provide a specific diagnostic for uninitialized variable definitions
// with incomplete array type, unless it is a global unbounded HLSL resource
// array.
if (Type->isIncompleteArrayType() &&
!(getLangOpts().HLSL && Var->hasGlobalStorage() &&
Type->isHLSLResourceRecordArray())) {
if (Var->isConstexpr())
Diag(Var->getLocation(), diag::err_constexpr_var_requires_const_init)
<< Var;
Expand Down Expand Up @@ -15471,6 +15474,14 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
}
}

// Incomplete resource arrays are not allowed as function parameters in HLSL
if (getLangOpts().HLSL && parmDeclType->isIncompleteArrayType() &&
parmDeclType->isHLSLResourceRecordArray()) {
Diag(D.getIdentifierLoc(),
diag::err_hlsl_incomplete_resource_array_in_function_param);
D.setInvalidType(true);
}

// Temporarily put parameter variables in the translation unit, not
// the enclosing context. This prevents them from accidentally
// looking like class members in C++.
Expand Down
18 changes: 10 additions & 8 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,8 @@ getResourceArrayHandleType(VarDecl *VD) {
assert(VD->getType()->isHLSLResourceRecordArray() &&
"expected array of resource records");
const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
Ty = CAT->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
while (const ArrayType *AT = dyn_cast<ArrayType>(Ty))
Ty = AT->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty);
}

Expand Down Expand Up @@ -2008,9 +2008,11 @@ static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
}

void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
if (isa<VarDecl>(TheDecl)) {
if (SemaRef.RequireCompleteType(TheDecl->getBeginLoc(),
cast<ValueDecl>(TheDecl)->getType(),
if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) {
QualType Ty = VD->getType();
if (Ty->isIncompleteArrayType())
Ty = cast<IncompleteArrayType>(Ty)->getElementType();
if (SemaRef.RequireCompleteType(TheDecl->getBeginLoc(), Ty,
diag::err_incomplete_type))
return;
}
Expand Down Expand Up @@ -3820,9 +3822,9 @@ void SemaHLSL::collectResourceBindingsOnVarDecl(VarDecl *VD) {
// Unwrap arrays
// FIXME: Calculate array size while unwrapping
const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
while (Ty->isConstantArrayType()) {
const ConstantArrayType *CAT = cast<ConstantArrayType>(Ty);
Ty = CAT->getElementType()->getUnqualifiedDesugaredType();
while (Ty->isArrayType()) {
const ArrayType *AT = cast<ArrayType>(Ty);
Ty = AT->getElementType()->getUnqualifiedDesugaredType();
}

// Resource (or array of resources)
Expand Down
Loading