Add TaggedPointer<T,N> template. A compact encoding for a (pointer, tag) pair, where |tag| is a small unsigned integer that uses no more than N bits, and |pointer| is guaranteed to be aligned on N bits as well. This will be used by future CLs to optimize memory usage of some critical GN data structures. Bug: None Change-Id: Ica9d720a4579cbc30b312c1f940e46dd9230a0c8 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13622 Reviewed-by: Sylvain Defresne <sdefresne@chromium.org> Commit-Queue: David Turner <digit@google.com>
diff --git a/build/gen.py b/build/gen.py index f695ccd..4c75502 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -803,6 +803,7 @@ 'src/gn/string_utils_unittest.cc', 'src/gn/substitution_pattern_unittest.cc', 'src/gn/substitution_writer_unittest.cc', + 'src/gn/tagged_pointer_unittest.cc', 'src/gn/target_unittest.cc', 'src/gn/template_unittest.cc', 'src/gn/test_with_scheduler.cc',
diff --git a/src/gn/tagged_pointer.h b/src/gn/tagged_pointer.h new file mode 100644 index 0000000..df760d1 --- /dev/null +++ b/src/gn/tagged_pointer.h
@@ -0,0 +1,58 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_TAGGED_POINTER_H_ +#define TOOLS_GN_TAGGED_POINTER_H_ + +#include "base/logging.h" + +// A TaggedPointer<T.N> is a compact encoding of a (pointer, tag) pair +// when all |tag| values are guaranteed to be less than N bits long, and +// all pointer values are guaranteed to be aligned to at least N bits. +template <typename T, size_t BITS> +class TaggedPointer { + public: + TaggedPointer() = default; + TaggedPointer(T* ptr, unsigned tag) + : value_(reinterpret_cast<uintptr_t>(ptr)) { + CheckPointerValue(ptr); + CheckTagValue(tag); + value_ |= static_cast<uintptr_t>(tag); + } + + T* ptr() const { return reinterpret_cast<T*>(value_ & ~kTagMask); } + unsigned tag() const { return static_cast<unsigned>(value_ & kTagMask); } + + void set_ptr(T* ptr) { + CheckPointerValue(ptr); + value_ = reinterpret_cast<uintptr_t>(ptr) | (value_ & kTagMask); + } + + void set_tag(unsigned tag) { + CheckTagValue(tag); + value_ = (value_ & ~kTagMask) | tag; + } + + bool operator==(TaggedPointer other) const { return value_ == other.value_; } + + bool operator!=(TaggedPointer other) const { return !(*this == other); } + + bool operator<(TaggedPointer other) const { return value_ < other.value_; } + + private: + static const uintptr_t kTagMask = (uintptr_t(1) << BITS) - 1u; + + static void CheckPointerValue(T* ptr) { + DCHECK((reinterpret_cast<uintptr_t>(ptr) & kTagMask) == 0) + << "Pointer is not aligned to " << BITS << " bits: " << ptr; + } + static void CheckTagValue(unsigned tag) { + DCHECK(tag <= kTagMask) + << "Tag value is larger than " << BITS << " bits: " << tag; + } + + uintptr_t value_ = 0; +}; + +#endif // TOOLS_GN_TAGGED_POINTER_H_
diff --git a/src/gn/tagged_pointer_unittest.cc b/src/gn/tagged_pointer_unittest.cc new file mode 100644 index 0000000..b6d68e5 --- /dev/null +++ b/src/gn/tagged_pointer_unittest.cc
@@ -0,0 +1,19 @@ +#include "gn/tagged_pointer.h" +#include "util/test/test.h" + +struct Point { + double x; + double y; +}; + +TEST(TaggedPointer, Creation) { + TaggedPointer<Point, 2> ptr; + + EXPECT_FALSE(ptr.ptr()); + EXPECT_EQ(0u, ptr.tag()); + + Point point1 = {1., 2.}; + TaggedPointer<Point, 2> ptr2(&point1, 2); + EXPECT_EQ(&point1, ptr2.ptr()); + EXPECT_EQ(2u, ptr2.tag()); +}