Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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: 2 additions & 1 deletion lib/HLSL/HLLegalizeParameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ bool HLLegalizeParameter::runOnModule(Module &M) {
continue;

for (Argument &Arg : F.args()) {
if (!Arg.getType()->isPointerTy())
Type *PtrTy = dyn_cast<PointerType>(Arg.getType());
if (!PtrTy || 0 != PtrTy->getPointerAddressSpace())
continue;
Type *EltTy = dxilutil::GetArrayEltTy(Arg.getType());
if (dxilutil::IsHLSLObjectType(EltTy) ||
Expand Down
8 changes: 8 additions & 0 deletions tools/clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
/// Whether the parameter is copied out.
unsigned IsModifierOut : 1;

/// Whether the parameter should be treated as a reference.
unsigned IsModifierRef : 1;

/// The number of parameters preceding this parameter in the
/// function parameter scope in which it was declared.
unsigned ParameterIndex : NumParameterIndexBits;
Expand Down Expand Up @@ -1377,6 +1380,7 @@ class ParmVarDecl : public VarDecl {
assert(ParmVarDeclBits.IsObjCMethodParam == false);
setDefaultArg(DefArg);
// HLSL Change Start
setModifierRef(ParamMod.isRef());
setModifierIn(ParamMod.isAnyIn());
setModifierOut(ParamMod.isAnyOut());
// change to reference type for out param
Expand Down Expand Up @@ -1456,8 +1460,12 @@ class ParmVarDecl : public VarDecl {
void setModifierIn(bool value) { ParmVarDeclBits.IsKNRPromoted = !value; }
bool isModifierOut() const { return ParmVarDeclBits.IsModifierOut; }
void setModifierOut(bool value) { ParmVarDeclBits.IsModifierOut = value; }
bool isModifierRef() const { return ParmVarDeclBits.IsModifierRef; }
void setModifierRef(bool value) { ParmVarDeclBits.IsModifierRef = value; }
/// Synthesize a ParameterModifier value for this parameter.
hlsl::ParameterModifier getParamModifiers() const {
if (isModifierRef())
return hlsl::ParameterModifier(hlsl::ParameterModifier::Kind::Ref);
if (isModifierIn() && !isModifierOut())
return hlsl::ParameterModifier(hlsl::ParameterModifier::Kind::In);
if (isModifierIn() && isModifierOut())
Expand Down
7 changes: 7 additions & 0 deletions tools/clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,13 @@ def warn_hlsl_sometimes_uninit_out_param : Warning<
"its declaration is reached|"
"%3 is called}2">,
InGroup<HLSLParameterUsage>;
def warn_hlsl_groupshared_202x: Warning<
"Support for groupshared parameter annotation not added until HLSL 202x">,
InGroup<HLSLParameterUsage>;
def warn_hlsl_groupshared_inout: Warning<
"Passing groupshared variable to a parameter annotated with inout. See"
"'groupshared' parameter annotation added in 202x.">,
InGroup<HLSLParameterUsage>;
// HLSL Change End - Add warning for uninitialized out param
def warn_maybe_uninit_var : Warning<
"variable %0 may be uninitialized when "
Expand Down
1 change: 1 addition & 0 deletions tools/clang/include/clang/Basic/Specifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ namespace hlsl {

unsigned getAsUnsigned() const { return (unsigned)m_Kind; }

bool isRef() const { return m_Kind == Kind::Ref; }
bool isIn() const { return m_Kind == Kind::In; }
bool isOut() const { return m_Kind == Kind::Out; }
bool isInOut() const { return m_Kind == Kind::InOut; }
Expand Down
14 changes: 14 additions & 0 deletions tools/clang/lib/AST/HlslTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ bool HasHLSLReorderCoherent(clang::QualType type) {
return false;
}

/// Checks whether the pAttributes indicate a parameter is groupshared
bool IsParamAttributedAsGroupShared(clang::AttributeList *pAttributes);

/// Checks whether the pAttributes indicate a parameter is inout or out; if
/// inout, pIsIn will be set to true.
bool IsParamAttributedAsOut(clang::AttributeList *pAttributes, bool *pIsIn);
Expand Down Expand Up @@ -934,6 +937,15 @@ unsigned GetHLSLOutputPatchCount(QualType type) {
return argList[1].getAsIntegral().getLimitedValue();
}

bool IsParamAttributedAsGroupShared(clang::AttributeList *pAttributes) {
while (pAttributes != nullptr) {
if (pAttributes->getKind() == AttributeList::AT_HLSLGroupShared)
return true;
pAttributes = pAttributes->getNext();
}
return false;
}

bool IsParamAttributedAsOut(clang::AttributeList *pAttributes, bool *pIsIn) {
bool anyFound = false;
bool inFound = false;
Expand Down Expand Up @@ -967,6 +979,8 @@ bool IsParamAttributedAsOut(clang::AttributeList *pAttributes, bool *pIsIn) {

hlsl::ParameterModifier
ParamModFromAttributeList(clang::AttributeList *pAttributes) {
if (IsParamAttributedAsGroupShared(pAttributes))
return ParameterModifier(hlsl::ParameterModifier::Kind::Ref);
bool isIn, isOut;
isOut = IsParamAttributedAsOut(pAttributes, &isIn);
return ParameterModifier::FromInOut(isIn, isOut);
Expand Down
2 changes: 2 additions & 0 deletions tools/clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2033,6 +2033,8 @@ void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T,
Qualifiers Quals, SourceRange Range) {
QualType PointeeType = T->getPointeeType();
Out << (Quals.hasVolatile() ? 'B' : 'A');
if (PointeeType.getQualifiers().getAddressSpace() == 3)
Out << 'G';
manglePointerExtQualifiers(Quals, PointeeType);
mangleType(PointeeType, Range);
}
Expand Down
6 changes: 5 additions & 1 deletion tools/clang/lib/CodeGen/CGHLSLMS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,9 @@ unsigned CGMSHLSLRuntime::AddTypeAnnotation(QualType Ty,
if (const ReferenceType *RefType = dyn_cast<ReferenceType>(paramTy))
paramTy = RefType->getPointeeType();

if (const ReferenceType *RefType = dyn_cast<ReferenceType>(Ty))
Ty = RefType->getPointeeType();

// Get size.
llvm::Type *Type = CGM.getTypes().ConvertType(paramTy);
unsigned size = dataLayout.getTypeAllocSize(Type);
Expand Down Expand Up @@ -6232,7 +6235,8 @@ void CGMSHLSLRuntime::EmitHLSLOutParamConversionInit(
}
} else if (isAggregateType) {
// aggregate in-only - emit RValue, unless LValueToRValue cast
EmitRValueAgg = true;
if (Param->isModifierIn())
EmitRValueAgg = true;
if (const ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(Arg)) {
if (cast->getCastKind() == CastKind::CK_LValueToRValue) {
EmitRValueAgg = false;
Expand Down
6 changes: 6 additions & 0 deletions tools/clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4945,6 +4945,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
handleSimpleAttribute<NoDuplicateAttr>(S, D, Attr);
break;
case AttributeList::AT_NoInline:
if (S.LangOpts.HLSL) {
bool Handled = false;
hlsl::HandleDeclAttributeForHLSL(S, D, Attr, Handled);
if (Handled)
break;
}
handleSimpleAttribute<NoInlineAttr>(S, D, Attr);
break;
case AttributeList::AT_NoInstrumentFunction: // Interacts with -pg.
Expand Down
32 changes: 31 additions & 1 deletion tools/clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14474,6 +14474,22 @@ void Sema::DiagnoseHLSLDeclAttr(const Decl *D, const Attr *A) {
HLSLExternalSource *ExtSource = HLSLExternalSource::FromSema(this);
const bool IsGCAttr = isa<HLSLGloballyCoherentAttr>(A);
const bool IsRCAttr = isa<HLSLReorderCoherentAttr>(A);
const bool IsExportAttr = isa<HLSLExportAttr>(A);
const bool IsNoInlineAttr = isa<NoInlineAttr>(A);
if (IsExportAttr || IsNoInlineAttr) {
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
for (ParmVarDecl *PVD : FD->parameters()) {
if (PVD->hasAttr<HLSLGroupSharedAttr>()) {
Diag(A->getLocation(), diag::err_hlsl_varmodifiersna)
<< "groupshared"
<< "export/noinline"
<< "parameter";
return;
}
}
}
return;
}
if (IsGCAttr || IsRCAttr) {
const ValueDecl *TD = cast<ValueDecl>(D);
if (TD->getType()->isDependentType())
Expand Down Expand Up @@ -14655,6 +14671,8 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
VD->setType(
S.Context.getAddrSpaceQualType(VD->getType(), DXIL::kTGSMAddrSpace));
}
if (ParmVarDecl *VD = dyn_cast<ParmVarDecl>(D))
VD->setType(S.Context.getLValueReferenceType(VD->getType()));
break;
case AttributeList::AT_HLSLUniform:
declAttr = ::new (S.Context) HLSLUniformAttr(
Expand Down Expand Up @@ -14996,6 +15014,7 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
}

if (declAttr != nullptr) {
S.DiagnoseHLSLDeclAttr(D, declAttr);
DXASSERT_NOMSG(Handled);
D->addAttr(declAttr);

Expand Down Expand Up @@ -15749,7 +15768,14 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
break;
case AttributeList::AT_HLSLGroupShared:
isGroupShared = true;
if (!isGlobal) {
if (isParameter && getLangOpts().HLSLVersion < hlsl::LangStd::v202x)
Diag(pAttr->getLoc(), diag::warn_hlsl_groupshared_202x);
if (isParameter && (usageIn || usageOut)) {
Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
<< pAttr->getName() << "in/out/inout modifiers" << declarationType;
result = false;
}
if (!(isGlobal || isParameter)) {
Diag(pAttr->getLoc(), diag::err_hlsl_varmodifierna)
<< pAttr->getName() << declarationType << pAttr->getRange();
result = false;
Expand Down Expand Up @@ -15786,6 +15812,10 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
Diag(pAttr->getLoc(), diag::err_hlsl_usage_not_on_parameter)
<< pAttr->getName() << pAttr->getRange();
result = false;
} else if (isGroupShared) {
Copy link

Choose a reason for hiding this comment

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

Is it guaranteed that groupshared will be processed/specified before inout?

Otherwise, we might need to handle the opposite case above

Copy link
Collaborator Author

@spall spall Jan 16, 2026

Choose a reason for hiding this comment

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

Looking at the cases for inout and for groupshared I think it is handled regardless of order, but good point and I think this points to a test is missing for this.

Diag(pAttr->getLoc(), diag::err_hlsl_varmodifiersna)
<< pAttr->getName() << "groupshared" << declarationType;
result = false;
}
if (!IsUsageAttributeCompatible(pAttr->getKind(), usageIn, usageOut)) {
Diag(pAttr->getLoc(), diag::err_hlsl_duplicate_parameter_usages)
Expand Down
20 changes: 20 additions & 0 deletions tools/clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4953,6 +4953,26 @@ InitCallParamConversions(Sema &S, const FunctionProtoType *Proto,
ImplicitConversionSequence &OutConversion) {
hlsl::ParameterModifier paramMods = Proto->getParamMods()[ArgIdx];
QualType ParamType = Proto->getParamType(ArgIdx);

// must be a Ref; don't allow any conversions
if (!(paramMods.isAnyIn() || paramMods.isAnyOut())) {
if (!S.getASTContext().hasSameUnqualifiedType(
ParamType.getNonReferenceType(), Arg->getType()) ||
Arg->getType().getQualifiers().getAddressSpace() !=
hlsl::DXIL::kTGSMAddrSpace) {
InConversion.setBad(BadConversionSequence::no_conversion, Arg->getType(),
ParamType.getNonReferenceType());
InConversion.Bad.FromExpr = Arg;
return;
}
}

if (S.getLangOpts().HLSLVersion >= hlsl::LangStd::v202x &&
paramMods.isAnyIn() && paramMods.isAnyOut() &&
Arg->getType().getQualifiers().getAddressSpace() ==
hlsl::DXIL::kTGSMAddrSpace)
S.Diag(Arg->getLocStart(), diag::warn_hlsl_groupshared_inout);

if (paramMods.isAnyIn()) {
InConversion =
TryCopyInitialization(S, Arg, ParamType, SuppressUserConversions,
Expand Down
16 changes: 16 additions & 0 deletions tools/clang/test/CodeGenHLSL/groupsharedArgs/ArrTest.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: %dxc -E main -T cs_6_0 -HV 202x -fcgl %s | FileCheck %s

groupshared float4 SharedArr[64];

// CHECK-LABEL: define internal void @"\01?fn@@YAXAGAY0EA@$$CAV?$vector@M$03@@M@Z"([64 x <4 x float>] addrspace(3)* dereferenceable(1024) %Arr, float %F)
// CHECK: [[ArrIdx:%.*]] = getelementptr inbounds [64 x <4 x float>], [64 x <4 x float>] addrspace(3)* %Arr, i32 0, i32 5
// CHECK-NEXT: store <4 x float> {{.*}}, <4 x float> addrspace(3)* [[ArrIdx]], align 4
void fn(groupshared float4 Arr[64], float F) {
float4 tmp = F.xxxx;
Arr[5] = tmp;
}

[numthreads(4,1,1)]
void main() {
fn(SharedArr, 6.0);
}
18 changes: 18 additions & 0 deletions tools/clang/test/CodeGenHLSL/groupsharedArgs/MatrixTest.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %dxc -E main -T cs_6_3 -HV 202x -fcgl %s | FileCheck %s

groupshared float4x4 SharedData;

// CHECK-LABEL: @"\01?fn1@@YAXAGAV?$matrix@M$03$03@@@Z"
// CHECK: [[M1:%.*]] = addrspacecast %class.matrix.float.4.4 addrspace(3)* %Sh to %class.matrix.float.4.4*
// CHECK: [[A:%.*]] = call <4 x float>* @"dx.hl.subscript.colMajor[].rn.<4 x float>* (i32, %class.matrix.float.4.4*, i32, i32, i32, i32)"(i32 1, %class.matrix.float.4.4* [[M1]], i32 0, i32 4, i32 8, i32 12)
// CHECK: [[B:%.*]] = getelementptr <4 x float>, <4 x float>* [[A]], i32 0, i32 1
// CHECK: store float 5.000000e+00, float* [[B]]
// CHECK: ret void
void fn1(groupshared float4x4 Sh) {
Sh[0][1] = 5.0;
}

[numthreads(4,1,1)]
void main(uint3 TID : SV_GroupThreadID) {
fn1(SharedData);
}
37 changes: 37 additions & 0 deletions tools/clang/test/CodeGenHLSL/groupsharedArgs/Overloads.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %dxc -E main -T cs_6_0 -HV 202x -fcgl %s | FileCheck %s

// Verify we are calling the correct overloads
void fn(groupshared float4 Arr[2]);
void fn(inout float4 Arr[2]);

void fn2(groupshared int4 Shared);
void fn2(int4 Local);

// CHECK-LABEL: define void @main()
[numthreads(4,1,1)]
void main() {
float4 Local[2] = {1.0.xxxx, 2.0.xxxx};
// CHECK-DAG: call void @"\01?fn@@YAXY01$$CAV?$vector@M$03@@@Z"([2 x <4 x float>]* %Local)
fn(Local);

// CHECK-DAG: call void @"\01?fn2@@YAXV?$vector@H$03@@@Z"(<4 x i32>
fn2(11.xxxx);
}

void fn(groupshared float4 Arr[2]) {
Arr[1] = 7.0.xxxx;
}

// CHECK-LABEL: define internal void @"\01?fn@@YAXY01$$CAV?$vector@M$03@@@Z"([2 x <4 x float>]* noalias %Arr)
void fn(inout float4 Arr[2]) {
Arr[1] = 5.0.xxxx;
}

void fn2(groupshared int4 Shared) {
Shared.x = 10;
}

// CHECK-DAG: define internal void @"\01?fn2@@YAXV?$vector@H$03@@@Z"(<4 x i32> %Local)
void fn2(int4 Local) {
int X = Local.y;
}
16 changes: 16 additions & 0 deletions tools/clang/test/CodeGenHLSL/groupsharedArgs/ScalarTest.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: %dxc -E main -T cs_6_2 -enable-16bit-types -HV 202x -fcgl %s | FileCheck %s

groupshared uint16_t SharedData;

// Make sure the mangling changes for groupshared parameter annotation is reflected in the function signature (the first G)
// The mangled function signature for void fn(uint16_t Sh) should be @"\01?fn1@@YAXAAG@Z" (without the extra G)
// CHECK-LABEL: @"\01?fn1@@YAXAGAG@Z"
// CHECK: store i16 5, i16 addrspace(3)* %Sh, align 4
void fn1(groupshared uint16_t Sh) {
Sh = 5;
}

[numthreads(4, 1, 1)]
void main(uint3 TID : SV_GroupThreadID) {
fn1(SharedData);
}
32 changes: 32 additions & 0 deletions tools/clang/test/CodeGenHLSL/groupsharedArgs/StructTest.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %dxc -E main -T cs_6_3 -HV 202x -fcgl %s | FileCheck %s

struct Shared {
int A;
float F;
double Arr[4];
};

groupshared Shared SharedData;

// CHECK-LABEL: @"\01?fn1@@YAXAGAUShared@@@Z"
// CHECK: [[D:%.*]] = alloca double, align 8
// CHECK: [[A:%.*]] = getelementptr inbounds %struct.Shared, %struct.Shared addrspace(3)* %Sh, i32 0, i32 0
// CHECK: store i32 10, i32 addrspace(3)* [[A]], align 4
// CHECK: [[F:%.*]] = getelementptr inbounds %struct.Shared, %struct.Shared addrspace(3)* %Sh, i32 0, i32 1
// CHECK: store float 0x40263851E0000000, float addrspace(3)* %F, align 4
// CHECK: store double 1.000000e+01, double* [[D]], align 8
// CHECK: [[Z:%.*]] = load double, double* [[D]], align 8
// CHECK: [[Arr:%.*]] = getelementptr inbounds %struct.Shared, %struct.Shared addrspace(3)* %Sh, i32 0, i32 2
// CHECK: [[ArrIdx:%.*]] = getelementptr inbounds [4 x double], [4 x double] addrspace(3)* [[Arr]], i32 0, i32 1
// CHECK: store double [[Z]], double addrspace(3)* [[ArrIdx]], align 4
void fn1(groupshared Shared Sh) {
Sh.A = 10;
Sh.F = 11.11;
double D = 10.0;
Sh.Arr[1] = D;
}

[numthreads(4, 1, 1)]
void main(uint3 TID : SV_GroupThreadID) {
fn1(SharedData);
}
32 changes: 32 additions & 0 deletions tools/clang/test/CodeGenHLSL/groupsharedArgs/TemplateTest.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %dxc -E main -T cs_6_3 -HV 202x -fcgl %s | FileCheck %s

template<typename T> struct Shared {
T A;
float F;
double Arr[4];
};

groupshared Shared<int> SharedData;

// CHECK-LABEL: @"\01?fn1@@YAXAGAU?$Shared@H@@@Z"
// CHECK: [[D:%.*]] = alloca double, align 8
// CHECK: [[A:%.*]] = getelementptr inbounds %"struct.Shared<int>", %"struct.Shared<int>" addrspace(3)* %Sh, i32 0, i32 0
// CHECK: store i32 10, i32 addrspace(3)* [[A]], align 4
// CHECK: [[F:%.*]] = getelementptr inbounds %"struct.Shared<int>", %"struct.Shared<int>" addrspace(3)* %Sh, i32 0, i32 1
// CHECK: store float 0x40263851E0000000, float addrspace(3)* %F, align 4
// CHECK: store double 1.000000e+01, double* [[D]], align 8
// CHECK: [[Z:%.*]] = load double, double* [[D]], align 8
// CHECK: [[Arr:%.*]] = getelementptr inbounds %"struct.Shared<int>", %"struct.Shared<int>" addrspace(3)* %Sh, i32 0, i32 2
// CHECK: [[ArrIdx:%.*]] = getelementptr inbounds [4 x double], [4 x double] addrspace(3)* [[Arr]], i32 0, i32 1
// CHECK: store double [[Z]], double addrspace(3)* [[ArrIdx]], align 4
void fn1(groupshared Shared<int> Sh) {
Sh.A = 10;
Sh.F = 11.11;
double D = 10.0;
Sh.Arr[1] = D;
}

[numthreads(4, 1, 1)]
void main(uint3 TID : SV_GroupThreadID) {
fn1(SharedData);
}
Loading