Skip to content

Commit 91b7cd6

Browse files
martijnvelscopybara-github
authored andcommitted
Change Cord internal layout, which reduces store-load penalties on ARM
PiperOrigin-RevId: 480511524 Change-Id: I73945b1150a2e2e75774684fb8e7364f9c1290a7
1 parent 73789eb commit 91b7cd6

File tree

1 file changed

+41
-36
lines changed

1 file changed

+41
-36
lines changed

absl/strings/internal/cord_internal.h

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ constexpr char GetOrNull(absl::string_view data, size_t pos) {
423423
return pos < data.size() ? data[pos] : '\0';
424424
}
425425

426-
// We store cordz_info as 64 bit pointer value in big endian format. This
427-
// guarantees that the least significant byte of cordz_info matches the last
426+
// We store cordz_info as 64 bit pointer value in little endian format. This
427+
// guarantees that the least significant byte of cordz_info matches the first
428428
// byte of the inline data representation in as_chars_, which holds the inlined
429429
// size or the 'is_tree' bit.
430430
using cordz_info_t = int64_t;
@@ -434,14 +434,14 @@ using cordz_info_t = int64_t;
434434
static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
435435
static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
436436

437-
// BigEndianByte() creates a big endian representation of 'value', i.e.: a big
438-
// endian value where the last byte in the host's representation holds 'value`,
439-
// with all other bytes being 0.
440-
static constexpr cordz_info_t BigEndianByte(unsigned char value) {
437+
// LittleEndianByte() creates a little endian representation of 'value', i.e.:
438+
// a little endian value where the first byte in the host's representation
439+
// holds 'value`, with all other bytes being 0.
440+
static constexpr cordz_info_t LittleEndianByte(unsigned char value) {
441441
#if defined(ABSL_IS_BIG_ENDIAN)
442-
return value;
443-
#else
444442
return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8);
443+
#else
444+
return value;
445445
#endif
446446
}
447447

@@ -450,25 +450,37 @@ class InlineData {
450450
// DefaultInitType forces the use of the default initialization constructor.
451451
enum DefaultInitType { kDefaultInit };
452452

453-
// kNullCordzInfo holds the big endian representation of intptr_t(1)
453+
// kNullCordzInfo holds the little endian representation of intptr_t(1)
454454
// This is the 'null' / initial value of 'cordz_info'. The null value
455455
// is specifically big endian 1 as with 64-bit pointers, the last
456456
// byte of cordz_info overlaps with the last byte holding the tag.
457-
static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1);
457+
static constexpr cordz_info_t kNullCordzInfo = LittleEndianByte(1);
458+
459+
// kTagOffset contains the offset of the control byte / tag. This constant is
460+
// intended mostly for debugging purposes: do not remove this constant as it
461+
// is actively inspected and used by gdb pretty printing code.
462+
static constexpr size_t kTagOffset = 0;
458463

459464
constexpr InlineData() : as_chars_{0} {}
460465
explicit InlineData(DefaultInitType) {}
461466
explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {}
462467
explicit constexpr InlineData(absl::string_view chars)
463-
: as_chars_{
464-
GetOrNull(chars, 0), GetOrNull(chars, 1),
465-
GetOrNull(chars, 2), GetOrNull(chars, 3),
466-
GetOrNull(chars, 4), GetOrNull(chars, 5),
467-
GetOrNull(chars, 6), GetOrNull(chars, 7),
468-
GetOrNull(chars, 8), GetOrNull(chars, 9),
469-
GetOrNull(chars, 10), GetOrNull(chars, 11),
470-
GetOrNull(chars, 12), GetOrNull(chars, 13),
471-
GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {}
468+
: as_chars_{static_cast<char>((chars.size() << 1)),
469+
GetOrNull(chars, 0),
470+
GetOrNull(chars, 1),
471+
GetOrNull(chars, 2),
472+
GetOrNull(chars, 3),
473+
GetOrNull(chars, 4),
474+
GetOrNull(chars, 5),
475+
GetOrNull(chars, 6),
476+
GetOrNull(chars, 7),
477+
GetOrNull(chars, 8),
478+
GetOrNull(chars, 9),
479+
GetOrNull(chars, 10),
480+
GetOrNull(chars, 11),
481+
GetOrNull(chars, 12),
482+
GetOrNull(chars, 13),
483+
GetOrNull(chars, 14)} {}
472484

473485
// Returns true if the current instance is empty.
474486
// The 'empty value' is an inlined data value of zero length.
@@ -499,8 +511,8 @@ class InlineData {
499511
// Requires the current instance to hold a tree value.
500512
CordzInfo* cordz_info() const {
501513
assert(is_tree());
502-
intptr_t info = static_cast<intptr_t>(
503-
absl::big_endian::ToHost64(static_cast<uint64_t>(as_tree_.cordz_info)));
514+
intptr_t info = static_cast<intptr_t>(absl::little_endian::ToHost64(
515+
static_cast<uint64_t>(as_tree_.cordz_info)));
504516
assert(info & 1);
505517
return reinterpret_cast<CordzInfo*>(info - 1);
506518
}
@@ -512,7 +524,7 @@ class InlineData {
512524
assert(is_tree());
513525
uintptr_t info = reinterpret_cast<uintptr_t>(cordz_info) | 1;
514526
as_tree_.cordz_info =
515-
static_cast<cordz_info_t>(absl::big_endian::FromHost64(info));
527+
static_cast<cordz_info_t>(absl::little_endian::FromHost64(info));
516528
}
517529

518530
// Resets the current cordz_info to null / empty.
@@ -525,7 +537,7 @@ class InlineData {
525537
// Requires the current instance to hold inline data.
526538
const char* as_chars() const {
527539
assert(!is_tree());
528-
return as_chars_;
540+
return &as_chars_[1];
529541
}
530542

531543
// Returns a mutable pointer to the character data inside this instance.
@@ -543,7 +555,7 @@ class InlineData {
543555
//
544556
// It's an error to read from the returned pointer without a preceding write
545557
// if the current instance does not hold inline data, i.e.: is_tree() == true.
546-
char* as_chars() { return as_chars_; }
558+
char* as_chars() { return &as_chars_[1]; }
547559

548560
// Returns the tree value of this value.
549561
// Requires the current instance to hold a tree value.
@@ -608,20 +620,13 @@ class InlineData {
608620
private:
609621
// See cordz_info_t for forced alignment and size of `cordz_info` details.
610622
struct AsTree {
611-
explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
612-
: rep(tree), cordz_info(kNullCordzInfo) {}
613-
// This union uses up extra space so that whether rep is 32 or 64 bits,
614-
// cordz_info will still start at the eighth byte, and the last
615-
// byte of cordz_info will still be the last byte of InlineData.
616-
union {
617-
absl::cord_internal::CordRep* rep;
618-
cordz_info_t unused_aligner;
619-
};
620-
cordz_info_t cordz_info;
623+
explicit constexpr AsTree(absl::cord_internal::CordRep* tree) : rep(tree) {}
624+
cordz_info_t cordz_info = kNullCordzInfo;
625+
absl::cord_internal::CordRep* rep;
621626
};
622627

623-
char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; }
624-
char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; }
628+
int8_t& tag() { return reinterpret_cast<int8_t*>(this)[0]; }
629+
int8_t tag() const { return reinterpret_cast<const int8_t*>(this)[0]; }
625630

626631
// If the data has length <= kMaxInline, we store it in `as_chars_`, and
627632
// store the size in the last char of `as_chars_` shifted left + 1.

0 commit comments

Comments
 (0)