Commit 5579ec34 authored by Kenton Varda's avatar Kenton Varda

Update far pointer implementation to match spec. Also adjust the spec slightly…

Update far pointer implementation to match spec.  Also adjust the spec slightly so that you can tell whether a far pointer is a double-far before you dereference it.
parent 42951751
...@@ -95,20 +95,19 @@ struct WireReference { ...@@ -95,20 +95,19 @@ struct WireReference {
offsetAndKind.set(((elementCount / ELEMENTS) << 2) | kind); offsetAndKind.set(((elementCount / ELEMENTS) << 2) | kind);
} }
CAPNPROTO_ALWAYS_INLINE(WordCount positionInSegment() const) { CAPNPROTO_ALWAYS_INLINE(WordCount farPositionInSegment() const) {
CAPNPROTO_DEBUG_ASSERT(kind() == FAR, CAPNPROTO_DEBUG_ASSERT(kind() == FAR,
"positionInSegment() should only be called on FAR references."); "positionInSegment() should only be called on FAR references.");
return (offsetAndKind.get() >> 2) * WORDS; return (offsetAndKind.get() >> 3) * WORDS;
} }
CAPNPROTO_ALWAYS_INLINE(void setKindAndPositionInSegment(Kind kind, WordCount pos)) { CAPNPROTO_ALWAYS_INLINE(bool isDoubleFar() const) {
offsetAndKind.set(((pos / WORDS) << 2) | kind); CAPNPROTO_DEBUG_ASSERT(kind() == FAR,
} "isDoubleFar() should only be called on FAR references.");
return (offsetAndKind.get() >> 2) & 1;
CAPNPROTO_ALWAYS_INLINE(bool landingPadIsFollowedByAnotherReference() const) {
return (offsetAndKind.get() & ~3) != 0;
} }
CAPNPROTO_ALWAYS_INLINE(void setLandingPad(Kind kind, bool followedByAnotherReference)) { CAPNPROTO_ALWAYS_INLINE(void setFar(bool isDoubleFar, WordCount pos)) {
offsetAndKind.set((static_cast<uint32_t>(followedByAnotherReference) << 2) | kind); offsetAndKind.set(((pos / WORDS) << 3) | (static_cast<uint32_t>(isDoubleFar) << 2) |
static_cast<uint32_t>(Kind::FAR));
} }
// ----------------------------------------------------------------- // -----------------------------------------------------------------
...@@ -218,12 +217,12 @@ struct WireHelpers { ...@@ -218,12 +217,12 @@ struct WireHelpers {
ptr = segment->allocate(amountPlusRef); ptr = segment->allocate(amountPlusRef);
// Set up the original reference to be a far reference to the new segment. // Set up the original reference to be a far reference to the new segment.
ref->setKindAndPositionInSegment(WireReference::FAR, segment->getOffsetTo(ptr)); ref->setFar(false, segment->getOffsetTo(ptr));
ref->farRef.set(segment->getSegmentId()); ref->farRef.set(segment->getSegmentId());
// Initialize the landing pad to indicate that the data immediately follows the pad. // Initialize the landing pad to indicate that the data immediately follows the pad.
ref = reinterpret_cast<WireReference*>(ptr); ref = reinterpret_cast<WireReference*>(ptr);
ref->setLandingPad(kind, false); ref->setKindAndTarget(kind, ptr + REFERENCE_SIZE_IN_WORDS);
// Allocated space follows new reference. // Allocated space follows new reference.
return ptr + REFERENCE_SIZE_IN_WORDS; return ptr + REFERENCE_SIZE_IN_WORDS;
...@@ -236,16 +235,19 @@ struct WireHelpers { ...@@ -236,16 +235,19 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(word* followFars(WireReference*& ref, SegmentBuilder*& segment)) { static CAPNPROTO_ALWAYS_INLINE(word* followFars(WireReference*& ref, SegmentBuilder*& segment)) {
if (ref->kind() == WireReference::FAR) { if (ref->kind() == WireReference::FAR) {
segment = segment->getArena()->getSegment(ref->farRef.segmentId.get()); segment = segment->getArena()->getSegment(ref->farRef.segmentId.get());
ref = reinterpret_cast<WireReference*>(segment->getPtrUnchecked(ref->positionInSegment())); WireReference* pad =
if (ref->landingPadIsFollowedByAnotherReference()) { reinterpret_cast<WireReference*>(segment->getPtrUnchecked(ref->farPositionInSegment()));
// Target lives elsewhere. Another far reference follows. if (!ref->isDoubleFar()) {
WireReference* far2 = ref + 1; ref = pad;
segment = segment->getArena()->getSegment(far2->farRef.segmentId.get()); return pad->target();
return segment->getPtrUnchecked(far2->positionInSegment());
} else {
// Target immediately follows landing pad.
return reinterpret_cast<word*>(ref + 1);
} }
// Landing pad is another far pointer. It is followed by a tag describing the pointed-to
// object.
ref = pad + 1;
segment = segment->getArena()->getSegment(pad->farRef.segmentId.get());
return segment->getPtrUnchecked(pad->farPositionInSegment());
} else { } else {
return ref->target(); return ref->target();
} }
...@@ -254,41 +256,37 @@ struct WireHelpers { ...@@ -254,41 +256,37 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE( static CAPNPROTO_ALWAYS_INLINE(
const word* followFars(const WireReference*& ref, SegmentReader*& segment)) { const word* followFars(const WireReference*& ref, SegmentReader*& segment)) {
if (ref->kind() == WireReference::FAR) { if (ref->kind() == WireReference::FAR) {
// Look up the segment containing the landing pad.
segment = segment->getArena()->tryGetSegment(ref->farRef.segmentId.get()); segment = segment->getArena()->tryGetSegment(ref->farRef.segmentId.get());
if (CAPNPROTO_EXPECT_FALSE(segment == nullptr)) { if (CAPNPROTO_EXPECT_FALSE(segment == nullptr)) {
return nullptr; return nullptr;
} }
const word* ptr = segment->getStartPtr() + ref->positionInSegment(); // Find the landing pad and check that it is within bounds.
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval( const word* ptr = segment->getStartPtr() + ref->farPositionInSegment();
ptr, ptr + REFERENCE_SIZE_IN_WORDS))) { WordCount padWords = (1 + ref->isDoubleFar()) * REFERENCE_SIZE_IN_WORDS;
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval(ptr, ptr + padWords))) {
return nullptr; return nullptr;
} }
ref = reinterpret_cast<const WireReference*>(ptr); const WireReference* pad = reinterpret_cast<const WireReference*>(ptr);
if (ref->landingPadIsFollowedByAnotherReference()) { // If this is not a double-far then the landing pad is our final pointer.
// Target is in another castle. Another far reference follows. if (!ref->isDoubleFar()) {
const WireReference* far2 = ref + 1; ref = pad;
segment = segment->getArena()->tryGetSegment(far2->farRef.segmentId.get()); return pad->target();
if (CAPNPROTO_EXPECT_FALSE(segment == nullptr)) { }
return nullptr;
}
if (CAPNPROTO_EXPECT_FALSE(far2->kind() != WireReference::FAR)) {
return nullptr;
}
ptr = segment->getStartPtr() + far2->positionInSegment(); // Landing pad is another far pointer. It is followed by a tag describing the pointed-to
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval( // object.
ptr, ptr + REFERENCE_SIZE_IN_WORDS))) { ref = pad + 1;
return nullptr;
}
return ptr; segment = segment->getArena()->tryGetSegment(pad->farRef.segmentId.get());
} else { if (CAPNPROTO_EXPECT_FALSE(segment == nullptr)) {
// Target immediately follows landing pad. return nullptr;
return reinterpret_cast<const word*>(ref + 1);
} }
return segment->getStartPtr() + pad->farPositionInSegment();
} else { } else {
return ref->target(); return ref->target();
} }
......
...@@ -201,25 +201,27 @@ When a pointer needs to point to a different segment, offsets no longer work. W ...@@ -201,25 +201,27 @@ When a pointer needs to point to a different segment, offsets no longer work. W
the pointer as a "far pointer", which looks like this: the pointer as a "far pointer", which looks like this:
lsb far pointer msb lsb far pointer msb
+-+-----------------------------+-------------------------------+ +-+-+---------------------------+-------------------------------+
|A| B | C | |A|B| C | D |
+-+-----------------------------+-------------------------------+ +-+-+---------------------------+-------------------------------+
A (2 bits) = 2, to indicate that this is a far pointer. A (2 bits) = 2, to indicate that this is a far pointer.
B (30 bits) = Offset, in words, from the start of the target segment B (1 bit) = 0 if the landing pad is one word, 1 if it is two words.
See explanation below.
C (30 bits) = Offset, in words, from the start of the target segment
to the location of the far-pointer landing-pad within that to the location of the far-pointer landing-pad within that
segment. segment. Unsigned.
C (32 bits) = ID of the target segment. (Segments are numbered D (32 bits) = ID of the target segment. (Segments are numbered
sequentially starting from zero.) sequentially starting from zero.)
The "landing pad" of a far pointer is normally just another pointer, which in turn points to the If B == 0, then the "landing pad" of a far pointer is normally just another pointer, which in turn
actual object. points to the actual object.
However, if the "landing pad" pointer is itself another far pointer, then it is interpreted If B == 1, then the "landing pad" is itself another far pointer that is interpreted differently:
differently: This far pointer points to the start of the object's _content_, located in some other This far pointer (which always has B = 0) points to the start of the object's _content_, located in
segment. The landing pad is itself immediately followed by a tag word. The tag word looks exactly some other segment. The landing pad is itself immediately followed by a tag word. The tag word
like an intra-segment pointer to the target object would look, except that the offset is always looks exactly like an intra-segment pointer to the target object would look, except that the offset
zero. is always zero.
The reason for the convoluted double-far convention is to make it possible to form a new pointer The reason for the convoluted double-far convention is to make it possible to form a new pointer
to an object in a segment that is full. If you can't allocate even one word in the segment where to an object in a segment that is full. If you can't allocate even one word in the segment where
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment