Skip to content

Commit 2db8386

Browse files
authored
[HLSL] Implement default constant buffer $Globals (2nd attempt) (#128589)
All variable declarations in the global scope that are not resources, static or empty are implicitly added to implicit constant buffer `$Globals`. They are created in `hlsl_constant` address space and collected in an implicit `HLSLBufferDecl` node that is added to the AST at the end of the translation unit. Codegen is the same as for explicit constant buffers. Fixes #123801 This is a second attempt to implement this feature. The first attempt had to be reverted because of memory leaks. The problem was adding a `SmallVector` member on `HLSLBufferDecl` node to represent a list of default buffer declarations. When this vector needed to grow, it allocated memory that was never released, because all memory used by AST nodes must be allocated by `ASTContext` allocator and is released all at once. Destructors on AST nodes are never called. It this change the list of default buffer declarations is collected in a `SmallVector` instance on `SemaHLSL`. The `HLSLBufDecl` representing `$Globals` is created at the end of the translation unit when the number of declarations is known, and the list is copied into an array allocated by the `ASTContext` allocator.
1 parent cd4c30b commit 2db8386

File tree

10 files changed

+250
-40
lines changed

10 files changed

+250
-40
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5045,15 +5045,26 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
50455045
// LayoutStruct - Layout struct for the buffer
50465046
CXXRecordDecl *LayoutStruct;
50475047

5048+
// For default (implicit) constant buffer, an array of references of global
5049+
// decls that belong to the buffer. The decls are already parented by the
5050+
// translation unit context. The array is allocated by the ASTContext
5051+
// allocator in HLSLBufferDecl::CreateDefaultCBuffer.
5052+
ArrayRef<Decl *> DefaultBufferDecls;
5053+
50485054
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
50495055
IdentifierInfo *ID, SourceLocation IDLoc,
50505056
SourceLocation LBrace);
50515057

5058+
void setDefaultBufferDecls(ArrayRef<Decl *> Decls);
5059+
50525060
public:
50535061
static HLSLBufferDecl *Create(ASTContext &C, DeclContext *LexicalParent,
50545062
bool CBuffer, SourceLocation KwLoc,
50555063
IdentifierInfo *ID, SourceLocation IDLoc,
50565064
SourceLocation LBrace);
5065+
static HLSLBufferDecl *
5066+
CreateDefaultCBuffer(ASTContext &C, DeclContext *LexicalParent,
5067+
ArrayRef<Decl *> DefaultCBufferDecls);
50575068
static HLSLBufferDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
50585069

50595070
SourceRange getSourceRange() const override LLVM_READONLY {
@@ -5079,6 +5090,28 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext {
50795090
return static_cast<HLSLBufferDecl *>(const_cast<DeclContext *>(DC));
50805091
}
50815092

5093+
// Iterator for the buffer decls. For constant buffers explicitly declared
5094+
// with `cbuffer` keyword this will the list of decls parented by this
5095+
// HLSLBufferDecl (equal to `decls()`).
5096+
// For implicit $Globals buffer this will be the list of default buffer
5097+
// declarations stored in DefaultBufferDecls plus the implicit layout
5098+
// struct (the only child of HLSLBufferDecl in this case).
5099+
//
5100+
// The iterator uses llvm::concat_iterator to concatenate the lists
5101+
// `decls()` and `DefaultBufferDecls`. For non-default buffers
5102+
// `DefaultBufferDecls` is always empty.
5103+
using buffer_decl_iterator =
5104+
llvm::concat_iterator<Decl *const, SmallVector<Decl *>::const_iterator,
5105+
decl_iterator>;
5106+
using buffer_decl_range = llvm::iterator_range<buffer_decl_iterator>;
5107+
5108+
buffer_decl_range buffer_decls() const {
5109+
return buffer_decl_range(buffer_decls_begin(), buffer_decls_end());
5110+
}
5111+
buffer_decl_iterator buffer_decls_begin() const;
5112+
buffer_decl_iterator buffer_decls_end() const;
5113+
bool buffer_decls_empty();
5114+
50825115
friend class ASTDeclReader;
50835116
friend class ASTDeclWriter;
50845117
};

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,13 @@ class SemaHLSL : public SemaBase {
105105
HLSLParamModifierAttr::Spelling Spelling);
106106
void ActOnTopLevelFunction(FunctionDecl *FD);
107107
void ActOnVariableDeclarator(VarDecl *VD);
108+
void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
108109
void CheckEntryPoint(FunctionDecl *FD);
109110
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
110111
const HLSLAnnotationAttr *AnnotationAttr);
111112
void DiagnoseAttrStageMismatch(
112113
const Attr *A, llvm::Triple::EnvironmentType Stage,
113114
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
114-
void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU);
115115

