From 8e47a3802d4d74eb11620f147ef5b29b8e931d35 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 16 Jan 2019 11:50:14 -0800 Subject: Improve handling of {} initializer list expressions (#778) Fixes #775 It was reported (in #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. --- source/slang/lower-to-ir.cpp | 208 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 180 insertions(+), 28 deletions(-) (limited to 'source/slang/lower-to-ir.cpp') diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 388ca884e..b1bc63fa1 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -1657,6 +1657,127 @@ struct ExprLoweringVisitorBase : ExprVisitor return lowerSubExpr(expr->base); } + LoweredValInfo getSimpleDefaultVal(IRType* type) + { + if(auto basicType = as(type)) + { + switch( basicType->getBaseType() ) + { + default: + SLANG_UNEXPECTED("missing case for getting IR default value"); + UNREACHABLE_RETURN(LoweredValInfo()); + break; + + case BaseType::Bool: + case BaseType::Int8: + case BaseType::Int16: + case BaseType::Int: + case BaseType::Int64: + case BaseType::UInt8: + case BaseType::UInt16: + case BaseType::UInt: + case BaseType::UInt64: + return LoweredValInfo::simple(getBuilder()->getIntValue(type, 0)); + + case BaseType::Half: + case BaseType::Float: + case BaseType::Double: + return LoweredValInfo::simple(getBuilder()->getFloatValue(type, 0.0)); + } + } + + SLANG_UNEXPECTED("missing case for getting IR default value"); + UNREACHABLE_RETURN(LoweredValInfo()); + } + + LoweredValInfo getDefaultVal(Type* type) + { + auto irType = lowerType(context, type); + if (auto basicType = type->As()) + { + return getSimpleDefaultVal(irType); + } + else if (auto vectorType = type->As()) + { + UInt elementCount = (UInt) GetIntVal(vectorType->elementCount); + + auto irDefaultValue = getSimpleVal(context, getDefaultVal(vectorType->elementType)); + + List args; + for(UInt ee = 0; ee < elementCount; ++ee) + { + args.Add(irDefaultValue); + } + return LoweredValInfo::simple( + getBuilder()->emitMakeVector(irType, args.Count(), args.Buffer())); + } + else if (auto matrixType = type->As()) + { + UInt rowCount = (UInt) GetIntVal(matrixType->getRowCount()); + + auto rowType = matrixType->getRowType(); + + auto irDefaultValue = getSimpleVal(context, getDefaultVal(rowType)); + + List args; + for(UInt rr = 0; rr < rowCount; ++rr) + { + args.Add(irDefaultValue); + } + return LoweredValInfo::simple( + getBuilder()->emitMakeMatrix(irType, args.Count(), args.Buffer())); + } + else if (auto arrayType = type->As()) + { + UInt elementCount = (UInt) GetIntVal(arrayType->ArrayLength); + + auto irDefaultElement = getSimpleVal(context, getDefaultVal(arrayType->baseType)); + + List args; + for(UInt ee = 0; ee < elementCount; ++ee) + { + args.Add(irDefaultElement); + } + + return LoweredValInfo::simple( + getBuilder()->emitMakeArray(irType, args.Count(), args.Buffer())); + } + else if (auto declRefType = type->As()) + { + DeclRef declRef = declRefType->declRef; + if (auto aggTypeDeclRef = declRef.As()) + { + List args; + for (auto ff : getMembersOfType(aggTypeDeclRef)) + { + if (ff.getDecl()->HasModifier()) + continue; + + auto irFieldVal = getSimpleVal(context, getDefaultVal(ff)); + args.Add(irFieldVal); + } + + return LoweredValInfo::simple( + getBuilder()->emitMakeStruct(irType, args.Count(), args.Buffer())); + } + } + + SLANG_UNEXPECTED("unexpected type when creating default value"); + UNREACHABLE_RETURN(LoweredValInfo()); + } + + LoweredValInfo getDefaultVal(StructField* decl) + { + if(auto initExpr = decl->initExpr) + { + return lowerRValueExpr(context, initExpr); + } + else + { + return getDefaultVal(decl->type); + } + } + LoweredValInfo visitInitializerListExpr(InitializerListExpr* expr) { // Allocate a temporary of the given type @@ -1666,23 +1787,33 @@ struct ExprLoweringVisitorBase : ExprVisitor UInt argCount = expr->args.Count(); + // If the initializer list was empty, then the user was + // asking for default initialization, which should apply + // to (almost) any type. + // + if(argCount == 0) + { + return getDefaultVal(type.type); + } + // Now for each argument in the initializer list, // fill in the appropriate field of the result if (auto arrayType = type->As()) { UInt elementCount = (UInt) GetIntVal(arrayType->ArrayLength); - for (UInt ee = 0; ee < elementCount; ++ee) + for (UInt ee = 0; ee < argCount; ++ee) { - if (ee < argCount) - { - auto argExpr = expr->args[ee]; - LoweredValInfo argVal = lowerRValueExpr(context, argExpr); - args.Add(getSimpleVal(context, argVal)); - } - else + auto argExpr = expr->args[ee]; + LoweredValInfo argVal = lowerRValueExpr(context, argExpr); + args.Add(getSimpleVal(context, argVal)); + } + if(elementCount > argCount) + { + auto irDefaultValue = getSimpleVal(context, getDefaultVal(arrayType->baseType)); + for(UInt ee = argCount; ee < elementCount; ++ee) { - SLANG_UNEXPECTED("need to default-initialize array elements"); + args.Add(irDefaultValue); } } @@ -1692,25 +1823,48 @@ struct ExprLoweringVisitorBase : ExprVisitor else if (auto vectorType = type->As()) { UInt elementCount = (UInt) GetIntVal(vectorType->elementCount); - UInt argCounter = 0; - for (UInt ee = 0; ee < elementCount; ++ee) + for (UInt ee = 0; ee < argCount; ++ee) { - UInt argIndex = argCounter++; - if (argIndex < argCount) + auto argExpr = expr->args[ee]; + LoweredValInfo argVal = lowerRValueExpr(context, argExpr); + args.Add(getSimpleVal(context, argVal)); + } + if(elementCount > argCount) + { + auto irDefaultValue = getSimpleVal(context, getDefaultVal(vectorType->elementType)); + for(UInt ee = argCount; ee < elementCount; ++ee) { - auto argExpr = expr->args[argIndex]; - LoweredValInfo argVal = lowerRValueExpr(context, argExpr); - args.Add(getSimpleVal(context, argVal)); + args.Add(irDefaultValue); } - else + } + + return LoweredValInfo::simple( + getBuilder()->emitMakeVector(irType, args.Count(), args.Buffer())); + } + else if (auto matrixType = type->As()) + { + UInt rowCount = (UInt) GetIntVal(matrixType->getRowCount()); + + for (UInt rr = 0; rr < argCount; ++rr) + { + auto argExpr = expr->args[rr]; + LoweredValInfo argVal = lowerRValueExpr(context, argExpr); + args.Add(getSimpleVal(context, argVal)); + } + if(rowCount > argCount) + { + auto rowType = matrixType->getRowType(); + auto irDefaultValue = getSimpleVal(context, getDefaultVal(rowType)); + + for(UInt rr = argCount; rr < rowCount; ++rr) { - SLANG_UNEXPECTED("need to default-initialize vector elements"); + args.Add(irDefaultValue); } } return LoweredValInfo::simple( - getBuilder()->emitMakeVector(irType, args.Count(), args.Buffer())); + getBuilder()->emitMakeMatrix(irType, args.Count(), args.Buffer())); } else if (auto declRefType = type->As()) { @@ -1732,23 +1886,21 @@ struct ExprLoweringVisitorBase : ExprVisitor } else { - SLANG_UNEXPECTED("need to default-initialize struct fields"); + auto irDefaultValue = getSimpleVal(context, getDefaultVal(ff)); + args.Add(irDefaultValue); } } return LoweredValInfo::simple( getBuilder()->emitMakeStruct(irType, args.Count(), args.Buffer())); } - else - { - SLANG_UNEXPECTED("not sure how to initialize this type"); - } - } - else - { - SLANG_UNEXPECTED("not sure how to initialize this type"); } + // If none of the above cases matched, then we had better + // have zero arguments in the initailizer list, in which + // case we are just looking for default initialization. + // + SLANG_UNEXPECTED("unhandled case for initializer list codegen"); UNREACHABLE_RETURN(LoweredValInfo()); } -- cgit v1.2.3