From 3e8e9f7bbdafe393ca3a088e72bf4958552bae7e Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Thu, 24 May 2018 18:26:44 -0600 Subject: [PATCH] PP: Implement locale-independent strtod, using istringstream and a fast path. Fixes #1228. Fixes #234. This uses imbue() to be locale independent. Notes: - 'sstream >> double' is much slower than strtod() * this was measurable in the test suite as a whole, despite being a tiny fraction of what the test suite does - so, this embeds a fast path that bypasses sstream most of the time => the test suite is faster than before - sstream is probably slower, because it does more accurate rounding than strtod() - sstream does not create INFINITY by itself, this was done based on failure inferencing --- .../preprocessor/PpContext.cpp | 3 + .../preprocessor/PpContext.h | 3 + .../preprocessor/PpScanner.cpp | 146 +++++++++++++++--- 3 files changed, 127 insertions(+), 25 deletions(-) mode change 100644 => 100755 glslang/MachineIndependent/preprocessor/PpContext.cpp mode change 100644 => 100755 glslang/MachineIndependent/preprocessor/PpScanner.cpp diff --git a/glslang/MachineIndependent/preprocessor/PpContext.cpp b/glslang/MachineIndependent/preprocessor/PpContext.cpp old mode 100644 new mode 100755 index 6a2e05fe..c89b3768 --- a/glslang/MachineIndependent/preprocessor/PpContext.cpp +++ b/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -77,6 +77,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \****************************************************************************/ #include +#include #include "PpContext.h" @@ -91,6 +92,8 @@ TPpContext::TPpContext(TParseContextBase& pc, const std::string& rootFileName, T for (elsetracker = 0; elsetracker < maxIfNesting; elsetracker++) elseSeen[elsetracker] = false; elsetracker = 0; + + strtodStream.imbue(std::locale::classic()); } TPpContext::~TPpContext() diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index 04d23b22..b3a39c5c 100755 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -80,6 +80,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include "../ParseHelper.h" @@ -620,6 +621,8 @@ protected: std::string rootFileName; std::stack includeStack; std::string currentSourceFile; + + std::istringstream strtodStream; }; } // end namespace glslang diff --git a/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/glslang/MachineIndependent/preprocessor/PpScanner.cpp old mode 100644 new mode 100755 index f4eaf57d..0c620a5f --- a/glslang/MachineIndependent/preprocessor/PpScanner.cpp +++ b/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -102,20 +102,36 @@ namespace glslang { int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) { - bool HasDecimalOrExponent = false; - int isDouble = 0; - const auto saveName = [&](int ch) { if (len <= MaxTokenLength) ppToken->name[len++] = static_cast(ch); }; - // Decimal: + // find the range of non-zero digits before the decimal point + int startNonZero = 0; + while (startNonZero < len && ppToken->name[startNonZero] == '0') + ++startNonZero; + int endNonZero = len; + while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0') + --endNonZero; + int numWholeNumberDigits = endNonZero - startNonZero; + // accumulate the range's value + bool fastPath = numWholeNumberDigits <= 15; // when the number gets too complex, set to false + unsigned long long wholeNumber = 0; + if (fastPath) { + for (int i = startNonZero; i < endNonZero; ++i) + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + int decimalShift = len - endNonZero; + + // Decimal point: + bool hasDecimalOrExponent = false; if (ch == '.') { - HasDecimalOrExponent = true; + hasDecimalOrExponent = true; saveName(ch); ch = getChar(); + int firstDecimal = len; // 1.#INF or -1.#INF if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) { @@ -145,38 +161,97 @@ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) } } - while (ch >= '0' && ch <= '9') { + // Consume leading-zero digits after the decimal point + while (ch == '0') { saveName(ch); ch = getChar(); } + int startNonZeroDecimal = len; + int endNonZeroDecimal = len; + + // Consume remaining digits, up to the exponent + while (ch >= '0' && ch <= '9') { + saveName(ch); + if (ch != '0') + endNonZeroDecimal = len; + ch = getChar(); + } + + // Compute accumulation up to the last non-zero digit + if (endNonZeroDecimal > startNonZeroDecimal) { + numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "." + if (numWholeNumberDigits > 15) + fastPath = false; + if (fastPath) { + for (int i = endNonZero; i < endNonZeroDecimal; ++i) { + if (ppToken->name[i] != '.') + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + } + decimalShift = firstDecimal - endNonZeroDecimal; + } } // Exponent: - - if (ch == 'e' || ch == 'E') { - HasDecimalOrExponent = true; - saveName(ch); - ch = getChar(); - if (ch == '+' || ch == '-') { + bool negativeExponent = false; + double exponentValue = 0.0; + int exponent = 0; + { + if (ch == 'e' || ch == 'E') { + hasDecimalOrExponent = true; saveName(ch); ch = getChar(); - } - if (ch >= '0' && ch <= '9') { - while (ch >= '0' && ch <= '9') { + if (ch == '+' || ch == '-') { + negativeExponent = ch == '-'; saveName(ch); ch = getChar(); } - } else { - parseContext.ppError(ppToken->loc, "bad character in float exponent", "", ""); + if (ch >= '0' && ch <= '9') { + while (ch >= '0' && ch <= '9') { + exponent = exponent * 10 + (ch - '0'); + saveName(ch); + ch = getChar(); + } + } else { + parseContext.ppError(ppToken->loc, "bad character in float exponent", "", ""); + } + } + + // Compensate for location of decimal + if (negativeExponent) + exponent -= decimalShift; + else { + exponent += decimalShift; + if (exponent < 0) { + negativeExponent = true; + exponent = -exponent; + } + } + if (exponent > 22) + fastPath = false; + + if (fastPath) { + // Compute the floating-point value of the exponent + exponentValue = 1.0; + if (exponent > 0) { + double expFactor = 10; + while (exponent > 0) { + if (exponent & 0x1) + exponentValue *= expFactor; + expFactor *= expFactor; + exponent >>= 1; + } + } } } // Suffix: + bool isDouble = false; bool isFloat16 = false; if (ch == 'l' || ch == 'L') { if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) parseContext.doubleCheck(ppToken->loc, "double floating-point suffix"); - if (ifdepth == 0 && !HasDecimalOrExponent) + if (ifdepth == 0 && !hasDecimalOrExponent) parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); if (parseContext.intermediate.getSource() == EShSourceGlsl) { int ch2 = getChar(); @@ -186,16 +261,16 @@ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) } else { saveName(ch); saveName(ch2); - isDouble = 1; + isDouble = true; } } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { saveName(ch); - isDouble = 1; + isDouble = true; } } else if (ch == 'h' || ch == 'H') { if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) parseContext.float16Check(ppToken->loc, "half floating-point suffix"); - if (ifdepth == 0 && !HasDecimalOrExponent) + if (ifdepth == 0 && !hasDecimalOrExponent) parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); if (parseContext.intermediate.getSource() == EShSourceGlsl) { int ch2 = getChar(); @@ -216,13 +291,13 @@ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix"); if (ifdepth == 0 && !parseContext.relaxedErrors()) parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix"); - if (ifdepth == 0 && !HasDecimalOrExponent) + if (ifdepth == 0 && !hasDecimalOrExponent) parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); saveName(ch); } else ungetChar(); - // Patch up the name, length, etc. + // Patch up the name and length for overflow if (len > MaxTokenLength) { len = MaxTokenLength; @@ -230,8 +305,29 @@ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) } ppToken->name[len] = '\0'; - // Get the numerical value - ppToken->dval = strtod(ppToken->name, nullptr); + // Compute the numerical value + if (fastPath) { + // compute the floating-point value of the exponent + if (exponentValue == 0.0) + ppToken->dval = (double)wholeNumber; + else if (negativeExponent) + ppToken->dval = (double)wholeNumber / exponentValue; + else + ppToken->dval = (double)wholeNumber * exponentValue; + } else { + // slow path + strtodStream.clear(); + strtodStream.str(ppToken->name); + strtodStream >> ppToken->dval; + // Assume failure combined with a large exponent was overflow, in + // an attempt to set INF. Otherwise, assume underflow, and set 0.0. + if (strtodStream.fail()) { + if (!negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->i64val = 0x7ff0000000000000; // +Infinity + else + ppToken->dval = 0.0; + } + } // Return the right token type if (isDouble)