//===--- FlaggedPointer.h - Explicit pointer tagging container --*- 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the FlaggedPointer class.
//
//===----------------------------------------------------------------------===//
//
#ifndef SWIFT_BASIC_FLAGGEDPOINTER_H
#define SWIFT_BASIC_FLAGGEDPOINTER_H

#include <algorithm>
#include <cassert>

#include "llvm/Support/Compiler.h"
#include "llvm/Support/PointerLikeTypeTraits.h"

namespace swift {

/// This class implements a pair of a pointer and boolean flag.
/// Like PointerIntPair, it represents this by mangling a bit into the low part
/// of the pointer, taking advantage of pointer alignment. Unlike
/// PointerIntPair, you must specify the bit position explicitly, instead of
/// automatically placing an integer into the highest bits possible.
///
/// Composing this with `PointerIntPair` is not allowed.
template <typename PointerTy,
          unsigned BitPosition,
          typename PtrTraits = llvm::PointerLikeTypeTraits<PointerTy>>
class FlaggedPointer {
  intptr_t Value;
  static_assert(PtrTraits::NumLowBitsAvailable > 0,
                "Not enough bits to store flag at this position");
  enum : uintptr_t {
    FlagMask = (uintptr_t)1 << BitPosition,
    PointerBitMask = ~FlagMask
  };
public:
  FlaggedPointer() : Value(0) {}
  FlaggedPointer(PointerTy PtrVal, bool FlagVal) {
    setPointerAndFlag(PtrVal, FlagVal);
  }
  explicit FlaggedPointer(PointerTy PtrVal) {
    initWithPointer(PtrVal);
  }

  /// Returns the underlying pointer with the flag bit masked out.
  PointerTy getPointer() const {
    return PtrTraits::getFromVoidPointer(
      reinterpret_cast<void*>(Value & PointerBitMask));
  }

  void setPointer(PointerTy PtrVal) {
    intptr_t PtrWord = reinterpret_cast<intptr_t>(
      PtrTraits::getAsVoidPointer(PtrVal));
    assert((PtrWord & ~PointerBitMask) == 0 &&
           "Pointer is not sufficiently aligned");
    Value = PtrWord | (Value & ~PointerBitMask);
  }

  bool getFlag() const {
    return (bool)(Value & FlagMask);
  }

  void setFlag(bool FlagVal) {
    intptr_t FlagWord = static_cast<intptr_t>(FlagVal);

    Value &= ~FlagMask;
    Value |= FlagWord << BitPosition;
  }

  /// Set the pointer value and assert if it overlaps with
  /// the flag's bit position.
  void initWithPointer(PointerTy PtrVal) {
    intptr_t PtrWord = reinterpret_cast<intptr_t>(
      PtrTraits::getAsVoidPointer(PtrVal));
    assert((PtrWord & ~PointerBitMask) == 0 &&
      "Pointer is not sufficiently aligned");
    Value = PtrWord;
  }

  /// Set the pointer value, set the flag, and assert
  /// if the pointer's value would overlap with the flag's
  /// bit position.
  void setPointerAndFlag(PointerTy PtrVal, bool FlagVal) {
    intptr_t PtrWord = reinterpret_cast<intptr_t>(
      PtrTraits::getAsVoidPointer(PtrVal));
    assert((PtrWord & ~PointerBitMask) == 0 &&
      "Pointer is not sufficiently aligned");
    intptr_t FlagWord = static_cast<intptr_t>(FlagVal);

    Value = PtrWord | (FlagWord << BitPosition);
  }

  PointerTy const *getAddrOfPointer() const {
    return const_cast<FlaggedPointer *>(this)->getAddrOfPointer();
  }

  PointerTy *getAddrOfPointer() {
    assert(Value == reinterpret_cast<intptr_t>(getPointer()) &&
      "Can only return the address if IntBits is cleared and "
      "PtrTraits doesn't change the pointer");
    return reinterpret_cast<PointerTy *>(&Value);
  }

  /// Get the raw pointer value for the underlying pointer
  /// including its flag value.
  void *getOpaqueValue() const {
    return reinterpret_cast<void*>(Value);
  }

  void setFromOpaqueValue(void *Val) {
    Value = reinterpret_cast<intptr_t>(Val);
  }

  static FlaggedPointer getFromOpaqueValue(const void *V) {
    FlaggedPointer P;
    P.setFromOpaqueValue(const_cast<void *>(V));
    return P;
  }

  bool operator==(const FlaggedPointer &RHS) const {
    return Value == RHS.Value;
  }
  bool operator!=(const FlaggedPointer &RHS) const {
    return Value != RHS.Value;
  }
  bool operator<(const FlaggedPointer &RHS) const {
    return Value < RHS.Value;
  }
  bool operator>(const FlaggedPointer &RHS) const {
    return Value > RHS.Value;
  }
  bool operator<=(const FlaggedPointer &RHS) const {
    return Value <= RHS.Value;
  }
  bool operator>=(const FlaggedPointer &RHS) const {
    return Value >= RHS.Value;
  }
};

} // end namespace swift

// Teach SmallPtrSet that FlaggedPointer is "basically a pointer".
template <typename PointerTy, unsigned BitPosition, typename PtrTraits>
struct llvm::PointerLikeTypeTraits<
  swift::FlaggedPointer<PointerTy, BitPosition, PtrTraits>> {
public:
  static inline void *
    getAsVoidPointer(const swift::FlaggedPointer<PointerTy, BitPosition> &P) {
      return P.getOpaqueValue();
  }
  static inline swift::FlaggedPointer<PointerTy, BitPosition>
    getFromVoidPointer(void *P) {
      return swift::FlaggedPointer<PointerTy, BitPosition>::getFromOpaqueValue(P);
  }
  static inline swift::FlaggedPointer<PointerTy, BitPosition>
    getFromVoidPointer(const void *P) {
      return swift::FlaggedPointer<PointerTy, BitPosition>::getFromOpaqueValue(P);
  }
  enum {
    NumLowBitsAvailable = (BitPosition >= PtrTraits::NumLowBitsAvailable)
      ? PtrTraits::NumLowBitsAvailable
      : (std::min(int(BitPosition + 1),
        int(PtrTraits::NumLowBitsAvailable)) - 1)
  };
};

#endif // SWIFT_BASIC_FLAGGEDPOINTER_H
