From 738bcb82d0327c463625ee6fcdf14b52e766cedc Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 20 Sep 2018 08:14:25 -0700 Subject: Improve support for non-32-bit types. (#643) The main change here is to fill out the `BaseType` enumeration so that it covers the full range of 8/16/32/64-bit signed and unsigned integers, as well as 16/32/64-bit floating-point numbers, and then propagate that completion through various places in the code. More details: * The current `half`, `float`, `double`, `int`, and `uint` types are still the default names for their types, so things like `float16_t` and `int32_t` were added as `typedef`s. * We still need to generate the full gamut of vector/matrix `typedef`s for the new types, so that things like `float16_t4x3` will work (yes, I know that is ugly as sin, but that's the HLSL syntax...). * A few pieces of dead code from earlier in the compiler's life got removed, since I did a find-in-files for `BaseType::` and tried to either update or delete every site. * A few call sites that were enumerating integer base types in an ad-hoc fashion were changed to use a single `isIntegerBaseType()` function that I added in `check.cpp` * When compiling with dxc for shader model 6.2 and up, we enable the compiler's support for native 16-bit types via a flag. * The public API enumeration for reflection of scalar types added cases for 8- and 16-bit integers (it already exposed the other cases we need) * The lexer was updated to be extremely liberal in what kinds of suffixes it allows on literals. I also removed the logic that was treating, e.g., `0f` as a floating-point literal (it doesn't seem to be the right behavior). That would now be an integer literal with an invalid suffix. * The logic in the parser that applies types to literals was updated to handle a few more cases: `LL` and `ULL` for 64-bit integers, and `H` for 16-bit floats. * The mangling logic needed to be updated to handle the new cases, and I consolidated the handling of those types in their front-end and IR forms. * Removed the explicit `BasicExpressionType::ToString` logic, since all basic types are `DeclRefType`s in the front end, and we can just print them out as such. * As a bit of a gross hack, fudged the conversion costs so that `int` to `int64_t` conversion is a bit more costly. The problem there is that given an operation like `int(0) + uint(0)`, the best applicable candidates ended up being `+(uint,uint)` and `+(int64_t,int64_t)` because the cost of a single `int`-to-`uint` conversion was the same as the sum of the cost of an `int`-to-`int64_t` and a `uint`-to-`int64_t`. A better long-term fix here is to completely change our overload resolution strategy, but that is obviously way too big to squeeze into this change. * Type layout computation was updated to handle all the new types and give them their natural size/alignment. Note that this does *not* work for down-level HLSL where `half` is treated as a synonym for `float`. It also doesn't deal with the fact that many of these types aren't actually allowed in constant buffers for certain shader models. A future change should work to add error messages for unsupported stuff during type layout (or just make the types themselves require support for certain capabilities) --- source/slang/check.cpp | 121 ++++++++++--------------------------------------- 1 file changed, 25 insertions(+), 96 deletions(-) (limited to 'source/slang/check.cpp') diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 7f91ac0c3..bddf813f2 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -205,41 +205,6 @@ namespace Slang typeCheckingCache = nullptr; } - bool IsNumeric(BaseType t) - { - return t == BaseType::Int || t == BaseType::Float || t == BaseType::UInt; - } - - String TranslateHLSLTypeNames(String name) - { - if (name == "float2" || name == "half2") - return "vec2"; - else if (name == "float3" || name == "half3") - return "vec3"; - else if (name == "float4" || name == "half4") - return "vec4"; - else if (name == "half") - return "float"; - else if (name == "int2") - return "ivec2"; - else if (name == "int3") - return "ivec3"; - else if (name == "int4") - return "ivec4"; - else if (name == "uint2") - return "uvec2"; - else if (name == "uint3") - return "uvec3"; - else if (name == "uint4") - return "uvec4"; - else if (name == "float3x3" || name == "half3x3") - return "mat3"; - else if (name == "float4x4" || name == "half4x4") - return "mat4"; - else - return name; - } - enum class CheckingPhase { Header, Body @@ -2916,25 +2881,37 @@ namespace Slang decl->SetCheckState(getCheckedState()); } + bool isIntegerBaseType(BaseType baseType) + { + switch(baseType) + { + default: + return false; + + 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 true; + } + } + // Validate that `type` is a suitable type to use // as the tag type for an `enum` void validateEnumTagType(Type* type, SourceLoc const& loc) { if(auto basicType = type->As()) { - switch(basicType->baseType) - { - default: - // By default, don't allow a type to be used - // as an `enum` tag type. - break; - - case BaseType::Int: - case BaseType::UInt: - case BaseType::UInt64: - // These are all allowed. + // Allow the built-in intteger types. + if(isIntegerBaseType(basicType->baseType)) return; - } + + // By default, don't allow other types to be used + // as an `enum` tag type. } getSink()->diagnose(loc, Diagnostics::invalidEnumTagType, type); @@ -4420,16 +4397,8 @@ namespace Slang // Check if type is acceptable for an integer constant expression if(auto basicType = exp->type.type->As()) { - switch(basicType->baseType) - { - default: + if(!isIntegerBaseType(basicType->baseType)) return nullptr; - - case BaseType::Int: - case BaseType::UInt: - case BaseType::UInt64: - break; - } } else { @@ -7724,46 +7693,6 @@ namespace Slang // Now process this like any other explicit call (so casts // and constructor calls are semantically equivalent). return CheckInvokeExprWithCheckedOperands(expr); - -#if 0 - expr->Expression = CheckTerm(expr->Expression); - auto targetType = CheckProperType(expr->TargetType); - expr->TargetType = targetType; - - // The way to perform casting depends on the types involved - if (expr->Expression->type->Equals(getSession()->getErrorType())) - { - // If the expression being casted has an error type, then just silently succeed - expr->type = targetType.Ptr(); - return expr; - } - else if (auto targetArithType = targetType->AsArithmeticType()) - { - if (auto exprArithType = expr->Expression->type->AsArithmeticType()) - { - // Both source and destination types are arithmetic, so we might - // have a valid cast - auto targetScalarType = targetArithType->GetScalarType(); - auto exprScalarType = exprArithType->GetScalarType(); - - if (!IsNumeric(exprScalarType->baseType)) goto fail; - if (!IsNumeric(targetScalarType->baseType)) goto fail; - - // TODO(tfoley): this checking is incomplete here, and could - // lead to downstream compilation failures - expr->type = targetType.Ptr(); - return expr; - } - } - // TODO: other cases? Should we allow a cast to succeeed whenever - // a single-argument constructor for the target type would work? - - fail: - // Default: in no other case succeds, then the cast failed and we emit a diagnostic. - getSink()->diagnose(expr, Diagnostics::invalidTypeCast, expr->Expression->type, targetType->ToString()); - expr->type = QualType(getSession()->getErrorType()); - return expr; -#endif } // Get the type to use when referencing a declaration -- cgit v1.2.3