Skip to content

Commit 8e47a38

Browse files
author
Tim Foley
authoredJan 16, 2019
Improve handling of {} initializer list expressions (shader-slang#778)
Fixes shader-slang#775 It was reported (in shader-slang#775) that Slang doesn't handle initializer-list syntax when initializing matrix variables. When starting on a fix for that it became apparent that the time was right to fix two broad issues in the compiler's current handling of `{}`-enclosed initializer lists. The first issue was that the front-end checking of initializer lists wasn't handling the C-style behavior where an initializer list can either contain nested `{}`-enclosed lists for sub-arrays/-structures, or directly contain "leaf" values for initializing those aggregates. For example, the following two variable declarations ought to be equivalent: ```hlsl int4 a[] = { {1, 2, 3, 4}, {5, 6, 7, 8} }; int4 b[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; ``` Getting this distinction right is important because we want to support initializing a matrix either from a list of vectors for its rows, or a list of scalars for its elements (in row-major order). The front-end semantic checking logic for initializer lists was revamped so that it conceptually tries to "read" an expression of a desired type from the initializer list, and decides at each step whether to consume a single expression by coercing it to the desired type, or to recursively read multiple sub-values to construct the type as an aggregate. The logic for deciding between direct vs aggregate initialization could potentially use some tweaking, but luckily it should always handle the case where users introduce explicit `{}`-enclosed sub-lists to make their intention clear, so that existing Slang code should continue to work as before. The second issue was that initializers without the expected number of elements weren't implemented in code generation, so they would lead to internal compiler errors. This change revamps the codegen logic for initializer lists so that it can synthesize default values for fields/elements that were left out during initialization. This includes an attempt to support default initialization of `struct` fields based on explicitly written initialization expressions.
1 parent 86e11e0 commit 8e47a38

19 files changed

+797
-237
lines changed
 

‎source/slang/check.cpp

+425-204
Large diffs are not rendered by default.

‎source/slang/compiler.h

+4
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,10 @@ namespace Slang
655655
Type* elementType,
656656
IntVal* elementCount);
657657

658+
RefPtr<VectorExpressionType> getVectorType(
659+
RefPtr<Type> elementType,
660+
RefPtr<IntVal> elementCount);
661+
658662
SyntaxClass<RefObject> findSyntaxClass(Name* name);
659663

660664
Dictionary<Name*, SyntaxClass<RefObject> > mapNameToSyntaxClass;

‎source/slang/diagnostic-defs.h

+8
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,14 @@ DIAGNOSTIC(39999, Fatal, localVariableUsedBeforeDeclared, "local variable '$0' i
283283
// 304xx: generics
284284
DIAGNOSTIC(30400, Error, genericTypeNeedsArgs, "generic type '$0' used without argument")
285285

286+
// 305xx: initializer lists
287+
DIAGNOSTIC(30500, Error, tooManyInitializers, "too many initializers (expected $0, got $1)");
288+
DIAGNOSTIC(30501, Error, cannotUseInitializerListForArrayOfUnknownSize, "cannot use initializer list for array of statically unknown size '$0'");
289+
DIAGNOSTIC(30502, Error, cannotUseInitializerListForVectorOfUnknownSize, "cannot use initializer list for vector of statically unknown size '$0'");
290+
DIAGNOSTIC(30503, Error, cannotUseInitializerListForMatrixOfUnknownSize, "cannot use initializer list for matrix of statically unknown size '$0' rows");
291+
292+
293+
286294
DIAGNOSTIC(39999, Error, expectedIntegerConstantWrongType, "expected integer constant (found: '$0')")
287295
DIAGNOSTIC(39999, Error, expectedIntegerConstantNotConstant, "expression does not evaluate to a compile-time constant")
288296
DIAGNOSTIC(39999, Error, expectedIntegerConstantNotLiteral, "could not extract value from integer constant")

‎source/slang/diagnostics.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ void printDiagnosticArg(StringBuilder& sb, Type* type)
6161
sb << type->ToString();
6262
}
6363

64+
void printDiagnosticArg(StringBuilder& sb, Val* val)
65+
{
66+
sb << val->ToString();
67+
}
68+
6469
void printDiagnosticArg(StringBuilder& sb, TypeExp const& type)
6570
{
6671
sb << type.type->ToString();

‎source/slang/diagnostics.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@ namespace Slang
6969

7070
class Name;
7171
class Decl;
72+
struct QualType;
7273
class Type;
7374
struct TypeExp;
74-
struct QualType;
75+
class Val;
7576

7677
enum class CodeGenTarget;
7778
enum class Stage : SlangStage;
@@ -92,6 +93,7 @@ namespace Slang
9293
void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val);
9394
void printDiagnosticArg(StringBuilder& sb, Stage val);
9495
void printDiagnosticArg(StringBuilder& sb, ProfileVersion val);
96+
void printDiagnosticArg(StringBuilder& sb, Val* val);
9597

