https://github.com/python3kgae updated https://github.com/llvm/llvm-project/pull/91999
>From de2b6cab831ac07bf6adcfe563fd9d866e295071 Mon Sep 17 00:00:00 2001 From: Xiang Li <python3k...@outlook.com> Date: Mon, 13 May 2024 12:59:48 -0400 Subject: [PATCH 1/2] [HLSL] support packoffset in clang codeGen Implement packoffset as offset in the layouted struct for cbuffer. cbuffer CB { float a : packoffset(c2); float b : packoffset(c4); float2 c : packoffset(c2.z); } will get layout struct like struct CBLayout { uint8_t padding0[32]; // c0/c1 float a; // c2.x uint8_t padding1[4]; // c2.y float2 c; // c2.zw uint8_t padding[8]; // c3 float b; // c4.x } --- clang/include/clang/AST/Decl.h | 5 ++ clang/lib/AST/Decl.cpp | 48 +++++++++++ clang/lib/CodeGen/CGHLSLRuntime.cpp | 103 +++++++++++++++++++---- clang/lib/CodeGen/CGHLSLRuntime.h | 8 +- clang/lib/Sema/SemaHLSL.cpp | 38 +-------- clang/test/CodeGenHLSL/cbuf.hlsl | 8 +- clang/test/CodeGenHLSL/packoffset.hlsl | 109 +++++++++++++++++++++++++ 7 files changed, 264 insertions(+), 55 deletions(-) create mode 100644 clang/test/CodeGenHLSL/packoffset.hlsl diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index de8b923645f8d..dfec2f3834e9d 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4959,6 +4959,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { SourceLocation LBrace); static HLSLBufferDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); + // Calculate the size of a legacy cbuffer type based on + // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules + static unsigned calculateLegacyCbufferSize(const ASTContext &Context, + QualType T); + SourceRange getSourceRange() const override LLVM_READONLY { return SourceRange(getLocStart(), RBraceLoc); } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index ec851c9371e10..ebfff00001087 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5671,6 +5671,54 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, SourceLocation(), SourceLocation()); } +static uint64_t calculateLegacyCbufferAlign(const ASTContext &Context, + QualType T) { + if (T->isAggregateType()) + return 128; + else if (const VectorType *VT = T->getAs<VectorType>()) + return Context.getTypeSize(VT->getElementType()); + else + return Context.getTypeSize(T); +} + +unsigned HLSLBufferDecl::calculateLegacyCbufferSize(const ASTContext &Context, + QualType T) { + unsigned Size = 0; + constexpr unsigned CBufferAlign = 128; + constexpr unsigned CBufferAlignMask = CBufferAlign - 1; + if (const RecordType *RT = T->getAs<RecordType>()) { + const RecordDecl *RD = RT->getDecl(); + for (const FieldDecl *Field : RD->fields()) { + QualType Ty = Field->getType().getCanonicalType(); + unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty); + unsigned FieldAlign = calculateLegacyCbufferAlign(Context, Ty); + if (FieldAlign < CBufferAlign) { + // Cross 16-byte boundary. + unsigned Base = CBufferAlignMask & Size; + if ((Base + FieldSize) > CBufferAlign) + FieldAlign = CBufferAlign; + } + Size = llvm::alignTo(Size, FieldAlign); + Size += FieldSize; + } + } else if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) { + if (unsigned ElementCount = AT->getSize().getZExtValue()) { + unsigned ElementSize = + calculateLegacyCbufferSize(Context, AT->getElementType()); + unsigned AlignedElementSize = llvm::alignTo(ElementSize, CBufferAlign); + Size = AlignedElementSize * (ElementCount - 1) + ElementSize; + } + } else if (const VectorType *VT = T->getAs<VectorType>()) { + unsigned ElementCount = VT->getNumElements(); + unsigned ElementSize = + calculateLegacyCbufferSize(Context, VT->getElementType()); + Size = ElementSize * ElementCount; + } else { + Size = Context.getTypeSize(T); + } + return Size; +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 5e6a3dd4878f4..0014bfbc68095 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -52,6 +52,17 @@ void addDisableOptimizations(llvm::Module &M) { StringRef Key = "dx.disable_optimizations"; M.addModuleFlag(llvm::Module::ModFlagBehavior::Override, Key, 1); } + +static uint64_t calculateLegacyCbufferAlign(const DataLayout &DL, + llvm::Type *T) { + if (T->isAggregateType()) + return 16; + else if (const auto *VT = dyn_cast<FixedVectorType>(T)) + return DL.getTypeAllocSize(VT->getElementType()); + else + return DL.getTypeAllocSize(T); +} + // cbuffer will be translated into global variable in special address space. // If translate into C, // cbuffer A { @@ -76,14 +87,71 @@ void layoutBuffer(CGHLSLRuntime::Buffer &Buf, const DataLayout &DL) { if (Buf.Constants.empty()) return; + // Sort Buffer.Constants based on offset. + std::stable_sort(Buf.Constants.begin(), Buf.Constants.end(), + [](const CGHLSLRuntime::Buffer::Constant &LHS, + const CGHLSLRuntime::Buffer::Constant &RHS) { + return LHS.Offset < RHS.Offset; + }); + + unsigned BufferSize = 0; + // Collect offset for allocated constants first. + for (auto &Const : Buf.Constants) { + unsigned Offset = Const.Offset; + if (Offset == UINT_MAX) + continue; + unsigned Size = Const.Size; + // No need to align, the packoffset is already aligned. + BufferSize = Offset + Size; + } + + constexpr unsigned CBufferAlign = 16; + constexpr unsigned CBufferAlignMask = CBufferAlign - 1; + // Allocate Offset for constant not allocated yet. + for (auto &Const : Buf.Constants) { + unsigned Offset = Const.Offset; + if (Offset != UINT_MAX) + continue; + + GlobalVariable *GV = Const.GV; + llvm::Type *Ty = GV->getValueType(); + unsigned Align = calculateLegacyCbufferAlign(DL, Ty); + unsigned Size = Const.Size; + if (Align < CBufferAlign) { + // Cross 16-byte boundary. + unsigned Base = CBufferAlignMask & Size; + if ((Base + Size) > CBufferAlign) + Align = CBufferAlign; + } + Offset = llvm::alignTo(BufferSize, Align); + BufferSize = Offset + Size; + Const.Offset = Offset; + } + + // Build struct type for cbuffer. + LLVMContext &Ctx = Buf.Constants[0].GV->getContext(); std::vector<llvm::Type *> EltTys; + unsigned CurOffset = 0; + auto *I8Ty = llvm::Type::getInt8Ty(Ctx); + for (auto &Const : Buf.Constants) { - GlobalVariable *GV = Const.first; - Const.second = EltTys.size(); + // Byte offset. + unsigned ConstOffset = Const.Offset; + // Add padding if needed. + if (CurOffset < ConstOffset) + EltTys.emplace_back( + llvm::ArrayType::get(I8Ty, (ConstOffset - CurOffset))); + assert(CurOffset <= ConstOffset && "constant overlap"); + GlobalVariable *GV = Const.GV; + // Change the offset to field index of the layout struct. + Const.ElementIndex = EltTys.size(); llvm::Type *Ty = GV->getValueType(); EltTys.emplace_back(Ty); + unsigned Size = Const.Size; + // No need to align, the ConstOffset is already aligned. + CurOffset = ConstOffset + Size; } - Buf.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys); + Buf.LayoutStruct = llvm::StructType::get(Ctx, EltTys); } GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) { @@ -97,11 +165,13 @@ GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) { IRBuilder<> B(CBGV->getContext()); Value *ZeroIdx = B.getInt32(0); // Replace Const use with CB use. - for (auto &[GV, Offset] : Buf.Constants) { - Value *GEP = - B.CreateGEP(Buf.LayoutStruct, CBGV, {ZeroIdx, B.getInt32(Offset)}); + for (auto &Constant : Buf.Constants) { + GlobalVariable *GV = Constant.GV; + Value *GEP = B.CreateGEP(Buf.LayoutStruct, CBGV, + {ZeroIdx, B.getInt32(Constant.ElementIndex)}); - assert(Buf.LayoutStruct->getElementType(Offset) == GV->getValueType() && + assert(Buf.LayoutStruct->getElementType(Constant.ElementIndex) == + GV->getValueType() && "constant type mismatch"); // Replace. @@ -134,13 +204,18 @@ void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) { codegenoptions::DebugInfoKind::LimitedDebugInfo) DI->EmitGlobalVariable(cast<GlobalVariable>(GV), D); - // FIXME: support packoffset. - // See https://github.com/llvm/llvm-project/issues/57914. - uint32_t Offset = 0; - bool HasUserOffset = false; - - unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX; - CB.Constants.emplace_back(std::make_pair(GV, LowerBound)); + unsigned LowerBound = UINT_MAX; + bool HasUserOffset = D->hasAttr<HLSLPackOffsetAttr>(); + if (HasUserOffset) { + auto *Attr = D->getAttr<HLSLPackOffsetAttr>(); + // Make the offset in bytes. + LowerBound = Attr->getOffset() * 4; + } + // Byte size. + unsigned Size = HLSLBufferDecl::calculateLegacyCbufferSize(CGM.getContext(), + D->getType()) >> + 3; + CB.Constants.emplace_back(Buffer::Constant{GV, LowerBound, Size, 0}); } void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) { diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 0abe39dedcb96..e9f6ccad72ee8 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -95,8 +95,14 @@ class CGHLSLRuntime { // IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer). bool IsCBuffer; BufferResBinding Binding; + struct Constant { + llvm::GlobalVariable *GV; + unsigned Offset; + unsigned Size; + unsigned ElementIndex; + }; // Global variable and offset for each constant. - std::vector<std::pair<llvm::GlobalVariable *, unsigned>> Constants; + std::vector<Constant> Constants; llvm::StructType *LayoutStruct = nullptr; }; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 6a12c417e2f3a..12496e6f01c7d 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -39,41 +39,6 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer, return Result; } -// Calculate the size of a legacy cbuffer type based on -// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules -static unsigned calculateLegacyCbufferSize(const ASTContext &Context, - QualType T) { - unsigned Size = 0; - constexpr unsigned CBufferAlign = 128; - if (const RecordType *RT = T->getAs<RecordType>()) { - const RecordDecl *RD = RT->getDecl(); - for (const FieldDecl *Field : RD->fields()) { - QualType Ty = Field->getType(); - unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty); - unsigned FieldAlign = 32; - if (Ty->isAggregateType()) - FieldAlign = CBufferAlign; - Size = llvm::alignTo(Size, FieldAlign); - Size += FieldSize; - } - } else if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) { - if (unsigned ElementCount = AT->getSize().getZExtValue()) { - unsigned ElementSize = - calculateLegacyCbufferSize(Context, AT->getElementType()); - unsigned AlignedElementSize = llvm::alignTo(ElementSize, CBufferAlign); - Size = AlignedElementSize * (ElementCount - 1) + ElementSize; - } - } else if (const VectorType *VT = T->getAs<VectorType>()) { - unsigned ElementCount = VT->getNumElements(); - unsigned ElementSize = - calculateLegacyCbufferSize(Context, VT->getElementType()); - Size = ElementSize * ElementCount; - } else { - Size = Context.getTypeSize(T); - } - return Size; -} - void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) { auto *BufDecl = cast<HLSLBufferDecl>(Dcl); BufDecl->setRBraceLoc(RBrace); @@ -110,7 +75,8 @@ void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) { for (unsigned i = 0; i < PackOffsetVec.size() - 1; i++) { VarDecl *Var = PackOffsetVec[i].first; HLSLPackOffsetAttr *Attr = PackOffsetVec[i].second; - unsigned Size = calculateLegacyCbufferSize(Context, Var->getType()); + unsigned Size = + HLSLBufferDecl::calculateLegacyCbufferSize(Context, Var->getType()); unsigned Begin = Attr->getOffset() * 32; unsigned End = Begin + Size; unsigned NextBegin = PackOffsetVec[i + 1].second->getOffset() * 32; diff --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl index dc2a6aaa8f433..2500562508274 100644 --- a/clang/test/CodeGenHLSL/cbuf.hlsl +++ b/clang/test/CodeGenHLSL/cbuf.hlsl @@ -2,13 +2,13 @@ // RUN: dxil-pc-shadermodel6.3-library %s \ // RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s -// CHECK: @[[CB:.+]] = external constant { float, double } +// CHECK: @[[CB:.+]] = external constant { float, [4 x i8], double } cbuffer A : register(b0, space2) { float a; double b; } -// CHECK: @[[TB:.+]] = external constant { float, double } +// CHECK: @[[TB:.+]] = external constant { float, [4 x i8], double } tbuffer A : register(t2, space1) { float c; double d; @@ -16,9 +16,9 @@ tbuffer A : register(t2, space1) { float foo() { // CHECK: load float, ptr @[[CB]], align 4 -// CHECK: load double, ptr getelementptr inbounds ({ float, double }, ptr @[[CB]], i32 0, i32 1), align 8 +// CHECK: load double, ptr getelementptr inbounds ({ float, [4 x i8], double }, ptr @[[CB]], i32 0, i32 2), align 8 // CHECK: load float, ptr @[[TB]], align 4 -// CHECK: load double, ptr getelementptr inbounds ({ float, double }, ptr @[[TB]], i32 0, i32 1), align 8 +// CHECK: load double, ptr getelementptr inbounds ({ float, [4 x i8], double }, ptr @[[TB]], i32 0, i32 2), align 8 return a + b + c*d; } diff --git a/clang/test/CodeGenHLSL/packoffset.hlsl b/clang/test/CodeGenHLSL/packoffset.hlsl new file mode 100644 index 0000000000000..72cfc11c00aee --- /dev/null +++ b/clang/test/CodeGenHLSL/packoffset.hlsl @@ -0,0 +1,109 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// CHECK: @A.cb. = external constant { [8 x i8], double, [8 x i8], float, float, half, i16, [4 x i8], i64, i32 } +cbuffer A { + float a : packoffset(c1.z); + double b : packoffset(c0.z); + float c; + half d; + int16_t e; + int64_t f; + int g; +} + +// CHECK: @B.cb. = external constant { [24 x i8], float, [4 x i8], double, [8 x i8], <3 x float>, [4 x i8], <3 x double>, half, [6 x i8], <2 x double>, float, <3 x half>, <3 x half> } +cbuffer B { + double B0; + float3 B1; + float B2 : packoffset(c1.z); + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; +} + +// CHECK: @C.cb. = external constant { [2 x double], [8 x i8], [3 x <3 x float>], float, [3 x double], half, [6 x i8], [1 x <2 x double>], float, [12 x i8], [2 x <3 x half>], <3 x half> } +cbuffer C { + double C0[2] : packoffset(c0); + float3 C1[3]; + float C2; + double C3[3]; + half C4; + double2 C5[1]; + float C6; + half3 C7[2]; + half3 C8; +} + +// CHECK: @D.cb. = external constant { [3 x <3 x double>], <3 x half> } +cbuffer D { + double3 D9[3] : packoffset(c); + half3 D10; +} + +struct S0 +{ + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; +}; + +struct S1 +{ + float A0; + double A1; + float A2; + half A3; + int16_t A4; + int64_t A5; + int A6; +}; + +struct S2 +{ + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; +}; + +struct S3 +{ + S1 C0; + float C1[1]; + S2 C2[2]; + half C3; +}; + +// CHECK: @E.cb. = external constant { [16 x i8], half, [2 x i8], i32, double, %struct.S3, [206 x i8], %struct.S0 } +cbuffer E { + int E0 : packoffset(c1.y); + S0 E1 : packoffset(c31); + half E2 : packoffset(c1); + S3 E3 : packoffset(c2); + double E4 : packoffset(c1.z); +} + +float foo() { +// CHECK: %[[a:.+]] = load float, ptr getelementptr inbounds ({ [8 x i8], double, [8 x i8], float, float, half, i16, [4 x i8], i64, i32 }, ptr @A.cb., i32 0, i32 3), align 4 +// CHECK: %[[B2:.+]] = load float, ptr getelementptr inbounds ({ [24 x i8], float, [4 x i8], double, [8 x i8], <3 x float>, [4 x i8], <3 x double>, half, [6 x i8], <2 x double>, float, <3 x half>, <3 x half> }, ptr @B.cb., i32 0, i32 1), align 4 +// CHECK: %[[C6:.+]] = load float, ptr getelementptr inbounds ({ [2 x double], [8 x i8], [3 x <3 x float>], float, [3 x double], half, [6 x i8], [1 x <2 x double>], float, [12 x i8], [2 x <3 x half>], <3 x half> }, ptr @C.cb., i32 0, i32 8), align 4 +// CHECK: %[[D10:.+]] = load <3 x half>, ptr getelementptr inbounds ({ [3 x <3 x double>], <3 x half> }, ptr @D.cb., i32 0, i32 1), align 8 +// CHECK: %[[E0:.+]] = load i32, ptr getelementptr inbounds ({ [16 x i8], half, [2 x i8], i32, double, %struct.S3, [206 x i8], %struct.S0 }, ptr @E.cb., i32 0, i32 3), align 4 + return a + B2 + C6*D10.z + E0; +} >From 87de4175301caa98387e7447111d6fda914e73d4 Mon Sep 17 00:00:00 2001 From: Xiang Li <python3k...@outlook.com> Date: Thu, 16 May 2024 11:17:52 -0400 Subject: [PATCH 2/2] Add unit test. --- clang/unittests/AST/CMakeLists.txt | 1 + .../AST/HLSLLegacyCbufferTypeSizeTest.cpp | 576 ++++++++++++++++++ 2 files changed, 577 insertions(+) create mode 100644 clang/unittests/AST/HLSLLegacyCbufferTypeSizeTest.cpp diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index 54765e36db008..5ce8b7c44554e 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -29,6 +29,7 @@ add_clang_unittest(ASTTests DeclTest.cpp EvaluateAsRValueTest.cpp ExternalASTSourceTest.cpp + HLSLLegacyCbufferTypeSizeTest.cpp NamedDeclPrinterTest.cpp RandstructTest.cpp RecursiveASTVisitorTest.cpp diff --git a/clang/unittests/AST/HLSLLegacyCbufferTypeSizeTest.cpp b/clang/unittests/AST/HLSLLegacyCbufferTypeSizeTest.cpp new file mode 100644 index 0000000000000..abf494d8b3482 --- /dev/null +++ b/clang/unittests/AST/HLSLLegacyCbufferTypeSizeTest.cpp @@ -0,0 +1,576 @@ +//===- unittests/AST/HLSLLegacyCbufferTypeSizeTest.cpp -- cbuf size test --===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for HLSLBufferDecl::calculateLegacyCbufferSize. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace ast_matchers; +using namespace tooling; + +// FIXME: Let buildASTFromCodeWithArgs find hlsl.h instead of using "nostdinc". + +TEST(HLSLLegacyCbufferTypeSize, BasicTypes) { + constexpr char Code[] = R"hlsl( + namespace hlsl { + // built-in scalar data types: + + #ifdef __HLSL_ENABLE_16_BIT + // 16-bit integer. + typedef short int16_t; + #endif + + // 64-bit integer. + typedef long int64_t; + + } // namespace hlsl + + struct A { + float a; + double b; + float c; + half d; + int16_t e; + int64_t f; + int g; + }; + )hlsl"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, + /*Args=*/ + {"--driver-mode=dxc", "-T", "lib_6_3", + "-enable-16bit-types", "-nostdinc"}, + /* FileName*/ "input.hlsl"); + ASTContext &Ctx = AST->getASTContext(); + + auto Results = match(decl(cxxRecordDecl(hasName("A")).bind("A")), Ctx); + + // struct A + // { + + // float a; ; Offset: 0 + // double b; ; Offset: 8 + // float c; ; Offset: 16 + // half d; ; Offset: 20 + // int16_t e; ; Offset: 22 + // int64_t f; ; Offset: 24 + // int g; ; Offset: 32 + + // } A; ; Offset: 0 Size: 36 + auto const *A = selectFirst<RecordDecl>("A", Results); + unsigned SizeA = HLSLBufferDecl::calculateLegacyCbufferSize( + Ctx, QualType(A->getTypeForDecl(), 0)); + ASSERT_EQ(SizeA, 36u * 8u); +} + +TEST(HLSLLegacyCbufferTypeSize, VectorTypes) { + constexpr char Code[] = R"hlsl( + namespace hlsl { + // built-in scalar data types: + + #ifdef __HLSL_ENABLE_16_BIT + // 16-bit integer. + typedef short int16_t; + #endif + + // 64-bit integer. + typedef long int64_t; + + // built-in vector data types: + + typedef vector<half, 2> half2; + typedef vector<half, 3> half3; + typedef vector<half, 4> half4; + + typedef vector<float, 2> float2; + typedef vector<float, 3> float3; + typedef vector<float, 4> float4; + typedef vector<double, 2> double2; + typedef vector<double, 3> double3; + typedef vector<double, 4> double4; + + } // namespace hlsl + + struct A { + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; + }; + )hlsl"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, + /*Args=*/ + {"--driver-mode=dxc", "-T", "lib_6_3", + "-enable-16bit-types", "-nostdinc"}, + /* FileName*/ "input.hlsl"); + ASTContext &Ctx = AST->getASTContext(); + + auto Results = match(decl(cxxRecordDecl(hasName("A")).bind("A")), Ctx); + // struct A + // { + + // double B0; ; Offset: 0 + // float3 B1; ; Offset: 16 + // float B2; ; Offset: 28 + // double3 B3; ; Offset: 32 + // half B4; ; Offset: 56 + // double2 B5; ; Offset: 64 + // float B6; ; Offset: 80 + // half3 B7; ; Offset: 84 + // half3 B8; ; Offset: 90 + + // } A; ; Offset: 0 Size: 96 + auto const *A = selectFirst<RecordDecl>("A", Results); + unsigned SizeA = HLSLBufferDecl::calculateLegacyCbufferSize( + Ctx, QualType(A->getTypeForDecl(), 0)); + ASSERT_EQ(SizeA, 96u * 8u); +} + +TEST(HLSLLegacyCbufferTypeSize, ArrayTypes) { + constexpr char Code[] = R"hlsl( + namespace hlsl { + // built-in scalar data types: + + #ifdef __HLSL_ENABLE_16_BIT + // 16-bit integer. + typedef short int16_t; + #endif + + // 64-bit integer. + typedef long int64_t; + + // built-in vector data types: + + typedef vector<half, 2> half2; + typedef vector<half, 3> half3; + typedef vector<half, 4> half4; + + typedef vector<float, 2> float2; + typedef vector<float, 3> float3; + typedef vector<float, 4> float4; + typedef vector<double, 2> double2; + typedef vector<double, 3> double3; + typedef vector<double, 4> double4; + + } // namespace hlsl + + struct A { + double C0[2]; + float3 C1[3]; + float C2; + double C3[3]; + half C4; + double2 C5[1]; + float C6; + half3 C7[2]; + half3 C8; + }; + )hlsl"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, + /*Args=*/ + {"--driver-mode=dxc", "-T", "lib_6_3", + "-enable-16bit-types", "-nostdinc"}, + /* FileName*/ "input.hlsl"); + ASTContext &Ctx = AST->getASTContext(); + + auto Results = match(decl(cxxRecordDecl(hasName("A")).bind("A")), Ctx); + + // struct A + // { + + // double C0[2]; ; Offset: 0 + // float3 C1[3]; ; Offset: 32 + // float C2; ; Offset: 76 + // double C3[3]; ; Offset: 80 + // half C4; ; Offset: 120 + // double2 C5[1]; ; Offset: 128 + // float C6; ; Offset: 144 + // half3 C7[2]; ; Offset: 160 + // half3 C8; ; Offset: 182 + + // } A; ; Offset: 0 Size: 188 + auto const *A = selectFirst<RecordDecl>("A", Results); + unsigned SizeA = HLSLBufferDecl::calculateLegacyCbufferSize( + Ctx, QualType(A->getTypeForDecl(), 0)); + ASSERT_EQ(SizeA, 188u * 8u); +} + +TEST(HLSLLegacyCbufferTypeSize, Vec3) { + constexpr char Code[] = R"hlsl( + namespace hlsl { + // built-in scalar data types: + + #ifdef __HLSL_ENABLE_16_BIT + // 16-bit integer. + typedef short int16_t; + #endif + + // 64-bit integer. + typedef long int64_t; + + // built-in vector data types: + + typedef vector<half, 2> half2; + typedef vector<half, 3> half3; + typedef vector<half, 4> half4; + + typedef vector<float, 2> float2; + typedef vector<float, 3> float3; + typedef vector<float, 4> float4; + typedef vector<double, 2> double2; + typedef vector<double, 3> double3; + typedef vector<double, 4> double4; + + } // namespace hlsl + + struct A { + double3 D9[3]; + half3 D10; + }; + )hlsl"; + auto AST = + tooling::buildASTFromCodeWithArgs(Code, + /*Args=*/ + {"--driver-mode=dxc", "-T", "lib_6_3", + "-enable-16bit-types", "-nostdinc"}, + /* FileName*/ "input.hlsl"); + ASTContext &Ctx = AST->getASTContext(); + + auto Results = match(decl(cxxRecordDecl(hasName("A")).bind("A")), Ctx); + + // struct A + // { + // + // double3 D9[3]; ; Offset: 0 + // half3 D10; ; Offset: 88 + // + // } A; ; Offset: 0 Size: 94 + auto const *A = selectFirst<RecordDecl>("A", Results); + unsigned SizeA = HLSLBufferDecl::calculateLegacyCbufferSize( + Ctx, QualType(A->getTypeForDecl(), 0)); + ASSERT_EQ(SizeA, 94u * 8u); +} + +TEST(HLSLLegacyCbufferTypeSize, NestedStruct) { + constexpr char Code[] = R"hlsl( + namespace hlsl { + // built-in scalar data types: + + #ifdef __HLSL_ENABLE_16_BIT + // 16-bit integer. + typedef short int16_t; + #endif + + // 64-bit integer. + typedef long int64_t; + + // built-in vector data types: + + typedef vector<half, 2> half2; + typedef vector<half, 3> half3; + typedef vector<half, 4> half4; + + typedef vector<float, 2> float2; + typedef vector<float, 3> float3; + typedef vector<float, 4> float4; + typedef vector<double, 2> double2; + typedef vector<double, 3> double3; + typedef vector<double, 4> double4; + + } // namespace hlsl + + struct S0 + { + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; + }; + + struct S1 + { + float A0; + double A1; + float A2; + half A3; + int16_t A4; + int64_t A5; + int A6; + }; + + struct S2 + { + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; + }; + + struct S3 + { + S1 C0; + float C1[1]; + S2 C2[2]; + half C3; + }; + + struct A { + int E0; + S0 E1; + half E2; + S3 E3; + double E4; + }; + )hlsl"; + + auto AST = + tooling::buildASTFromCodeWithArgs(Code, + /*Args=*/ + {"--driver-mode=dxc", "-T", "lib_6_3", + "-enable-16bit-types", "-nostdinc"}, + /* FileName*/ "input.hlsl"); + ASTContext &Ctx = AST->getASTContext(); + + auto Results = match(decl(cxxRecordDecl(hasName("A")).bind("A")), Ctx); + + // struct E + // { + // int E0; ; Offset: 0 + // struct struct.S0 + // { + + // double B0; ; Offset: 16 + // float3 B1; ; Offset: 32 + // float B2; ; Offset: 44 + // double3 B3; ; Offset: 48 + // half B4; ; Offset: 72 + // double2 B5; ; Offset: 80 + // float B6; ; Offset: 96 + // half3 B7; ; Offset: 100 + // half3 B8; ; Offset: 106 + + // } E1; ; Offset: 16 + + // half E2; ; Offset: 112 + // struct struct.S3 + // { + + // struct struct.S1 + // { + + // float A0; ; Offset: 128 + // double A1; ; Offset: 136 + // float A2; ; Offset: 144 + // half A3; ; Offset: 148 + // int16_t A4; ; Offset: 150 + // int64_t A5; ; Offset: 152 + // int A6; ; Offset: 160 + + // } C0; ; Offset: 128 + + // float C1[1]; ; Offset: 176 + // struct struct.S2 + // { + + // double B0; ; Offset: 192 + // float3 B1; ; Offset: 208 + // float B2; ; Offset: 220 + // double3 B3; ; Offset: 224 + // half B4; ; Offset: 248 + // double2 B5; ; Offset: 256 + // float B6; ; Offset: 272 + // half3 B7; ; Offset: 276 + // half3 B8; ; Offset: 282 + + // } C2[2];; ; Offset: 192 + + // half C3; ; Offset: 384 + + // } E3; ; Offset: 128 + + // double E4; ; Offset: 392 + + // } E; ; Offset: 0 Size: 400 + + auto const *A = selectFirst<RecordDecl>("A", Results); + unsigned SizeA = HLSLBufferDecl::calculateLegacyCbufferSize( + Ctx, QualType(A->getTypeForDecl(), 0)); + ASSERT_EQ(SizeA, 400u * 8u); +} + +TEST(HLSLLegacyCbufferTypeSize, NestedStructDisable16bitTypes) { + constexpr char Code[] = R"hlsl( + namespace hlsl { + + // built-in vector data types: + + typedef vector<half, 2> half2; + typedef vector<half, 3> half3; + typedef vector<half, 4> half4; + + typedef vector<float, 2> float2; + typedef vector<float, 3> float3; + typedef vector<float, 4> float4; + typedef vector<double, 2> double2; + typedef vector<double, 3> double3; + typedef vector<double, 4> double4; + + } // namespace hlsl + + struct S0 + { + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; + }; + + struct S1 + { + float A0; + double A1; + float A2; + half A3; + half A4; + double A5; + int A6; + }; + + struct S2 + { + double B0; + float3 B1; + float B2; + double3 B3; + half B4; + double2 B5; + float B6; + half3 B7; + half3 B8; + }; + + struct S3 + { + S1 C0; + float C1[1]; + S2 C2[2]; + half C3; + }; + + struct A { + int E0; + S0 E1; + half E2; + S3 E3; + double E4; + }; + )hlsl"; + + auto AST = tooling::buildASTFromCodeWithArgs( + Code, + /*Args=*/{"--driver-mode=dxc", "-T", "lib_6_3", "-nostdinc"}, + /* FileName*/ "input.hlsl"); + ASTContext &Ctx = AST->getASTContext(); + + auto Results = match(decl(cxxRecordDecl(hasName("A")).bind("A")), Ctx); + + // struct E + // { + + // int E0; ; Offset: 0 + // struct struct.S0 + // { + + // double B0; ; Offset: 16 + // float3 B1; ; Offset: 32 + // float B2; ; Offset: 44 + // double3 B3; ; Offset: 48 + // float B4; ; Offset: 72 + // double2 B5; ; Offset: 80 + // float B6; ; Offset: 96 + // float3 B7; ; Offset: 100 + // float3 B8; ; Offset: 112 + + // } E1; ; Offset: 16 + + // float E2; ; Offset: 124 + // struct struct.S3 + // { + + // struct struct.S1 + // { + + // float A0; ; Offset: 128 + // double A1; ; Offset: 136 + // float A2; ; Offset: 144 + // float A3; ; Offset: 148 + // float A4; ; Offset: 152 + // double A5; ; Offset: 160 + // int A6; ; Offset: 168 + + // } C0; ; Offset: 128 + + // float C1[1]; ; Offset: 176 + // struct struct.S2 + // { + + // double B0; ; Offset: 192 + // float3 B1; ; Offset: 208 + // float B2; ; Offset: 220 + // double3 B3; ; Offset: 224 + // float B4; ; Offset: 248 + // double2 B5; ; Offset: 256 + // float B6; ; Offset: 272 + // float3 B7; ; Offset: 276 + // float3 B8; ; Offset: 288 + + // } C2[2];; ; Offset: 192 + + // float C3; ; Offset: 412 + + // } E3; ; Offset: 128 + + // double E4; ; Offset: 416 + + // } E; ; Offset: 0 Size: 424 + + auto const *A = selectFirst<RecordDecl>("A", Results); + unsigned SizeA = HLSLBufferDecl::calculateLegacyCbufferSize( + Ctx, QualType(A->getTypeForDecl(), 0)); + ASSERT_EQ(SizeA, 424u * 8u); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits