Skip to content

Commit 8457635

Browse files
committed
[HLSL] Implement HLSL intialization list support
This PR implements HLSL's initialization list behvaior as specified in the draft language specifcation under [*Decl.Init.Agg*](https://microsoft.github.io/hlsl-specs/specs/hlsl.html #Decl.Init.Agg). This behavior is a bit unusual for C/C++ because intermediate braces in initializer lists are ignored and a whole array of additional conversions occur unintuitively to how initializaiton works in C. The implementaiton in this PR generates a valid C/C++ initialization list AST for the HLSL initializer so that there are no changes required to Clang's CodeGen to support this. This design will also allow us to use Clang's rewrite to convert HLSL initializers to valid C/C++ initializers that are equivalent. It does have the downside that it will generate often redundant accesses during codegen. The IR optimizer is extremely good at eliminating those so this will have no impact on the final executable performance. There is some opportunity for optimizing the initializer list generation that we could consider in subsequent commits. One notable opportunity would be to identify aggregate objects that occur in the same place in both initializers and do not require converison, those aggregates could be initialized as aggregates rather than fully scalarized. Closes llvm#56067
1 parent 6ffc445 commit 8457635

File tree

9 files changed

+964
-5
lines changed

9 files changed

+964
-5
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12574,6 +12574,9 @@ def err_hlsl_pointers_unsupported : Error<
1257412574
"%select{pointers|references}0 are unsupported in HLSL">;
1257512575
def err_hlsl_missing_resource_class : Error<"HLSL resource needs to have [[hlsl::resource_class()]] attribute">;
1257612576
def err_hlsl_attribute_needs_intangible_type: Error<"attribute %0 can be used only on HLSL intangible type %1">;
12577+
def err_hlsl_incorrect_num_initializers: Error<
12578+
"too %select{few|many}0 initializers in list for type %1 "
12579+
"(expected %2 but found %3)">;
1257712580

