This change drops the use of the "Layout" type and instead uses explicit padding throughout the compiler to represent types in HLSL buffers. There are a few parts to this, though it's difficult to split them up as they're very interdependent: 1. Refactor HLSLBufferLayoutBuilder to allow us to calculate the padding of arbitrary types. 2. Teach Clang CodeGen to use HLSL specific paths for cbuffers when generating aggregate copies, array accesses, and structure accesses. 3. Simplify DXILCBufferAccesses such that it directly replaces accesses with dx.resource.getpointer rather than recalculating the layout. 4. Basic infrastructure for SPIR-V handling, but the implementation itself will need work in follow ups. Fixes several issues, including #138996, #144573, and #156084. Resolves #147352.
78 lines
2.6 KiB
C++
78 lines
2.6 KiB
C++
//===- CBuffer.cpp - HLSL constant buffer handling ------------------------===//
|
|
//
|
|
// 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/Frontend/HLSL/CBuffer.h"
|
|
#include "llvm/Frontend/HLSL/HLSLResource.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::hlsl;
|
|
|
|
static SmallVector<size_t>
|
|
getMemberOffsets(const DataLayout &DL, GlobalVariable *Handle,
|
|
llvm::function_ref<bool(Type *)> IsPadding) {
|
|
SmallVector<size_t> Offsets;
|
|
|
|
auto *HandleTy = cast<TargetExtType>(Handle->getValueType());
|
|
assert((HandleTy->getName().ends_with(".CBuffer") ||
|
|
HandleTy->getName() == "spirv.VulkanBuffer") &&
|
|
"Not a cbuffer type");
|
|
assert(HandleTy->getNumTypeParameters() == 1 && "Expected layout type");
|
|
auto *LayoutTy = cast<StructType>(HandleTy->getTypeParameter(0));
|
|
|
|
const StructLayout *SL = DL.getStructLayout(LayoutTy);
|
|
for (int I = 0, E = LayoutTy->getNumElements(); I < E; ++I)
|
|
if (!IsPadding(LayoutTy->getElementType(I)))
|
|
Offsets.push_back(SL->getElementOffset(I));
|
|
|
|
return Offsets;
|
|
}
|
|
|
|
std::optional<CBufferMetadata>
|
|
CBufferMetadata::get(Module &M, llvm::function_ref<bool(Type *)> IsPadding) {
|
|
NamedMDNode *CBufMD = M.getNamedMetadata("hlsl.cbs");
|
|
if (!CBufMD)
|
|
return std::nullopt;
|
|
|
|
std::optional<CBufferMetadata> Result({CBufMD});
|
|
|
|
for (const MDNode *MD : CBufMD->operands()) {
|
|
assert(MD->getNumOperands() && "Invalid cbuffer metadata");
|
|
|
|
// For an unused cbuffer, the handle may have been optimized out
|
|
Metadata *OpMD = MD->getOperand(0);
|
|
if (!OpMD)
|
|
continue;
|
|
|
|
auto *Handle =
|
|
cast<GlobalVariable>(cast<ValueAsMetadata>(OpMD)->getValue());
|
|
CBufferMapping &Mapping = Result->Mappings.emplace_back(Handle);
|
|
|
|
SmallVector<size_t> MemberOffsets =
|
|
getMemberOffsets(M.getDataLayout(), Handle, IsPadding);
|
|
|
|
for (int I = 1, E = MD->getNumOperands(); I < E; ++I) {
|
|
Metadata *OpMD = MD->getOperand(I);
|
|
// Some members may be null if they've been optimized out.
|
|
if (!OpMD)
|
|
continue;
|
|
auto *V = cast<GlobalVariable>(cast<ValueAsMetadata>(OpMD)->getValue());
|
|
Mapping.Members.emplace_back(V, MemberOffsets[I - 1]);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void CBufferMetadata::eraseFromModule() {
|
|
// Remove the cbs named metadata
|
|
MD->eraseFromParent();
|
|
}
|