From 69e54e29086b7035acffb304cec57a350225f8b0 Mon Sep 17 00:00:00 2001 From: Greg Roth Date: Mon, 14 Aug 2023 11:29:54 -0600 Subject: [PATCH] Fix attribute collision for HL intrinsics (#5451) (#5543) HL Intrinsic functions share declarations with those that match group and function signature, regardless of the original intrinsic name. This means that intrinsics with differing attributes can be collapsed into the same HL functions, leading to incorrect attributes for some HL intrinsics. This fixes this issue by adding the attributes to the HL operation mangling, the same way this issue was fixed for the HLWaveSensitive attribute before. Fixes #3505 --------- Co-authored-by: Joshua Batista (cherry picked from commit d9c07e90e1a7fa2654d4b52e6f5704b986549c15) Co-authored-by: Tex Riddell --- lib/HLSL/HLMatrixLowerPass.cpp | 11 +- lib/HLSL/HLOperations.cpp | 185 ++++++++++++++---- .../hlsl/intrinsics/helper/IsHelperLane.hlsl | 38 ++-- .../hlsl/objects/Buffer/buf_index.hlsl | 2 +- .../RayQuery/intrinsicAttributeCollision.hlsl | 39 ++++ .../readnone_intrinsicAttributeCollision.hlsl | 41 ++++ .../wave_intrinsicAttributeCollision.hlsl | 46 +++++ .../hlsl/template/ByteAddressBufferLoad.hlsl | 4 +- 8 files changed, 299 insertions(+), 67 deletions(-) create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/intrinsicAttributeCollision.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/readnone_intrinsicAttributeCollision.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/wave_intrinsicAttributeCollision.hlsl diff --git a/lib/HLSL/HLMatrixLowerPass.cpp b/lib/HLSL/HLMatrixLowerPass.cpp index a6422da240..e35ff832ec 100644 --- a/lib/HLSL/HLMatrixLowerPass.cpp +++ b/lib/HLSL/HLMatrixLowerPass.cpp @@ -1517,10 +1517,15 @@ void HLMatrixLowerPass::lowerHLMatSubscript(CallInst *Call, Value *MatPtr, Small HLMatLoadStoreOpcode Opcode = (HLSubscriptOpcode)GetHLOpcode(Call) == HLSubscriptOpcode::RowMatSubscript ? HLMatLoadStoreOpcode::RowMatLoad : HLMatLoadStoreOpcode::ColMatLoad; HLMatrixType MatTy = HLMatrixType::cast(MatPtr->getType()->getPointerElementType()); + // Don't pass attributes from subscript (ReadNone) - load is ReadOnly. + // Attributes will be set when HL function is created. + // FIXME: This seems to indicate a potential bug, since the load should be + // placed where pointer users would have loaded from the pointer. LoweredMatrix = callHLFunction( - *m_pModule, HLOpcodeGroup::HLMatLoadStore, static_cast(Opcode), - MatTy.getLoweredVectorTypeForReg(), { CallBuilder.getInt32((uint32_t)Opcode), MatPtr }, - Call->getCalledFunction()->getAttributes().getFnAttributes(), CallBuilder); + *m_pModule, HLOpcodeGroup::HLMatLoadStore, + static_cast(Opcode), MatTy.getLoweredVectorTypeForReg(), + {CallBuilder.getInt32((uint32_t)Opcode), MatPtr}, AttributeSet(), + CallBuilder); } // For global variables, we can GEP directly into the lowered vector pointer. // This is necessary to support group shared memory atomics and the likes. diff --git a/lib/HLSL/HLOperations.cpp b/lib/HLSL/HLOperations.cpp index 96bc9e0da4..bdfd9361ee 100644 --- a/lib/HLSL/HLOperations.cpp +++ b/lib/HLSL/HLOperations.cpp @@ -305,7 +305,10 @@ bool IsHLWaveSensitive(Function *F) { return attrSet.hasAttribute(AttributeSet::FunctionIndex, HLWaveSensitive); } -std::string GetHLFullName(HLOpcodeGroup op, unsigned opcode) { +static std::string GetHLFunctionAttributeMangling(const AttributeSet &attribs); + +std::string GetHLFullName(HLOpcodeGroup op, unsigned opcode, + const AttributeSet &attribs = AttributeSet()) { assert(op != HLOpcodeGroup::HLExtIntrinsic && "else table name should be used"); std::string opName = GetHLOpcodeGroupFullName(op).str() + "."; @@ -321,7 +324,7 @@ std::string GetHLFullName(HLOpcodeGroup op, unsigned opcode) { case HLOpcodeGroup::HLIntrinsic: { // intrinsic with same signature will share the funciton now // The opcode is in arg0. - return opName; + return opName + GetHLFunctionAttributeMangling(attribs); } case HLOpcodeGroup::HLMatLoadStore: { HLMatLoadStoreOpcode matOp = static_cast(opcode); @@ -329,14 +332,18 @@ std::string GetHLFullName(HLOpcodeGroup op, unsigned opcode) { } case HLOpcodeGroup::HLSubscript: { HLSubscriptOpcode subOp = static_cast(opcode); - return opName + GetHLOpcodeName(subOp).str(); + return opName + GetHLOpcodeName(subOp).str() + "." + + GetHLFunctionAttributeMangling(attribs); } case HLOpcodeGroup::HLCast: { HLCastOpcode castOp = static_cast(opcode); return opName + GetHLOpcodeName(castOp).str(); } - default: + case HLOpcodeGroup::HLCreateHandle: + case HLOpcodeGroup::HLAnnotateHandle: return opName; + default: + return opName + GetHLFunctionAttributeMangling(attribs); } } @@ -417,38 +424,59 @@ HLBinaryOpcode GetUnsignedOpcode(HLBinaryOpcode opcode) { } } -static void SetHLFunctionAttribute(Function *F, HLOpcodeGroup group, - unsigned opcode) { - F->addFnAttr(Attribute::NoUnwind); +static AttributeSet +GetHLFunctionAttributes(LLVMContext &C, FunctionType *funcTy, + const AttributeSet &origAttribs, + HLOpcodeGroup group, unsigned opcode) { + // Always add nounwind + AttributeSet attribs = + AttributeSet::get(C, AttributeSet::FunctionIndex, + ArrayRef({Attribute::NoUnwind})); + + auto addAttr = [&](Attribute::AttrKind Attr) { + if (!attribs.hasAttribute(AttributeSet::FunctionIndex, Attr)) + attribs = attribs.addAttribute(C, AttributeSet::FunctionIndex, Attr); + }; + auto copyAttr = [&](Attribute::AttrKind Attr) { + if (origAttribs.hasAttribute(AttributeSet::FunctionIndex, Attr)) + addAttr(Attr); + }; + auto copyStrAttr = [&](StringRef Kind) { + if (origAttribs.hasAttribute(AttributeSet::FunctionIndex, Kind)) + attribs = attribs.addAttribute( + C, AttributeSet::FunctionIndex, Kind, + origAttribs.getAttribute(AttributeSet::FunctionIndex, Kind) + .getValueAsString()); + }; + + // Copy attributes we preserve from the original function. + copyAttr(Attribute::ReadOnly); + copyAttr(Attribute::ReadNone); + copyStrAttr(HLWaveSensitive); switch (group) { case HLOpcodeGroup::HLUnOp: case HLOpcodeGroup::HLBinOp: case HLOpcodeGroup::HLCast: case HLOpcodeGroup::HLSubscript: - if (!F->hasFnAttribute(Attribute::ReadNone)) { - F->addFnAttr(Attribute::ReadNone); - } + addAttr(Attribute::ReadNone); break; case HLOpcodeGroup::HLInit: - if (!F->hasFnAttribute(Attribute::ReadNone)) - if (!F->getReturnType()->isVoidTy()) { - F->addFnAttr(Attribute::ReadNone); - } + if (!funcTy->getReturnType()->isVoidTy()) { + addAttr(Attribute::ReadNone); + } break; case HLOpcodeGroup::HLMatLoadStore: { HLMatLoadStoreOpcode matOp = static_cast(opcode); if (matOp == HLMatLoadStoreOpcode::ColMatLoad || matOp == HLMatLoadStoreOpcode::RowMatLoad) - if (!F->hasFnAttribute(Attribute::ReadOnly)) { - F->addFnAttr(Attribute::ReadOnly); - } + addAttr(Attribute::ReadOnly); } break; case HLOpcodeGroup::HLCreateHandle: { - F->addFnAttr(Attribute::ReadNone); + addAttr(Attribute::ReadNone); } break; case HLOpcodeGroup::HLAnnotateHandle: { - F->addFnAttr(Attribute::ReadNone); + addAttr(Attribute::ReadNone); } break; case HLOpcodeGroup::HLIntrinsic: { IntrinsicOp intrinsicOp = static_cast(opcode); @@ -461,7 +489,7 @@ static void SetHLFunctionAttribute(Function *F, HLOpcodeGroup group, case IntrinsicOp::IOP_GroupMemoryBarrier: case IntrinsicOp::IOP_AllMemoryBarrierWithGroupSync: case IntrinsicOp::IOP_AllMemoryBarrier: - F->addFnAttr(Attribute::NoDuplicate); + addAttr(Attribute::NoDuplicate); break; } } break; @@ -472,6 +500,75 @@ static void SetHLFunctionAttribute(Function *F, HLOpcodeGroup group, // No default attributes for these opcodes. break; } + assert(!(attribs.hasAttribute(AttributeSet::FunctionIndex, + Attribute::ReadNone) && + attribs.hasAttribute(AttributeSet::FunctionIndex, + Attribute::ReadOnly)) && + "conflicting ReadNone and ReadOnly attributes"); + return attribs; +} + +static std::string GetHLFunctionAttributeMangling(const AttributeSet &attribs) { + std::string mangledName; + raw_string_ostream mangledNameStr(mangledName); + + // Capture for adding in canonical order later. + bool ReadNone = false; + bool ReadOnly = false; + bool NoDuplicate = false; + bool WaveSensitive = false; + + // Ensure every function attribute is recognized. + for (unsigned Slot = 0; Slot < attribs.getNumSlots(); Slot++) { + if (attribs.getSlotIndex(Slot) == AttributeSet::FunctionIndex) { + for (auto it = attribs.begin(Slot), e = attribs.end(Slot); it != e; + it++) { + if (it->isEnumAttribute()) { + switch (it->getKindAsEnum()) { + case Attribute::ReadNone: + ReadNone = true; + break; + case Attribute::ReadOnly: + ReadOnly = true; + break; + case Attribute::NoDuplicate: + NoDuplicate = true; + break; + case Attribute::NoUnwind: + // All intrinsics have this attribute, so mangling is unaffected. + break; + default: + assert(false && "unexpected attribute for HLOperation"); + } + } else if (it->isStringAttribute()) { + StringRef Kind = it->getKindAsString(); + if (Kind == HLWaveSensitive) { + assert(it->getValueAsString() == "y" && + "otherwise, unexpected value for WaveSensitive attribute"); + WaveSensitive = true; + } else { + assert(false && + "unexpected string function attribute for HLOperation"); + } + } + } + } + } + + // Validate attribute combinations. + assert(!(ReadNone && ReadOnly) && + "ReadNone and ReadOnly are mutually exclusive"); + + // Add mangling in canonical order + if (NoDuplicate) + mangledNameStr << "nd"; + if (ReadNone) + mangledNameStr << "rn"; + if (ReadOnly) + mangledNameStr << "ro"; + if (WaveSensitive) + mangledNameStr << "wave"; + return mangledName; } @@ -497,7 +594,11 @@ Function *GetOrCreateHLFunction(Module &M, FunctionType *funcTy, Function *GetOrCreateHLFunction(Module &M, FunctionType *funcTy, HLOpcodeGroup group, StringRef *groupName, StringRef *fnName, unsigned opcode, - const AttributeSet &attribs) { + const AttributeSet &origAttribs) { + // Set/transfer all common attributes + AttributeSet attribs = GetHLFunctionAttributes( + M.getContext(), funcTy, origAttribs, group, opcode); + std::string mangledName; raw_string_ostream mangledNameStr(mangledName); if (group == HLOpcodeGroup::HLExtIntrinsic) { @@ -506,33 +607,31 @@ Function *GetOrCreateHLFunction(Module &M, FunctionType *funcTy, mangledNameStr << *groupName; mangledNameStr << '.'; mangledNameStr << *fnName; + attribs = attribs.addAttribute(M.getContext(), AttributeSet::FunctionIndex, + hlsl::HLPrefix, *groupName); } else { - mangledNameStr << GetHLFullName(group, opcode); - // Need to add wave sensitivity to name to prevent clashes with non-wave intrinsic - if(attribs.hasAttribute(AttributeSet::FunctionIndex, HLWaveSensitive)) - mangledNameStr << "wave"; + mangledNameStr << GetHLFullName(group, opcode, attribs); mangledNameStr << '.'; funcTy->print(mangledNameStr); } mangledNameStr.flush(); - Function *F = cast(M.getOrInsertFunction(mangledName, funcTy)); - if (group == HLOpcodeGroup::HLExtIntrinsic) { - F->addFnAttr(hlsl::HLPrefix, *groupName); + // Avoid getOrInsertFunction to verify attributes and type without casting. + Function *F = cast_or_null(M.getNamedValue(mangledName)); + if (F) { + assert(F->getFunctionType() == funcTy && + "otherwise, function type mismatch not captured by mangling"); + // Compare attribute mangling to ensure function attributes are as expected. + assert( + GetHLFunctionAttributeMangling(F->getAttributes().getFnAttributes()) == + GetHLFunctionAttributeMangling(attribs) && + "otherwise, function attribute mismatch not captured by mangling"); + } else { + F = cast(M.getOrInsertFunction(mangledName, funcTy, attribs)); } - SetHLFunctionAttribute(F, group, opcode); - - // Copy attributes - if (attribs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadNone)) - F->addFnAttr(Attribute::ReadNone); - if (attribs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly)) - F->addFnAttr(Attribute::ReadOnly); - if (attribs.hasAttribute(AttributeSet::FunctionIndex, HLWaveSensitive)) - F->addFnAttr(HLWaveSensitive, "y"); - return F; } @@ -541,15 +640,17 @@ Function *GetOrCreateHLFunction(Module &M, FunctionType *funcTy, Function *GetOrCreateHLFunctionWithBody(Module &M, FunctionType *funcTy, HLOpcodeGroup group, unsigned opcode, StringRef name) { - std::string operatorName = GetHLFullName(group, opcode); + // Set/transfer all common attributes + AttributeSet attribs = GetHLFunctionAttributes( + M.getContext(), funcTy, AttributeSet(), group, opcode); + + std::string operatorName = GetHLFullName(group, opcode, attribs); std::string mangledName = operatorName + "." + name.str(); raw_string_ostream mangledNameStr(mangledName); funcTy->print(mangledNameStr); mangledNameStr.flush(); - Function *F = cast(M.getOrInsertFunction(mangledName, funcTy)); - - SetHLFunctionAttribute(F, group, opcode); + Function *F = cast(M.getOrInsertFunction(mangledName, funcTy, attribs)); F->setLinkage(llvm::GlobalValue::LinkageTypes::InternalLinkage); diff --git a/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/helper/IsHelperLane.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/helper/IsHelperLane.hlsl index 0a7a5b5450..6a73706f66 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/helper/IsHelperLane.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/helper/IsHelperLane.hlsl @@ -70,25 +70,25 @@ // One HL call from each function // 18 functions for HL lib due to entry cloning -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id:.*]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) -// CHECKHLLIB-NOT: call i1 @"dx.hl.op..i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id:.*]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) +// CHECKHLLIB-NOT: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 [[id]]) // CHECKGV: %[[cov:.*]] = call i32 @dx.op.coverage.i32(i32 91) ; Coverage() diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl index 08b419b289..555bbdcdbc 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl @@ -41,7 +41,7 @@ float4 main(uint i:I) : SV_Target { // FCGL-SAME: %dx.types.ResourceProperties { i32 {{[0-9]+}}, i32 {{[0-9]+}} }, // FCGL-SAME: %"class.Buffer >" undef) -// FCGL: {{%.+}} = call <4 x float>* @"dx.hl.subscript.[].<4 x float>* (i32, %dx.types.Handle, i32)" +// FCGL: {{%.+}} = call <4 x float>* @"dx.hl.subscript.[].rn.<4 x float>* (i32, %dx.types.Handle, i32)" // FCGL-SAME: (i32 0, %dx.types.Handle [[AnnHandle]], i32 2) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/intrinsicAttributeCollision.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/intrinsicAttributeCollision.hlsl new file mode 100644 index 0000000000..a8d77d03ff --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/intrinsicAttributeCollision.hlsl @@ -0,0 +1,39 @@ +// RUN: %dxc -T cs_6_5 -E CS -fcgl %s | FileCheck %s +// RUN: %dxc -T cs_6_5 -E CS %s | FileCheck %s -check-prefix=CHECKDXIL + +// Proceed called before CommittedTriangleFrontFace. +// Don't be sensitive to HL Opcode because those can change. +// CHECK: call i1 [[HLProceed:@"[^"]+"]](i32 +// CHECK: call i1 [[HLCommittedTriangleFrontFace:@"[^".]+\.[^.]+\.[^.]+\.ro[^"]+"]](i32 +// ^ matches call i1 @"dx.hl.op.ro.i1 (i32, %\22class.RayQuery<5>\22*)"(i32 +// CHECK-LABEL: ret void, + +// Ensure HL declarations are not collapsed when attributes differ +// CHECK-DAG: declare i1 [[HLProceed]]({{.*}}) #[[AttrProceed:[0-9]+]] +// CHECK-DAG: declare i1 [[HLCommittedTriangleFrontFace]]({{.*}}) #[[AttrCommittedTriangleFrontFace:[0-9]+]] + +// Ensure correct attributes for each HL intrinsic +// CHECK-DAG: attributes #[[AttrProceed]] = { nounwind } +// CHECK-DAG: attributes #[[AttrCommittedTriangleFrontFace]] = { nounwind readonly } + +// Ensure Proceed not eliminated in final DXIL: +// CHECKDXIL: call i1 @dx.op.rayQuery_Proceed.i1(i32 180, +// CHECKDXIL: call i1 @dx.op.rayQuery_StateScalar.i1(i32 192, + +RaytracingAccelerationStructure AccelerationStructure : register(t0); +RWByteAddressBuffer log : register(u0); + +[numThreads(1,1,1)] +void CS() +{ + RayQuery q; + RayDesc ray = { {0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0.0f, 9999.0f}; + q.TraceRayInline(AccelerationStructure, RAY_FLAG_NONE, 0xFF, ray); + + q.Proceed(); + + if(q.CommittedTriangleFrontFace()) + { + log.Store(0,1); + } +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/readnone_intrinsicAttributeCollision.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/readnone_intrinsicAttributeCollision.hlsl new file mode 100644 index 0000000000..afeab05452 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/readnone_intrinsicAttributeCollision.hlsl @@ -0,0 +1,41 @@ +// RUN: %dxc -T ps_6_7 -E PS -fcgl %s | FileCheck %s +// RUN: %dxc -T ps_6_7 -E PS %s | FileCheck %s -check-prefix=CHECKDXIL + +// IncrementCounter called before GetRenderTargetSampleCount. +// Don't be sensitive to HL Opcode because those can change. +// make sure that we are detecting the .rn attribute in the call +// CHECK: call float [[EvaluateAttributeCentroid:@"[^".]+\.[^.]+\.[^.]+\.rn[^"]+"]](i32 +// ^ matches call i32 @"dx.hl.op.rn.i32 (i32)"(i32 19), !dbg !45 ; line:33 col:14 +// CHECK: call float [[QuadReadAcrossX:@"[^"]+"]](i32 + +// Ensure HL declarations are not collapsed when attributes differ +// CHECK-DAG: declare float [[EvaluateAttributeCentroid]]({{.*}}) #[[AttrEvauluateAttributeCentroid:[0-9]+]] +// CHECK-DAG: declare float [[QuadReadAcrossX]]({{.*}}) #[[AttrQuadReadAcrossX:[0-9]+]] + +// Ensure correct attributes for each HL intrinsic +// CHECK-DAG: attributes #[[AttrEvauluateAttributeCentroid]] = { nounwind readnone } +// CHECK-DAG: attributes #[[AttrQuadReadAcrossX]] = { nounwind } + +// Ensure EvaluateAttributeCentroid not eliminated in final DXIL: +// CHECKDXIL: call float @dx.op.evalCentroid.f32( +// CHECKDXIL: call float @dx.op.quadOp.f32( + +StructuredBuffer buf[]: register(t3); +RWStructuredBuffer uav; + +// test read-none attr +int PS(float a : A, int b : B) : SV_Target +{ + int res = 0; + + for (;;) { + float x = EvaluateAttributeCentroid(a); + x += QuadReadAcrossX(x); + + if (a != x) { + res += buf[(int)x][b]; + break; + } + } + return res; +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/wave_intrinsicAttributeCollision.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/wave_intrinsicAttributeCollision.hlsl new file mode 100644 index 0000000000..84c6a26955 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/RayQuery/wave_intrinsicAttributeCollision.hlsl @@ -0,0 +1,46 @@ +// RUN: %dxc -T ps_6_5 -E PS -fcgl %s | FileCheck %s +// RUN: %dxc -T ps_6_5 -E PS %s | FileCheck %s -check-prefix=CHECKDXIL + +// QuadAny called before WaveActiveAnyTrue. +// Don't be sensitive to HL Opcode because those can change. +// CHECK: call i1 [[QuadAny:@"[^"]+"]](i32 +// CHECK: call i1 [[WaveActiveAnyTrue:@"[^".]+\.[^.]+\.[^.]+\.wave[^"][^"]+"]](i32 +// ^ matches call i1 @"dx.hl.op.wave.i1 (i32, i1)"(i32 + +// Ensure HL declarations are not collapsed when attributes differ +// CHECK-DAG: declare i1 [[QuadAny]]({{.*}}) #[[AttrQuadAny:[0-9]+]] +// CHECK-DAG: declare i1 [[WaveActiveAnyTrue]]({{.*}}) #[[AttrWaveActiveAnyTrue:[0-9]+]] + +// Ensure correct attributes for each HL intrinsic +// CHECK-DAG: attributes #[[AttrQuadAny]] = { nounwind } +// CHECK-DAG: attributes #[[AttrWaveActiveAnyTrue]] = { nounwind "dx.wave-sensitive"="y" } + +// Ensure WaveActiveAnyTrue not eliminated in final DXIL: +// CHECKDXIL: call i32 @dx.op.quadOp.i32( +// CHECKDXIL-NEXT: call i32 @dx.op.quadOp.i32( +// CHECKDXIL-NEXT: call i32 @dx.op.quadOp.i32( +// CHECKDXIL: call i1 @dx.op.waveAnyTrue( + +RaytracingAccelerationStructure AccelerationStructure : register(t0); +RWByteAddressBuffer log : register(u0); + +StructuredBuffer buf[]: register(t3); +RWStructuredBuffer uav; + +// test wave attr +int PS(int a : A, int b : B) : SV_Target +{ + int res = 0; + + for (;;) { + bool u = QuadAny(a); + u = WaveActiveAnyTrue(a); + if (a != u) { + res += buf[(int)u][b]; + break; + } + } + return res; +} + + diff --git a/tools/clang/test/HLSLFileCheck/hlsl/template/ByteAddressBufferLoad.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/template/ByteAddressBufferLoad.hlsl index d560b565cb..c86c4981f8 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/template/ByteAddressBufferLoad.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/template/ByteAddressBufferLoad.hlsl @@ -25,11 +25,11 @@ RWBuffer result; [numthreads(1,1,1)] void CSMain() { - // CHECK: call %"struct.MyStructA"* @"dx.hl.op..%\22struct.MyStructA\22* (i32, %dx.types.Handle, i32)"(i32 {{[0-9]+}}, %dx.types.Handle %{{[0-9]+}}, i32 0) + // CHECK: call %"struct.MyStructA"* @"dx.hl.op.ro.%\22struct.MyStructA\22* (i32, %dx.types.Handle, i32)"(i32 {{[0-9]+}}, %dx.types.Handle %{{[0-9]+}}, i32 0) MyStructA a = g_bab.Load >(0); result[0] = a.m_0; - // CHECK: call %struct.MyStructB* @"dx.hl.op..%struct.MyStructB* (i32, %dx.types.Handle, i32)"(i32 {{[0-9]+}}, %dx.types.Handle %{{[0-9]+}}, i32 1) + // CHECK: call %struct.MyStructB* @"dx.hl.op.ro.%struct.MyStructB* (i32, %dx.types.Handle, i32)"(i32 {{[0-9]+}}, %dx.types.Handle %{{[0-9]+}}, i32 1) MyStructB b = g_bab.Load(1); result[1] = b.m_a.m_0; }