Skip to content
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
75 changes: 75 additions & 0 deletions clang/include/clang/AST/HLSLResource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===- HLSLResource.h - Routines for HLSL resources and bindings ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides shared routines to help analyze HLSL resources and
// theirs bindings during Sema and CodeGen.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_HLSLRESOURCE_H
#define LLVM_CLANG_AST_HLSLRESOURCE_H

#include "clang/AST/ASTContext.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/DeclBase.h"
#include "clang/Basic/TargetInfo.h"

namespace clang {

class HLSLResourceBindingAttr;
class HLSLRVkBindingAttr;

namespace hlsl {

struct ResourceBindingAttrs {
HLSLResourceBindingAttr *RegBinding;
HLSLVkBindingAttr *VkBinding;

ResourceBindingAttrs(const Decl *D) {
RegBinding = D->getAttr<HLSLResourceBindingAttr>();
bool IsSpirv = D->getASTContext().getTargetInfo().getTriple().isSPIRV();
VkBinding = IsSpirv ? D->getAttr<HLSLVkBindingAttr>() : nullptr;
Copy link
Contributor

@bob80905 bob80905 Sep 29, 2025

Choose a reason for hiding this comment

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

I think getAttr should return a nullptr if the attr isn't available, which should make IsSpirv and this ternary unnecessary.
i.e, consider VkBinding = D->getAttr<HLSLVkBindingAttr>();

Copy link
Member Author

Choose a reason for hiding this comment

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

There can still be [[vk::binding]] attribute in HLSL that is compiled for DirectX and we need to handle that.

BTW SemaHLSL currently does not add the parsed attribute to the AST if the target is DirectX, which is not correct. The attribute needs to be preserved in the AST as is even if it is not used for rewriter scenarios. I will fix that in a follow-up PR.

}

bool hasBinding() const { return RegBinding || VkBinding; }
bool isExplicit() const {
return (RegBinding && RegBinding->hasRegisterSlot()) || VkBinding;
}

unsigned getSlot() const {
assert(isExplicit() && "no explicit binding");
if (VkBinding)
return VkBinding->getBinding();
if (RegBinding && RegBinding->hasRegisterSlot())
return RegBinding->getSlotNumber();
llvm_unreachable("no explicit binding");
}

unsigned getSpace() const {
if (VkBinding)
return VkBinding->getSet();
if (RegBinding)
return RegBinding->getSpaceNumber();
return 0;
}

bool hasImplicitOrderID() const {
return RegBinding && RegBinding->hasImplicitBindingOrderID();
}

unsigned getImplicitOrderID() const {
assert(hasImplicitOrderID());
return RegBinding->getImplicitBindingOrderID();
}
};

} // namespace hlsl

} // namespace clang

#endif // LLVM_CLANG_AST_HLSLRESOURCE_H
95 changes: 31 additions & 64 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/AST/HLSLResource.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/TargetOptions.h"
Expand Down Expand Up @@ -131,35 +132,24 @@ static CXXMethodDecl *lookupMethod(CXXRecordDecl *Record, StringRef Name,

static CXXMethodDecl *lookupResourceInitMethodAndSetupArgs(
CodeGenModule &CGM, CXXRecordDecl *ResourceDecl, llvm::Value *Range,
llvm::Value *Index, StringRef Name, HLSLResourceBindingAttr *RBA,
HLSLVkBindingAttr *VkBinding, CallArgList &Args) {
assert((VkBinding || RBA) && "at least one a binding attribute expected");
llvm::Value *Index, StringRef Name, ResourceBindingAttrs &Binding,
CallArgList &Args) {
assert(Binding.hasBinding() && "at least one binding attribute expected");

ASTContext &AST = CGM.getContext();
std::optional<uint32_t> RegisterSlot;
uint32_t SpaceNo = 0;
if (VkBinding) {
RegisterSlot = VkBinding->getBinding();
SpaceNo = VkBinding->getSet();
} else {
if (RBA->hasRegisterSlot())
RegisterSlot = RBA->getSlotNumber();
SpaceNo = RBA->getSpaceNumber();
}

CXXMethodDecl *CreateMethod = nullptr;
Value *NameStr = buildNameForResource(Name, CGM);
Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo);
Value *Space = llvm::ConstantInt::get(CGM.IntTy, Binding.getSpace());

if (RegisterSlot.has_value()) {
if (Binding.isExplicit()) {
// explicit binding
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RegisterSlot.value());
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, Binding.getSlot());
Args.add(RValue::get(RegSlot), AST.UnsignedIntTy);
CreateMethod = lookupMethod(ResourceDecl, "__createFromBinding", SC_Static);
} else {
// implicit binding
auto *OrderID =
llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID());
llvm::ConstantInt::get(CGM.IntTy, Binding.getImplicitOrderID());
Args.add(RValue::get(OrderID), AST.UnsignedIntTy);
CreateMethod =
lookupMethod(ResourceDecl, "__createFromImplicitBinding", SC_Static);
Expand Down Expand Up @@ -194,8 +184,8 @@ static std::optional<llvm::Value *> initializeLocalResourceArray(
CodeGenFunction &CGF, CXXRecordDecl *ResourceDecl,
const ConstantArrayType *ArrayTy, AggValueSlot &ValueSlot,
llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName,
HLSLResourceBindingAttr *RBA, HLSLVkBindingAttr *VkBinding,
ArrayRef<llvm::Value *> PrevGEPIndices, SourceLocation ArraySubsExprLoc) {
ResourceBindingAttrs &Binding, ArrayRef<llvm::Value *> PrevGEPIndices,
SourceLocation ArraySubsExprLoc) {

ASTContext &AST = CGF.getContext();
llvm::IntegerType *IntTy = CGF.CGM.IntTy;
Expand All @@ -220,7 +210,7 @@ static std::optional<llvm::Value *> initializeLocalResourceArray(
}
std::optional<llvm::Value *> MaybeIndex = initializeLocalResourceArray(
CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index, ResourceName,
RBA, VkBinding, GEPIndices, ArraySubsExprLoc);
Binding, GEPIndices, ArraySubsExprLoc);
if (!MaybeIndex)
return std::nullopt;
Index = *MaybeIndex;
Expand All @@ -244,8 +234,7 @@ static std::optional<llvm::Value *> initializeLocalResourceArray(

CallArgList Args;
CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs(
CGF.CGM, ResourceDecl, Range, Index, ResourceName, RBA, VkBinding,
Args);
CGF.CGM, ResourceDecl, Range, Index, ResourceName, Binding, Args);