9698
template<typename T>
9799
void printDiagnosticArg(StringBuilder& sb, RefPtr<T> ptr)

‎source/slang/emit.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3581,7 +3581,7 @@ struct EmitVisitor
35813581

35823582
case kIROp_Construct:
35833583
case kIROp_makeVector:
3584-
case kIROp_makeMatrix:
3584+
case kIROp_MakeMatrix:
35853585
// Simple constructor call
35863586
if( inst->getOperandCount() == 1 && getTarget(ctx) == CodeGenTarget::HLSL)
35873587
{

‎source/slang/ir-constexpr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ bool opCanBeConstExpr(IROp op)
7878
case kIROp_Construct:
7979
case kIROp_makeVector:
8080
case kIROp_makeArray:
81-
case kIROp_makeMatrix:
81+
case kIROp_MakeMatrix:
8282
// TODO: more cases
8383
return true;
8484

‎source/slang/ir-inst-defs.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ INST(BindGlobalGenericParam, bind_global_generic_param, 2, 0)
192192
INST(Construct, construct, 0, 0)
193193

194194
INST(makeVector, makeVector, 0, 0)
195-
INST(makeMatrix, makeMatrix, 0, 0)
195+
INST(MakeMatrix, makeMatrix, 0, 0)
196196
INST(makeArray, makeArray, 0, 0)
197197
INST(makeStruct, makeStruct, 0, 0)
198198

‎source/slang/ir-insts.h

+5
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,11 @@ struct IRBuilder
816816
UInt argCount,
817817
IRInst* const* args);
818818

819+
IRInst* emitMakeMatrix(
820+
IRType* type,
821+
UInt argCount,
822+
IRInst* const* args);
823+
819824
IRInst* emitMakeArray(
820825
IRType* type,
821826
UInt argCount,

‎source/slang/ir.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,14 @@ namespace Slang
19111911
return emitIntrinsicInst(type, kIROp_makeVector, argCount, args);
19121912
}
19131913

1914+
IRInst* IRBuilder::emitMakeMatrix(
1915+
IRType* type,
1916+
UInt argCount,
1917+
IRInst* const* args)
1918+
{
1919+
return emitIntrinsicInst(type, kIROp_MakeMatrix, argCount, args);
1920+
}
1921+
19141922
IRInst* IRBuilder::emitMakeArray(
19151923
IRType* type,
19161924
UInt argCount,
@@ -3855,7 +3863,7 @@ namespace Slang
38553863
case kIROp_lookup_interface_method:
38563864
case kIROp_Construct:
38573865
case kIROp_makeVector:
3858-
case kIROp_makeMatrix:
3866+
case kIROp_MakeMatrix:
38593867
case kIROp_makeArray:
38603868
case kIROp_makeStruct:
38613869
case kIROp_Load: // We are ignoring the possibility of loads from bad addresses, or `volatile` loads

‎source/slang/lower-to-ir.cpp

+180-28
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,127 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
16571657
return lowerSubExpr(expr->base);
16581658
}
16591659

