Skip to content

Commit 69450a2

Browse files
Initial sizeof/alignof implementation. (shader-slang#2954)
* Initial sizeof implementation. * Small macro improvement. * Fix some typos. * Refactor NaturalSize. Add more sizeof tests. * Use _makeParseExpr to add sizeof support. * Add size-of.slang diagnostic result. * Fix typo in folding with macro change. * Add a sizeof test of This. * Some more NaturalSize coverage. * Simple alignof support. * Testing for alignof. * Added 8 bit enum to check enums values are correctly sized. * Add alignof to completion. * Lower sizeof/alignof to IR. sizeof/alignof IR pass. Tests for simple generic scenarios. * Make append handle invalid properly. Improve comments. --------- Co-authored-by: Theresa Foley <10618364+tangent-vector@users.noreply.github.com>
1 parent f9b73ea commit 69450a2

37 files changed

+1280
-64
lines changed

build/visual-studio/slang/slang.vcxproj

+4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
317317
<ClInclude Include="..\..\..\source\slang\slang-ast-expr.h" />
318318
<ClInclude Include="..\..\..\source\slang\slang-ast-iterator.h" />
319319
<ClInclude Include="..\..\..\source\slang\slang-ast-modifier.h" />
320+
<ClInclude Include="..\..\..\source\slang\slang-ast-natural-layout.h" />
320321
<ClInclude Include="..\..\..\source\slang\slang-ast-print.h" />
321322
<ClInclude Include="..\..\..\source\slang\slang-ast-reflect.h" />
322323
<ClInclude Include="..\..\..\source\slang\slang-ast-stmt.h" />
@@ -415,6 +416,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
415416
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-optional-type.h" />
416417
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-reinterpret.h" />
417418
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-result-type.h" />
419+
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-size-of.h" />
418420
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-tuple-types.h" />
419421
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-witness-lookup.h" />
420422
<ClInclude Include="..\..\..\source\slang\slang-ir-marshal-native-call.h" />
@@ -512,6 +514,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
512514
<ClCompile Include="..\..\..\source\slang\slang-ast-decl.cpp" />
513515
<ClCompile Include="..\..\..\source\slang\slang-ast-dump.cpp" />
514516
<ClCompile Include="..\..\..\source\slang\slang-ast-modifier.cpp" />
517+
<ClCompile Include="..\..\..\source\slang\slang-ast-natural-layout.cpp" />
515518
<ClCompile Include="..\..\..\source\slang\slang-ast-print.cpp" />
516519
<ClCompile Include="..\..\..\source\slang\slang-ast-reflect.cpp" />
517520
<ClCompile Include="..\..\..\source\slang\slang-ast-substitutions.cpp" />
@@ -614,6 +617,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla
614617
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-optional-type.cpp" />
615618
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-reinterpret.cpp" />
616619
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-result-type.cpp" />
620+
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-size-of.cpp" />
617621
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-tuple-types.cpp" />
618622
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-witness-lookup.cpp" />
619623
<ClCompile Include="..\..\..\source\slang\slang-ir-marshal-native-call.cpp" />

build/visual-studio/slang/slang.vcxproj.filters

+12
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
<ClInclude Include="..\..\..\source\slang\slang-ast-modifier.h">
4040
<Filter>Header Files</Filter>
4141
</ClInclude>
42+
<ClInclude Include="..\..\..\source\slang\slang-ast-natural-layout.h">
43+
<Filter>Header Files</Filter>
44+
</ClInclude>
4245
<ClInclude Include="..\..\..\source\slang\slang-ast-print.h">
4346
<Filter>Header Files</Filter>
4447
</ClInclude>
@@ -333,6 +336,9 @@
333336
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-result-type.h">
334337
<Filter>Header Files</Filter>
335338
</ClInclude>
339+
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-size-of.h">
340+
<Filter>Header Files</Filter>
341+
</ClInclude>
336342
<ClInclude Include="..\..\..\source\slang\slang-ir-lower-tuple-types.h">
337343
<Filter>Header Files</Filter>
338344
</ClInclude>
@@ -620,6 +626,9 @@
620626
<ClCompile Include="..\..\..\source\slang\slang-ast-modifier.cpp">
621627
<Filter>Source Files</Filter>
622628
</ClCompile>
629+
<ClCompile Include="..\..\..\source\slang\slang-ast-natural-layout.cpp">
630+
<Filter>Source Files</Filter>
631+
</ClCompile>
623632
<ClCompile Include="..\..\..\source\slang\slang-ast-print.cpp">
624633
<Filter>Source Files</Filter>
625634
</ClCompile>
@@ -926,6 +935,9 @@
926935
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-result-type.cpp">
927936
<Filter>Source Files</Filter>
928937
</ClCompile>
938+
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-size-of.cpp">
939+
<Filter>Source Files</Filter>
940+
</ClCompile>
929941
<ClCompile Include="..\..\..\source\slang\slang-ir-lower-tuple-types.cpp">
930942
<Filter>Source Files</Filter>
931943
</ClCompile>

source/slang/slang-ast-expr.h

+21
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,27 @@ class AsTypeExpr : public Expr
376376

377377
};
378378