if (!CreateMethod)
// This can happen if someone creates an array of structs that looks like
Expand Down Expand Up @@ -439,14 +428,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
emitBufferGlobalsAndMetadata(BufDecl, BufGV);

// Initialize cbuffer from binding (implicit or explicit)
if (HLSLVkBindingAttr *VkBinding = BufDecl->getAttr<HLSLVkBindingAttr>()) {
initializeBufferFromBinding(BufDecl, BufGV, VkBinding);
} else {
HLSLResourceBindingAttr *RBA = BufDecl->getAttr<HLSLResourceBindingAttr>();
assert(RBA &&
"cbuffer/tbuffer should always have resource binding attribute");
initializeBufferFromBinding(BufDecl, BufGV, RBA);
}
initializeBufferFromBinding(BufDecl, BufGV);
}

void CGHLSLRuntime::addRootSignature(
Expand Down Expand Up @@ -810,44 +792,29 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV,
}

void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV,
HLSLVkBindingAttr *VkBinding) {
assert(VkBinding && "expect a nonnull binding attribute");
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1);
auto *Set = llvm::ConstantInt::get(CGM.IntTy, VkBinding->getSet());
auto *Binding = llvm::ConstantInt::get(CGM.IntTy, VkBinding->getBinding());
Value *Name = buildNameForResource(BufDecl->getName(), CGM);
llvm::Intrinsic::ID IntrinsicID =
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic();

SmallVector<Value *> Args{Set, Binding, RangeSize, Index, Name};
initializeBuffer(CGM, GV, IntrinsicID, Args);
}
llvm::GlobalVariable *GV) {
ResourceBindingAttrs Binding(BufDecl);
assert(Binding.hasBinding() &&
"cbuffer/tbuffer should always have resource binding attribute");

void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV,
HLSLResourceBindingAttr *RBA) {
assert(RBA && "expect a nonnull binding attribute");
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1);
auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
auto *Space = llvm::ConstantInt::get(CGM.IntTy, Binding.getSpace());
Value *Name = buildNameForResource(BufDecl->getName(), CGM);

llvm::Intrinsic::ID IntrinsicID =
RBA->hasRegisterSlot()
? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic()
: CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();

