Skip to content

[HLSL] Implement HLSL intialization list support #123141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -12626,6 +12626,9 @@ def err_hlsl_pointers_unsupported : Error<
"%select{pointers|references}0 are unsupported in HLSL">;
def err_hlsl_missing_resource_class : Error<"HLSL resource needs to have [[hlsl::resource_class()]] attribute">;
def err_hlsl_attribute_needs_intangible_type: Error<"attribute %0 can be used only on HLSL intangible type %1">;
def err_hlsl_incorrect_num_initializers: Error<
"too %select{few|many}0 initializers in list for type %1 "
"(expected %2 but found %3)">;

def err_hlsl_operator_unsupported : Error<
"the '%select{&|*|->}0' operator is unsupported in HLSL">;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/SemaHLSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
namespace clang {
class AttributeCommonInfo;
class IdentifierInfo;
class InitializedEntity;
class InitializationKind;
class ParsedAttr;
class Scope;
class VarDecl;
Expand Down Expand Up @@ -149,6 +151,9 @@ class SemaHLSL : public SemaBase {

QualType getInoutParameterType(QualType Ty);

bool TransformInitList(const InitializedEntity &Entity,
const InitializationKind &Kind, InitListExpr *Init);

private:
// HLSL resource type attributes need to be processed all at once.
// This is a list to collect them.
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,18 @@ void CXXRecordDecl::addedMember(Decl *D) {
if (Using->getDeclName().getCXXOverloadedOperator() == OO_Equal)
data().HasInheritedAssignment = true;
}

// HLSL: All user-defined data types are aggregates and use aggregate
// initialization, meanwhile most, but not all built-in types behave like
// aggregates. Resource types, and some other HLSL types that wrap handles
// don't behave like aggregates. We can identify these as different because we
// implicitly define "special" member functions, which aren't spellable in
// HLSL. This all _needs_ to change in the future. There are two
// relevant HLSL feature proposals that will depend on this changing:
// * 0005-strict-initializer-lists.md
// * https://github.com/microsoft/hlsl-specs/pull/325
if (getLangOpts().HLSL)
data().Aggregate = data().UserDeclaredSpecialMembers == 0;
Comment on lines +1475 to +1476
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this comment match what this is doing? If I'm reading this correctly we're using the fact that there are no user declared constructors/destructors/assignment operators to decide that this is an aggregate - I guess that's because users aren't allowed to write those in HLSL so we can use it as a proxy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea... I'll update the comment too. We can't strictly use implicit because some of the implicit types do behave like aggregates.

}

bool CXXRecordDecl::isLiteral() const {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5551,6 +5551,12 @@ CodeGenFunction::getOrCreateOpaqueRValueMapping(const OpaqueValueExpr *e) {
return EmitAnyExpr(e->getSourceExpr());
}

bool CodeGenFunction::isOpaqueValueEmitted(const OpaqueValueExpr *E) {
if (OpaqueValueMapping::shouldBindAsLValue(E))
return OpaqueLValues.contains(E);
return OpaqueRValues.contains(E);
}

RValue CodeGenFunction::EmitRValueForField(LValue LV,
const FieldDecl *FD,
SourceLocation Loc) {
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "CGCXXABI.h"
#include "CGHLSLRuntime.h"
#include "CGObjCRuntime.h"
#include "CGRecordLayout.h"
#include "CodeGenFunction.h"
Expand Down Expand Up @@ -1776,6 +1777,18 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
}
#endif

// HLSL initialization lists in the AST are an expansion which can contain
// side-effecting expressions wrapped in opaque value expressions. To properly
// emit these we need to emit the opaque values before we emit the argument
// expressions themselves. This is a little hacky, but it prevents us needing
// to do a bigger AST-level change for a language feature that we need
// deprecate in the near future. See related HLSL language proposals:
// * 0005-strict-initializer-lists.md
// * https://github.com/microsoft/hlsl-specs/pull/325
if (CGF.getLangOpts().HLSL && isa<InitListExpr>(ExprToVisit))
CGF.CGM.getHLSLRuntime().emitInitListOpaqueValues(
CGF, cast<InitListExpr>(ExprToVisit));

AggValueSlot Dest = EnsureSlot(ExprToVisit->getType());

LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), ExprToVisit->getType());
Expand Down
32 changes: 32 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

#include "CGHLSLRuntime.h"
#include "CGDebugInfo.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "TargetInfo.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
Expand Down Expand Up @@ -617,3 +619,33 @@ llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
llvm_unreachable("Convergence token should have been emitted.");
return nullptr;
}

class OpaqueValueVisitor : public RecursiveASTVisitor<OpaqueValueVisitor> {
public:
llvm::SmallPtrSet<OpaqueValueExpr *, 8> OVEs;
OpaqueValueVisitor() {}

bool VisitOpaqueValueExpr(OpaqueValueExpr *E) {
OVEs.insert(E);
return true;
}
};

void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
InitListExpr *E) {

typedef CodeGenFunction::OpaqueValueMappingData OpaqueValueMappingData;
OpaqueValueVisitor Visitor;
Visitor.TraverseStmt(E);
for (auto *OVE : Visitor.OVEs) {
if (CGF.isOpaqueValueEmitted(OVE))
continue;
if (OpaqueValueMappingData::shouldBindAsLValue(OVE)) {
LValue LV = CGF.EmitLValue(OVE->getSourceExpr());
OpaqueValueMappingData::bind(CGF, OVE, LV);
} else {
RValue RV = CGF.EmitAnyExpr(OVE->getSourceExpr());
OpaqueValueMappingData::bind(CGF, OVE, RV);
}
}
}
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class StructType;
namespace clang {
class VarDecl;
class ParmVarDecl;
class InitListExpr;
class HLSLBufferDecl;
class HLSLResourceBindingAttr;
class Type;
Expand All @@ -65,6 +66,7 @@ class FunctionDecl;
namespace CodeGen {

class CodeGenModule;
class CodeGenFunction;

class CGHLSLRuntime {
public:
Expand Down Expand Up @@ -161,6 +163,8 @@ class CGHLSLRuntime {

llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);

void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);

private:
void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
llvm::hlsl::ResourceClass RC,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3011,6 +3011,10 @@ class CodeGenFunction : public CodeGenTypeCache {
/// otherwise create one.
RValue getOrCreateOpaqueRValueMapping(const OpaqueValueExpr *e);

/// isOpaqueValueEmitted - Return true if the opaque value expression has
/// already been emitted.
bool isOpaqueValueEmitted(const OpaqueValueExpr *E);

/// Get the index of the current ArrayInitLoopExpr, if any.
llvm::Value *getArrayInitIndex() { return ArrayInitIndex; }

Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11679,8 +11679,12 @@ static void AnalyzeImplicitConversions(
// Propagate whether we are in a C++ list initialization expression.
// If so, we do not issue warnings for implicit int-float conversion
// precision loss, because C++11 narrowing already handles it.
bool IsListInit = Item.IsListInit ||
(isa<InitListExpr>(OrigE) && S.getLangOpts().CPlusPlus);
//
// HLSL's initialization lists are special, so they shouldn't observe the C++
// behavior here.
bool IsListInit =
Item.IsListInit || (isa<InitListExpr>(OrigE) &&
S.getLangOpts().CPlusPlus && !S.getLangOpts().HLSL);

if (E->isTypeDependent() || E->isValueDependent())
return;
Expand Down
Loading
Loading