From 96a74dabfde5b9ad9734d8b1a9d8e2b2870681c0 Mon Sep 17 00:00:00 2001 From: Morten Engvoldsen Date: Mon, 23 Aug 2010 07:21:51 +0200 Subject: Doc: Updating menu links --- tools/qdoc3/test/qt-html-templates.qdocconf | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/qdoc3/test/qt-html-templates.qdocconf b/tools/qdoc3/test/qt-html-templates.qdocconf index c70bcaf..8d4d27f 100644 --- a/tools/qdoc3/test/qt-html-templates.qdocconf +++ b/tools/qdoc3/test/qt-html-templates.qdocconf @@ -98,10 +98,13 @@ HTML.postheader = "
\n" \ " Qt Topics\n" \ " \n" \ "
\n" \ -- cgit v0.12 From fa2a1a8c584494a0e75732444ad3d7561a39d6f7 Mon Sep 17 00:00:00 2001 From: Morten Engvoldsen Date: Mon, 23 Aug 2010 08:06:07 +0200 Subject: Doc: linking up orphant files --- doc/src/getting-started/examples.qdoc | 4 ++++ doc/src/platforms/x11overlays.qdoc | 1 + 2 files changed, 5 insertions(+) diff --git a/doc/src/getting-started/examples.qdoc b/doc/src/getting-started/examples.qdoc index 708c44e..e8c85e6 100644 --- a/doc/src/getting-started/examples.qdoc +++ b/doc/src/getting-started/examples.qdoc @@ -255,6 +255,7 @@ \o \l{graphicsview/flowlayout}{Flow Layout} \o \l{graphicsview/simpleanchorlayout}{Simple Anchor Layout} \o \l{graphicsview/weatheranchorlayout}{Weather Anchor Layout} + \o \l{graphicsview/basicgraphicslayouts}{Basic Graphics Layouts} \endlist Some examples demonstrate the use of graphics effects with canvas items. @@ -471,6 +472,7 @@ \o \l{network/googlesuggest}{Google Suggest} \o \l{network/bearercloud}{Bearer Cloud}\raisedaster \o \l{network/bearermonitor}{Bearer Monitor} + \o \l{network/securesocketclient}{Secure Socket Client} \endlist Examples marked with an asterisk (*) are fully documented. @@ -609,6 +611,7 @@ \o \l{sql/querymodel}{Query Model} \o \l{sql/relationaltablemodel}{Relational Table Model} \o \l{sql/tablemodel}{Table Model} + \o \l{sql/masterdetail}{Master Detail} \o \l{sql/sqlwidgetmapper}{SQL Widget Mapper}\raisedaster \endlist @@ -633,6 +636,7 @@ \o \l{xml/streambookmarks}{QXmlStream Bookmarks}\raisedaster \o \l{xml/rsslisting}{RSS-Listing} \o \l{xml/xmlstreamlint}{XML Stream Lint Example}\raisedaster + \o \l{xml/htmlinfo}{XML HTML Info} \endlist The XQuery/XPath and XML Schema engines in the QtXmlPatterns modules diff --git a/doc/src/platforms/x11overlays.qdoc b/doc/src/platforms/x11overlays.qdoc index 1a03ea6..949a500 100644 --- a/doc/src/platforms/x11overlays.qdoc +++ b/doc/src/platforms/x11overlays.qdoc @@ -28,6 +28,7 @@ /*! \page x11overlays.html \title How to Use X11 Overlays with Qt + \ingroup best-practices X11 overlays are a powerful mechanism for drawing annotations etc., on top of an image without destroying it, thus saving -- cgit v0.12 From 6b1bcb2a469d21e409bb0dbd3b5b559627e5c2ea Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 30 Jun 2010 11:02:36 +0200 Subject: Streamline lupdate's QtScript frontend's error messaging Like the C++ parser, use a yyMsg() function. Make the lexer a member of the parser, so yyMsg() can access the filename and current line number. This refactoring is done in preparation of two changes that introduce quite a few more potential error message calls (comment processing and qtTrId support). Reviewed-by: Oswald Buddenhagen --- tools/linguist/lupdate/qscript.cpp | 61 ++++++++++++++++++++++++-------------- tools/linguist/lupdate/qscript.g | 61 ++++++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 44 deletions(-) diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp index 188ac36..08670ac 100644 --- a/tools/linguist/lupdate/qscript.cpp +++ b/tools/linguist/lupdate/qscript.cpp @@ -790,9 +790,10 @@ public: Lexer(); ~Lexer(); - void setCode(const QString &c, int lineno); + void setCode(const QString &c, const QString &fileName, int lineno); int lex(); + QString fileName() const { return yyfilename; } int currentLineNo() const { return yylineno; } int currentColumnNo() const { return yycolumn; } @@ -872,6 +873,7 @@ public: { err = NoError; } private: + QString yyfilename; int yylineno; bool done; char *buffer8; @@ -1053,9 +1055,10 @@ QScript::Lexer::~Lexer() delete [] buffer16; } -void QScript::Lexer::setCode(const QString &c, int lineno) +void QScript::Lexer::setCode(const QString &c, const QString &fileName, int lineno) { errmsg = QString(); + yyfilename = fileName; yylineno = lineno; yycolumn = 1; restrKeyword = false; @@ -2052,10 +2055,12 @@ public: QScriptParser(); ~QScriptParser(); - bool parse(QScript::Lexer *lexer, - const QString &fileName, - Translator *translator); + void setLexer(QScript::Lexer *); + bool parse(Translator *translator); + + QString fileName() const + { return lexer->fileName(); } inline QString errorMessage() const { return error_message; } inline int errorLineNumber() const @@ -2072,6 +2077,8 @@ protected: inline Location &loc(int index) { return location_stack [tos + index - 2]; } + std::ostream &yyMsg(int line = 0); + protected: int tos; int stack_size; @@ -2081,6 +2088,9 @@ protected: QString error_message; int error_lineno; int error_column; + +private: + QScript::Lexer *lexer; }; inline void QScriptParser::reallocateStack() @@ -2107,7 +2117,8 @@ QScriptParser::QScriptParser(): stack_size(0), sym_stack(0), state_stack(0), - location_stack(0) + location_stack(0), + lexer(0) { } @@ -2129,10 +2140,14 @@ static inline QScriptParser::Location location(QScript::Lexer *lexer) return loc; } -bool QScriptParser::parse(QScript::Lexer *lexer, - const QString &fileName, - Translator *translator) +void QScriptParser::setLexer(QScript::Lexer *lex) +{ + lexer = lex; +} + +bool QScriptParser::parse(Translator *translator) { + Q_ASSERT(lexer != 0); const int INITIAL_STATE = 0; int yytoken = -1; @@ -2216,13 +2231,11 @@ case 66: { if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) { QVariantList args = sym(2).toList(); if (args.size() < 2) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "() requires at least two arguments.\n"; + yyMsg(identLineNo) << qPrintable(name) << "() requires at least two arguments.\n"; } else { if ((args.at(0).type() != QVariant::String) || (args.at(1).type() != QVariant::String)) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "(): both arguments must be literal strings.\n"; + yyMsg(identLineNo) << qPrintable(name) << "(): both arguments must be literal strings.\n"; } else { QString context = args.at(0).toString(); QString text = args.at(1).toString(); @@ -2230,26 +2243,24 @@ case 66: { QString extracomment; bool plural = (args.size() > 4); recordMessage(translator, context, text, comment, extracomment, - plural, fileName, identLineNo); + plural, fileName(), identLineNo); } } } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { QVariantList args = sym(2).toList(); if (args.size() < 1) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "() requires at least one argument.\n"; + yyMsg(identLineNo) << qPrintable(name) << "() requires at least one argument.\n"; } else { if (args.at(0).type() != QVariant::String) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "(): text to translate must be a literal string.\n"; + yyMsg(identLineNo) << qPrintable(name) << "(): text to translate must be a literal string.\n"; } else { - QString context = QFileInfo(fileName).baseName(); + QString context = QFileInfo(fileName()).baseName(); QString text = args.at(0).toString(); QString comment = args.value(1).toString(); QString extracomment; bool plural = (args.size() > 2); recordMessage(translator, context, text, comment, extracomment, - plural, fileName, identLineNo); + plural, fileName(), identLineNo); } } } @@ -2356,6 +2367,11 @@ case 94: { return false; } +std::ostream &QScriptParser::yyMsg(int line) +{ + return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": "; +} + bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { @@ -2376,9 +2392,10 @@ bool loadQScript(Translator &translator, const QString &filename, ConversionData QString code = ts.readAll(); QScript::Lexer lexer; - lexer.setCode(code, /*lineNumber=*/1); + lexer.setCode(code, filename, /*lineNumber=*/1); QScriptParser parser; - if (!parser.parse(&lexer, filename, &translator)) { + parser.setLexer(&lexer); + if (!parser.parse(&translator)) { std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": " << qPrintable(parser.errorMessage()) << std::endl; return false; diff --git a/tools/linguist/lupdate/qscript.g b/tools/linguist/lupdate/qscript.g index 857c58a..2f82a55 100644 --- a/tools/linguist/lupdate/qscript.g +++ b/tools/linguist/lupdate/qscript.g @@ -121,9 +121,10 @@ public: Lexer(); ~Lexer(); - void setCode(const QString &c, int lineno); + void setCode(const QString &c, const QString &fileName, int lineno); int lex(); + QString fileName() const { return yyfilename; } int currentLineNo() const { return yylineno; } int currentColumnNo() const { return yycolumn; } @@ -203,6 +204,7 @@ public: { err = NoError; } private: + QString yyfilename; int yylineno; bool done; char *buffer8; @@ -384,9 +386,10 @@ QScript::Lexer::~Lexer() delete [] buffer16; } -void QScript::Lexer::setCode(const QString &c, int lineno) +void QScript::Lexer::setCode(const QString &c, const QString &fileName, int lineno) { errmsg = QString(); + yyfilename = fileName; yylineno = lineno; yycolumn = 1; restrKeyword = false; @@ -1383,10 +1386,12 @@ public: QScriptParser(); ~QScriptParser(); - bool parse(QScript::Lexer *lexer, - const QString &fileName, - Translator *translator); + void setLexer(QScript::Lexer *); + bool parse(Translator *translator); + + QString fileName() const + { return lexer->fileName(); } inline QString errorMessage() const { return error_message; } inline int errorLineNumber() const @@ -1403,6 +1408,8 @@ protected: inline Location &loc(int index) { return location_stack [tos + index - 2]; } + std::ostream &yyMsg(int line = 0); + protected: int tos; int stack_size; @@ -1412,6 +1419,9 @@ protected: QString error_message; int error_lineno; int error_column; + +private: + QScript::Lexer *lexer; }; inline void QScriptParser::reallocateStack() @@ -1438,7 +1448,8 @@ QScriptParser::QScriptParser(): stack_size(0), sym_stack(0), state_stack(0), - location_stack(0) + location_stack(0), + lexer(0) { } @@ -1460,10 +1471,14 @@ static inline QScriptParser::Location location(QScript::Lexer *lexer) return loc; } -bool QScriptParser::parse(QScript::Lexer *lexer, - const QString &fileName, - Translator *translator) +void QScriptParser::setLexer(QScript::Lexer *lex) +{ + lexer = lex; +} + +bool QScriptParser::parse(Translator *translator) { + Q_ASSERT(lexer != 0); const int INITIAL_STATE = 0; int yytoken = -1; @@ -1632,13 +1647,11 @@ case $rule_number: { if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) { QVariantList args = sym(2).toList(); if (args.size() < 2) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "() requires at least two arguments.\n"; + yyMsg(identLineNo) << qPrintable(name) << "() requires at least two arguments.\n"; } else { if ((args.at(0).type() != QVariant::String) || (args.at(1).type() != QVariant::String)) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "(): both arguments must be literal strings.\n"; + yyMsg(identLineNo) << qPrintable(name) << "(): both arguments must be literal strings.\n"; } else { QString context = args.at(0).toString(); QString text = args.at(1).toString(); @@ -1646,26 +1659,24 @@ case $rule_number: { QString extracomment; bool plural = (args.size() > 4); recordMessage(translator, context, text, comment, extracomment, - plural, fileName, identLineNo); + plural, fileName(), identLineNo); } } } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { QVariantList args = sym(2).toList(); if (args.size() < 1) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "() requires at least one argument.\n"; + yyMsg(identLineNo) << qPrintable(name) << "() requires at least one argument.\n"; } else { if (args.at(0).type() != QVariant::String) { - std::cerr << qPrintable(fileName) << ':' << identLineNo << ": " - << qPrintable(name) << "(): text to translate must be a literal string.\n"; + yyMsg(identLineNo) << qPrintable(name) << "(): text to translate must be a literal string.\n"; } else { - QString context = QFileInfo(fileName).baseName(); + QString context = QFileInfo(fileName()).baseName(); QString text = args.at(0).toString(); QString comment = args.value(1).toString(); QString extracomment; bool plural = (args.size() > 2); recordMessage(translator, context, text, comment, extracomment, - plural, fileName, identLineNo); + plural, fileName(), identLineNo); } } } @@ -1988,6 +1999,11 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ; return false; } +std::ostream &QScriptParser::yyMsg(int line) +{ + return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": "; +} + bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { @@ -2008,9 +2024,10 @@ bool loadQScript(Translator &translator, const QString &filename, ConversionData QString code = ts.readAll(); QScript::Lexer lexer; - lexer.setCode(code, /*lineNumber=*/1); + lexer.setCode(code, filename, /*lineNumber=*/1); QScriptParser parser; - if (!parser.parse(&lexer, filename, &translator)) { + parser.setLexer(&lexer); + if (!parser.parse(&translator)) { std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": " << qPrintable(parser.errorMessage()) << std::endl; return false; -- cgit v0.12 From 1c4e3cf9870df591c60dc7d59df2b98b8eb534b0 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 29 Jun 2010 16:48:49 +0200 Subject: Add support for comments and meta-data in the lupdate QtScript frontend This brings the support more in line with the C++ frontend. No support for magic TRANSLATOR comment yet. Add a proper test for the QtScript parser. Support for qtTrId() and friends coming in a separate commit. Previously the lexer just skipped comments. Now the contents of each comment is retained and passed to a "comment processor" interface. The parser implements the interface and performs logic very similar to the C++ parser to extract data from the comment. Task-number: QTBUG-11774 Reviewed-by: Oswald Buddenhagen --- .../linguist/lupdate/testdata/good/parsejs/main.js | 83 ++++++++++ .../lupdate/testdata/good/parsejs/project.pro | 3 + .../testdata/good/parsejs/project.ts.result | 168 ++++++++++++++++++++ tools/linguist/lupdate/qscript.cpp | 159 +++++++++++++++++-- tools/linguist/lupdate/qscript.g | 174 +++++++++++++++++++-- 5 files changed, 565 insertions(+), 22 deletions(-) create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs/main.js create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs/project.pro create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js b/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js new file mode 100644 index 0000000..edd7529 --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js @@ -0,0 +1,83 @@ +qsTr("One"); +qsTranslate("FooContext", "Two"); + +var greeting_strings = [ + QT_TR_NOOP("Hello"), + QT_TRANSLATE_NOOP("FooContext", "Goodbye") +]; + +qsTr("One", "not the same one"); + +//: My first comment. +qsTr("See comment"); + +//: My second comment. +qsTranslate("BarContext", "See other comment"); + +//: My third comment +//: spans two lines. +qsTr("The comment explains it all"); + +//: My fourth comment +//: spans a whopping +//: three lines. +qsTranslate("BazContext", "It should be clear by now"); + +/*: C-style comment. */ +qsTr("I love C++"); + +/*: Another C-style comment. */ +qsTranslate("FooContext", "I really love C++"); + +/*: C-style comment, followed by */ +/*: another one. */ +qsTr("Qt is the best"); + +/*: Another C-style comment, followed by */ +/*: yet another one. */ +qsTranslate("BarContext", "Qt is the very best"); + +// This comment doesn't have any effect. +qsTr("The comment had no effect"); + +// This comment doesn't have any effect either. +qsTranslate("BazContext", "The comment had no effect, really"); + +/* This C-style comment doesn't have any effect. */ +qsTr("No comment to your comment"); + +/* This C-style comment doesn't have any effect either. */ +qsTranslate("FooContext", "I refuse to comment on that"); + +//= id_foo +qsTr("This string has an identifier"); + +//= id_bar +qsTranslate("BarContext", "This string also has an identifier"); + +//~ loc-blank False +qsTr("This string has meta-data"); + +//~ loc-layout_id foo_dialog +qsTranslate("BazContext", "This string also has meta-data"); + +// This comment is to be ignored. +//: This is a comment for the translator. +//= id_baz +//~ foo 123 +//~ magic-stuff This means something special. +qsTr("This string has a lot of information"); + +// This comment is also to be ignored. +//: This is another comment for the translator. +//= id_babar +//~ foo-bar Important stuff +//~ needle-in-haystack Found +//~ overflow True +qsTranslate("FooContext", "This string has even more information"); + +qsTr("This string has disambiguation", "Disambiguation"); + +qsTranslate("BarContext", "This string also has disambiguation", "Another disambiguation"); + +qsTr("This string contains plurals", "", 10); diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs/project.pro b/tests/auto/linguist/lupdate/testdata/good/parsejs/project.pro new file mode 100644 index 0000000..d549039 --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs/project.pro @@ -0,0 +1,3 @@ +SOURCES += main.js + +TRANSLATIONS = project.ts diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result new file mode 100644 index 0000000..d2016de --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result @@ -0,0 +1,168 @@ + + + + + BarContext + + + See other comment + My second comment. + + + + + Qt is the very best + Another C-style comment, followed by yet another one. + + + + + This string also has an identifier + + + + + This string also has disambiguation + Another disambiguation + + + + + BazContext + + + It should be clear by now + My fourth comment spans a whopping three lines. + + + + + The comment had no effect, really + + + + + This string also has meta-data + + foo_dialog + + + + FooContext + + + Two + + + + + Goodbye + + + + + I really love C++ + Another C-style comment. + + + + + I refuse to comment on that + + + + + This string has even more information + This is another comment for the translator. + + Found + True + Important stuff + + + + main + + + One + + + + + Hello + + + + + One + not the same one + + + + + See comment + My first comment. + + + + + The comment explains it all + My third comment spans two lines. + + + + + I love C++ + C-style comment. + + + + + Qt is the best + C-style comment, followed by another one. + + + + + The comment had no effect + + + + + No comment to your comment + + + + + This string has an identifier + + + + + This string has meta-data + + False + + + + This string has a lot of information + This is a comment for the translator. + + 123 + This means something special. + + + + This string has disambiguation + Disambiguation + + + + + This string contains plurals + + + + + + diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp index 08670ac..dc885a3 100644 --- a/tools/linguist/lupdate/qscript.cpp +++ b/tools/linguist/lupdate/qscript.cpp @@ -770,13 +770,16 @@ const int QScriptGrammar::action_check [] = { static void recordMessage( Translator *tor, const QString &context, const QString &text, const QString &comment, - const QString &extracomment, bool plural, const QString &fileName, int lineNo) + const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra, + bool plural, const QString &fileName, int lineNo) { TranslatorMessage msg( context, text, comment, QString(), fileName, lineNo, QStringList(), TranslatorMessage::Unfinished, plural); msg.setExtraComment(extracomment.simplified()); + msg.setId(msgid); + msg.setExtras(extra); tor->extend(msg); } @@ -784,10 +787,17 @@ static void recordMessage( namespace QScript { +class CommentProcessor +{ +public: + virtual ~CommentProcessor() {} + virtual void processComment(const QChar *chars, int length) = 0; +}; + class Lexer { public: - Lexer(); + Lexer(CommentProcessor *); ~Lexer(); void setCode(const QString &c, const QString &fileName, int lineno); @@ -927,6 +937,8 @@ private: void syncProhibitAutomaticSemicolon(); + void processComment(const QChar *, int); + const QChar *code; uint length; int yycolumn; @@ -953,6 +965,8 @@ private: ParenthesesState parenthesesState; int parenthesesCount; bool prohibitAutomaticSemicolon; + + CommentProcessor *commentProcessor; }; } // namespace QScript @@ -1029,7 +1043,7 @@ double integerFromString(const char *buf, int size, int radix) } // namespace QScript -QScript::Lexer::Lexer() +QScript::Lexer::Lexer(QScript::CommentProcessor *proc) : yylineno(0), size8(128), size16(128), restrKeyword(false), @@ -1040,7 +1054,8 @@ QScript::Lexer::Lexer() err(NoError), check_reserved(true), parenthesesState(IgnoreParentheses), - prohibitAutomaticSemicolon(false) + prohibitAutomaticSemicolon(false), + commentProcessor(proc) { // allocate space for read buffers buffer8 = new char[size8]; @@ -1407,10 +1422,12 @@ int QScript::Lexer::lex() } else if (current == '/' && next1 == '/') { recordStartPos(); shift(1); + Q_ASSERT(pos16 == 0); state = InSingleLineComment; } else if (current == '/' && next1 == '*') { recordStartPos(); shift(1); + Q_ASSERT(pos16 == 0); state = InMultiLineComment; } else if (current == 0) { syncProhibitAutomaticSemicolon(); @@ -1550,9 +1567,12 @@ int QScript::Lexer::lex() break; case InSingleLineComment: if (isLineTerminator()) { + record16(current); // include newline + processComment(buffer16, pos16); shiftWindowsLineBreak(); yylineno++; yycolumn = 0; + pos16 = 0; terminator = true; bol = true; if (restrKeyword) { @@ -1562,6 +1582,8 @@ int QScript::Lexer::lex() state = Start; } else if (current == 0) { setDone(Eof); + } else { + record16(current); } break; case InMultiLineComment: @@ -1573,8 +1595,12 @@ int QScript::Lexer::lex() shiftWindowsLineBreak(); yylineno++; } else if (current == '*' && next1 == '/') { + processComment(buffer16, pos16); + pos16 = 0; state = Start; shift(1); + } else { + record16(current); } break; case InIdentifier: @@ -2036,10 +2062,15 @@ void QScript::Lexer::syncProhibitAutomaticSemicolon() } } +void QScript::Lexer::processComment(const QChar *chars, int length) +{ + commentProcessor->processComment(chars, length); +} + class Translator; -class QScriptParser: protected QScriptGrammar +class QScriptParser: protected QScriptGrammar, public QScript::CommentProcessor { public: QVariant val; @@ -2079,6 +2110,8 @@ protected: std::ostream &yyMsg(int line = 0); + virtual void processComment(const QChar *, int); + protected: int tos; int stack_size; @@ -2091,6 +2124,10 @@ protected: private: QScript::Lexer *lexer; + QString extracomment; + QString msgid; + QString sourcetext; + TranslatorMessage::ExtraData extra; }; inline void QScriptParser::reallocateStack() @@ -2229,6 +2266,8 @@ case 8: { case 66: { QString name = sym(1).toString(); if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) { + if (!sourcetext.isEmpty()) + yyMsg(identLineNo) << "//% cannot be used with " << qPrintable(name) << "(). Ignoring\n"; QVariantList args = sym(2).toList(); if (args.size() < 2) { yyMsg(identLineNo) << qPrintable(name) << "() requires at least two arguments.\n"; @@ -2240,13 +2279,18 @@ case 66: { QString context = args.at(0).toString(); QString text = args.at(1).toString(); QString comment = args.value(2).toString(); - QString extracomment; bool plural = (args.size() > 4); recordMessage(translator, context, text, comment, extracomment, - plural, fileName(), identLineNo); + msgid, extra, plural, fileName(), identLineNo); } } + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { + if (!sourcetext.isEmpty()) + yyMsg(identLineNo) << "//% cannot be used with " << qPrintable(name) << "(). Ignoring\n"; QVariantList args = sym(2).toList(); if (args.size() < 1) { yyMsg(identLineNo) << qPrintable(name) << "() requires at least one argument.\n"; @@ -2257,12 +2301,15 @@ case 66: { QString context = QFileInfo(fileName()).baseName(); QString text = args.at(0).toString(); QString comment = args.value(1).toString(); - QString extracomment; bool plural = (args.size() > 2); recordMessage(translator, context, text, comment, extracomment, - plural, fileName(), identLineNo); + msgid, extra, plural, fileName(), identLineNo); } } + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); } } break; @@ -2289,6 +2336,44 @@ case 94: { sym(1) = QVariant(); } break; + case 171: + + case 172: + + case 173: + + case 174: + + case 175: + + case 176: + + case 177: + + case 178: + + case 179: + + case 180: + + case 181: + + case 182: + + case 183: + + case 184: + + case 185: + if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) { + yyMsg() << "Discarding unconsumed meta data\n"; + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); + } + break; + } // switch state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT); @@ -2372,6 +2457,58 @@ std::ostream &QScriptParser::yyMsg(int line) return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": "; } +void QScriptParser::processComment(const QChar *chars, int length) +{ + if (!length) + return; + // Try to match the logic of the C++ parser. + if (*chars == QLatin1Char(':') && chars[1].isSpace()) { + extracomment += QString(chars+2, length-2); + } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) { + msgid = QString(chars+2, length-2).simplified(); + } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) { + QString text = QString(chars+2, length-2).trimmed(); + int k = text.indexOf(QLatin1Char(' ')); + if (k > -1) + extra.insert(text.left(k), text.mid(k + 1).trimmed()); + } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) { + sourcetext.reserve(sourcetext.length() + length-2); + ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length(); + int p = 2, c; + forever { + if (p >= length) + break; + c = chars[p++].unicode(); + if (isspace(c)) + continue; + if (c != '"') { + yyMsg() << "Unexpected character in meta string\n"; + break; + } + forever { + if (p >= length) { + whoops: + yyMsg() << "Unterminated meta string\n"; + break; + } + c = chars[p++].unicode(); + if (c == '"') + break; + if (c == '\\') { + if (p >= length) + goto whoops; + c = chars[p++].unicode(); + if (c == '\n') + goto whoops; + *ptr++ = '\\'; + } + *ptr++ = c; + } + } + sourcetext.resize(ptr - (ushort *)sourcetext.data()); + } +} + bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { @@ -2391,9 +2528,9 @@ bool loadQScript(Translator &translator, const QString &filename, ConversionData ts.setAutoDetectUnicode(true); QString code = ts.readAll(); - QScript::Lexer lexer; - lexer.setCode(code, filename, /*lineNumber=*/1); QScriptParser parser; + QScript::Lexer lexer(&parser); + lexer.setCode(code, filename, /*lineNumber=*/1); parser.setLexer(&lexer); if (!parser.parse(&translator)) { std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": " diff --git a/tools/linguist/lupdate/qscript.g b/tools/linguist/lupdate/qscript.g index 2f82a55..200b641 100644 --- a/tools/linguist/lupdate/qscript.g +++ b/tools/linguist/lupdate/qscript.g @@ -101,13 +101,16 @@ QT_BEGIN_NAMESPACE static void recordMessage( Translator *tor, const QString &context, const QString &text, const QString &comment, - const QString &extracomment, bool plural, const QString &fileName, int lineNo) + const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra, + bool plural, const QString &fileName, int lineNo) { TranslatorMessage msg( context, text, comment, QString(), fileName, lineNo, QStringList(), TranslatorMessage::Unfinished, plural); msg.setExtraComment(extracomment.simplified()); + msg.setId(msgid); + msg.setExtras(extra); tor->extend(msg); } @@ -115,10 +118,17 @@ static void recordMessage( namespace QScript { +class CommentProcessor +{ +public: + virtual ~CommentProcessor() {} + virtual void processComment(const QChar *chars, int length) = 0; +}; + class Lexer { public: - Lexer(); + Lexer(CommentProcessor *); ~Lexer(); void setCode(const QString &c, const QString &fileName, int lineno); @@ -258,6 +268,8 @@ private: void syncProhibitAutomaticSemicolon(); + void processComment(const QChar *, int); + const QChar *code; uint length; int yycolumn; @@ -284,6 +296,8 @@ private: ParenthesesState parenthesesState; int parenthesesCount; bool prohibitAutomaticSemicolon; + + CommentProcessor *commentProcessor; }; } // namespace QScript @@ -360,7 +374,7 @@ double integerFromString(const char *buf, int size, int radix) } // namespace QScript -QScript::Lexer::Lexer() +QScript::Lexer::Lexer(QScript::CommentProcessor *proc) : yylineno(0), size8(128), size16(128), restrKeyword(false), @@ -371,7 +385,8 @@ QScript::Lexer::Lexer() err(NoError), check_reserved(true), parenthesesState(IgnoreParentheses), - prohibitAutomaticSemicolon(false) + prohibitAutomaticSemicolon(false), + commentProcessor(proc) { // allocate space for read buffers buffer8 = new char[size8]; @@ -738,10 +753,12 @@ int QScript::Lexer::lex() } else if (current == '/' && next1 == '/') { recordStartPos(); shift(1); + Q_ASSERT(pos16 == 0); state = InSingleLineComment; } else if (current == '/' && next1 == '*') { recordStartPos(); shift(1); + Q_ASSERT(pos16 == 0); state = InMultiLineComment; } else if (current == 0) { syncProhibitAutomaticSemicolon(); @@ -881,9 +898,12 @@ int QScript::Lexer::lex() break; case InSingleLineComment: if (isLineTerminator()) { + record16(current); // include newline + processComment(buffer16, pos16); shiftWindowsLineBreak(); yylineno++; yycolumn = 0; + pos16 = 0; terminator = true; bol = true; if (restrKeyword) { @@ -893,6 +913,8 @@ int QScript::Lexer::lex() state = Start; } else if (current == 0) { setDone(Eof); + } else { + record16(current); } break; case InMultiLineComment: @@ -904,8 +926,12 @@ int QScript::Lexer::lex() shiftWindowsLineBreak(); yylineno++; } else if (current == '*' && next1 == '/') { + processComment(buffer16, pos16); + pos16 = 0; state = Start; shift(1); + } else { + record16(current); } break; case InIdentifier: @@ -1367,10 +1393,15 @@ void QScript::Lexer::syncProhibitAutomaticSemicolon() } } +void QScript::Lexer::processComment(const QChar *chars, int length) +{ + commentProcessor->processComment(chars, length); +} + class Translator; -class QScriptParser: protected $table +class QScriptParser: protected $table, public QScript::CommentProcessor { public: QVariant val; @@ -1410,6 +1441,8 @@ protected: std::ostream &yyMsg(int line = 0); + virtual void processComment(const QChar *, int); + protected: int tos; int stack_size; @@ -1422,6 +1455,10 @@ protected: private: QScript::Lexer *lexer; + QString extracomment; + QString msgid; + QString sourcetext; + TranslatorMessage::ExtraData extra; }; inline void QScriptParser::reallocateStack() @@ -1645,6 +1682,8 @@ CallExpression: MemberExpression Arguments ; case $rule_number: { QString name = sym(1).toString(); if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) { + if (!sourcetext.isEmpty()) + yyMsg(identLineNo) << "//% cannot be used with " << qPrintable(name) << "(). Ignoring\n"; QVariantList args = sym(2).toList(); if (args.size() < 2) { yyMsg(identLineNo) << qPrintable(name) << "() requires at least two arguments.\n"; @@ -1656,13 +1695,18 @@ case $rule_number: { QString context = args.at(0).toString(); QString text = args.at(1).toString(); QString comment = args.value(2).toString(); - QString extracomment; bool plural = (args.size() > 4); recordMessage(translator, context, text, comment, extracomment, - plural, fileName(), identLineNo); + msgid, extra, plural, fileName(), identLineNo); } } + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { + if (!sourcetext.isEmpty()) + yyMsg(identLineNo) << "//% cannot be used with " << qPrintable(name) << "(). Ignoring\n"; QVariantList args = sym(2).toList(); if (args.size() < 1) { yyMsg(identLineNo) << qPrintable(name) << "() requires at least one argument.\n"; @@ -1673,12 +1717,15 @@ case $rule_number: { QString context = QFileInfo(fileName()).baseName(); QString text = args.at(0).toString(); QString comment = args.value(1).toString(); - QString extracomment; bool plural = (args.size() > 2); recordMessage(translator, context, text, comment, extracomment, - plural, fileName(), identLineNo); + msgid, extra, plural, fileName(), identLineNo); } } + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); } } break; ./ @@ -1824,20 +1871,73 @@ ExpressionNotInOpt: ; ExpressionNotInOpt: ExpressionNotIn ; Statement: Block ; +/. + case $rule_number: +./ Statement: VariableStatement ; +/. + case $rule_number: +./ Statement: EmptyStatement ; +/. + case $rule_number: +./ Statement: ExpressionStatement ; +/. + case $rule_number: +./ Statement: IfStatement ; +/. + case $rule_number: +./ Statement: IterationStatement ; +/. + case $rule_number: +./ Statement: ContinueStatement ; +/. + case $rule_number: +./ Statement: BreakStatement ; +/. + case $rule_number: +./ Statement: ReturnStatement ; +/. + case $rule_number: +./ Statement: WithStatement ; +/. + case $rule_number: +./ Statement: LabelledStatement ; +/. + case $rule_number: +./ Statement: SwitchStatement ; +/. + case $rule_number: +./ Statement: ThrowStatement ; +/. + case $rule_number: +./ Statement: TryStatement ; +/. + case $rule_number: +./ Statement: DebuggerStatement ; +/. + case $rule_number: + if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) { + yyMsg() << "Discarding unconsumed meta data\n"; + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); + } + break; +./ Block: T_LBRACE StatementListOpt T_RBRACE ; StatementList: Statement ; @@ -2004,6 +2104,58 @@ std::ostream &QScriptParser::yyMsg(int line) return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": "; } +void QScriptParser::processComment(const QChar *chars, int length) +{ + if (!length) + return; + // Try to match the logic of the C++ parser. + if (*chars == QLatin1Char(':') && chars[1].isSpace()) { + extracomment += QString(chars+2, length-2); + } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) { + msgid = QString(chars+2, length-2).simplified(); + } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) { + QString text = QString(chars+2, length-2).trimmed(); + int k = text.indexOf(QLatin1Char(' ')); + if (k > -1) + extra.insert(text.left(k), text.mid(k + 1).trimmed()); + } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) { + sourcetext.reserve(sourcetext.length() + length-2); + ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length(); + int p = 2, c; + forever { + if (p >= length) + break; + c = chars[p++].unicode(); + if (isspace(c)) + continue; + if (c != '"') { + yyMsg() << "Unexpected character in meta string\n"; + break; + } + forever { + if (p >= length) { + whoops: + yyMsg() << "Unterminated meta string\n"; + break; + } + c = chars[p++].unicode(); + if (c == '"') + break; + if (c == '\\') { + if (p >= length) + goto whoops; + c = chars[p++].unicode(); + if (c == '\n') + goto whoops; + *ptr++ = '\\'; + } + *ptr++ = c; + } + } + sourcetext.resize(ptr - (ushort *)sourcetext.data()); + } +} + bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { @@ -2023,9 +2175,9 @@ bool loadQScript(Translator &translator, const QString &filename, ConversionData ts.setAutoDetectUnicode(true); QString code = ts.readAll(); - QScript::Lexer lexer; - lexer.setCode(code, filename, /*lineNumber=*/1); QScriptParser parser; + QScript::Lexer lexer(&parser); + lexer.setCode(code, filename, /*lineNumber=*/1); parser.setLexer(&lexer); if (!parser.parse(&translator)) { std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": " -- cgit v0.12 From a5867a413d14f307c0f20b7080840b15bd8743e4 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 29 Jun 2010 16:56:18 +0200 Subject: Make lupdate's QtScript frontend recognize qsTrId() / QT_TRID_NOOP() Extracting the translation data is only the first step; a separate commit will make the functions available in the QtScript runtime. Task-number: QTBUG-8454 Reviewed-by: Oswald Buddenhagen --- .../linguist/lupdate/testdata/good/parsejs/main.js | 8 +++++++ .../testdata/good/parsejs/project.ts.result | 27 ++++++++++++++++++++++ tools/linguist/lupdate/qscript.cpp | 20 ++++++++++++++++ tools/linguist/lupdate/qscript.g | 20 ++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js b/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js index edd7529..9f61cea 100644 --- a/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs/main.js @@ -81,3 +81,11 @@ qsTr("This string has disambiguation", "Disambiguation"); qsTranslate("BarContext", "This string also has disambiguation", "Another disambiguation"); qsTr("This string contains plurals", "", 10); + +qsTrId("qtn_foo_bar"); + +var more_greeting_strings = [ QT_TRID_NOOP("qtn_needle"), QT_TRID_NOOP("qtn_haystack") ]; + +//: qsTrId() with comment, meta-data and plurals. +//~ well-tested True +qsTrId("qtn_bar_baz", 10); diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result index d2016de..d03c713 100644 --- a/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs/project.ts.result @@ -2,6 +2,33 @@ + + + + + + + + + + + + + + + + + + + + qsTrId() with comment, meta-data and plurals. + + + + True + + + BarContext diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp index dc885a3..7ca0987 100644 --- a/tools/linguist/lupdate/qscript.cpp +++ b/tools/linguist/lupdate/qscript.cpp @@ -2310,6 +2310,26 @@ case 66: { extracomment.clear(); msgid.clear(); extra.clear(); + } else if ((name == QLatin1String("qsTrId")) || (name == QLatin1String("QT_TRID_NOOP"))) { + if (!msgid.isEmpty()) + yyMsg(identLineNo) << "//= cannot be used with " << qPrintable(name) << "(). Ignoring\n"; + QVariantList args = sym(2).toList(); + if (args.size() < 1) { + yyMsg(identLineNo) << qPrintable(name) << "() requires at least one argument.\n"; + } else { + if (args.at(0).type() != QVariant::String) { + yyMsg(identLineNo) << qPrintable(name) << "(): identifier must be a literal string.\n"; + } else { + msgid = args.at(0).toString(); + bool plural = (args.size() > 1); + recordMessage(translator, QString(), sourcetext, QString(), extracomment, + msgid, extra, plural, fileName(), identLineNo); + } + } + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); } } break; diff --git a/tools/linguist/lupdate/qscript.g b/tools/linguist/lupdate/qscript.g index 200b641..e4c2d22 100644 --- a/tools/linguist/lupdate/qscript.g +++ b/tools/linguist/lupdate/qscript.g @@ -1726,6 +1726,26 @@ case $rule_number: { extracomment.clear(); msgid.clear(); extra.clear(); + } else if ((name == QLatin1String("qsTrId")) || (name == QLatin1String("QT_TRID_NOOP"))) { + if (!msgid.isEmpty()) + yyMsg(identLineNo) << "//= cannot be used with " << qPrintable(name) << "(). Ignoring\n"; + QVariantList args = sym(2).toList(); + if (args.size() < 1) { + yyMsg(identLineNo) << qPrintable(name) << "() requires at least one argument.\n"; + } else { + if (args.at(0).type() != QVariant::String) { + yyMsg(identLineNo) << qPrintable(name) << "(): identifier must be a literal string.\n"; + } else { + msgid = args.at(0).toString(); + bool plural = (args.size() > 1); + recordMessage(translator, QString(), sourcetext, QString(), extracomment, + msgid, extra, plural, fileName(), identLineNo); + } + } + sourcetext.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); } } break; ./ -- cgit v0.12 From d1c482180352aeecabcc8d9f65ab3883175c75c3 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 29 Jun 2010 17:06:15 +0200 Subject: Make qsTrId() / QT_TRID_NOOP() accessible from QtScript QScriptEngine::installTranslatorFunctions() now installs wrapper functions for qsTrId and QT_TRID_NOOP (similar to the existing ones for tr() and translate()). Task-number: QTBUG-8454 Reviewed-by: Jedrzej Nowacki --- src/script/api/qscriptengine.cpp | 28 +++++++++ tests/auto/qscriptengine/idtranslatable.js | 5 ++ tests/auto/qscriptengine/qscriptengine.qrc | 1 + .../translations/idtranslatable_la.qm | Bin 0 -> 342 bytes .../translations/idtranslatable_la.ts | 30 +++++++++ tests/auto/qscriptengine/tst_qscriptengine.cpp | 69 +++++++++++++++++++++ 6 files changed, 133 insertions(+) create mode 100644 tests/auto/qscriptengine/idtranslatable.js create mode 100644 tests/auto/qscriptengine/translations/idtranslatable_la.qm create mode 100644 tests/auto/qscriptengine/translations/idtranslatable_la.ts diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index 8560214..8347626 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -782,6 +782,8 @@ static JSC::JSValue JSC_HOST_CALL functionQsTranslate(JSC::ExecState*, JSC::JSOb static JSC::JSValue JSC_HOST_CALL functionQsTranslateNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); static JSC::JSValue JSC_HOST_CALL functionQsTr(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); static JSC::JSValue JSC_HOST_CALL functionQsTrNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTrId(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTrIdNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); JSC::JSValue JSC_HOST_CALL functionQsTranslate(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) { @@ -892,6 +894,28 @@ JSC::JSValue JSC_HOST_CALL functionQsTrNoOp(JSC::ExecState *, JSC::JSObject*, JS return args.at(0); } +JSC::JSValue JSC_HOST_CALL functionQsTrId(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 1) + return JSC::throwError(exec, JSC::GeneralError, "qsTrId() requires at least one argument"); + if (!args.at(0).isString()) + return JSC::throwError(exec, JSC::TypeError, "qsTrId(): first argument (id) must be a string"); + if ((args.size() > 1) && !args.at(1).isNumber()) + return JSC::throwError(exec, JSC::TypeError, "qsTrId(): second argument (n) must be a number"); + JSC::UString id = args.at(0).toString(exec); + int n = -1; + if (args.size() > 1) + n = args.at(1).toInt32(exec); + return JSC::jsString(exec, qtTrId(QScript::convertToLatin1(id).constData(), n)); +} + +JSC::JSValue JSC_HOST_CALL functionQsTrIdNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 1) + return JSC::jsUndefined(); + return args.at(0); +} + static JSC::JSValue JSC_HOST_CALL stringProtoFuncArg(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); JSC::JSValue JSC_HOST_CALL stringProtoFuncArg(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisObject, const JSC::ArgList &args) @@ -3435,6 +3459,8 @@ void QScriptEngine::registerCustomType(int type, MarshalFunction mf, \row \o QT_TR_NOOP() \o QT_TR_NOOP() \row \o qsTranslate() \o QCoreApplication::translate() \row \o QT_TRANSLATE_NOOP() \o QT_TRANSLATE_NOOP() + \row \o qsTrId() (since 4.7) \o qtTrId() + \row \o QT_TRID_NOOP() (since 4.7) \o QT_TRID_NOOP() \endtable \sa {Internationalization with Qt} @@ -3453,6 +3479,8 @@ void QScriptEngine::installTranslatorFunctions(const QScriptValue &object) JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 2, JSC::Identifier(exec, "QT_TRANSLATE_NOOP"), QScript::functionQsTranslateNoOp)); JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 3, JSC::Identifier(exec, "qsTr"), QScript::functionQsTr)); JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "QT_TR_NOOP"), QScript::functionQsTrNoOp)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "qsTrId"), QScript::functionQsTrId)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "QT_TRID_NOOP"), QScript::functionQsTrIdNoOp)); glob->stringPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "arg"), QScript::stringProtoFuncArg)); } diff --git a/tests/auto/qscriptengine/idtranslatable.js b/tests/auto/qscriptengine/idtranslatable.js new file mode 100644 index 0000000..554ca88 --- /dev/null +++ b/tests/auto/qscriptengine/idtranslatable.js @@ -0,0 +1,5 @@ +qsTrId("qtn_foo_bar"); + +var more_greeting_strings = [ QT_TRID_NOOP("qtn_needle"), QT_TRID_NOOP("qtn_haystack") ]; + +qsTrId("qtn_bar_baz", 10); diff --git a/tests/auto/qscriptengine/qscriptengine.qrc b/tests/auto/qscriptengine/qscriptengine.qrc index b87f985..fa55a5b 100644 --- a/tests/auto/qscriptengine/qscriptengine.qrc +++ b/tests/auto/qscriptengine/qscriptengine.qrc @@ -1,5 +1,6 @@ translations/translatable_la.qm + translations/idtranslatable_la.qm diff --git a/tests/auto/qscriptengine/translations/idtranslatable_la.qm b/tests/auto/qscriptengine/translations/idtranslatable_la.qm new file mode 100644 index 0000000..c8c0b72 Binary files /dev/null and b/tests/auto/qscriptengine/translations/idtranslatable_la.qm differ diff --git a/tests/auto/qscriptengine/translations/idtranslatable_la.ts b/tests/auto/qscriptengine/translations/idtranslatable_la.ts new file mode 100644 index 0000000..b6d7053 --- /dev/null +++ b/tests/auto/qscriptengine/translations/idtranslatable_la.ts @@ -0,0 +1,30 @@ + + + + + + + + + First string + + + + + Second string + + + + + Third string + + + + + + Fourth string + %n fooish bar(s) found + + + + diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 7a732cc..4cff043 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -162,6 +162,7 @@ private slots: void translateWithInvalidArgs(); void translationContext_data(); void translationContext(); + void translateScriptIdBased(); void functionScopes(); void nativeFunctionScopes(); void evaluateProgram(); @@ -4386,6 +4387,8 @@ void tst_QScriptEngine::installTranslatorFunctions() QVERIFY(!global.property("QT_TRANSLATE_NOOP").isValid()); QVERIFY(!global.property("qsTr").isValid()); QVERIFY(!global.property("QT_TR_NOOP").isValid()); + QVERIFY(!global.property("qsTrId").isValid()); + QVERIFY(!global.property("QT_TRID_NOOP").isValid()); QVERIFY(!globalOrig.property("String").property("prototype").property("arg").isValid()); eng.installTranslatorFunctions(); @@ -4393,6 +4396,8 @@ void tst_QScriptEngine::installTranslatorFunctions() QVERIFY(global.property("QT_TRANSLATE_NOOP").isFunction()); QVERIFY(global.property("qsTr").isFunction()); QVERIFY(global.property("QT_TR_NOOP").isFunction()); + QVERIFY(global.property("qsTrId").isFunction()); + QVERIFY(global.property("QT_TRID_NOOP").isFunction()); QVERIFY(globalOrig.property("String").property("prototype").property("arg").isFunction()); if (useCustomGlobalObject) { @@ -4400,6 +4405,8 @@ void tst_QScriptEngine::installTranslatorFunctions() QVERIFY(!globalOrig.property("QT_TRANSLATE_NOOP").isValid()); QVERIFY(!globalOrig.property("qsTr").isValid()); QVERIFY(!globalOrig.property("QT_TR_NOOP").isValid()); + QVERIFY(!globalOrig.property("qsTrId").isValid()); + QVERIFY(!globalOrig.property("QT_TRID_NOOP").isValid()); } { @@ -4427,6 +4434,17 @@ void tst_QScriptEngine::installTranslatorFunctions() QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("foobar")); } + + { + QScriptValue ret = eng.evaluate("qsTrId('foo')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QScriptValue ret = eng.evaluate("QT_TRID_NOOP('foo')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } } static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng) @@ -4537,6 +4555,10 @@ void tst_QScriptEngine::translateWithInvalidArgs_data() QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string"; QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number"; QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'"; + + QTest::newRow("qsTrId()") << "qsTrId()" << "Error: qsTrId() requires at least one argument"; + QTest::newRow("qsTrId(123)") << "qsTrId(123)" << "TypeError: qsTrId(): first argument (id) must be a string"; + QTest::newRow("qsTrId('foo', 'bar')") << "qsTrId('foo', 'bar')" << "TypeError: qsTrId(): second argument (n) must be a number"; } void tst_QScriptEngine::translateWithInvalidArgs() @@ -4598,6 +4620,53 @@ void tst_QScriptEngine::translationContext() QCoreApplication::instance()->removeTranslator(&translator); } +void tst_QScriptEngine::translateScriptIdBased() +{ + QScriptEngine engine; + + QTranslator translator; + translator.load(":/translations/idtranslatable_la"); + QCoreApplication::instance()->installTranslator(&translator); + engine.installTranslatorFunctions(); + + QString fileName = QString::fromLatin1("idtranslatable.js"); + + QHash expectedTranslations; + expectedTranslations["qtn_foo_bar"] = "First string"; + expectedTranslations["qtn_needle"] = "Second string"; + expectedTranslations["qtn_haystack"] = "Third string"; + expectedTranslations["qtn_bar_baz"] = "Fourth string"; + + QHash::const_iterator it; + for (it = expectedTranslations.constBegin(); it != expectedTranslations.constEnd(); ++it) { + for (int x = 0; x < 2; ++x) { + QString fn; + if (x) + fn = fileName; + // Top-level + QCOMPARE(engine.evaluate(QString::fromLatin1("qsTrId('%0')") + .arg(it.key()), fn).toString(), + it.value()); + QCOMPARE(engine.evaluate(QString::fromLatin1("QT_TRID_NOOP('%0')") + .arg(it.key()), fn).toString(), + it.key()); + // From function + QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return qsTrId('%0'); })()") + .arg(it.key()), fn).toString(), + it.value()); + QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return QT_TRID_NOOP('%0'); })()") + .arg(it.key()), fn).toString(), + it.key()); + } + } + + // Plural form + QCOMPARE(engine.evaluate("qsTrId('qtn_bar_baz', 10)").toString(), + QString::fromLatin1("10 fooish bar(s) found")); + QCOMPARE(engine.evaluate("qsTrId('qtn_foo_bar', 10)").toString(), + QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural +} + void tst_QScriptEngine::functionScopes() { QScriptEngine eng; -- cgit v0.12 From 2d60953c4664958c665114c52ce2b92ed11b1488 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 29 Jun 2010 15:28:00 +0200 Subject: Add test that exercises lupdate warnings for QtScript Reviewed-by: Oswald Buddenhagen --- .../testdata/good/parsejs2/expectedoutput.txt | 29 +++++++++++ .../lupdate/testdata/good/parsejs2/main.js | 56 ++++++++++++++++++++++ .../lupdate/testdata/good/parsejs2/project.pro | 3 ++ .../testdata/good/parsejs2/project.ts.result | 30 ++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs2/expectedoutput.txt create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs2/main.js create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs2/project.pro create mode 100644 tests/auto/linguist/lupdate/testdata/good/parsejs2/project.ts.result diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs2/expectedoutput.txt b/tests/auto/linguist/lupdate/testdata/good/parsejs2/expectedoutput.txt new file mode 100644 index 0000000..d6c977f --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs2/expectedoutput.txt @@ -0,0 +1,29 @@ +.*/lupdate/testdata/good/parsejs2/main.js:3: qsTranslate\(\) requires at least two arguments. +.*/lupdate/testdata/good/parsejs2/main.js:4: qsTranslate\(\) requires at least two arguments. +.*/lupdate/testdata/good/parsejs2/main.js:5: qsTranslate\(\): both arguments must be literal strings. +.*/lupdate/testdata/good/parsejs2/main.js:6: qsTranslate\(\): both arguments must be literal strings. +.*/lupdate/testdata/good/parsejs2/main.js:7: qsTranslate\(\): both arguments must be literal strings. +.*/lupdate/testdata/good/parsejs2/main.js:9: QT_TRANSLATE_NOOP\(\) requires at least two arguments. +.*/lupdate/testdata/good/parsejs2/main.js:10: QT_TRANSLATE_NOOP\(\) requires at least two arguments. +.*/lupdate/testdata/good/parsejs2/main.js:11: QT_TRANSLATE_NOOP\(\): both arguments must be literal strings. +.*/lupdate/testdata/good/parsejs2/main.js:12: QT_TRANSLATE_NOOP\(\): both arguments must be literal strings. +.*/lupdate/testdata/good/parsejs2/main.js:13: QT_TRANSLATE_NOOP\(\): both arguments must be literal strings. +.*/lupdate/testdata/good/parsejs2/main.js:15: qsTr\(\) requires at least one argument. +.*/lupdate/testdata/good/parsejs2/main.js:16: qsTr\(\): text to translate must be a literal string. +.*/lupdate/testdata/good/parsejs2/main.js:18: QT_TR_NOOP\(\) requires at least one argument. +.*/lupdate/testdata/good/parsejs2/main.js:19: QT_TR_NOOP\(\): text to translate must be a literal string. +.*/lupdate/testdata/good/parsejs2/main.js:21: qsTrId\(\) requires at least one argument. +.*/lupdate/testdata/good/parsejs2/main.js:22: qsTrId\(\): identifier must be a literal string. +.*/lupdate/testdata/good/parsejs2/main.js:24: QT_TRID_NOOP\(\) requires at least one argument. +.*/lupdate/testdata/good/parsejs2/main.js:25: QT_TRID_NOOP\(\): identifier must be a literal string. +.*/lupdate/testdata/good/parsejs2/main.js:27: Unexpected character in meta string +.*/lupdate/testdata/good/parsejs2/main.js:28: Unexpected character in meta string +.*/lupdate/testdata/good/parsejs2/main.js:29: Unterminated meta string +.*/lupdate/testdata/good/parsejs2/main.js:30: Unterminated meta string +.*/lupdate/testdata/good/parsejs2/main.js:33: //% cannot be used with qsTranslate\(\). Ignoring +.*/lupdate/testdata/good/parsejs2/main.js:35: //% cannot be used with QT_TRANSLATE_NOOP\(\). Ignoring +.*/lupdate/testdata/good/parsejs2/main.js:37: //% cannot be used with qsTr\(\). Ignoring +.*/lupdate/testdata/good/parsejs2/main.js:39: //% cannot be used with QT_TR_NOOP\(\). Ignoring +.*/lupdate/testdata/good/parsejs2/main.js:42: Discarding unconsumed meta data +.*/lupdate/testdata/good/parsejs2/main.js:44: Discarding unconsumed meta data +.*/lupdate/testdata/good/parsejs2/main.js:46: Discarding unconsumed meta data diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs2/main.js b/tests/auto/linguist/lupdate/testdata/good/parsejs2/main.js new file mode 100644 index 0000000..ea02957 --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs2/main.js @@ -0,0 +1,56 @@ +// This script exercises lupdate errors and warnings. + +qsTranslate(); +qsTranslate(10); +qsTranslate(10, 20); +qsTranslate("10", 20); +qsTranslate(10, "20"); + +QT_TRANSLATE_NOOP(); +QT_TRANSLATE_NOOP(10); +QT_TRANSLATE_NOOP(10, 20); +QT_TRANSLATE_NOOP("10", 20); +QT_TRANSLATE_NOOP(10, "20"); + +qsTr(); +qsTr(10); + +QT_TR_NOOP(); +QT_TR_NOOP(10); + +qsTrId(); +qsTrId(10); + +QT_TRID_NOOP(); +QT_TRID_NOOP(10); + +//% This is wrong +//% "This is not wrong" This is wrong +//% "I forgot to close the meta string +//% "Being evil \ + +//% "Should cause a warning" +qsTranslate("FooContext", "Hello"); +//% "Should cause a warning" +QT_TRANSLATE_NOOP("FooContext", "World"); +//% "Should cause a warning" +qsTr("Hello"); +//% "Should cause a warning" +QT_TR_NOOP("World"); + +//: This comment will be discarded. +Math.sin(1); +//= id_foobar +Math.cos(2); +//~ underflow False +Math.tan(3); + +/* +Not tested for now, because these should perhaps not cause +translation entries to be generated at all; see QTBUG-11843. + +//= qtn_foo +qsTrId("qtn_foo"); +//= qtn_bar +QT_TRID_NOOP("qtn_bar"); +*/ diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs2/project.pro b/tests/auto/linguist/lupdate/testdata/good/parsejs2/project.pro new file mode 100644 index 0000000..d549039 --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs2/project.pro @@ -0,0 +1,3 @@ +SOURCES += main.js + +TRANSLATIONS = project.ts diff --git a/tests/auto/linguist/lupdate/testdata/good/parsejs2/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/parsejs2/project.ts.result new file mode 100644 index 0000000..bfa1b3d --- /dev/null +++ b/tests/auto/linguist/lupdate/testdata/good/parsejs2/project.ts.result @@ -0,0 +1,30 @@ + + + + + FooContext + + + Hello + + + + + World + + + + + main + + + Hello + + + + + World + + + + -- cgit v0.12 From 82d5f0ae622d2579b925a5f6a8054eeeccb9a233 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 18 Aug 2010 11:44:27 +0200 Subject: add indirect input/output specification capability to QMAKE_SUBSTITUTES like in SUBDIRS, the specified strings can now be basenames of "structures" which specify the actual input and output files: QMAKE_SUBSTITUTES += test test.input = infile.txt.in test.output = foobar.out Reviewed-by: joerg --- qmake/generators/makefile.cpp | 37 ++++++++++++++++++++----- tests/auto/qmake/testdata/substitutes/test.pro | 6 +++- tests/auto/qmake/testdata/substitutes/test3.txt | 1 + tests/auto/qmake/tst_qmake.cpp | 2 ++ 4 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tests/auto/qmake/testdata/substitutes/test3.txt diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp index 852471d..03732ba 100644 --- a/qmake/generators/makefile.cpp +++ b/qmake/generators/makefile.cpp @@ -466,14 +466,37 @@ MakefileGenerator::init() if(!project->isEmpty("QMAKE_SUBSTITUTES")) { const QStringList &subs = v["QMAKE_SUBSTITUTES"]; for(int i = 0; i < subs.size(); ++i) { - if(!subs.at(i).endsWith(".in")) { - warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'", - subs.at(i).toLatin1().constData()); - continue; + QString inn = subs.at(i) + ".input", outn = subs.at(i) + ".output"; + if (v.contains(inn) || v.contains(outn)) { + if (!v.contains(inn) || !v.contains(outn)) { + warn_msg(WarnLogic, "Substitute '%s' has only one of .input and .output", + subs.at(i).toLatin1().constData()); + continue; + } + const QStringList &tinn = v[inn], &toutn = v[outn]; + if (tinn.length() != 1) { + warn_msg(WarnLogic, "Substitute '%s.input' does not have exactly one value", + subs.at(i).toLatin1().constData()); + continue; + } + if (toutn.length() != 1) { + warn_msg(WarnLogic, "Substitute '%s.output' does not have exactly one value", + subs.at(i).toLatin1().constData()); + continue; + } + inn = tinn.first(); + outn = toutn.first(); + } else { + inn = subs.at(i); + if(!inn.endsWith(".in")) { + warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'", + inn.toLatin1().constData()); + continue; + } + outn = inn.left(inn.length()-3); } - QFile in(fileFixify(subs.at(i))); - QFile out(fileFixify(subs.at(i).left(subs.at(i).length()-3), - qmake_getpwd(), Option::output_dir)); + QFile in(fileFixify(inn)); + QFile out(fileFixify(outn, qmake_getpwd(), Option::output_dir)); if(in.open(QFile::ReadOnly)) { QString contents; QStack state; diff --git a/tests/auto/qmake/testdata/substitutes/test.pro b/tests/auto/qmake/testdata/substitutes/test.pro index ddad93f..26b0272 100644 --- a/tests/auto/qmake/testdata/substitutes/test.pro +++ b/tests/auto/qmake/testdata/substitutes/test.pro @@ -1 +1,5 @@ -QMAKE_SUBSTITUTES += test.in sub/test2.in +QMAKE_SUBSTITUTES += test.in sub/test2.in indirect + +indirect.input = $$PWD/test3.txt +indirect.output = $$OUT_PWD/sub/indirect_test.txt + diff --git a/tests/auto/qmake/testdata/substitutes/test3.txt b/tests/auto/qmake/testdata/substitutes/test3.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/auto/qmake/testdata/substitutes/test3.txt @@ -0,0 +1 @@ +hello diff --git a/tests/auto/qmake/tst_qmake.cpp b/tests/auto/qmake/tst_qmake.cpp index 060fa01..277e9f8 100644 --- a/tests/auto/qmake/tst_qmake.cpp +++ b/tests/auto/qmake/tst_qmake.cpp @@ -484,12 +484,14 @@ void tst_qmake::substitutes() QVERIFY( test_compiler.qmake( workDir, "test" )); QVERIFY( test_compiler.exists( workDir, "test", Plain, "" )); QVERIFY( test_compiler.exists( workDir, "sub/test2", Plain, "" )); + QVERIFY( test_compiler.exists( workDir, "sub/indirect_test.txt", Plain, "" )); QVERIFY( test_compiler.makeDistClean( workDir )); QString buildDir = base_path + "/testdata/substitutes_build"; QVERIFY( test_compiler.qmake( workDir, "test", buildDir )); QVERIFY( test_compiler.exists( buildDir, "test", Plain, "" )); QVERIFY( test_compiler.exists( buildDir, "sub/test2", Plain, "" )); + QVERIFY( test_compiler.exists( buildDir, "sub/indirect_test.txt", Plain, "" )); QVERIFY( test_compiler.makeDistClean( buildDir )); } -- cgit v0.12 From b5b6cbb477b50c582d545b0e5e3a04626834e7e2 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Tue, 17 Aug 2010 18:59:51 +0200 Subject: let WebKit inject itself into the qt configuration i.e., don't explicitly deal with qt_webkit_version.pri outside of the webkit source directory. Task-number: QTBUG-12379 Reviewed-by: Simon Hausmann --- configure | 2 +- mkspecs/modules/README | 3 +++ mkspecs/modules/qt_webkit_version.pri | 4 ---- projects.pro | 4 +++- src/3rdparty/webkit/WebCore/WebCore.pro | 2 +- src/3rdparty/webkit/WebKit.pro | 6 ++++++ src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri | 2 +- tools/configure/configureapp.cpp | 3 +-- 8 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 mkspecs/modules/README delete mode 100644 mkspecs/modules/qt_webkit_version.pri diff --git a/configure b/configure index bd69a0a..159feaf 100755 --- a/configure +++ b/configure @@ -7163,7 +7163,7 @@ if [ "$CFG_WEBKIT" = "auto" ]; then fi if [ "$CFG_WEBKIT" = "yes" ]; then - QT_CONFIG="$QT_CONFIG webkit" + # Don't add "webkit" to QT_CONFIG here - it injects itself via the module. # The reason we set CFG_WEBKIT, is such that the printed overview of what will be enabled, shows correctly. CFG_WEBKIT="yes" else diff --git a/mkspecs/modules/README b/mkspecs/modules/README new file mode 100644 index 0000000..f095982 --- /dev/null +++ b/mkspecs/modules/README @@ -0,0 +1,3 @@ +Externally provided Qt modules may drop a qmake file here to become part of +the current Qt configuration. The file name must follow the pattern +"qt_.pri". It must contain a "QT_CONFIG += " statement. diff --git a/mkspecs/modules/qt_webkit_version.pri b/mkspecs/modules/qt_webkit_version.pri deleted file mode 100644 index ffd192c..0000000 --- a/mkspecs/modules/qt_webkit_version.pri +++ /dev/null @@ -1,4 +0,0 @@ -QT_WEBKIT_VERSION = 4.7.0 -QT_WEBKIT_MAJOR_VERSION = 4 -QT_WEBKIT_MINOR_VERSION = 7 -QT_WEBKIT_PATCH_VERSION = 0 diff --git a/projects.pro b/projects.pro index 373b049..f94e1de 100644 --- a/projects.pro +++ b/projects.pro @@ -159,11 +159,13 @@ INSTALLS += qmake #mkspecs mkspecs.path=$$[QT_INSTALL_DATA]/mkspecs -mkspecs.files=$$QT_BUILD_TREE/mkspecs/qconfig.pri $$QT_SOURCE_TREE/mkspecs/* +mkspecs.files=$$QT_BUILD_TREE/mkspecs/qconfig.pri $$files($$QT_SOURCE_TREE/mkspecs/*) +mkspecs.files -= $$QT_SOURCE_TREE/mkspecs/modules unix { DEFAULT_QMAKESPEC = $$QMAKESPEC DEFAULT_QMAKESPEC ~= s,^.*mkspecs/,,g mkspecs.commands += $(DEL_FILE) $(INSTALL_ROOT)$$mkspecs.path/default; $(SYMLINK) $$DEFAULT_QMAKESPEC $(INSTALL_ROOT)$$mkspecs.path/default + mkspecs.files -= $$QT_SOURCE_TREE/mkspecs/default } win32:!equals(QT_BUILD_TREE, $$QT_SOURCE_TREE) { # When shadow building on Windows, the default mkspec only exists in the build tree. diff --git a/src/3rdparty/webkit/WebCore/WebCore.pro b/src/3rdparty/webkit/WebCore/WebCore.pro index 2047143..ac0c47c 100644 --- a/src/3rdparty/webkit/WebCore/WebCore.pro +++ b/src/3rdparty/webkit/WebCore/WebCore.pro @@ -82,7 +82,7 @@ CONFIG(QTDIR_build) { symbian: TARGET =$$TARGET$${QT_LIBINFIX} } moduleFile=$$PWD/../WebKit/qt/qt_webkit_version.pri -include($$moduleFile) +isEmpty(QT_BUILD_TREE):include($$moduleFile) VERSION = $${QT_WEBKIT_MAJOR_VERSION}.$${QT_WEBKIT_MINOR_VERSION}.$${QT_WEBKIT_PATCH_VERSION} unix { diff --git a/src/3rdparty/webkit/WebKit.pro b/src/3rdparty/webkit/WebKit.pro index c7df391..ef251c5 100644 --- a/src/3rdparty/webkit/WebKit.pro +++ b/src/3rdparty/webkit/WebKit.pro @@ -34,3 +34,9 @@ symbian { } include(WebKit/qt/docs/docs.pri) + +!isEmpty(QT_BUILD_TREE) { + QMAKE_SUBSTITUTES += qt_webkit_version + qt_webkit_version.input = WebKit/qt/qt_webkit_version.pri + qt_webkit_version.output = $$QT_BUILD_TREE/mkspecs/modules/qt_webkit_version.pri +} diff --git a/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri b/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri index d8cf06c..4594d1e 100644 --- a/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri +++ b/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri @@ -2,4 +2,4 @@ QT_WEBKIT_VERSION = 4.7.0 QT_WEBKIT_MAJOR_VERSION = 4 QT_WEBKIT_MINOR_VERSION = 7 QT_WEBKIT_PATCH_VERSION = 0 -QT_CONFIG *= webkit +QT_CONFIG += webkit diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 0c716d1..3a0fcde 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -2667,8 +2667,7 @@ void Configure::generateOutputVars() qtConfig += "audio-backend"; } - if (dictionary["WEBKIT"] == "yes") - qtConfig += "webkit"; + // Don't add "webkit" to QT_CONFIG here - it injects itself via the module. if (dictionary["DECLARATIVE"] == "yes") { if (dictionary[ "SCRIPT" ] == "no") { -- cgit v0.12 From 40fef4036007e1b0d69d1f731c591c324bd0c6ec Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Mon, 23 Aug 2010 14:02:51 +0200 Subject: Fixed touch event delivery in QGraphicsView. When a touch event with a second touch pressed is delivered inside graphicsview, we should combine it with the closest touch point even if the item under the second touch is not direct ancestor or child of the first touches' target item. So adding a second touch inside the item's bounding rect will send a TouchUpdate event to the item instead or starting a new touch event sequence. Task-number: QT-3795 Reviewed-by: Bradley T. Hughes --- src/gui/graphicsview/qgraphicsscene.cpp | 9 +---- tests/auto/qtouchevent/tst_qtouchevent.cpp | 59 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a98ce6f..a02f3ac 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -5740,16 +5740,11 @@ void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent) } if (sceneTouchEvent->deviceType() == QTouchEvent::TouchScreen) { - // on touch-screens, combine this touch point with the closest one we find if it - // is a a direct descendent or ancestor ( + // on touch-screens, combine this touch point with the closest one we find int closestTouchPointId = findClosestTouchPointId(touchPoint.scenePos()); QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPointId); - if (!item - || (closestItem - && (item->isAncestorOf(closestItem) - || closestItem->isAncestorOf(item)))) { + if (!item || (closestItem && cachedItemsUnderMouse.contains(closestItem))) item = closestItem; - } } if (!item) continue; diff --git a/tests/auto/qtouchevent/tst_qtouchevent.cpp b/tests/auto/qtouchevent/tst_qtouchevent.cpp index bb80fde..4219ef4 100644 --- a/tests/auto/qtouchevent/tst_qtouchevent.cpp +++ b/tests/auto/qtouchevent/tst_qtouchevent.cpp @@ -109,6 +109,7 @@ class tst_QTouchEventGraphicsItem : public QGraphicsItem public: QList touchBeginPoints, touchUpdatePoints, touchEndPoints; bool seenTouchBegin, seenTouchUpdate, seenTouchEnd; + int touchBeginCounter, touchUpdateCounter, touchEndCounter; bool acceptTouchBegin, acceptTouchUpdate, acceptTouchEnd; bool deleteInTouchBegin, deleteInTouchUpdate, deleteInTouchEnd; tst_QTouchEventGraphicsItem **weakpointer; @@ -131,6 +132,7 @@ public: touchUpdatePoints.clear(); touchEndPoints.clear(); seenTouchBegin = seenTouchUpdate = seenTouchEnd = false; + touchBeginCounter = touchUpdateCounter = touchEndCounter = 0; acceptTouchBegin = acceptTouchUpdate = acceptTouchEnd = true; deleteInTouchBegin = deleteInTouchUpdate = deleteInTouchEnd = false; } @@ -146,6 +148,7 @@ public: if (seenTouchUpdate) qWarning("TouchBegin: TouchUpdate cannot happen before TouchBegin"); if (seenTouchEnd) qWarning("TouchBegin: TouchEnd cannot happen before TouchBegin"); seenTouchBegin = !seenTouchBegin && !seenTouchUpdate && !seenTouchEnd; + ++touchBeginCounter; touchBeginPoints = static_cast(event)->touchPoints(); event->setAccepted(acceptTouchBegin); if (deleteInTouchBegin) @@ -155,6 +158,7 @@ public: if (!seenTouchBegin) qWarning("TouchUpdate: have not seen TouchBegin"); if (seenTouchEnd) qWarning("TouchUpdate: TouchEnd cannot happen before TouchUpdate"); seenTouchUpdate = seenTouchBegin && !seenTouchEnd; + ++touchUpdateCounter; touchUpdatePoints = static_cast(event)->touchPoints(); event->setAccepted(acceptTouchUpdate); if (deleteInTouchUpdate) @@ -164,6 +168,7 @@ public: if (!seenTouchBegin) qWarning("TouchEnd: have not seen TouchBegin"); if (seenTouchEnd) qWarning("TouchEnd: already seen a TouchEnd"); seenTouchEnd = seenTouchBegin && !seenTouchEnd; + ++touchEndCounter; touchEndPoints = static_cast(event)->touchPoints(); event->setAccepted(acceptTouchEnd); if (deleteInTouchEnd) @@ -194,6 +199,7 @@ private slots: void deleteInEventHandler(); void deleteInRawEventTranslation(); void crashInQGraphicsSceneAfterNotHandlingTouchBegin(); + void touchBeginWithGraphicsWidget(); }; void tst_QTouchEvent::touchDisabledByDefault() @@ -1334,6 +1340,59 @@ void tst_QTouchEvent::crashInQGraphicsSceneAfterNotHandlingTouchBegin() QTest::touchEvent(view.viewport()).release(0, view.mapFromScene(QPoint(10, 10))); } +void tst_QTouchEvent::touchBeginWithGraphicsWidget() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + tst_QTouchEventGraphicsItem *root; + root = new tst_QTouchEventGraphicsItem; + root->setAcceptTouchEvents(true); + scene.addItem(root); + + QGraphicsWidget *glassWidget = new QGraphicsWidget; + glassWidget->setMinimumSize(100, 100); + scene.addItem(glassWidget); + + view.resize(200, 200); + view.show(); + QTest::qWaitForWindowShown(&view); + view.fitInView(scene.sceneRect()); + + QTest::touchEvent() + .press(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()); + QTest::touchEvent() + .stationary(0) + .press(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); + QTest::touchEvent() + .release(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()) + .release(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); + + QCOMPARE(root->touchBeginCounter, 1); + QCOMPARE(root->touchUpdateCounter, 1); + QCOMPARE(root->touchEndCounter, 1); + QCOMPARE(root->touchUpdatePoints.size(), 2); + + root->reset(); + glassWidget->setWindowFlags(Qt::Window); // make the glassWidget a panel + + QTest::touchEvent() + .press(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()); + QTest::touchEvent() + .stationary(0) + .press(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); + QTest::touchEvent() + .release(0, view.mapFromScene(root->mapToScene(3,3)), view.viewport()) + .release(1, view.mapFromScene(root->mapToScene(6,6)), view.viewport()); + + QCOMPARE(root->touchBeginCounter, 0); + QCOMPARE(root->touchUpdateCounter, 0); + QCOMPARE(root->touchEndCounter, 0); + + + delete root; + delete glassWidget; +} + QTEST_MAIN(tst_QTouchEvent) #include "tst_qtouchevent.moc" -- cgit v0.12 From e645157a5dcf702df21b566441a56753eaefe6c4 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 23 Aug 2010 19:42:00 +0200 Subject: fix build after first webkit self-injection attempt it didn't work for several reasons: - if the configures don't add webkit to QT_CONFIG, src.pro doesn't even know that it needs to build WebKit at all, so WebKit would never inject itself into the build. hen-and-egg problem. - the in-Qt build doesn't use WebKit.pro in the first place, so a proper recursive qmake would never create qt_webkit_version.pri. it worked under unix because configure collects all project files irrespective of the actual SUBDIRS structure. - a proper recursive qmake will cache the qt config, so the injection wouldn't be effective during the first qmake run so instead let the configures copy the pri file. --- configure | 3 ++- configure.exe | Bin 1320448 -> 1320960 bytes src/3rdparty/webkit/WebKit.pro | 6 ------ tools/configure/configureapp.cpp | 8 +++++++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/configure b/configure index 159feaf..3230086 100755 --- a/configure +++ b/configure @@ -7163,7 +7163,8 @@ if [ "$CFG_WEBKIT" = "auto" ]; then fi if [ "$CFG_WEBKIT" = "yes" ]; then - # Don't add "webkit" to QT_CONFIG here - it injects itself via the module. + # This include takes care of adding "webkit" to QT_CONFIG. + cp -f "$relpath/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri" "$outpath/mkspecs/modules/qt_webkit_version.pri" # The reason we set CFG_WEBKIT, is such that the printed overview of what will be enabled, shows correctly. CFG_WEBKIT="yes" else diff --git a/configure.exe b/configure.exe index c5bff85..982e038 100755 Binary files a/configure.exe and b/configure.exe differ diff --git a/src/3rdparty/webkit/WebKit.pro b/src/3rdparty/webkit/WebKit.pro index ef251c5..c7df391 100644 --- a/src/3rdparty/webkit/WebKit.pro +++ b/src/3rdparty/webkit/WebKit.pro @@ -34,9 +34,3 @@ symbian { } include(WebKit/qt/docs/docs.pri) - -!isEmpty(QT_BUILD_TREE) { - QMAKE_SUBSTITUTES += qt_webkit_version - qt_webkit_version.input = WebKit/qt/qt_webkit_version.pri - qt_webkit_version.output = $$QT_BUILD_TREE/mkspecs/modules/qt_webkit_version.pri -} diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 3a0fcde..7049306 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -2667,7 +2667,13 @@ void Configure::generateOutputVars() qtConfig += "audio-backend"; } - // Don't add "webkit" to QT_CONFIG here - it injects itself via the module. + if (dictionary["WEBKIT"] == "yes") { + // This include takes care of adding "webkit" to QT_CONFIG. + QString src = sourcePath + "/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri"; + QString dst = buildPath + "/mkspecs/modules/qt_webkit_version.pri"; + QFile::remove(dst); + QFile::copy(src, dst); + } if (dictionary["DECLARATIVE"] == "yes") { if (dictionary[ "SCRIPT" ] == "no") { -- cgit v0.12 From 9d77ff2064be61d2fdf2c034b13ef75cc4905bb3 Mon Sep 17 00:00:00 2001 From: Aaron McCarthy Date: Tue, 24 Aug 2010 12:14:03 +1000 Subject: Fix race condition on bearer management initialisation. Defer initialisation and changing thread affinity until after the global static is constructed. Task-number: QTBUG-12686 --- src/network/bearer/qnetworkconfigmanager.cpp | 12 ++++++++++-- src/network/bearer/qnetworkconfigmanager_p.cpp | 10 +++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp index 102b347..65014a6 100644 --- a/src/network/bearer/qnetworkconfigmanager.cpp +++ b/src/network/bearer/qnetworkconfigmanager.cpp @@ -54,7 +54,15 @@ Q_GLOBAL_STATIC(QNetworkConfigurationManagerPrivate, connManager); QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() { - return connManager(); + static bool initialized = false; + + QNetworkConfigurationManagerPrivate *m = connManager(); + if (!initialized) { + initialized = true; + m->updateConfigurations(); + } + + return m; } /*! @@ -178,7 +186,7 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() QNetworkConfigurationManager::QNetworkConfigurationManager( QObject* parent ) : QObject(parent) { - QNetworkConfigurationManagerPrivate *priv = connManager(); + QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate(); connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)), this, SIGNAL(configurationAdded(QNetworkConfiguration))); diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp index dd174bf..d388920 100644 --- a/src/network/bearer/qnetworkconfigmanager_p.cpp +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -64,9 +64,6 @@ QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate() { qRegisterMetaType("QNetworkConfiguration"); qRegisterMetaType("QNetworkConfigurationPrivatePointer"); - - moveToThread(QCoreApplicationPrivate::mainThread()); - updateConfigurations(); } QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() @@ -359,6 +356,13 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations() if (sender()) return; + if (thread() != QCoreApplicationPrivate::mainThread()) { + if (thread() != QThread::currentThread()) + return; + + moveToThread(QCoreApplicationPrivate::mainThread()); + } + updating = false; #ifndef QT_NO_LIBRARY -- cgit v0.12 From fdd6436987a114521168f48b9b96013772af7d49 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 24 Aug 2010 08:41:22 +0200 Subject: qmake vcxproj generator: fix bug when using CharacterSet=1 in .pro file Task-number: QTBUG-13080 Reviewed-by: Martin Petersson --- qmake/generators/win32/msvc_vcxproj.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qmake/generators/win32/msvc_vcxproj.cpp b/qmake/generators/win32/msvc_vcxproj.cpp index f68a435..271d9ae 100644 --- a/qmake/generators/win32/msvc_vcxproj.cpp +++ b/qmake/generators/win32/msvc_vcxproj.cpp @@ -202,7 +202,6 @@ void VcxprojGenerator::initConfiguration() conf.CharacterSet = "NotSet"; break; } - conf.CharacterSet = charSet(temp.isEmpty() ? (short)charSetNotSet : temp.toShort()); } conf.DeleteExtensionsOnClean = project->first("DeleteExtensionsOnClean"); conf.ImportLibrary = conf.linker.ImportLibrary; -- cgit v0.12 From 409d41185e85f2eefe6bb4872c4dd3005bef8170 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 24 Aug 2010 08:58:17 +0200 Subject: qmake vc[x]proj generators: support /MAP option without file name Task-number: QTBUG-13081 Reviewed-by: Martin Petersson --- qmake/generators/win32/msbuild_objectmodel.cpp | 3 ++- qmake/generators/win32/msvc_objectmodel.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/qmake/generators/win32/msbuild_objectmodel.cpp b/qmake/generators/win32/msbuild_objectmodel.cpp index 17c4d5a..2505056 100644 --- a/qmake/generators/win32/msbuild_objectmodel.cpp +++ b/qmake/generators/win32/msbuild_objectmodel.cpp @@ -1573,7 +1573,8 @@ bool VCXLinkerTool::parseOption(const char* option) break; case 0x0034160: // /MAP[:filename] GenerateMapFile = _True; - MapFileName = option+5; + if (option[4] == ':') + MapFileName = option+5; break; case 0x164e1ef: // /MAPINFO:{EXPORTS} if(*(option+9) == 'E') diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp index 1e060a0..980e686 100644 --- a/qmake/generators/win32/msvc_objectmodel.cpp +++ b/qmake/generators/win32/msvc_objectmodel.cpp @@ -1430,7 +1430,8 @@ bool VCLinkerTool::parseOption(const char* option) break; case 0x0034160: // /MAP[:filename] GenerateMapFile = _True; - MapFileName = option+5; + if (option[4] == ':') + MapFileName = option+5; break; case 0x164e1ef: // /MAPINFO:{EXPORTS|LINES} if(*(option+9) == 'E') -- cgit v0.12 From ae29cdf5cd319e447ef75b11ac1247375c6c8192 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 16:17:55 +0200 Subject: Keep the scopeid that getaddrinfo(3) returns to us. Task-number: QTBUG-12608, QTBUG-12243 Reviewed-by: Markus Goetz --- src/network/kernel/qhostinfo_unix.cpp | 5 ++++- src/network/socket/qnativesocketengine_unix.cpp | 9 ++++++--- src/network/socket/qnativesocketengine_win.cpp | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 3112dd6..9e3da61 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -247,7 +247,10 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) #ifndef QT_NO_IPV6 else if (node->ai_family == AF_INET6) { QHostAddress addr; - addr.setAddress(((sockaddr_in6 *) node->ai_addr)->sin6_addr.s6_addr); + sockaddr_in6 *sa6 = (sockaddr_in6 *) node->ai_addr; + addr.setAddress(sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + addr.setScopeId(QString::number(sa6->sin6_scope_id)); if (!addresses.contains(addr)) addresses.append(addr); } diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index fe28863..f6bfbac 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -352,10 +352,13 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); sockAddrIPv6.sin6_family = AF_INET6; sockAddrIPv6.sin6_port = htons(port); + + QString scopeid = addr.scopeId(); + bool ok; + sockAddrIPv6.sin6_scope_id = scopeid.toInt(&ok); #ifndef QT_NO_IPV6IFNAME - sockAddrIPv6.sin6_scope_id = ::if_nametoindex(addr.scopeId().toLatin1().data()); -#else - sockAddrIPv6.sin6_scope_id = addr.scopeId().toInt(); + if (!ok) + sockAddrIPv6.sin6_scope_id = ::if_nametoindex(scopeid.toLatin1()); #endif Q_IPV6ADDR ip6 = addr.toIPv6Address(); memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6)); diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 8177b4f..477ef45 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -207,6 +207,7 @@ static inline void qt_socket_setPortAndAddress(SOCKET socketDescriptor, sockaddr if (address.protocol() == QAbstractSocket::IPv6Protocol) { memset(sockAddrIPv6, 0, sizeof(qt_sockaddr_in6)); sockAddrIPv6->sin6_family = AF_INET6; + sockAddrIPv6->sin6_scope_id = address.scopeId().toInt(); WSAHtons(socketDescriptor, port, &(sockAddrIPv6->sin6_port)); Q_IPV6ADDR tmp = address.toIPv6Address(); memcpy(&(sockAddrIPv6->sin6_addr.qt_s6_addr), &tmp, sizeof(tmp)); -- cgit v0.12 From aa442648396e1367d6236801c5e1ad36884e4a81 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 11:17:58 +0200 Subject: Introduce a second compatibility build key to Qt. Some compilers are compatible with one another. In particular, the Intel CC on Unix systems is compatible with g++ on the same system. We should be saving the ABI "name" in the build key, not the compiler name, but it's too late for that. Choose "g++-{VERSION}" as the standard ABI name and make ICC support that. Reviewed-by: Bradley T. Hughes --- configure | 32 ++++++++++++++++++++++++++++++++ src/corelib/global/qlibraryinfo.cpp | 8 ++++++++ src/corelib/plugin/qlibrary.cpp | 5 ++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 3230086..1221d62 100755 --- a/configure +++ b/configure @@ -7278,6 +7278,7 @@ fi # some compilers generate binary incompatible code between different versions, # so we need to generate a build key that is different between these compilers +COMPAT_COMPILER= case "$COMPILER" in g++*) # GNU C++ @@ -7311,6 +7312,22 @@ g++*) esac [ '!' -z "$COMPILER_VERSION" ] && COMPILER="g++-${COMPILER_VERSION}" ;; +icc*) + # The Intel CC compiler on Unix systems matches the ABI of the g++ + # that is found on PATH + COMPILER="icc" + COMPAT_COMPILER="g++-4" + case "`g++ -dumpversion` 2>/dev/null" in + 2.95.*) + COMPAT_COMPILER="g++-2.95.*" + ;; + 3.*) + COMPAT_COMPILER="g++-3.*" + ;; + *) + ;; + esac + ;; *) # ;; @@ -7453,9 +7470,20 @@ if [ "$QT_CROSS_COMPILE" = "no" ]; then QT_BUILD_KEY_COMPAT="$QT_BUILD_KEY_COMPAT $QT_NAMESPACE" fi fi + +# is this compiler compatible with some other "standard" build key +QT_BUILD_KEY_COMPAT_COMPILER= +if [ ! -z "$COMPAT_COMPILER" ]; then + QT_BUILD_KEY_COMPAT_COMPILER="$CFG_USER_BUILD_KEY $CFG_ARCH $TARGET_OPERATING_SYSTEM $COMPAT_COMPILER $BUILD_OPTIONS" + if [ -n "$QT_NAMESPACE" ]; then + QT_BUILD_KEY_COMPAT_COMPILER="$QT_BUILD_KEY_COMPAT_COMPILER $QT_NAMESPACE" + fi +fi + # strip out leading/trailing/extra whitespace QT_BUILD_KEY=`echo $QT_BUILD_KEY | sed -e "s, *, ,g" -e "s,^ *,," -e "s, *$,,"` QT_BUILD_KEY_COMPAT=`echo $QT_BUILD_KEY_COMPAT | sed -e "s, *, ,g" -e "s,^ *,," -e "s, *$,,"` +QT_BUILD_KEY_COMPAT_COMPILER=`echo $QT_BUILD_KEY_COMPAT_COMPILER | sed -e "s, *, ,g" -e "s,^ *,," -e "s, *$,,"` #------------------------------------------------------------------------------- # part of configuration information goes into qconfig.h @@ -7504,6 +7532,10 @@ if [ -n "$QT_BUILD_KEY_COMPAT" ]; then echo "#define QT_BUILD_KEY_COMPAT \"$QT_BUILD_KEY_COMPAT\"" \ >> "$outpath/src/corelib/global/qconfig.h.new" fi +if [ -n "$QT_BUILD_KEY_COMPAT_COMPILER" ]; then + echo "#define QT_BUILD_KEY_COMPAT2 \"$QT_BUILD_KEY_COMPAT_COMPILER\"" \ + >> "$outpath/src/corelib/global/qconfig.h.new" +fi echo "" >>"$outpath/src/corelib/global/qconfig.h.new" echo "#ifdef QT_BOOTSTRAPPED" >>"$outpath/src/corelib/global/qconfig.h.new" diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index ea7e2e4..7ebee3d 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -508,6 +508,14 @@ void qt_core_boilerplate() "Contact: Nokia Corporation (qt-info@nokia.com)\n" "\n" "Build key: " QT_BUILD_KEY "\n" + "Compat build key: " +#ifdef QT_BUILD_KEY_COMPAT + "| " QT_BUILD_KEY_COMPAT " " +#endif +#ifdef QT_BUILD_KEY_COMPAT2 + "| " QT_BUILD_KEY_COMPAT2 " " +#endif + "|\n" "Build date: %s\n" "Installation prefix: %s\n" "Library path: %s\n" diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 0f99948..a9ae2ab 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -790,10 +790,13 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) .arg(qt_version&0xff) .arg(debug ? QLatin1String("debug") : QLatin1String("release")); } else if (key != QT_BUILD_KEY + // we may have some compatibility keys, try them too: #ifdef QT_BUILD_KEY_COMPAT - // be sure to load plugins using an older but compatible build key && key != QT_BUILD_KEY_COMPAT #endif +#ifdef QT_BUILD_KEY_COMPAT2 + && key != QT_BUILD_KEY_COMPAT2 +#endif ) { if (qt_debug_component()) { qWarning("In %s:\n" -- cgit v0.12 From b46f40ac8998a98122b3d85a9fed6bf9f094edd4 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 11 Aug 2010 20:30:18 +0200 Subject: linux-icc can take -msse2, -msse3, etc. flags, so enable this as well Otherwise, we actually get compilation errors because configure detected that the compiler supports this, so QT_HAVE_SSSE3 is defined, but we then compile qimage_ssse3.cpp without -mssse3 (Among others) Reviewed-By: Benjamin Poulain --- src/gui/gui.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 3943e26..13d2c77 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -99,7 +99,7 @@ neon:*-g++* { contains(QMAKE_MAC_XARCH, no) { DEFINES += QT_NO_MAC_XARCH } else { - win32-g++*|!win32:!*-icc* { + win32-g++*|!win32:!win32-icc*:!macx-icc* { mmx { mmx_compiler.commands = $$QMAKE_CXX -c -Winline -- cgit v0.12 From 8585a340712e901bfe8389c6b951ae7b548daeca Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 16 Aug 2010 13:12:45 +0200 Subject: Include the SSE4.2 intrinsics header Reviewed-By: Benjamin Poulain --- src/corelib/tools/qsimd_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index a3148fb..2626657 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -85,6 +85,7 @@ QT_BEGIN_HEADER // SSE4.1 and SSE4.2 intrinsics #if (defined(QT_HAVE_SSE4_1) || defined(QT_HAVE_SSE4_2)) && (defined(__SSE4_1__) || defined(Q_CC_MSVC)) #include +#include #endif // AVX intrinsics -- cgit v0.12 From cb9fa64c9ff61027e34c29fd8dd638f1237b41a7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 11:17:13 +0200 Subject: Allow other compilers than GCC on Linux to have a boilerplate --- src/corelib/global/global.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index b916b4d..2505e72 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -19,7 +19,7 @@ INCLUDEPATH += $$QT_BUILD_TREE/src/corelib/global # Only used on platforms with CONFIG += precompile_header PRECOMPILED_HEADER = global/qt_pch.h -linux*-g++*:!static { +linux*:!static { QMAKE_LFLAGS += -Wl,-e,qt_core_boilerplate prog=$$quote(if (/program interpreter: (.*)]/) { print $1; }) DEFINES += ELF_INTERPRETER=\\\"$$system(readelf -l /bin/ls | perl -n -e \'$$prog\')\\\" -- cgit v0.12 From 06080c767602715861ca4f1e7406d7834f52f30e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 21:11:18 +0200 Subject: Add some quick benchmarks for QChar comparison --- tests/benchmarks/corelib/tools/qstring/main.cpp | 143 +++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 12826eb..ee7ce5c 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include #include -#include +#include #ifdef Q_OS_SYMBIAN // In Symbian OS test data is located in applications private dir @@ -48,12 +48,18 @@ #define SRCDIR "" #endif +#include + class tst_QString: public QObject { Q_OBJECT +public: + tst_QString(); private slots: void equals() const; void equals_data() const; + void equals2_data() const; + void equals2() const; void fromUtf8() const; }; @@ -67,6 +73,22 @@ void tst_QString::equals() const } } +static ushort databuffer[4096]; + +tst_QString::tst_QString() +{ + // populate databuffer with our seed, each byte 3 times in a row + // include the NUL! + static const char seed[] = "AAAAAAAAAEhlbGxvIFdvcmxkIAAAAAA="; + static const int repeat = 3; + int pos = 0; + for (ushort *p = databuffer; p < databuffer + (sizeof(databuffer) / sizeof(databuffer[0])); p += repeat) { + for (int j = 0; j < repeat; ++p, ++j) + *p = seed[pos]; + pos = (pos + 1) % sizeof(seed); + } +} + void tst_QString::equals_data() const { static const struct { @@ -126,6 +148,125 @@ void tst_QString::equals_data() const << QString::fromRawData(ptr + 1, 58) << QString::fromRawData(ptr + 3, 58); } +static bool equals2_memcmp_call(ushort *p1, ushort *p2, int len) +{ + return memcmp(p1, p2, len * 2) == 0; +} + +static bool equals2_bytewise(ushort *p1, ushort *p2, int len) +{ + uchar *b1 = (uchar *)p1; + uchar *b2 = (uchar *)p2; + len *= 2; + while (len--) + if (*b1++ != *b2++) + return false; + return true; +} + +static bool equals2_shortwise(ushort *p1, ushort *p2, int len) +{ + register ushort * const end = p1 + len; + for ( ; p1 != end; ++p1, ++p2) + if (*p1 != *p2) + return false; + return true; +} + +void tst_QString::equals2_data() const +{ + QTest::addColumn("algorithm"); + QTest::newRow("selftest") << -1; + QTest::newRow("memcmp_call") << 0; + QTest::newRow("bytewise") << 1; + QTest::newRow("shortwise") << 2; +} + +void tst_QString::equals2() const +{ + static const short positions[] = { + 190, 1719, 2149, 1752, + 158, 244, 365, 1117, + 254, 265, 1047, 1785, + 1435, 552, 1476, 2030, + // 16 + 421, 1840, 2209, 232, + 1389, 907, 1500, 1479, + 1152, 541, 655, 1960, + 1642, 299, 740, 1995, + // 32 + 1946, 1407, 1272, 1946, + 1459, 1851, 1717, 1484, + 1761, 1630, 1377, 1675, + 629, 341, 661, 244 + // 48 + }; + // the length list must not contain 0 + static const int lens[] = { + 11, // 0 + 40, + 28, + 38, + 9, + 52, // 5 + 48, + 38, + 29, + 7, + 2, // 10 + 49, + 41, + 5, + 20, + 62 // 15 + }; + + typedef bool (* FuncPtr)(ushort *, ushort *, int); + static const FuncPtr func[] = { + equals2_memcmp_call, // 0 + equals2_bytewise, // 1 + equals2_shortwise, // 1 + 0 + }; + + QFETCH(int, algorithm); + if (algorithm == -1) { + for (uint pos1 = 0; pos1 < sizeof positions / sizeof positions[0]; ++pos1) + for (uint pos2 = 0; pos2 < (sizeof positions / sizeof positions[0]) - 32; ++pos2) + for (uint len = 0; len < sizeof lens / sizeof lens[0]; ++len) { + ushort *p1 = databuffer + positions[pos1]; + ushort *p2 = databuffer + positions[pos2]; + bool expected = memcmp(p1, p2, lens[len] * 2) == 0; + + for (uint algo = 0; algo < -1 + (sizeof func / sizeof func[0]); ++algo) { + bool result = (func[algo])(p1, p2, lens[len]); + if (expected != result) + qWarning().nospace() + << "algo=" << algo + << " pos1=" << positions[pos1] + << " pos2=" << positions[pos2] + << " len=" << lens[len] + << " failed (" << result << "!=" << expected + << "); strings were " + << QByteArray((char*)p1, lens[len]).toHex() + << " and " + << QByteArray((char*)p2, lens[len]).toHex(); + } + + } + return; + } + + QBENCHMARK { + for (uint pos1 = 0; pos1 < sizeof positions / sizeof positions[0]; ++pos1) + for (uint pos2 = 0; pos2 < (sizeof positions / sizeof positions[0]) - 32; ++pos2) + for (uint len = 0; len < sizeof lens / sizeof lens[0]; ++len) { + bool result = (func[algorithm])(databuffer + positions[pos1], databuffer + positions[pos2], lens[len]); + Q_UNUSED(result); + } + } +} + void tst_QString::fromUtf8() const { QFile file(SRCDIR "utf-8.txt"); -- cgit v0.12 From de76f8f32b84409a7053d5d95a3855b8590389e1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 21:12:22 +0200 Subject: Add a 4-byte comparison routine. This is a copy of qMemEquals from qstring.cpp --- tests/benchmarks/corelib/tools/qstring/main.cpp | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index ee7ce5c..9eb6294 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -173,6 +173,52 @@ static bool equals2_shortwise(ushort *p1, ushort *p2, int len) return true; } +static bool equals2_intwise(ushort *p1, ushort *p2, int length) +{ + register union { + const quint16 *w; + const quint32 *d; + quintptr value; + } sa, sb; + sa.w = p1; + sb.w = p2; + + // check alignment + if ((sa.value & 2) == (sb.value & 2)) { + // both addresses have the same alignment + if (sa.value & 2) { + // both addresses are not aligned to 4-bytes boundaries + // compare the first character + if (*sa.w != *sb.w) + return false; + --length; + ++sa.w; + ++sb.w; + + // now both addresses are 4-bytes aligned + } + + // both addresses are 4-bytes aligned + // do a fast 32-bit comparison + register const quint32 *e = sa.d + (length >> 1); + for ( ; sa.d != e; ++sa.d, ++sb.d) { + if (*sa.d != *sb.d) + return false; + } + + // do we have a tail? + return (length & 1) ? *sa.w == *sb.w : true; + } else { + // one of the addresses isn't 4-byte aligned but the other is + register const quint16 *e = sa.w + length; + for ( ; sa.w != e; ++sa.w, ++sb.w) { + if (*sa.w != *sb.w) + return false; + } + } + return true; +} + void tst_QString::equals2_data() const { QTest::addColumn("algorithm"); @@ -180,6 +226,7 @@ void tst_QString::equals2_data() const QTest::newRow("memcmp_call") << 0; QTest::newRow("bytewise") << 1; QTest::newRow("shortwise") << 2; + QTest::newRow("intwise") << 3; } void tst_QString::equals2() const @@ -226,6 +273,7 @@ void tst_QString::equals2() const equals2_memcmp_call, // 0 equals2_bytewise, // 1 equals2_shortwise, // 1 + equals2_intwise, // 3 0 }; -- cgit v0.12 From d8ad0812c7fdc2684ce7f09cc13b69f567e82031 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 21:14:00 +0200 Subject: Add a benchmark for SSE2 comparison. This function uses unaligned loads. I'll try to write one with aligned loads later. --- tests/benchmarks/corelib/tools/qstring/main.cpp | 27 ++++++++++++++++++++++ tests/benchmarks/corelib/tools/qstring/qstring.pro | 1 + 2 files changed, 28 insertions(+) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 9eb6294..5210034 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -219,6 +219,27 @@ static bool equals2_intwise(ushort *p1, ushort *p2, int length) return true; } +#ifdef __SSE2__ +static bool equals2_sse2(ushort *p1, ushort *p2, int len) +{ + if (len > 8) { + while (len > 8) { + __m128i q1 = _mm_loadu_si128((__m128i *)p1); + __m128i q2 = _mm_loadu_si128((__m128i *)p2); + __m128i cmp = _mm_cmpeq_epi16(q1, q2); + if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) + return false; + + len -= 8; + p1 += 8; + p2 += 8; + } + } + + return equals2_shortwise(p1, p2, len); +} +#endif + void tst_QString::equals2_data() const { QTest::addColumn("algorithm"); @@ -227,6 +248,9 @@ void tst_QString::equals2_data() const QTest::newRow("bytewise") << 1; QTest::newRow("shortwise") << 2; QTest::newRow("intwise") << 3; +#ifdef __SSE2__ + QTest::newRow("sse2") << 4; +#endif } void tst_QString::equals2() const @@ -274,6 +298,9 @@ void tst_QString::equals2() const equals2_bytewise, // 1 equals2_shortwise, // 1 equals2_intwise, // 3 +#ifdef __SSE2__ + equals2_sse2, // 4 +#endif 0 }; diff --git a/tests/benchmarks/corelib/tools/qstring/qstring.pro b/tests/benchmarks/corelib/tools/qstring/qstring.pro index fa4310e..388e3c2 100644 --- a/tests/benchmarks/corelib/tools/qstring/qstring.pro +++ b/tests/benchmarks/corelib/tools/qstring/qstring.pro @@ -14,3 +14,4 @@ wince*:{ DEFINES += SRCDIR=\\\"$$PWD/\\\" } +sse2:QMAKE_CXXFLAGS += -msse2 -- cgit v0.12 From 531a8f198c152e1135db103f22bab648a314926e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 21:18:49 +0200 Subject: Add an SSE2 comparison with prolog The prolog tries to align p1 to a multiple of 16, so as to run aligned loads, which are faster. Unfortunately, my tests so far indicate that the prolog ends up taking longer than the benefit of having aligned loads. --- tests/benchmarks/corelib/tools/qstring/main.cpp | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 5210034..4e5d1c0 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -238,6 +238,42 @@ static bool equals2_sse2(ushort *p1, ushort *p2, int len) return equals2_shortwise(p1, p2, len); } + +static inline +#ifdef Q_CC_GNU +__attribute__((always_inline)) +#endif +bool prolog_align(ushort *&p1, ushort *&p2, int &len) +{ + const ushort *end = (ushort*) ((quintptr(p1) + 15) & ~15); + if (end > p1 + len) + end = p1 + len; + for ( ; p1 != end; ++p1, ++p2, --len) + if (*p1 != *p2) + return false; + return true; +} + +static bool equals2_sse2_aligning(ushort *p1, ushort *p2, int len) +{ + if (len > 8) { + if (!prolog_align(p1, p2, len)) + return false; + while (len > 8) { + __m128i q1 = _mm_load_si128((__m128i *)p1); + __m128i q2 = _mm_loadu_si128((__m128i *)p2); + __m128i cmp = _mm_cmpeq_epi16(q1, q2); + if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) + return false; + + len -= 8; + p1 += 8; + p2 += 8; + } + } + + return equals2_shortwise(p1, p2, len); +} #endif void tst_QString::equals2_data() const @@ -250,6 +286,7 @@ void tst_QString::equals2_data() const QTest::newRow("intwise") << 3; #ifdef __SSE2__ QTest::newRow("sse2") << 4; + QTest::newRow("sse2_aligning") << 5; #endif } @@ -300,6 +337,7 @@ void tst_QString::equals2() const equals2_intwise, // 3 #ifdef __SSE2__ equals2_sse2, // 4 + equals2_sse2_aligning, // 5 #endif 0 }; -- cgit v0.12 From 7790cf5b2922a7adf684dc0b7cd0fc1583c0684a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Aug 2010 23:12:21 +0200 Subject: Add an SSSE3 version that uses palignr to align. Instead of using a non-SIMD method for aligning, we instead load more bytes from p1 and use the PALIGNR instruction to realign to what we want. The result is that it's bit slower than the non-SIMD comparison, due to the complexity. For strings over 8 QChars wide, it's only slightly worse than the non-SIMD comparison. --- tests/benchmarks/corelib/tools/qstring/main.cpp | 110 +++++++++++++++++++++ tests/benchmarks/corelib/tools/qstring/qstring.pro | 3 +- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 4e5d1c0..e1800c0 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -274,6 +274,110 @@ static bool equals2_sse2_aligning(ushort *p1, ushort *p2, int len) return equals2_shortwise(p1, p2, len); } + +template static inline bool equals2_ssse3_alignr(__m128i *m1, __m128i *m2, int len) +{ + __m128i lower = _mm_load_si128(m1); + while (len > 8) { + __m128i upper = _mm_load_si128(m1 + 1); + __m128i correct; + correct = _mm_alignr_epi8(upper, lower, N); + + __m128i q2 = _mm_loadu_si128(m2); + __m128i cmp = _mm_cmpeq_epi16(correct, q2); + if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) + return false; + + len -= 8; + ++m2; + ++m1; + lower = upper; + } + + // tail + return len == 0 || equals2_shortwise((ushort *)m1 + N / 2, (ushort*)m2, len); +} + +static inline bool equals2_ssse3_aligned(__m128i *m1, __m128i *m2, int len) +{ + while (len > 8) { + __m128i q2 = _mm_loadu_si128(m2); + __m128i cmp = _mm_cmpeq_epi16(*m1, q2); + if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) + return false; + + len -= 8; + ++m1; + ++m2; + } + return len == 0 || equals2_shortwise((ushort *)m1, (ushort *)m2, len); +} + +//#ifdef __SSSE3__ +static bool equals2_ssse3(ushort *p1, ushort *p2, int len) +{ + // p1 & 0xf can be: + // 0, 2, 4, 6, 8, 10, 12, 14 + // If it's 0, we're aligned + // If it's not, then we're interested in the 16 - (p1 & 0xf) bytes only + + if (len > 8) { + // find the last aligned position below the p1 memory + __m128i *m1 = (__m128i *)(quintptr(p1) & ~0xf); + __m128i *m2 = (__m128i *)p2; + uchar diff = quintptr(p1) - quintptr(m1); + + // diff contains the number of extra bytes + if (diff < 8) { + if (diff < 4) { + if (diff == 0) + return equals2_ssse3_aligned(m1, m2, len); + else // diff == 2 + return equals2_ssse3_alignr<2>(m1, m2, len); + } else { + if (diff == 4) + return equals2_ssse3_alignr<4>(m1, m2, len); + else // diff == 6 + return equals2_ssse3_alignr<6>(m1, m2, len); + } + } else { + if (diff < 12) { + if (diff == 8) + return equals2_ssse3_alignr<8>(m1, m2, len); + else // diff == 10 + return equals2_ssse3_alignr<10>(m1, m2, len); + } else { + if (diff == 12) + return equals2_ssse3_alignr<12>(m1, m2, len); + else // diff == 14 + return equals2_ssse3_alignr<14>(m1, m2, len); + } + } + +// switch (diff) { +// case 0: +// return equals2_ssse3_aligned(m1, m2, len); +// case 2: +// return equals2_ssse3_alignr<2>(m1, m2, len); +// case 4: +// return equals2_ssse3_alignr<4>(m1, m2, len); +// case 6: +// return equals2_ssse3_alignr<6>(m1, m2, len); +// case 8: +// return equals2_ssse3_alignr<8>(m1, m2, len); +// case 10: +// return equals2_ssse3_alignr<10>(m1, m2, len); +// case 12: +// return equals2_ssse3_alignr<12>(m1, m2, len); +// case 14: +// return equals2_ssse3_alignr<14>(m1, m2, len); +// } + } + + // tail + return equals2_shortwise(p1, p2, len); +} +//#endif #endif void tst_QString::equals2_data() const @@ -287,6 +391,9 @@ void tst_QString::equals2_data() const #ifdef __SSE2__ QTest::newRow("sse2") << 4; QTest::newRow("sse2_aligning") << 5; +#ifdef __SSSE3__ + QTest::newRow("ssse3") << 6; +#endif #endif } @@ -338,6 +445,9 @@ void tst_QString::equals2() const #ifdef __SSE2__ equals2_sse2, // 4 equals2_sse2_aligning, // 5 +#ifdef __SSSE3__ + equals2_ssse3, // 6 +#endif #endif 0 }; diff --git a/tests/benchmarks/corelib/tools/qstring/qstring.pro b/tests/benchmarks/corelib/tools/qstring/qstring.pro index 388e3c2..bc6254c 100644 --- a/tests/benchmarks/corelib/tools/qstring/qstring.pro +++ b/tests/benchmarks/corelib/tools/qstring/qstring.pro @@ -14,4 +14,5 @@ wince*:{ DEFINES += SRCDIR=\\\"$$PWD/\\\" } -sse2:QMAKE_CXXFLAGS += -msse2 +ssse3:QMAKE_FLAGS += -mssse3 +else:sse2:QMAKE_CXXFLAGS += -msse2 -- cgit v0.12 From a9dae34157aeef00512bea49f11a8dd1895f676b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 13 Aug 2010 00:59:27 +0200 Subject: Add an SSE4.2 version of the string comparison It's currently slightly worse than SSE2 with prolog aligning (i.e., it's no good) --- tests/benchmarks/corelib/tools/qstring/main.cpp | 41 ++++++++++++++++++++++ tests/benchmarks/corelib/tools/qstring/qstring.pro | 3 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index e1800c0..1c55f82 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -378,6 +378,41 @@ static bool equals2_ssse3(ushort *p1, ushort *p2, int len) return equals2_shortwise(p1, p2, len); } //#endif + +//#ifdef __SSE4_1__ +static bool equals2_sse4(ushort *p1, ushort *p2, int len) +{ + // We use the pcmpestrm instruction searching for differences (negative polarity) + // it will reset CF if it's all equal + // it will reset OF if the first char is equal + // it will set ZF & SF if the length is less than 8 (which means we've done the last operation) + // the three possible conditions are: + // difference found: CF = 1 + // all equal, not finished: CF = ZF = SF = 0 + // all equal, finished: CF = 0, ZF = SF = 1 + len += 8; + asm ( + "0:\n\t" + "movdqu (%[p1]), %%xmm0\n\t" // load 8 ushorts + "movdqu (%[p2]), %%xmm1\n\t" + "addl $16, %[p2]\n\t" + "addl $16, %[p1]\n\t" + "subl $8, %[len]\n\t" + "movl %[len], %%edx\n\t" + "pcmpestrm %[mode], %%xmm1, %%xmm0\n\t" + "ja 0b\n\t" + "1:\n\t" + "mov $0, %[len]\n\t" + "setnc %%al\n\t" + : [len] "+a" (len) + : [p1] "r" (p1), + [p2] "r" (p2), + [mode] "K" (_SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY | _SIDD_UNIT_MASK) + : "%edx", "%xmm0", "%xmm1" + ); + return len; +} +//#endif #endif void tst_QString::equals2_data() const @@ -393,6 +428,9 @@ void tst_QString::equals2_data() const QTest::newRow("sse2_aligning") << 5; #ifdef __SSSE3__ QTest::newRow("ssse3") << 6; +#ifdef __SSE4_1__ + QTest::newRow("sse4.2") << 7; +#endif #endif #endif } @@ -447,6 +485,9 @@ void tst_QString::equals2() const equals2_sse2_aligning, // 5 #ifdef __SSSE3__ equals2_ssse3, // 6 +#ifdef __SSE4_1__ + equals2_sse4, // 7 +#endif #endif #endif 0 diff --git a/tests/benchmarks/corelib/tools/qstring/qstring.pro b/tests/benchmarks/corelib/tools/qstring/qstring.pro index bc6254c..44fb46b 100644 --- a/tests/benchmarks/corelib/tools/qstring/qstring.pro +++ b/tests/benchmarks/corelib/tools/qstring/qstring.pro @@ -14,5 +14,6 @@ wince*:{ DEFINES += SRCDIR=\\\"$$PWD/\\\" } -ssse3:QMAKE_FLAGS += -mssse3 +sse4:QMAKE_CXXFLAGS += -msse4 +else:ssse3:QMAKE_FLAGS += -mssse3 else:sse2:QMAKE_CXXFLAGS += -msse2 -- cgit v0.12 From ca9d0f597fe6212aa89fa2d6d45b27bae147d195 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 13 Aug 2010 01:37:47 +0200 Subject: Slightly better version that saves EBX in an XMM register --- tests/benchmarks/corelib/tools/qstring/main.cpp | 35 ++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 1c55f82..fb65f65 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -390,27 +390,32 @@ static bool equals2_sse4(ushort *p1, ushort *p2, int len) // difference found: CF = 1 // all equal, not finished: CF = ZF = SF = 0 // all equal, finished: CF = 0, ZF = SF = 1 - len += 8; + bool result; asm ( + "movd %%ebx, %%xmm1\n\t" + "sub %[p1], %[p2]\n\t" + "mov %[p1], %%ebx\n\t" + "sub $16, %%ebx\n\t" + "add $8, %[len]\n\t" "0:\n\t" - "movdqu (%[p1]), %%xmm0\n\t" // load 8 ushorts - "movdqu (%[p2]), %%xmm1\n\t" - "addl $16, %[p2]\n\t" - "addl $16, %[p1]\n\t" - "subl $8, %[len]\n\t" - "movl %[len], %%edx\n\t" - "pcmpestrm %[mode], %%xmm1, %%xmm0\n\t" + "add $16, %%ebx\n\t" + "sub $8, %[len]\n\t" + "movdqu (%%ebx), %%xmm0\n\t" + "mov %[len], %%edx\n\t" + "pcmpestrm %[mode], (%[p2],%%ebx), %%xmm0\n\t" "ja 0b\n\t" - "1:\n\t" - "mov $0, %[len]\n\t" + "1:\n\t" + "mov $0, %%eax\n\t" "setnc %%al\n\t" - : [len] "+a" (len) - : [p1] "r" (p1), + "movd %%xmm1, %%ebx\n\t" + : [result] "=a" (result) + : [len] "0" (len), + [p1] "d" (p1), [p2] "r" (p2), - [mode] "K" (_SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY | _SIDD_UNIT_MASK) - : "%edx", "%xmm0", "%xmm1" + [mode] "K" (_SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY) + : "%xmm0", "%xmm1" ); - return len; + return result; } //#endif #endif -- cgit v0.12 From 64a6ce5fb031f151994f77b9d9abea52bb9e00d3 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 17 Aug 2010 20:20:28 +0200 Subject: Add a script to generate real-world data from applications --- .../corelib/tools/qstring/generatelist.pl | 200 +++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 tests/benchmarks/corelib/tools/qstring/generatelist.pl diff --git a/tests/benchmarks/corelib/tools/qstring/generatelist.pl b/tests/benchmarks/corelib/tools/qstring/generatelist.pl new file mode 100644 index 0000000..ed4c8eb --- /dev/null +++ b/tests/benchmarks/corelib/tools/qstring/generatelist.pl @@ -0,0 +1,200 @@ +#!/usr/bin/perl +## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +## All rights reserved. +## Contact: Nokia Corporation (qt-info@nokia.com) +## +## This file is part of the QtCore module of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## No Commercial Usage +## This file contains pre-release code and may not be distributed. +## You may use this file in accordance with the terms and conditions +## contained in the Technology Preview License Agreement accompanying +## this package. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 2.1 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 2.1 requirements +## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Nokia gives you certain additional +## rights. These rights are described in the Nokia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## If you have questions regarding the use of this file, please contact +## Nokia at qt-info@nokia.com. +## +## +## +## +## +## +## +## +## $QT_END_LICENSE$ +# +# Parses a file (passed as argument) that contains a dump of pairs of +# strings and generates C source code including said data. +# +# The format of the file is: +# LEN = \n\n +# where: +# LEN the literal string "LEN" +# the length of the data, in 16-bit words +# the literal string "SAME" or "DIFF" +# the alignment or pointer value of the first data +# the alignment or pointer value of the second data +# the first data +# the second data +# \n newline +# +# The C code to write this data would be: +# fprintf(out, "LEN = %d %s %d %d\n", len, +# (p1 == p2) : "SAME" : "DIFF", +# (int)p1 & 0xfff, (int)p2 & 0xfff); +# fwrite(p1, 2, len, out); +# fwrite(p2, 2, len, out); +# fwrite("\n", 1, 1, out); + +sub printUshortArray($$$) { + $str = $_[0]; + $align = $_[1] & 0x1f; + $offset = $_[2]; + + die if ($align & 1) != 0; + $align /= 2; + + $len = (length $str) / 2; + $headpadding = $align & 0x7; + $tailpadding = 8 - (($len + $headpadding) & 0x7); + $multiplecachelines = ($align + $len) > 0x20; + + if ($multiplecachelines) { + # if this string crosses into a new cacheline, then + # replicate the result + $headpadding |= ($offset & ~0x1f); + $headpadding += 0x20 + if ($headpadding < $offset); + $headpadding -= $offset; + ++$cachelinecrosses; + } + for $i (1..$headpadding) { + print 65536-$i,","; + } + print "\n " if ($headpadding > 0); + print " " if ($headpadding == 0); + + for ($i = 0; $i < $len * 2; $i += 2) { + print " ", ord(substr($str, $i, 1)) + + ord(substr($str, $i + 1, 1)) * 256, + ","; + } + print "\n " if ($tailpadding > 0); + + for $i (1..$tailpadding) { + print 65536-$i, ","; + } + print " // ", $offset + $headpadding + $len + $tailpadding; + print "+" if $multiplecachelines; + + return ($offset + $headpadding, $offset + $headpadding + $len + $tailpadding); +} + +print "static const ushort stringCollectionData[] __attribute__((aligned(16))) = { \n"; +$count = 0; +$offset = 0; +$totalsize = 0; +$maxlen = 0; +$cachelinecrosses = 0; + +open IN, "<" . $ARGV[0]; +while (1) { + $line = readline(*IN); + last unless defined($line); + $line =~ /LEN = (\d+) (\w+) (\d+) (\d+)/; + $len = $1; + $data[$count]->{len} = $len; + $sameptr = $2; + $data[$count]->{align1} = $3 - 0; + $data[$count]->{align2} = $4 - 0; + + # statistics + $alignhistogram{$3 & 0xf}++; + $alignhistogram{$4 & 0xf}++; + $samealignments{$3 & 0xf}++ if ($3 & 0xf) == ($4 & 0xf); + + read IN, $a, $len * 2; + read IN, $b, $len * 2; + + ; # Eat the newline + + if ($len == 0) { + $data[$count]->{offset1} = $offset; + $data[$count]->{offset2} = $data[$count]->{offset1}; + ++$data[$count]->{offset2} if ($sameptr eq "DIFF"); + } else { + print " // #$count\n"; + print " "; + ($data[$count]->{offset1}, $offset) = + printUshortArray($a, $data[$count]->{align1}, $offset); + print "\n "; + die if ($offset & 0x7) != 0; + + if ($sameptr eq "DIFF") { + ($data[$count]->{offset2}, $offset) = + printUshortArray($b, $data[$count]->{align2}, $offset); + print "\n\n"; + } else { + $data[$count]->{offset2} = $data[$count]->{offset1}; + print "\n\n"; + } + } + ++$count; + + $totalsize += $len; + $maxlen = $len if $len > $maxlen; +} +print "\n};\n"; +close IN; + +print "static struct StringCollection\n"; +print "{\n"; +print " int len;\n"; +print " int offset1, offset2;\n"; +print " ushort align1, align2;\n"; +print "} stringCollection[] = {\n"; + +for $i (0..$count-1) { + print " {", + $data[$i]->{len}, ", ", + $data[$i]->{offset1}, ", ", + $data[$i]->{offset2}, ", ", + $data[$i]->{align1}, ", ", + $data[$i]->{align2}, + "}, // #$i\n"; + next if $data[$i]->{len} == 0; + die if (($data[$i]->{offset1} & 0x7) != ($data[$i]->{align1} & 0xf)/2); + die if (($data[$i]->{offset2} & 0x7) != ($data[$i]->{align2} & 0xf)/2); +} +print "};\n"; + +print "static const int stringCollectionCount = $count;\n"; +print "static const int stringCollectionMaxLen = $maxlen;\n"; +printf "// average comparison length: %.4f\n", ($totalsize * 1.0 / $count); +printf "// cache-line crosses: %d (%.1f%%)\n", + $cachelinecrosses, ($cachelinecrosses * 100.0 / $count / 2); + +print "// alignment histogram:\n"; +for $key (sort { $a <=> $b } keys(%alignhistogram)) { + $value = $alignhistogram{$key}; + $samealigned = $samealignments{$key}; + printf "// 0xXXX%x = %d (%.1f%%) strings, %d (%.1f%%) of which same-aligned\n", + $key, $value, $value * 100.0 / ($count*2), + $samealigned, $samealigned * 100.0 / $value; + $samealignedtotal += $samealigned; +} +printf "// total = %d (100%) strings, %d (%.1f%%) of which same-aligned\n", + $count * 2, $samealignedtotal, $samealignedtotal * 100 / $count / 2; -- cgit v0.12 From c58293407519e7413cb6493f831ef69cf2323030 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 17 Aug 2010 20:32:27 +0200 Subject: Major improvements to the comparison functions. Also use the real-world data that I collected. The resulting files are quite large, so I've added to Git only the smallest dump (apparently Teambuilder scanning the environment for "TEAMBUILDER="). --- tests/benchmarks/corelib/tools/qstring/data.cpp | 1283 +++++++++++++++++++++++ tests/benchmarks/corelib/tools/qstring/main.cpp | 675 ++++++++---- 2 files changed, 1770 insertions(+), 188 deletions(-) create mode 100644 tests/benchmarks/corelib/tools/qstring/data.cpp diff --git a/tests/benchmarks/corelib/tools/qstring/data.cpp b/tests/benchmarks/corelib/tools/qstring/data.cpp new file mode 100644 index 0000000..89f50d0 --- /dev/null +++ b/tests/benchmarks/corelib/tools/qstring/data.cpp @@ -0,0 +1,1283 @@ +static const ushort stringCollectionData[] __attribute__((aligned(16))) = { + // #0 + 65535, + 99, 111, 109, 112, 105, 108, 101, 114, 32, 118, 101, 114, 115, 105, 111, 110, 115, 47, + 65535,65534,65533,65532,65531, // 24 + 65535,65534,65533,65532,65531, + 99, 111, 109, 112, 105, 108, 101, 114, 32, 118, 101, 114, 115, 105, 111, 110, 115, 47, + 65535, // 48 + + // #1 + 65535,65534,65533,65532,65531, + 99, 111, 109, 112, 105, 108, 101, 114, 32, 118, 101, 114, 115, 105, 111, 110, 115, 47, + 65535, // 72 + 65535,65534,65533,65532,65531, + 67, 111, 109, 112, 105, 108, 101, 114, 32, 86, 101, 114, 115, 105, 111, 110, 115, 47, + 65535, // 96 + + // #2 + 65535, + 99, 111, 109, 112, 105, 108, 101, 114, 32, 116, 105, 109, 101, 115, 116, 97, 109, 112, 115, 47, + 65535,65534,65533, // 120 + 65535,65534,65533,65532,65531, + 99, 111, 109, 112, 105, 108, 101, 114, 32, 116, 105, 109, 101, 115, 116, 97, 109, 112, 115, 47, + 65535,65534,65533,65532,65531,65530,65529, // 152 + + // #3 + 65535,65534,65533,65532,65531, + 99, 111, 109, 112, 105, 108, 101, 114, 32, 116, 105, 109, 101, 115, 116, 97, 109, 112, 115, 47, + 65535,65534,65533,65532,65531,65530,65529, // 184 + 65535, + 67, 111, 109, 112, 105, 108, 101, 114, 32, 84, 105, 109, 101, 115, 116, 97, 109, 112, 115, 47, + 65535,65534,65533, // 208 + + // #4 + 65535,65534,65533,65532,65531,65530,65529,65528,65527,65526,65525,65524,65523,65522,65521,65520,65519, + 47, 118, 97, 114, 47, 116, 109, 112, 47, 116, 101, 97, 109, 98, 117, 105, 108, 100, 101, 114, 45, 116, 109, 97, 99, 105, 101, 105, 114, 47, 99, 108, 105, 101, 110, 116, 47, 99, 111, 109, 112, 105, 108, 101, 114, 115, 46, 99, 111, 110, 102, + 65535,65534,65533,65532, // 280+ + + + // #5 + 65535,65534,65533,65532,65531,65530,65529,65528,65527,65526,65525,65524,65523, + 47, 118, 97, 114, 47, 116, 109, 112, 47, 116, 101, 97, 109, 98, 117, 105, 108, 100, 101, 114, 45, 116, 109, 97, 99, 105, 101, 105, 114, 47, 99, 108, 105, 101, 110, 116, 47, 99, 111, 109, 112, 105, 108, 101, 114, 115, 46, 99, 111, 110, 102, + 65535,65534,65533,65532,65531,65530,65529,65528, // 352+ + 65535, + 47, 118, 97, 114, 47, 116, 109, 112, 47, 116, 101, 97, 109, 98, 117, 105, 108, 100, 101, 114, 45, 116, 109, 97, 99, 105, 101, 105, 114, 47, 99, 108, 105, 101, 110, 116, 47, 99, 111, 109, 112, 105, 108, 101, 114, 115, 46, 99, 111, 110, 102, + 65535,65534,65533,65532, // 408+ + + // #6 + 65535,65534,65533,65532,65531,65530,65529,65528,65527, + 47, 118, 97, 114, 47, 116, 109, 112, 47, 116, 101, 97, 109, 98, 117, 105, 108, 100, 101, 114, 45, 116, 109, 97, 99, 105, 101, 105, 114, 47, 99, 108, 105, 101, 110, 116, 47, 99, 111, 109, 112, 105, 108, 101, 114, 115, 46, 99, 111, 110, 102, + 65535,65534,65533,65532, // 472+ + + + // #7 + 65535, + 97, 114, 99, 104, 105, 118, 101, 100, 32, 99, 111, 109, 112, 105, 108, 101, 114, 115, 47, + 65535,65534,65533,65532, // 496 + 65535,65534,65533,65532,65531, + 97, 114, 99, 104, 105, 118, 101, 100, 32, 99, 111, 109, 112, 105, 108, 101, 114, 115, 47, + 65535,65534,65533,65532,65531,65530,65529,65528, // 528 + + // #8 + 65535,65534,65533,65532,65531, + 97, 114, 99, 104, 105, 118, 101, 100, 32, 99, 111, 109, 112, 105, 108, 101, 114, 115, 47, + 65535,65534,65533,65532,65531,65530,65529,65528, // 560 + 65535,65534,65533,65532,65531, + 65, 114, 99, 104, 105, 118, 101, 100, 32, 67, 111, 109, 112, 105, 108, 101, 114, 115, 47, + 65535,65534,65533,65532,65531,65530,65529,65528, // 592 + + // #9 + 65535,65534,65533,65532,65531,65530,65529,65528,65527,65526,65525,65524,65523,65522,65521,65520,65519, + 47, 118, 97, 114, 47, 116, 109, 112, 47, 116, 101, 97, 109, 98, 117, 105, 108, 100, 101, 114, 45, 116, 109, 97, 99, 105, 101, 105, 114, 47, 99, 108, 105, 101, 110, 116, 47, 99, 111, 109, 112, 105, 108, 101, 114, 115, 46, 99, 111, 110, 102, + 65535,65534,65533,65532, // 664+ + 65535,65534,65533,65532,65531,65530,65529,65528,65527,65526,65525,65524,65523, + 47, 118, 97, 114, 47, 116, 109, 112, 47, 116, 101, 97, 109, 98, 117, 105, 108, 100, 101, 114, 45, 116, 109, 97, 99, 105, 101, 105, 114, 47, 99, 108, 105, 101, 110, 116, 47, 99, 111, 109, 112, 105, 108, 101, 114, 115, 46, 99, 111, 110, 102, + 65535,65534,65533,65532,65531,65530,65529,65528, // 736+ + + // #10 + 65535,65534,65533,65532,65531, + 76, 105, 110, 117, 120, + 65535,65534,65533,65532,65531,65530, // 752 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 760 + + // #11 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 776 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 792 + + // #12 + 65535, + 105, 99, 99, + 65535,65534,65533,65532, // 800 + 65535,65534,65533,65532,65531, + 103, 43, 43, + 65535,65534,65533,65532,65531,65530,65529,65528, // 816 + + // #13 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 824 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 832 + + // #14 + 65535, + 105, 51, 56, 54, + 65535,65534,65533, // 840 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 856 + + // #15 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 864 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 872 + + // #16 + 65535, + 105, 51, 56, 54, + 65535,65534,65533, // 880 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 896 + + // #17 + 65535, + 103, 99, 99, + 65535,65534,65533,65532, // 904 + 65535,65534,65533,65532,65531, + 103, 43, 43, + 65535,65534,65533,65532,65531,65530,65529,65528, // 920 + + // #18 + 65535,65534,65533,65532,65531, + 76, 105, 110, 117, 120, + 65535,65534,65533,65532,65531,65530, // 936 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 944 + + // #19 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 960 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 976 + + // #20 + 65535, + 103, 43, 43, + 65535,65534,65533,65532, // 984 + 65535,65534,65533,65532,65531, + 103, 43, 43, + 65535,65534,65533,65532,65531,65530,65529,65528, // 1000 + + // #21 + 65535,65534,65533,65532,65531, + 52, 46, 52, 46, 51, + 65535,65534,65533,65532,65531,65530, // 1016 + 65535,65534,65533,65532,65531, + 52, 46, 52, 46, 51, + 65535,65534,65533,65532,65531,65530, // 1032 + + // #22 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1040 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1048 + + // #23 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1064 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1080 + + // #24 + 65535, + 47, 117, 115, 114, 47, 98, 105, 110, 47, 103, 43, 43, + 65535,65534,65533, // 1096 + 65535, + 47, 117, 115, 114, 47, 98, 105, 110, 47, 103, 43, 43, + 65535,65534,65533, // 1112 + + // #25 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1120 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1128 + + // #26 + 65535, + 105, 51, 56, 54, + 65535,65534,65533, // 1136 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1152 + + // #27 + 65535, + 105, 99, 99, + 65535,65534,65533,65532, // 1160 + 65535,65534,65533,65532,65531, + 103, 43, 43, + 65535,65534,65533,65532,65531,65530,65529,65528, // 1176 + + // #28 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1184 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1192 + + // #29 + 65535, + 105, 51, 56, 54, + 65535,65534,65533, // 1200 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1216 + + // #30 + 65535,65534,65533,65532,65531, + 76, 105, 110, 117, 120, + 65535,65534,65533,65532,65531,65530, // 1232 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1240 + + // #31 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1256 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1272 + + // #32 + 65535,65534,65533,65532,65531, + 76, 105, 110, 117, 120, + 65535,65534,65533,65532,65531,65530, // 1288 + 65535, + 76, 105, 110, 117, 120, + 65535,65534, // 1296 + + // #33 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1312 + 65535,65534,65533,65532,65531, + 105, 51, 56, 54, + 65535,65534,65533,65532,65531,65530,65529, // 1328 + + // #34 + 65535, + 45, 109, 97, 114, 99, 104, 61, 99, 111, 114, 101, 50, + 65535,65534,65533, // 1344 + 65535, + 116, 98, 51, 54, 57, 54, 56, 95, 50, 46, 105, 105, + 65535,65534,65533, // 1360 + + // #35 + 65535,65534,65533,65532,65531, + 45, 102, 108, 111, 111, 112, 45, 98, 108, 111, 99, 107, + 65535,65534,65533,65532,65531,65530,65529, // 1384 + 65535, + 116, 98, 51, 54, 57, 54, 56, 95, 50, 46, 105, 105, + 65535,65534,65533, // 1400 + + // #36 + 65535,65534,65533,65532,65531, + 116, 98, 51, 54, 57, 54, 56, 95, 50, 46, 105, 105, + 65535,65534,65533,65532,65531,65530,65529, // 1424 + 65535, + 116, 98, 51, 54, 57, 54, 56, 95, 50, 46, 105, 105, + 65535,65534,65533, // 1440 + + // #37 + 65535,65534,65533,65532,65531, + 45, 109, 115, 115, 101, 52, + 65535,65534,65533,65532,65531, // 1456 + 65535,65534,65533,65532,65531, + 108, 101, 110, 103, 116, 104, + 65535,65534,65533,65532,65531, // 1472 + + // #38 + 65535,65534,65533,65532,65531, + 116, 98, 51, 54, 57, 54, 56, 95, 49, 46, 111, + 65535,65534,65533,65532,65531,65530,65529,65528, // 1496 + 65535,65534,65533,65532,65531, + 116, 98, 51, 54, 57, 54, 56, 95, 49, 46, 111, + 65535,65534,65533,65532,65531,65530,65529,65528, // 1520 + + // #39 + 65535,65534,65533,65532,65531, + 68, 69, 83, 75, 84, 79, 80, 95, 83, 69, 83, 83, + 65535,65534,65533,65532,65531,65530,65529, // 1544 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1560 + + // #40 + 65535,65534,65533,65532,65531, + 76, 67, 95, 83, 79, 85, 82, 67, 69, 68, 61, 49, + 65535,65534,65533,65532,65531,65530,65529, // 1584 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1600 + + // #41 + 65535,65534,65533,65532,65531, + 81, 84, 68, 73, 82, 61, 47, 104, 111, 109, 101, 47, + 65535,65534,65533,65532,65531,65530,65529, // 1624 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1640 + + // #42 + 65535, + 76, 67, 95, 67, 84, 89, 80, 69, 61, 112, 116, 95, + 65535,65534,65533, // 1656 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1672 + + // #43 + 65535, + 71, 84, 75, 95, 82, 67, 95, 70, 73, 76, 69, 83, + 65535,65534,65533, // 1688 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1704 + + // #44 + 65535, + 88, 77, 79, 68, 73, 70, 73, 69, 82, 83, 61, 64, + 65535,65534,65533, // 1720 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1736 + + // #45 + 65535, + 83, 72, 69, 76, 76, 61, 47, 98, 105, 110, 47, 122, + 65535,65534,65533, // 1752 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1768 + + // #46 + 65535,65534,65533,65532,65531, + 85, 61, 64, 123, 117, 112, 115, 116, 114, 101, 97, 109, + 65535,65534,65533,65532,65531,65530,65529, // 1792 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1808 + + // #47 + 65535, + 95, 61, 47, 117, 115, 114, 47, 98, 105, 110, 47, 105, + 65535,65534,65533, // 1824 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1840 + + // #48 + 65535, + 88, 68, 71, 95, 67, 79, 78, 70, 73, 71, 95, 68, + 65535,65534,65533, // 1856 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1872 + + // #49 + 65535,65534,65533,65532,65531, + 83, 65, 86, 69, 72, 73, 83, 84, 61, 49, 48, 48, + 65535,65534,65533,65532,65531,65530,65529, // 1896 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1912 + + // #50 + 65535, + 75, 68, 69, 95, 77, 85, 76, 84, 73, 72, 69, 65, + 65535,65534,65533, // 1928 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1944 + + // #51 + 65535,65534,65533,65532,65531, + 77, 65, 76, 76, 79, 67, 95, 67, 72, 69, 67, 75, + 65535,65534,65533,65532,65531,65530,65529, // 1968 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 1984 + + // #52 + 65535,65534,65533,65532,65531, + 72, 73, 83, 84, 67, 79, 78, 84, 82, 79, 76, 61, + 65535,65534,65533,65532,65531,65530,65529, // 2008 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2024 + + // #53 + 65535,65534,65533,65532,65531, + 88, 68, 71, 95, 68, 65, 84, 65, 95, 68, 73, 82, + 65535,65534,65533,65532,65531,65530,65529, // 2048 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2064 + + // #54 + 65535,65534,65533,65532,65531, + 88, 68, 77, 95, 77, 65, 78, 65, 71, 69, 68, 61, + 65535,65534,65533,65532,65531,65530,65529, // 2088 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2104 + + // #55 + 65535, + 76, 67, 95, 67, 79, 76, 76, 65, 84, 69, 61, 112, + 65535,65534,65533, // 2120 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2136 + + // #56 + 65535, + 81, 84, 95, 80, 76, 85, 71, 73, 78, 95, 80, 65, + 65535,65534,65533, // 2152 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2168 + + // #57 + 65535, + 83, 67, 82, 69, 69, 78, 68, 73, 82, 61, 47, 104, + 65535,65534,65533, // 2184 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2200 + + // #58 + 65535,65534,65533,65532,65531, + 76, 69, 83, 83, 79, 80, 69, 78, 61, 124, 47, 117, + 65535,65534,65533,65532,65531,65530,65529, // 2224 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2240 + + // #59 + 65535, + 76, 67, 95, 78, 65, 77, 69, 61, 110, 98, 95, 78, + 65535,65534,65533, // 2256 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2272 + + // #60 + 65535,65534,65533,65532,65531, + 80, 52, 67, 76, 73, 69, 78, 84, 61, 116, 109, 97, + 65535,65534,65533,65532,65531,65530,65529, // 2296 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2312 + + // #61 + 65535, + 80, 65, 84, 72, 61, 47, 104, 111, 109, 101, 47, 116, + 65535,65534,65533, // 2328 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2344 + + // #62 + 65535,65534,65533,65532,65531, + 71, 80, 71, 95, 65, 71, 69, 78, 84, 95, 73, 78, + 65535,65534,65533,65532,65531,65530,65529, // 2368 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2384 + + // #63 + 65535,65534,65533,65532,65531, + 88, 67, 85, 82, 83, 79, 82, 95, 84, 72, 69, 77, + 65535,65534,65533,65532,65531,65530,65529, // 2408 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2424 + + // #64 + 65535,65534,65533,65532,65531, + 83, 69, 83, 83, 73, 79, 78, 95, 77, 65, 78, 65, + 65535,65534,65533,65532,65531,65530,65529, // 2448 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2464 + + // #65 + 65535, + 81, 84, 83, 82, 67, 68, 73, 82, 61, 47, 104, 111, + 65535,65534,65533, // 2480 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2496 + + // #66 + 65535,65534,65533,65532,65531, + 87, 73, 78, 68, 79, 87, 73, 68, 61, 52, 54, 49, + 65535,65534,65533,65532,65531,65530,65529, // 2520 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2536 + + // #67 + 65535,65534,65533,65532,65531, + 76, 67, 95, 77, 69, 83, 83, 65, 71, 69, 83, 61, + 65535,65534,65533,65532,65531,65530,65529, // 2560 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2576 + + // #68 + 65535, + 76, 67, 95, 78, 85, 77, 69, 82, 73, 67, 61, 110, + 65535,65534,65533, // 2592 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2608 + + // #69 + 65535, + 71, 84, 75, 50, 95, 82, 67, 95, 70, 73, 76, 69, + 65535,65534,65533, // 2624 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2640 + + // #70 + 65535, + 80, 82, 79, 70, 73, 76, 69, 72, 79, 77, 69, 61, + 65535,65534,65533, // 2656 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2672 + + // #71 + 65535,65534,65533,65532,65531, + 68, 77, 95, 67, 79, 78, 84, 82, 79, 76, 61, 47, + 65535,65534,65533,65532,65531,65530,65529, // 2696 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2712 + + // #72 + 65535, + 76, 83, 95, 67, 79, 76, 79, 82, 83, 61, 114, 115, + 65535,65534,65533, // 2728 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2744 + + // #73 + 65535,65534,65533,65532,65531, + 83, 83, 72, 95, 65, 85, 84, 72, 95, 83, 79, 67, + 65535,65534,65533,65532,65531,65530,65529, // 2768 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2784 + + // #74 + 65535,65534,65533,65532,65531, + 75, 68, 69, 68, 73, 82, 83, 61, 47, 104, 111, 109, + 65535,65534,65533,65532,65531,65530,65529, // 2808 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2824 + + // #75 + 65535,65534,65533,65532,65531, + 76, 68, 95, 80, 82, 69, 76, 79, 65, 68, 61, 47, + 65535,65534,65533,65532,65531,65530,65529, // 2848 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2864 + + // #76 + 65535,65534,65533,65532,65531, + 88, 67, 85, 82, 83, 79, 82, 95, 80, 65, 84, 72, + 65535,65534,65533,65532,65531,65530,65529, // 2888 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2904 + + // #77 + 65535, + 115, 114, 99, 100, 105, 114, 61, 47, 104, 111, 109, 101, + 65535,65534,65533, // 2920 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2936 + + // #78 + 65535,65534,65533,65532,65531, + 72, 79, 77, 69, 61, 47, 104, 111, 109, 101, 47, 116, + 65535,65534,65533,65532,65531,65530,65529, // 2960 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 2976 + + // #79 + 65535, + 81, 84, 52, 68, 79, 67, 68, 73, 82, 61, 47, 117, + 65535,65534,65533, // 2992 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3008 + + // #80 + 65535,65534,65533,65532,65531, + 80, 87, 68, 61, 47, 104, 111, 109, 101, 47, 116, 109, + 65535,65534,65533,65532,65531,65530,65529, // 3032 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3048 + + // #81 + 65535,65534,65533,65532,65531, + 75, 68, 69, 95, 83, 69, 83, 83, 73, 79, 78, 95, + 65535,65534,65533,65532,65531,65530,65529, // 3072 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3088 + + // #82 + 65535, + 73, 78, 83, 73, 68, 69, 95, 83, 80, 69, 67, 73, + 65535,65534,65533, // 3104 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3120 + + // #83 + 65535, + 83, 83, 72, 95, 65, 71, 69, 78, 84, 95, 80, 73, + 65535,65534,65533, // 3136 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3152 + + // #84 + 65535, + 80, 75, 71, 95, 67, 79, 78, 70, 73, 71, 95, 80, + 65535,65534,65533, // 3168 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3184 + + // #85 + 65535,65534,65533,65532,65531, + 68, 66, 85, 83, 95, 83, 69, 83, 83, 73, 79, 78, + 65535,65534,65533,65532,65531,65530,65529, // 3208 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3224 + + // #86 + 65535, + 76, 68, 95, 76, 73, 66, 82, 65, 82, 89, 95, 80, + 65535,65534,65533, // 3240 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3256 + + // #87 + 65535,65534,65533,65532,65531, + 80, 52, 85, 83, 69, 82, 61, 116, 106, 109, 97, 99, + 65535,65534,65533,65532,65531,65530,65529, // 3280 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3296 + + // #88 + 65535,65534,65533,65532,65531, + 81, 84, 69, 83, 84, 95, 67, 79, 76, 79, 82, 69, + 65535,65534,65533,65532,65531,65530,65529, // 3320 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3336 + + // #89 + 65535,65534,65533,65532,65531, + 88, 68, 71, 95, 83, 69, 83, 83, 73, 79, 78, 95, + 65535,65534,65533,65532,65531,65530,65529, // 3360 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3376 + + // #90 + 65535,65534,65533,65532,65531, + 76, 69, 83, 83, 75, 69, 89, 61, 47, 101, 116, 99, + 65535,65534,65533,65532,65531,65530,65529, // 3400 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3416 + + // #91 + 65535, + 76, 79, 71, 78, 65, 77, 69, 61, 116, 109, 97, 99, + 65535,65534,65533, // 3432 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3448 + + // #92 + 65535, + 71, 95, 70, 73, 76, 69, 78, 65, 77, 69, 95, 69, + 65535,65534,65533, // 3464 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3480 + + // #93 + 65535, + 75, 68, 69, 95, 70, 85, 76, 76, 95, 83, 69, 83, + 65535,65534,65533, // 3496 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3512 + + // #94 + 65535,65534,65533,65532,65531, + 72, 79, 83, 84, 78, 65, 77, 69, 61, 108, 111, 116, + 65535,65534,65533,65532,65531,65530,65529, // 3536 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3552 + + // #95 + 65535,65534,65533,65532,65531, + 76, 67, 95, 84, 73, 77, 69, 61, 112, 116, 95, 66, + 65535,65534,65533,65532,65531,65530,65529, // 3576 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3592 + + // #96 + 65535, + 83, 83, 72, 95, 65, 83, 75, 80, 65, 83, 83, 61, + 65535,65534,65533, // 3608 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3624 + + // #97 + 65535,65534,65533,65532,65531, + 72, 73, 83, 84, 70, 73, 76, 69, 61, 47, 104, 111, + 65535,65534,65533,65532,65531,65530,65529, // 3648 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3664 + + // #98 + 65535, + 75, 79, 78, 83, 79, 76, 69, 95, 68, 66, 85, 83, + 65535,65534,65533, // 3680 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3696 + + // #99 + 65535, + 77, 65, 75, 69, 61, 47, 117, 115, 114, 47, 98, 105, + 65535,65534,65533, // 3712 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3728 + + // #100 + 65535, + 67, 65, 78, 66, 69, 82, 82, 65, 95, 68, 82, 73, + 65535,65534,65533, // 3744 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3760 + + // #101 + 65535, + 71, 67, 79, 78, 70, 95, 84, 77, 80, 68, 73, 82, + 65535,65534,65533, // 3776 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3792 + + // #102 + 65535,65534,65533,65532,65531, + 85, 83, 69, 82, 61, 116, 109, 97, 99, 105, 101, 105, + 65535,65534,65533,65532,65531,65530,65529, // 3816 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3832 + + // #103 + 65535, + 111, 98, 106, 100, 105, 114, 61, 47, 104, 111, 109, 101, + 65535,65534,65533, // 3848 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3864 + + // #104 + 65535,65534,65533,65532,65531, + 76, 67, 95, 77, 79, 78, 69, 84, 65, 82, 89, 61, + 65535,65534,65533,65532,65531,65530,65529, // 3888 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3904 + + // #105 + 65535,65534,65533,65532,65531, + 81, 84, 76, 73, 66, 61, 47, 117, 115, 114, 47, 108, + 65535,65534,65533,65532,65531,65530,65529, // 3928 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3944 + + // #106 + 65535,65534,65533,65532,65531, + 76, 67, 95, 84, 69, 76, 69, 80, 72, 79, 78, 69, + 65535,65534,65533,65532,65531,65530,65529, // 3968 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 3984 + + // #107 + 65535, + 80, 89, 84, 72, 79, 78, 68, 79, 78, 84, 87, 82, + 65535,65534,65533, // 4000 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4016 + + // #108 + 65535,65534,65533,65532,65531, + 84, 77, 80, 68, 73, 82, 61, 47, 116, 109, 112, 47, + 65535,65534,65533,65532,65531,65530,65529, // 4040 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4056 + + // #109 + 65535,65534,65533,65532,65531, + 65, 82, 77, 76, 77, 68, 95, 76, 73, 67, 69, 78, + 65535,65534,65533,65532,65531,65530,65529, // 4080 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4096 + + // #110 + 65535, + 80, 89, 84, 72, 79, 78, 80, 65, 84, 72, 61, 47, + 65535,65534,65533, // 4112 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4128 + + // #111 + 65535,65534,65533,65532,65531, + 77, 65, 75, 69, 70, 76, 65, 71, 83, 61, 119, 32, + 65535,65534,65533,65532,65531,65530,65529, // 4152 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4168 + + // #112 + 65535, + 77, 70, 76, 65, 71, 83, 61, 45, 119, 32, 45, 45, + 65535,65534,65533, // 4184 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4200 + + // #113 + 65535, + 77, 65, 73, 76, 61, 47, 118, 97, 114, 47, 115, 112, + 65535,65534,65533, // 4216 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4232 + + // #114 + 65535,65534,65533,65532,65531, + 83, 72, 69, 76, 76, 95, 83, 69, 83, 83, 73, 79, + 65535,65534,65533,65532,65531,65530,65529, // 4256 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4272 + + // #115 + 65535, + 75, 68, 69, 68, 73, 82, 61, 47, 104, 111, 109, 101, + 65535,65534,65533, // 4288 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4304 + + // #116 + 65535,65534,65533,65532,65531, + 76, 69, 83, 83, 67, 72, 65, 82, 83, 69, 84, 61, + 65535,65534,65533,65532,65531,65530,65529, // 4328 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4344 + + // #117 + 65535,65534,65533,65532,65531, + 76, 67, 95, 80, 65, 80, 69, 82, 61, 110, 98, 95, + 65535,65534,65533,65532,65531,65530,65529, // 4368 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4384 + + // #118 + 65535, + 66, 82, 79, 87, 83, 69, 82, 61, 47, 117, 115, 114, + 65535,65534,65533, // 4400 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4416 + + // #119 + 65535, + 77, 69, 84, 65, 95, 67, 76, 65, 83, 83, 61, 100, + 65535,65534,65533, // 4432 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4448 + + // #120 + 65535,65534,65533,65532,65531, + 77, 68, 86, 95, 77, 69, 78, 85, 95, 83, 84, 89, + 65535,65534,65533,65532,65531,65530,65529, // 4472 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4488 + + // #121 + 65535,65534,65533,65532,65531, + 67, 79, 76, 79, 82, 70, 71, 66, 71, 61, 49, 53, + 65535,65534,65533,65532,65531,65530,65529, // 4512 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4528 + + // #122 + 65535,65534,65533,65532,65531, + 80, 89, 84, 72, 79, 78, 83, 84, 65, 82, 84, 85, + 65535,65534,65533,65532,65531,65530,65529, // 4552 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4568 + + // #123 + 65535, + 76, 67, 95, 77, 69, 65, 83, 85, 82, 69, 77, 69, + 65535,65534,65533, // 4584 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4600 + + // #124 + 65535,65534,65533,65532,65531, + 69, 68, 73, 84, 79, 82, 61, 47, 117, 115, 114, 47, + 65535,65534,65533,65532,65531,65530,65529, // 4624 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4640 + + // #125 + 65535,65534,65533,65532,65531, + 69, 78, 95, 84, 66, 61, 109, 111, 99, 58, 117, 105, + 65535,65534,65533,65532,65531,65530,65529, // 4664 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4680 + + // #126 + 65535, + 72, 73, 83, 84, 83, 73, 90, 69, 61, 49, 48, 48, + 65535,65534,65533, // 4696 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4712 + + // #127 + 65535,65534,65533,65532,65531, + 71, 83, 95, 76, 73, 66, 61, 47, 104, 111, 109, 101, + 65535,65534,65533,65532,65531,65530,65529, // 4736 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4752 + + // #128 + 65535,65534,65533,65532,65531, + 78, 76, 83, 80, 65, 84, 72, 61, 47, 117, 115, 114, + 65535,65534,65533,65532,65531,65530,65529, // 4776 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4792 + + // #129 + 65535,65534,65533,65532,65531, + 87, 73, 78, 68, 79, 87, 80, 65, 84, 72, 61, 55, + 65535,65534,65533,65532,65531,65530,65529, // 4816 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4832 + + // #130 + 65535,65534,65533,65532,65531, + 75, 79, 78, 83, 79, 76, 69, 95, 68, 66, 85, 83, + 65535,65534,65533,65532,65531,65530,65529, // 4856 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4872 + + // #131 + 65535, + 76, 67, 95, 73, 68, 69, 78, 84, 73, 70, 73, 67, + 65535,65534,65533, // 4888 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4904 + + // #132 + 65535, + 73, 78, 80, 85, 84, 82, 67, 61, 47, 101, 116, 99, + 65535,65534,65533, // 4920 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4936 + + // #133 + 65535,65534,65533,65532,65531, + 81, 84, 73, 78, 67, 61, 47, 117, 115, 114, 47, 108, + 65535,65534,65533,65532,65531,65530,65529, // 4960 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 4976 + + // #134 + 65535, + 76, 67, 95, 65, 68, 68, 82, 69, 83, 83, 61, 110, + 65535,65534,65533, // 4992 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5008 + + // #135 + 65535,65534,65533,65532,65531, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 95, + 65535,65534,65533,65532,65531,65530,65529, // 5032 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5048 + + // #136 + 65535, + 76, 65, 78, 71, 61, 112, 116, 95, 66, 82, 46, 85, + 65535,65534,65533, // 5064 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5080 + + // #137 + 65535, + 80, 52, 80, 79, 82, 84, 61, 112, 52, 46, 116, 114, + 65535,65534,65533, // 5096 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5112 + + // #138 + 65535,65534,65533,65532,65531, + 80, 73, 76, 79, 84, 80, 79, 82, 84, 61, 117, 115, + 65535,65534,65533,65532,65531,65530,65529, // 5136 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5152 + + // #139 + 65535, + 75, 68, 69, 95, 83, 69, 83, 83, 73, 79, 78, 95, + 65535,65534,65533, // 5168 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5184 + + // #140 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5200 + 65535, + 84, 69, 65, 77, 66, 85, 73, 76, 68, 69, 82, 61, + 65535,65534,65533, // 5216 + + +}; +static struct StringCollection +{ + int len; + int offset1, offset2; + ushort align1, align2; +} stringCollection[] = { + {18, 1, 29, 3666, 106}, // #0 + {18, 53, 77, 106, 1978}, // #1 + {20, 97, 125, 2850, 3210}, // #2 + {20, 157, 185, 3210, 3138}, // #3 + {51, 225, 225, 3362, 3362}, // #4 + {51, 293, 353, 1434, 3362}, // #5 + {51, 417, 417, 3362, 3362}, // #6 + {19, 473, 501, 2850, 10}, // #7 + {19, 533, 565, 10, 442}, // #8 + {51, 609, 677, 3362, 1434}, // #9 + {5, 741, 753, 2666, 2066}, // #10 + {4, 765, 781, 2362, 3930}, // #11 + {3, 793, 805, 3330, 2138}, // #12 + {5, 817, 825, 738, 2066}, // #13 + {4, 833, 845, 434, 3930}, // #14 + {5, 857, 865, 3842, 2066}, // #15 + {4, 873, 885, 3538, 3930}, // #16 + {3, 897, 909, 3330, 2138}, // #17 + {5, 925, 937, 1898, 2066}, // #18 + {4, 949, 965, 1594, 3930}, // #19 + {3, 977, 989, 3330, 2138}, // #20 + {5, 1005, 1021, 2218, 762}, // #21 + {5, 1033, 1041, 3346, 2066}, // #22 + {4, 1053, 1069, 3082, 3930}, // #23 + {12, 1081, 1097, 2082, 962}, // #24 + {5, 1113, 1121, 3362, 2066}, // #25 + {4, 1129, 1141, 322, 3930}, // #26 + {3, 1153, 1165, 2050, 2138}, // #27 + {5, 1177, 1185, 1538, 2066}, // #28 + {4, 1193, 1205, 1234, 3930}, // #29 + {5, 1221, 1233, 554, 2066}, // #30 + {4, 1245, 1261, 250, 3930}, // #31 + {5, 1277, 1289, 2858, 2066}, // #32 + {4, 1301, 1317, 2554, 3930}, // #33 + {12, 1329, 1345, 2194, 1762}, // #34 + {12, 1365, 1385, 2170, 1762}, // #35 + {12, 1405, 1425, 2314, 1762}, // #36 + {6, 1445, 1461, 3626, 666}, // #37 + {11, 1477, 1501, 3882, 842}, // #38 + {12, 1525, 1545, 1722, 2930}, // #39 + {12, 1565, 1585, 1914, 2930}, // #40 + {12, 1605, 1625, 442, 2930}, // #41 + {12, 1641, 1657, 626, 2930}, // #42 + {12, 1673, 1689, 946, 2930}, // #43 + {12, 1705, 1721, 738, 2930}, // #44 + {12, 1737, 1753, 2066, 2930}, // #45 + {12, 1773, 1793, 1210, 2930}, // #46 + {12, 1809, 1825, 1426, 2930}, // #47 + {12, 1841, 1857, 1650, 2930}, // #48 + {12, 1877, 1897, 1530, 2930}, // #49 + {12, 1913, 1929, 1858, 2930}, // #50 + {12, 1949, 1969, 2106, 2930}, // #51 + {12, 1989, 2009, 2202, 2930}, // #52 + {12, 2029, 2049, 2490, 2930}, // #53 + {12, 2069, 2089, 2794, 2930}, // #54 + {12, 2105, 2121, 2322, 2930}, // #55 + {12, 2137, 2153, 2834, 2930}, // #56 + {12, 2169, 2185, 1266, 2930}, // #57 + {12, 2205, 2225, 2538, 2930}, // #58 + {12, 2241, 2257, 2706, 2930}, // #59 + {12, 2277, 2297, 3402, 2930}, // #60 + {12, 2313, 2329, 146, 2930}, // #61 + {12, 2349, 2369, 3690, 2930}, // #62 + {12, 2389, 2409, 810, 2930}, // #63 + {12, 2429, 2449, 1178, 2930}, // #64 + {12, 2465, 2481, 1442, 2930}, // #65 + {12, 2501, 2521, 3546, 2930}, // #66 + {12, 2541, 2561, 1930, 2930}, // #67 + {12, 2577, 2593, 1634, 2930}, // #68 + {12, 2609, 2625, 1986, 2930}, // #69 + {12, 2641, 2657, 1970, 2930}, // #70 + {12, 2677, 2697, 1834, 2930}, // #71 + {12, 2713, 2729, 1474, 2930}, // #72 + {12, 2749, 2769, 2250, 2930}, // #73 + {12, 2789, 2809, 2458, 2930}, // #74 + {12, 2829, 2849, 2618, 2930}, // #75 + {12, 2869, 2889, 3066, 2930}, // #76 + {12, 2905, 2921, 3330, 2930}, // #77 + {12, 2941, 2961, 1706, 2930}, // #78 + {12, 2977, 2993, 2802, 2930}, // #79 + {12, 3013, 3033, 3770, 2930}, // #80 + {12, 3053, 3073, 3594, 2930}, // #81 + {12, 3089, 3105, 2, 2930}, // #82 + {12, 3121, 3137, 2962, 2930}, // #83 + {12, 3153, 3169, 290, 2930}, // #84 + {12, 3189, 3209, 794, 2930}, // #85 + {12, 3225, 3241, 1058, 2930}, // #86 + {12, 3261, 3281, 2394, 2930}, // #87 + {12, 3301, 3321, 138, 2930}, // #88 + {12, 3341, 3361, 1482, 2930}, // #89 + {12, 3381, 3401, 570, 2930}, // #90 + {12, 3417, 3433, 674, 2930}, // #91 + {12, 3449, 3465, 1282, 2930}, // #92 + {12, 3481, 3497, 1746, 2930}, // #93 + {12, 3517, 3537, 1866, 2930}, // #94 + {12, 3557, 3577, 1978, 2930}, // #95 + {12, 3593, 3609, 3954, 2930}, // #96 + {12, 3629, 3649, 2570, 2930}, // #97 + {12, 3665, 3681, 2754, 2930}, // #98 + {12, 3697, 3713, 3666, 2930}, // #99 + {12, 3729, 3745, 34, 2930}, // #100 + {12, 3761, 3777, 2914, 2930}, // #101 + {12, 3797, 3817, 1194, 2930}, // #102 + {12, 3833, 3849, 3202, 2930}, // #103 + {12, 3869, 3889, 3018, 2930}, // #104 + {12, 3909, 3929, 202, 2930}, // #105 + {12, 3949, 3969, 3546, 2930}, // #106 + {12, 3985, 4001, 3682, 2930}, // #107 + {12, 4021, 4041, 3466, 2930}, // #108 + {12, 4061, 4081, 4074, 2930}, // #109 + {12, 4097, 4113, 306, 2930}, // #110 + {12, 4133, 4153, 634, 2930}, // #111 + {12, 4169, 4185, 802, 2930}, // #112 + {12, 4201, 4217, 962, 2930}, // #113 + {12, 4237, 4257, 1114, 2930}, // #114 + {12, 4273, 4289, 1250, 2930}, // #115 + {12, 4309, 4329, 3898, 2930}, // #116 + {12, 4349, 4369, 1386, 2930}, // #117 + {12, 4385, 4401, 1586, 2930}, // #118 + {12, 4417, 4433, 1730, 2930}, // #119 + {12, 4453, 4473, 1914, 2930}, // #120 + {12, 4493, 4513, 1498, 2930}, // #121 + {12, 4533, 4553, 2138, 2930}, // #122 + {12, 4569, 4585, 2290, 2930}, // #123 + {12, 4605, 4625, 2426, 2930}, // #124 + {12, 4645, 4665, 2666, 2930}, // #125 + {12, 4681, 4697, 2050, 2930}, // #126 + {12, 4717, 4737, 2874, 2930}, // #127 + {12, 4757, 4777, 3018, 2930}, // #128 + {12, 4797, 4817, 1834, 2930}, // #129 + {12, 4837, 4857, 3178, 2930}, // #130 + {12, 4873, 4889, 3314, 2930}, // #131 + {12, 4905, 4921, 2546, 2930}, // #132 + {12, 4941, 4961, 3546, 2930}, // #133 + {12, 4977, 4993, 3682, 2930}, // #134 + {12, 5013, 5033, 3802, 2930}, // #135 + {12, 5049, 5065, 3922, 2930}, // #136 + {12, 5081, 5097, 4018, 2930}, // #137 + {12, 5117, 5137, 42, 2930}, // #138 + {12, 5153, 5169, 130, 2930}, // #139 + {12, 5185, 5201, 242, 2930}, // #140 +}; +static const int stringCollectionCount = 141; +static const int stringCollectionMaxLen = 51; +// average comparison length: 12.0922 +// cache-line crosses: 6 (2.1%) +// alignment histogram: +// 0xXXX2 = 188 (66.7%) strings, 57 (30.3%) of which same-aligned +// 0xXXXa = 94 (33.3%) strings, 10 (10.6%) of which same-aligned +// total = 282 (100%) strings, 67 (23.8%) of which same-aligned diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index fb65f65..88dc40b 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -48,8 +48,15 @@ #define SRCDIR "" #endif +#ifdef Q_OS_UNIX +#include +#include +#endif + #include +#include "data.cpp" + class tst_QString: public QObject { Q_OBJECT @@ -73,20 +80,8 @@ void tst_QString::equals() const } } -static ushort databuffer[4096]; - tst_QString::tst_QString() { - // populate databuffer with our seed, each byte 3 times in a row - // include the NUL! - static const char seed[] = "AAAAAAAAAEhlbGxvIFdvcmxkIAAAAAA="; - static const int repeat = 3; - int pos = 0; - for (ushort *p = databuffer; p < databuffer + (sizeof(databuffer) / sizeof(databuffer[0])); p += repeat) { - for (int j = 0; j < repeat; ++p, ++j) - *p = seed[pos]; - pos = (pos + 1) % sizeof(seed); - } } void tst_QString::equals_data() const @@ -148,13 +143,15 @@ void tst_QString::equals_data() const << QString::fromRawData(ptr + 1, 58) << QString::fromRawData(ptr + 3, 58); } -static bool equals2_memcmp_call(ushort *p1, ushort *p2, int len) +static bool equals2_memcmp_call(const ushort *p1, const ushort *p2, int len) { return memcmp(p1, p2, len * 2) == 0; } -static bool equals2_bytewise(ushort *p1, ushort *p2, int len) +static bool equals2_bytewise(const ushort *p1, const ushort *p2, int len) { + if (p1 == p2 || !len) + return true; uchar *b1 = (uchar *)p1; uchar *b2 = (uchar *)p2; len *= 2; @@ -164,17 +161,24 @@ static bool equals2_bytewise(ushort *p1, ushort *p2, int len) return true; } -static bool equals2_shortwise(ushort *p1, ushort *p2, int len) +static bool __attribute__((optimize("unroll-loops"))) equals2_shortwise(const ushort *p1, const ushort *p2, int len) { - register ushort * const end = p1 + len; - for ( ; p1 != end; ++p1, ++p2) - if (*p1 != *p2) + if (p1 == p2 || !len) + return true; +// for (register int counter; counter < len; ++counter) +// if (p1[counter] != p2[counter]) +// return false; + while (len--) { + if (p1[len] != p2[len]) return false; + } return true; } -static bool equals2_intwise(ushort *p1, ushort *p2, int length) +static bool equals2_intwise(const ushort *p1, const ushort *p2, int length) { + if (p1 == p2 || !length) + return true; register union { const quint16 *w; const quint32 *d; @@ -219,71 +223,204 @@ static bool equals2_intwise(ushort *p1, ushort *p2, int length) return true; } +static inline bool equals2_short_tail(const ushort *p1, const ushort *p2, int len) +{ + if (len) { + if (*p1 != *p2) + return false; + if (--len) { + if (p1[1] != p2[1]) + return false; + if (--len) { + if (p1[2] != p2[2]) + return false; + if (--len) { + if (p1[3] != p2[3]) + return false; + if (--len) { + if (p1[4] != p2[4]) + return false; + if (--len) { + if (p1[5] != p2[5]) + return false; + if (--len) { + if (p1[6] != p2[6]) + return false; + return p1[7] == p2[7]; + } + } + } + } + } + } + } + return true; +} + +#pragma GCC optimize("no-unroll-loops") #ifdef __SSE2__ -static bool equals2_sse2(ushort *p1, ushort *p2, int len) +static bool equals2_sse2_aligned(const ushort *p1, const ushort *p2, int len) { - if (len > 8) { + if (len >= 8) { + qptrdiff counter = 0; while (len > 8) { - __m128i q1 = _mm_loadu_si128((__m128i *)p1); - __m128i q2 = _mm_loadu_si128((__m128i *)p2); + __m128i q1 = _mm_load_si128((__m128i *)(p1 + counter)); + __m128i q2 = _mm_load_si128((__m128i *)(p2 + counter)); + __m128i cmp = _mm_cmpeq_epi16(q1, q2); + if (ushort(_mm_movemask_epi8(cmp)) != ushort(0xffff)) + return false; + + len -= 8; + counter += 8; + } + p1 += counter; + p2 += counter; + } + + return equals2_short_tail(p1, p2, len); +} + +static bool __attribute__((optimize("no-unroll-loops"))) equals2_sse2(const ushort *p1, const ushort *p2, int len) +{ + if (p1 == p2 || !len) + return true; + + if (len >= 8) { + qptrdiff counter = 0; + while (len >= 8) { + __m128i q1 = _mm_loadu_si128((__m128i *)(p1 + counter)); + __m128i q2 = _mm_loadu_si128((__m128i *)(p2 + counter)); __m128i cmp = _mm_cmpeq_epi16(q1, q2); if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) return false; len -= 8; - p1 += 8; - p2 += 8; + counter += 8; } + p1 += counter; + p2 += counter; } - return equals2_shortwise(p1, p2, len); + return equals2_short_tail(p1, p2, len); } -static inline -#ifdef Q_CC_GNU -__attribute__((always_inline)) -#endif -bool prolog_align(ushort *&p1, ushort *&p2, int &len) +//static bool equals2_sse2(const ushort *p1, const ushort *p2, int len) +//{ +// register int val1 = quintptr(p1) & 0xf; +// register int val2 = quintptr(p2) & 0xf; +// if (false && val1 + val2 == 0) +// return equals2_sse2_aligned(p1, p2, len); +// else +// return equals2_sse2_unaligned(p1, p2, len); +//} + +static bool equals2_sse2_aligning(const ushort *p1, const ushort *p2, int len) { - const ushort *end = (ushort*) ((quintptr(p1) + 15) & ~15); - if (end > p1 + len) - end = p1 + len; - for ( ; p1 != end; ++p1, ++p2, --len) - if (*p1 != *p2) + if (len < 8) + return equals2_short_tail(p1, p2, len); + + qptrdiff counter = 0; + + // which one is easier to align, p1 or p2 ? + register int val1 = quintptr(p1) & 0xf; + register int val2 = quintptr(p2) & 0xf; + if (val1 && val2) { +#if 0 + // we'll align the one which requires the least number of steps + if (val1 > val2) { + qSwap(p1, p2); + val1 = val2; + } + + // val1 contains the number of bytes past the 16-aligned mark + // we must read 16-val1 bytes to align + val1 = 16 - val1; + if (val1 & 0x2) { + if (*p1 != *p2) + return false; + --len; + ++counter; + } + while (val1 & 12) { + if (*(uint*)p1 != *(uint*)p2) + return false; + --len; + counter += 2; + val1 -= 4; + } +#else + // we'll align the one closest to the 16-byte mark + if (val1 > val2) { + qSwap(p1, p2); + val1 = val2; + } + + // we're reading val1 bytes too many + __m128i q2 = _mm_loadu_si128((__m128i *)(p2 - val1/2)); + __m128i cmp = _mm_cmpeq_epi16(*(__m128i *)(p1 - val1/2), q2); + if (short(_mm_movemask_epi8(cmp)) >> val1 != short(-1)) return false; - return true; + + counter = 8 - val1/2; + len -= 8 - val1/2; +#endif + } else if (!val2) { + // p2 is already aligned + qSwap(p1, p2); + } + + // p1 is aligned + + while (len >= 8) { + __m128i q1 = _mm_load_si128((__m128i *)(p1 + counter)); + __m128i q2 = _mm_loadu_si128((__m128i *)(p2 + counter)); + __m128i cmp = _mm_cmpeq_epi16(q1, q2); + if (ushort(_mm_movemask_epi8(cmp)) != ushort(0xffff)) + return false; + + len -= 8; + counter += 8; + } + + // tail + return equals2_short_tail(p1 + counter, p2 + counter, len); } -static bool equals2_sse2_aligning(ushort *p1, ushort *p2, int len) +#ifdef __SSE3__ +static bool __attribute__((optimize("no-unroll-loops"))) equals2_sse3(const ushort *p1, const ushort *p2, int len) { - if (len > 8) { - if (!prolog_align(p1, p2, len)) - return false; - while (len > 8) { - __m128i q1 = _mm_load_si128((__m128i *)p1); - __m128i q2 = _mm_loadu_si128((__m128i *)p2); + if (p1 == p2 || !len) + return true; + + if (len >= 8) { + qptrdiff counter = 0; + while (len >= 8) { + __m128i q1 = _mm_lddqu_si128((__m128i *)(p1 + counter)); + __m128i q2 = _mm_lddqu_si128((__m128i *)(p2 + counter)); __m128i cmp = _mm_cmpeq_epi16(q1, q2); if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) return false; len -= 8; - p1 += 8; - p2 += 8; + counter += 8; } + p1 += counter; + p2 += counter; } - return equals2_shortwise(p1, p2, len); + return equals2_short_tail(p1, p2, len); } -template static inline bool equals2_ssse3_alignr(__m128i *m1, __m128i *m2, int len) +#ifdef __SSSE3__ +template static __attribute__((optimize("unroll-loops"))) inline bool equals2_ssse3_alignr(__m128i *m1, __m128i *m2, int len) { __m128i lower = _mm_load_si128(m1); - while (len > 8) { + while (len >= 8) { __m128i upper = _mm_load_si128(m1 + 1); __m128i correct; correct = _mm_alignr_epi8(upper, lower, N); - __m128i q2 = _mm_loadu_si128(m2); + __m128i q2 = _mm_lddqu_si128(m2); __m128i cmp = _mm_cmpeq_epi16(correct, q2); if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) return false; @@ -295,13 +432,13 @@ template static inline bool equals2_ssse3_alignr(__m128i *m1, __m128i *m2 } // tail - return len == 0 || equals2_shortwise((ushort *)m1 + N / 2, (ushort*)m2, len); + return len == 0 || equals2_short_tail((const ushort *)m1 + N / 2, (const ushort*)m2, len); } -static inline bool equals2_ssse3_aligned(__m128i *m1, __m128i *m2, int len) +static inline __attribute__((optimize("unroll-loops"))) bool equals2_ssse3_aligned(__m128i *m1, __m128i *m2, int len) { - while (len > 8) { - __m128i q2 = _mm_loadu_si128(m2); + while (len >= 8) { + __m128i q2 = _mm_lddqu_si128(m2); __m128i cmp = _mm_cmpeq_epi16(*m1, q2); if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) return false; @@ -310,30 +447,30 @@ static inline bool equals2_ssse3_aligned(__m128i *m1, __m128i *m2, int len) ++m1; ++m2; } - return len == 0 || equals2_shortwise((ushort *)m1, (ushort *)m2, len); + return len == 0 || equals2_short_tail((const ushort *)m1, (const ushort *)m2, len); } -//#ifdef __SSSE3__ -static bool equals2_ssse3(ushort *p1, ushort *p2, int len) +static bool equals2_ssse3(const ushort *p1, const ushort *p2, int len) { // p1 & 0xf can be: // 0, 2, 4, 6, 8, 10, 12, 14 // If it's 0, we're aligned // If it's not, then we're interested in the 16 - (p1 & 0xf) bytes only - if (len > 8) { + if (len >= 8) { // find the last aligned position below the p1 memory __m128i *m1 = (__m128i *)(quintptr(p1) & ~0xf); __m128i *m2 = (__m128i *)p2; - uchar diff = quintptr(p1) - quintptr(m1); + qptrdiff diff = quintptr(p1) - quintptr(m1); // diff contains the number of extra bytes + if (diff == 10) + return equals2_ssse3_alignr<10>(m1, m2, len); + else if (diff == 2) + return equals2_ssse3_alignr<2>(m1, m2, len); if (diff < 8) { if (diff < 4) { - if (diff == 0) - return equals2_ssse3_aligned(m1, m2, len); - else // diff == 2 - return equals2_ssse3_alignr<2>(m1, m2, len); + return equals2_ssse3_aligned(m1, m2, len); } else { if (diff == 4) return equals2_ssse3_alignr<4>(m1, m2, len); @@ -342,10 +479,7 @@ static bool equals2_ssse3(ushort *p1, ushort *p2, int len) } } else { if (diff < 12) { - if (diff == 8) - return equals2_ssse3_alignr<8>(m1, m2, len); - else // diff == 10 - return equals2_ssse3_alignr<10>(m1, m2, len); + return equals2_ssse3_alignr<8>(m1, m2, len); } else { if (diff == 12) return equals2_ssse3_alignr<12>(m1, m2, len); @@ -353,34 +487,99 @@ static bool equals2_ssse3(ushort *p1, ushort *p2, int len) return equals2_ssse3_alignr<14>(m1, m2, len); } } + } + + // tail + return equals2_short_tail(p1, p2, len); +} + +template static inline bool equals2_ssse3_aligning_alignr(__m128i *m1, __m128i *m2, int len) +{ + __m128i lower = _mm_load_si128(m1); + while (len >= 8) { + __m128i upper = _mm_load_si128(m1 + 1); + __m128i correct; + correct = _mm_alignr_epi8(upper, lower, N); + + __m128i cmp = _mm_cmpeq_epi16(correct, *m2); + if (ushort(_mm_movemask_epi8(cmp)) != 0xffff) + return false; -// switch (diff) { -// case 0: -// return equals2_ssse3_aligned(m1, m2, len); -// case 2: -// return equals2_ssse3_alignr<2>(m1, m2, len); -// case 4: -// return equals2_ssse3_alignr<4>(m1, m2, len); -// case 6: -// return equals2_ssse3_alignr<6>(m1, m2, len); -// case 8: -// return equals2_ssse3_alignr<8>(m1, m2, len); -// case 10: -// return equals2_ssse3_alignr<10>(m1, m2, len); -// case 12: -// return equals2_ssse3_alignr<12>(m1, m2, len); -// case 14: -// return equals2_ssse3_alignr<14>(m1, m2, len); -// } + len -= 8; + ++m2; + ++m1; + lower = upper; } // tail - return equals2_shortwise(p1, p2, len); + return len == 0 || equals2_short_tail((const ushort *)m1 + N / 2, (const ushort*)m2, len); } -//#endif -//#ifdef __SSE4_1__ -static bool equals2_sse4(ushort *p1, ushort *p2, int len) +static bool equals2_ssse3_aligning(const ushort *p1, const ushort *p2, int len) +{ + if (len < 8) + return equals2_short_tail(p1, p2, len); + qptrdiff counter = 0; + + // which one is easier to align, p1 or p2 ? + { + register int val1 = quintptr(p1) & 0xf; + register int val2 = quintptr(p2) & 0xf; + if (val1 && val2) { + // we'll align the one closest to the 16-byte mark + if (val1 < val2) { + qSwap(p1, p2); + val2 = val1; + } + + // we're reading val1 bytes too many + __m128i q1 = _mm_lddqu_si128((__m128i *)(p1 - val2/2)); + __m128i cmp = _mm_cmpeq_epi16(q1, *(__m128i *)(p2 - val2/2)); + if (short(_mm_movemask_epi8(cmp)) >> val1 != short(-1)) + return false; + + counter = 8 - val2/2; + len -= 8 - val2/2; + } else if (!val1) { + // p1 is already aligned + qSwap(p1, p2); + } + } + + // p2 is aligned now + // we want to use palignr in the mis-alignment of p1 + __m128i *m1 = (__m128i *)(quintptr(p1 + counter) & ~0xf); + __m128i *m2 = (__m128i *)(p2 + counter); + register int val1 = quintptr(p1 + counter) - quintptr(m1); + + // val1 contains the number of extra bytes + if (val1 == 8) + return equals2_ssse3_aligning_alignr<8>(m1, m2, len); + if (val1 == 0) + return equals2_sse2_aligned(p1 + counter, p2 + counter, len); + if (val1 < 8) { + if (val1 < 4) { + return equals2_ssse3_aligning_alignr<2>(m1, m2, len); + } else { + if (val1 == 4) + return equals2_ssse3_aligning_alignr<4>(m1, m2, len); + else // diff == 6 + return equals2_ssse3_aligning_alignr<6>(m1, m2, len); + } + } else { + if (val1 < 12) { + return equals2_ssse3_aligning_alignr<10>(m1, m2, len); + } else { + if (val1 == 12) + return equals2_ssse3_aligning_alignr<12>(m1, m2, len); + else // diff == 14 + return equals2_ssse3_aligning_alignr<14>(m1, m2, len); + } + } +} + +#ifdef __SSE4_1__ +static bool equals2_sse4(const ushort *p1, const ushort *p2, int len) { // We use the pcmpestrm instruction searching for differences (negative polarity) // it will reset CF if it's all equal @@ -390,36 +589,150 @@ static bool equals2_sse4(ushort *p1, ushort *p2, int len) // difference found: CF = 1 // all equal, not finished: CF = ZF = SF = 0 // all equal, finished: CF = 0, ZF = SF = 1 + // We use the JA instruction that jumps if ZF = 0 and CF = 0 + if (p1 == p2 || !len) + return true; + + // This function may read some bytes past the end of p1 or p2 + // It is safe to do that, as long as those extra bytes (beyond p1+len and p2+len) + // are on the same page as the last valid byte. + // If len is a multiple of 8, we'll never load invalid bytes. + if (len & 7) { + // The last load would load (len & ~7) valid bytes and (8 - (len & ~7)) invalid bytes. + // So we can't do the last load if any of those bytes is in a different + // page. That is, if: + // pX + len is on a different page from pX + (len & ~7) + 8 + // + // that is, if second-to-last load ended up less than 16 bytes from the page end: + // pX + (len & ~7) is the last ushort read in the second-to-last load + if (len < 8) + return equals2_short_tail(p1, p2, len); + if ((quintptr(p1 + (len & ~7)) & 0xfff) > 0xff0 || + (quintptr(p2 + (len & ~7)) & 0xfff) > 0xff0) { + + // yes, so we mustn't do the final 128-bit load + bool result; + asm ( + "sub %[p1], %[p2]\n\t" + "sub $16, %[p1]\n\t" + "add $8, %[len]\n\t" + + // main loop: + "0:\n\t" + "add $16, %[p1]\n\t" + "sub $8, %[len]\n\t" + "jz 1f\n\t" + "lddqu (%[p1]), %%xmm0\n\t" + "mov %[len], %%edx\n\t" + "pcmpestri %[mode], (%[p2],%[p1]), %%xmm0\n\t" + + "jna 1f\n\t" + "add $16, %[p1]\n\t" + "sub $8, %[len]\n\t" + "jz 1f\n\t" + "lddqu (%[p1]), %%xmm0\n\t" + "mov %[len], %%edx\n\t" + "pcmpestri %[mode], (%[p2],%[p1]), %%xmm0\n\t" + + "ja 0b\n\t" + "1:\n\t" + "setnc %[result]\n\t" + : [result] "=a" (result), + [p1] "+r" (p1), + [p2] "+r" (p2) + : [len] "0" (len & ~7), + [mode] "i" (_SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY) + : "%edx", "%ecx", "%xmm0" + ); + return result && equals2_short_tail(p1, (const ushort *)(quintptr(p1) + quintptr(p2)), len & 7); + } + } + +// const qptrdiff disp = p2 - p1; +// p1 -= 8; +// len += 8; +// while (true) { +// enum { Mode = _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY }; + +// p1 += 8; +// len -= 8; +// if (!len) +// return true; + +// __m128i q1 = _mm_lddqu_si128((__m128i *)(p1 + disp)); +// __m128i *m2 = (__m128i *)p1; + +// bool cmp_a = _mm_cmpestra(q1, len, *m2, len, Mode); +// if (cmp_a) +// continue; +// return !_mm_cmpestrc(q1, len, *m2, len, Mode); +// } +// return true; bool result; asm ( - "movd %%ebx, %%xmm1\n\t" "sub %[p1], %[p2]\n\t" - "mov %[p1], %%ebx\n\t" - "sub $16, %%ebx\n\t" + "sub $16, %[p1]\n\t" "add $8, %[len]\n\t" - "0:\n\t" - "add $16, %%ebx\n\t" + + "0:\n\t" + "add $16, %[p1]\n\t" "sub $8, %[len]\n\t" - "movdqu (%%ebx), %%xmm0\n\t" + "jz 1f\n\t" + "lddqu (%[p2],%[p1]), %%xmm0\n\t" "mov %[len], %%edx\n\t" - "pcmpestrm %[mode], (%[p2],%%ebx), %%xmm0\n\t" + "pcmpestri %[mode], (%[p1]), %%xmm0\n\t" + + "jna 1f\n\t" + "add $16, %[p1]\n\t" + "sub $8, %[len]\n\t" + "jz 1f\n\t" + "lddqu (%[p2],%[p1]), %%xmm0\n\t" + "mov %[len], %%edx\n\t" + "pcmpestri %[mode], (%[p1]), %%xmm0\n\t" + "ja 0b\n\t" - "1:\n\t" - "mov $0, %%eax\n\t" - "setnc %%al\n\t" - "movd %%xmm1, %%ebx\n\t" + + "1:\n\t" + "setnc %[result]\n\t" : [result] "=a" (result) : [len] "0" (len), - [p1] "d" (p1), + [p1] "r" (p1), [p2] "r" (p2), - [mode] "K" (_SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY) - : "%xmm0", "%xmm1" + [mode] "i" (_SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY) + : "%edx", "%ecx", "%xmm0" ); return result; } -//#endif + +#endif +#endif +#endif #endif +typedef bool (* FuncPtr)(const ushort *, const ushort *, int); +static const FuncPtr func[] = { + equals2_memcmp_call, // 0 + equals2_bytewise, // 1 + equals2_shortwise, // 1 + equals2_intwise, // 3 +#ifdef __SSE2__ + equals2_sse2, // 4 + equals2_sse2_aligning, // 5 +#ifdef __SSE3__ + equals2_sse3, // 6 +#ifdef __SSSE3__ + equals2_ssse3, // 7 + equals2_ssse3, // 8 +#ifdef __SSE4_1__ + equals2_sse4, // 9 +#endif +#endif +#endif +#endif + 0 +}; +static const int functionCount = sizeof(func)/sizeof(func[0]) - 1; + void tst_QString::equals2_data() const { QTest::addColumn("algorithm"); @@ -431,108 +744,94 @@ void tst_QString::equals2_data() const #ifdef __SSE2__ QTest::newRow("sse2") << 4; QTest::newRow("sse2_aligning") << 5; +#ifdef __SSE3__ + QTest::newRow("sse3") << 6; #ifdef __SSSE3__ - QTest::newRow("ssse3") << 6; + QTest::newRow("ssse3") << 7; + QTest::newRow("ssse3_aligning") << 8; #ifdef __SSE4_1__ - QTest::newRow("sse4.2") << 7; + QTest::newRow("sse4.2") << 9; +#endif #endif #endif #endif } -void tst_QString::equals2() const +static void __attribute__((noinline)) equals2_selftest() { - static const short positions[] = { - 190, 1719, 2149, 1752, - 158, 244, 365, 1117, - 254, 265, 1047, 1785, - 1435, 552, 1476, 2030, - // 16 - 421, 1840, 2209, 232, - 1389, 907, 1500, 1479, - 1152, 541, 655, 1960, - 1642, 299, 740, 1995, - // 32 - 1946, 1407, 1272, 1946, - 1459, 1851, 1717, 1484, - 1761, 1630, 1377, 1675, - 629, 341, 661, 244 - // 48 - }; - // the length list must not contain 0 - static const int lens[] = { - 11, // 0 - 40, - 28, - 38, - 9, - 52, // 5 - 48, - 38, - 29, - 7, - 2, // 10 - 49, - 41, - 5, - 20, - 62 // 15 +#ifdef Q_OS_UNIX + const long pagesize = sysconf(_SC_PAGESIZE); + void *page1, *page3; + ushort *page2; + page1 = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + page2 = (ushort *)mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0); + page3 = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + Q_ASSERT(quintptr(page2) == quintptr(page1) + pagesize || quintptr(page2) == quintptr(page1) - pagesize); + Q_ASSERT(quintptr(page3) == quintptr(page2) + pagesize || quintptr(page3) == quintptr(page2) - pagesize); + munmap(page1, pagesize); + munmap(page3, pagesize); + + // populate our page + for (uint i = 0; i < pagesize / sizeof(long long); ++i) + ((long long *)page2)[i] = Q_INT64_C(0x0041004100410041); + + // the following should crash: + //page2[-1] = 0xdead; + //page2[pagesize / sizeof(ushort) + 1] = 0xbeef; + + static const ushort needle[] = { + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41 }; - typedef bool (* FuncPtr)(ushort *, ushort *, int); - static const FuncPtr func[] = { - equals2_memcmp_call, // 0 - equals2_bytewise, // 1 - equals2_shortwise, // 1 - equals2_intwise, // 3 -#ifdef __SSE2__ - equals2_sse2, // 4 - equals2_sse2_aligning, // 5 -#ifdef __SSSE3__ - equals2_ssse3, // 6 -#ifdef __SSE4_1__ - equals2_sse4, // 7 -#endif -#endif + for (int algo = 0; algo < functionCount; ++algo) { + // boundary condition test: + for (int i = 0; i < 8; ++i) { + (func[algo])(page2 + i, needle, sizeof needle / 2); + (func[algo])(page2 - i - 1 - sizeof(needle)/2 + pagesize/2, needle, sizeof needle/2); + } + } + + munmap(page2, pagesize); #endif - 0 - }; + for (int algo = 0; algo < functionCount; ++algo) { + for (int i = 0; i < stringCollectionCount; ++i) { + const ushort *p1 = stringCollectionData + stringCollection[i].offset1; + const ushort *p2 = stringCollectionData + stringCollection[i].offset2; + bool expected = memcmp(p1, p2, stringCollection[i].len * 2) == 0; + + bool result = (func[algo])(p1, p2, stringCollection[i].len); + if (expected != result) + qWarning().nospace() + << "algo=" << algo + << " i=" << i + << " failed (" << result << "!=" << expected + << "); strings were " + << QByteArray((char*)p1, stringCollection[i].len).toHex() + << " and " + << QByteArray((char*)p2, stringCollection[i].len).toHex(); + } + } +} + +void tst_QString::equals2() const +{ QFETCH(int, algorithm); if (algorithm == -1) { - for (uint pos1 = 0; pos1 < sizeof positions / sizeof positions[0]; ++pos1) - for (uint pos2 = 0; pos2 < (sizeof positions / sizeof positions[0]) - 32; ++pos2) - for (uint len = 0; len < sizeof lens / sizeof lens[0]; ++len) { - ushort *p1 = databuffer + positions[pos1]; - ushort *p2 = databuffer + positions[pos2]; - bool expected = memcmp(p1, p2, lens[len] * 2) == 0; - - for (uint algo = 0; algo < -1 + (sizeof func / sizeof func[0]); ++algo) { - bool result = (func[algo])(p1, p2, lens[len]); - if (expected != result) - qWarning().nospace() - << "algo=" << algo - << " pos1=" << positions[pos1] - << " pos2=" << positions[pos2] - << " len=" << lens[len] - << " failed (" << result << "!=" << expected - << "); strings were " - << QByteArray((char*)p1, lens[len]).toHex() - << " and " - << QByteArray((char*)p2, lens[len]).toHex(); - } - - } + equals2_selftest(); return; } QBENCHMARK { - for (uint pos1 = 0; pos1 < sizeof positions / sizeof positions[0]; ++pos1) - for (uint pos2 = 0; pos2 < (sizeof positions / sizeof positions[0]) - 32; ++pos2) - for (uint len = 0; len < sizeof lens / sizeof lens[0]; ++len) { - bool result = (func[algorithm])(databuffer + positions[pos1], databuffer + positions[pos2], lens[len]); - Q_UNUSED(result); - } + for (int i = 0; i < stringCollectionCount; ++i) { + const ushort *p1 = stringCollectionData + stringCollection[i].offset1; + const ushort *p2 = stringCollectionData + stringCollection[i].offset2; + bool result = (func[algorithm])(p1, p2, stringCollection[i].len); + Q_UNUSED(result); + } } } -- cgit v0.12 From 8063e04f3bf6e46ec972b6cafea39454a47e16de Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 17 Aug 2010 20:34:54 +0200 Subject: Update comments in QString about alignment performance. This commit makes no code change at all. Write down the conclusions from experimenting with the x86 SIMD instructions for faster string comparison routines. Long story short: we're already pretty good. The SSE4.2 string instructions are probably more useful for "implicit-length mode" (that is, the end of the string is marked by a NUL), rather than QString's "explicit-length mode". --- src/corelib/tools/qstring.cpp | 63 ++++++++++++++++++++++++++++++++----------- src/corelib/tools/qstring.h | 1 + 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 2fd9a0b..d940bf8 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -173,7 +173,7 @@ static int ucstricmp(const ushort *a, const ushort *ae, const uchar *b) return 1; } -// Unicode case-insensitive comparison +// Unicode case-sensitive comparison static int ucstrcmp(const QChar *a, int alen, const QChar *b, int blen) { if (a == b && alen == blen) @@ -202,22 +202,55 @@ static int ucstrnicmp(const ushort *a, const ushort *b, int l) return ucstricmp(a, a + l, b, b + l); } +// Benchmarking indicates that doing memcmp is much slower than +// executing the comparison ourselves. +// +// The profiling was done on a population of calls to qMemEquals, generated +// during a run of the demo browser. The profile of the data (32-bit x86 +// Linux) was: +// +// total number of comparisons: 21353 +// longest string compared: 95 +// average comparison length: 14.8786 +// cache-line crosses: 5661 (13.3%) +// alignment histogram: +// 0xXXX0 = 512 (1.2%) strings, 0 (0.0%) of which same-aligned +// 0xXXX2 = 15087 (35.3%) strings, 5145 (34.1%) of which same-aligned +// 0xXXX4 = 525 (1.2%) strings, 0 (0.0%) of which same-aligned +// 0xXXX6 = 557 (1.3%) strings, 6 (1.1%) of which same-aligned +// 0xXXX8 = 509 (1.2%) strings, 0 (0.0%) of which same-aligned +// 0xXXXa = 24358 (57.0%) strings, 9901 (40.6%) of which same-aligned +// 0xXXXc = 557 (1.3%) strings, 0 (0.0%) of which same-aligned +// 0xXXXe = 601 (1.4%) strings, 15 (2.5%) of which same-aligned +// total = 42706 (100%) strings, 15067 (35.3%) of which same-aligned +// +// 92% of the strings have alignment of 2 or 10, which is due to malloc on +// 32-bit Linux returning values aligned to 8 bytes, and offsetof(array, QString::Data) == 18. +// +// The profile on 64-bit will be different since offsetof(array, QString::Data) == 26. +// +// The benchmark results were, for a Core-i7 @ 2.67 GHz 32-bit, compiled with -O3 -funroll-loops: +// 16-bit loads only: 872,301 CPU ticks [Qt 4.5 / memcmp] +// 32- and 16-bit loads: 773,362 CPU ticks [Qt 4.6] +// SSE2 "movdqu" 128-bit loads: 618,736 CPU ticks +// SSE3 "lddqu" 128-bit loads: 619,954 CPU ticks +// SSSE3 "palignr" corrections: 852,147 CPU ticks +// SSE4.2 "pcmpestrm": 738,702 CPU ticks +// +// The same benchmark on an Atom N450 @ 1.66 GHz, is: +// 16-bit loads only: 2,185,882 CPU ticks +// 32- and 16-bit loads: 1,805,060 CPU ticks +// SSE2 "movdqu" 128-bit loads: 2,529,843 CPU ticks +// SSE3 "lddqu" 128-bit loads: 2,514,858 CPU ticks +// SSSE3 "palignr" corrections: 2,160,325 CPU ticks +// SSE4.2 not available +// +// The conclusion we reach is that alignment the SSE2 unaligned code can gain +// 20% improvement in performance in some systems, but suffers a penalty due +// to the unaligned loads on others. + static bool qMemEquals(const quint16 *a, const quint16 *b, int length) { - // Benchmarking indicates that doing memcmp is much slower than - // executing the comparison ourselves. - // To make it even faster, we do a 32-bit comparison, comparing - // twice the amount of data as a normal word-by-word comparison. - // - // Benchmarking results on a 2.33 GHz Core2 Duo, with a 64-QChar - // block of data, with 4194304 iterations (per iteration): - // operation usec cpu ticks - // memcmp 330 710 - // 16-bit 79 167-171 - // 32-bit aligned 49 105-109 - // - // Testing also indicates that unaligned 32-bit loads are as - // performant as 32-bit aligned. if (a == b || !length) return true; diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index e52f59f..06e4d47 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -614,6 +614,7 @@ private: ushort asciiCache : 1; ushort capacity : 1; ushort reserved : 11; + // ### Qt5: try to ensure that "array" is aligned to 16 bytes on both 32- and 64-bit ushort array[1]; }; static Data shared_null; -- cgit v0.12 From d28522a1b0e2d1170751b2c559d13e6c96a2a54c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 17 Aug 2010 22:47:32 +0200 Subject: Update the data generation script to use a non-including .cpp This speeds up the writing of algorithms, so I don't have to recompile megabytes of data dumps. --- tests/benchmarks/corelib/tools/qstring/data.h | 12 ++++++++++++ .../corelib/tools/qstring/generatelist.pl | 21 +++++++++------------ tests/benchmarks/corelib/tools/qstring/main.cpp | 2 +- tests/benchmarks/corelib/tools/qstring/qstring.pro | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 tests/benchmarks/corelib/tools/qstring/data.h diff --git a/tests/benchmarks/corelib/tools/qstring/data.h b/tests/benchmarks/corelib/tools/qstring/data.h new file mode 100644 index 0000000..a23dae3 --- /dev/null +++ b/tests/benchmarks/corelib/tools/qstring/data.h @@ -0,0 +1,12 @@ +#include + +struct StringCollection +{ + int len; + int offset1, offset2; + ushort align1, align2; +}; + +extern const ushort stringCollectionData[]; +extern StringCollection stringCollection[]; +extern const int stringCollectionCount; diff --git a/tests/benchmarks/corelib/tools/qstring/generatelist.pl b/tests/benchmarks/corelib/tools/qstring/generatelist.pl index ed4c8eb..2777329 100644 --- a/tests/benchmarks/corelib/tools/qstring/generatelist.pl +++ b/tests/benchmarks/corelib/tools/qstring/generatelist.pl @@ -51,10 +51,10 @@ # the second data # \n newline # -# The C code to write this data would be: +# The code to write this data would be: # fprintf(out, "LEN = %d %s %d %d\n", len, -# (p1 == p2) : "SAME" : "DIFF", -# (int)p1 & 0xfff, (int)p2 & 0xfff); +# (p1 == p2) ? "SAME" : "DIFF", +# uint(quintptr(p1)) & 0xfff, uint(quintptr(p2)) & 0xfff); # fwrite(p1, 2, len, out); # fwrite(p2, 2, len, out); # fwrite("\n", 1, 1, out); @@ -103,7 +103,9 @@ sub printUshortArray($$$) { return ($offset + $headpadding, $offset + $headpadding + $len + $tailpadding); } -print "static const ushort stringCollectionData[] __attribute__((aligned(16))) = { \n"; +print "#include \"data.h\"\n\n"; + +print "const ushort stringCollectionData[] __attribute__((aligned(64))) = { \n"; $count = 0; $offset = 0; $totalsize = 0; @@ -160,12 +162,7 @@ while (1) { print "\n};\n"; close IN; -print "static struct StringCollection\n"; -print "{\n"; -print " int len;\n"; -print " int offset1, offset2;\n"; -print " ushort align1, align2;\n"; -print "} stringCollection[] = {\n"; +print "struct StringCollection stringCollection[] = {\n"; for $i (0..$count-1) { print " {", @@ -181,8 +178,8 @@ for $i (0..$count-1) { } print "};\n"; -print "static const int stringCollectionCount = $count;\n"; -print "static const int stringCollectionMaxLen = $maxlen;\n"; +print "const int stringCollectionCount = $count;\n"; +print "const int stringCollectionMaxLen = $maxlen;\n"; printf "// average comparison length: %.4f\n", ($totalsize * 1.0 / $count); printf "// cache-line crosses: %d (%.1f%%)\n", $cachelinecrosses, ($cachelinecrosses * 100.0 / $count / 2); diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 88dc40b..a23244d 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -55,7 +55,7 @@ #include -#include "data.cpp" +#include "data.h" class tst_QString: public QObject { diff --git a/tests/benchmarks/corelib/tools/qstring/qstring.pro b/tests/benchmarks/corelib/tools/qstring/qstring.pro index 44fb46b..e8720e1 100644 --- a/tests/benchmarks/corelib/tools/qstring/qstring.pro +++ b/tests/benchmarks/corelib/tools/qstring/qstring.pro @@ -1,7 +1,7 @@ load(qttest_p4) TARGET = tst_bench_qstring QT -= gui -SOURCES += main.cpp +SOURCES += main.cpp data.cpp wince*:{ DEFINES += SRCDIR=\\\"\\\" -- cgit v0.12 From 47b98097ac26de6153c7a4894aaa110133e478dc Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 17 Aug 2010 23:52:07 +0200 Subject: Add the ucstrncmp benchmarks --- tests/benchmarks/corelib/tools/qstring/main.cpp | 136 ++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index a23244d..3d88756 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -67,6 +67,8 @@ private slots: void equals_data() const; void equals2_data() const; void equals2() const; + void ucstrncmp_data() const; + void ucstrncmp() const; void fromUtf8() const; }; @@ -835,6 +837,140 @@ void tst_QString::equals2() const } } +static int ucstrncmp_shortwise(const ushort *a, const ushort *b, int l) +{ + while (l-- && *a == *b) + a++,b++; + if (l==-1) + return 0; + return *a - *b; +} + +static int ucstrncmp_intwise(const ushort *a, const ushort *b, int len) +{ + // do both strings have the same alignment? + if ((quintptr(a) & 2) == (quintptr(b) & 2)) { + // are we aligned to 4 bytes? + if (quintptr(a) & 2) { + if (*a != *b) + return *a - *b; + ++a; + ++b; + --len; + } + + const uint *p1 = (const uint *)a; + const uint *p2 = (const uint *)b; + quintptr counter = 0; + for ( ; len > 1 ; len -= 2, ++counter) { + if (p1[counter] != p2[counter]) { + // which ushort isn't equal? + int diff = a[2*counter] - b[2*counter]; + return diff ? diff : a[2*counter + 1] - b[2*counter + 1]; + } + } + + return len ? a[2*counter] - b[2*counter] : 0; + } else { + while (len-- && *a == *b) + a++,b++; + if (len==-1) + return 0; + return *a - *b; + } +} + +typedef int (* UcstrncmpFunction)(const ushort *, const ushort *, int); +Q_DECLARE_METATYPE(UcstrncmpFunction) + +void tst_QString::ucstrncmp_data() const +{ + QTest::addColumn("function"); + QTest::newRow("selftest") << UcstrncmpFunction(0); + QTest::newRow("shortwise") << &ucstrncmp_shortwise; + QTest::newRow("intwise") << &ucstrncmp_intwise; +} + +void tst_QString::ucstrncmp() const +{ + QFETCH(UcstrncmpFunction, function); + if (!function) { + static const UcstrncmpFunction func[] = { + &ucstrncmp_shortwise, + &ucstrncmp_intwise + }; + static const int functionCount = sizeof func / sizeof func[0]; + +#ifdef Q_OS_UNIX + const long pagesize = sysconf(_SC_PAGESIZE); + void *page1, *page3; + ushort *page2; + page1 = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + page2 = (ushort *)mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0); + page3 = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + Q_ASSERT(quintptr(page2) == quintptr(page1) + pagesize || quintptr(page2) == quintptr(page1) - pagesize); + Q_ASSERT(quintptr(page3) == quintptr(page2) + pagesize || quintptr(page3) == quintptr(page2) - pagesize); + munmap(page1, pagesize); + munmap(page3, pagesize); + + // populate our page + for (uint i = 0; i < pagesize / sizeof(long long); ++i) + ((long long *)page2)[i] = Q_INT64_C(0x0041004100410041); + + // the following should crash: + //page2[-1] = 0xdead; + //page2[pagesize / sizeof(ushort) + 1] = 0xbeef; + + static const ushort needle[] = { + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41 + }; + + for (int algo = 0; algo < functionCount; ++algo) { + // boundary condition test: + for (int i = 0; i < 8; ++i) { + (func[algo])(page2 + i, needle, sizeof needle / 2); + (func[algo])(page2 - i - 1 - sizeof(needle)/2 + pagesize/2, needle, sizeof needle/2); + } + } + + munmap(page2, pagesize); +#endif + + for (int algo = 0; algo < functionCount; ++algo) { + for (int i = 0; i < stringCollectionCount; ++i) { + const ushort *p1 = stringCollectionData + stringCollection[i].offset1; + const ushort *p2 = stringCollectionData + stringCollection[i].offset2; + int expected = ucstrncmp_shortwise(p1, p2, stringCollection[i].len); + expected = qBound(-1, expected, 1); + + int result = (func[algo])(p1, p2, stringCollection[i].len); + result = qBound(-1, result, 1); + if (expected != result) + qWarning().nospace() + << "algo=" << algo + << " i=" << i + << " failed (" << result << "!=" << expected + << "); strings were " + << QByteArray((char*)p1, stringCollection[i].len).toHex() + << " and " + << QByteArray((char*)p2, stringCollection[i].len).toHex(); + } + } + return; + } + + QBENCHMARK { + for (int i = 0; i < stringCollectionCount; ++i) { + const ushort *p1 = stringCollectionData + stringCollection[i].offset1; + const ushort *p2 = stringCollectionData + stringCollection[i].offset2; + (function)(p1, p2, stringCollection[i].len); + } + } +} + void tst_QString::fromUtf8() const { QFile file(SRCDIR "utf-8.txt"); -- cgit v0.12 From 2bd9d7fbec0bb61298ba0f48a93a4a186b558a38 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 10:52:27 +0200 Subject: Add an SSE2-optimised version of ucstrncmp First results make it 34% faster than current ucstrncmp, 16% faster than the 32-bit version. --- tests/benchmarks/corelib/tools/qstring/main.cpp | 39 ++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 3d88756..f338e49 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -880,6 +880,41 @@ static int ucstrncmp_intwise(const ushort *a, const ushort *b, int len) } } +#ifdef __SSE2__ +static inline int bsf_nonzero(register long val) +{ + int result; +# ifdef Q_CC_GNU + // returns the first non-zero bit on a non-zero reg + asm ("bsf %1, %0" : "=r" (result) : "r" (val)); + return result; +# elif defined(Q_CC_MSVC) + _BitScanForward(&result, val); + return result; +# endif +} + +static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2(const ushort *a, const ushort *b, int len) +{ + qptrdiff counter = 0; + while (len >= 8) { + __m128i m1 = _mm_loadu_si128((__m128i *)(a + counter)); + __m128i m2 = _mm_loadu_si128((__m128i *)(b + counter)); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + counter += bsf_nonzero(mask)/2; + return a[counter] - b[counter]; + } + + counter += 8; + len -= 8; + } + return ucstrncmp_shortwise(a + counter, b + counter, len); +} +#endif + typedef int (* UcstrncmpFunction)(const ushort *, const ushort *, int); Q_DECLARE_METATYPE(UcstrncmpFunction) @@ -889,6 +924,7 @@ void tst_QString::ucstrncmp_data() const QTest::newRow("selftest") << UcstrncmpFunction(0); QTest::newRow("shortwise") << &ucstrncmp_shortwise; QTest::newRow("intwise") << &ucstrncmp_intwise; + QTest::newRow("sse2") << &ucstrncmp_sse2; } void tst_QString::ucstrncmp() const @@ -897,7 +933,8 @@ void tst_QString::ucstrncmp() const if (!function) { static const UcstrncmpFunction func[] = { &ucstrncmp_shortwise, - &ucstrncmp_intwise + &ucstrncmp_intwise, + &ucstrncmp_sse2 }; static const int functionCount = sizeof func / sizeof func[0]; -- cgit v0.12 From 6f913f94c6b6aaf8514bc62d4f9939ac44e211fb Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 11:07:51 +0200 Subject: Add a version of ucstrncmp with SSE2 with aligning. This is a different technique of aligning. Instead of reading some bytes before the string, we will read some bytes of the string twice. Best results are only 2% improvement over the unaligned SSE2 on a Core-i7. --- tests/benchmarks/corelib/tools/qstring/main.cpp | 44 ++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index f338e49..608e2bc 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -913,6 +913,46 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2(const ush } return ucstrncmp_shortwise(a + counter, b + counter, len); } + +static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligning(const ushort *a, const ushort *b, int len) +{ + if (len >= 8) { + __m128i m1 = _mm_loadu_si128((__m128i *)a); + __m128i m2 = _mm_loadu_si128((__m128i *)b); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + int counter = bsf_nonzero(mask)/2; + return a[counter] - b[counter]; + } + + + // now align to do 16-byte loads + int diff = 8 - (quintptr(a) & 0xf)/2; + len -= diff; + a += diff; + b += diff; + } + + qptrdiff counter = 0; + while (len >= 8) { + __m128i m1 = _mm_load_si128((__m128i *)(a + counter)); + __m128i m2 = _mm_loadu_si128((__m128i *)(b + counter)); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + counter += bsf_nonzero(mask)/2; + return a[counter] - b[counter]; + } + + counter += 8; + len -= 8; + } + return ucstrncmp_shortwise(a + counter, b + counter, len); +} + #endif typedef int (* UcstrncmpFunction)(const ushort *, const ushort *, int); @@ -925,6 +965,7 @@ void tst_QString::ucstrncmp_data() const QTest::newRow("shortwise") << &ucstrncmp_shortwise; QTest::newRow("intwise") << &ucstrncmp_intwise; QTest::newRow("sse2") << &ucstrncmp_sse2; + QTest::newRow("sse2_aligning") << &ucstrncmp_sse2_aligning; } void tst_QString::ucstrncmp() const @@ -934,7 +975,8 @@ void tst_QString::ucstrncmp() const static const UcstrncmpFunction func[] = { &ucstrncmp_shortwise, &ucstrncmp_intwise, - &ucstrncmp_sse2 + &ucstrncmp_sse2, + &ucstrncmp_sse2_aligning }; static const int functionCount = sizeof func / sizeof func[0]; -- cgit v0.12 From 6cea948a0ceabe5493bb2a75f236b008b47fd71e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 12:26:36 +0200 Subject: Optimise the tail comparison of ucstrncmp --- tests/benchmarks/corelib/tools/qstring/main.cpp | 38 +++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 608e2bc..0b7254f 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -881,6 +881,40 @@ static int ucstrncmp_intwise(const ushort *a, const ushort *b, int len) } #ifdef __SSE2__ +static inline int ucstrncmp_short_tail(const ushort *p1, const ushort *p2, int len) +{ + if (len) { + if (*p1 != *p2) + return *p1 - *p2; + if (--len) { + if (p1[1] != p2[1]) + return p1[1] - p2[1]; + if (--len) { + if (p1[2] != p2[2]) + return p1[2] - p2[2]; + if (--len) { + if (p1[3] != p2[3]) + return p1[3] - p2[3]; + if (--len) { + if (p1[4] != p2[4]) + return p1[4] - p2[4]; + if (--len) { + if (p1[5] != p2[5]) + return p1[5] - p2[5]; + if (--len) { + if (p1[6] != p2[6]) + return p1[6] - p2[6]; + return p1[7] - p2[7]; + } + } + } + } + } + } + } + return 0; +} + static inline int bsf_nonzero(register long val) { int result; @@ -911,7 +945,7 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2(const ush counter += 8; len -= 8; } - return ucstrncmp_shortwise(a + counter, b + counter, len); + return ucstrncmp_short_tail(a + counter, b + counter, len); } static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligning(const ushort *a, const ushort *b, int len) @@ -950,7 +984,7 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligning( counter += 8; len -= 8; } - return ucstrncmp_shortwise(a + counter, b + counter, len); + return ucstrncmp_short_tail(a + counter, b + counter, len); } #endif -- cgit v0.12 From 08fa99c43897b16f8be924090316f5a4db548c10 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 12:26:53 +0200 Subject: Add an SSSE3 version of ucstrncmp --- tests/benchmarks/corelib/tools/qstring/main.cpp | 80 ++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 0b7254f..3c3d1ad 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -987,6 +987,82 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligning( return ucstrncmp_short_tail(a + counter, b + counter, len); } +static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligned(const ushort *a, const ushort *b, int len) +{ + qptrdiff counter = 0; + while (len >= 8) { + __m128i m1 = _mm_load_si128((__m128i *)(a + counter)); + __m128i m2 = _mm_load_si128((__m128i *)(b + counter)); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + counter += bsf_nonzero(mask)/2; + return a[counter] - b[counter]; + } + + counter += 8; + len -= 8; + } + return ucstrncmp_short_tail(a + counter, b + counter, len); +} + +template static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_ssse3_alignr(const ushort *a, const ushort *b, int len) +{ + qptrdiff counter = 0; + __m128i lower, upper; + upper = _mm_load_si128((__m128i *)a); + + do { + lower = upper; + upper = _mm_load_si128((__m128i *)(a + counter) + 1); + __m128i merged = _mm_alignr_epi8(upper, lower, N); + + __m128i m2 = _mm_lddqu_si128((__m128i *)(b + counter)); + __m128i cmp = _mm_cmpeq_epi16(merged, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + counter += bsf_nonzero(mask)/2; + return a[counter + N/2] - b[counter]; + } + + counter += 8; + len -= 8; + } while (len >= 8); + + return ucstrncmp_short_tail(a + counter + N/2, b + counter, len); +} + +static int ucstrncmp_ssse3(const ushort *a, const ushort *b, int len) +{ + if (len >= 8) { + int val = quintptr(a) & 0xf; + a -= val/2; + + if (val == 10) + return ucstrncmp_ssse3_alignr<10>(a, b, len); + else if (val == 2) + return ucstrncmp_ssse3_alignr<2>(a, b, len); + if (val < 8) { + if (val < 4) + return ucstrncmp_sse2_aligned(a, b, len); + else if (val == 4) + return ucstrncmp_ssse3_alignr<4>(a, b, len); + else + return ucstrncmp_ssse3_alignr<6>(a, b, len); + } else { + if (val < 12) + return ucstrncmp_ssse3_alignr<8>(a, b, len); + else if (val == 12) + return ucstrncmp_ssse3_alignr<12>(a, b, len); + else + return ucstrncmp_ssse3_alignr<14>(a, b, len); + } + } + return ucstrncmp_short_tail(a, b, len); +} + #endif typedef int (* UcstrncmpFunction)(const ushort *, const ushort *, int); @@ -1000,6 +1076,7 @@ void tst_QString::ucstrncmp_data() const QTest::newRow("intwise") << &ucstrncmp_intwise; QTest::newRow("sse2") << &ucstrncmp_sse2; QTest::newRow("sse2_aligning") << &ucstrncmp_sse2_aligning; + QTest::newRow("ssse3") << &ucstrncmp_ssse3; } void tst_QString::ucstrncmp() const @@ -1010,7 +1087,8 @@ void tst_QString::ucstrncmp() const &ucstrncmp_shortwise, &ucstrncmp_intwise, &ucstrncmp_sse2, - &ucstrncmp_sse2_aligning + &ucstrncmp_sse2_aligning, + &ucstrncmp_ssse3 }; static const int functionCount = sizeof func / sizeof func[0]; -- cgit v0.12 From 4f891889118d4bcc417382a0a646f3683c621b10 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 12:36:07 +0200 Subject: Add an ucstrncmp that uses SSSE3 with aligning. The results on i7 are 32% improvement over current code, 13% improvement over 4-byte loads, 6% over the unaligned SSSE3 loads. However, it's about 2.5% slower than pure SSE2 code due to complexity. The results on Atom are 30% improvement over current code, 7% over 4-byte loads, 15% over pure unaligned SSE2 and 9% over unaligned SSSE3. --- tests/benchmarks/corelib/tools/qstring/main.cpp | 53 ++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 3c3d1ad..d40b9bc 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -1063,6 +1063,55 @@ static int ucstrncmp_ssse3(const ushort *a, const ushort *b, int len) return ucstrncmp_short_tail(a, b, len); } +static int ucstrncmp_ssse3_aligning(const ushort *a, const ushort *b, int len) +{ + if (len >= 8) { + __m128i m1 = _mm_loadu_si128((__m128i *)a); + __m128i m2 = _mm_loadu_si128((__m128i *)b); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + int counter = bsf_nonzero(mask)/2; + return a[counter] - b[counter]; + } + + + // now 'b' align to do 16-byte loads + int diff = 8 - (quintptr(b) & 0xf)/2; + len -= diff; + a += diff; + b += diff; + } + + if (len < 8) + return ucstrncmp_short_tail(a, b, len); + + // 'b' is aligned + int val = quintptr(a) & 0xf; + a -= val/2; + + if (val == 8) + return ucstrncmp_ssse3_alignr<8>(a, b, len); + else if (val == 0) + return ucstrncmp_sse2_aligned(a, b, len); + if (val < 8) { + if (val < 4) + return ucstrncmp_ssse3_alignr<2>(a, b, len); + else if (val == 4) + return ucstrncmp_ssse3_alignr<4>(a, b, len); + else + return ucstrncmp_ssse3_alignr<6>(a, b, len); + } else { + if (val < 12) + return ucstrncmp_ssse3_alignr<10>(a, b, len); + else if (val == 12) + return ucstrncmp_ssse3_alignr<12>(a, b, len); + else + return ucstrncmp_ssse3_alignr<14>(a, b, len); + } +} + #endif typedef int (* UcstrncmpFunction)(const ushort *, const ushort *, int); @@ -1077,6 +1126,7 @@ void tst_QString::ucstrncmp_data() const QTest::newRow("sse2") << &ucstrncmp_sse2; QTest::newRow("sse2_aligning") << &ucstrncmp_sse2_aligning; QTest::newRow("ssse3") << &ucstrncmp_ssse3; + QTest::newRow("ssse3_aligning") << &ucstrncmp_ssse3_aligning; } void tst_QString::ucstrncmp() const @@ -1088,7 +1138,8 @@ void tst_QString::ucstrncmp() const &ucstrncmp_intwise, &ucstrncmp_sse2, &ucstrncmp_sse2_aligning, - &ucstrncmp_ssse3 + &ucstrncmp_ssse3, + &ucstrncmp_ssse3_aligning }; static const int functionCount = sizeof func / sizeof func[0]; -- cgit v0.12 From 5e8d8f82d38ce5a1b30d5d90ecb6bc096d52f4d8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 12:47:31 +0200 Subject: Update the SSSE3-with-alignment function to use aligned loads. This results in no change on the Core-i7, but another 2.6% on the Atom (so it's now 8% better than 4-byte loads and 31% better than current code) --- tests/benchmarks/corelib/tools/qstring/main.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index d40b9bc..e11d92e 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -1007,7 +1007,9 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligned(c return ucstrncmp_short_tail(a + counter, b + counter, len); } -template static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_ssse3_alignr(const ushort *a, const ushort *b, int len) +typedef __m128i (* MMLoadFunction)(const __m128i *); +template +static inline __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_ssse3_alignr(const ushort *a, const ushort *b, int len) { qptrdiff counter = 0; __m128i lower, upper; @@ -1018,7 +1020,7 @@ template static __attribute__((optimize("no-unroll-loops"))) int ucstrncm upper = _mm_load_si128((__m128i *)(a + counter) + 1); __m128i merged = _mm_alignr_epi8(upper, lower, N); - __m128i m2 = _mm_lddqu_si128((__m128i *)(b + counter)); + __m128i m2 = LoadFunction((__m128i *)(b + counter)); __m128i cmp = _mm_cmpeq_epi16(merged, m2); ushort mask = ~uint(_mm_movemask_epi8(cmp)); if (mask) { @@ -1092,23 +1094,23 @@ static int ucstrncmp_ssse3_aligning(const ushort *a, const ushort *b, int len) a -= val/2; if (val == 8) - return ucstrncmp_ssse3_alignr<8>(a, b, len); + return ucstrncmp_ssse3_alignr<8, _mm_load_si128>(a, b, len); else if (val == 0) return ucstrncmp_sse2_aligned(a, b, len); if (val < 8) { if (val < 4) - return ucstrncmp_ssse3_alignr<2>(a, b, len); + return ucstrncmp_ssse3_alignr<2, _mm_load_si128>(a, b, len); else if (val == 4) - return ucstrncmp_ssse3_alignr<4>(a, b, len); + return ucstrncmp_ssse3_alignr<4, _mm_load_si128>(a, b, len); else - return ucstrncmp_ssse3_alignr<6>(a, b, len); + return ucstrncmp_ssse3_alignr<6, _mm_load_si128>(a, b, len); } else { if (val < 12) - return ucstrncmp_ssse3_alignr<10>(a, b, len); + return ucstrncmp_ssse3_alignr<10, _mm_load_si128>(a, b, len); else if (val == 12) - return ucstrncmp_ssse3_alignr<12>(a, b, len); + return ucstrncmp_ssse3_alignr<12, _mm_load_si128>(a, b, len); else - return ucstrncmp_ssse3_alignr<14>(a, b, len); + return ucstrncmp_ssse3_alignr<14, _mm_load_si128>(a, b, len); } } -- cgit v0.12 From 637d94620c86172714ab11a258f6c5d4d02c272b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Aug 2010 13:59:58 +0200 Subject: Small fixup --- tests/benchmarks/corelib/tools/qstring/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index e11d92e..6f9c333 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -987,7 +987,7 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligning( return ucstrncmp_short_tail(a + counter, b + counter, len); } -static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligned(const ushort *a, const ushort *b, int len) +static inline __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligned(const ushort *a, const ushort *b, int len) { qptrdiff counter = 0; while (len >= 8) { -- cgit v0.12 From 410d836b5b4f029e9bc08a0ed3304df2e4a944a5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 19 Aug 2010 11:59:34 +0200 Subject: Add the beginnings of a new SSSE3-based aligning algorithm --- tests/benchmarks/corelib/tools/qstring/main.cpp | 84 ++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 6f9c333..4c4d921 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -1114,6 +1114,86 @@ static int ucstrncmp_ssse3_aligning(const ushort *a, const ushort *b, int len) } } +static inline __attribute__((optimize("no-unroll-loops"))) +int ucstrncmp_ssse3_aligning2_aligned(const ushort *a, const ushort *b, int len) +{ + // len >= 8 + // align(b) == align(a) + + // round down the alignment so align(b) == align(a) == 0 + // calculate how many garbage bytes we'll read on the first load + // corresponds to how many bits we must ignore from movemask's result + int garbage = (quintptr(b) & 0xf); + a -= garbage / 2; + b -= garbage / 2; + + __m128i m1 = _mm_load_si128((const __m128i *)a); + __m128i m2 = _mm_load_si128((const __m128i *)b); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + int mask = short(_mm_movemask_epi8(cmp)); // force sign extension + mask >>= garbage; + if (~mask) { + // which ushort isn't equal? + int counter = (garbage + bsf_nonzero(~mask))/2; + return a[counter] - b[counter]; + } + + // the first 16-garbage bytes (8-garbage/2 ushorts) were equal + len -= 8 - garbage/2; + return ucstrncmp_sse2_aligned(a + 8, b + 8, len); +} + +template static inline __attribute__((optimize("no-unroll-loops"))) +int ucstrncmp_ssse3_aligning2_alignr(const ushort *a, const ushort *b, int len) +{ + return ucstrncmp_intwise(a, b, len); +} + +static int ucstrncmp_ssse3_aligning2(const ushort *a, const ushort *b, int len) +{ + // Different strategy from above: instead of doing two unaligned loads + // when trying to align, we'll only do aligned loads and round down the + // addresses of a and b. This means the first load will contain garbage + // in the beginning of the string, which we'll shift out of the way + // (after _mm_movemask_epi8) + + if (len < 16) + return ucstrncmp_intwise(a, b, len); + + // both a and b are misaligned + // we'll call the alignr function with the alignment *difference* between the two + int val = (quintptr(b) & 0xf) - (quintptr(a) & 0xf); + int sign = 1; + if (val < 0) { + val = -val; + qSwap(a, b); + sign = -1; + } + + // from this point on, b has the shortest alignment + // and align(a) = align(b) + val + + if (val == 8) + return ucstrncmp_ssse3_aligning2_alignr<8>(a, b, len) * sign; + else if (val == 0) + return ucstrncmp_ssse3_aligning2_aligned(a, b, len) * sign; + if (val < 8) { + if (val < 4) + return ucstrncmp_ssse3_aligning2_alignr<2>(a, b, len) * sign; + else if (val == 4) + return ucstrncmp_ssse3_aligning2_alignr<4>(a, b, len) * sign; + else + return ucstrncmp_ssse3_aligning2_alignr<6>(a, b, len) * sign; + } else { + if (val < 12) + return ucstrncmp_ssse3_aligning2_alignr<10>(a, b, len) * sign; + else if (val == 12) + return ucstrncmp_ssse3_aligning2_alignr<12>(a, b, len) * sign; + else + return ucstrncmp_ssse3_aligning2_alignr<14>(a, b, len) * sign; + } +} + #endif typedef int (* UcstrncmpFunction)(const ushort *, const ushort *, int); @@ -1129,6 +1209,7 @@ void tst_QString::ucstrncmp_data() const QTest::newRow("sse2_aligning") << &ucstrncmp_sse2_aligning; QTest::newRow("ssse3") << &ucstrncmp_ssse3; QTest::newRow("ssse3_aligning") << &ucstrncmp_ssse3_aligning; + QTest::newRow("ssse3_aligning2") << &ucstrncmp_ssse3_aligning2; } void tst_QString::ucstrncmp() const @@ -1141,7 +1222,8 @@ void tst_QString::ucstrncmp() const &ucstrncmp_sse2, &ucstrncmp_sse2_aligning, &ucstrncmp_ssse3, - &ucstrncmp_ssse3_aligning + &ucstrncmp_ssse3_aligning, + &ucstrncmp_ssse3_aligning2 }; static const int functionCount = sizeof func / sizeof func[0]; -- cgit v0.12 From 3cfd8330d5252d6ca9439882cb4a0f2246cccfc0 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 19 Aug 2010 15:09:49 +0200 Subject: Improve on the SSSE3 with alternate aligning function. Even though this function *only* does aligned loads, it's worse than the other function --- tests/benchmarks/corelib/tools/qstring/main.cpp | 139 +++++++++++++++++------- 1 file changed, 101 insertions(+), 38 deletions(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 4c4d921..d2861b6 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -259,7 +259,7 @@ static inline bool equals2_short_tail(const ushort *p1, const ushort *p2, int le return true; } -#pragma GCC optimize("no-unroll-loops") +#pragma GCC optimize("unroll-loops off") #ifdef __SSE2__ static bool equals2_sse2_aligned(const ushort *p1, const ushort *p2, int len) { @@ -989,7 +989,7 @@ static __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligning( static inline __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_aligned(const ushort *a, const ushort *b, int len) { - qptrdiff counter = 0; + quintptr counter = 0; while (len >= 8) { __m128i m1 = _mm_load_si128((__m128i *)(a + counter)); __m128i m2 = _mm_load_si128((__m128i *)(b + counter)); @@ -1007,8 +1007,29 @@ static inline __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_sse2_al return ucstrncmp_short_tail(a + counter, b + counter, len); } +static inline __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_ssse3_alignr_aligned(const ushort *a, const ushort *b, int len) +{ + quintptr counter = 0; + while (len >= 8) { + __m128i m1 = _mm_load_si128((__m128i *)(a + counter)); + __m128i m2 = _mm_lddqu_si128((__m128i *)(b + counter)); + __m128i cmp = _mm_cmpeq_epi16(m1, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + counter += bsf_nonzero(mask)/2; + return a[counter] - b[counter]; + } + + counter += 8; + len -= 8; + } + return ucstrncmp_short_tail(a + counter, b + counter, len); +} + + typedef __m128i (* MMLoadFunction)(const __m128i *); -template +template static inline __attribute__((optimize("no-unroll-loops"))) int ucstrncmp_ssse3_alignr(const ushort *a, const ushort *b, int len) { qptrdiff counter = 0; @@ -1043,23 +1064,23 @@ static int ucstrncmp_ssse3(const ushort *a, const ushort *b, int len) a -= val/2; if (val == 10) - return ucstrncmp_ssse3_alignr<10>(a, b, len); + return ucstrncmp_ssse3_alignr<10, _mm_lddqu_si128>(a, b, len); else if (val == 2) - return ucstrncmp_ssse3_alignr<2>(a, b, len); + return ucstrncmp_ssse3_alignr<2, _mm_lddqu_si128>(a, b, len); if (val < 8) { if (val < 4) - return ucstrncmp_sse2_aligned(a, b, len); + return ucstrncmp_ssse3_alignr_aligned(a, b, len); else if (val == 4) - return ucstrncmp_ssse3_alignr<4>(a, b, len); + return ucstrncmp_ssse3_alignr<4, _mm_lddqu_si128>(a, b, len); else - return ucstrncmp_ssse3_alignr<6>(a, b, len); + return ucstrncmp_ssse3_alignr<6, _mm_lddqu_si128>(a, b, len); } else { if (val < 12) - return ucstrncmp_ssse3_alignr<8>(a, b, len); + return ucstrncmp_ssse3_alignr<8, _mm_lddqu_si128>(a, b, len); else if (val == 12) - return ucstrncmp_ssse3_alignr<12>(a, b, len); + return ucstrncmp_ssse3_alignr<12, _mm_lddqu_si128>(a, b, len); else - return ucstrncmp_ssse3_alignr<14>(a, b, len); + return ucstrncmp_ssse3_alignr<14, _mm_lddqu_si128>(a, b, len); } } return ucstrncmp_short_tail(a, b, len); @@ -1115,18 +1136,9 @@ static int ucstrncmp_ssse3_aligning(const ushort *a, const ushort *b, int len) } static inline __attribute__((optimize("no-unroll-loops"))) -int ucstrncmp_ssse3_aligning2_aligned(const ushort *a, const ushort *b, int len) +int ucstrncmp_ssse3_aligning2_aligned(const ushort *a, const ushort *b, int len, int garbage) { // len >= 8 - // align(b) == align(a) - - // round down the alignment so align(b) == align(a) == 0 - // calculate how many garbage bytes we'll read on the first load - // corresponds to how many bits we must ignore from movemask's result - int garbage = (quintptr(b) & 0xf); - a -= garbage / 2; - b -= garbage / 2; - __m128i m1 = _mm_load_si128((const __m128i *)a); __m128i m2 = _mm_load_si128((const __m128i *)b); __m128i cmp = _mm_cmpeq_epi16(m1, m2); @@ -1134,8 +1146,8 @@ int ucstrncmp_ssse3_aligning2_aligned(const ushort *a, const ushort *b, int len) mask >>= garbage; if (~mask) { // which ushort isn't equal? - int counter = (garbage + bsf_nonzero(~mask))/2; - return a[counter] - b[counter]; + uint counter = (garbage + bsf_nonzero(~mask)); + return a[counter/2] - b[counter/2]; } // the first 16-garbage bytes (8-garbage/2 ushorts) were equal @@ -1144,9 +1156,53 @@ int ucstrncmp_ssse3_aligning2_aligned(const ushort *a, const ushort *b, int len) } template static inline __attribute__((optimize("no-unroll-loops"))) -int ucstrncmp_ssse3_aligning2_alignr(const ushort *a, const ushort *b, int len) +int ucstrncmp_ssse3_aligning2_alignr(const ushort *a, const ushort *b, int len, int garbage) +{ + // len >= 8 + __m128i lower, upper, merged; + lower = _mm_load_si128((const __m128i*)a); + upper = _mm_load_si128((const __m128i*)(a + 8)); + merged = _mm_alignr_epi8(upper, lower, N); + + __m128i m2 = _mm_load_si128((const __m128i*)b); + __m128i cmp = _mm_cmpeq_epi16(merged, m2); + int mask = short(_mm_movemask_epi8(cmp)); // force sign extension + mask >>= garbage; + if (~mask) { + // which ushort isn't equal? + uint counter = (garbage + bsf_nonzero(~mask)); + return a[counter/2 + N/2] - b[counter/2]; + } + + // the first 16-garbage bytes (8-garbage/2 ushorts) were equal + quintptr counter = 8; + len -= 8 - garbage/2; + while (len >= 8) { + lower = upper; + upper = _mm_load_si128((__m128i *)(a + counter) + 1); + merged = _mm_alignr_epi8(upper, lower, N); + + m2 = _mm_load_si128((__m128i *)(b + counter)); + cmp = _mm_cmpeq_epi16(merged, m2); + ushort mask = ~uint(_mm_movemask_epi8(cmp)); + if (mask) { + // which ushort isn't equal? + counter += bsf_nonzero(mask)/2; + return a[counter + N/2] - b[counter]; + } + + counter += 8; + len -= 8; + } + + return ucstrncmp_short_tail(a + counter + N/2, b + counter, len); +} + +static inline int conditional_invert(int result, bool invert) { - return ucstrncmp_intwise(a, b, len); + if (invert) + return -result; + return result; } static int ucstrncmp_ssse3_aligning2(const ushort *a, const ushort *b, int len) @@ -1157,40 +1213,47 @@ static int ucstrncmp_ssse3_aligning2(const ushort *a, const ushort *b, int len) // in the beginning of the string, which we'll shift out of the way // (after _mm_movemask_epi8) - if (len < 16) + if (len < 8) return ucstrncmp_intwise(a, b, len); // both a and b are misaligned // we'll call the alignr function with the alignment *difference* between the two - int val = (quintptr(b) & 0xf) - (quintptr(a) & 0xf); - int sign = 1; + int val = (quintptr(a) & 0xf) - (quintptr(b) & 0xf); + bool invert = false; if (val < 0) { val = -val; - qSwap(a, b); - sign = -1; + //qSwap(a, b); + asm ("xchg %0, %1" : "+r" (a), "+r" (b)); + invert = true; } // from this point on, b has the shortest alignment // and align(a) = align(b) + val + // round down the alignment so align(b) == align(a) == 0 + int garbage = (quintptr(b) & 0xf); + a = (const ushort*)(quintptr(a) & ~0xf); + b = (const ushort*)(quintptr(b) & ~0xf); + // now the first load of b will load 'garbage' extra bytes + // and the first load of a will load 'garbage + val' extra bytes if (val == 8) - return ucstrncmp_ssse3_aligning2_alignr<8>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<8>(a, b, len, garbage), invert); else if (val == 0) - return ucstrncmp_ssse3_aligning2_aligned(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_aligned(a, b, len, garbage), invert); if (val < 8) { if (val < 4) - return ucstrncmp_ssse3_aligning2_alignr<2>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<2>(a, b, len, garbage), invert); else if (val == 4) - return ucstrncmp_ssse3_aligning2_alignr<4>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<4>(a, b, len, garbage), invert); else - return ucstrncmp_ssse3_aligning2_alignr<6>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<6>(a, b, len, garbage), invert); } else { if (val < 12) - return ucstrncmp_ssse3_aligning2_alignr<10>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<10>(a, b, len, garbage), invert); else if (val == 12) - return ucstrncmp_ssse3_aligning2_alignr<12>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<12>(a, b, len, garbage), invert); else - return ucstrncmp_ssse3_aligning2_alignr<14>(a, b, len) * sign; + return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<14>(a, b, len, garbage), invert); } } -- cgit v0.12 From 469210fdf9287dc8a4933e0761bbff91a031045f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 24 Aug 2010 12:14:41 +0200 Subject: Don't try to compile the SSE2 and SSSE3 code with compilers that don't support them (e.g. ARM) --- tests/benchmarks/corelib/tools/qstring/main.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index d2861b6..3b86792 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -163,7 +163,7 @@ static bool equals2_bytewise(const ushort *p1, const ushort *p2, int len) return true; } -static bool __attribute__((optimize("unroll-loops"))) equals2_shortwise(const ushort *p1, const ushort *p2, int len) +static bool equals2_shortwise(const ushort *p1, const ushort *p2, int len) { if (p1 == p2 || !len) return true; @@ -259,7 +259,7 @@ static inline bool equals2_short_tail(const ushort *p1, const ushort *p2, int le return true; } -#pragma GCC optimize("unroll-loops off") +//#pragma GCC optimize("no-unroll-loops") #ifdef __SSE2__ static bool equals2_sse2_aligned(const ushort *p1, const ushort *p2, int len) { @@ -1155,7 +1155,7 @@ int ucstrncmp_ssse3_aligning2_aligned(const ushort *a, const ushort *b, int len, return ucstrncmp_sse2_aligned(a + 8, b + 8, len); } -template static inline __attribute__((optimize("no-unroll-loops"))) +template static inline __attribute__((optimize("no-unroll-loops"),always_inline)) int ucstrncmp_ssse3_aligning2_alignr(const ushort *a, const ushort *b, int len, int garbage) { // len >= 8 @@ -1268,11 +1268,15 @@ void tst_QString::ucstrncmp_data() const QTest::newRow("selftest") << UcstrncmpFunction(0); QTest::newRow("shortwise") << &ucstrncmp_shortwise; QTest::newRow("intwise") << &ucstrncmp_intwise; +#ifdef __SSE2__ QTest::newRow("sse2") << &ucstrncmp_sse2; QTest::newRow("sse2_aligning") << &ucstrncmp_sse2_aligning; +#ifdef __SSSE3__ QTest::newRow("ssse3") << &ucstrncmp_ssse3; QTest::newRow("ssse3_aligning") << &ucstrncmp_ssse3_aligning; QTest::newRow("ssse3_aligning2") << &ucstrncmp_ssse3_aligning2; +#endif +#endif } void tst_QString::ucstrncmp() const @@ -1282,11 +1286,15 @@ void tst_QString::ucstrncmp() const static const UcstrncmpFunction func[] = { &ucstrncmp_shortwise, &ucstrncmp_intwise, +#ifdef __SSE2__ &ucstrncmp_sse2, &ucstrncmp_sse2_aligning, +#ifdef __SSSE3__ &ucstrncmp_ssse3, &ucstrncmp_ssse3_aligning, &ucstrncmp_ssse3_aligning2 +#endif +#endif }; static const int functionCount = sizeof func / sizeof func[0]; -- cgit v0.12 From 9a468f59472e8978aa18b75e9786718a8823bbec Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 24 Aug 2010 12:15:36 +0200 Subject: Unroll the SSSE3 code even more to avoid the need to keep an extra variable for inverting the result On 32-bit, we're out of registers already, so this variable ended up in memory --- tests/benchmarks/corelib/tools/qstring/main.cpp | 88 +++++++++++++++---------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/tests/benchmarks/corelib/tools/qstring/main.cpp b/tests/benchmarks/corelib/tools/qstring/main.cpp index 3b86792..9616052 100644 --- a/tests/benchmarks/corelib/tools/qstring/main.cpp +++ b/tests/benchmarks/corelib/tools/qstring/main.cpp @@ -1218,42 +1218,60 @@ static int ucstrncmp_ssse3_aligning2(const ushort *a, const ushort *b, int len) // both a and b are misaligned // we'll call the alignr function with the alignment *difference* between the two - int val = (quintptr(a) & 0xf) - (quintptr(b) & 0xf); - bool invert = false; - if (val < 0) { - val = -val; - //qSwap(a, b); - asm ("xchg %0, %1" : "+r" (a), "+r" (b)); - invert = true; - } - - // from this point on, b has the shortest alignment - // and align(a) = align(b) + val - // round down the alignment so align(b) == align(a) == 0 - int garbage = (quintptr(b) & 0xf); - a = (const ushort*)(quintptr(a) & ~0xf); - b = (const ushort*)(quintptr(b) & ~0xf); - - // now the first load of b will load 'garbage' extra bytes - // and the first load of a will load 'garbage + val' extra bytes - if (val == 8) - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<8>(a, b, len, garbage), invert); - else if (val == 0) - return conditional_invert(ucstrncmp_ssse3_aligning2_aligned(a, b, len, garbage), invert); - if (val < 8) { - if (val < 4) - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<2>(a, b, len, garbage), invert); - else if (val == 4) - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<4>(a, b, len, garbage), invert); - else - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<6>(a, b, len, garbage), invert); + int offset = (quintptr(a) & 0xf) - (quintptr(b) & 0xf); + if (offset >= 0) { + // from this point on, b has the shortest alignment + // and align(a) = align(b) + offset + // round down the alignment so align(b) == align(a) == 0 + int garbage = (quintptr(b) & 0xf); + a = (const ushort*)(quintptr(a) & ~0xf); + b = (const ushort*)(quintptr(b) & ~0xf); + + // now the first load of b will load 'garbage' extra bytes + // and the first load of a will load 'garbage + offset' extra bytes + if (offset == 8) + return ucstrncmp_ssse3_aligning2_alignr<8>(a, b, len, garbage); + if (offset == 0) + return ucstrncmp_ssse3_aligning2_aligned(a, b, len, garbage); + if (offset < 8) { + if (offset < 4) + return ucstrncmp_ssse3_aligning2_alignr<2>(a, b, len, garbage); + else if (offset == 4) + return ucstrncmp_ssse3_aligning2_alignr<4>(a, b, len, garbage); + else + return ucstrncmp_ssse3_aligning2_alignr<6>(a, b, len, garbage); + } else { + if (offset < 12) + return ucstrncmp_ssse3_aligning2_alignr<10>(a, b, len, garbage); + else if (offset == 12) + return ucstrncmp_ssse3_aligning2_alignr<12>(a, b, len, garbage); + else + return ucstrncmp_ssse3_aligning2_alignr<14>(a, b, len, garbage); + } } else { - if (val < 12) - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<10>(a, b, len, garbage), invert); - else if (val == 12) - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<12>(a, b, len, garbage), invert); - else - return conditional_invert(ucstrncmp_ssse3_aligning2_alignr<14>(a, b, len, garbage), invert); + // same as above but inverted + int garbage = (quintptr(a) & 0xf); + a = (const ushort*)(quintptr(a) & ~0xf); + b = (const ushort*)(quintptr(b) & ~0xf); + + offset = -offset; + if (offset == 8) + return -ucstrncmp_ssse3_aligning2_alignr<8>(b, a, len, garbage); + if (offset < 8) { + if (offset < 4) + return -ucstrncmp_ssse3_aligning2_alignr<2>(b, a, len, garbage); + else if (offset == 4) + return -ucstrncmp_ssse3_aligning2_alignr<4>(b, a, len, garbage); + else + return -ucstrncmp_ssse3_aligning2_alignr<6>(b, a, len, garbage); + } else { + if (offset < 12) + return -ucstrncmp_ssse3_aligning2_alignr<10>(b, a, len, garbage); + else if (offset == 12) + return -ucstrncmp_ssse3_aligning2_alignr<12>(b, a, len, garbage); + else + return -ucstrncmp_ssse3_aligning2_alignr<14>(b, a, len, garbage); + } } } -- cgit v0.12 From 189a18eddfa7504a604060172a394bda60aeba10 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 24 Aug 2010 13:07:09 +0200 Subject: Fixed delivering gestures to a toplevel widget. If there is only one widget which is a toplevel, then gestures were not delivered to it because we assumed (wrong) that there is at least one child widget. Reviewed-by: Frederik Gladhorn --- src/gui/kernel/qgesturemanager.cpp | 5 +++-- tests/auto/gestures/tst_gestures.cpp | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index e768a21..cb4061e 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -595,8 +595,9 @@ void QGestureManager::deliverEvents(const QSet &gestures, if (gesture->hasHotSpot()) { // guess the target widget using the hotspot of the gesture QPoint pt = gesture->hotSpot().toPoint(); - if (QWidget *w = qApp->topLevelAt(pt)) { - target = w->childAt(w->mapFromGlobal(pt)); + if (QWidget *topLevel = qApp->topLevelAt(pt)) { + QWidget *child = topLevel->childAt(topLevel->mapFromGlobal(pt)); + target = child ? child : topLevel; } } else { // or use the context of the gesture diff --git a/tests/auto/gestures/tst_gestures.cpp b/tests/auto/gestures/tst_gestures.cpp index a968520..ddc3939 100644 --- a/tests/auto/gestures/tst_gestures.cpp +++ b/tests/auto/gestures/tst_gestures.cpp @@ -395,7 +395,12 @@ void tst_Gestures::customGesture() { GestureWidget widget; widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + widget.show(); + QTest::qWaitForWindowShown(&widget); + CustomEvent event; + event.hotSpot = widget.mapToGlobal(QPoint(5,5)); + event.hasHotSpot = true; sendCustomGesture(&event, &widget); static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; -- cgit v0.12 From db1a70b33ce03197111556344542e931e9345839 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 20 Aug 2010 23:18:50 +0200 Subject: Split the CPU-detection code into multiple functions for readability Reviewed-By: Benjamin Poulain --- src/corelib/tools/qsimd.cpp | 86 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index 8005d7d..d6e6c03 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -52,13 +52,9 @@ QT_BEGIN_NAMESPACE -uint qDetectCPUFeatures() -{ - static uint features = 0xffffffff; - if (features != 0xffffffff) - return features; - #if defined (Q_OS_WINCE) +static inline uint detectProcessorFeatures() +{ #if defined (ARM) if (IsProcessorFeaturePresent(PF_ARM_INTEL_WMMX)) { features = IWMMXT; @@ -78,7 +74,14 @@ uint qDetectCPUFeatures() #endif features = 0; return features; -#elif defined(QT_HAVE_IWMMXT) +} + +#elif defined(__arm__) || defined(__arm) || defined(QT_HAVE_IWMMXT) || defined(QT_HAVE_NEON) +static inline uint detectProcessorFeatures() +{ + uint features = 0; + +#if defined(QT_HAVE_IWMMXT) // runtime detection only available when running as a previlegied process static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt(); features = doIWMMXT ? IWMMXT : 0; @@ -87,13 +90,14 @@ uint qDetectCPUFeatures() static const bool doNEON = !qgetenv("QT_NO_NEON").toInt(); features = doNEON ? NEON : 0; return features; -#else - features = 0; -#if defined(__x86_64__) || defined(Q_OS_WIN64) - features = MMX|SSE|SSE2|CMOV; -#elif defined(__ia64__) - features = MMX|SSE|SSE2; +#endif +} + #elif defined(__i386__) || defined(_M_IX86) +static inline uint detectProcessorFeatures() +{ + uint features = 0; + unsigned int extended_result = 0; unsigned int feature_result = 0; uint result = 0; @@ -149,6 +153,7 @@ uint qDetectCPUFeatures() : : "%eax", "%ecx", "%edx" ); + #elif defined (Q_OS_WIN) _asm { push eax @@ -210,6 +215,7 @@ uint qDetectCPUFeatures() } #endif + // result now contains the standard feature bits if (result & (1u << 15)) features |= CMOV; @@ -236,9 +242,36 @@ uint qDetectCPUFeatures() if (feature_result & (1u << 28)) features |= AVX; -#endif // i386 +#if defined(QT_HAVE_MMX) + if (qgetenv("QT_NO_MMX").toInt()) + features ^= MMX; +#endif + if (qgetenv("QT_NO_MMXEXT").toInt()) + features ^= MMXEXT; + +#if defined(QT_HAVE_3DNOW) + if (qgetenv("QT_NO_3DNOW").toInt()) + features ^= MMX3DNOW; +#endif + if (qgetenv("QT_NO_3DNOWEXT").toInt()) + features ^= MMX3DNOWEXT; + +#if defined(QT_HAVE_SSE) + if (qgetenv("QT_NO_SSE").toInt()) + features ^= SSE; +#endif +#if defined(QT_HAVE_SSE2) + if (qgetenv("QT_NO_SSE2").toInt()) + features ^= SSE2; +#endif -#if defined(__x86_64__) || defined(Q_OS_WIN64) + return features; +} + +#elif defined(__x86_64) || defined(Q_OS_WIN64) +static inline uint detectProcessorFeatures() +{ + uint features = MMX|SSE|SSE2|CMOV; uint feature_result = 0; #if defined(Q_CC_GNU) @@ -282,7 +315,6 @@ uint qDetectCPUFeatures() features |= SSE4_2; if (feature_result & (1u << 28)) features |= AVX; -#endif // x86_64 #if defined(QT_HAVE_MMX) if (qgetenv("QT_NO_MMX").toInt()) @@ -306,9 +338,29 @@ uint qDetectCPUFeatures() if (qgetenv("QT_NO_SSE2").toInt()) features ^= SSE2; #endif +} - return features; +#elif defined(__ia64__) +static inline uint detectProcessorFeatures() +{ + return MMX|SSE|SSE2; +} + +#else +static inline uint detectProcessorFeatures() +{ + return 0; +} #endif + +uint qDetectCPUFeatures() +{ + static QBasicAtomicInt features = Q_BASIC_ATOMIC_INITIALIZER(-1); + if (features != -1) + return features; + + features = detectProcessorFeatures(); + return features; } QT_END_NAMESPACE -- cgit v0.12 From 5070c3ae331faf18f6997535356853cc61ef0ad7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 24 Aug 2010 13:00:33 +0200 Subject: Detect CPU features on ARM by reading the ELF auxvec. Reviewed-by: Benjamin Poulain --- src/corelib/tools/qsimd.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index d6e6c03..a5ec3c5 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -50,6 +50,13 @@ #include #endif +#if defined(Q_OS_LINUX) && defined(__arm__) +#include "private/qcore_unix_p.h" + +#include +#include +#endif + QT_BEGIN_NAMESPACE #if defined (Q_OS_WINCE) @@ -81,16 +88,50 @@ static inline uint detectProcessorFeatures() { uint features = 0; +#if defined(Q_OS_LINUX) + int auxv = ::qt_safe_open("/proc/self/auxv", O_RDONLY); + if (auxv != -1) { + unsigned long vector[64]; + int nread; + while (features == 0) { + nread = ::qt_safe_read(auxv, (char *)vector, sizeof vector); + if (nread <= 0) { + // EOF or error + break; + } + + int max = nread / (sizeof vector[0]); + for (int i = 0; i < max; i += 2) + if (vector[i] == AT_HWCAP) { + if (vector[i+1] & HWCAP_IWMMXT) + features |= IWMMXT; + if (vector[i+1] & HWCAP_NEON) + features |= NEON; + break; + } + } + + if (qgetenv("QT_NO_IWMMXT").toInt()) + features ^= IWMMXT; + if (qgetenv("QT_NO_NEON").toInt()) + features ^= NEON; + + ::qt_safe_close(auxv); + return features; + } + // fall back if /proc/self/auxv wasn't found +#endif + #if defined(QT_HAVE_IWMMXT) // runtime detection only available when running as a previlegied process static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt(); features = doIWMMXT ? IWMMXT : 0; - return features; #elif defined(QT_HAVE_NEON) static const bool doNEON = !qgetenv("QT_NO_NEON").toInt(); features = doNEON ? NEON : 0; - return features; #endif + + return features; } #elif defined(__i386__) || defined(_M_IX86) -- cgit v0.12 From d9b567288f301e44abedebf5b9b553c2878f2c77 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 20 Aug 2010 23:19:54 +0200 Subject: Report the detected CPU features in the corelib boilerplate --- src/corelib/global/qlibraryinfo.cpp | 3 +++ src/corelib/tools/qsimd.cpp | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 7ebee3d..b4f5d0f 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -525,6 +525,9 @@ void qt_core_boilerplate() qt_configure_libraries_path_str + 12, qt_configure_headers_path_str + 12); + extern void qDumpCPUFeatures(); + qDumpCPUFeatures(); + #ifdef QT_EVAL extern void qt_core_eval_init(uint); qt_core_eval_init(1); diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index a5ec3c5..2472449 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -41,6 +41,7 @@ #include "qsimd_p.h" #include +#include #if defined(Q_OS_WINCE) #include @@ -404,4 +405,56 @@ uint qDetectCPUFeatures() return features; } +void qDumpCPUFeatures() +{ + /* + * Use kdesdk/scripts/generate_string_table.pl to update the table below. + * Here's the data: +mmx +mmxext +mmx3dnow +mmx3dnowext +sse +sse2 +cmov +iwmmxt +neon +sse3 +ssse3 +sse4.1 +sse4.2 +avx + */ + static const char features_string[] = + "mmx\0" + "mmxext\0" + "mmx3dnow\0" + "mmx3dnowext\0" + "sse\0" + "sse2\0" + "cmov\0" + "iwmmxt\0" + "neon\0" + "sse3\0" + "ssse3\0" + "sse4.1\0" + "sse4.2\0" + "avx\0" + "\0"; + + static const int features_indices[] = { + 0, 4, 11, 20, 32, 36, 41, 46, + 53, 58, 63, 69, 76, 83, -1 + }; + const int features_count = (sizeof features_indices - 1) / (sizeof features_indices[0]); + + uint features = qDetectCPUFeatures(); + printf("Processor features: "); + for (int i = 0; i < features_count; ++i) { + if (features & (1 << i)) + printf(" %s", features_string + features_indices[i]); + } + puts(""); +} + QT_END_NAMESPACE -- cgit v0.12 From ebf9d5dd5174b7c82fec83b56d71a59d5277bd51 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 24 Aug 2010 13:52:09 +0200 Subject: Properly implement the CPU feature disabling in qsimd.cpp. Don't use ^=, since that might enable a feature that wasn't detected. The new use is: export QT_NO_CPU_FEATURE=" []" Reviewed-by: Benjamin Poulain --- src/corelib/tools/qsimd.cpp | 157 +++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 97 deletions(-) diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index 2472449..c34644c 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -112,11 +112,6 @@ static inline uint detectProcessorFeatures() } } - if (qgetenv("QT_NO_IWMMXT").toInt()) - features ^= IWMMXT; - if (qgetenv("QT_NO_NEON").toInt()) - features ^= NEON; - ::qt_safe_close(auxv); return features; } @@ -125,11 +120,9 @@ static inline uint detectProcessorFeatures() #if defined(QT_HAVE_IWMMXT) // runtime detection only available when running as a previlegied process - static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt(); - features = doIWMMXT ? IWMMXT : 0; + features = IWMMXT; #elif defined(QT_HAVE_NEON) - static const bool doNEON = !qgetenv("QT_NO_NEON").toInt(); - features = doNEON ? NEON : 0; + features = NEON; #endif return features; @@ -284,29 +277,6 @@ static inline uint detectProcessorFeatures() if (feature_result & (1u << 28)) features |= AVX; -#if defined(QT_HAVE_MMX) - if (qgetenv("QT_NO_MMX").toInt()) - features ^= MMX; -#endif - if (qgetenv("QT_NO_MMXEXT").toInt()) - features ^= MMXEXT; - -#if defined(QT_HAVE_3DNOW) - if (qgetenv("QT_NO_3DNOW").toInt()) - features ^= MMX3DNOW; -#endif - if (qgetenv("QT_NO_3DNOWEXT").toInt()) - features ^= MMX3DNOWEXT; - -#if defined(QT_HAVE_SSE) - if (qgetenv("QT_NO_SSE").toInt()) - features ^= SSE; -#endif -#if defined(QT_HAVE_SSE2) - if (qgetenv("QT_NO_SSE2").toInt()) - features ^= SSE2; -#endif - return features; } @@ -358,28 +328,7 @@ static inline uint detectProcessorFeatures() if (feature_result & (1u << 28)) features |= AVX; -#if defined(QT_HAVE_MMX) - if (qgetenv("QT_NO_MMX").toInt()) - features ^= MMX; -#endif - if (qgetenv("QT_NO_MMXEXT").toInt()) - features ^= MMXEXT; - -#if defined(QT_HAVE_3DNOW) - if (qgetenv("QT_NO_3DNOW").toInt()) - features ^= MMX3DNOW; -#endif - if (qgetenv("QT_NO_3DNOWEXT").toInt()) - features ^= MMX3DNOWEXT; - -#if defined(QT_HAVE_SSE) - if (qgetenv("QT_NO_SSE").toInt()) - features ^= SSE; -#endif -#if defined(QT_HAVE_SSE2) - if (qgetenv("QT_NO_SSE2").toInt()) - features ^= SSE2; -#endif + return features; } #elif defined(__ia64__) @@ -395,64 +344,78 @@ static inline uint detectProcessorFeatures() } #endif +/* + * Use kdesdk/scripts/generate_string_table.pl to update the table below. + * Here's the data (don't forget the ONE leading space): + mmx + mmxext + mmx3dnow + mmx3dnowext + sse + sse2 + cmov + iwmmxt + neon + sse3 + ssse3 + sse4.1 + sse4.2 + avx + */ + +// begin generated +static const char features_string[] = + " mmx\0" + " mmxext\0" + " mmx3dnow\0" + " mmx3dnowext\0" + " sse\0" + " sse2\0" + " cmov\0" + " iwmmxt\0" + " neon\0" + " sse3\0" + " ssse3\0" + " sse4.1\0" + " sse4.2\0" + " avx\0" + "\0"; + +static const int features_indices[] = { + 0, 5, 13, 23, 36, 41, 47, 53, + 61, 67, 73, 80, 88, 96, -1 +}; +// end generated + +const int features_count = (sizeof features_indices - 1) / (sizeof features_indices[0]); + uint qDetectCPUFeatures() { static QBasicAtomicInt features = Q_BASIC_ATOMIC_INITIALIZER(-1); if (features != -1) return features; - features = detectProcessorFeatures(); + uint f = detectProcessorFeatures(); + QByteArray disable = qgetenv("QT_NO_CPU_FEATURE"); + if (!disable.isEmpty()) { + disable.prepend(' '); + for (int i = 0; i < features_count; ++i) { + if (disable.contains(features_string + features_indices[i])) + f &= ~(1 << i); + } + } + + features = f; return features; } void qDumpCPUFeatures() { - /* - * Use kdesdk/scripts/generate_string_table.pl to update the table below. - * Here's the data: -mmx -mmxext -mmx3dnow -mmx3dnowext -sse -sse2 -cmov -iwmmxt -neon -sse3 -ssse3 -sse4.1 -sse4.2 -avx - */ - static const char features_string[] = - "mmx\0" - "mmxext\0" - "mmx3dnow\0" - "mmx3dnowext\0" - "sse\0" - "sse2\0" - "cmov\0" - "iwmmxt\0" - "neon\0" - "sse3\0" - "ssse3\0" - "sse4.1\0" - "sse4.2\0" - "avx\0" - "\0"; - - static const int features_indices[] = { - 0, 4, 11, 20, 32, 36, 41, 46, - 53, 58, 63, 69, 76, 83, -1 - }; - const int features_count = (sizeof features_indices - 1) / (sizeof features_indices[0]); - uint features = qDetectCPUFeatures(); printf("Processor features: "); for (int i = 0; i < features_count; ++i) { if (features & (1 << i)) - printf(" %s", features_string + features_indices[i]); + printf("%s", features_string + features_indices[i]); } puts(""); } -- cgit v0.12 From a55f392edc2145a071d0d59cb0fc69b0d5205a76 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 21 Aug 2010 00:57:07 +0200 Subject: Use QElapsedTimer for the benchlib tests. It's faster to calculate the time with it, since it doesn't need to convert to local time first. Reviewed-By: Harald Fernengel --- dist/changes-4.7.1 | 118 ++++++++++++++++++++++++++++++++++ src/testlib/qbenchmarkmeasurement_p.h | 4 +- 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 dist/changes-4.7.1 diff --git a/dist/changes-4.7.1 b/dist/changes-4.7.1 new file mode 100644 index 0000000..c8b26c2 --- /dev/null +++ b/dist/changes-4.7.1 @@ -0,0 +1,118 @@ +Qt 4.7.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 4.7.0. For more details, +refer to the online documentation included in this distribution. The +documentation is also available online: + + http://qt.nokia.com/doc/4.7 + +The Qt version 4.7 series is binary compatible with the 4.6.x series. +Applications compiled for 4.6 will continue to run with 4.7. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker, the (now obsolete) Task +Tracker, or the Merge Request queue of the public source repository. + +Qt Bug Tracker: http://bugreports.qt.nokia.com +Task Tracker: http://qt.nokia.com/developer/task-tracker +Merge Request: http://qt.gitorious.org + +**************************************************************************** +* General * +**************************************************************************** + +Optimizations +------------- + + - Improved the benchmarking library's timing code + * Uses a faster access to the system clock + + * See list of Important Behavior Changes below + + +**************************************************************************** +* Library * +**************************************************************************** + +QtCore +------ + + +QtGui +----- + + +QtDBus +------ + + +QtMultimedia +------------ + + +QtNetwork +--------- + + +QtOpenGL +-------- + + +QtOpenVG +-------- + + +QtWebKit +-------- + + +QtSql +----- + + +QtSvg +----- + + +Qt Plugins +---------- + + + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Qt for Unix (X11 and Mac OS X) +------------------------------ + + +Qt for Linux/X11 +---------------- + + +Qt for Windows +-------------- + + +Qt for Mac OS X +--------------- + + +Qt for Symbian +-------------- + + + +**************************************************************************** +* Tools * +**************************************************************************** + + - Designer + + - uic + +**************************************************************************** +* Important Behavior Changes * +**************************************************************************** + + diff --git a/src/testlib/qbenchmarkmeasurement_p.h b/src/testlib/qbenchmarkmeasurement_p.h index 932852c..20a3bc1 100644 --- a/src/testlib/qbenchmarkmeasurement_p.h +++ b/src/testlib/qbenchmarkmeasurement_p.h @@ -53,7 +53,7 @@ // We mean it. // -#include +#include #include "3rdparty/cycle_p.h" #include "qbenchmark.h" @@ -87,7 +87,7 @@ public: bool needsWarmupIteration(); QTest::QBenchmarkMetric metricType(); private: - QTime time; + QElapsedTimer time; }; #ifdef HAVE_TICK_COUNTER // defined in 3rdparty/cycle_p.h -- cgit v0.12 From d8cd04e97540ac1c048a35ad54c8c6337d639ca1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 24 Aug 2010 20:17:48 +0200 Subject: Fix building of qsimd.cpp on Windows CE --- src/corelib/tools/qsimd.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index c34644c..68ab033 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -63,6 +63,8 @@ QT_BEGIN_NAMESPACE #if defined (Q_OS_WINCE) static inline uint detectProcessorFeatures() { + uint features = 0; + #if defined (ARM) if (IsProcessorFeaturePresent(PF_ARM_INTEL_WMMX)) { features = IWMMXT; -- cgit v0.12 From ed1fecfe6b7ce370184ef4dfb421fb387807633b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 24 Aug 2010 23:22:13 +0200 Subject: Add the missing license headers to the QString benchmark data --- tests/auto/headers/tst_headers.cpp | 3 +- tests/benchmarks/corelib/tools/qstring/data.cpp | 1 + tests/benchmarks/corelib/tools/qstring/data.h | 41 ++++++++++++++++++++++ .../corelib/tools/qstring/generatelist.pl | 3 +- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/tests/auto/headers/tst_headers.cpp b/tests/auto/headers/tst_headers.cpp index 06c70f9..7ccf058 100644 --- a/tests/auto/headers/tst_headers.cpp +++ b/tests/auto/headers/tst_headers.cpp @@ -213,7 +213,8 @@ void tst_Headers::licenseCheck() return; if (content.first().contains("generated")) { - content.takeFirst(); + // don't scan generated files + return; } if (sourceFile.endsWith("/tests/auto/linguist/lupdate/testdata/good/merge_ordering/foo.cpp") diff --git a/tests/benchmarks/corelib/tools/qstring/data.cpp b/tests/benchmarks/corelib/tools/qstring/data.cpp index 89f50d0..6d1a069 100644 --- a/tests/benchmarks/corelib/tools/qstring/data.cpp +++ b/tests/benchmarks/corelib/tools/qstring/data.cpp @@ -1,3 +1,4 @@ +// This is a generated file - DO NOT EDIT static const ushort stringCollectionData[] __attribute__((aligned(16))) = { // #0 65535, diff --git a/tests/benchmarks/corelib/tools/qstring/data.h b/tests/benchmarks/corelib/tools/qstring/data.h index a23dae3..c7a7467 100644 --- a/tests/benchmarks/corelib/tools/qstring/data.h +++ b/tests/benchmarks/corelib/tools/qstring/data.h @@ -1,3 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + #include struct StringCollection diff --git a/tests/benchmarks/corelib/tools/qstring/generatelist.pl b/tests/benchmarks/corelib/tools/qstring/generatelist.pl index 2777329..d027adb 100644 --- a/tests/benchmarks/corelib/tools/qstring/generatelist.pl +++ b/tests/benchmarks/corelib/tools/qstring/generatelist.pl @@ -105,7 +105,8 @@ sub printUshortArray($$$) { print "#include \"data.h\"\n\n"; -print "const ushort stringCollectionData[] __attribute__((aligned(64))) = { \n"; +print "// This is a generated file - DO NOT EDIT\n"; +print "const ushort stringCollectionData[] __attribute__((aligned(64))) = {\n"; $count = 0; $offset = 0; $totalsize = 0; -- cgit v0.12