// buffer with explicit binding
if (RBA->hasRegisterSlot()) {
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
if (Binding.isExplicit()) {
llvm::Intrinsic::ID IntrinsicID =
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic();
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, Binding.getSlot());
SmallVector<Value *> Args{Space, RegSlot, RangeSize, Index, Name};
initializeBuffer(CGM, GV, IntrinsicID, Args);
} else {
// buffer with implicit binding
llvm::Intrinsic::ID IntrinsicID =
CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
auto *OrderID =
llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID());
llvm::ConstantInt::get(CGM.IntTy, Binding.getImplicitOrderID());
SmallVector<Value *> Args{OrderID, Space, RangeSize, Index, Name};
initializeBuffer(CGM, GV, IntrinsicID, Args);
}
Expand Down Expand Up @@ -960,9 +927,9 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(

// Find binding info for the resource array. For implicit binding
// an HLSLResourceBindingAttr should have been added by SemaHLSL.
HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>();
HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
assert((VkBinding || RBA) && "resource array must have a binding attribute");
ResourceBindingAttrs Binding(ArrayDecl);
assert((Binding.hasBinding()) &&
"resource array must have a binding attribute");

// Find the individual resource type.
QualType ResultTy = ArraySubsExpr->getType();
Expand Down Expand Up @@ -992,7 +959,7 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
CallArgList Args;
CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs(
CGF.CGM, ResourceTy->getAsCXXRecordDecl(), Range, Index,
ArrayDecl->getName(), RBA, VkBinding, Args);
ArrayDecl->getName(), Binding, Args);

if (!CreateMethod)
// This can happen if someone creates an array of structs that looks like
Expand All @@ -1009,8 +976,8 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
cast<ConstantArrayType>(ResultTy.getTypePtr());
std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray(
CGF, ResourceTy->getAsCXXRecordDecl(), ArrayTy, ValueSlot, Range, Index,
ArrayDecl->getName(), RBA, VkBinding,
{llvm::ConstantInt::get(CGM.IntTy, 0)}, ArraySubsExpr->getExprLoc());
ArrayDecl->getName(), Binding, {llvm::ConstantInt::get(CGM.IntTy, 0)},
ArraySubsExpr->getExprLoc());
if (!EndIndex)
return std::nullopt;
}
Expand Down
6 changes: 1 addition & 5 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,7 @@ class CGHLSLRuntime {
void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *BufGV);
void initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV,
HLSLVkBindingAttr *VkBinding);
void initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV,
HLSLResourceBindingAttr *RBA);
llvm::GlobalVariable *GV);
llvm::Triple::ArchType getArch();

llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
Expand Down
31 changes: 11 additions & 20 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "clang/AST/DeclarationName.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/HLSLResource.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
Expand Down Expand Up @@ -52,6 +53,7 @@
#include <utility>

using namespace clang;
using namespace clang::hlsl;
using RegisterType = HLSLResourceBindingAttr::RegisterType;

static CXXRecordDecl *createHostLayoutStruct(Sema &S,
Expand Down Expand Up @@ -3799,41 +3801,30 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);

// Gather resource binding information from attributes.
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
HLSLVkBindingAttr *VkBinding = VD->getAttr<HLSLVkBindingAttr>();
std::optional<uint32_t> RegisterSlot;
uint32_t SpaceNo = 0;
if (VkBinding) {
RegisterSlot = VkBinding->getBinding();
SpaceNo = VkBinding->getSet();
} else if (RBA) {
if (RBA->hasRegisterSlot())
RegisterSlot = RBA->getSlotNumber();
SpaceNo = RBA->getSpaceNumber();
}
// Gather resource binding attributes.
ResourceBindingAttrs Binding(VD);

// Find correct initialization method and create its arguments.
QualType ResourceTy = VD->getType();
CXXRecordDecl *ResourceDecl = ResourceTy->getAsCXXRecordDecl();
CXXMethodDecl *CreateMethod = nullptr;
llvm::SmallVector<Expr *> Args;

if (RegisterSlot.has_value()) {
if (Binding.isExplicit()) {
// The resource has explicit binding.
CreateMethod = lookupMethod(SemaRef, ResourceDecl, "__createFromBinding",
VD->getLocation());
IntegerLiteral *RegSlot = IntegerLiteral::Create(
AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
SourceLocation());
IntegerLiteral *RegSlot =
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, Binding.getSlot()),
AST.UnsignedIntTy, SourceLocation());
Args.push_back(RegSlot);
} else {
// The resource has implicit binding.
CreateMethod =
lookupMethod(SemaRef, ResourceDecl, "__createFromImplicitBinding",
VD->getLocation());
uint32_t OrderID = (RBA && RBA->hasImplicitBindingOrderID())
? RBA->getImplicitBindingOrderID()
uint32_t OrderID = (Binding.hasImplicitOrderID())
? Binding.getImplicitOrderID()
: getNextImplicitBindingOrderID();
IntegerLiteral *OrderId =
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, OrderID),
Expand All @@ -3848,7 +3839,7 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
return false;

IntegerLiteral *Space =
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, Binding.getSpace()),
AST.UnsignedIntTy, SourceLocation());
Args.push_back(Space);

Expand Down