[DirectX] Implement Shader Flags Analysis for ResMayNotAlias (#131070)

Fixes #112270

Completed ACs:
- `-res-may-alias` clang-dxc command-line option added
  - It inserts and sets a module metadata flag `dx.resmayalias` to 1
- Shader flag set appropriately:
- The flag IS NOT set if DXIL Version <= 1.6 OR the command-line option
`-res-may-alias` is specified
  - Otherwise the flag IS set when:
    - DXIL Version > 1.7 AND function uses UAVs, OR
    - DXIL Version <= 1.7 AND UAVs present globally
- Add tests 
- Tests for Shader Models 6.6, 6.7, and 6.8 corresponding to DXIL
Versions 1.6, 1.7, and 1.8
- Tests (`res-may-alias-0.ll`/`res-may-alias-1.ll`) for when the module
metadata flag `dx.resmayalias` is set to 0 or 1 respectively
- A frontend test (`res-may-alias.hlsl`) for testing that that the
command-line option `-res-may-alias` inserts `dx.resmayalias` module
metadata correctly
This commit is contained in:
Deric C.
2025-04-10 16:06:48 -07:00
committed by GitHub
parent b39ab7a620
commit 727f3921e7
13 changed files with 258 additions and 8 deletions

View File

@@ -476,6 +476,9 @@ CODEGENOPT(ImportCallOptimization, 1, 0)
/// (BlocksRuntime) on Windows.
CODEGENOPT(StaticClosure, 1, 0)
/// Assume that UAVs/SRVs may alias
CODEGENOPT(ResMayAlias, 1, 0)
/// FIXME: Make DebugOptions its own top-level .def file.
#include "DebugOptions.def"

View File

@@ -9131,6 +9131,11 @@ def dxil_validator_version : Option<["/", "-"], "validator-version", KIND_SEPARA
HelpText<"Override validator version for module. Format: <major.minor>;"
"Default: DXIL.dll version or current internal version">,
MarshallingInfoString<TargetOpts<"DxilValidatorVersion">, "\"1.8\"">;
def res_may_alias : Option<["/", "-"], "res-may-alias", KIND_FLAG>,
Group<dxc_Group>, Flags<[HelpHidden]>,
Visibility<[DXCOption, ClangOption, CC1Option]>,
HelpText<"Assume that UAVs/SRVs may alias">,
MarshallingInfoFlag<CodeGenOpts<"ResMayAlias">>;
def target_profile : DXCJoinedOrSeparate<"T">, MetaVarName<"<profile>">,
HelpText<"Set target profile">,
Values<"ps_6_0, ps_6_1, ps_6_2, ps_6_3, ps_6_4, ps_6_5, ps_6_6, ps_6_7,"

View File

@@ -282,11 +282,14 @@ void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType,
void CGHLSLRuntime::finishCodeGen() {
auto &TargetOpts = CGM.getTarget().getTargetOpts();
auto &CodeGenOpts = CGM.getCodeGenOpts();
auto &LangOpts = CGM.getLangOpts();
llvm::Module &M = CGM.getModule();
Triple T(M.getTargetTriple());
if (T.getArch() == Triple::ArchType::dxil)
addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
if (CodeGenOpts.ResMayAlias)
M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.resmayalias", 1);
// NativeHalfType corresponds to the -fnative-half-type clang option which is
// aliased by clang-dxc's -enable-16bit-types option. This option is used to

View File

@@ -3974,6 +3974,7 @@ static void RenderOpenCLOptions(const ArgList &Args, ArgStringList &CmdArgs,
static void RenderHLSLOptions(const ArgList &Args, ArgStringList &CmdArgs,
types::ID InputType) {
const unsigned ForwardedArguments[] = {options::OPT_dxil_validator_version,
options::OPT_res_may_alias,
options::OPT_D,
options::OPT_I,
options::OPT_O,

View File

@@ -0,0 +1,7 @@
// RUN: %clang_dxc -res-may-alias -T lib_6_3 -HV 202x -Vd -Xclang -emit-llvm %s | FileCheck %s --check-prefix=FLAG
// RUN: %clang_dxc -T lib_6_3 -HV 202x -Vd -Xclang -emit-llvm %s | FileCheck %s --check-prefix=NOFLAG
// FLAG-DAG: ![[RMA:.*]] = !{i32 1, !"dx.resmayalias", i32 1}
// FLAG-DAG: !llvm.module.flags = !{{{.*}}![[RMA]]{{.*}}}
// NOFLAG-NOT: dx.resmayalias

View File

@@ -74,7 +74,8 @@ static bool checkWaveOps(Intrinsic::ID IID) {
/// \param I Instruction to check.
void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,
const Instruction &I,
DXILResourceTypeMap &DRTM) {
DXILResourceTypeMap &DRTM,
const ModuleMetadataInfo &MMDI) {
if (!CSF.Doubles)
CSF.Doubles = I.getType()->isDoubleTy();
@@ -128,8 +129,15 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,
switch (II->getIntrinsicID()) {
default:
break;
case Intrinsic::dx_resource_handlefrombinding:
switch (DRTM[cast<TargetExtType>(II->getType())].getResourceKind()) {
case Intrinsic::dx_resource_handlefrombinding: {
dxil::ResourceTypeInfo &RTI = DRTM[cast<TargetExtType>(II->getType())];
// Set ResMayNotAlias if DXIL version >= 1.8 and function uses UAVs
if (!CSF.ResMayNotAlias && CanSetResMayNotAlias &&
MMDI.DXILVersion >= VersionTuple(1, 8) && RTI.isUAV())
CSF.ResMayNotAlias = true;
switch (RTI.getResourceKind()) {
case dxil::ResourceKind::StructuredBuffer:
case dxil::ResourceKind::RawBuffer:
CSF.EnableRawAndStructuredBuffers = true;
@@ -138,6 +146,7 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,
break;
}
break;
}
case Intrinsic::dx_resource_load_typedbuffer: {
dxil::ResourceTypeInfo &RTI =
DRTM[cast<TargetExtType>(II->getArgOperand(0)->getType())];
@@ -163,7 +172,18 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,
/// Construct ModuleShaderFlags for module Module M
void ModuleShaderFlags::initialize(Module &M, DXILResourceTypeMap &DRTM,
DXILResourceMap &DRM,
const ModuleMetadataInfo &MMDI) {
CanSetResMayNotAlias = MMDI.DXILVersion >= VersionTuple(1, 7);
// Check if -res-may-alias was provided on the command line.
// The command line option will set the dx.resmayalias module flag to 1.
if (auto *RMA = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("dx.resmayalias")))
if (RMA->getValue() != 0)
CanSetResMayNotAlias = false;
CallGraph CG(M);
// Compute Shader Flags Mask for all functions using post-order visit of SCC
@@ -188,6 +208,11 @@ void ModuleShaderFlags::initialize(Module &M, DXILResourceTypeMap &DRTM,
continue;
}
// Set ResMayNotAlias to true if DXIL version < 1.8 and there are UAVs
// present globally.
if (CanSetResMayNotAlias && MMDI.DXILVersion < VersionTuple(1, 8))
SCCSF.ResMayNotAlias = !DRM.uavs().empty();
// Set UseNativeLowPrecision using dx.nativelowprec module metadata
if (auto *NativeLowPrec = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("dx.nativelowprec")))
@@ -198,7 +223,7 @@ void ModuleShaderFlags::initialize(Module &M, DXILResourceTypeMap &DRTM,
ComputedShaderFlags CSF;
for (const auto &BB : *F)
for (const auto &I : BB)
updateFunctionFlags(CSF, I, DRTM);
updateFunctionFlags(CSF, I, DRTM, MMDI);
// Update combined shader flags mask for all functions in this SCC
SCCSF.merge(CSF);
@@ -268,10 +293,11 @@ AnalysisKey ShaderFlagsAnalysis::Key;
ModuleShaderFlags ShaderFlagsAnalysis::run(Module &M,
ModuleAnalysisManager &AM) {
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);
const ModuleMetadataInfo MMDI = AM.getResult<DXILMetadataAnalysis>(M);
ModuleShaderFlags MSFI;
MSFI.initialize(M, DRTM, MMDI);
MSFI.initialize(M, DRTM, DRM, MMDI);
return MSFI;
}
@@ -301,16 +327,18 @@ PreservedAnalyses ShaderFlagsAnalysisPrinter::run(Module &M,
bool ShaderFlagsAnalysisWrapper::runOnModule(Module &M) {
DXILResourceTypeMap &DRTM =
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
DXILResourceMap &DRM = getAnalysis<DXILResourceWrapperPass>().getBindingMap();
const ModuleMetadataInfo MMDI =
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
MSFI.initialize(M, DRTM, MMDI);
MSFI.initialize(M, DRTM, DRM, MMDI);
return false;
}
void ShaderFlagsAnalysisWrapper::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
AU.addRequiredTransitive<DXILResourceWrapperPass>();
AU.addRequired<DXILMetadataAnalysisWrapperPass>();
}

View File

@@ -28,6 +28,7 @@ namespace llvm {
class Module;
class GlobalVariable;
class DXILResourceTypeMap;
class DXILResourceMap;
namespace dxil {
@@ -84,12 +85,13 @@ struct ComputedShaderFlags {
};
struct ModuleShaderFlags {
void initialize(Module &, DXILResourceTypeMap &DRTM,
void initialize(Module &, DXILResourceTypeMap &DRTM, DXILResourceMap &DRM,
const ModuleMetadataInfo &MMDI);
const ComputedShaderFlags &getFunctionFlags(const Function *) const;
const ComputedShaderFlags &getCombinedFlags() const { return CombinedSFMask; }
private:
bool CanSetResMayNotAlias;
/// Map of Function-Shader Flag Mask pairs representing properties of each of
/// the functions in the module. Shader Flags of each function represent both
/// module-level and function-level flags
@@ -97,7 +99,7 @@ private:
/// Combined Shader Flag Mask of all functions of the module
ComputedShaderFlags CombinedSFMask{};
void updateFunctionFlags(ComputedShaderFlags &, const Instruction &,
DXILResourceTypeMap &);
DXILResourceTypeMap &, const ModuleMetadataInfo &);
};
class ShaderFlagsAnalysis : public AnalysisInfoMixin<ShaderFlagsAnalysis> {

View File

@@ -0,0 +1,43 @@
; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
; This test checks to ensure that setting the LLVM module flag "dx.resmayalias"
; to 0 has no effect on the DXIL shader flag analysis for the flag
; ResMayNotAlias.
target triple = "dxil-pc-shadermodel6.8-library"
; CHECK: Combined Shader Flags for Module
; CHECK-NEXT: Shader Flags Value: 0x200000010
; CHECK: Note: extra DXIL module flags:
; CHECK: Raw and Structured buffers
; CHECK: Any UAV may not alias any other UAV
;
; CHECK: Function loadUAV : 0x20000000
define float @loadUAV() #0 {
%res = call target("dx.TypedBuffer", float, 1, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.typedbuffer(
target("dx.TypedBuffer", float, 1, 0, 0) %res, i32 0)
%val = extractvalue {float, i1} %load, 0
ret float %val
}
; CHECK: Function loadSRV : 0x00000010
define float @loadSRV() #0 {
%res = tail call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.rawbuffer(
target("dx.RawBuffer", float, 0, 0) %res, i32 0, i32 0)
%val = extractvalue { float, i1 } %load, 0
ret float %val
}
!llvm.module.flags = !{!0}
; dx.resmayalias should never appear with a value of 0.
; But if it does, ensure that it has no effect.
!0 = !{i32 1, !"dx.resmayalias", i32 0}
attributes #0 = { convergent norecurse nounwind "hlsl.export"}

View File

@@ -0,0 +1,41 @@
; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
; This test checks to ensure that setting the LLVM module flag "dx.resmayalias"
; to 1 prevents the DXIL shader flag analysis from setting the flag
; ResMayNotAlias.
target triple = "dxil-pc-shadermodel6.8-library"
; CHECK: Combined Shader Flags for Module
; CHECK-NEXT: Shader Flags Value: 0x00000010
; CHECK: Note: extra DXIL module flags:
; CHECK: Raw and Structured buffers
; CHECK-NOT: Any UAV may not alias any other UAV
;
; CHECK: Function loadUAV : 0x00000000
define float @loadUAV() #0 {
%res = call target("dx.TypedBuffer", float, 1, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.typedbuffer(
target("dx.TypedBuffer", float, 1, 0, 0) %res, i32 0)
%val = extractvalue {float, i1} %load, 0
ret float %val
}
; CHECK: Function loadSRV : 0x00000010
define float @loadSRV() #0 {
%res = tail call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.rawbuffer(
target("dx.RawBuffer", float, 0, 0) %res, i32 0, i32 0)
%val = extractvalue { float, i1 } %load, 0
ret float %val
}
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"dx.resmayalias", i32 1}
attributes #0 = { convergent norecurse nounwind "hlsl.export"}

View File

@@ -0,0 +1,37 @@
; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
; This test checks to ensure the behavior of the DXIL shader flag analysis
; for the flag ResMayNotAlias is correct when the DXIL Version is 1.6. The
; ResMayNotAlias flag (0x20000000) should not be set at all.
target triple = "dxil-pc-shadermodel6.6-library"
; CHECK: Combined Shader Flags for Module
; CHECK-NEXT: Shader Flags Value: 0x00000010
; CHECK: Note: extra DXIL module flags:
; CHECK: Raw and Structured buffers
; CHECK-NOT: Any UAV may not alias any other UAV
;
; CHECK: Function loadUAV : 0x00000000
define float @loadUAV() #0 {
%res = call target("dx.TypedBuffer", float, 1, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.typedbuffer(
target("dx.TypedBuffer", float, 1, 0, 0) %res, i32 0)
%val = extractvalue {float, i1} %load, 0
ret float %val
}
; CHECK: Function loadSRV : 0x00000010
define float @loadSRV() #0 {
%res = tail call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.rawbuffer(
target("dx.RawBuffer", float, 0, 0) %res, i32 0, i32 0)
%val = extractvalue { float, i1 } %load, 0
ret float %val
}
attributes #0 = { convergent norecurse nounwind "hlsl.export"}

View File

@@ -0,0 +1,38 @@
; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
; This test checks to ensure the behavior of the DXIL shader flag analysis
; for the flag ResMayNotAlias is correct when the DXIL Version is 1.7. The
; ResMayNotAlias flag (0x20000000) should be set on all functions if there are
; one or more UAVs present globally in the module.
target triple = "dxil-pc-shadermodel6.7-library"
; CHECK: Combined Shader Flags for Module
; CHECK-NEXT: Shader Flags Value: 0x200000010
; CHECK: Note: extra DXIL module flags:
; CHECK: Raw and Structured buffers
; CHECK: Any UAV may not alias any other UAV
;
; CHECK: Function loadUAV : 0x200000000
define float @loadUAV() #0 {
%res = call target("dx.TypedBuffer", float, 1, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.typedbuffer(
target("dx.TypedBuffer", float, 1, 0, 0) %res, i32 0)
%val = extractvalue {float, i1} %load, 0
ret float %val
}
; CHECK: Function loadSRV : 0x200000010
define float @loadSRV() #0 {
%res = tail call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.rawbuffer(
target("dx.RawBuffer", float, 0, 0) %res, i32 0, i32 0)
%val = extractvalue { float, i1 } %load, 0
ret float %val
}
attributes #0 = { convergent norecurse nounwind "hlsl.export"}

View File

@@ -0,0 +1,38 @@
; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
; This test checks to ensure the behavior of the DXIL shader flag analysis
; for the flag ResMayNotAlias is correct when the DXIL Version is 1.8. The
; ResMayNotAlias flag (0x20000000) should only be set when a function uses a
; UAV.
target triple = "dxil-pc-shadermodel6.8-library"
; CHECK: Combined Shader Flags for Module
; CHECK-NEXT: Shader Flags Value: 0x200000010
; CHECK: Note: extra DXIL module flags:
; CHECK: Raw and Structured buffers
; CHECK: Any UAV may not alias any other UAV
;
; CHECK: Function loadUAV : 0x20000000
define float @loadUAV() #0 {
%res = call target("dx.TypedBuffer", float, 1, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.typedbuffer(
target("dx.TypedBuffer", float, 1, 0, 0) %res, i32 0)
%val = extractvalue {float, i1} %load, 0
ret float %val
}
; CHECK: Function loadSRV : 0x00000010
define float @loadSRV() #0 {
%res = tail call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
%load = call {float, i1} @llvm.dx.resource.load.rawbuffer(
target("dx.RawBuffer", float, 0, 0) %res, i32 0, i32 0)
%val = extractvalue { float, i1 } %load, 0
ret float %val
}
attributes #0 = { convergent norecurse nounwind "hlsl.export"}

View File

@@ -43,4 +43,8 @@ define void @noload(<4 x float> %val) #0 {
ret void
}
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"dx.resmayalias", i32 1}
attributes #0 = { convergent norecurse nounwind "hlsl.export"}