diff --git a/llvm/include/llvm/Support/Handle.h b/llvm/include/llvm/Support/Handle.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/Handle.h @@ -0,0 +1,305 @@ +//===- Handle.h - Handles for working on IR with type-erasure ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines a set of classes for working with type-erased opaque +/// handles. It also defines handle types for core SSA-form IR concepts: +/// +/// - \ref BlockHandle +/// - \ref InstructionHandle +/// - \ref SsaValueHandle +/// +/// To assist with type-safety, conversions between handles and concrete +/// "references" to the underlying objects are designed to be performed only via +/// the static methods provided by instantiations of \ref HandleWrapper. +/// +/// Note that the concrete "references" are generally expected to \em not be +/// C++ reference types. They are often pointers, but could also be value +/// types that act like references, e.g. mlir::Value. +/// +/// Handle types are expected to be pointer-sized and built using the +/// \ref Handle class template, which uses the type system to enforce that +/// conversion can only happen via \ref HandleWrapper (or the \ref handle_detail +/// namespace). +/// +/// Instantiations of \ref HandleWrapper can be explicitly named in non-generic +/// code (e.g., llvm::MachineSsaContext::Wrapper) or via some other traits +/// mechanism (e.g., typename SsaContextFor::Wrapper). +/// +/// An instantiation can also obtained be requested explicitly +/// (HandleWrapper<...>), but this should be avoided where possible since it can +/// introduce type errors (e.g., accidentally declaring a wrapper that will wrap +/// a value reference in a BlockHandle). +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_HANDLE_H +#define LLVM_SUPPORT_HANDLE_H + +#include "llvm/ADT/DenseMapInfo.h" + +namespace llvm { + +namespace handle_detail { + +template class WrapperImplBase; + +} + +template class Handle; + +template bool operator==(Handle lhs, Handle rhs); +template bool operator<(Handle lhs, Handle rhs); + +/// \brief Standard, pointer-sized type-erased handle to an object +/// (e.g. basic block, value). +/// +/// Use HandleWrapper::{wrapRef, unwrapRef} to wrap and unwrap concrete object +/// references. +/// +/// The most common use is to hold a pointer, but arbitrary uintptr_t values +/// may be stored. Note that 0, -1, and -2 have special interpretations: +/// * 0 / nullptr: default-constructed value; evaluates to false in boolean +/// contexts. +/// * -1: dense map empty marker +/// * -2: dense map tombstone +template class Handle { + friend struct DenseMapInfo>; + friend class handle_detail::WrapperImplBase; + template friend bool operator==(Handle, Handle); + template friend bool operator<(Handle, Handle); + + void *ptr = nullptr; + + explicit Handle(void *ptr) : ptr(ptr) {} + void *get() const { return ptr; } + +public: + using Tag = TagT; + + Handle() = default; + + explicit operator bool() const { return ptr != nullptr; } +}; + +template bool operator==(Handle lhs, Handle rhs) { + return lhs.get() == rhs.get(); +} + +template bool operator!=(Handle lhs, Handle rhs) { + return !(lhs == rhs); +} + +template bool operator<(Handle lhs, Handle rhs) { + return lhs.get() < rhs.get(); +} + +template struct DenseMapInfo> { + using Type = Handle; + + static Type getEmptyKey() { + uintptr_t val = static_cast(-1); + return Type(reinterpret_cast(val)); + } + + static Type getTombstoneKey() { + uintptr_t val = static_cast(-2); + return Type(reinterpret_cast(val)); + } + + static unsigned getHashValue(Type val) { + return llvm::DenseMapInfo::getHashValue(val.get()); + } + static bool isEqual(Type lhs, Type rhs) { return lhs == rhs; } +}; + +namespace handle_detail { + +/// \brief Implementation of wrapRef/unwrapRef for a specific HandleType/RefType +/// pair +/// +/// Specializing this template allows support of concrete reference types that +/// are non-pointers (e.g., WrapperImpl). +/// +/// Specializations should be derived from \ref WrapperImplBase. +template class WrapperImpl; + +/// \brief Base class for WrapperImpl specialization whose HandleType is +/// a Handle +template class WrapperImplBase { +protected: + using Type = Handle; + + static Type make(void *ptr) { return Type(ptr); } + static void *get(Type handle) { return handle.get(); } +}; + +/// Default specialization of WrapperImpl for the common case where the RefType +/// is a pointer. +template +class WrapperImpl, PointeeType *> : WrapperImplBase { +public: + static Handle wrapRef(PointeeType *ref) { + return WrapperImplBase::make(ref); + } + static PointeeType *unwrapRef(Handle handle) { + return static_cast(WrapperImplBase::get(handle)); + } +}; + +/// Variadic template underlying \ref HandleWrapper +template class Wrapper; + +template <> class Wrapper<> {}; + +template +class Wrapper : public WrapperImpl {}; + +template +class Wrapper : Wrapper, + WrapperImpl { +public: + using Wrapper::wrapRef; + using Wrapper::unwrapRef; + using WrapperImpl::wrapRef; + using WrapperImpl::unwrapRef; +}; + +template +struct unwrapping_iterator; + +template +using unwrapping_iterator_base = iterator_adaptor_base< + unwrapping_iterator, BaseIteratorT, + typename std::iterator_traits::iterator_category, + // value_type + decltype(BaseWrapper::unwrapRef(*std::declval())), + typename std::iterator_traits::difference_type, + // pointer (not really usable, but we need to put something here) + decltype(BaseWrapper::unwrapRef(*std::declval())) *, + // reference (not a true reference, because operator* doesn't return one) + decltype(BaseWrapper::unwrapRef(*std::declval()))>; + +/// \brief Adapt an iterator over handles to an iterator over concrete +/// references. +template +struct unwrapping_iterator + : unwrapping_iterator_base { + using Base = unwrapping_iterator_base; + + unwrapping_iterator() = default; + explicit unwrapping_iterator(BaseIteratorT &&it) + : Base(std::forward(it)) {} + + auto operator*() const { return BaseWrapper::unwrapRef(*this->I); } +}; + +template +struct wrapping_iterator; + +template +using wrapping_iterator_base = iterator_adaptor_base< + wrapping_iterator, BaseIteratorT, + typename std::iterator_traits::iterator_category, + // value_type + decltype(BaseWrapper::wrapRef(*std::declval())), + typename std::iterator_traits::difference_type, + // pointer (not really usable, but we need to put something here) + decltype(BaseWrapper::wrapRef(*std::declval())) *, + // reference (not a true reference, because operator* doesn't return one) + decltype(BaseWrapper::wrapRef(*std::declval()))>; + +/// \brief Adapt an iterator over concrete references to an iterator over +/// handles. +template +struct wrapping_iterator : wrapping_iterator_base { + using Base = wrapping_iterator_base; + + wrapping_iterator() = default; + explicit wrapping_iterator(BaseIteratorT &&it) + : Base(std::forward(it)) {} + + auto operator*() const { return BaseWrapper::wrapRef(*this->I); } +}; + +} // namespace handle_detail + +/// \brief Access to static wrapping/unwrapping functions for a related set of +/// "reference" types that can be wrapped in handles. +/// +/// Specializations of this template are used at the boundary between an +/// algorithm that is written generically using opaque handles and an outside +/// world that makes use of concrete, non-opaque handle types. +/// +/// The template is instantiated as +/// HandleWrapper. The +/// resulting wrapping/unwrapping methods will wrap RefType1 in a HandleType1 +/// etc. +/// +/// This class also provides {wrap,unwrap}{Iterator,Range} helper functions for +/// additional convenience. +template +class HandleWrapper : public handle_detail::Wrapper { + using Base = handle_detail::Wrapper; + +public: + // wrapRef, unwrapRef inherited from Base. + + template + using Ref = decltype(unwrapRef(std::declval())); + template + using Handle = decltype(wrapRef(std::declval())); + + /// Convert an iterator of opaque handles (e.g., SsaValueHandle) into an + /// iterator of concrete references (e.g., llvm::Value*). + template static auto unwrapIterator(IteratorT &&it) { + return handle_detail::unwrapping_iterator( + std::forward(it)); + } + + /// Convert a range of opaque handles (e.g., SsaValueHandle) into a range of + /// concrete references (e.g., llvm::Value*). + template static auto unwrapRange(RangeT &&range) { + return llvm::make_range( + unwrapIterator(adl_begin(std::forward(range))), + unwrapIterator(adl_end(std::forward(range)))); + } + + /// Convert an iterator of concrete references (e.g., llvm::Value*) into an + /// iterator of opaque handles (e.g., SsaValueHandle). + template static auto wrapIterator(IteratorT &&it) { + return handle_detail::wrapping_iterator( + std::forward(it)); + } + + /// Convert a range of concrete references (e.g., llvm::Value*) into a range + /// of opaque handles (e.g., SsaValueHandle). + template static auto wrapRange(RangeT &&range) { + return llvm::make_range( + wrapIterator(adl_begin(std::forward(range))), + wrapIterator(adl_end(std::forward(range)))); + } +}; + +class BlockHandleTag; +class InstructionHandleTag; +class SsaValueHandleTag; + +/// Standard opaque handle to "blocks" that are typically basic blocks of an +/// IR, but could be more general nodes of a graph (used in dominator trees). +using BlockHandle = Handle; + +/// Standard opaque handle to an instruction in an SSA-form IR. +using InstructionHandle = Handle; + +/// Standard opaque handle to an SSA value. +using SsaValueHandle = Handle; + +} // namespace llvm + +#endif // LLVM_SUPPORT_HANDLE_H