116116
QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
117117
QualType LHSType, QualType RHSType,
@@ -168,11 +168,17 @@ class SemaHLSL : public SemaBase {
168168
// List of all resource bindings
169169
ResourceBindings Bindings;
170170

171+
// Global declaration collected for the $Globals default constant
172+
// buffer which will be created at the end of the translation unit.
173+
llvm::SmallVector<Decl *> DefaultCBufferDecls;
174+
171175
private:
172176
void collectResourceBindingsOnVarDecl(VarDecl *D);
173177
void collectResourceBindingsOnUserRecordDecl(const VarDecl *VD,
174178
const RecordType *RT);
175179
void processExplicitBindingsOnDecl(VarDecl *D);
180+
181+
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
176182
};
177183

178184
} // namespace clang

clang/lib/AST/Decl.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "llvm/ADT/SmallVector.h"
5858
#include "llvm/ADT/StringRef.h"
5959
#include "llvm/ADT/StringSwitch.h"
60+
#include "llvm/ADT/iterator_range.h"
6061
#include "llvm/Support/Casting.h"
6162
#include "llvm/Support/ErrorHandling.h"
6263
#include "llvm/Support/raw_ostream.h"
@@ -5745,6 +5746,18 @@ HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
57455746
return Result;
57465747
}
57475748

5749+
HLSLBufferDecl *
5750+
HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C, DeclContext *LexicalParent,
5751+
ArrayRef<Decl *> DefaultCBufferDecls) {
5752+
DeclContext *DC = LexicalParent;
5753+
IdentifierInfo *II = &C.Idents.get("$Globals", tok::TokenKind::identifier);
5754+
HLSLBufferDecl *Result = new (C, DC) HLSLBufferDecl(
5755+
DC, true, SourceLocation(), II, SourceLocation(), SourceLocation());
5756+
Result->setImplicit(true);
5757+
Result->setDefaultBufferDecls(DefaultCBufferDecls);
5758+
return Result;
5759+
}
5760+
57485761
HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C,
57495762
GlobalDeclID ID) {
57505763
return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr,
@@ -5757,6 +5770,36 @@ void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) {
57575770
addDecl(LS);
57585771
}
57595772

5773+
void HLSLBufferDecl::setDefaultBufferDecls(ArrayRef<Decl *> Decls) {
5774+
assert(!Decls.empty());
5775+
assert(DefaultBufferDecls.empty() && "default decls are already set");
5776+
assert(isImplicit() &&
5777+
"default decls can only be added to the implicit/default constant "
5778+
"buffer $Globals");
5779+
5780+
// allocate array for default decls with ASTContext allocator
5781+
Decl **DeclsArray = new (getASTContext()) Decl *[Decls.size()];
5782+
std::copy(Decls.begin(), Decls.end(), DeclsArray);
5783+
DefaultBufferDecls = ArrayRef<Decl *>(DeclsArray, Decls.size());
5784+
}
5785+
5786+
HLSLBufferDecl::buffer_decl_iterator
5787+
HLSLBufferDecl::buffer_decls_begin() const {
5788+
return buffer_decl_iterator(llvm::iterator_range(DefaultBufferDecls.begin(),
5789+
DefaultBufferDecls.end()),
5790+
decl_range(decls_begin(), decls_end()));
5791+
}
5792+
5793+
HLSLBufferDecl::buffer_decl_iterator HLSLBufferDecl::buffer_decls_end() const {
5794+
return buffer_decl_iterator(
5795+
llvm::iterator_range(DefaultBufferDecls.end(), DefaultBufferDecls.end()),
5796+
decl_range(decls_end(), decls_end()));
5797+
}
5798+
5799+
bool HLSLBufferDecl::buffer_decls_empty() {
5800+
return DefaultBufferDecls.empty() && decls_empty();
5801+
}
5802+
57605803
//===----------------------------------------------------------------------===//
57615804
// ImportDecl Implementation
57625805
//===----------------------------------------------------------------------===//

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
116116
BufGlobals.push_back(ValueAsMetadata::get(BufGV));
117117

