blob: e78fe4bad64cca36a751434e7b58f06db3edc17e [file] [log] [blame] [edit]
///////////////////////////////////////////////////////////////////////////////
// //
// PixPassHelpers.cpp
// // Copyright (C) Microsoft Corporation. All rights reserved. // This file is
// distributed under the University of Illinois Open Source // License. See
// LICENSE.TXT for details. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/DXIL/DxilFunctionProps.h"
#include "dxc/DXIL/DxilInstructions.h"
#include "dxc/DXIL/DxilModule.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilResourceBinding.h"
#include "dxc/DXIL/DxilResourceProperties.h"
#include "dxc/DxilRootSignature/DxilRootSignature.h"
#include "dxc/HLSL/DxilSpanAllocator.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "PixPassHelpers.h"
#include "dxc/Support/Global.h"
#include "dxc/Support/WinIncludes.h"
#include "dxc/dxcapi.h"
#ifdef PIX_DEBUG_DUMP_HELPER
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include <iostream>
#endif
using namespace llvm;
using namespace hlsl;
namespace PIXPassHelpers {
static void FindRayQueryHandlesFromUse(Value *U,
SmallPtrSetImpl<Value *> &Handles) {
if (Handles.insert(U).second) {
auto RayQueryHandleUses = U->uses();
for (Use &Use : RayQueryHandleUses) {
iterator_range<Value::user_iterator> Users = Use->users();
for (User *User : Users) {
if (isa<PHINode>(User) || isa<SelectInst>(User))
FindRayQueryHandlesFromUse(User, Handles);
}
}
}
}
void FindRayQueryHandlesForFunction(llvm::Function *F,
SmallPtrSetImpl<Value *> &RayQueryHandles) {
auto &blocks = F->getBasicBlockList();
if (!blocks.empty()) {
for (auto &block : blocks) {
for (auto &instruction : block) {
if (hlsl::OP::IsDxilOpFuncCallInst(
&instruction, hlsl::OP::OpCode::AllocateRayQuery)) {
FindRayQueryHandlesFromUse(&instruction, RayQueryHandles);
}
}
}
}
}
static unsigned int
GetNextRegisterIdForClass(hlsl::DxilModule &DM,
DXIL::ResourceClass resourceClass) {
switch (resourceClass) {
case DXIL::ResourceClass::CBuffer:
return static_cast<unsigned int>(DM.GetCBuffers().size());
case DXIL::ResourceClass::UAV:
return static_cast<unsigned int>(DM.GetUAVs().size());
default:
DXASSERT(false, "Unexpected resource class");
return 0;
}
}
static bool IsDynamicResourceShaderModel(DxilModule &DM) {
return DM.GetShaderModel()->IsSMAtLeast(6, 6);
}
static bool ShaderModelRequiresAnnotateHandle(DxilModule &DM) {
return DM.GetShaderModel()->IsSMAtLeast(6, 6);
}
llvm::CallInst *CreateHandleForResource(hlsl::DxilModule &DM,
llvm::IRBuilder<> &Builder,
hlsl::DxilResourceBase *resource,
const char *name) {
OP *HlslOP = DM.GetOP();
LLVMContext &Ctx = DM.GetModule()->getContext();
DXIL::ResourceClass resourceClass = resource->GetClass();
unsigned int resourceMetaDataId =
GetNextRegisterIdForClass(DM, resourceClass);
auto const *shaderModel = DM.GetShaderModel();
if (shaderModel->IsLib()) {
llvm::Constant *object = resource->GetGlobalSymbol();
auto *load = Builder.CreateLoad(object);
Function *CreateHandleForLibOpFunc =
HlslOP->GetOpFunc(DXIL::OpCode::CreateHandleForLib,
resource->GetHLSLType()->getVectorElementType());
Constant *CreateHandleForLibOpcodeArg =
HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandleForLib);
auto *handle = Builder.CreateCall(CreateHandleForLibOpFunc,
{CreateHandleForLibOpcodeArg, load});
if (ShaderModelRequiresAnnotateHandle(DM)) {
Function *annotHandleFn =
HlslOP->GetOpFunc(DXIL::OpCode::AnnotateHandle, Type::getVoidTy(Ctx));
Value *annotHandleArg =
HlslOP->GetI32Const((unsigned)DXIL::OpCode::AnnotateHandle);
DxilResourceProperties RP =
resource_helper::loadPropsFromResourceBase(resource);
Type *resPropertyTy = HlslOP->GetResourcePropertiesType();
Value *propertiesV = resource_helper::getAsConstant(RP, resPropertyTy,
*DM.GetShaderModel());
return Builder.CreateCall(annotHandleFn,
{annotHandleArg, handle, propertiesV});
} else {
return handle;
}
} else if (IsDynamicResourceShaderModel(DM)) {
Function *CreateHandleFromBindingOpFunc = HlslOP->GetOpFunc(
DXIL::OpCode::CreateHandleFromBinding, Type::getVoidTy(Ctx));
Constant *CreateHandleFromBindingOpcodeArg =
HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandleFromBinding);
DxilResourceBinding binding =
resource_helper::loadBindingFromResourceBase(resource);
Value *bindingV = resource_helper::getAsConstant(
binding, HlslOP->GetResourceBindingType(), *DM.GetShaderModel());
Value *registerIndex = HlslOP->GetU32Const(0);
Value *isUniformRes = HlslOP->GetI1Const(0);
Value *createHandleFromBindingArgs[] = {CreateHandleFromBindingOpcodeArg,
bindingV, registerIndex,
isUniformRes};
auto *handle = Builder.CreateCall(CreateHandleFromBindingOpFunc,
createHandleFromBindingArgs, name);
Function *annotHandleFn =
HlslOP->GetOpFunc(DXIL::OpCode::AnnotateHandle, Type::getVoidTy(Ctx));
Value *annotHandleArg =
HlslOP->GetI32Const((unsigned)DXIL::OpCode::AnnotateHandle);
DxilResourceProperties RP =
resource_helper::loadPropsFromResourceBase(resource);
Type *resPropertyTy = HlslOP->GetResourcePropertiesType();
Value *propertiesV =
resource_helper::getAsConstant(RP, resPropertyTy, *DM.GetShaderModel());
return Builder.CreateCall(annotHandleFn,
{annotHandleArg, handle, propertiesV});
} else {
Function *CreateHandleOpFunc =
HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
Constant *CreateHandleOpcodeArg =
HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
Constant *ClassArg = HlslOP->GetI8Const(
static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
resourceClass));
Constant *MetaDataArg = HlslOP->GetU32Const(
resourceMetaDataId); // position of the metadata record in the
// corresponding metadata list
Constant *IndexArg = HlslOP->GetU32Const(0); //
Constant *FalseArg =
HlslOP->GetI1Const(0); // non-uniform resource index: false
return Builder.CreateCall(
CreateHandleOpFunc,
{CreateHandleOpcodeArg, ClassArg, MetaDataArg, IndexArg, FalseArg},
name);
}
}
static std::vector<uint8_t> SerializeRootSignatureToVector(
DxilVersionedRootSignatureDesc const *rootSignature) {
CComPtr<IDxcBlob> serializedRootSignature;
CComPtr<IDxcBlobEncoding> errorBlob;
constexpr bool allowReservedRegisterSpace = true;
SerializeRootSignature(rootSignature, &serializedRootSignature, &errorBlob,
allowReservedRegisterSpace);
std::vector<uint8_t> ret;
auto const *serializedData = reinterpret_cast<const uint8_t *>(
serializedRootSignature->GetBufferPointer());
ret.assign(serializedData,
serializedData + serializedRootSignature->GetBufferSize());
return ret;
}
constexpr uint32_t toolsRegisterSpace = static_cast<uint32_t>(-2);
constexpr uint32_t toolsUAVRegister = 0;
template <typename RootSigDesc, typename RootParameterDesc>
void ExtendRootSig(RootSigDesc &rootSigDesc) {
auto *existingParams = rootSigDesc.pParameters;
auto *newParams = new RootParameterDesc[rootSigDesc.NumParameters + 1];
if (existingParams != nullptr) {
memcpy(newParams, existingParams,
rootSigDesc.NumParameters * sizeof(RootParameterDesc));
delete[] existingParams;
}
rootSigDesc.pParameters = newParams;
rootSigDesc.pParameters[rootSigDesc.NumParameters].ParameterType =
DxilRootParameterType::UAV;
rootSigDesc.pParameters[rootSigDesc.NumParameters].Descriptor.RegisterSpace =
toolsRegisterSpace;
rootSigDesc.pParameters[rootSigDesc.NumParameters].Descriptor.ShaderRegister =
toolsUAVRegister;
rootSigDesc.pParameters[rootSigDesc.NumParameters].ShaderVisibility =
DxilShaderVisibility::All;
rootSigDesc.NumParameters++;
}
static std::vector<uint8_t> AddUAVParamterToRootSignature(const void *Data,
uint32_t Size) {
DxilVersionedRootSignature rootSignature;
DeserializeRootSignature(Data, Size, rootSignature.get_address_of());
auto *rs = rootSignature.get_mutable();
switch (rootSignature->Version) {
case DxilRootSignatureVersion::Version_1_0:
ExtendRootSig<DxilRootSignatureDesc, DxilRootParameter>(rs->Desc_1_0);
break;
case DxilRootSignatureVersion::Version_1_1:
ExtendRootSig<DxilRootSignatureDesc1, DxilRootParameter1>(rs->Desc_1_1);
rs->Desc_1_1.pParameters[rs->Desc_1_1.NumParameters - 1].Descriptor.Flags =
hlsl::DxilRootDescriptorFlags::None;
break;
}
return SerializeRootSignatureToVector(rs);
}
static void AddUAVToShaderAttributeRootSignature(DxilModule &DM) {
auto rs = DM.GetSerializedRootSignature();
if (!rs.empty()) {
std::vector<uint8_t> asVector = AddUAVParamterToRootSignature(
rs.data(), static_cast<uint32_t>(rs.size()));
DM.ResetSerializedRootSignature(asVector);
}
}
static void AddUAVToDxilDefinedGlobalRootSignatures(DxilModule &DM) {
auto *subObjects = DM.GetSubobjects();
if (subObjects != nullptr) {
for (auto const &subObject : subObjects->GetSubobjects()) {
if (subObject.second->GetKind() ==
DXIL::SubobjectKind::GlobalRootSignature) {
const void *Data = nullptr;
uint32_t Size = 0;
constexpr bool notALocalRS = false;
if (subObject.second->GetRootSignature(notALocalRS, Data, Size,
nullptr)) {
auto extendedRootSig = AddUAVParamterToRootSignature(Data, Size);
auto rootSignatureSubObjectName = subObject.first;
subObjects->RemoveSubobject(rootSignatureSubObjectName);
subObjects->CreateRootSignature(
rootSignatureSubObjectName, notALocalRS, extendedRootSig.data(),
static_cast<uint32_t>(extendedRootSig.size()));
break;
}
}
}
}
}
// Set up a UAV with structure of a single int
llvm::CallInst *CreateUAV(DxilModule &DM, IRBuilder<> &Builder,
unsigned int registerId, const char *name) {
LLVMContext &Ctx = DM.GetModule()->getContext();
const char *PIXStructTypeName = "struct.RWByteAddressBuffer";
llvm::StructType *UAVStructTy =
DM.GetModule()->getTypeByName(PIXStructTypeName);
if (UAVStructTy == nullptr) {
SmallVector<llvm::Type *, 1> Elements{Type::getInt32Ty(Ctx)};
UAVStructTy = llvm::StructType::create(Elements, PIXStructTypeName);
// Since we only have to do this once per module, we can do it now when
// we're adding the singular UAV structure type to the module:
AddUAVToDxilDefinedGlobalRootSignatures(DM);
AddUAVToShaderAttributeRootSignature(DM);
}
std::unique_ptr<DxilResource> pUAV = llvm::make_unique<DxilResource>();
auto const *shaderModel = DM.GetShaderModel();
if (shaderModel->IsLib()) {
auto *Global = DM.GetModule()->getOrInsertGlobal(
("PIXUAV" + std::to_string(registerId)).c_str(), UAVStructTy);
GlobalVariable *NewGV = cast<GlobalVariable>(Global);
NewGV->setConstant(true);
NewGV->setLinkage(GlobalValue::ExternalLinkage);
NewGV->setThreadLocal(false);
NewGV->setAlignment(4);
pUAV->SetGlobalSymbol(NewGV);
} else {
pUAV->SetGlobalSymbol(UndefValue::get(UAVStructTy->getPointerTo()));
}
pUAV->SetGlobalName(name);
pUAV->SetID(GetNextRegisterIdForClass(DM, DXIL::ResourceClass::UAV));
pUAV->SetRW(true); // sets UAV class
pUAV->SetSpaceID(
(unsigned int)-2); // This is the reserved-for-tools register space
pUAV->SetSampleCount(1);
pUAV->SetGloballyCoherent(false);
pUAV->SetHasCounter(false);
pUAV->SetCompType(CompType::getI32());
pUAV->SetLowerBound(registerId);
pUAV->SetRangeSize(1);
pUAV->SetKind(DXIL::ResourceKind::RawBuffer);
auto pAnnotation = DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
if (pAnnotation == nullptr) {
pAnnotation = DM.GetTypeSystem().AddStructAnnotation(UAVStructTy);
pAnnotation->GetFieldAnnotation(0).SetCBufferOffset(0);
pAnnotation->GetFieldAnnotation(0).SetCompType(
hlsl::DXIL::ComponentType::I32);
pAnnotation->GetFieldAnnotation(0).SetFieldName("count");
}
auto *handle = CreateHandleForResource(DM, Builder, pUAV.get(), name);
DM.AddUAV(std::move(pUAV));
return handle;
}
llvm::Function *GetEntryFunction(hlsl::DxilModule &DM) {
if (DM.GetEntryFunction() != nullptr) {
return DM.GetEntryFunction();
}
return DM.GetPatchConstantFunction();
}
std::vector<llvm::Function *>
GetAllInstrumentableFunctions(hlsl::DxilModule &DM) {
std::vector<llvm::Function *> ret;
for (llvm::Function &F : DM.GetModule()->functions()) {
if (F.isDeclaration() || F.isIntrinsic() || hlsl::OP::IsDxilOpFunc(&F))
continue;
if (F.getBasicBlockList().empty())
continue;
ret.push_back(&F);
}
return ret;
}
hlsl::DXIL::ShaderKind GetFunctionShaderKind(hlsl::DxilModule &DM,
llvm::Function *fn) {
hlsl::DXIL::ShaderKind shaderKind = hlsl::DXIL::ShaderKind::Invalid;
if (!DM.HasDxilFunctionProps(fn)) {
auto ShaderModel = DM.GetShaderModel();
shaderKind = ShaderModel->GetKind();
} else {
hlsl::DxilFunctionProps const &props = DM.GetDxilFunctionProps(fn);
shaderKind = props.shaderKind;
}
return shaderKind;
}
std::vector<llvm::BasicBlock *> GetAllBlocks(hlsl::DxilModule &DM) {
std::vector<llvm::BasicBlock *> ret;
auto entryPoints = DM.GetExportedFunctions();
for (auto &fn : entryPoints) {
auto &blocks = fn->getBasicBlockList();
for (auto &block : blocks) {
ret.push_back(&block);
}
}
return ret;
}
ExpandedStruct ExpandStructType(LLVMContext &Ctx,
Type *OriginalPayloadStructType) {
SmallVector<Type *, 16> Elements;
for (unsigned int i = 0;
i < OriginalPayloadStructType->getStructNumElements(); ++i) {
Elements.push_back(OriginalPayloadStructType->getStructElementType(i));
}
Elements.push_back(Type::getInt32Ty(Ctx));
Elements.push_back(Type::getInt32Ty(Ctx));
Elements.push_back(Type::getInt32Ty(Ctx));
ExpandedStruct ret;
ret.ExpandedPayloadStructType =
StructType::create(Ctx, Elements, "PIX_AS2MS_Expanded_Type");
ret.ExpandedPayloadStructPtrType =
ret.ExpandedPayloadStructType->getPointerTo();
return ret;
}
void ReplaceAllUsesOfInstructionWithNewValueAndDeleteInstruction(
Instruction *Instr, Value *newValue, Type *newType) {
std::vector<Value *> users;
for (auto u = Instr->user_begin(); u != Instr->user_end(); ++u) {
users.push_back(*u);
}
for (auto user : users) {
if (auto *instruction = llvm::cast<Instruction>(user)) {
for (unsigned int i = 0; i < instruction->getNumOperands(); ++i) {
auto *Operand = instruction->getOperand(i);
if (Operand == Instr) {
instruction->setOperand(i, newValue);
}
}
if (llvm::isa<GetElementPtrInst>(instruction)) {
auto *GEP = llvm::cast<GetElementPtrInst>(instruction);
GEP->setSourceElementType(newType);
} else if (hlsl::OP::IsDxilOpFuncCallInst(
instruction, hlsl::OP::OpCode::DispatchMesh)) {
DxilModule &DM = instruction->getModule()->GetOrCreateDxilModule();
OP *HlslOP = DM.GetOP();
DxilInst_DispatchMesh DispatchMesh(instruction);
IRBuilder<> B(instruction);
SmallVector<Value *, 5> args;
args.push_back(
HlslOP->GetU32Const((unsigned)hlsl::OP::OpCode::DispatchMesh));
args.push_back(DispatchMesh.get_threadGroupCountX());
args.push_back(DispatchMesh.get_threadGroupCountY());
args.push_back(DispatchMesh.get_threadGroupCountZ());
args.push_back(newValue);
B.CreateCall(HlslOP->GetOpFunc(DXIL::OpCode::DispatchMesh,
newType->getPointerTo()),
args);
instruction->removeFromParent();
delete instruction;
}
}
}
Instr->removeFromParent();
delete Instr;
}
#ifdef PIX_DEBUG_DUMP_HELPER
static int g_logIndent = 0;
void IncreaseLogIndent() { g_logIndent++; }
void DecreaseLogIndent() { --g_logIndent; }
void Log(const char *format, ...) {
va_list argumentPointer;
va_start(argumentPointer, format);
char buffer[512];
vsnprintf(buffer, _countof(buffer), format, argumentPointer);
va_end(argumentPointer);
for (int i = 0; i < g_logIndent; ++i) {
OutputDebugFormatA(" ");
}
OutputDebugFormatA(buffer);
OutputDebugFormatA("\n");
}
void LogPartialLine(const char *format, ...) {
va_list argumentPointer;
va_start(argumentPointer, format);
char buffer[512];
vsnprintf(buffer, _countof(buffer), format, argumentPointer);
va_end(argumentPointer);
for (int i = 0; i < g_logIndent; ++i) {
OutputDebugFormatA(" ");
}
OutputDebugFormatA(buffer);
}
static llvm::DIType const *DITypePeelTypeAlias(llvm::DIType const *Ty) {
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
const llvm::DITypeIdentifierMap EmptyMap;
switch (DerivedTy->getTag()) {
case llvm::dwarf::DW_TAG_restrict_type:
case llvm::dwarf::DW_TAG_reference_type:
case llvm::dwarf::DW_TAG_const_type:
case llvm::dwarf::DW_TAG_typedef:
case llvm::dwarf::DW_TAG_pointer_type:
case llvm::dwarf::DW_TAG_member:
return DITypePeelTypeAlias(DerivedTy->getBaseType().resolve(EmptyMap));
}
}
return Ty;
}
void DumpArrayType(llvm::DICompositeType const *Ty);
void DumpStructType(llvm::DICompositeType const *Ty);
void DumpFullType(llvm::DIType const *type) {
auto *Ty = DITypePeelTypeAlias(type);
const llvm::DITypeIdentifierMap EmptyMap;
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
switch (DerivedTy->getTag()) {
default:
assert(!"Unhandled DIDerivedType");
std::abort();
return;
case llvm::dwarf::DW_TAG_arg_variable: // "this" pointer
case llvm::dwarf::DW_TAG_pointer_type: // "this" pointer
case llvm::dwarf::DW_TAG_restrict_type:
case llvm::dwarf::DW_TAG_reference_type:
case llvm::dwarf::DW_TAG_const_type:
case llvm::dwarf::DW_TAG_typedef:
case llvm::dwarf::DW_TAG_inheritance:
DumpFullType(DerivedTy->getBaseType().resolve(EmptyMap));
return;
case llvm::dwarf::DW_TAG_member: {
Log("Member variable");
ScopedIndenter indent;
DumpFullType(DerivedTy->getBaseType().resolve(EmptyMap));
}
return;
case llvm::dwarf::DW_TAG_subroutine_type:
std::abort();
return;
}
} else if (auto *CompositeTy = llvm::dyn_cast<llvm::DICompositeType>(Ty)) {
switch (CompositeTy->getTag()) {
default:
assert(!"Unhandled DICompositeType");
std::abort();
return;
case llvm::dwarf::DW_TAG_array_type:
DumpArrayType(CompositeTy);
return;
case llvm::dwarf::DW_TAG_structure_type:
case llvm::dwarf::DW_TAG_class_type:
DumpStructType(CompositeTy);
return;
case llvm::dwarf::DW_TAG_enumeration_type:
// enum base type is int:
std::abort();
return;
}
} else if (auto *BasicTy = llvm::dyn_cast<llvm::DIBasicType>(Ty)) {
Log("%d: %s", BasicTy->getOffsetInBits(), BasicTy->getName().str().c_str());
return;
} else {
std::abort();
}
}
static unsigned NumArrayElements(llvm::DICompositeType const *Array) {
if (Array->getElements().size() == 0) {
return 0;
}
unsigned NumElements = 1;
for (llvm::DINode *N : Array->getElements()) {
if (auto *Subrange = llvm::dyn_cast<llvm::DISubrange>(N)) {
NumElements *= Subrange->getCount();
} else {
assert(!"Unhandled array element");
return 0;
}
}
return NumElements;
}
void DumpArrayType(llvm::DICompositeType const *Ty) {
unsigned NumElements = NumArrayElements(Ty);
Log("Array %s: size: %d", Ty->getName().str().c_str(), NumElements);
if (NumElements == 0) {
std::abort();
return;
}
const llvm::DITypeIdentifierMap EmptyMap;
llvm::DIType *ElementTy = Ty->getBaseType().resolve(EmptyMap);
ScopedIndenter indent;
DumpFullType(ElementTy);
}
void DumpStructType(llvm::DICompositeType const *Ty) {
Log("Struct %s", Ty->getName().str().c_str());
ScopedIndenter indent;
auto Elements = Ty->getElements();
if (Elements.begin() == Elements.end()) {
Log("Resource member: size %d", Ty->getSizeInBits());
return;
}
for (auto *Element : Elements) {
switch (Element->getTag()) {
case llvm::dwarf::DW_TAG_member: {
if (auto *Member = llvm::dyn_cast<llvm::DIDerivedType>(Element)) {
DumpFullType(Member);
break;
}
assert(!"member is not a Member");
std::abort();
return;
}
case llvm::dwarf::DW_TAG_subprogram: {
if (auto *SubProgram = llvm::dyn_cast<llvm::DISubprogram>(Element)) {
Log("Member function %s", SubProgram->getName().str().c_str());
continue;
}
assert(!"DISubprogram not understood");
std::abort();
return;
}
case llvm::dwarf::DW_TAG_inheritance: {
if (auto *Member = llvm::dyn_cast<llvm::DIDerivedType>(Element)) {
DumpFullType(Member);
} else {
std::abort();
}
continue;
}
default:
assert(!"Unhandled field type in DIStructType");
std::abort();
}
}
}
#endif
} // namespace PIXPassHelpers