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/slang/slang-parser.cpp | |
| 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/slang/slang-parser.cpp')
| -rw-r--r-- | source/slang/slang-parser.cpp | 172 |
1 files changed, 158 insertions, 14 deletions
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; |