379+
class SizeOfLikeExpr : public Expr
380+
{
381+
SLANG_AST_CLASS(SizeOfLikeExpr);
382+
383+
// Set during the parse, could be an expression, a variable or a type
384+
Expr* value = nullptr;
385+
386+
// The type the size/alignment needs to operate on. Set during traversal of SemanticsExprVisitor
387+
Type* sizedType = nullptr;
388+
};
389+
390+
class SizeOfExpr : public SizeOfLikeExpr
391+
{
392+
SLANG_AST_CLASS(SizeOfExpr);
393+
};
394+
395+
class AlignOfExpr : public SizeOfLikeExpr
396+
{
397+
SLANG_AST_CLASS(AlignOfExpr);
398+
};
399+
379400
class MakeOptionalExpr : public Expr
380401
{
381402
SLANG_AST_CLASS(MakeOptionalExpr)
+249
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// slang-ast-natural-layout.cpp
2+
#include "slang-ast-natural-layout.h"
3+
4+
#include "slang-ast-builder.h"
5+
6+
// For BaseInfo
7+
#include "slang-compiler.h"
8+
9+
namespace Slang
10+
{
11+
12+
/* !!!!!!!!!!!!!!!!!!!!!!!!! NaturalSize !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
13+
14+
15+
NaturalSize NaturalSize::operator*(Count count) const
16+
{
17+
// If the count is < 0 or the size is invalid, the result is invalid
18+
if (isInvalid() || count < 0)
19+
{
20+
return makeInvalid();
21+
}
22+
23+
if (count <= 0)
24+
{
25+
// If the count is 0, in effect the result doesn't take up any space
26+
return makeEmpty();
27+
}
28+
else
29+
{
30+
// We don't want to produce an aligned size, as we allow the last element to not
31+
// take up a whole stride (only up to size)
32+
return make(size + (getStride() * (count - 1)), alignment);
33+
}
34+
}
35+
36+
/* static */NaturalSize NaturalSize::makeFromBaseType(BaseType baseType)
37+
{
38+
// Special case void
39+
if (baseType == BaseType::Void)
40+
{
41+
return makeEmpty();
42+
}
43+
else
44+
{
45+
// In "natural" layout the alignment of a base type is always the same
46+
// as the size of the type itself
47+
auto info = BaseTypeInfo::getInfo(baseType);
48+
return make(info.sizeInBytes, info.sizeInBytes);
49+
}
50+
}
51+
52+
/* static */NaturalSize NaturalSize::calcUnion(NaturalSize a, NaturalSize b)
53+
{
54+
const auto alignment = maxAlignment(a.alignment, b.alignment);
55+
Count size = (alignment == kInvalidAlignment) ? 0 : Math::Max(a.size, b.size);
56+
return make(size, alignment);
57+
}
58+
59+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTNaturalLayoutContext !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
60+
61+
ASTNaturalLayoutContext::ASTNaturalLayoutContext(ASTBuilder* astBuilder, DiagnosticSink* sink):
62+
m_astBuilder(astBuilder),
63+
m_sink(sink)
64+
{
65+
// A null type always maps to invalid
66+
m_typeToSize.add(nullptr, NaturalSize::makeInvalid());
67+
}
68+
69+
Count ASTNaturalLayoutContext::_getCount(IntVal* intVal)
70+
{
71+
if (auto constIntVal = as<ConstantIntVal>(intVal))
72+
{
73+
if (constIntVal->value >= 0)
74+
{
75+
return Count(constIntVal->value);
76+
}
77+
}
78+
79+
if (m_sink)
80+
{
81+
// Could output an error
82+
}
83+
84+
return -1;
85+
}
86+
87+
NaturalSize ASTNaturalLayoutContext::calcSize(Type* type)
88+
{
89+
if (auto sizePtr = m_typeToSize.tryGetValue(type))
90+
{
91+
return *sizePtr;
92+
}
93+
94+
// Calc the size
95+
const NaturalSize size = _calcSizeImpl(type);
96+
97+
// We want to add to the cache, but we need to special case
98+
// in case there is an aggregate type that `poisoned` the cache entry, to stop infinite recursion.
99+
//
100+
// A requirement is that when the agg type completes it must set the cache entry, and return the same result.
101+
if (auto foundSize = m_typeToSize.tryGetValueOrAdd(type, size))
102+
{
103+
// If there is a found size, it must match. If not we update the state as invalid.
104+
if (*foundSize != size)
105+
{
106+
*foundSize = NaturalSize::makeInvalid();
107+
return *foundSize;
108+
}
109+
}
110+
111+
return size;
112+
}
113+
114+
NaturalSize ASTNaturalLayoutContext::_calcSizeImpl(Type* type)
115+
{
116+
if (VectorExpressionType* vecType = as<VectorExpressionType>(type))
117+
{
118+
const Count elementCount = _getCount(vecType->elementCount);
119+
return (elementCount > 0) ?
120+
calcSize(vecType->elementType) * elementCount :
121+
NaturalSize::makeInvalid();
122+
}
123+
else if (auto matType = as<MatrixExpressionType>(type))
124+
{
125+
const Count colCount = _getCount(matType->getColumnCount());
126+
const Count rowCount = _getCount(matType->getRowCount());
127+
return (colCount > 0 && rowCount > 0) ?
128+
calcSize(matType->getElementType()) * (colCount * rowCount) :
129+
NaturalSize::makeInvalid();
130+
}
131+
else if (auto basicType = as<BasicExpressionType>(type))
132+
{
133+
return NaturalSize::makeFromBaseType(basicType->baseType);
134+
}
135+
else if (as<PtrTypeBase>(type) || as<NullPtrType>(type))
136+
{
137+
// We assume 64 bits/8 bytes across the board
138+
return NaturalSize::makeFromBaseType(BaseType::UInt64);
139+
}
140+
else if (auto arrayType = as<ArrayExpressionType>(type))
141+
{
142+
const Count elementCount = _getCount(arrayType->getElementCount());
143+
return (elementCount > 0) ?
144+
calcSize(arrayType->getElementType()) * elementCount :
145+
NaturalSize::makeInvalid();
146+
}
147+
else if (auto namedType = as<NamedExpressionType>(type))
148+
{
149+
return calcSize(namedType->innerType);
150+
}
151+
else if (const auto tupleType = as<TupleType>(type))
152+
{
153+
// Initialize empty
154+
NaturalSize size = NaturalSize::makeEmpty();
155+
156+
// Accumulate over all the member types
157+
for (auto cur : tupleType->memberTypes)
158+
{
159+
const auto curSize = calcSize(cur);
160+
if (!curSize)
161+
{
162+
return NaturalSize::makeInvalid();
163+
}
164+
size.append(curSize);
165+
}
166+
167+
return size;
168+
}
169+
else if (const auto taggedUnion = as<TaggedUnionType>(type))
170+
{
171+
NaturalSize size = NaturalSize::makeInvalid();
172+
173+
for( auto caseType : taggedUnion->caseTypes )
174+
{
175+
const NaturalSize caseSize = calcSize(caseType);
176+
if (!caseSize)
177+
{
178+
return NaturalSize::makeInvalid();
179+
}
180+
size = NaturalSize::calcUnion(size, caseSize);
181+
}
182+
183+
// After we've computed the size required to hold all the
184+
// case types, we will allocate space for the tag field.
185+
186+
// Currently we assume uint32_t on all targets
187+
size.append(NaturalSize::makeFromBaseType(BaseType::UInt));
188+
189+
return size;
190+
}
191+
else if( auto declRefType = as<DeclRefType>(type) )
192+
{
193+
if (const auto enumDeclRef = declRefType->declRef.as<EnumDecl>())
194+
{
195+
Type* tagType = getTagType(m_astBuilder, enumDeclRef);
196+
return calcSize(tagType);
197+
}
198+
else if(const auto structDeclRef = declRefType->declRef.as<StructDecl>())
199+
{
200+
// Poison the cache whilst we construct
201+
m_typeToSize.add(type, NaturalSize::makeInvalid());
202+
203+
// Initialize empty
204+
NaturalSize size = NaturalSize::makeEmpty();
205+
206+
for (auto inherited : structDeclRef.getDecl()->getMembersOfType<InheritanceDecl>())
207+
{
208+
// Look for a struct type that it inherits from
209+
if (auto inheritedDeclRef = as<DeclRefType>(inherited->base.type))
210+
{
211+
if (auto parentDecl = inheritedDeclRef->declRef.as<StructDecl>())
212+
{
213+
// We can only inherit from one thing
214+
size = calcSize(inherited->base.type);
215+
if (!size)
216+
{
217+
return size;
218+
}
219+
break;
220+
}
221+
}
222+
}
223+
224+
// Accumulate over all of the fields
225+
for (auto field : structDeclRef.getDecl()->getFields())
226+
{
227+
const auto fieldSize = calcSize(field->getType());
228+
if (!fieldSize)
229+
{
230+
return NaturalSize::makeInvalid();
231+
}
232+
size.append(fieldSize);
233+
}
234+
235+
// Set the cached result to the size.
236+
m_typeToSize.set(type, size);
237+
238+
return size;
239+
}
240+
else if (const auto typeDef = declRefType->declRef.as<TypeDefDecl>())
241+
{
242+
return calcSize(typeDef.getDecl()->type);
243+
}
244+
}
245+
246+
return NaturalSize::makeInvalid();
247+
}
248+
249+
} // namespace Slang

0 commit comments

Comments
 (0)