Path: blob/main/contrib/llvm-project/llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp
213799 views
//===- HLSLRootSignatureValidations.cpp - HLSL Root Signature helpers -----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file This file contains helpers for working with HLSL Root Signatures.9///10//===----------------------------------------------------------------------===//1112#include "llvm/Frontend/HLSL/RootSignatureValidations.h"1314#include <cmath>1516namespace llvm {17namespace hlsl {18namespace rootsig {1920bool verifyRootFlag(uint32_t Flags) { return (Flags & ~0xfff) == 0; }2122bool verifyVersion(uint32_t Version) { return (Version == 1 || Version == 2); }2324bool verifyRegisterValue(uint32_t RegisterValue) {25return RegisterValue != ~0U;26}2728// This Range is reserverved, therefore invalid, according to the spec29// https://github.com/llvm/wg-hlsl/blob/main/proposals/0002-root-signature-in-clang.md#all-the-values-should-be-legal30bool verifyRegisterSpace(uint32_t RegisterSpace) {31return !(RegisterSpace >= 0xFFFFFFF0 && RegisterSpace <= 0xFFFFFFFF);32}3334bool verifyRootDescriptorFlag(uint32_t Version, uint32_t FlagsVal) {35using FlagT = dxbc::RootDescriptorFlags;36FlagT Flags = FlagT(FlagsVal);37if (Version == 1)38return Flags == FlagT::DataVolatile;3940assert(Version == 2 && "Provided invalid root signature version");4142// The data-specific flags are mutually exclusive.43FlagT DataFlags = FlagT::DataVolatile | FlagT::DataStatic |44FlagT::DataStaticWhileSetAtExecute;4546if (popcount(llvm::to_underlying(Flags & DataFlags)) > 1)47return false;4849// Only a data flag or no flags is valid50return (Flags | DataFlags) == DataFlags;51}5253bool verifyRangeType(uint32_t Type) {54switch (Type) {55case llvm::to_underlying(dxbc::DescriptorRangeType::CBV):56case llvm::to_underlying(dxbc::DescriptorRangeType::SRV):57case llvm::to_underlying(dxbc::DescriptorRangeType::UAV):58case llvm::to_underlying(dxbc::DescriptorRangeType::Sampler):59return true;60};6162return false;63}6465bool verifyDescriptorRangeFlag(uint32_t Version, uint32_t Type,66uint32_t FlagsVal) {67using FlagT = dxbc::DescriptorRangeFlags;68FlagT Flags = FlagT(FlagsVal);6970const bool IsSampler =71(Type == llvm::to_underlying(dxbc::DescriptorRangeType::Sampler));7273if (Version == 1) {74// Since the metadata is unversioned, we expect to explicitly see the values75// that map to the version 1 behaviour here.76if (IsSampler)77return Flags == FlagT::DescriptorsVolatile;78return Flags == (FlagT::DataVolatile | FlagT::DescriptorsVolatile);79}8081// The data-specific flags are mutually exclusive.82FlagT DataFlags = FlagT::DataVolatile | FlagT::DataStatic |83FlagT::DataStaticWhileSetAtExecute;8485if (popcount(llvm::to_underlying(Flags & DataFlags)) > 1)86return false;8788// The descriptor-specific flags are mutually exclusive.89FlagT DescriptorFlags = FlagT::DescriptorsStaticKeepingBufferBoundsChecks |90FlagT::DescriptorsVolatile;91if (popcount(llvm::to_underlying(Flags & DescriptorFlags)) > 1)92return false;9394// For volatile descriptors, DATA_is never valid.95if ((Flags & FlagT::DescriptorsVolatile) == FlagT::DescriptorsVolatile) {96FlagT Mask = FlagT::DescriptorsVolatile;97if (!IsSampler) {98Mask |= FlagT::DataVolatile;99Mask |= FlagT::DataStaticWhileSetAtExecute;100}101return (Flags & ~Mask) == FlagT::None;102}103104// For "KEEPING_BUFFER_BOUNDS_CHECKS" descriptors,105// the other data-specific flags may all be set.106if ((Flags & FlagT::DescriptorsStaticKeepingBufferBoundsChecks) ==107FlagT::DescriptorsStaticKeepingBufferBoundsChecks) {108FlagT Mask = FlagT::DescriptorsStaticKeepingBufferBoundsChecks;109if (!IsSampler) {110Mask |= FlagT::DataVolatile;111Mask |= FlagT::DataStatic;112Mask |= FlagT::DataStaticWhileSetAtExecute;113}114return (Flags & ~Mask) == FlagT::None;115}116117// When no descriptor flag is set, any data flag is allowed.118FlagT Mask = FlagT::None;119if (!IsSampler) {120Mask |= FlagT::DataVolatile;121Mask |= FlagT::DataStaticWhileSetAtExecute;122Mask |= FlagT::DataStatic;123}124return (Flags & ~Mask) == FlagT::None;125}126127bool verifyNumDescriptors(uint32_t NumDescriptors) {128return NumDescriptors > 0;129}130131bool verifySamplerFilter(uint32_t Value) {132switch (Value) {133#define FILTER(Num, Val) case llvm::to_underlying(dxbc::SamplerFilter::Val):134#include "llvm/BinaryFormat/DXContainerConstants.def"135return true;136}137return false;138}139140// Values allowed here:141// https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_texture_address_mode#syntax142bool verifyAddress(uint32_t Address) {143switch (Address) {144#define TEXTURE_ADDRESS_MODE(Num, Val) \145case llvm::to_underlying(dxbc::TextureAddressMode::Val):146#include "llvm/BinaryFormat/DXContainerConstants.def"147return true;148}149return false;150}151152bool verifyMipLODBias(float MipLODBias) {153return MipLODBias >= -16.f && MipLODBias <= 15.99f;154}155156bool verifyMaxAnisotropy(uint32_t MaxAnisotropy) {157return MaxAnisotropy <= 16u;158}159160bool verifyComparisonFunc(uint32_t ComparisonFunc) {161switch (ComparisonFunc) {162#define COMPARISON_FUNC(Num, Val) \163case llvm::to_underlying(dxbc::ComparisonFunc::Val):164#include "llvm/BinaryFormat/DXContainerConstants.def"165return true;166}167return false;168}169170bool verifyBorderColor(uint32_t BorderColor) {171switch (BorderColor) {172#define STATIC_BORDER_COLOR(Num, Val) \173case llvm::to_underlying(dxbc::StaticBorderColor::Val):174#include "llvm/BinaryFormat/DXContainerConstants.def"175return true;176}177return false;178}179180bool verifyLOD(float LOD) { return !std::isnan(LOD); }181182std::optional<const RangeInfo *>183ResourceRange::getOverlapping(const RangeInfo &Info) const {184MapT::const_iterator Interval = Intervals.find(Info.LowerBound);185if (!Interval.valid() || Info.UpperBound < Interval.start())186return std::nullopt;187return Interval.value();188}189190const RangeInfo *ResourceRange::lookup(uint32_t X) const {191return Intervals.lookup(X, nullptr);192}193194void ResourceRange::clear() { return Intervals.clear(); }195196std::optional<const RangeInfo *> ResourceRange::insert(const RangeInfo &Info) {197uint32_t LowerBound = Info.LowerBound;198uint32_t UpperBound = Info.UpperBound;199200std::optional<const RangeInfo *> Res = std::nullopt;201MapT::iterator Interval = Intervals.begin();202203while (true) {204if (UpperBound < LowerBound)205break;206207Interval.advanceTo(LowerBound);208if (!Interval.valid()) // No interval found209break;210211// Let Interval = [x;y] and [LowerBound;UpperBound] = [a;b] and note that212// a <= y implicitly from Intervals.find(LowerBound)213if (UpperBound < Interval.start())214break; // found interval does not overlap with inserted one215216if (!Res.has_value()) // Update to be the first found intersection217Res = Interval.value();218219if (Interval.start() <= LowerBound && UpperBound <= Interval.stop()) {220// x <= a <= b <= y implies that [a;b] is covered by [x;y]221// -> so we don't need to insert this, report an overlap222return Res;223} else if (LowerBound <= Interval.start() &&224Interval.stop() <= UpperBound) {225// a <= x <= y <= b implies that [x;y] is covered by [a;b]226// -> so remove the existing interval that we will cover with the227// overwrite228Interval.erase();229} else if (LowerBound < Interval.start() && UpperBound <= Interval.stop()) {230// a < x <= b <= y implies that [a; x] is not covered but [x;b] is231// -> so set b = x - 1 such that [a;x-1] is now the interval to insert232UpperBound = Interval.start() - 1;233} else if (Interval.start() <= LowerBound && Interval.stop() < UpperBound) {234// a < x <= b <= y implies that [y; b] is not covered but [a;y] is235// -> so set a = y + 1 such that [y+1;b] is now the interval to insert236LowerBound = Interval.stop() + 1;237}238}239240assert(LowerBound <= UpperBound && "Attempting to insert an empty interval");241Intervals.insert(LowerBound, UpperBound, &Info);242return Res;243}244245llvm::SmallVector<OverlappingRanges>246findOverlappingRanges(ArrayRef<RangeInfo> Infos) {247// It is expected that Infos is filled with valid RangeInfos and that248// they are sorted with respect to the RangeInfo <operator249assert(llvm::is_sorted(Infos) && "Ranges must be sorted");250251llvm::SmallVector<OverlappingRanges> Overlaps;252using GroupT = std::pair<dxil::ResourceClass, /*Space*/ uint32_t>;253254// First we will init our state to track:255if (Infos.size() == 0)256return Overlaps; // No ranges to overlap257GroupT CurGroup = {Infos[0].Class, Infos[0].Space};258259// Create a ResourceRange for each Visibility260ResourceRange::MapT::Allocator Allocator;261std::array<ResourceRange, 8> Ranges = {262ResourceRange(Allocator), // All263ResourceRange(Allocator), // Vertex264ResourceRange(Allocator), // Hull265ResourceRange(Allocator), // Domain266ResourceRange(Allocator), // Geometry267ResourceRange(Allocator), // Pixel268ResourceRange(Allocator), // Amplification269ResourceRange(Allocator), // Mesh270};271272// Reset the ResourceRanges for when we iterate through a new group273auto ClearRanges = [&Ranges]() {274for (ResourceRange &Range : Ranges)275Range.clear();276};277278// Iterate through collected RangeInfos279for (const RangeInfo &Info : Infos) {280GroupT InfoGroup = {Info.Class, Info.Space};281// Reset our ResourceRanges when we enter a new group282if (CurGroup != InfoGroup) {283ClearRanges();284CurGroup = InfoGroup;285}286287// Insert range info into corresponding Visibility ResourceRange288ResourceRange &VisRange = Ranges[llvm::to_underlying(Info.Visibility)];289if (std::optional<const RangeInfo *> Overlapping = VisRange.insert(Info))290Overlaps.push_back(OverlappingRanges(&Info, Overlapping.value()));291292// Check for overlap in all overlapping Visibility ResourceRanges293//294// If the range that we are inserting has ShaderVisiblity::All it needs to295// check for an overlap in all other visibility types as well.296// Otherwise, the range that is inserted needs to check that it does not297// overlap with ShaderVisibility::All.298//299// OverlapRanges will be an ArrayRef to all non-all visibility300// ResourceRanges in the former case and it will be an ArrayRef to just the301// all visiblity ResourceRange in the latter case.302ArrayRef<ResourceRange> OverlapRanges =303Info.Visibility == llvm::dxbc::ShaderVisibility::All304? ArrayRef<ResourceRange>{Ranges}.drop_front()305: ArrayRef<ResourceRange>{Ranges}.take_front();306307for (const ResourceRange &Range : OverlapRanges)308if (std::optional<const RangeInfo *> Overlapping =309Range.getOverlapping(Info))310Overlaps.push_back(OverlappingRanges(&Info, Overlapping.value()));311}312313return Overlaps;314}315316} // namespace rootsig317} // namespace hlsl318} // namespace llvm319320321