//===--- RelativePointer.h - Relative Pointer Support -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Some data structures emitted by the Swift compiler use relative indirect
/// addresses in order to minimize startup cost for a process. By referring to
/// the offset of the global offset table entry for a symbol, instead of
/// directly referring to the symbol, compiler-emitted data structures avoid
/// requiring unnecessary relocation at dynamic linking time. This header
/// contains types to help dereference these relative addresses.
///
/// Theory of references to objects
/// -------------------------------
///
/// A reference can be absolute or relative:
///
///   - An absolute reference is a pointer to the object.
///
///   - A relative reference is a (signed) offset from the address of the
///     reference to the address of its direct referent.
///
/// A relative reference can be direct, indirect, or symbolic.
///
/// In a direct reference, the direct referent is simply the target object.
/// Generally, a statically-emitted relative reference can only be direct
/// if it can be resolved to a constant offset by the linker, because loaders
/// do not support forming relative references.  This means that either the
/// reference and object must lie within the same linkage unit or the
/// difference must be computed at runtime by code.
///
/// In a symbolic reference, the direct referent is a string holding the symbol
/// name of the object.  A relative reference can only be symbolic if the
/// object actually has a symbol at runtime, which may require exporting
/// many internal symbols that would otherwise be strippable.
///
/// In an indirect reference, the direct referent is a variable holding an
/// absolute reference to the object.  An indirect relative reference may
/// refer to an arbitrary symbol, be it anonymous within the linkage unit
/// or completely external to it, but it requires the introduction of an
/// intermediate absolute reference that requires load-time initialization.
/// However, this initialization can be shared among all indirect references
/// within the linkage unit, and the linker will generally place all such
/// references adjacent to one another to improve load-time locality.
///
/// A reference can be made a dynamic union of more than one of these options.
/// This allows the compiler/linker to use a direct reference when possible
/// and a less-efficient option where required.  However, it also requires
/// the cases to be dynamically distinguished.  This can be done by setting
/// a low bit of the offset, as long as the difference between the direct
/// referent's address and the reference is a multiple of 2.  This works well
/// for "indirectable" references because most objects are known to be
/// well-aligned, and the cases that aren't (chiefly functions and strings)
/// rarely need the flexibility of this kind of reference.  It does not
/// work quite as well for "possibly symbolic" references because C strings
/// are not naturally aligned, and making them aligned generally requires
/// moving them out of the linker's ordinary string section; however, it's
/// still workable.
///
/// Finally, a relative reference can be near or far.  A near reference
/// is potentially smaller, but it requires the direct referent to lie
/// within a certain distance of the reference, even if dynamically
/// initialized.
///
/// In Swift, we always prefer to use a near direct relative reference
/// when it is possible to do so: that is, when the relationship is always
/// between two global objects emitted in the same linkage unit, and there
/// is no compatibility constraint requiring the use of an absolute reference.
///
/// When more flexibility is required, there are several options:
///
///   1. Use an absolute reference.  Size penalty on 64-bit.  Requires
///      load-time work.
///
///   2. Use a far direct relative reference.  Size penalty on 64-bit.
///      Requires load-time work when object is outside linkage unit.
///      Generally not directly supported by loaders.
///
///   3. Use an always-indirect relative reference.  Size penalty of one
///      pointer (shared).  Requires load-time work even when object is
///      within linkage unit.
///
///   4. Use a near indirectable relative reference.  Size penalty of one
///      pointer (shared) when reference exceeds range.  Runtime / code-size
///      penalty on access.  Requires load-time work (shared) only when
///      object is outside linkage unit.
///
///   5. Use a far indirectable relative reference.  Size penalty on 64-bit.
///      Size penalty of one pointer (shared) when reference exceeds range
///      and is initialized statically.  Runtime / code-size penalty on access.
///      Requires load-time work (shared) only when object is outside linkage
///      unit.
///
///   6. Use a near or far symbolic relative reference.  No load-time work.
///      Severe runtime penalty on access.  Requires custom logic to statically
///      optimize.  Requires emission of symbol for target even if private
///      to linkage unit.
///
///   7. Use a near or far direct-or-symbolic relative reference.  No
///      load-time work.  Severe runtime penalty on access if object is
///      outside of linkage unit.  Requires custom logic to statically optimize.
///
/// In general, it's our preference in Swift to use option #4 when there
/// is no possibility of initializing the reference dynamically and option #5
/// when there is.  This is because it is infeasible to actually share the
/// memory for the intermediate absolute reference when it must be allocated
/// dynamically.
///
/// Symbolic references are an interesting idea that we have not yet made
/// use of.  They may be acceptable in reflective metadata cases where it
/// is desirable to heavily bias towards never using the metadata.  However,
/// they're only profitable if there wasn't any other indirect reference
/// to the target, and it is likely that their optimal use requires a more
/// intelligent toolchain from top to bottom.
///
/// Note that the cost of load-time work also includes a binary-size penalty
/// to store the loader metadata necessary to perform that work.  Therefore
/// it is better to avoid it even when there are dynamic optimizations in
/// place to skip the work itself.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_RELATIVEPOINTER_H
#define SWIFT_BASIC_RELATIVEPOINTER_H

#include <cstdint>

namespace swift {

namespace detail {

/// Apply a relative offset to a base pointer. The offset is applied to the base
/// pointer using sign-extended, wrapping arithmetic.
template<typename BasePtrTy, typename Offset>
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");

  auto base = reinterpret_cast<uintptr_t>(basePtr);
  // We want to do wrapping arithmetic, but with a sign-extended
  // offset. To do this in C, we need to do signed promotion to get
  // the sign extension, but we need to perform arithmetic on unsigned values,
  // since signed overflow is undefined behavior.
  auto extendOffset = (uintptr_t)(intptr_t)offset;
  return base + extendOffset;
}

/// Measure the relative offset between two pointers. This measures
/// (referent - base) using wrapping arithmetic. The result is truncated if
/// Offset is smaller than a pointer, with an assertion that the
/// pre-truncation result is a sign extension of the truncated result.
template<typename Offset, typename A, typename B>
static inline Offset measureRelativeOffset(A *referent, B *base) {
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");

  auto distance = (uintptr_t)referent - (uintptr_t)base;
  // Truncate as unsigned, then wrap around to signed.
  auto truncatedDistance =
    (Offset)(typename std::make_unsigned<Offset>::type)distance;
  // Assert that the truncation didn't discard any non-sign-extended bits.
  assert((intptr_t)truncatedDistance == (intptr_t)distance
         && "pointers are too far apart to fit in offset type");
  return truncatedDistance;
}

} // namespace detail

/// A relative reference to an object stored in memory. The reference may be
/// direct or indirect, and uses the low bit of the (assumed at least
/// 2-byte-aligned) pointer to differentiate.
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t>
class RelativeIndirectPointer {
private:
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");

  /// The relative offset of the pointer's memory from the `this` pointer.
  /// This is an indirect reference.
  Offset RelativeOffset;

  /// RelativePointers should appear in statically-generated metadata. They
  /// shouldn't be constructed or copied.
  RelativeIndirectPointer() = delete;
  RelativeIndirectPointer(RelativeIndirectPointer &&) = delete;
  RelativeIndirectPointer(const RelativeIndirectPointer &) = delete;
  RelativeIndirectPointer &operator=(RelativeIndirectPointer &&)
    = delete;
  RelativeIndirectPointer &operator=(const RelativeIndirectPointer &)
    = delete;

public:
  const ValueTy *get() const & {
    // Check for null.
    if (Nullable && RelativeOffset == 0)
      return nullptr;

    uintptr_t address = detail::applyRelativeOffset(this, RelativeOffset);
    return *reinterpret_cast<const ValueTy * const *>(address);
  }

  /// A zero relative offset encodes a null reference.
  bool isNull() const & {
    return RelativeOffset == 0;
  }

  operator const ValueTy* () const & {
    return get();
  }

  const ValueTy *operator->() const & {
    return get();
  }
};

/// A relative reference to an object stored in memory. The reference may be
/// direct or indirect, and uses the low bit of the (assumed at least
/// 2-byte-aligned) pointer to differentiate.
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t, typename IndirectType = const ValueTy *>
class RelativeIndirectablePointer {
private:
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");
  
