Merge all conversions from Latin-1 in qstring.cpp into a single function

Amazing how many places had the conversion duplicated. When compiling
with GCC under -O3 (which enables -ftree-vectorize), GCC would use SIMD
by using the PMOVZXBW instruction, but only if the -msse4.1 was passed
(or equivalent -march= switch), which almost no one did.

Also, the two lastIndexOf and the qt_find_latin1_string updates are also
fixing bugs because the old code forgot to cast the input to uchar
first. That meant the compiler was generating sign-extension from 8 to
16 bits, as opposed to zero-extension.

Change-Id: I4e2430a51dfc337994834524d3540382157509ef
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Thiago Macieira 2014-01-16 14:22:39 -08:00 committed by The Qt Project
parent 388bfb2731
commit 1f6ae7444b

View File

@ -132,6 +132,12 @@ QT_BEGIN_NAMESPACE
* for the common case. * for the common case.
*/ */
#if defined(__mips_dsp)
// From qstring_mips_dsp_asm.S
extern "C" void qt_fromlatin1_mips_asm_unroll4 (ushort*, const char*, uint);
extern "C" void qt_fromlatin1_mips_asm_unroll8 (ushort*, const char*, uint);
#endif
// internal // internal
int qFindString(const QChar *haystack, int haystackLen, int from, int qFindString(const QChar *haystack, int haystackLen, int from,
const QChar *needle, int needleLen, Qt::CaseSensitivity cs); const QChar *needle, int needleLen, Qt::CaseSensitivity cs);
@ -190,6 +196,45 @@ inline RetType UnrollTailLoop<0>::exec(int, RetType returnIfExited, Functor1, Fu
} }
#endif #endif
// conversion between Latin 1 and UTF-16
static void qt_from_latin1(ushort *dst, const char *str, size_t size)
{
/* SIMD:
* Unpacking with SSE has been shown to improve performance on recent CPUs
* The same method gives no improvement with NEON.
*/
#if defined(__SSE2__)
if (size >= 16) {
int chunkCount = size >> 4; // divided by 16
const __m128i nullMask = _mm_set1_epi32(0);
for (int i = 0; i < chunkCount; ++i) {
const __m128i chunk = _mm_loadu_si128((__m128i*)str); // load
str += 16;
// unpack the first 8 bytes, padding with zeros
const __m128i firstHalf = _mm_unpacklo_epi8(chunk, nullMask);
_mm_storeu_si128((__m128i*)dst, firstHalf); // store
dst += 8;
// unpack the last 8 bytes, padding with zeros
const __m128i secondHalf = _mm_unpackhi_epi8 (chunk, nullMask);
_mm_storeu_si128((__m128i*)dst, secondHalf); // store
dst += 8;
}
size = size % 16;
}
#endif
#if defined(__mips_dsp)
if (size > 20)
qt_fromlatin1_mips_asm_unroll8(dst, str, size);
else
qt_fromlatin1_mips_asm_unroll4(dst, str, size);
#else
while (size--)
*dst++ = (uchar)*str++;
#endif
}
// Unicode case-insensitive comparison // Unicode case-insensitive comparison
static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const ushort *be) static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const ushort *be)
{ {
@ -1614,7 +1659,7 @@ QString &QString::operator=(QChar ch)
*/ */
QString &QString::insert(int i, QLatin1String str) QString &QString::insert(int i, QLatin1String str)
{ {
const uchar *s = (const uchar *)str.latin1(); const char *s = str.latin1();
if (i < 0 || !s || !(*s)) if (i < 0 || !s || !(*s))
return *this; return *this;
@ -1622,8 +1667,7 @@ QString &QString::insert(int i, QLatin1String str)
expand(qMax(d->size, i) + len - 1); expand(qMax(d->size, i) + len - 1);
::memmove(d->data() + i + len, d->data() + i, (d->size - i - len) * sizeof(QChar)); ::memmove(d->data() + i + len, d->data() + i, (d->size - i - len) * sizeof(QChar));
for (int j = 0; j < len; ++j) qt_from_latin1(d->data() + i, s, uint(len));
d->data()[i + j] = s[j];
return *this; return *this;
} }
@ -1735,14 +1779,14 @@ QString &QString::append(const QChar *str, int len)
*/ */
QString &QString::append(QLatin1String str) QString &QString::append(QLatin1String str)
{ {
const uchar *s = (const uchar *)str.latin1(); const char *s = str.latin1();
if (s) { if (s) {
int len = str.size(); int len = str.size();
if (d->ref.isShared() || uint(d->size + len) + 1u > d->alloc) if (d->ref.isShared() || uint(d->size + len) + 1u > d->alloc)
reallocData(uint(d->size + len) + 1u, true); reallocData(uint(d->size + len) + 1u, true);
ushort *i = d->data() + d->size; ushort *i = d->data() + d->size;
while ((*i++ = *s++)) qt_from_latin1(i, s, uint(len));
; i[len] = '\0';
d->size += len; d->size += len;
} }
return *this; return *this;
@ -2249,13 +2293,11 @@ QString& QString::replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString &QString::replace(QLatin1String before, QLatin1String after, Qt::CaseSensitivity cs) QString &QString::replace(QLatin1String before, QLatin1String after, Qt::CaseSensitivity cs)
{ {
int alen = after.size(); int alen = after.size();
QVarLengthArray<ushort> a(alen);
for (int i = 0; i < alen; ++i)
a[i] = (uchar)after.latin1()[i];
int blen = before.size(); int blen = before.size();
QVarLengthArray<ushort> a(alen);
QVarLengthArray<ushort> b(blen); QVarLengthArray<ushort> b(blen);
for (int i = 0; i < blen; ++i) qt_from_latin1(a.data(), after.latin1(), alen);
b[i] = (uchar)before.latin1()[i]; qt_from_latin1(b.data(), before.latin1(), blen);
return replace((const QChar *)b.data(), blen, (const QChar *)a.data(), alen, cs); return replace((const QChar *)b.data(), blen, (const QChar *)a.data(), alen, cs);
} }
@ -2275,8 +2317,7 @@ QString &QString::replace(QLatin1String before, const QString &after, Qt::CaseSe
{ {
int blen = before.size(); int blen = before.size();
QVarLengthArray<ushort> b(blen); QVarLengthArray<ushort> b(blen);
for (int i = 0; i < blen; ++i) qt_from_latin1(b.data(), before.latin1(), blen);
b[i] = (uchar)before.latin1()[i];
return replace((const QChar *)b.data(), blen, after.constData(), after.d->size, cs); return replace((const QChar *)b.data(), blen, after.constData(), after.d->size, cs);
} }
@ -2296,8 +2337,7 @@ QString &QString::replace(const QString &before, QLatin1String after, Qt::CaseSe
{ {
int alen = after.size(); int alen = after.size();
QVarLengthArray<ushort> a(alen); QVarLengthArray<ushort> a(alen);
for (int i = 0; i < alen; ++i) qt_from_latin1(a.data(), after.latin1(), alen);
a[i] = (uchar)after.latin1()[i];
return replace(before.constData(), before.d->size, (const QChar *)a.data(), alen, cs); return replace(before.constData(), before.d->size, (const QChar *)a.data(), alen, cs);
} }
@ -2317,8 +2357,7 @@ QString &QString::replace(QChar c, QLatin1String after, Qt::CaseSensitivity cs)
{ {
int alen = after.size(); int alen = after.size();
QVarLengthArray<ushort> a(alen); QVarLengthArray<ushort> a(alen);
for (int i = 0; i < alen; ++i) qt_from_latin1(a.data(), after.latin1(), alen);
a[i] = (uchar)after.latin1()[i];
return replace(&c, 1, (const QChar *)a.data(), alen, cs); return replace(&c, 1, (const QChar *)a.data(), alen, cs);
} }
@ -2886,8 +2925,7 @@ int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) co
from = delta; from = delta;
QVarLengthArray<ushort> s(sl); QVarLengthArray<ushort> s(sl);
for (int i = 0; i < sl; ++i) qt_from_latin1(s.data(), str.latin1(), sl);
s[i] = str.latin1()[i];
return lastIndexOfHelper(d->data(), from, s.data(), sl, cs); return lastIndexOfHelper(d->data(), from, s.data(), sl, cs);
} }
@ -4295,12 +4333,6 @@ QVector<uint> QString::toUcs4() const
return v; return v;
} }
#if defined(__mips_dsp)
// From qstring_mips_dsp_asm.S
extern "C" void qt_fromlatin1_mips_asm_unroll4 (ushort*, const char*, uint);
extern "C" void qt_fromlatin1_mips_asm_unroll8 (ushort*, const char*, uint);
#endif
QString::Data *QString::fromLatin1_helper(const char *str, int size) QString::Data *QString::fromLatin1_helper(const char *str, int size)
{ {
Data *d; Data *d;
@ -4316,40 +4348,8 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size)
d->size = size; d->size = size;
d->data()[size] = '\0'; d->data()[size] = '\0';
ushort *dst = d->data(); ushort *dst = d->data();
/* SIMD:
* Unpacking with SSE has been shown to improve performance on recent CPUs
* The same method gives no improvement with NEON.
*/
#if defined(__SSE2__)
if (size >= 16) {
int chunkCount = size >> 4; // divided by 16
const __m128i nullMask = _mm_set1_epi32(0);
for (int i = 0; i < chunkCount; ++i) {
const __m128i chunk = _mm_loadu_si128((__m128i*)str); // load
str += 16;
// unpack the first 8 bytes, padding with zeros qt_from_latin1(dst, str, uint(size));
const __m128i firstHalf = _mm_unpacklo_epi8(chunk, nullMask);
_mm_storeu_si128((__m128i*)dst, firstHalf); // store
dst += 8;
// unpack the last 8 bytes, padding with zeros
const __m128i secondHalf = _mm_unpackhi_epi8 (chunk, nullMask);
_mm_storeu_si128((__m128i*)dst, secondHalf); // store
dst += 8;
}
size = size % 16;
}
#endif
#if defined(__mips_dsp)
if (size > 20)
qt_fromlatin1_mips_asm_unroll8(dst, str, size);
else
qt_fromlatin1_mips_asm_unroll4(dst, str, size);
#else
while (size--)
*dst++ = (uchar)*str++;
#endif
} }
return d; return d;
} }
@ -9003,8 +9003,7 @@ int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs)
from = delta; from = delta;
QVarLengthArray<ushort> s(sl); QVarLengthArray<ushort> s(sl);
for (int i = 0; i < sl; ++i) qt_from_latin1(s.data(), str.latin1(), sl);
s[i] = str.latin1()[i];
return lastIndexOfHelper(reinterpret_cast<const ushort*>(unicode()), from, s.data(), sl, cs); return lastIndexOfHelper(reinterpret_cast<const ushort*>(unicode()), from, s.data(), sl, cs);
} }
@ -9342,8 +9341,7 @@ static inline int qt_find_latin1_string(const QChar *haystack, int size,
const char *latin1 = needle.latin1(); const char *latin1 = needle.latin1();
int len = needle.size(); int len = needle.size();
QVarLengthArray<ushort> s(len); QVarLengthArray<ushort> s(len);
for (int i = 0; i < len; ++i) qt_from_latin1(s.data(), latin1, len);
s[i] = latin1[i];
return qFindString(haystack, size, from, return qFindString(haystack, size, from,
reinterpret_cast<const QChar*>(s.constData()), len, cs); reinterpret_cast<const QChar*>(s.constData()), len, cs);