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
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -5017,6 +5017,10 @@ def HLSLUnparsedSemantic : HLSLAnnotationAttr {
let Documentation = [InternalOnly];
}

def HLSLUserSemantic : HLSLSemanticAttr</* Indexable= */ 1> {
let Documentation = [InternalOnly];
}

def HLSLSV_Position : HLSLSemanticAttr</* Indexable= */ 1> {
let Documentation = [HLSLSV_PositionDocs];
}
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -13184,6 +13184,7 @@ def err_hlsl_semantic_indexing_not_supported
: Error<"semantic %0 does not allow indexing">;
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;

def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric types}0">, InGroup<LegacyConstantRegisterBinding>;
def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
Expand Down
8 changes: 6 additions & 2 deletions clang/include/clang/Sema/SemaHLSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/SemaBase.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/TargetParser/Triple.h"
#include <initializer_list>

Expand Down Expand Up @@ -259,9 +261,11 @@ class SemaHLSL : public SemaBase {
HLSLSemanticAttr *createSemantic(const SemanticInfo &Semantic,
DeclaratorDecl *TargetDecl);
bool determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *D,
SemanticInfo &ActiveSemantic);
SemanticInfo &ActiveSemantic,
llvm::StringSet<> &ActiveInputSemantics);
bool determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *D,
SemanticInfo &ActiveSemantic);
SemanticInfo &ActiveSemantic,
llvm::StringSet<> &ActiveInputSemantics);

void processExplicitBindingsOnDecl(VarDecl *D);

Expand Down
76 changes: 76 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,16 @@ static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV,
GV->addMetadata("spirv.Decorations", *Decoration);
}

static void addLocationDecoration(llvm::GlobalVariable *GV, unsigned Location) {
LLVMContext &Ctx = GV->getContext();
IRBuilder<> B(GV->getContext());
MDNode *Operands =
MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(/* Location */ 30)),
ConstantAsMetadata::get(B.getInt32(Location))});
MDNode *Decoration = MDNode::get(Ctx, {Operands});
GV->addMetadata("spirv.Decorations", *Decoration);
}

static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
llvm::Type *Ty, const Twine &Name,
unsigned BuiltInID) {
Expand All @@ -562,6 +572,69 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
return B.CreateLoad(Ty, GV);
}

static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M,
llvm::Type *Ty, unsigned Location,
StringRef Name) {
auto *GV = new llvm::GlobalVariable(
M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
/* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr,
llvm::GlobalVariable::GeneralDynamicTLSModel,
/* AddressSpace */ 7, /* isExternallyInitialized= */ true);
Comment on lines +578 to +582
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we be trying to find an existing global variable? This assumes that can be just one load, which may be true for now, but I'm not sure it will always be true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a good point, the thing is I don't think it is well defined yet what happens if:

  • input semantics are duplicated
  • semantic is duplicated using HLSL semantics and vk::builtin

Ex: DXC fails to build SPIR-V if I have the following shader:

RWStructuredBuffer<uint> output;
void main(uint a : SV_GroupThreadID, uint b : SV_GroupThreadID) {
    output[0] = a + b;
}

I don't think we should allow having an I/O semantic also be reachable with a vk::builtin global, nor we should have duplicated I/O semantics.

GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
addLocationDecoration(GV, Location);
return B.CreateLoad(Ty, GV);
}

llvm::Value *
CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
HLSLSemanticAttr *Semantic,
std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));

unsigned Location = SPIRVLastAssignedInputSemanticLocation;

// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Type);
unsigned ElementCount = AT ? AT->getNumElements() : 1;
SPIRVLastAssignedInputSemanticLocation += ElementCount;
return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location,
VariableName.str());
}

llvm::Value *
CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
HLSLSemanticAttr *Semantic,
std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));

// DXIL packing rules etc shall be handled here.
// FIXME: generate proper sigpoint, index, col, row values.
// FIXME: also DXIL loads vectors element by element.
SmallVector<Value *> Args{B.getInt32(4), B.getInt32(0), B.getInt32(0),
B.getInt8(0),
llvm::PoisonValue::get(B.getInt32Ty())};

llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_load_input;
llvm::Value *Value = B.CreateIntrinsic(/*ReturnType=*/Type, IntrinsicID, Args,
nullptr, VariableName);
return Value;
}

llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
HLSLSemanticAttr *Semantic, std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
return emitSPIRVUserSemanticLoad(B, Type, Semantic, Index);

if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticLoad(B, Type, Semantic, Index);

llvm_unreachable("Unsupported target for user-semantic load.");
}

llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
Attr *Semantic, std::optional<unsigned> Index) {
Expand Down Expand Up @@ -626,6 +699,9 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, const FunctionDecl *FD,
std::optional<unsigned> Index = std::nullopt;
if (Semantic->isSemanticIndexExplicit())
Index = Semantic->getSemanticIndex();

if (isa<HLSLUserSemanticAttr>(Semantic))
return emitUserSemanticLoad(B, Type, Decl, Semantic, Index);
return emitSystemSemanticLoad(B, Type, Decl, Semantic, Index);
}

Expand Down
16 changes: 16 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,25 @@ class CGHLSLRuntime {
llvm::GlobalVariable *BufGV);
void initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV);
void initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::GlobalVariable *GV,
HLSLResourceBindingAttr *RBA);

llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
HLSLSemanticAttr *Semantic,
std::optional<unsigned> Index);
llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
HLSLSemanticAttr *Semantic,
std::optional<unsigned> Index);
llvm::Value *emitUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
const clang::DeclaratorDecl *Decl,
HLSLSemanticAttr *Semantic,
std::optional<unsigned> Index);

llvm::Triple::ArchType getArch();

llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
unsigned SPIRVLastAssignedInputSemanticLocation = 0;
};

} // namespace CodeGen
Expand Down
48 changes: 39 additions & 9 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,10 @@ HLSLSemanticAttr *SemaHLSL::createSemantic(const SemanticInfo &Info,
DeclaratorDecl *TargetDecl) {
std::string SemanticName = Info.Semantic->getAttrName()->getName().upper();

if (dyn_cast<HLSLUserSemanticAttr>(Info.Semantic))
return createSemanticAttr<HLSLUserSemanticAttr>(*Info.Semantic, TargetDecl,
Info.Index);

if (SemanticName == "SV_DISPATCHTHREADID") {
return createSemanticAttr<HLSLSV_DispatchThreadIDAttr>(
*Info.Semantic, TargetDecl, Info.Index);
Expand All @@ -797,9 +801,10 @@ HLSLSemanticAttr *SemaHLSL::createSemantic(const SemanticInfo &Info,
return nullptr;
}

bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
DeclaratorDecl *D,
SemanticInfo &ActiveSemantic) {
bool SemaHLSL::determineActiveSemanticOnScalar(
FunctionDecl *FD, DeclaratorDecl *D, SemanticInfo &ActiveSemantic,
llvm::StringSet<> &ActiveInputSemantics) {

if (ActiveSemantic.Semantic == nullptr) {
ActiveSemantic.Semantic = D->getAttr<HLSLSemanticAttr>();
if (ActiveSemantic.Semantic &&
Expand All @@ -818,11 +823,31 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,

checkSemanticAnnotation(FD, D, A);
FD->addAttr(A);

unsigned Location = ActiveSemantic.Index.value_or(0);

const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
unsigned ElementCount = AT ? AT->getZExtSize() : 1;
ActiveSemantic.Index = Location + ElementCount;

Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
for (unsigned I = 0; I < ElementCount; ++I) {
Twine VariableName = BaseName.concat(Twine(Location + I));

auto [_, Inserted] = ActiveInputSemantics.insert(VariableName.str());
if (!Inserted) {
Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
<< VariableName.str();
return false;
}
}

return true;
}

bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *D,
SemanticInfo &ActiveSemantic) {
bool SemaHLSL::determineActiveSemantic(
FunctionDecl *FD, DeclaratorDecl *D, SemanticInfo &ActiveSemantic,
llvm::StringSet<> &ActiveInputSemantics) {
if (ActiveSemantic.Semantic == nullptr) {
ActiveSemantic.Semantic = D->getAttr<HLSLSemanticAttr>();
if (ActiveSemantic.Semantic &&
Expand All @@ -833,12 +858,13 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *D,
const Type *T = D->getType()->getUnqualifiedDesugaredType();
const RecordType *RT = dyn_cast<RecordType>(T);
if (!RT)
return determineActiveSemanticOnScalar(FD, D, ActiveSemantic);
return determineActiveSemanticOnScalar(FD, D, ActiveSemantic,
ActiveInputSemantics);

const RecordDecl *RD = RT->getDecl();
for (FieldDecl *Field : RD->fields()) {
SemanticInfo Info = ActiveSemantic;
if (!determineActiveSemantic(FD, Field, Info)) {
if (!determineActiveSemantic(FD, Field, Info, ActiveInputSemantics)) {
Diag(Field->getLocation(), diag::note_hlsl_semantic_used_here) << Field;
return false;
}
Expand Down Expand Up @@ -911,12 +937,14 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
llvm_unreachable("Unhandled environment in triple");
}

llvm::StringSet<> ActiveInputSemantics;
for (ParmVarDecl *Param : FD->parameters()) {
SemanticInfo ActiveSemantic;
ActiveSemantic.Semantic = nullptr;
ActiveSemantic.Index = std::nullopt;

if (!determineActiveSemantic(FD, Param, ActiveSemantic)) {
if (!determineActiveSemantic(FD, Param, ActiveSemantic,
ActiveInputSemantics)) {
Diag(Param->getLocation(), diag::note_previous_decl) << Param;
FD->setInvalidDecl();
}
Expand Down Expand Up @@ -947,6 +975,8 @@ void SemaHLSL::checkSemanticAnnotation(FunctionDecl *EntryPoint,
return;
DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Pixel});
break;
case attr::HLSLUserSemantic:
return;
default:
llvm_unreachable("Unknown SemanticAttr");
}
Expand Down Expand Up @@ -1766,7 +1796,7 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) {
if (AL.getAttrName()->getName().starts_with_insensitive("SV_"))
diagnoseSystemSemanticAttr(D, AL, Index);
else
Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
D->addAttr(createSemanticAttr<HLSLUserSemanticAttr>(AL, nullptr, Index));
}

void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {
Expand Down
1 change: 0 additions & 1 deletion clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,3 @@ void foo(uint Idx : SV_DispatchThreadID) {}
[shader("compute")]
[numthreads(8,8,1)]
void bar(uint2 Idx : SV_DispatchThreadID) {}

36 changes: 36 additions & 0 deletions clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %clang_cc1 -triple spirv-unknown-vulkan-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx

// CHECK-SPIRV-DAG: @AAA0 = external hidden thread_local addrspace(7) externally_initialized constant float, !spirv.Decorations ![[#METADATA_0:]]
// CHECK-SPIRV-DAG: @B0 = external hidden thread_local addrspace(7) externally_initialized constant i32, !spirv.Decorations ![[#METADATA_2:]]
// CHECK-SPIRV-DAG: @CC0 = external hidden thread_local addrspace(7) externally_initialized constant <2 x float>, !spirv.Decorations ![[#METADATA_4:]]


// FIXME: replace `float2 c` with a matrix when available.
void main(float a : AAA, int b : B, float2 c : CC) {
float tmp = a + b + c.x + c.y;
}
// CHECK-SPIRV: define internal spir_func void @_Z4mainfiDv2_f(float noundef nofpclass(nan inf) %a, i32 noundef %b, <2 x float> noundef nofpclass(nan inf) %c) #0 {

// CHECK: define void @main()

// CHECK-DXIL: %AAA0 = call float @llvm.dx.load.input.f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: %B0 = call i32 @llvm.dx.load.input.i32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL %CC0 = call <2 x float> @llvm.dx.load.input.v2f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: call void @_Z4mainfiDv2_f(float %AAA0, i32 %B0, <2 x float> %CC0)

// CHECK-SPIRV: %[[#AAA0:]] = load float, ptr addrspace(7) @AAA0, align 4
// CHECK-SPIRV: %[[#B0:]] = load i32, ptr addrspace(7) @B0, align 4
// CHECK-SPIRV: %[[#CC0:]] = load <2 x float>, ptr addrspace(7) @CC0, align 8
// CHECK-SPIRV: call spir_func void @_Z4mainfiDv2_f(float %[[#AAA0]], i32 %[[#B0]], <2 x float> %[[#CC0]]) [ "convergencectrl"(token %0) ]


// CHECK-SPIRV-DAG: ![[#METADATA_0]] = !{![[#METADATA_1:]]}
// CHECK-SPIRV-DAG: ![[#METADATA_2]] = !{![[#METADATA_3:]]}
// CHECK-SPIRV-DAG: ![[#METADATA_4]] = !{![[#METADATA_5:]]}

// CHECK-SPIRV-DAG: ![[#METADATA_1]] = !{i32 30, i32 0}
// CHECK-SPIRV-DAG: ![[#METADATA_3]] = !{i32 30, i32 1}
// CHECK-SPIRV-DAG: ![[#METADATA_5]] = !{i32 30, i32 2}
// | `- Location index
// `-> Decoration "Location"
37 changes: 37 additions & 0 deletions clang/test/CodeGenHLSL/semantics/semantic.array.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
// RUN: %clang_cc1 -triple dxil-px-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx

struct S0 {
float4 position[2];
float4 color;
};

// CHECK: %struct.S0 = type { [2 x <4 x float>], <4 x float> }

// CHECK-SPIRV: @A0 = external hidden thread_local addrspace(7) externally_initialized constant [2 x <4 x float>], !spirv.Decorations ![[#MD_0:]]
// CHECK-SPIRV: @A2 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_2:]]

// CHECK: define void @main0()
// CHECK-DXIL: %A0 = call [2 x <4 x float>] @llvm.dx.load.input.a2v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: %[[#TMP0:]] = insertvalue %struct.S0 poison, [2 x <4 x float>] %A0, 0
// CHECK-DXIL: %A2 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
// CHECK-DXIL: %[[#TMP1:]] = insertvalue %struct.S0 %[[#TMP0]], <4 x float> %A2, 1

// CHECK-SPIRV: %[[#A0:]] = load [2 x <4 x float>], ptr addrspace(7) @A0, align 16
// CHECK-SPIRV: %[[#TMP0:]] = insertvalue %struct.S0 poison, [2 x <4 x float>] %[[#A0]], 0
// CHECK-SPIRV: %[[#A2:]] = load <4 x float>, ptr addrspace(7) @A2, align 16
// CHECK-SPIRV: %[[#TMP1:]] = insertvalue %struct.S0 %[[#TMP0]], <4 x float> %[[#A2]], 1

// CHECK: %[[#ARG:]] = alloca %struct.S0, align 16
// CHECK: store %struct.S0 %[[#TMP1]], ptr %[[#ARG]], align 16
// CHECK-DXIL: call void @{{.*}}main0{{.*}}(ptr %[[#ARG]])
// CHECK-SPIRV: call spir_func void @{{.*}}main0{{.*}}(ptr %[[#ARG]])
[shader("pixel")]
void main0(S0 p : A) {
float tmp = p.position[0] + p.position[1] + p.color;
}

// CHECK-SPIRV: ![[#MD_0]] = !{![[#MD_1:]]}
// CHECK-SPIRV: ![[#MD_1]] = !{i32 30, i32 0}
// CHECK-SPIRV: ![[#MD_2]] = !{![[#MD_3:]]}
// CHECK-SPIRV: ![[#MD_3]] = !{i32 30, i32 2}
Loading