  /// The relative offset of the pointer's memory from the `this` pointer.
  /// If the low bit is clear, this is a direct reference; otherwise, it is
  /// an indirect reference.
  Offset RelativeOffsetPlusIndirect;

  /// RelativePointers should appear in statically-generated metadata. They
  /// shouldn't be constructed or copied.
  RelativeIndirectablePointer() = delete;
  RelativeIndirectablePointer(RelativeIndirectablePointer &&) = delete;
  RelativeIndirectablePointer(const RelativeIndirectablePointer &) = delete;
  RelativeIndirectablePointer &operator=(RelativeIndirectablePointer &&)
    = delete;
  RelativeIndirectablePointer &operator=(const RelativeIndirectablePointer &)
    = delete;

public:
  /// Allow construction and reassignment from an absolute pointer.
  /// These always produce a direct relative offset.
  RelativeIndirectablePointer(ValueTy *absolute)
  : RelativeOffsetPlusIndirect(
      Nullable && absolute == nullptr
        ? 0
        : detail::measureRelativeOffset<Offset>(absolute, this)) {
    if (!Nullable)
      assert(absolute != nullptr &&
             "constructing non-nullable relative pointer from null");
  }
  
  RelativeIndirectablePointer &operator=(ValueTy *absolute) & {
    if (!Nullable)
      assert(absolute != nullptr &&
             "constructing non-nullable relative pointer from null");
      
    RelativeOffsetPlusIndirect = Nullable && absolute == nullptr
      ? 0
      : detail::measureRelativeOffset<Offset>(absolute, this);
    return *this;
  }

  const ValueTy *get() const & {
    static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
                  "alignment of value and offset must be at least 2 to "
                  "make room for indirectable flag");
  
    // Check for null.
    if (Nullable && RelativeOffsetPlusIndirect == 0)
      return nullptr;
    
    Offset offsetPlusIndirect = RelativeOffsetPlusIndirect;
    uintptr_t address = detail::applyRelativeOffset(this,
                                                    offsetPlusIndirect & ~1);

    // If the low bit is set, then this is an indirect address. Otherwise,
    // it's direct.
    if (offsetPlusIndirect & 1) {
      return *reinterpret_cast<IndirectType const *>(address);
    } else {
      return reinterpret_cast<const ValueTy *>(address);
    }
  }

  /// A zero relative offset encodes a null reference.
  bool isNull() const & {
    return RelativeOffsetPlusIndirect == 0;
  }
  
  operator const ValueTy* () const & {
    return get();
  }

  const ValueTy *operator->() const & {
    return get();
  }
};

/// A relative reference to an aligned object stored in memory. The reference
/// may be direct or indirect, and uses the low bit of the (assumed at least
/// 2-byte-aligned) pointer to differentiate. The remaining low bits store
/// an additional tiny integer value.
template<typename ValueTy, typename IntTy, bool Nullable = false,
         typename Offset = int32_t,
         typename IndirectType = const ValueTy *>
class RelativeIndirectablePointerIntPair {
private:
  static_assert(std::is_integral<Offset>::value &&
                std::is_signed<Offset>::value,
                "offset type should be signed integer");

  /// The relative offset of the pointer's memory from the `this` pointer.
  /// If the low bit is clear, this is a direct reference; otherwise, it is
  /// an indirect reference.
  Offset RelativeOffsetPlusIndirectAndInt;

  /// RelativePointers should appear in statically-generated metadata. They
  /// shouldn't be constructed or copied.
  RelativeIndirectablePointerIntPair() = delete;
  RelativeIndirectablePointerIntPair(
                           RelativeIndirectablePointerIntPair &&) = delete;
  RelativeIndirectablePointerIntPair(
                      const RelativeIndirectablePointerIntPair &) = delete;
  RelativeIndirectablePointerIntPair& operator=(
                           RelativeIndirectablePointerIntPair &&) = delete;
  RelativeIndirectablePointerIntPair &operator=(
                      const RelativeIndirectablePointerIntPair &) = delete;