1660+
LoweredValInfo getSimpleDefaultVal(IRType* type)
1661+
{
1662+
if(auto basicType = as<IRBasicType>(type))
1663+
{
1664+
switch( basicType->getBaseType() )
1665+
{
1666+
default:
1667+
SLANG_UNEXPECTED("missing case for getting IR default value");
1668+
UNREACHABLE_RETURN(LoweredValInfo());
1669+
break;
1670+
1671+
case BaseType::Bool:
1672+
case BaseType::Int8:
1673+
case BaseType::Int16:
1674+
case BaseType::Int:
1675+
case BaseType::Int64:
1676+
case BaseType::UInt8:
1677+
case BaseType::UInt16:
1678+
case BaseType::UInt:
1679+
case BaseType::UInt64:
1680+
return LoweredValInfo::simple(getBuilder()->getIntValue(type, 0));
1681+
1682+
case BaseType::Half:
1683+
case BaseType::Float:
1684+
case BaseType::Double:
1685+
return LoweredValInfo::simple(getBuilder()->getFloatValue(type, 0.0));
1686+
}
1687+
}
1688+
1689+
SLANG_UNEXPECTED("missing case for getting IR default value");
1690+
UNREACHABLE_RETURN(LoweredValInfo());
1691+
}
1692+
1693+
LoweredValInfo getDefaultVal(Type* type)
1694+
{
1695+
auto irType = lowerType(context, type);
1696+
if (auto basicType = type->As<BasicExpressionType>())
1697+
{
1698+
return getSimpleDefaultVal(irType);
1699+
}
1700+
else if (auto vectorType = type->As<VectorExpressionType>())
1701+
{
1702+
UInt elementCount = (UInt) GetIntVal(vectorType->elementCount);
1703+
1704+
auto irDefaultValue = getSimpleVal(context, getDefaultVal(vectorType->elementType));
1705+
1706+
List<IRInst*> args;
1707+
for(UInt ee = 0; ee < elementCount; ++ee)
1708+
{
1709+
args.Add(irDefaultValue);
1710+
}
1711+
return LoweredValInfo::simple(
1712+
getBuilder()->emitMakeVector(irType, args.Count(), args.Buffer()));
1713+
}
1714+
else if (auto matrixType = type->As<MatrixExpressionType>())
1715+
{
1716+
UInt rowCount = (UInt) GetIntVal(matrixType->getRowCount());
1717+
1718+
auto rowType = matrixType->getRowType();
1719+
1720+
auto irDefaultValue = getSimpleVal(context, getDefaultVal(rowType));
1721+
1722+
List<IRInst*> args;
1723+
for(UInt rr = 0; rr < rowCount; ++rr)
1724+
{
1725+
args.Add(irDefaultValue);
1726+
}
1727+
return LoweredValInfo::simple(
1728+
getBuilder()->emitMakeMatrix(irType, args.Count(), args.Buffer()));
1729+
}
1730+
else if (auto arrayType = type->As<ArrayExpressionType>())
1731+
{
1732+
UInt elementCount = (UInt) GetIntVal(arrayType->ArrayLength);
1733+
1734+
auto irDefaultElement = getSimpleVal(context, getDefaultVal(arrayType->baseType));
1735+
1736+
List<IRInst*> args;
1737+
for(UInt ee = 0; ee < elementCount; ++ee)
1738+
{
1739+
args.Add(irDefaultElement);
1740+
}
1741+
1742+
return LoweredValInfo::simple(
1743+
getBuilder()->emitMakeArray(irType, args.Count(), args.Buffer()));
1744+
}
1745+
else if (auto declRefType = type->As<DeclRefType>())
1746+
{
1747+
DeclRef<Decl> declRef = declRefType->declRef;
1748+
if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>())
1749+
{
1750+
List<IRInst*> args;
1751+
for (auto ff : getMembersOfType<StructField>(aggTypeDeclRef))
1752+
{
1753+
if (ff.getDecl()->HasModifier<HLSLStaticModifier>())
1754+
continue;
1755+
1756+
auto irFieldVal = getSimpleVal(context, getDefaultVal(ff));
1757+
args.Add(irFieldVal);
1758+
}
1759+
1760+
return LoweredValInfo::simple(
1761+
getBuilder()->emitMakeStruct(irType, args.Count(), args.Buffer()));
1762+
}
1763+
}
1764+
1765+
SLANG_UNEXPECTED("unexpected type when creating default value");
1766+
UNREACHABLE_RETURN(LoweredValInfo());
1767+
}
1768+
1769+
LoweredValInfo getDefaultVal(StructField* decl)
1770+
{
1771+
if(auto initExpr = decl->initExpr)
1772+
{
1773+
return lowerRValueExpr(context, initExpr);
1774+
}
1775+
else
1776+
{
1777+
return getDefaultVal(decl->type);
1778+
}
1779+
}
1780+
16601781
LoweredValInfo visitInitializerListExpr(InitializerListExpr* expr)
16611782
{
16621783
// Allocate a temporary of the given type
@@ -1666,23 +1787,33 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
16661787

16671788
UInt argCount = expr->args.Count();
16681789

1790+
// If the initializer list was empty, then the user was
1791+
// asking for default initialization, which should apply
1792+
// to (almost) any type.
1793+
//
1794+
if(argCount == 0)
1795+
{
1796+
return getDefaultVal(type.type);
1797+
}
1798+
16691799
// Now for each argument in the initializer list,
16701800
// fill in the appropriate field of the result
16711801
if (auto arrayType = type->As<ArrayExpressionType>())
16721802
{
16731803
UInt elementCount = (UInt) GetIntVal(arrayType->ArrayLength);
16741804

1675-
for (UInt ee = 0; ee < elementCount; ++ee)
1805+
for (UInt ee = 0; ee < argCount; ++ee)
16761806
{
1677-
if (ee < argCount)
1678-
{
1679-
auto argExpr = expr->args[ee];
1680-
LoweredValInfo argVal = lowerRValueExpr(context, argExpr);
1681-
args.Add(getSimpleVal(context, argVal));
1682-
}
1683-
else
1807+
auto argExpr = expr->args[ee];
1808+
LoweredValInfo argVal = lowerRValueExpr(context, argExpr);
1809+
args.Add(getSimpleVal(context, argVal));
1810+
}
1811+
if(elementCount > argCount)
1812+
{
1813+
auto irDefaultValue = getSimpleVal(context, getDefaultVal(arrayType->baseType));
1814+
for(UInt ee = argCount; ee < elementCount; ++ee)
16841815
{
1685-
SLANG_UNEXPECTED("need to default-initialize array elements");
1816+
args.Add(irDefaultValue);
16861817
}
16871818
}
16881819

@@ -1692,25 +1823,48 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
16921823
else if (auto vectorType = type->As<VectorExpressionType>())
16931824
{
16941825
UInt elementCount = (UInt) GetIntVal(vectorType->elementCount);
1695-
UInt argCounter = 0;
16961826

1697-
for (UInt ee = 0; ee < elementCount; ++ee)
1827+
for (UInt ee = 0; ee < argCount; ++ee)
16981828
{
1699-
UInt argIndex = argCounter++;
1700-
if (argIndex < argCount)
1829+
auto argExpr = expr->args[ee];
1830+
LoweredValInfo argVal = lowerRValueExpr(context, argExpr);
1831+
args.Add(getSimpleVal(context, argVal));
1832+
}
1833+
if(elementCount > argCount)
1834+
{
1835+
auto irDefaultValue = getSimpleVal(context, getDefaultVal(vectorType->elementType));
1836+
for(UInt ee = argCount; ee < elementCount; ++ee)
17011837
{
1702-
auto argExpr = expr->args[argIndex];
1703-
LoweredValInfo argVal = lowerRValueExpr(context, argExpr);
1704-
args.Add(getSimpleVal(context, argVal));
1838+
args.Add(irDefaultValue);
17051839
}
1706-
else
1840+
}
1841+
1842+
return LoweredValInfo::simple(
1843+
getBuilder()->emitMakeVector(irType, args.Count(), args.Buffer()));
1844+
}
1845+
else if (auto matrixType = type->As<MatrixExpressionType>())
1846+
{
1847+
UInt rowCount = (UInt) GetIntVal(matrixType->getRowCount());
1848+
1849+
for (UInt rr = 0; rr < argCount; ++rr)
1850+
{
1851+
auto argExpr = expr->args[rr];
1852+
LoweredValInfo argVal = lowerRValueExpr(context, argExpr);
1853+
args.Add(getSimpleVal(context, argVal));
1854+
}
1855+
if(rowCount > argCount)
1856+
{
1857+
auto rowType = matrixType->getRowType();
1858+
auto irDefaultValue = getSimpleVal(context, getDefaultVal(rowType));
1859+
1860+
for(UInt rr = argCount; rr < rowCount; ++rr)
17071861
{
1708-
SLANG_UNEXPECTED("need to default-initialize vector elements");
1862+
args.Add(irDefaultValue);
17091863
}
17101864
}
17111865

17121866
return LoweredValInfo::simple(
1713-
getBuilder()->emitMakeVector(irType, args.Count(), args.Buffer()));
1867+
getBuilder()->emitMakeMatrix(irType, args.Count(), args.Buffer()));
17141868
}
17151869
else if (auto declRefType = type->As<DeclRefType>())
17161870
{
@@ -1732,23 +1886,21 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
17321886
}
17331887
else
17341888
{
1735-
SLANG_UNEXPECTED("need to default-initialize struct fields");
1889+
auto irDefaultValue = getSimpleVal(context, getDefaultVal(ff));
1890+
args.Add(irDefaultValue);
17361891
}
17371892
}
17381893

17391894
return LoweredValInfo::simple(
17401895
getBuilder()->emitMakeStruct(irType, args.Count(), args.Buffer()));
17411896
}
1742-
else
1743-
{
1744-
SLANG_UNEXPECTED("not sure how to initialize this type");
1745-
}
1746-
}
1747-
else
1748-
{
1749-
SLANG_UNEXPECTED("not sure how to initialize this type");
17501897
}
17511898

1899+
// If none of the above cases matched, then we had better
1900+
// have zero arguments in the initailizer list, in which
1901+
// case we are just looking for default initialization.
1902+
//
1903+
SLANG_UNEXPECTED("unhandled case for initializer list codegen");
17521904
UNREACHABLE_RETURN(LoweredValInfo());
17531905
}
17541906

0 commit comments

Comments
 (0)