118118
const auto *ElemIt = LayoutStruct->element_begin();
119-
for (Decl *D : BufDecl->decls()) {
119+
for (Decl *D : BufDecl->buffer_decls()) {
120120
if (isa<CXXRecordDecl, EmptyDecl>(D))
121121
// Nothing to do for this declaration.
122122
continue;

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5513,6 +5513,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
55135513
if (getLangOpts().OpenCL && ASTTy->isSamplerT())
55145514
return;
55155515

5516+
// HLSL default buffer constants will be emitted during HLSLBufferDecl codegen
5517+
if (getLangOpts().HLSL &&
5518+
D->getType().getAddressSpace() == LangAS::hlsl_constant)
5519+
return;
5520+
55165521
// If this is OpenMP device, check if it is legal to emit this global
55175522
// normally.
55185523
if (LangOpts.OpenMPIsTargetDevice && OpenMPRuntime &&

clang/lib/Sema/Sema.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,8 +1417,7 @@ void Sema::ActOnEndOfTranslationUnit() {
14171417
}
14181418

14191419
if (LangOpts.HLSL)
1420-
HLSL().DiagnoseAvailabilityViolations(
1421-
getASTContext().getTranslationUnitDecl());
1420+
HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
14221421

14231422
// If there were errors, disable 'unused' warnings since they will mostly be
14241423
// noise. Don't warn for a use from a module: either we should warn on all

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//===----------------------------------------------------------------------===//
1010

1111
#include "clang/Sema/SemaHLSL.h"
12+
#include "clang/AST/ASTConsumer.h"
1213
#include "clang/AST/ASTContext.h"
1314
#include "clang/AST/Attr.h"
1415
#include "clang/AST/Attrs.inc"
@@ -246,7 +247,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
246247
// or on none.
247248
bool HasPackOffset = false;
248249
bool HasNonPackOffset = false;
249-
for (auto *Field : BufDecl->decls()) {
250+
for (auto *Field : BufDecl->buffer_decls()) {
250251
VarDecl *Var = dyn_cast<VarDecl>(Field);
251252
if (!Var)
252253
continue;
@@ -513,7 +514,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
513514
LS->setImplicit(true);
514515
LS->startDefinition();
515516

516-
for (Decl *D : BufDecl->decls()) {
517+
for (Decl *D : BufDecl->buffer_decls()) {
517518
VarDecl *VD = dyn_cast<VarDecl>(D);
518519
if (!VD || VD->getStorageClass() == SC_Static ||
519520
VD->getType().getAddressSpace() == LangAS::hlsl_groupshared)
@@ -1949,7 +1950,22 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
19491950

19501951
} // namespace
19511952

1952-
void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
1953+
void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
1954+
// process default CBuffer - create buffer layout struct and invoke codegenCGH
1955+
if (!DefaultCBufferDecls.empty()) {
1956+
HLSLBufferDecl *DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
1957+
SemaRef.getASTContext(), SemaRef.getCurLexicalContext(),
1958+
DefaultCBufferDecls);
1959+
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
1960+
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
1961+
1962+
DeclGroupRef DG(DefaultCBuffer);
1963+
SemaRef.Consumer.HandleTopLevelDecl(DG);
1964+
}
1965+
diagnoseAvailabilityViolations(TU);
1966+
}
1967+
1968+
void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
19531969
// Skip running the diagnostics scan if the diagnostic mode is
19541970
// strict (-fhlsl-strict-availability) and the target shader stage is known
19551971
// because all relevant diagnostics were already emitted in the
@@ -3012,6 +3028,14 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
30123028
return Ty;
30133029
}
30143030

3031+
static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
3032+
QualType QT = VD->getType();
3033+
return VD->getDeclContext()->isTranslationUnit() &&
3034+
QT.getAddressSpace() == LangAS::Default &&
3035+
VD->getStorageClass() != SC_Static &&
3036+
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
3037+
}
3038+
30153039
void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
30163040
if (VD->hasGlobalStorage()) {
30173041
// make sure the declaration has a complete type
@@ -3023,7 +3047,18 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
30233047
return;
30243048
}
30253049

3026-
// find all resources on decl
3050+
// Global variables outside a cbuffer block that are not a resource, static,
3051+
// groupshared, or an empty array or struct belong to the default constant
3052+
// buffer $Globals (to be created at the end of the translation unit).
3053+
if (IsDefaultBufferConstantDecl(VD)) {
3054+
// update address space to hlsl_constant
3055+
QualType NewTy = getASTContext().getAddrSpaceQualType(
3056+
VD->getType(), LangAS::hlsl_constant);
3057+
VD->setType(NewTy);
3058+
DefaultCBufferDecls.push_back(VD);
3059+
}
3060+
3061+
// find all resources bindings on decl
30273062
if (VD->getType()->isHLSLIntangibleType())
30283063
collectResourceBindingsOnVarDecl(VD);
30293064

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump -o - %s | FileCheck %s
2+
3+
struct EmptyStruct {
4+
};
5+
6+
struct S {
7+
RWBuffer<float> buf;
8+
EmptyStruct es;
9+
float ea[0];
10+
float b;
11+
};
12+
13+
// CHECK: VarDecl {{.*}} used a 'hlsl_constant float'
14+
float a;
15+
16+
// CHECK: VarDecl {{.*}} b 'RWBuffer<float>':'hlsl::RWBuffer<float>'
17+
RWBuffer<float> b;
18+
19+
// CHECK: VarDecl {{.*}} c 'EmptyStruct'
20+
EmptyStruct c;
21+
22+
// CHECK: VarDecl {{.*}} d 'float[0]'
23+
float d[0];
24+
25+
// CHECK: VarDecl {{.*}} e 'RWBuffer<float>[2]'
26+
RWBuffer<float> e[2];
27+
28+
// CHECK: VarDecl {{.*}} f 'groupshared float'
29+
groupshared float f;
30+
31+
// CHECK: VarDecl {{.*}} g 'hlsl_constant float'
32+
float g;
33+
34+
// CHECK: VarDecl {{.*}} h 'hlsl_constant S'
35+
S h;
36+
37+
// CHECK: HLSLBufferDecl {{.*}} implicit cbuffer $Globals
38+
// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_$Globals definition
39+
// CHECK: PackedAttr
40+
// CHECK-NEXT: FieldDecl {{.*}} a 'float'
41+
// CHECK-NEXT: FieldDecl {{.*}} g 'float'
42+
// CHECK-NEXT: FieldDecl {{.*}} h '__cblayout_S'
43+
44+
// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_S definition
45+
// CHECK: PackedAttr {{.*}} Implicit
46+
// CHECK-NEXT: FieldDecl {{.*}} b 'float'
47+
48+
export float foo() {
49+
return a;
50+
}

clang/test/CodeGenHLSL/basic_types.hlsl

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,38 @@
66
// RUN: -emit-llvm -disable-llvm-passes -o - -DNAMESPACED| FileCheck %s
77

88

9-
// CHECK: @uint16_t_Val = global i16 0, align 2
10-
// CHECK: @int16_t_Val = global i16 0, align 2
11-
// CHECK: @uint_Val = global i32 0, align 4
12-
// CHECK: @uint64_t_Val = global i64 0, align 8
13-
// CHECK: @int64_t_Val = global i64 0, align 8
14-
// CHECK: @int16_t2_Val = global <2 x i16> zeroinitializer, align 4
15-
// CHECK: @int16_t3_Val = global <3 x i16> zeroinitializer, align 8
16-
// CHECK: @int16_t4_Val = global <4 x i16> zeroinitializer, align 8
17-
// CHECK: @uint16_t2_Val = global <2 x i16> zeroinitializer, align 4
18-
// CHECK: @uint16_t3_Val = global <3 x i16> zeroinitializer, align 8
19-
// CHECK: @uint16_t4_Val = global <4 x i16> zeroinitializer, align 8
20-
// CHECK: @int2_Val = global <2 x i32> zeroinitializer, align 8
21-
// CHECK: @int3_Val = global <3 x i32> zeroinitializer, align 16
22-
// CHECK: @int4_Val = global <4 x i32> zeroinitializer, align 16
23-
// CHECK: @uint2_Val = global <2 x i32> zeroinitializer, align 8
24-
// CHECK: @uint3_Val = global <3 x i32> zeroinitializer, align 16
25-
// CHECK: @uint4_Val = global <4 x i32> zeroinitializer, align 16
26-
// CHECK: @int64_t2_Val = global <2 x i64> zeroinitializer, align 16
27-
// CHECK: @int64_t3_Val = global <3 x i64> zeroinitializer, align 32
28-
// CHECK: @int64_t4_Val = global <4 x i64> zeroinitializer, align 32
29-
// CHECK: @uint64_t2_Val = global <2 x i64> zeroinitializer, align 16
30-
// CHECK: @uint64_t3_Val = global <3 x i64> zeroinitializer, align 32
31-
// CHECK: @uint64_t4_Val = global <4 x i64> zeroinitializer, align 32
32-
// CHECK: @half2_Val = global <2 x half> zeroinitializer, align 4
33-
// CHECK: @half3_Val = global <3 x half> zeroinitializer, align 8
34-
// CHECK: @half4_Val = global <4 x half> zeroinitializer, align 8
35-
// CHECK: @float2_Val = global <2 x float> zeroinitializer, align 8
36-
// CHECK: @float3_Val = global <3 x float> zeroinitializer, align 16
37-
// CHECK: @float4_Val = global <4 x float> zeroinitializer, align 16
38-
// CHECK: @double2_Val = global <2 x double> zeroinitializer, align 16
39-
// CHECK: @double3_Val = global <3 x double> zeroinitializer, align 32
40-
// CHECK: @double4_Val = global <4 x double> zeroinitializer, align 32
9+
// CHECK: @uint16_t_Val = external addrspace(2) global i16, align 2
10+
// CHECK: @int16_t_Val = external addrspace(2) global i16, align 2
11+
// CHECK: @uint_Val = external addrspace(2) global i32, align 4
12+
// CHECK: @uint64_t_Val = external addrspace(2) global i64, align 8
13+
// CHECK: @int64_t_Val = external addrspace(2) global i64, align 8
14+
// CHECK: @int16_t2_Val = external addrspace(2) global <2 x i16>, align 4
15+
// CHECK: @int16_t3_Val = external addrspace(2) global <3 x i16>, align 8
16+
// CHECK: @int16_t4_Val = external addrspace(2) global <4 x i16>, align 8
17+
// CHECK: @uint16_t2_Val = external addrspace(2) global <2 x i16>, align 4
18+
// CHECK: @uint16_t3_Val = external addrspace(2) global <3 x i16>, align 8
19+
// CHECK: @uint16_t4_Val = external addrspace(2) global <4 x i16>, align 8
20+
// CHECK: @int2_Val = external addrspace(2) global <2 x i32>, align 8
21+
// CHECK: @int3_Val = external addrspace(2) global <3 x i32>, align 16
22+
// CHECK: @int4_Val = external addrspace(2) global <4 x i32>, align 16
23+
// CHECK: @uint2_Val = external addrspace(2) global <2 x i32>, align 8
24+
// CHECK: @uint3_Val = external addrspace(2) global <3 x i32>, align 16
25+
// CHECK: @uint4_Val = external addrspace(2) global <4 x i32>, align 16
26+
// CHECK: @int64_t2_Val = external addrspace(2) global <2 x i64>, align 16
27+
// CHECK: @int64_t3_Val = external addrspace(2) global <3 x i64>, align 32
28+
// CHECK: @int64_t4_Val = external addrspace(2) global <4 x i64>, align 32
29+
// CHECK: @uint64_t2_Val = external addrspace(2) global <2 x i64>, align 16
30+
// CHECK: @uint64_t3_Val = external addrspace(2) global <3 x i64>, align 32
31+
// CHECK: @uint64_t4_Val = external addrspace(2) global <4 x i64>, align 32
32+
// CHECK: @half2_Val = external addrspace(2) global <2 x half>, align 4
33+
// CHECK: @half3_Val = external addrspace(2) global <3 x half>, align 8
34+
// CHECK: @half4_Val = external addrspace(2) global <4 x half>, align 8
35+
// CHECK: @float2_Val = external addrspace(2) global <2 x float>, align 8
36+
// CHECK: @float3_Val = external addrspace(2) global <3 x float>, align 16
37+
// CHECK: @float4_Val = external addrspace(2) global <4 x float>, align 16
38+
// CHECK: @double2_Val = external addrspace(2) global <2 x double>, align 16
39+
// CHECK: @double3_Val = external addrspace(2) global <3 x double>, align 32
40+
// CHECK: @double4_Val = external addrspace(2) global <4 x double>, align 32
4141

4242
#ifdef NAMESPACED
4343
#define TYPE_DECL(T) hlsl::T T##_Val

0 commit comments

Comments
 (0)