  // Retrieve the mask for the stored integer value.
  static Offset getIntMask() {
    return (alignof(Offset) - 1) & ~(Offset)0x01;
  }

public:
  const ValueTy *getPointer() const & {
    static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
                  "alignment of value and offset must be at least 2 to "
                  "make room for indirectable flag");

    Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());

    // Check for null.
    if (Nullable && offset == 0)
      return nullptr;

    Offset offsetPlusIndirect = offset;
    uintptr_t address = detail::applyRelativeOffset(this,
                                                    offsetPlusIndirect & ~1);

    // If the low bit is set, then this is an indirect address. Otherwise,
    // it's direct.
    if (offsetPlusIndirect & 1) {
      return *reinterpret_cast<const IndirectType *>(address);
    } else {
      return reinterpret_cast<const ValueTy *>(address);
    }
  }

  /// A zero relative offset encodes a null reference.
  bool isNull() const & {
    Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
    return offset == 0;
  }

  IntTy getInt() const & {
    return IntTy((RelativeOffsetPlusIndirectAndInt & getIntMask()) >> 1);
  }
};

/// A relative reference to a function, intended to reference private metadata
/// functions for the current executable or dynamic library image from
/// position-independent constant data.
template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointerImpl {
private:
  /// The relative offset of the function's entry point from *this.
  Offset RelativeOffset;

  /// RelativePointers should appear in statically-generated metadata. They
  /// shouldn't be constructed or copied.
  RelativeDirectPointerImpl() = delete;
  /// RelativePointers should appear in statically-generated metadata. They
  /// shouldn't be constructed or copied.
  RelativeDirectPointerImpl(RelativeDirectPointerImpl &&) = delete;
  RelativeDirectPointerImpl(const RelativeDirectPointerImpl &) = delete;
  RelativeDirectPointerImpl &operator=(RelativeDirectPointerImpl &&)
    = delete;
  RelativeDirectPointerImpl &operator=(const RelativeDirectPointerImpl &)
    = delete;


public:
  using ValueTy = T;
  using PointerTy = T*;

  // Allow construction and reassignment from an absolute pointer.
  RelativeDirectPointerImpl(PointerTy absolute)
    : RelativeOffset(Nullable && absolute == nullptr
                       ? 0
                       : detail::measureRelativeOffset<Offset>(absolute, this))
  {
    if (!Nullable)
      assert(absolute != nullptr &&
             "constructing non-nullable relative pointer from null");
  }
  explicit constexpr RelativeDirectPointerImpl(std::nullptr_t)
  : RelativeOffset (0) {
    static_assert(Nullable, "can't construct non-nullable pointer from null");
  }
  
  RelativeDirectPointerImpl &operator=(PointerTy absolute) & {
    if (!Nullable)
      assert(absolute != nullptr &&
             "constructing non-nullable relative pointer from null");
    RelativeOffset = Nullable && absolute == nullptr
      ? 0
      : detail::measureRelativeOffset<Offset>(absolute, this);
    return *this;
  }

  PointerTy get() const & {
    // Check for null.
    if (Nullable && RelativeOffset == 0)
      return nullptr;
    
    // The value is addressed relative to `this`.
    uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
    return reinterpret_cast<PointerTy>(absolute);
  }

  /// A zero relative offset encodes a null reference.
  bool isNull() const & {
    return RelativeOffset == 0;
  }
};

template <typename T, bool Nullable = true, typename Offset = int32_t,
          typename = void>
class RelativeDirectPointer;

/// A direct relative reference to an object that is not a function pointer.
template <typename T, bool Nullable, typename Offset>
class RelativeDirectPointer<T, Nullable, Offset,
    typename std::enable_if<!std::is_function<T>::value>::type>
    : private RelativeDirectPointerImpl<T, Nullable, Offset>
{
  using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
public:
  using super::get;
  using super::super;
  
  RelativeDirectPointer &operator=(T *absolute) & {
    super::operator=(absolute);
    return *this;
  }

  operator typename super::PointerTy() const & {
    return this->get();
  }

  const typename super::ValueTy *operator->() const & {
    return this->get();
  }

  using super::isNull;
};