1257812581
def err_hlsl_operator_unsupported : Error<
1257912582
"the '%select{&|*|->}0' operator is unsupported in HLSL">;

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
namespace clang {
2727
class AttributeCommonInfo;
2828
class IdentifierInfo;
29+
class InitializedEntity;
30+
class InitializationKind;
2931
class ParsedAttr;
3032
class Scope;
3133
class VarDecl;
@@ -145,6 +147,9 @@ class SemaHLSL : public SemaBase {
145147

146148
QualType getInoutParameterType(QualType Ty);
147149

150+
bool TransformInitList(const InitializedEntity &Entity,
151+
const InitializationKind &Kind, InitListExpr *Init);
152+
148153
private:
149154
// HLSL resource type attributes need to be processed all at once.
150155
// This is a list to collect them.

clang/lib/Sema/SemaChecking.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11625,9 +11625,12 @@ static void AnalyzeImplicitConversions(
1162511625

1162611626
// Propagate whether we are in a C++ list initialization expression.
1162711627
// If so, we do not issue warnings for implicit int-float conversion
11628-
// precision loss, because C++11 narrowing already handles it.
11629-
bool IsListInit = Item.IsListInit ||
11630-
(isa<InitListExpr>(OrigE) && S.getLangOpts().CPlusPlus);
11628+
// precision loss, because C++11 narrowing already handles it. HLSL's
11629+
// initialization lists are special, so they shouldn't observe the C++
11630+
// behavior here.
11631+
bool IsListInit =
11632+
Item.IsListInit || (isa<InitListExpr>(OrigE) &&
11633+
S.getLangOpts().CPlusPlus && !S.getLangOpts().HLSL);
1163111634

1163211635
if (E->isTypeDependent() || E->isValueDependent())
1163311636
return;

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,3 +2576,162 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
25762576
}
25772577
}
25782578
}
2579+
2580+
static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
2581+
llvm::SmallVectorImpl<Expr *> &List,
2582+
llvm::SmallVectorImpl<QualType> &DestTypes) {
2583+
if (List.size() >= DestTypes.size())
2584+
return false;
2585+
InitializedEntity Entity =
2586+
InitializedEntity::InitializeParameter(Ctx, DestTypes[List.size()], false);
2587+
ExprResult Res =
2588+
S.PerformCopyInitialization(Entity, E->getBeginLoc(), E);
2589+
if (Res.isInvalid())
2590+
return false;
2591+
Expr *Init = Res.get();
2592+
List.push_back(Init);
2593+
return true;
2594+
}
2595+
2596+
static void BuildIntializerList(Sema &S, ASTContext &Ctx, Expr *E,
2597+
llvm::SmallVectorImpl<Expr *> &List,
2598+
llvm::SmallVectorImpl<QualType> &DestTypes,
2599+
bool &ExcessInits) {
2600+
if (List.size() >= DestTypes.size()) {
2601+
ExcessInits = true;
2602+
return;
2603+
}
2604+
2605+
// If this is an initialization list, traverse the sub initializers.
2606+
if (auto *Init = dyn_cast<InitListExpr>(E)) {
2607+
for (auto *SubInit : Init->inits())
2608+
BuildIntializerList(S, Ctx, SubInit, List, DestTypes, ExcessInits);
2609+
return;
2610+
}
2611+
2612+
// If this is a scalar type, just enqueue the expression.
2613+
QualType Ty = E->getType();
2614+
if (Ty->isScalarType()) {
2615+
(void)CastInitializer(S, Ctx, E, List, DestTypes);
2616+
return;
2617+
}
2618+
2619+
if (auto *ATy = Ty->getAs<VectorType>()) {
2620+
uint64_t Size = ATy->getNumElements();
2621+
2622+
if (List.size() + Size > DestTypes.size()) {
2623+
ExcessInits = true;
2624+
return;
2625+
}
2626+
QualType SizeTy = Ctx.getSizeType();
2627+
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
2628+
for (uint64_t I = 0; I < Size; ++I) {
2629+
auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I),
2630+
SizeTy, SourceLocation());
2631+
2632+
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
2633+
E, E->getBeginLoc(), Idx, E->getEndLoc());
2634+
if (ElExpr.isInvalid())
2635+
return;
2636+
if (!CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes))
2637+
return;
2638+
}
2639+
return;
2640+
}
2641+
2642+
if (auto *VTy = dyn_cast<ConstantArrayType>(Ty.getTypePtr())) {
2643+
uint64_t Size = VTy->getZExtSize();
2644+
QualType SizeTy = Ctx.getSizeType();
2645+
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
2646+
for (uint64_t I = 0; I < Size; ++I) {
2647+
auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I),
2648+
SizeTy, SourceLocation());
2649+
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
2650+
E, E->getBeginLoc(), Idx, E->getEndLoc());
2651+
if (ElExpr.isInvalid())
2652+
return;
2653+
BuildIntializerList(S, Ctx, ElExpr.get(), List, DestTypes, ExcessInits);
2654+
}
2655+
return;
2656+
}
2657+
2658+
if (auto *RTy = Ty->getAs<RecordType>()) {
2659+
for (auto *FD : RTy->getDecl()->fields()) {
2660+
DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess());
2661+
DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc());
2662+
ExprResult Res = S.BuildFieldReferenceExpr(
2663+
E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
2664+
if (Res.isInvalid())
2665+
return;
2666+
BuildIntializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
2667+
}
2668+
}
2669+
}
2670+
2671+
static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
2672+
llvm::SmallVectorImpl<Expr *>::iterator &It) {
2673+
if (Ty->isScalarType()) {
2674+
return *(It++);
2675+
}
2676+
llvm::SmallVector<Expr *> Inits;
2677+
assert(!isa<MatrixType>(Ty) && "Matrix types not yet supported in HLSL");
2678+
if (Ty->isVectorType() || Ty->isConstantArrayType()) {
2679+
QualType ElTy;
2680+
uint64_t Size = 0;
2681+
if (auto *ATy = Ty->getAs<VectorType>()) {
2682+
ElTy = ATy->getElementType();
2683+
Size = ATy->getNumElements();
2684+
} else {
2685+
auto *VTy = cast<ConstantArrayType>(Ty.getTypePtr());
2686+
ElTy = VTy->getElementType();
2687+
Size = VTy->getZExtSize();
2688+
}
2689+
for (uint64_t I = 0; I < Size; ++I)
2690+
Inits.push_back(GenerateInitLists(Ctx, ElTy, It));
2691+
}
2692+
if (const RecordDecl *RD = Ty->getAsRecordDecl()) {
2693+
for (auto *FD : RD->fields()) {
2694+
Inits.push_back(GenerateInitLists(Ctx, FD->getType(), It));
2695+
}
2696+
}
2697+
auto *NewInit = new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(),
2698+
Inits, Inits.back()->getEndLoc());
2699+
NewInit->setType(Ty);
2700+
return NewInit;
2701+
}
2702+
2703+
bool SemaHLSL::TransformInitList(const InitializedEntity &Entity,
2704+
const InitializationKind &Kind,
2705+
InitListExpr *Init) {
2706+
// If the initializer is a scalar, just return it.
2707+
if (Init->getType()->isScalarType())
2708+
return true;
2709+
ASTContext &Ctx = SemaRef.getASTContext();
2710+
llvm::SmallVector<QualType, 16> DestTypes;
2711+
// An initializer list might be attempting to initialize a reference or
2712+
// rvalue-reference. When checking the initializer we should look through the
2713+
// reference.
2714+
QualType InitTy = Entity.getType().getNonReferenceType();
2715+
BuildFlattenedTypeList(InitTy, DestTypes);
2716+
2717+
llvm::SmallVector<Expr *, 16> ArgExprs;
2718+
bool ExcessInits = false;
2719+
for (Expr *Arg : Init->inits())
2720+
BuildIntializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes, ExcessInits);
2721+
2722+
if (DestTypes.size() != ArgExprs.size() || ExcessInits) {
2723+
int TooManyOrFew = ExcessInits ? 1 : 0;
2724+
SemaRef.Diag(Init->getBeginLoc(), diag::err_hlsl_incorrect_num_initializers)
2725+
<< TooManyOrFew << InitTy << DestTypes.size() << ArgExprs.size();
2726+
return false;
2727+
}
2728+
2729+
auto It = ArgExprs.begin();
2730+
// GenerateInitLists will always return an InitListExpr here, because the
2731+
// scalar case is handled above.
2732+
auto *NewInit = cast<InitListExpr>(GenerateInitLists(Ctx, InitTy, It));
2733+
Init->resizeInits(Ctx, NewInit->getNumInits());
2734+
for (unsigned I = 0; I < NewInit->getNumInits(); ++I)
2735+
Init->updateInit(Ctx, I, NewInit->getInit(I));
2736+
return true;
2737+
}

