Justin Bogner c4898f3f22
[HLSL][DirectX] Use a padding type for HLSL buffers. (#167404)
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.
2025-11-18 13:38:43 -08:00

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();
}