cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
1281 lines
42 KiB
C++
1281 lines
42 KiB
C++
//===- DXILResource.cpp - Representations of DXIL resources ---------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/DXILResource.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/IntrinsicsDirectX.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include <cstdint>
|
|
#include <optional>
|
|
|
|
#define DEBUG_TYPE "dxil-resource"
|
|
|
|
using namespace llvm;
|
|
using namespace dxil;
|
|
|
|
static StringRef getResourceClassName(ResourceClass RC) {
|
|
switch (RC) {
|
|
case ResourceClass::SRV:
|
|
return "SRV";
|
|
case ResourceClass::UAV:
|
|
return "UAV";
|
|
case ResourceClass::CBuffer:
|
|
return "CBuffer";
|
|
case ResourceClass::Sampler:
|
|
return "Sampler";
|
|
}
|
|
llvm_unreachable("Unhandled ResourceClass");
|
|
}
|
|
|
|
static StringRef getResourceKindName(ResourceKind RK) {
|
|
switch (RK) {
|
|
case ResourceKind::Texture1D:
|
|
return "Texture1D";
|
|
case ResourceKind::Texture2D:
|
|
return "Texture2D";
|
|
case ResourceKind::Texture2DMS:
|
|
return "Texture2DMS";
|
|
case ResourceKind::Texture3D:
|
|
return "Texture3D";
|
|
case ResourceKind::TextureCube:
|
|
return "TextureCube";
|
|
case ResourceKind::Texture1DArray:
|
|
return "Texture1DArray";
|
|
case ResourceKind::Texture2DArray:
|
|
return "Texture2DArray";
|
|
case ResourceKind::Texture2DMSArray:
|
|
return "Texture2DMSArray";
|
|
case ResourceKind::TextureCubeArray:
|
|
return "TextureCubeArray";
|
|
case ResourceKind::TypedBuffer:
|
|
return "Buffer";
|
|
case ResourceKind::RawBuffer:
|
|
return "RawBuffer";
|
|
case ResourceKind::StructuredBuffer:
|
|
return "StructuredBuffer";
|
|
case ResourceKind::CBuffer:
|
|
return "CBuffer";
|
|
case ResourceKind::Sampler:
|
|
return "Sampler";
|
|
case ResourceKind::TBuffer:
|
|
return "TBuffer";
|
|
case ResourceKind::RTAccelerationStructure:
|
|
return "RTAccelerationStructure";
|
|
case ResourceKind::FeedbackTexture2D:
|
|
return "FeedbackTexture2D";
|
|
case ResourceKind::FeedbackTexture2DArray:
|
|
return "FeedbackTexture2DArray";
|
|
case ResourceKind::NumEntries:
|
|
case ResourceKind::Invalid:
|
|
return "<invalid>";
|
|
}
|
|
llvm_unreachable("Unhandled ResourceKind");
|
|
}
|
|
|
|
static StringRef getElementTypeName(ElementType ET) {
|
|
switch (ET) {
|
|
case ElementType::I1:
|
|
return "i1";
|
|
case ElementType::I16:
|
|
return "i16";
|
|
case ElementType::U16:
|
|
return "u16";
|
|
case ElementType::I32:
|
|
return "i32";
|
|
case ElementType::U32:
|
|
return "u32";
|
|
case ElementType::I64:
|
|
return "i64";
|
|
case ElementType::U64:
|
|
return "u64";
|
|
case ElementType::F16:
|
|
return "f16";
|
|
case ElementType::F32:
|
|
return "f32";
|
|
case ElementType::F64:
|
|
return "f64";
|
|
case ElementType::SNormF16:
|
|
return "snorm_f16";
|
|
case ElementType::UNormF16:
|
|
return "unorm_f16";
|
|
case ElementType::SNormF32:
|
|
return "snorm_f32";
|
|
case ElementType::UNormF32:
|
|
return "unorm_f32";
|
|
case ElementType::SNormF64:
|
|
return "snorm_f64";
|
|
case ElementType::UNormF64:
|
|
return "unorm_f64";
|
|
case ElementType::PackedS8x32:
|
|
return "p32i8";
|
|
case ElementType::PackedU8x32:
|
|
return "p32u8";
|
|
case ElementType::Invalid:
|
|
return "<invalid>";
|
|
}
|
|
llvm_unreachable("Unhandled ElementType");
|
|
}
|
|
|
|
static StringRef getElementTypeNameForTemplate(ElementType ET) {
|
|
switch (ET) {
|
|
case ElementType::I1:
|
|
return "bool";
|
|
case ElementType::I16:
|
|
return "int16_t";
|
|
case ElementType::U16:
|
|
return "uint16_t";
|
|
case ElementType::I32:
|
|
return "int32_t";
|
|
case ElementType::U32:
|
|
return "uint32_t";
|
|
case ElementType::I64:
|
|
return "int64_t";
|
|
case ElementType::U64:
|
|
return "uint32_t";
|
|
case ElementType::F16:
|
|
case ElementType::SNormF16:
|
|
case ElementType::UNormF16:
|
|
return "half";
|
|
case ElementType::F32:
|
|
case ElementType::SNormF32:
|
|
case ElementType::UNormF32:
|
|
return "float";
|
|
case ElementType::F64:
|
|
case ElementType::SNormF64:
|
|
case ElementType::UNormF64:
|
|
return "double";
|
|
case ElementType::PackedS8x32:
|
|
return "int8_t4_packed";
|
|
case ElementType::PackedU8x32:
|
|
return "uint8_t4_packed";
|
|
case ElementType::Invalid:
|
|
return "<invalid>";
|
|
}
|
|
llvm_unreachable("Unhandled ElementType");
|
|
}
|
|
|
|
static StringRef getSamplerTypeName(SamplerType ST) {
|
|
switch (ST) {
|
|
case SamplerType::Default:
|
|
return "Default";
|
|
case SamplerType::Comparison:
|
|
return "Comparison";
|
|
case SamplerType::Mono:
|
|
return "Mono";
|
|
}
|
|
llvm_unreachable("Unhandled SamplerType");
|
|
}
|
|
|
|
static StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) {
|
|
switch (SFT) {
|
|
case SamplerFeedbackType::MinMip:
|
|
return "MinMip";
|
|
case SamplerFeedbackType::MipRegionUsed:
|
|
return "MipRegionUsed";
|
|
}
|
|
llvm_unreachable("Unhandled SamplerFeedbackType");
|
|
}
|
|
|
|
static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) {
|
|
// TODO: Handle unorm, snorm, and packed.
|
|
Ty = Ty->getScalarType();
|
|
|
|
if (Ty->isIntegerTy()) {
|
|
switch (Ty->getIntegerBitWidth()) {
|
|
case 16:
|
|
return IsSigned ? ElementType::I16 : ElementType::U16;
|
|
case 32:
|
|
return IsSigned ? ElementType::I32 : ElementType::U32;
|
|
case 64:
|
|
return IsSigned ? ElementType::I64 : ElementType::U64;
|
|
case 1:
|
|
default:
|
|
return ElementType::Invalid;
|
|
}
|
|
} else if (Ty->isFloatTy()) {
|
|
return ElementType::F32;
|
|
} else if (Ty->isDoubleTy()) {
|
|
return ElementType::F64;
|
|
} else if (Ty->isHalfTy()) {
|
|
return ElementType::F16;
|
|
}
|
|
|
|
return ElementType::Invalid;
|
|
}
|
|
|
|
ResourceTypeInfo::ResourceTypeInfo(TargetExtType *HandleTy,
|
|
const dxil::ResourceClass RC_,
|
|
const dxil::ResourceKind Kind_)
|
|
: HandleTy(HandleTy) {
|
|
// If we're provided a resource class and kind, trust them.
|
|
if (Kind_ != dxil::ResourceKind::Invalid) {
|
|
RC = RC_;
|
|
Kind = Kind_;
|
|
return;
|
|
}
|
|
|
|
if (auto *Ty = dyn_cast<RawBufferExtType>(HandleTy)) {
|
|
RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
|
|
Kind = Ty->isStructured() ? ResourceKind::StructuredBuffer
|
|
: ResourceKind::RawBuffer;
|
|
} else if (auto *Ty = dyn_cast<TypedBufferExtType>(HandleTy)) {
|
|
RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
|
|
Kind = ResourceKind::TypedBuffer;
|
|
} else if (auto *Ty = dyn_cast<TextureExtType>(HandleTy)) {
|
|
RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
|
|
Kind = Ty->getDimension();
|
|
} else if (auto *Ty = dyn_cast<MSTextureExtType>(HandleTy)) {
|
|
RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
|
|
Kind = Ty->getDimension();
|
|
} else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(HandleTy)) {
|
|
RC = ResourceClass::UAV;
|
|
Kind = Ty->getDimension();
|
|
} else if (isa<CBufferExtType>(HandleTy)) {
|
|
RC = ResourceClass::CBuffer;
|
|
Kind = ResourceKind::CBuffer;
|
|
} else if (isa<SamplerExtType>(HandleTy)) {
|
|
RC = ResourceClass::Sampler;
|
|
Kind = ResourceKind::Sampler;
|
|
} else
|
|
llvm_unreachable("Unknown handle type");
|
|
}
|
|
|
|
static void formatTypeName(SmallString<64> &Dest, StringRef Name,
|
|
bool IsWriteable, bool IsROV,
|
|
Type *ContainedType = nullptr,
|
|
bool IsSigned = true) {
|
|
raw_svector_ostream DestStream(Dest);
|
|
if (IsWriteable)
|
|
DestStream << (IsROV ? "RasterizerOrdered" : "RW");
|
|
DestStream << Name;
|
|
|
|
if (!ContainedType)
|
|
return;
|
|
|
|
StringRef ElementName;
|
|
ElementType ET = toDXILElementType(ContainedType, IsSigned);
|
|
if (ET != ElementType::Invalid) {
|
|
ElementName = getElementTypeNameForTemplate(ET);
|
|
} else {
|
|
assert(isa<StructType>(ContainedType) &&
|
|
"invalid element type for raw buffer");
|
|
StructType *ST = cast<StructType>(ContainedType);
|
|
if (!ST->hasName())
|
|
return;
|
|
ElementName = ST->getStructName();
|
|
}
|
|
|
|
DestStream << "<" << ElementName;
|
|
if (const FixedVectorType *VTy = dyn_cast<FixedVectorType>(ContainedType))
|
|
DestStream << VTy->getNumElements();
|
|
DestStream << ">";
|
|
}
|
|
|
|
static StructType *getOrCreateElementStruct(Type *ElemType, StringRef Name) {
|
|
StructType *Ty = StructType::getTypeByName(ElemType->getContext(), Name);
|
|
if (Ty && Ty->getNumElements() == 1 && Ty->getElementType(0) == ElemType)
|
|
return Ty;
|
|
return StructType::create(ElemType, Name);
|
|
}
|
|
|
|
StructType *ResourceTypeInfo::createElementStruct(StringRef CBufferName) {
|
|
SmallString<64> TypeName;
|
|
|
|
switch (Kind) {
|
|
case ResourceKind::Texture1D:
|
|
case ResourceKind::Texture2D:
|
|
case ResourceKind::Texture3D:
|
|
case ResourceKind::TextureCube:
|
|
case ResourceKind::Texture1DArray:
|
|
case ResourceKind::Texture2DArray:
|
|
case ResourceKind::TextureCubeArray: {
|
|
auto *RTy = cast<TextureExtType>(HandleTy);
|
|
formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
|
|
RTy->isROV(), RTy->getResourceType(), RTy->isSigned());
|
|
return getOrCreateElementStruct(RTy->getResourceType(), TypeName);
|
|
}
|
|
case ResourceKind::Texture2DMS:
|
|
case ResourceKind::Texture2DMSArray: {
|
|
auto *RTy = cast<MSTextureExtType>(HandleTy);
|
|
formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
|
|
/*IsROV=*/false, RTy->getResourceType(), RTy->isSigned());
|
|
return getOrCreateElementStruct(RTy->getResourceType(), TypeName);
|
|
}
|
|
case ResourceKind::TypedBuffer: {
|
|
auto *RTy = cast<TypedBufferExtType>(HandleTy);
|
|
formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
|
|
RTy->isROV(), RTy->getResourceType(), RTy->isSigned());
|
|
return getOrCreateElementStruct(RTy->getResourceType(), TypeName);
|
|
}
|
|
case ResourceKind::RawBuffer: {
|
|
auto *RTy = cast<RawBufferExtType>(HandleTy);
|
|
formatTypeName(TypeName, "ByteAddressBuffer", RTy->isWriteable(),
|
|
RTy->isROV());
|
|
return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),
|
|
TypeName);
|
|
}
|
|
case ResourceKind::StructuredBuffer: {
|
|
auto *RTy = cast<RawBufferExtType>(HandleTy);
|
|
Type *Ty = RTy->getResourceType();
|
|
formatTypeName(TypeName, "StructuredBuffer", RTy->isWriteable(),
|
|
RTy->isROV(), RTy->getResourceType(), true);
|
|
return getOrCreateElementStruct(Ty, TypeName);
|
|
}
|
|
case ResourceKind::FeedbackTexture2D:
|
|
case ResourceKind::FeedbackTexture2DArray: {
|
|
auto *RTy = cast<FeedbackTextureExtType>(HandleTy);
|
|
TypeName = formatv("{0}<{1}>", getResourceKindName(Kind),
|
|
llvm::to_underlying(RTy->getFeedbackType()));
|
|
return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),
|
|
TypeName);
|
|
}
|
|
case ResourceKind::CBuffer: {
|
|
auto *RTy = cast<CBufferExtType>(HandleTy);
|
|
LayoutExtType *LayoutType = cast<LayoutExtType>(RTy->getResourceType());
|
|
StructType *Ty = cast<StructType>(LayoutType->getWrappedType());
|
|
SmallString<64> Name = getResourceKindName(Kind);
|
|
if (!CBufferName.empty()) {
|
|
Name.append(".");
|
|
Name.append(CBufferName);
|
|
}
|
|
return StructType::create(Ty->elements(), Name);
|
|
}
|
|
case ResourceKind::Sampler: {
|
|
auto *RTy = cast<SamplerExtType>(HandleTy);
|
|
TypeName = formatv("SamplerState<{0}>",
|
|
llvm::to_underlying(RTy->getSamplerType()));
|
|
return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),
|
|
TypeName);
|
|
}
|
|
case ResourceKind::TBuffer:
|
|
case ResourceKind::RTAccelerationStructure:
|
|
llvm_unreachable("Unhandled resource kind");
|
|
case ResourceKind::Invalid:
|
|
case ResourceKind::NumEntries:
|
|
llvm_unreachable("Invalid resource kind");
|
|
}
|
|
llvm_unreachable("Unhandled ResourceKind enum");
|
|
}
|
|
|
|
bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; }
|
|
|
|
bool ResourceTypeInfo::isCBuffer() const {
|
|
return RC == ResourceClass::CBuffer;
|
|
}
|
|
|
|
bool ResourceTypeInfo::isSampler() const {
|
|
return RC == ResourceClass::Sampler;
|
|
}
|
|
|
|
bool ResourceTypeInfo::isStruct() const {
|
|
return Kind == ResourceKind::StructuredBuffer;
|
|
}
|
|
|
|
bool ResourceTypeInfo::isTyped() const {
|
|
switch (Kind) {
|
|
case ResourceKind::Texture1D:
|
|
case ResourceKind::Texture2D:
|
|
case ResourceKind::Texture2DMS:
|
|
case ResourceKind::Texture3D:
|
|
case ResourceKind::TextureCube:
|
|
case ResourceKind::Texture1DArray:
|
|
case ResourceKind::Texture2DArray:
|
|
case ResourceKind::Texture2DMSArray:
|
|
case ResourceKind::TextureCubeArray:
|
|
case ResourceKind::TypedBuffer:
|
|
return true;
|
|
case ResourceKind::RawBuffer:
|
|
case ResourceKind::StructuredBuffer:
|
|
case ResourceKind::FeedbackTexture2D:
|
|
case ResourceKind::FeedbackTexture2DArray:
|
|
case ResourceKind::CBuffer:
|
|
case ResourceKind::Sampler:
|
|
case ResourceKind::TBuffer:
|
|
case ResourceKind::RTAccelerationStructure:
|
|
return false;
|
|
case ResourceKind::Invalid:
|
|
case ResourceKind::NumEntries:
|
|
llvm_unreachable("Invalid resource kind");
|
|
}
|
|
llvm_unreachable("Unhandled ResourceKind enum");
|
|
}
|
|
|
|
bool ResourceTypeInfo::isFeedback() const {
|
|
return Kind == ResourceKind::FeedbackTexture2D ||
|
|
Kind == ResourceKind::FeedbackTexture2DArray;
|
|
}
|
|
|
|
bool ResourceTypeInfo::isMultiSample() const {
|
|
return Kind == ResourceKind::Texture2DMS ||
|
|
Kind == ResourceKind::Texture2DMSArray;
|
|
}
|
|
|
|
static bool isROV(dxil::ResourceKind Kind, TargetExtType *Ty) {
|
|
switch (Kind) {
|
|
case ResourceKind::Texture1D:
|
|
case ResourceKind::Texture2D:
|
|
case ResourceKind::Texture3D:
|
|
case ResourceKind::TextureCube:
|
|
case ResourceKind::Texture1DArray:
|
|
case ResourceKind::Texture2DArray:
|
|
case ResourceKind::TextureCubeArray:
|
|
return cast<TextureExtType>(Ty)->isROV();
|
|
case ResourceKind::TypedBuffer:
|
|
return cast<TypedBufferExtType>(Ty)->isROV();
|
|
case ResourceKind::RawBuffer:
|
|
case ResourceKind::StructuredBuffer:
|
|
return cast<RawBufferExtType>(Ty)->isROV();
|
|
case ResourceKind::Texture2DMS:
|
|
case ResourceKind::Texture2DMSArray:
|
|
case ResourceKind::FeedbackTexture2D:
|
|
case ResourceKind::FeedbackTexture2DArray:
|
|
return false;
|
|
case ResourceKind::CBuffer:
|
|
case ResourceKind::Sampler:
|
|
case ResourceKind::TBuffer:
|
|
case ResourceKind::RTAccelerationStructure:
|
|
case ResourceKind::Invalid:
|
|
case ResourceKind::NumEntries:
|
|
llvm_unreachable("Resource cannot be ROV");
|
|
}
|
|
llvm_unreachable("Unhandled ResourceKind enum");
|
|
}
|
|
|
|
ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const {
|
|
assert(isUAV() && "Not a UAV");
|
|
return {isROV(Kind, HandleTy)};
|
|
}
|
|
|
|
uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const {
|
|
assert(isCBuffer() && "Not a CBuffer");
|
|
|
|
Type *ElTy = cast<CBufferExtType>(HandleTy)->getResourceType();
|
|
|
|
if (auto *LayoutTy = dyn_cast<LayoutExtType>(ElTy))
|
|
return LayoutTy->getSize();
|
|
|
|
// TODO: What should we do with unannotated arrays?
|
|
return DL.getTypeAllocSize(ElTy);
|
|
}
|
|
|
|
dxil::SamplerType ResourceTypeInfo::getSamplerType() const {
|
|
assert(isSampler() && "Not a Sampler");
|
|
return cast<SamplerExtType>(HandleTy)->getSamplerType();
|
|
}
|
|
|
|
ResourceTypeInfo::StructInfo
|
|
ResourceTypeInfo::getStruct(const DataLayout &DL) const {
|
|
assert(isStruct() && "Not a Struct");
|
|
|
|
Type *ElTy = cast<RawBufferExtType>(HandleTy)->getResourceType();
|
|
|
|
uint32_t Stride = DL.getTypeAllocSize(ElTy);
|
|
MaybeAlign Alignment;
|
|
if (auto *STy = dyn_cast<StructType>(ElTy))
|
|
Alignment = DL.getStructLayout(STy)->getAlignment();
|
|
uint32_t AlignLog2 = Alignment ? Log2(*Alignment) : 0;
|
|
return {Stride, AlignLog2};
|
|
}
|
|
|
|
static std::pair<Type *, bool> getTypedElementType(dxil::ResourceKind Kind,
|
|
TargetExtType *Ty) {
|
|
switch (Kind) {
|
|
case ResourceKind::Texture1D:
|
|
case ResourceKind::Texture2D:
|
|
case ResourceKind::Texture3D:
|
|
case ResourceKind::TextureCube:
|
|
case ResourceKind::Texture1DArray:
|
|
case ResourceKind::Texture2DArray:
|
|
case ResourceKind::TextureCubeArray: {
|
|
auto *RTy = cast<TextureExtType>(Ty);
|
|
return {RTy->getResourceType(), RTy->isSigned()};
|
|
}
|
|
case ResourceKind::Texture2DMS:
|
|
case ResourceKind::Texture2DMSArray: {
|
|
auto *RTy = cast<MSTextureExtType>(Ty);
|
|
return {RTy->getResourceType(), RTy->isSigned()};
|
|
}
|
|
case ResourceKind::TypedBuffer: {
|
|
auto *RTy = cast<TypedBufferExtType>(Ty);
|
|
return {RTy->getResourceType(), RTy->isSigned()};
|
|
}
|
|
case ResourceKind::RawBuffer:
|
|
case ResourceKind::StructuredBuffer:
|
|
case ResourceKind::FeedbackTexture2D:
|
|
case ResourceKind::FeedbackTexture2DArray:
|
|
case ResourceKind::CBuffer:
|
|
case ResourceKind::Sampler:
|
|
case ResourceKind::TBuffer:
|
|
case ResourceKind::RTAccelerationStructure:
|
|
case ResourceKind::Invalid:
|
|
case ResourceKind::NumEntries:
|
|
llvm_unreachable("Resource is not typed");
|
|
}
|
|
llvm_unreachable("Unhandled ResourceKind enum");
|
|
}
|
|
|
|
ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const {
|
|
assert(isTyped() && "Not typed");
|
|
|
|
auto [ElTy, IsSigned] = getTypedElementType(Kind, HandleTy);
|
|
dxil::ElementType ET = toDXILElementType(ElTy, IsSigned);
|
|
uint32_t Count = 1;
|
|
if (auto *VTy = dyn_cast<FixedVectorType>(ElTy))
|
|
Count = VTy->getNumElements();
|
|
return {ET, Count};
|
|
}
|
|
|
|
dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const {
|
|
assert(isFeedback() && "Not Feedback");
|
|
return cast<FeedbackTextureExtType>(HandleTy)->getFeedbackType();
|
|
}
|
|
uint32_t ResourceTypeInfo::getMultiSampleCount() const {
|
|
assert(isMultiSample() && "Not MultiSampled");
|
|
return cast<MSTextureExtType>(HandleTy)->getSampleCount();
|
|
}
|
|
|
|
bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const {
|
|
return HandleTy == RHS.HandleTy;
|
|
}
|
|
|
|
bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const {
|
|
// An empty datalayout is sufficient for sorting purposes.
|
|
DataLayout DummyDL;
|
|
if (std::tie(RC, Kind) < std::tie(RHS.RC, RHS.Kind))
|
|
return true;
|
|
if (isCBuffer() && RHS.isCBuffer() &&
|
|
getCBufferSize(DummyDL) < RHS.getCBufferSize(DummyDL))
|
|
return true;
|
|
if (isSampler() && RHS.isSampler() && getSamplerType() < RHS.getSamplerType())
|
|
return true;
|
|
if (isUAV() && RHS.isUAV() && getUAV() < RHS.getUAV())
|
|
return true;
|
|
if (isStruct() && RHS.isStruct() &&
|
|
getStruct(DummyDL) < RHS.getStruct(DummyDL))
|
|
return true;
|
|
if (isFeedback() && RHS.isFeedback() &&
|
|
getFeedbackType() < RHS.getFeedbackType())
|
|
return true;
|
|
if (isTyped() && RHS.isTyped() && getTyped() < RHS.getTyped())
|
|
return true;
|
|
if (isMultiSample() && RHS.isMultiSample() &&
|
|
getMultiSampleCount() < RHS.getMultiSampleCount())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const {
|
|
OS << " Class: " << getResourceClassName(RC) << "\n"
|
|
<< " Kind: " << getResourceKindName(Kind) << "\n";
|
|
|
|
if (isCBuffer()) {
|
|
OS << " CBuffer size: " << getCBufferSize(DL) << "\n";
|
|
} else if (isSampler()) {
|
|
OS << " Sampler Type: " << getSamplerTypeName(getSamplerType()) << "\n";
|
|
} else {
|
|
if (isUAV()) {
|
|
UAVInfo UAVFlags = getUAV();
|
|
OS << " IsROV: " << UAVFlags.IsROV << "\n";
|
|
}
|
|
if (isMultiSample())
|
|
OS << " Sample Count: " << getMultiSampleCount() << "\n";
|
|
|
|
if (isStruct()) {
|
|
StructInfo Struct = getStruct(DL);
|
|
OS << " Buffer Stride: " << Struct.Stride << "\n";
|
|
OS << " Alignment: " << Struct.AlignLog2 << "\n";
|
|
} else if (isTyped()) {
|
|
TypedInfo Typed = getTyped();
|
|
OS << " Element Type: " << getElementTypeName(Typed.ElementTy) << "\n"
|
|
<< " Element Count: " << Typed.ElementCount << "\n";
|
|
} else if (isFeedback())
|
|
OS << " Feedback Type: " << getSamplerFeedbackTypeName(getFeedbackType())
|
|
<< "\n";
|
|
}
|
|
}
|
|
|
|
GlobalVariable *ResourceInfo::createSymbol(Module &M, StructType *Ty) {
|
|
assert(!Symbol && "Symbol has already been created");
|
|
Symbol = new GlobalVariable(M, Ty, /*isConstant=*/true,
|
|
GlobalValue::ExternalLinkage,
|
|
/*Initializer=*/nullptr, Name);
|
|
return Symbol;
|
|
}
|
|
|
|
MDTuple *ResourceInfo::getAsMetadata(Module &M,
|
|
dxil::ResourceTypeInfo &RTI) const {
|
|
LLVMContext &Ctx = M.getContext();
|
|
const DataLayout &DL = M.getDataLayout();
|
|
|
|
SmallVector<Metadata *, 11> MDVals;
|
|
|
|
Type *I32Ty = Type::getInt32Ty(Ctx);
|
|
Type *I1Ty = Type::getInt1Ty(Ctx);
|
|
auto getIntMD = [&I32Ty](uint32_t V) {
|
|
return ConstantAsMetadata::get(
|
|
Constant::getIntegerValue(I32Ty, APInt(32, V)));
|
|
};
|
|
auto getBoolMD = [&I1Ty](uint32_t V) {
|
|
return ConstantAsMetadata::get(
|
|
Constant::getIntegerValue(I1Ty, APInt(1, V)));
|
|
};
|
|
|
|
MDVals.push_back(getIntMD(Binding.RecordID));
|
|
assert(Symbol && "Cannot yet create useful resource metadata without symbol");
|
|
MDVals.push_back(ValueAsMetadata::get(Symbol));
|
|
MDVals.push_back(MDString::get(Ctx, Name));
|
|
MDVals.push_back(getIntMD(Binding.Space));
|
|
MDVals.push_back(getIntMD(Binding.LowerBound));
|
|
MDVals.push_back(getIntMD(Binding.Size));
|
|
|
|
if (RTI.isCBuffer()) {
|
|
MDVals.push_back(getIntMD(RTI.getCBufferSize(DL)));
|
|
MDVals.push_back(nullptr);
|
|
} else if (RTI.isSampler()) {
|
|
MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getSamplerType())));
|
|
MDVals.push_back(nullptr);
|
|
} else {
|
|
MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getResourceKind())));
|
|
|
|
if (RTI.isUAV()) {
|
|
ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV();
|
|
MDVals.push_back(getBoolMD(GloballyCoherent));
|
|
MDVals.push_back(getBoolMD(hasCounter()));
|
|
MDVals.push_back(getBoolMD(UAVFlags.IsROV));
|
|
} else {
|
|
// All SRVs include sample count in the metadata, but it's only meaningful
|
|
// for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+,
|
|
// but this just isn't reflected in the metadata at all.
|
|
uint32_t SampleCount =
|
|
RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;
|
|
MDVals.push_back(getIntMD(SampleCount));
|
|
}
|
|
|
|
// Further properties are attached to a metadata list of tag-value pairs.
|
|
SmallVector<Metadata *> Tags;
|
|
if (RTI.isStruct()) {
|
|
Tags.push_back(
|
|
getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride)));
|
|
Tags.push_back(getIntMD(RTI.getStruct(DL).Stride));
|
|
} else if (RTI.isTyped()) {
|
|
Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType)));
|
|
Tags.push_back(getIntMD(llvm::to_underlying(RTI.getTyped().ElementTy)));
|
|
} else if (RTI.isFeedback()) {
|
|
Tags.push_back(
|
|
getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind)));
|
|
Tags.push_back(getIntMD(llvm::to_underlying(RTI.getFeedbackType())));
|
|
}
|
|
MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags));
|
|
}
|
|
|
|
return MDNode::get(Ctx, MDVals);
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t>
|
|
ResourceInfo::getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const {
|
|
const DataLayout &DL = M.getDataLayout();
|
|
|
|
uint32_t ResourceKind = llvm::to_underlying(RTI.getResourceKind());
|
|
uint32_t AlignLog2 = RTI.isStruct() ? RTI.getStruct(DL).AlignLog2 : 0;
|
|
bool IsUAV = RTI.isUAV();
|
|
ResourceTypeInfo::UAVInfo UAVFlags =
|
|
IsUAV ? RTI.getUAV() : ResourceTypeInfo::UAVInfo{};
|
|
bool IsROV = IsUAV && UAVFlags.IsROV;
|
|
bool IsGloballyCoherent = IsUAV && GloballyCoherent;
|
|
uint8_t SamplerCmpOrHasCounter = 0;
|
|
if (IsUAV)
|
|
SamplerCmpOrHasCounter = hasCounter();
|
|
else if (RTI.isSampler())
|
|
SamplerCmpOrHasCounter = RTI.getSamplerType() == SamplerType::Comparison;
|
|
|
|
// TODO: Document this format. Currently the only reference is the
|
|
// implementation of dxc's DxilResourceProperties struct.
|
|
uint32_t Word0 = 0;
|
|
Word0 |= ResourceKind & 0xFF;
|
|
Word0 |= (AlignLog2 & 0xF) << 8;
|
|
Word0 |= (IsUAV & 1) << 12;
|
|
Word0 |= (IsROV & 1) << 13;
|
|
Word0 |= (IsGloballyCoherent & 1) << 14;
|
|
Word0 |= (SamplerCmpOrHasCounter & 1) << 15;
|
|
|
|
uint32_t Word1 = 0;
|
|
if (RTI.isStruct())
|
|
Word1 = RTI.getStruct(DL).Stride;
|
|
else if (RTI.isCBuffer())
|
|
Word1 = RTI.getCBufferSize(DL);
|
|
else if (RTI.isFeedback())
|
|
Word1 = llvm::to_underlying(RTI.getFeedbackType());
|
|
else if (RTI.isTyped()) {
|
|
ResourceTypeInfo::TypedInfo Typed = RTI.getTyped();
|
|
uint32_t CompType = llvm::to_underlying(Typed.ElementTy);
|
|
uint32_t CompCount = Typed.ElementCount;
|
|
uint32_t SampleCount = RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;
|
|
|
|
Word1 |= (CompType & 0xFF) << 0;
|
|
Word1 |= (CompCount & 0xFF) << 8;
|
|
Word1 |= (SampleCount & 0xFF) << 16;
|
|
}
|
|
|
|
return {Word0, Word1};
|
|
}
|
|
|
|
void ResourceInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI,
|
|
const DataLayout &DL) const {
|
|
if (!Name.empty())
|
|
OS << " Name: " << Name << "\n";
|
|
|
|
if (Symbol) {
|
|
OS << " Symbol: ";
|
|
Symbol->printAsOperand(OS);
|
|
OS << "\n";
|
|
}
|
|
|
|
OS << " Binding:\n"
|
|
<< " Record ID: " << Binding.RecordID << "\n"
|
|
<< " Space: " << Binding.Space << "\n"
|
|
<< " Lower Bound: " << Binding.LowerBound << "\n"
|
|
<< " Size: " << Binding.Size << "\n";
|
|
|
|
OS << " Globally Coherent: " << GloballyCoherent << "\n";
|
|
OS << " Counter Direction: ";
|
|
|
|
switch (CounterDirection) {
|
|
case ResourceCounterDirection::Increment:
|
|
OS << "Increment\n";
|
|
break;
|
|
case ResourceCounterDirection::Decrement:
|
|
OS << "Decrement\n";
|
|
break;
|
|
case ResourceCounterDirection::Unknown:
|
|
OS << "Unknown\n";
|
|
break;
|
|
case ResourceCounterDirection::Invalid:
|
|
OS << "Invalid\n";
|
|
break;
|
|
}
|
|
|
|
RTI.print(OS, DL);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA,
|
|
ModuleAnalysisManager::Invalidator &Inv) {
|
|
// Passes that introduce resource types must explicitly invalidate this pass.
|
|
auto PAC = PA.getChecker<DXILResourceTypeAnalysis>();
|
|
return !PAC.preservedWhenStateless();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
static bool isUpdateCounterIntrinsic(Function &F) {
|
|
return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter;
|
|
}
|
|
|
|
StringRef dxil::getResourceNameFromBindingCall(CallInst *CI) {
|
|
Value *Op = nullptr;
|
|
switch (CI->getCalledFunction()->getIntrinsicID()) {
|
|
default:
|
|
llvm_unreachable("unexpected handle creation intrinsic");
|
|
case Intrinsic::dx_resource_handlefrombinding:
|
|
case Intrinsic::dx_resource_handlefromimplicitbinding:
|
|
Op = CI->getArgOperand(5);
|
|
break;
|
|
}
|
|
|
|
auto *GV = dyn_cast<llvm::GlobalVariable>(Op);
|
|
if (!GV)
|
|
return "";
|
|
|
|
auto *CA = dyn_cast<ConstantDataArray>(GV->getInitializer());
|
|
assert(CA && CA->isString() && "expected constant string");
|
|
StringRef Name = CA->getAsString();
|
|
// strip trailing 0
|
|
if (Name.ends_with('\0'))
|
|
Name = Name.drop_back(1);
|
|
return Name;
|
|
}
|
|
|
|
void DXILResourceMap::populateResourceInfos(Module &M,
|
|
DXILResourceTypeMap &DRTM) {
|
|
SmallVector<std::tuple<CallInst *, ResourceInfo, ResourceTypeInfo>> CIToInfos;
|
|
|
|
for (Function &F : M.functions()) {
|
|
if (!F.isDeclaration())
|
|
continue;
|
|
LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n");
|
|
Intrinsic::ID ID = F.getIntrinsicID();
|
|
switch (ID) {
|
|
default:
|
|
continue;
|
|
case Intrinsic::dx_resource_handlefrombinding: {
|
|
auto *HandleTy = cast<TargetExtType>(F.getReturnType());
|
|
ResourceTypeInfo &RTI = DRTM[HandleTy];
|
|
|
|
for (User *U : F.users())
|
|
if (CallInst *CI = dyn_cast<CallInst>(U)) {
|
|
LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n");
|
|
uint32_t Space =
|
|
cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
|
|
uint32_t LowerBound =
|
|
cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
|
|
uint32_t Size =
|
|
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
|
|
StringRef Name = getResourceNameFromBindingCall(CI);
|
|
|
|
ResourceInfo RI =
|
|
ResourceInfo{/*RecordID=*/0, Space, LowerBound,
|
|
Size, HandleTy, Name};
|
|
|
|
CIToInfos.emplace_back(CI, RI, RTI);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::stable_sort(CIToInfos, [](auto &LHS, auto &RHS) {
|
|
const auto &[LCI, LRI, LRTI] = LHS;
|
|
const auto &[RCI, RRI, RRTI] = RHS;
|
|
// Sort by resource class first for grouping purposes, and then by the
|
|
// binding and type so we can remove duplicates.
|
|
ResourceClass LRC = LRTI.getResourceClass();
|
|
ResourceClass RRC = RRTI.getResourceClass();
|
|
|
|
return std::tie(LRC, LRI, LRTI) < std::tie(RRC, RRI, RRTI);
|
|
});
|
|
for (auto [CI, RI, RTI] : CIToInfos) {
|
|
if (Infos.empty() || RI != Infos.back())
|
|
Infos.push_back(RI);
|
|
CallMap[CI] = Infos.size() - 1;
|
|
}
|
|
|
|
unsigned Size = Infos.size();
|
|
// In DXC, Record ID is unique per resource type. Match that.
|
|
FirstUAV = FirstCBuffer = FirstSampler = Size;
|
|
uint32_t NextID = 0;
|
|
for (unsigned I = 0, E = Size; I != E; ++I) {
|
|
ResourceInfo &RI = Infos[I];
|
|
ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
|
|
if (RTI.isUAV() && FirstUAV == Size) {
|
|
FirstUAV = I;
|
|
NextID = 0;
|
|
} else if (RTI.isCBuffer() && FirstCBuffer == Size) {
|
|
FirstCBuffer = I;
|
|
NextID = 0;
|
|
} else if (RTI.isSampler() && FirstSampler == Size) {
|
|
FirstSampler = I;
|
|
NextID = 0;
|
|
}
|
|
|
|
// We need to make sure the types of resource are ordered even if some are
|
|
// missing.
|
|
FirstCBuffer = std::min({FirstCBuffer, FirstSampler});
|
|
FirstUAV = std::min({FirstUAV, FirstCBuffer});
|
|
|
|
// Adjust the resource binding to use the next ID.
|
|
RI.setBindingID(NextID++);
|
|
}
|
|
}
|
|
|
|
void DXILResourceMap::populateCounterDirections(Module &M) {
|
|
for (Function &F : M.functions()) {
|
|
if (!isUpdateCounterIntrinsic(F))
|
|
continue;
|
|
|
|
LLVM_DEBUG(dbgs() << "Update Counter Function: " << F.getName() << "\n");
|
|
|
|
for (const User *U : F.users()) {
|
|
const CallInst *CI = dyn_cast<CallInst>(U);
|
|
assert(CI && "Users of dx_resource_updateCounter must be call instrs");
|
|
|
|
// Determine if the use is an increment or decrement
|
|
Value *CountArg = CI->getArgOperand(1);
|
|
ConstantInt *CountValue = cast<ConstantInt>(CountArg);
|
|
int64_t CountLiteral = CountValue->getSExtValue();
|
|
|
|
// 0 is an unknown direction and shouldn't result in an insert
|
|
if (CountLiteral == 0)
|
|
continue;
|
|
|
|
ResourceCounterDirection Direction = ResourceCounterDirection::Decrement;
|
|
if (CountLiteral > 0)
|
|
Direction = ResourceCounterDirection::Increment;
|
|
|
|
// Collect all potential creation points for the handle arg
|
|
Value *HandleArg = CI->getArgOperand(0);
|
|
SmallVector<ResourceInfo *> RBInfos = findByUse(HandleArg);
|
|
for (ResourceInfo *RBInfo : RBInfos) {
|
|
if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown)
|
|
RBInfo->CounterDirection = Direction;
|
|
else if (RBInfo->CounterDirection != Direction) {
|
|
RBInfo->CounterDirection = ResourceCounterDirection::Invalid;
|
|
HasInvalidDirection = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) {
|
|
populateResourceInfos(M, DRTM);
|
|
populateCounterDirections(M);
|
|
}
|
|
|
|
void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM,
|
|
const DataLayout &DL) const {
|
|
for (unsigned I = 0, E = Infos.size(); I != E; ++I) {
|
|
OS << "Resource " << I << ":\n";
|
|
const dxil::ResourceInfo &RI = Infos[I];
|
|
RI.print(OS, DRTM[RI.getHandleTy()], DL);
|
|
OS << "\n";
|
|
}
|
|
|
|
for (const auto &[CI, Index] : CallMap) {
|
|
OS << "Call bound to " << Index << ":";
|
|
CI->print(OS);
|
|
OS << "\n";
|
|
}
|
|
}
|
|
|
|
SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
|
|
if (const PHINode *Phi = dyn_cast<PHINode>(Key)) {
|
|
SmallVector<dxil::ResourceInfo *> Children;
|
|
for (const Value *V : Phi->operands()) {
|
|
Children.append(findByUse(V));
|
|
}
|
|
return Children;
|
|
}
|
|
|
|
const CallInst *CI = dyn_cast<CallInst>(Key);
|
|
if (!CI)
|
|
return {};
|
|
|
|
switch (CI->getIntrinsicID()) {
|
|
// Found the create, return the binding
|
|
case Intrinsic::dx_resource_handlefrombinding: {
|
|
auto Pos = CallMap.find(CI);
|
|
assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map");
|
|
return {&Infos[Pos->second]};
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Check if any of the parameters are the resource we are following. If so
|
|
// keep searching. If none of them are return an empty list
|
|
const Type *UseType = CI->getType();
|
|
SmallVector<dxil::ResourceInfo *> Children;
|
|
for (const Value *V : CI->args()) {
|
|
if (V->getType() != UseType)
|
|
continue;
|
|
|
|
Children.append(findByUse(V));
|
|
}
|
|
|
|
return Children;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
|
|
struct Binding {
|
|
ResourceClass RC;
|
|
uint32_t Space;
|
|
uint32_t LowerBound;
|
|
uint32_t UpperBound;
|
|
Value *Name;
|
|
Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound,
|
|
uint32_t UpperBound, Value *Name)
|
|
: RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
|
|
Name(Name) {}
|
|
};
|
|
SmallVector<Binding> Bindings;
|
|
|
|
// collect all of the llvm.dx.resource.handlefrombinding calls;
|
|
// make a note if there is llvm.dx.resource.handlefromimplicitbinding
|
|
for (Function &F : M.functions()) {
|
|
if (!F.isDeclaration())
|
|
continue;
|
|
|
|
switch (F.getIntrinsicID()) {
|
|
default:
|
|
continue;
|
|
case Intrinsic::dx_resource_handlefrombinding: {
|
|
auto *HandleTy = cast<TargetExtType>(F.getReturnType());
|
|
ResourceTypeInfo &RTI = DRTM[HandleTy];
|
|
|
|
for (User *U : F.users())
|
|
if (CallInst *CI = dyn_cast<CallInst>(U)) {
|
|
uint32_t Space =
|
|
cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
|
|
uint32_t LowerBound =
|
|
cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
|
|
int32_t Size =
|
|
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
|
|
Value *Name = CI->getArgOperand(5);
|
|
|
|
// negative size means unbounded resource array;
|
|
// upper bound register overflow should be detected in Sema
|
|
assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&
|
|
"upper bound register overflow");
|
|
uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;
|
|
Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound,
|
|
UpperBound, Name);
|
|
}
|
|
break;
|
|
}
|
|
case Intrinsic::dx_resource_handlefromimplicitbinding: {
|
|
ImplicitBinding = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort all the collected bindings
|
|
llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) {
|
|
return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) <
|
|
std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
|
|
});
|
|
|
|
// remove duplicates
|
|
Binding *NewEnd = llvm::unique(Bindings, [](auto &LHS, auto &RHS) {
|
|
return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound,
|
|
LHS.Name) == std::tie(RHS.RC, RHS.Space, RHS.LowerBound,
|
|
RHS.UpperBound, RHS.Name);
|
|
});
|
|
if (NewEnd != Bindings.end())
|
|
Bindings.erase(NewEnd);
|
|
|
|
// Go over the sorted bindings and build up lists of free register ranges
|
|
// for each binding type and used spaces. Bindings are sorted by resource
|
|
// class, space, and lower bound register slot.
|
|
BindingSpaces *BS = &SRVSpaces;
|
|
for (const Binding &B : Bindings) {
|
|
if (BS->RC != B.RC)
|
|
// move to the next resource class spaces
|
|
BS = &getBindingSpaces(B.RC);
|
|
|
|
RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(B.Space)
|
|
: &BS->Spaces.back();
|
|
assert(S->Space <= B.Space && "bindings not sorted correctly?");
|
|
if (B.Space != S->Space)
|
|
// add new space
|
|
S = &BS->Spaces.emplace_back(B.Space);
|
|
|
|
// the space is full - set flag to report overlapping binding later
|
|
if (S->FreeRanges.empty()) {
|
|
OverlappingBinding = true;
|
|
continue;
|
|
}
|
|
|
|
// adjust the last free range lower bound, split it in two, or remove it
|
|
BindingRange &LastFreeRange = S->FreeRanges.back();
|
|
assert(LastFreeRange.UpperBound == UINT32_MAX);
|
|
if (LastFreeRange.LowerBound == B.LowerBound) {
|
|
if (B.UpperBound < UINT32_MAX)
|
|
LastFreeRange.LowerBound = B.UpperBound + 1;
|
|
else
|
|
S->FreeRanges.pop_back();
|
|
} else if (LastFreeRange.LowerBound < B.LowerBound) {
|
|
LastFreeRange.UpperBound = B.LowerBound - 1;
|
|
if (B.UpperBound < UINT32_MAX)
|
|
S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
|
|
} else {
|
|
OverlappingBinding = true;
|
|
if (B.UpperBound < UINT32_MAX)
|
|
LastFreeRange.LowerBound =
|
|
std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
|
|
else
|
|
S->FreeRanges.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns std::nulopt if binding could not be found in given space
|
|
std::optional<uint32_t>
|
|
DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
|
|
uint32_t Space, int32_t Size) {
|
|
BindingSpaces &BS = getBindingSpaces(RC);
|
|
RegisterSpace &RS = BS.getOrInsertSpace(Space);
|
|
return RS.findAvailableBinding(Size);
|
|
}
|
|
|
|
DXILResourceBindingInfo::RegisterSpace &
|
|
DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
|
|
for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
|
|
if (I->Space == Space)
|
|
return *I;
|
|
if (I->Space < Space)
|
|
continue;
|
|
return *Spaces.insert(I, Space);
|
|
}
|
|
return Spaces.emplace_back(Space);
|
|
}
|
|
|
|
std::optional<uint32_t>
|
|
DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
|
|
assert((Size == -1 || Size > 0) && "invalid size");
|
|
|
|
if (FreeRanges.empty())
|
|
return std::nullopt;
|
|
|
|
// unbounded array
|
|
if (Size == -1) {
|
|
BindingRange &Last = FreeRanges.back();
|
|
if (Last.UpperBound != UINT32_MAX)
|
|
// this space is already occupied by an unbounded array
|
|
return std::nullopt;
|
|
uint32_t RegSlot = Last.LowerBound;
|
|
FreeRanges.pop_back();
|
|
return RegSlot;
|
|
}
|
|
|
|
// single resource or fixed-size array
|
|
for (BindingRange &R : FreeRanges) {
|
|
// compare the size as uint64_t to prevent overflow for range (0,
|
|
// UINT32_MAX)
|
|
if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
|
|
continue;
|
|
uint32_t RegSlot = R.LowerBound;
|
|
// This might create a range where (LowerBound == UpperBound + 1). When
|
|
// that happens, the next time this function is called the range will
|
|
// skipped over by the check above (at this point Size is always > 0).
|
|
R.LowerBound += Size;
|
|
return RegSlot;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
AnalysisKey DXILResourceTypeAnalysis::Key;
|
|
AnalysisKey DXILResourceAnalysis::Key;
|
|
AnalysisKey DXILResourceBindingAnalysis::Key;
|
|
|
|
DXILResourceMap DXILResourceAnalysis::run(Module &M,
|
|
ModuleAnalysisManager &AM) {
|
|
DXILResourceMap Data;
|
|
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
|
|
Data.populate(M, DRTM);
|
|
return Data;
|
|
}
|
|
|
|
DXILResourceBindingInfo
|
|
DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
|
|
DXILResourceBindingInfo Data;
|
|
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
|
|
Data.populate(M, DRTM);
|
|
return Data;
|
|
}
|
|
|
|
PreservedAnalyses DXILResourcePrinterPass::run(Module &M,
|
|
ModuleAnalysisManager &AM) {
|
|
DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);
|
|
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
|
|
|
|
DRM.print(OS, DRTM, M.getDataLayout());
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
void DXILResourceTypeWrapperPass::anchor() {}
|
|
|
|
DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass()
|
|
: ImmutablePass(ID) {}
|
|
|
|
INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type",
|
|
"DXIL Resource Type Analysis", false, true)
|
|
char DXILResourceTypeWrapperPass::ID = 0;
|
|
|
|
ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {
|
|
return new DXILResourceTypeWrapperPass();
|
|
}
|
|
|
|
DXILResourceWrapperPass::DXILResourceWrapperPass() : ModulePass(ID) {}
|
|
|
|
DXILResourceWrapperPass::~DXILResourceWrapperPass() = default;
|
|
|
|
void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
|
|
AU.setPreservesAll();
|
|
}
|
|
|
|
bool DXILResourceWrapperPass::runOnModule(Module &M) {
|
|
Map.reset(new DXILResourceMap());
|
|
|
|
DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
|
|
Map->populate(M, *DRTM);
|
|
|
|
return false;
|
|
}
|
|
|
|
void DXILResourceWrapperPass::releaseMemory() { Map.reset(); }
|
|
|
|
void DXILResourceWrapperPass::print(raw_ostream &OS, const Module *M) const {
|
|
if (!Map) {
|
|
OS << "No resource map has been built!\n";
|
|
return;
|
|
}
|
|
Map->print(OS, *DRTM, M->getDataLayout());
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD
|
|
void DXILResourceWrapperPass::dump() const { print(dbgs(), nullptr); }
|
|
#endif
|
|
|
|
INITIALIZE_PASS(DXILResourceWrapperPass, "dxil-resources",
|
|
"DXIL Resources Analysis", false, true)
|
|
char DXILResourceWrapperPass::ID = 0;
|
|
|
|
ModulePass *llvm::createDXILResourceWrapperPassPass() {
|
|
return new DXILResourceWrapperPass();
|
|
}
|
|
|
|
DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
|
|
: ModulePass(ID) {}
|
|
|
|
DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
|
|
|
|
void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
|
|
AU.setPreservesAll();
|
|
}
|
|
|
|
bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
|
|
BindingInfo.reset(new DXILResourceBindingInfo());
|
|
|
|
DXILResourceTypeMap &DRTM =
|
|
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
|
|
BindingInfo->populate(M, DRTM);
|
|
|
|
return false;
|
|
}
|
|
|
|
void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }
|
|
|
|
INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",
|
|
"DXIL Resource Binding Analysis", false, true)
|
|
char DXILResourceBindingWrapperPass::ID = 0;
|
|
|
|
ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
|
|
return new DXILResourceWrapperPass();
|
|
}
|