clang/lib/Sema/SemaInit.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "clang/Sema/Initialization.h"
2727
#include "clang/Sema/Lookup.h"
2828
#include "clang/Sema/Ownership.h"
29+
#include "clang/Sema/SemaHLSL.h"
2930
#include "clang/Sema/SemaObjC.h"
3031
#include "llvm/ADT/APInt.h"
3132
#include "llvm/ADT/FoldingSet.h"
@@ -4783,6 +4784,10 @@ static void TryListInitialization(Sema &S,
47834784
bool TreatUnavailableAsInvalid) {
47844785
QualType DestType = Entity.getType();
47854786

4787+
if (S.getLangOpts().HLSL &&
4788+
!S.HLSL().TransformInitList(Entity, Kind, InitList))
4789+
return;
4790+
47864791
// C++ doesn't allow scalar initialization with more than one argument.
47874792
// But C99 complex numbers are scalars and it makes sense there.
47884793
if (S.getLangOpts().CPlusPlus && DestType->isScalarType() &&

clang/test/CodeGenHLSL/ArrayTemporary.hlsl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
23

34
void fn(float x[2]) { }
@@ -27,7 +28,7 @@ void fn2(Obj O[4]) { }
2728
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 32, i1 false)
2829
// CHECK: call void {{.*}}fn2{{.*}}(ptr noundef byval([4 x %struct.Obj]) align 4 [[Tmp]])
2930
void call2() {
30-
Obj Arr[4] = {};
31+
Obj Arr[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
3132
fn2(Arr);
3233
}
3334

0 commit comments

Comments
 (0)