diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2020-02-06 14:31:09 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-06 14:31:09 -0500 |
| commit | d3331fba6eaab44646010b556106da38925d43e0 (patch) | |
| tree | f54115540a457375a5d050bbfe1b04855b3f791b /source | |
| parent | 9c84cceffba26817721a23a1a85a48644bf3a560 (diff) | |
Literal handling improvements (#1202)
* WIP: 64 literal diagnostic and truncation.
* Improve how integer truncation is handled/supported.
Added literal-int64.slang test.
Set a suffix on all literals.
Fixed problem on C++ based targets where l suffix was not the same as int() cast. So on C++ derived emitters, int() is used instead of l suffix to have same behavior across targets.
* Add literal diagnostic testing.
* Allow lexer to lex - in front of literals.
* Fix lexing and converting int literal with -.
* Too large small values of floats become inf.
Handling writing inf types out on different targets.
Add function to deterimine if a float literals kind.
* Roll back the support of lexer lexing negative literals.
* Fixed tests broken because of diagnostics numbers.
Improved _isFinite
* Fix compilation on linux.
* Fix problem with abs on linux - use Math::Abs.
* Fix typo.
* * Improve warnings for float literals zeroed
* Improved 64 bit type documentation
* Handle half
* Improved comments
* Fixed tests broken
* Use capital letters for suffixes.
* Make default behavior on outputting a int literal that is an 'int32_t' is cast (not suffix) to avoid platform inconsistencies.
Improve documentation for 64 bit types.
Make tests cover material in docs.
* Fixed tests.
* Rename FloatKind::Normal -> Finite
* Fix half zero check.
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-math.h | 14 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 16 | ||||
| -rw-r--r-- | source/slang/slang-compiler.h | 30 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-diagnostics.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-diagnostics.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 36 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 48 | ||||
| -rw-r--r-- | source/slang/slang-emit-glsl.cpp | 51 | ||||
| -rw-r--r-- | source/slang/slang-emit-hlsl.cpp | 36 | ||||
| -rw-r--r-- | source/slang/slang-emit-hlsl.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 32 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 15 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 172 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-syntax.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 62 |
17 files changed, 482 insertions, 55 deletions
diff --git a/source/core/slang-math.h b/source/core/slang-math.h index 0daad0d5a..fa7eea6be 100644 --- a/source/core/slang-math.h +++ b/source/core/slang-math.h @@ -5,6 +5,13 @@ namespace Slang { + // Some handy constants + + // The largest positive (or negative) number +# define SLANG_HALF_MAX 65504.0f + // Smallest (denormalized) value. 1 / 2^24 +# define SLANG_HALF_SUB_NORMAL_MIN (1.0f / 16777216.0f) + class Math { public: @@ -20,6 +27,13 @@ namespace Slang static const float Pi; + + template <typename T> + static T Abs(T a) + { + return (a < 0) ? -a : a; + } + template<typename T> static T Min(const T& v1, const T&v2) { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index dbc96f9b1..f8df46ff7 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -1697,21 +1697,7 @@ namespace Slang bool SemanticsVisitor::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; - } + return (BaseTypeInfo::getInfo(baseType).flags & BaseTypeInfo::Flag::Integer) != 0; } void SemanticsVisitor::validateEnumTagType(Type* type, SourceLoc const& loc) diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 2c58cb901..151e89b34 100644 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1922,6 +1922,34 @@ namespace Slang struct TypeCheckingCache; // + // Information about BaseType that's useful for checking literals + struct BaseTypeInfo + { + typedef uint8_t Flags; + struct Flag + { + enum Enum : Flags + { + Signed = 0x1, + FloatingPoint = 0x2, + Integer = 0x4, + }; + }; + + SLANG_FORCE_INLINE static const BaseTypeInfo& getInfo(BaseType baseType) { return s_info[Index(baseType)]; } + + static UnownedStringSlice asText(BaseType baseType); + + uint8_t sizeInBytes; ///< Size of type in bytes + Flags flags; + uint8_t baseType; + + static bool check(); + + private: + static const BaseTypeInfo s_info[Index(BaseType::CountOf)]; + }; + class Session : public RefObject, public slang::IGlobalSession { public: @@ -2014,7 +2042,7 @@ namespace Slang RefPtr<Type> enumTypeType; - Dictionary<int, RefPtr<Type>> builtinTypes; + RefPtr<Type> builtinTypes[Index(BaseType::CountOf)]; Dictionary<String, Decl*> magicDecls; void initializeTypes(); diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 430423464..b80650c05 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -317,6 +317,8 @@ DIAGNOSTIC(30600, Error, varWithoutTypeMustHaveInitializer, "a variable declarat // 307xx: parameters DIAGNOSTIC(30700, Error, outputParameterCannotHaveDefaultValue, "an 'out' or 'inout' parameter cannot have a default-value expression"); +// 39999 waiting to be placed in the right range + DIAGNOSTIC(39999, Error, expectedIntegerConstantWrongType, "expected integer constant (found: '$0')") DIAGNOSTIC(39999, Error, expectedIntegerConstantNotConstant, "expression does not evaluate to a compile-time constant") DIAGNOSTIC(39999, Error, expectedIntegerConstantNotLiteral, "could not extract value from integer constant") @@ -345,7 +347,6 @@ DIAGNOSTIC(39999, Error, ambiguousExpression, "ambiguous reference"); DIAGNOSTIC(39999, Error, declarationDidntDeclareAnything, "declaration does not declare anything"); - DIAGNOSTIC(39999, Error, expectedPrefixOperator, "function called as prefix operator was not declared `__prefix`") DIAGNOSTIC(39999, Error, expectedPostfixOperator, "function called as postfix operator was not declared `__postfix`") @@ -355,7 +356,11 @@ DIAGNOSTIC(39999, Error, tooManyArguments, "too many arguments to call (got $0, DIAGNOSTIC(39999, Error, invalidIntegerLiteralSuffix, "invalid suffix '$0' on integer literal") DIAGNOSTIC(39999, Error, invalidFloatingPointLiteralSuffix, "invalid suffix '$0' on floating-point literal") +DIAGNOSTIC(39999, Warning, integerLiteralTruncated, "integer literal '$0' too large for type '$1' truncated to '$2'") +DIAGNOSTIC(39999, Warning, floatLiteralUnrepresentable, "$0 literal '$1' unrepresentable, converted to '$2'") +DIAGNOSTIC(39999, Warning, floatLiteralTooSmall, "'$1' is smaller than the smallest representable value for type $0, converted to '$2'") +// 38xxx DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'") DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'") diff --git a/source/slang/slang-diagnostics.cpp b/source/slang/slang-diagnostics.cpp index 76b2fcffa..be799c3bd 100644 --- a/source/slang/slang-diagnostics.cpp +++ b/source/slang/slang-diagnostics.cpp @@ -43,6 +43,11 @@ void printDiagnosticArg(StringBuilder& sb, uint64_t val) sb << val; } +void printDiagnosticArg(StringBuilder& sb, double val) +{ + sb << val; +} + void printDiagnosticArg(StringBuilder& sb, Slang::String const& str) { sb << str; diff --git a/source/slang/slang-diagnostics.h b/source/slang/slang-diagnostics.h index 9b70ef0d9..385a40167 100644 --- a/source/slang/slang-diagnostics.h +++ b/source/slang/slang-diagnostics.h @@ -87,6 +87,8 @@ namespace Slang void printDiagnosticArg(StringBuilder& sb, int64_t val); void printDiagnosticArg(StringBuilder& sb, uint64_t val); + void printDiagnosticArg(StringBuilder& sb, double val); + void printDiagnosticArg(StringBuilder& sb, Slang::String const& str); void printDiagnosticArg(StringBuilder& sb, Slang::UnownedStringSlice const& str); void printDiagnosticArg(StringBuilder& sb, Name* name); diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index c2bfad0a7..a383caecf 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -756,27 +756,57 @@ void CLikeSourceEmitter::emitSimpleValueImpl(IRInst* inst) case BaseType::Int16: case BaseType::Int: { + // NOTE! This hack is required, otherwise we get different results across targets. + // You'd hope that outputting L suffix would be enough to make this work, and not require a cast, but testing shows L suffix + // does not have the same meaning across targets. + // + // For example + // + // uint64_t v = 0x80000000; + // + // When output this becomes... + // v_0 = uint64_t(-2147483648L); + // + // On MSVC/Gcc/Clang this is equal to 0x80000000, elsewhere it's 0xffffffff80000000 elsewhere. + // Note that '-' isn't the issue because v0 = uint64_t(0x80000000L); produces the same issue + // + // If we use a cast, we get the same result across targets (which is why the hack is here). + // + // Why? It's not clear - it seems likely that it's related to the order of how the promotion takes place. + // + // If we convert from int32_t -> uint64_t, there are two possible scenarios + // 1) int32_t -> int64_t -> uint64_t (ie widen first then do sign type change) + // 2) int32_t -> uint32_t -> uint64_t (ie do sign type change then widen) + // + // 2 would produce what we see on C++, 1 what we see everywhere else. + // + // Why having a cast or having a suffix would make a difference though is not clear. It is also possible that the + // L suffix is just ignored, and the literal is really a 'non typed' int literal in C++. + + m_writer->emit("int("); m_writer->emit(litInst->value.intVal); - break; + m_writer->emit(")"); + return; } case BaseType::UInt8: case BaseType::UInt16: case BaseType::UInt: { m_writer->emit(UInt(litInst->value.intVal)); + m_writer->emit("U"); break; } case BaseType::Int64: { m_writer->emitInt64(int64_t(litInst->value.intVal)); - m_writer->emit("ll"); + m_writer->emit("LL"); break; } case BaseType::UInt64: { SLANG_COMPILE_TIME_ASSERT(sizeof(litInst->value.intVal) >= sizeof(uint64_t)); m_writer->emitUInt64(uint64_t(litInst->value.intVal)); - m_writer->emit("ull"); + m_writer->emit("ULL"); break; } } diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 30388479e..ae1798382 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -1883,23 +1883,47 @@ void CPPSourceEmitter::emitSimpleFuncImpl(IRFunc* func) void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst) { - switch (inst->op) + if (inst->op == kIROp_FloatLit) { - case kIROp_FloatLit: + IRConstant* constantInst = static_cast<IRConstant*>(inst); + switch (constantInst->getFloatKind()) { - IRConstant* constantInst = static_cast<IRConstant*>(inst); - - m_writer->emit(constantInst->value.floatVal); - - // If the literal is a float, then we need to add 'f' at end - IRType* type = constantInst->getDataType(); - if (type && type->op == kIROp_FloatType ) + case IRConstant::FloatKind::Nan: { - m_writer->emitChar('f'); + // TODO(JS): + // It's not clear this will work on all targets. + // In particular Visual Studio reports an error with this expression. + m_writer->emit("(0.0 / 0.0)"); + break; + } + case IRConstant::FloatKind::PositiveInfinity: + { + m_writer->emit("SLANG_INFINITY"); + break; + } + case IRConstant::FloatKind::NegativeInfinity: + { + m_writer->emit("(-SLANG_INFINITY)"); + break; + } + default: + { + m_writer->emit(constantInst->value.floatVal); + + // If the literal is a float, then we need to add 'f' at end, as + // without literal suffix the value defaults to double. + IRType* type = constantInst->getDataType(); + if (type && type->op == kIROp_FloatType) + { + m_writer->emitChar('f'); + } + break; } - break; } - default: Super::emitSimpleValueImpl(inst); + } + else + { + Super::emitSimpleValueImpl(inst); } } diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index c319f44e9..fbae21f75 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -671,24 +671,69 @@ void GLSLSourceEmitter::emitSimpleValueImpl(IRInst* inst) { switch (type->getBaseType()) { - default: break; + default: + + case BaseType::Int8: + case BaseType::Int16: + case BaseType::Int: + { + m_writer->emit(litInst->value.intVal); + return; + } + case BaseType::UInt8: + case BaseType::UInt16: + case BaseType::UInt: + { + m_writer->emit(UInt(litInst->value.intVal)); + m_writer->emit("U"); + return; + } case BaseType::Int64: { m_writer->emitInt64(int64_t(litInst->value.intVal)); - m_writer->emit("l"); + m_writer->emit("L"); return; } case BaseType::UInt64: { SLANG_COMPILE_TIME_ASSERT(sizeof(litInst->value.intVal) >= sizeof(uint64_t)); m_writer->emitUInt64(uint64_t(litInst->value.intVal)); - m_writer->emit("ul"); + m_writer->emit("UL"); return; } + } } break; } + case kIROp_FloatLit: + { + IRConstant* constantInst = static_cast<IRConstant*>(inst); + + IRConstant::FloatKind kind = constantInst->getFloatKind(); + + switch (kind) + { + case IRConstant::FloatKind::Nan: + { + m_writer->emit("(0.0 / 0.0)"); + return; + } + case IRConstant::FloatKind::PositiveInfinity: + { + m_writer->emit("(1.0 / 0.0)"); + return; + } + case IRConstant::FloatKind::NegativeInfinity: + { + m_writer->emit("(-1.0 / 0.0)"); + return; + } + default: break; + } + break; + } + default: break; } diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp index 240ef6ad0..f615d41fb 100644 --- a/source/slang/slang-emit-hlsl.cpp +++ b/source/slang/slang-emit-hlsl.cpp @@ -488,6 +488,42 @@ void HLSLSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerVal m_writer->emit(">"); } +void HLSLSourceEmitter::emitSimpleValueImpl(IRInst* inst) +{ + switch (inst->op) + { + case kIROp_FloatLit: + { + IRConstant* constantInst = static_cast<IRConstant*>(inst); + IRConstant::FloatKind kind = constantInst->getFloatKind(); + switch (kind) + { + case IRConstant::FloatKind::Nan: + { + m_writer->emit("(0.0 / 0.0)"); + return; + } + case IRConstant::FloatKind::PositiveInfinity: + { + m_writer->emit("(1.0 / 0.0)"); + return; + } + case IRConstant::FloatKind::NegativeInfinity: + { + m_writer->emit("(-1.0 / 0.0)"); + return; + } + default: break; + } + break; + } + + default: break; + } + + Super::emitSimpleValueImpl(inst); +} + void HLSLSourceEmitter::emitSimpleTypeImpl(IRType* type) { switch (type->op) diff --git a/source/slang/slang-emit-hlsl.h b/source/slang/slang-emit-hlsl.h index 2120562e6..05fa3bb11 100644 --- a/source/slang/slang-emit-hlsl.h +++ b/source/slang/slang-emit-hlsl.h @@ -32,6 +32,7 @@ protected: virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) SLANG_OVERRIDE; virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE; + virtual void emitSimpleValueImpl(IRInst* inst) SLANG_OVERRIDE; // Emit a single `register` semantic, as appropriate for a given resource-type-specific layout info // Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`) diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 4f33e08ee..b121307ee 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -1674,6 +1674,38 @@ namespace Slang } } + bool IRConstant::isFinite() const + { + SLANG_ASSERT(op == kIROp_FloatLit); + + // Lets check we can analyze as double, at least in principal + SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(double)); + // We are in effect going to type pun (yay!), lets make sure they are the same size + SLANG_COMPILE_TIME_ASSERT(sizeof(IRIntegerValue) == sizeof(IRFloatingPointValue)); + + const uint64_t i = uint64_t(value.intVal); + int e = int(i >> 52) & 0x7ff; + return (e != 0x7ff); + } + + IRConstant::FloatKind IRConstant::getFloatKind() const + { + SLANG_ASSERT(op == kIROp_FloatLit); + + const uint64_t i = uint64_t(value.intVal); + int e = int(i >> 52) & 0x7ff; + if ( e == 0x7ff) + { + if (i << 12) + { + return FloatKind::Nan; + } + // Sign bit (top bit) will indicate positive or negative nan + return value.intVal < 0 ? FloatKind::NegativeInfinity : FloatKind::PositiveInfinity; + } + return FloatKind::Finite; + } + bool IRConstant::isValueEqual(IRConstant* rhs) { // If they are literally the same thing.. diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index ac7eab198..06dd0f94c 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -638,6 +638,14 @@ typedef double IRFloatingPointValue; struct IRConstant : IRInst { + enum class FloatKind + { + Finite, + PositiveInfinity, + NegativeInfinity, + Nan, + }; + struct StringValue { uint32_t numChars; ///< The number of chars @@ -665,6 +673,13 @@ struct IRConstant : IRInst /// Returns a string slice (or empty string if not appropriate) UnownedStringSlice getStringSlice(); + /// Returns the kind of floating point value we have + FloatKind getFloatKind() const; + + /// Returns true if the value is finite. + /// NOTE! Only works on floating point types + bool isFinite() const; + /// True if constants are equal bool equal(IRConstant* rhs); /// True if the value is equal. diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 9be2e49e5..17fe792ab 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1,6 +1,7 @@ #include "slang-parser.h" #include <assert.h> +#include <float.h> #include "slang-compiler.h" #include "slang-lookup.h" @@ -3871,6 +3872,20 @@ namespace Slang return parseBoolLitExpr(parser, false); } + static bool _isFinite(double value) + { + // Lets type pun double to uint64_t, so we can detect special double values + union + { + double d; + uint64_t i; + } u = { value }; + // Detects nan and +-inf + const uint64_t i = u.i; + int e = int(i >> 52) & 0x7ff; + return (e != 0x7ff); + } + static RefPtr<Expr> parseAtomicExpr(Parser* parser) { switch( peekTokenType(parser) ) @@ -3961,9 +3976,14 @@ namespace Slang char const* suffixCursor = suffix.begin(); const char*const suffixEnd = suffix.end(); - RefPtr<Type> suffixType = nullptr; + // If no suffix is defined go with the default + BaseType suffixBaseType = BaseType::Int; + if( suffixCursor < suffixEnd ) { + // Mark as void, taken as an error + suffixBaseType = BaseType::Void; + int lCount = 0; int uCount = 0; int unknownCount = 0; @@ -3988,36 +4008,85 @@ namespace Slang if(unknownCount) { parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); - suffixType = parser->getSession()->getErrorType(); + suffixBaseType = BaseType::Void; } // `u` or `ul` suffix -> `uint` else if(uCount == 1 && (lCount <= 1)) { - suffixType = parser->getSession()->getUIntType(); + suffixBaseType = BaseType::UInt; } // `l` suffix on integer -> `int` (== `long`) else if(lCount == 1 && !uCount) { - suffixType = parser->getSession()->getIntType(); + suffixBaseType = BaseType::Int; } // `ull` suffix -> `uint64_t` else if(uCount == 1 && lCount == 2) { - suffixType = parser->getSession()->getUInt64Type(); + suffixBaseType = BaseType::UInt64; } // `ll` suffix -> `int64_t` else if(uCount == 0 && lCount == 2) { - suffixType = parser->getSession()->getInt64Type(); + suffixBaseType = BaseType::Int64; } // TODO: do we need suffixes for smaller integer types? else { parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); - suffixType = parser->getSession()->getErrorType(); + suffixBaseType = BaseType::Void; + } + } + + // TODO(JS): + // It is worth noting here that because of the way that the lexer works, that literals + // are always handled as if they are positive (a preceding - is taken as a negate on a + // positive value). + // The code here is designed to work with positive and negative values, as this behavior + // might change in the future, and is arguably more 'correct'. + + const BaseTypeInfo& info = BaseTypeInfo::getInfo(suffixBaseType); + SLANG_ASSERT(info.flags & BaseTypeInfo::Flag::Integer); + SLANG_COMPILE_TIME_ASSERT(sizeof(value) == sizeof(uint64_t)); + + // If the type is 64 bits, do nothing, we'll assume all is good + if (suffixBaseType != BaseType::Void && info.sizeInBytes != sizeof(value)) + { + const IntegerLiteralValue signBit = IntegerLiteralValue(1) << (8 * info.sizeInBytes - 1); + // Same as (~IntegerLiteralValue(0)) << (8 * info.sizeInBytes);, without the need for variable shift + const IntegerLiteralValue mask = -(signBit + signBit); + + IntegerLiteralValue truncatedValue = value; + // If it's signed, and top bit is set, sign extend it negative + if (info.flags & BaseTypeInfo::Flag::Signed) + { + // Sign extend + truncatedValue = (value & signBit) ? (value | mask) : (value & ~mask); + } + else + { + // 0 top bits + truncatedValue = value & ~mask; } + + const IntegerLiteralValue maskedValue = value & mask; + + // If the masked value is 0 or equal to the mask, we 'assume' no information is + // lost + // This allows for example -1u, to give 0xffffffff + // It also means 0xfffffffffffffffffu will give 0xffffffff, without a warning. + if (!(maskedValue == 0 || maskedValue == mask)) + { + // Output a warning that number has been altered + parser->sink->diagnose(token, Diagnostics::integerLiteralTruncated, token.Content, BaseTypeInfo::asText(suffixBaseType), truncatedValue); + } + + value = truncatedValue; } + auto session = parser->getSession(); + Type* suffixType = (suffixBaseType == BaseType::Void) ? session->getErrorType() : session->getBuiltinType(suffixBaseType); + constExpr->value = value; constExpr->type = QualType(suffixType); @@ -4040,7 +4109,9 @@ namespace Slang char const* suffixCursor = suffix.begin(); const char*const suffixEnd = suffix.end(); - RefPtr<Type> suffixType = nullptr; + + // Default is Float + BaseType suffixBaseType = BaseType::Float; if( suffixCursor < suffixEnd ) { int fCount = 0; @@ -4072,32 +4143,105 @@ namespace Slang if (unknownCount) { parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix); - suffixType = parser->getSession()->getErrorType(); + suffixBaseType = BaseType::Void; } // `f` suffix -> `float` if(fCount == 1 && !lCount && !hCount) { - suffixType = parser->getSession()->getFloatType(); + suffixBaseType = BaseType::Float; } // `l` or `lf` suffix on floating-point literal -> `double` else if(lCount == 1 && (fCount <= 1)) { - suffixType = parser->getSession()->getDoubleType(); + suffixBaseType = BaseType::Double; } // `h` or `hf` suffix on floating-point literal -> `half` else if(hCount == 1 && (fCount <= 1)) { - suffixType = parser->getSession()->getHalfType(); + suffixBaseType = BaseType::Half; } // TODO: are there other suffixes we need to handle? else { parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix); - suffixType = parser->getSession()->getErrorType(); + suffixBaseType = BaseType::Void; } } - constExpr->value = value; + // TODO(JS): + // It is worth noting here that because of the way that the lexer works, that literals + // are always handled as if they are positive (a preceding - is taken as a negate on a + // positive value). + // The code here is designed to work with positive and negative values, as this behavior + // might change in the future, and is arguably more 'correct'. + + FloatingPointLiteralValue fixedValue = value; + + // Check the value is finite for checking narrowing to literal type losing information + if (_isFinite(fixedValue)) + { + switch (suffixBaseType) + { + case BaseType::Float: + { + // Fix out of range + if (fixedValue > FLT_MAX) + { + fixedValue = float(INFINITY); + } + else if (fixedValue < -FLT_MAX) + { + fixedValue = -float(INFINITY); + } + else if (fixedValue && float(fixedValue) == 0.0f) + { + fixedValue = 0.0f; + } + break; + } + case BaseType::Double: + { + break; + } + case BaseType::Half: + { + // Fix out of range + if (fixedValue > SLANG_HALF_MAX) + { + fixedValue = float(INFINITY); + } + else if (fixedValue < -SLANG_HALF_MAX) + { + fixedValue = -float(INFINITY); + } + else if (fixedValue && Math::Abs(fixedValue) < SLANG_HALF_SUB_NORMAL_MIN) + { + fixedValue = 0.0f; + } + break; + } + default: break; + } + + + if (fixedValue != value) + { + if (fixedValue == 0.0) + { + parser->sink->diagnose(token, Diagnostics::floatLiteralTooSmall, BaseTypeInfo::asText(suffixBaseType), token.Content, fixedValue); + } + else + { + parser->sink->diagnose(token, Diagnostics::floatLiteralUnrepresentable, BaseTypeInfo::asText(suffixBaseType), token.Content, fixedValue); + } + } + } + + Session* session = parser->getSession(); + + Type* suffixType = (suffixBaseType == BaseType::Void) ? session->getErrorType() : session->getBuiltinType(suffixBaseType); + + constExpr->value = fixedValue; constExpr->type = QualType(suffixType); return constExpr; diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index 26a7dbb65..551a911c8 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -90,7 +90,7 @@ namespace Slang // Here we declare the table of all our builtin types, so that we can generate all the relevant declarations. // - struct BaseTypeInfo + struct BaseTypeConversionInfo { char const* name; BaseType tag; @@ -98,7 +98,7 @@ namespace Slang BaseTypeConversionKind conversionKind; BaseTypeConversionRank conversionRank; }; - static const BaseTypeInfo kBaseTypes[] = { + static const BaseTypeConversionInfo kBaseTypes[] = { // TODO: `void` really shouldn't be in the `BaseType` enumeration, since it behaves so differently across the board { "void", BaseType::Void, 0, kBaseTypeConversionKind_Error, kBaseTypeConversionRank_Error}, @@ -121,8 +121,8 @@ namespace Slang // Given two base types, we need to be able to compute the cost of converting between them. ConversionCost getBaseTypeConversionCost( - BaseTypeInfo const& toInfo, - BaseTypeInfo const& fromInfo) + BaseTypeConversionInfo const& toInfo, + BaseTypeConversionInfo const& fromInfo) { if(toInfo.conversionKind == fromInfo.conversionKind && toInfo.conversionRank == fromInfo.conversionRank) diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp index 19e4256fe..502425da8 100644 --- a/source/slang/slang-syntax.cpp +++ b/source/slang/slang-syntax.cpp @@ -236,7 +236,7 @@ void Type::accept(IValVisitor* visitor, void* extra) Type* Session::getBuiltinType(BaseType flavor) { - return RefPtr<Type>(builtinTypes[(int)flavor]); + return builtinTypes[int(flavor)]; } Type* Session::getInitializerListType() diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index bfc77b2e3..5fdec8259 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -37,6 +37,61 @@ namespace Slang { +/* static */const BaseTypeInfo BaseTypeInfo::s_info[Index(BaseType::CountOf)] = +{ + { 0, 0, uint8_t(BaseType::Void) }, + { uint8_t(sizeof(bool)), 0, uint8_t(BaseType::Bool) }, + { uint8_t(sizeof(int8_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int8) }, + { uint8_t(sizeof(int16_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int16) }, + { uint8_t(sizeof(int32_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int) }, + { uint8_t(sizeof(int64_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int64) }, + { uint8_t(sizeof(uint8_t)), BaseTypeInfo::Flag::Integer , uint8_t(BaseType::UInt8) }, + { uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::Integer , uint8_t(BaseType::UInt16) }, + { uint8_t(sizeof(uint32_t)), BaseTypeInfo::Flag::Integer , uint8_t(BaseType::UInt) }, + { uint8_t(sizeof(uint64_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt64) }, + { uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::FloatingPoint , uint8_t(BaseType::Half) }, + { uint8_t(sizeof(float)), BaseTypeInfo::Flag::FloatingPoint , uint8_t(BaseType::Float) }, + { uint8_t(sizeof(double)), BaseTypeInfo::Flag::FloatingPoint , uint8_t(BaseType::Double) }, +}; + +/* static */bool BaseTypeInfo::check() +{ + for (Index i = 0; i < SLANG_COUNT_OF(s_info); ++i) + { + if (s_info[i].baseType != i) + { + SLANG_ASSERT(!"Inconsistency between the s_info table and BaseInfo"); + return false; + } + } + return true; +} + +/* static */UnownedStringSlice BaseTypeInfo::asText(BaseType baseType) +{ + switch (baseType) + { + case BaseType::Void: return UnownedStringSlice::fromLiteral("void"); + case BaseType::Bool: return UnownedStringSlice::fromLiteral("bool"); + case BaseType::Int8: return UnownedStringSlice::fromLiteral("int8_t"); + case BaseType::Int16: return UnownedStringSlice::fromLiteral("int16_t"); + case BaseType::Int: return UnownedStringSlice::fromLiteral("int"); + case BaseType::Int64: return UnownedStringSlice::fromLiteral("int64_t"); + case BaseType::UInt8: return UnownedStringSlice::fromLiteral("uint8_t"); + case BaseType::UInt16: return UnownedStringSlice::fromLiteral("uint16_t"); + case BaseType::UInt: return UnownedStringSlice::fromLiteral("uint"); + case BaseType::UInt64: return UnownedStringSlice::fromLiteral("uint64_t"); + case BaseType::Half: return UnownedStringSlice::fromLiteral("half"); + case BaseType::Float: return UnownedStringSlice::fromLiteral("float"); + case BaseType::Double: return UnownedStringSlice::fromLiteral("double"); + default: + { + SLANG_ASSERT(!"Unknown basic type"); + return UnownedStringSlice(); + } + } +} + // Allocate static const storage for the various interface IDs that the Slang API needs to expose static const Guid IID_IComponentType = SLANG_UUID_IComponentType; static const Guid IID_IEntryPoint = SLANG_UUID_IEntryPoint; @@ -48,6 +103,8 @@ static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; void Session::init() { + SLANG_ASSERT(BaseTypeInfo::check()); + ::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators)); DownstreamCompilerUtil::setDefaultLocators(m_downstreamCompilerLocators); m_downstreamCompilerSet = new DownstreamCompilerSet; @@ -2548,7 +2605,10 @@ Session::~Session() destroyTypeCheckingCache(); - builtinTypes = decltype(builtinTypes)(); + for (Index i = 0; i < SLANG_COUNT_OF(builtinTypes); ++i) + { + builtinTypes[i].setNull(); + } // destroy modules next loadedModuleCode = decltype(loadedModuleCode)(); } |