/// A specialization of RelativeDirectPointer for function pointers,
/// allowing for calls.
template<typename T, bool Nullable, typename Offset>
class RelativeDirectPointer<T, Nullable, Offset,
    typename std::enable_if<std::is_function<T>::value>::type>
    : private RelativeDirectPointerImpl<T, Nullable, Offset>
{
  using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
public:
  using super::super;

  RelativeDirectPointer &operator=(T absolute) & {
    super::operator=(absolute);
    return *this;
  }

  typename super::PointerTy get() const & {
    auto ptr = this->super::get();
#if SWIFT_PTRAUTH
    if (Nullable && !ptr)
      return ptr;
    return ptrauth_sign_unauthenticated(ptr, ptrauth_key_function_pointer, 0);
#else
    return ptr;
#endif
  }

  operator typename super::PointerTy() const & {
    return this->get();
  }

  template <typename...ArgTy>
  typename std::result_of<T* (ArgTy...)>::type operator()(ArgTy...arg) const {
#if SWIFT_PTRAUTH
    return ptrauth_sign_unauthenticated(this->super::get(),
                                        ptrauth_key_function_pointer,
                                        0)(std::forward<ArgTy>(arg)...);
#else
    return this->super::get()(std::forward<ArgTy>(arg)...);
#endif
  }

  using super::isNull;
};

/// A direct relative reference to an aligned object, with an additional
/// tiny integer value crammed into its low bits.
template<typename PointeeTy, typename IntTy, bool Nullable = false,
         typename Offset = int32_t>
class RelativeDirectPointerIntPairImpl {
  Offset RelativeOffsetPlusInt;

  /// RelativePointers should appear in statically-generated metadata. They
  /// shouldn't be constructed or copied.
  RelativeDirectPointerIntPairImpl() = delete;
  RelativeDirectPointerIntPairImpl(RelativeDirectPointerIntPairImpl &&) = delete;
  RelativeDirectPointerIntPairImpl(const RelativeDirectPointerIntPairImpl &) = delete;
  RelativeDirectPointerIntPairImpl &operator=(RelativeDirectPointerIntPairImpl &&)
    = delete;
  RelativeDirectPointerIntPairImpl &operator=(const RelativeDirectPointerIntPairImpl&)
    = delete;

  static Offset getMask() {
    return alignof(Offset) - 1;
  }

public:
  using ValueTy = PointeeTy;
  using PointerTy = PointeeTy*;

  PointerTy getPointer() const & {
    Offset offset = (RelativeOffsetPlusInt & ~getMask());

    // Check for null.
    if (Nullable && offset == 0)
      return nullptr;

    // The value is addressed relative to `this`.
    uintptr_t absolute = detail::applyRelativeOffset(this, offset);
    return reinterpret_cast<PointerTy>(absolute);
  }

  IntTy getInt() const & {
    return IntTy(RelativeOffsetPlusInt & getMask());
  }
  
  Offset getOpaqueValue() const & {
    return RelativeOffsetPlusInt;
  }
};

/// A direct relative reference to an aligned object, with an additional
/// tiny integer value crammed into its low bits.
template<typename PointeeTy, typename IntTy, bool Nullable = false,
         typename Offset = int32_t, typename = void>
class RelativeDirectPointerIntPair;

template<typename PointeeTy, typename IntTy, bool Nullable, typename Offset>
class RelativeDirectPointerIntPair<PointeeTy, IntTy, Nullable, Offset,
    typename std::enable_if<!std::is_function<PointeeTy>::value>::type>
    : private RelativeDirectPointerIntPairImpl<PointeeTy, IntTy, Nullable, Offset>
{
  using super = RelativeDirectPointerIntPairImpl<PointeeTy, IntTy, Nullable, Offset>;
public:
  using super::getPointer;
  using super::getInt;
  using super::getOpaqueValue;
};

// Type aliases for "far" relative pointers, which need to be able to reach
// across the full address space instead of only across a single small-code-
// model image.

template<typename T, bool Nullable = false>
using FarRelativeIndirectablePointer =
  RelativeIndirectablePointer<T, Nullable, intptr_t>;

template<typename T, bool Nullable = false>
using FarRelativeDirectPointer = RelativeDirectPointer<T, Nullable, intptr_t>;

} // end namespace swift

#endif // SWIFT_BASIC_RELATIVEPOINTER_H
