From 8e37ee25985eaf28a6d4a74d3fe4d2c937c12a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Sat, 15 Jun 2024 10:45:52 -0700 Subject: [PATCH] ctl::string cleanup (#1215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now fully initialize a ctl::string’s memory, so that it is always set to a well-defined value, thus making it always safe to memcpy out of it. This incidentally makes our string::swap function legal, which it wasn’t before. This also saves us a store in string::reserve. Now that we have made both big_string and small_string POD, I believe it is safe to elide the launder calls, and have done so, thus cleaning up a lot of the blob-related code. I also got rid of set_big_capacity and replaced it with a set_big_string that leaves us in a well-defined state afterwards. This function also is able to be somewhat simpler; rather than delicate bit-twiddling, it just reaches straight into blob and rewrites it wholesale. Overall, this shaves about 1–2ns off of most benchmarks, and adds 1ns to only one of them - creating a string from a char *. --- ctl/string.cc | 7 ++----- ctl/string.h | 40 +++++++++++++++++----------------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/ctl/string.cc b/ctl/string.cc index f5184786f..503ade6a8 100644 --- a/ctl/string.cc +++ b/ctl/string.cc @@ -91,16 +91,13 @@ string::reserve(size_t c2) noexcept if (!isbig()) { if (!(p2 = (char*)malloc(c2))) __builtin_trap(); - memcpy(p2, data(), size()); - p2[size()] = 0; + memcpy(p2, data(), __::string_size); } else { if (!(p2 = (char*)realloc(big()->p, c2))) __builtin_trap(); } std::atomic_signal_fence(std::memory_order_seq_cst); - set_big_capacity(c2); - big()->n = n; - big()->p = p2; + set_big_string(p2, n, c2); } void diff --git a/ctl/string.h b/ctl/string.h index 5315a0a45..736067721 100644 --- a/ctl/string.h +++ b/ctl/string.h @@ -81,28 +81,23 @@ class string string() noexcept { - set_small_size(0); -#if 0 - small()->buf[0] = 0; -#endif + __builtin_memset(blob, 0, sizeof(size_t) * 2); + // equivalent to set_small_size(0) but also zeroes memory + *(((size_t *)blob) + 2) = __::sso_max << (sizeof(size_t) - 1) * 8; } void swap(string& s) noexcept { char tmp[__::string_size]; - __builtin_memcpy(tmp, __builtin_launder(blob), sizeof(tmp)); - __builtin_memcpy( - __builtin_launder(blob), __builtin_launder(s.blob), sizeof(tmp)); - __builtin_memcpy(__builtin_launder(s.blob), tmp, sizeof(tmp)); + __builtin_memcpy(tmp, blob, __::string_size); + __builtin_memcpy(blob, s.blob, __::string_size); + __builtin_memcpy(s.blob, tmp, __::string_size); } string(string&& s) noexcept { - __builtin_memcpy(blob, __builtin_launder(s.blob), sizeof(blob)); + __builtin_memcpy(blob, s.blob, __::string_size); s.set_small_size(0); -#if 0 - s.small()->buf[0] = 0; -#endif } void clear() noexcept @@ -288,52 +283,51 @@ class string private: inline bool isbig() const noexcept { - return *(__builtin_launder(blob) + __::sso_max) & 0x80; + return *(blob + __::sso_max) & 0x80; } inline void set_small_size(size_t size) noexcept { if (size > __::sso_max) __builtin_trap(); - *(__builtin_launder(blob) + __::sso_max) = (__::sso_max - size); + *(blob + __::sso_max) = (__::sso_max - size); } - inline void set_big_capacity(size_t c2) noexcept + inline void set_big_string(char *p, size_t n, size_t c2) noexcept { if (c2 > __::big_mask) __builtin_trap(); - *(__builtin_launder(blob) + __::sso_max) = 0x80; - big()->c &= ~__::big_mask; - big()->c |= c2; + *(char **)blob = p; + *(((size_t *)blob) + 1) = n; + *(((size_t *)blob) + 2) = c2 | ~__::big_mask; } inline __::small_string* small() noexcept { if (isbig()) __builtin_trap(); - return __builtin_launder(reinterpret_cast<__::small_string*>(blob)); + return reinterpret_cast<__::small_string*>(blob); } inline const __::small_string* small() const noexcept { if (isbig()) __builtin_trap(); - return __builtin_launder( - reinterpret_cast(blob)); + return reinterpret_cast(blob); } inline __::big_string* big() noexcept { if (!isbig()) __builtin_trap(); - return __builtin_launder(reinterpret_cast<__::big_string*>(blob)); + return reinterpret_cast<__::big_string*>(blob); } inline const __::big_string* big() const noexcept { if (!isbig()) __builtin_trap(); - return __builtin_launder(reinterpret_cast(blob)); + return reinterpret_cast(blob); } friend string strcat(const string_view, const string_view);