Merge remote-tracking branch 'origin/master' into swift-3-api-guidelines

This commit is contained in:
Dmitri Gribenko
2016-02-17 14:40:05 -08:00
450 changed files with 8406 additions and 5202 deletions

View File

@@ -0,0 +1,298 @@
//===--- ImmutablePointerSet.h ----------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This file contains an implementation of a bump ptr allocated immutable
/// pointer set.
///
/// The target of this data structure are sets of pointers (with N < 100) that
/// are propagated through many basic blocks. These pointer sets will be merged
/// and copied far more than being created from an array.
///
/// Thus we assume the following constraints:
///
/// 1. Our set operations are purely additive. Given a set, one can only add
/// elements to it. One can not remove elements to it. This means we only
/// support construction of sets from arrays and concatenation of pointer sets.
///
/// 2. Our sets must always be ordered and be able to be iterated over
/// efficiently in that order.
///
/// 3. An O(log(n)) set contains method.
///
/// Beyond these constraints, we would like for our data structure to have the
/// following properties for performance reasons:
///
/// 1. Its memory should be bump ptr allocated. We like fast allocation.
///
/// 2. No destructors need to be called when the bump ptr allocator is being
/// destroyed. We like fast destruction and do not want to have to iterate over
/// potentially many of these sets and invoke destructors.
///
/// Thus our design is to represent our sets as bump ptr allocated arrays whose
/// elements are sorted and uniqued. The actual uniquing of the arrays
/// themselves is performed via folding set node.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_IMMUTABLEPOINTERSET_H
#define SWIFT_BASIC_IMMUTABLEPOINTERSET_H
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/NullablePtr.h"
#include "llvm/Support/Allocator.h"
#include "llvm/ADT/FoldingSet.h"
#include <algorithm>
#include <type_traits>
namespace swift {
template <typename PtrTy> class ImmutablePointerSetFactory;
/// An immutable set of pointers. It is backed by a tail allocated sorted array
/// ref.
template <typename T> class ImmutablePointerSet : public llvm::FoldingSetNode {
using PtrTy = typename std::add_pointer<T>::type;
friend class ImmutablePointerSetFactory<T>;
NullablePtr<ImmutablePointerSetFactory<T>> ParentFactory;
ArrayRef<PtrTy> Data;
ImmutablePointerSet(ImmutablePointerSetFactory<T> *ParentFactory,
ArrayRef<PtrTy> NewData)
: ParentFactory(ParentFactory), Data(NewData) {}
public:
~ImmutablePointerSet() = default;
ImmutablePointerSet(const ImmutablePointerSet &) = default;
ImmutablePointerSet(ImmutablePointerSet &&) = default;
ImmutablePointerSet &operator=(const ImmutablePointerSet &) = default;
ImmutablePointerSet &operator=(ImmutablePointerSet &&) = default;
bool operator==(const ImmutablePointerSet<T> &P) const {
// If this and P have different sizes, we can not be equivalent.
if (size() != P.size())
return false;
// Ok, we now know that both have the same size. If one is empty, the other
// must be as well, implying equality.
if (empty())
return true;
// Ok, both sets are not empty and the same number of elements. Compare
// element wise.
return std::equal(begin(), end(), P.begin());
}
bool operator!=(const ImmutablePointerSet<T> &P) const {
return !(*this == P);
}
unsigned count(PtrTy Ptr) const {
// This returns the first element >= Ptr. Since we know that our array is
// sorted and uniqued, Ptr must be that element.
auto LowerBound = std::lower_bound(begin(), end(), Ptr);
// If Ptr is > than everything in the array, then we obviously have 0.
if (LowerBound == end())
return 0;
// Then check if Ptr is > or Ptr is ==. We only have Ptr if we have == to.
return *LowerBound == Ptr;
}
using iterator = typename decltype(Data)::iterator;
iterator begin() const { return Data.begin(); }
iterator end() const { return Data.end(); }
unsigned size() const { return Data.size(); }
bool empty() const { return Data.empty(); }
void Profile(llvm::FoldingSetNodeID &ID) const {
assert(!Data.empty() && "Should not profile empty ImmutablePointerSet");
for (PtrTy P : Data) {
ID.AddPointer(P);
}
}
ImmutablePointerSet<T> *merge(ImmutablePointerSet<T> *Other) {
if (empty())
return Other;
if (Other->empty())
return this;
assert(Other->ParentFactory.get() == ParentFactory.get());
return ParentFactory.get()->merge(this, Other);
}
};
template <typename T> class ImmutablePointerSetFactory {
using PtrTy = typename std::add_pointer<T>::type;
using PtrSet = ImmutablePointerSet<T>;
static constexpr unsigned AllocAlignment =
(alignof(PtrSet) > alignof(PtrTy)) ? alignof(PtrSet) : alignof(PtrTy);
llvm::BumpPtrAllocator &Allocator;
llvm::FoldingSetVector<PtrSet> Set;
static PtrSet EmptyPtrSet;
public:
ImmutablePointerSetFactory(llvm::BumpPtrAllocator &A) : Allocator(A), Set() {}
ImmutablePointerSetFactory(const ImmutablePointerSetFactory &) = delete;
ImmutablePointerSetFactory(ImmutablePointerSetFactory &&) = delete;
ImmutablePointerSetFactory &
operator=(const ImmutablePointerSetFactory &) = delete;
ImmutablePointerSetFactory &operator=(ImmutablePointerSetFactory &&) = delete;
// We use a sentinel value here so that we can create an empty value
// statically.
static PtrSet *getEmptySet() { return &EmptyPtrSet; }
/// Given a sorted and uniqued list \p Array, return the ImmutablePointerSet
/// containing Array. Asserts if \p Array is not sorted and uniqued.
PtrSet *get(ArrayRef<PtrTy> Array) {
if (Array.empty())
return ImmutablePointerSetFactory::getEmptySet();
// We expect our users to sort/unique the input array. This is because doing
// it here would either require us to allocate more memory than we need or
// write into the input Array, which we don't want.
assert(is_sorted_and_uniqued(Array));
llvm::FoldingSetNodeID ID;
for (PtrTy Ptr : Array) {
ID.AddPointer(Ptr);
}
void *InsertPt;
if (auto *PSet = Set.FindNodeOrInsertPos(ID, InsertPt)) {
return PSet;
}
size_t NumElts = Array.size();
size_t MemSize = sizeof(PtrSet) + sizeof(PtrTy) * NumElts;
// Allocate the memory.
auto *Mem =
reinterpret_cast<PtrSet *>(Allocator.Allocate(MemSize, AllocAlignment));
// Copy in the pointers into the tail allocated memory. We do not need to do
// any sorting/uniquing ourselves since we assume that our users perform
// this task for us.
MutableArrayRef<PtrTy> DataMem(reinterpret_cast<PtrTy *>(&Mem[1]), NumElts);
std::copy(Array.begin(), Array.end(), DataMem.begin());
// Allocate the new node and insert it into the Set.
auto *NewNode = new (Mem) PtrSet(this, DataMem);
Set.InsertNode(NewNode, InsertPt);
return NewNode;
}
PtrSet *merge(PtrSet *S1, ArrayRef<PtrTy> S2) {
if (S1->empty())
return get(S2);
if (S2.empty())
return S1;
// We assume that S2 is sorted and uniqued.
assert(is_sorted_and_uniqued(S2));
llvm::FoldingSetNodeID ID;
// We know that both of our pointer sets are sorted, so we can essentially
// perform a sorted set merging algorithm to create the ID. We also count
// the number of unique elements for allocation purposes.
unsigned NumElts = 0;
set_union_for_each(*S1, S2, [&ID, &NumElts](const PtrTy Ptr) -> void {
ID.AddPointer(Ptr);
NumElts++;
});
// If we find our ID then continue.
void *InsertPt;
if (auto *PSet = Set.FindNodeOrInsertPos(ID, InsertPt)) {
return PSet;
}
unsigned MemSize = sizeof(PtrSet) + sizeof(PtrTy) * NumElts;
// Allocate the memory.
auto *Mem =
reinterpret_cast<PtrSet *>(Allocator.Allocate(MemSize, AllocAlignment));
// Copy in the union of the two pointer sets into the tail allocated
// memory. Since we know that our sorted arrays are uniqued, we can use
// set_union to get the uniqued sorted array that we want.
MutableArrayRef<PtrTy> DataMem(reinterpret_cast<PtrTy *>(&Mem[1]), NumElts);
std::set_union(S1->begin(), S1->end(), S2.begin(), S2.end(),
DataMem.begin());
// Allocate the new node, insert it into the Set, and return it.
auto *NewNode = new (Mem) PtrSet(this, DataMem);
Set.InsertNode(NewNode, InsertPt);
return NewNode;
}
PtrSet *merge(PtrSet *S1, PtrSet *S2) {
// If either S1 or S2 are the empty PtrSet, just return S2 or S1.
if (S1->empty())
return S2;
if (S2->empty())
return S1;
llvm::FoldingSetNodeID ID;
// We know that both of our pointer sets are sorted, so we can essentially
// perform a sorted set merging algorithm to create the ID. We also count
// the number of unique elements for allocation purposes.
unsigned NumElts = 0;
set_union_for_each(*S1, *S2, [&ID, &NumElts](const PtrTy Ptr) -> void {
ID.AddPointer(Ptr);
NumElts++;
});
// If we find our ID then continue.
void *InsertPt;
if (auto *PSet = Set.FindNodeOrInsertPos(ID, InsertPt)) {
return PSet;
}
unsigned MemSize = sizeof(PtrSet) + sizeof(PtrTy) * NumElts;
// Allocate the memory.
auto *Mem =
reinterpret_cast<PtrSet *>(Allocator.Allocate(MemSize, AllocAlignment));
// Copy in the union of the two pointer sets into the tail allocated
// memory. Since we know that our sorted arrays are uniqued, we can use
// set_union to get the uniqued sorted array that we want.
MutableArrayRef<PtrTy> DataMem(reinterpret_cast<PtrTy *>(&Mem[1]), NumElts);
std::set_union(S1->begin(), S1->end(), S2->begin(), S2->end(),
DataMem.begin());
// Allocate the new node, insert it into the Set, and return it.
auto *NewNode = new (Mem) PtrSet(this, DataMem);
Set.InsertNode(NewNode, InsertPt);
return NewNode;
}
};
template <typename T>
ImmutablePointerSet<T> ImmutablePointerSetFactory<T>::EmptyPtrSet =
ImmutablePointerSet<T>(nullptr, {});
} // end swift namespace
#endif

View File

@@ -158,7 +158,7 @@ namespace swift {
/// Enable the Swift 3 migration via Fix-Its.
bool Swift3Migration = false;
/// Sets the target we are building for and updates configuration options
/// Sets the target we are building for and updates platform conditions
/// to match.
///
/// \returns A pair - the first element is true if the OS was invalid.
@@ -187,63 +187,58 @@ namespace swift {
return clang::VersionTuple(major, minor, revision);
}
/// Implicit target configuration options. There are currently three
/// supported target configuration values:
/// os - The active os target (OSX or IOS)
/// arch - The active arch target (X64, I386, ARM, ARM64)
/// _runtime - Runtime support (_ObjC or _Native)
void addTargetConfigOption(StringRef Name, StringRef Value) {
/// Sets an implicit platform condition.
///
/// There are currently three supported platform conditions:
/// - os: The active os target (OSX or iOS)
/// - arch: The active arch target (x86_64, i386, arm, arm64)
/// - _runtime: Runtime support (_ObjC or _Native)
void addPlatformConditionValue(StringRef Name, StringRef Value) {
assert(!Name.empty() && !Value.empty());
TargetConfigOptions.push_back(std::make_pair(Name, Value));
PlatformConditionValues.emplace_back(Name, Value);
}
/// Removes all configuration options added with addTargetConfigOption.
void clearAllTargetConfigOptions() {
TargetConfigOptions.clear();
/// Removes all values added with addPlatformConditionValue.
void clearAllPlatformConditionValues() {
PlatformConditionValues.clear();
}
/// Returns the value for the given target configuration or an empty string.
StringRef getTargetConfigOption(StringRef Name) const;
/// Explicit build configuration options, initialized via the '-D'
/// Returns the value for the given platform condition or an empty string.
StringRef getPlatformConditionValue(StringRef Name) const;
/// Explicit conditional compilation flags, initialized via the '-D'
/// compiler flag.
void addBuildConfigOption(StringRef Name) {
void addCustomConditionalCompilationFlag(StringRef Name) {
assert(!Name.empty());
BuildConfigOptions.push_back(Name);
CustomConditionalCompilationFlags.push_back(Name);
}
/// Determines if a given build configuration has been defined.
bool hasBuildConfigOption(StringRef Name) const;
/// Determines if a given conditional compilation flag has been set.
bool isCustomConditionalCompilationFlagSet(StringRef Name) const;
ArrayRef<std::pair<std::string, std::string>>
getTargetConfigOptions() const {
return TargetConfigOptions;
getPlatformConditionValues() const {
return PlatformConditionValues;
}
ArrayRef<std::string> getBuildConfigOptions() const {
return BuildConfigOptions;
ArrayRef<std::string> getCustomConditionalCompilationFlags() const {
return CustomConditionalCompilationFlags;
}
/// The constant list of supported os build configuration arguments.
static const std::vector<std::string> SupportedOSBuildConfigArguments;
/// Returns true if the os build configuration argument represents
/// Returns true if the 'os' platform condition argument represents
/// a supported target operating system.
static bool isOSBuildConfigSupported(StringRef OSName);
static bool isPlatformConditionOSSupported(StringRef OSName);
/// The constant list of supported arch build configuration arguments.
static const std::vector<std::string> SupportedArchBuildConfigArguments;
/// Returns true if the arch build configuration argument represents
/// Returns true if the 'arch' platform condition argument represents
/// a supported target architecture.
static bool isArchBuildConfigSupported(StringRef ArchName);
static bool isPlatformConditionArchSupported(StringRef ArchName);
private:
llvm::SmallVector<std::pair<std::string, std::string>, 2>
TargetConfigOptions;
llvm::SmallVector<std::string, 2> BuildConfigOptions;
llvm::SmallVector<std::pair<std::string, std::string>, 3>
PlatformConditionValues;
llvm::SmallVector<std::string, 2> CustomConditionalCompilationFlags;
};
}
#endif // LLVM_SWIFT_LANGOPTIONS_H
#endif // SWIFT_LANGOPTIONS_H

View File

@@ -19,6 +19,7 @@
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <functional>
@@ -101,6 +102,84 @@ inline void for_each3(const Container1 &c1, const Container2 &c2,
for_each3(c1.begin(), c1.end(), c2.begin(), c3.begin(), f);
}
/// The equivalent of std::for_each, but visits the set union of two sorted
/// lists without allocating additional memory.
///
/// This has the following requirements:
///
/// 1. The ranges must be sorted.
/// 2. The elements must have the same type.
/// 3. There are no duplicate elements.
/// 4. All elements must be comparable with std::less.
template <typename InputIt1, typename InputIt2, typename BinaryFunction>
inline void set_union_for_each(InputIt1 I1, InputIt1 E1, InputIt2 I2,
InputIt2 E2, BinaryFunction f) {
static_assert(
std::is_same<
typename std::iterator_traits<InputIt1>::value_type,
typename std::iterator_traits<InputIt2>::value_type
>::value,
"Expected both iterator types to have the same underlying value type");
using RefTy = typename std::iterator_traits<InputIt1>::reference;
while (true) {
// If we have reached the end of either list, visit the rest of the other
// list, We do not need to worry about duplicates since each array we know
// is unique.
if (I1 == E1) {
std::for_each(I2, E2, f);
return;
}
if (I2 == E2) {
std::for_each(I1, E1, f);
return;
}
// If I1 < I2, then visit I1 and continue.
if (std::less<RefTy>()(*I1, *I2)) {
f(*I1);
++I1;
continue;
}
// If I2 < I1, visit I2 and continue.
if (std::less<RefTy>()(*I2, *I1)) {
f(*I2);
++I2;
continue;
}
// Otherwise, we know that I1 and I2 equal. We know that we can only have
// one of each element in each list, so we can just visit I1 and continue.
f(*I1);
++I1;
++I2;
}
}
/// A container adapter for set_union_for_each.
///
/// To see the requirements upon the containers, please see the iterator based
/// set_union_for_each.
template <typename Container1, typename Container2, typename UnaryFunction>
inline void set_union_for_each(const Container1 &C1, const Container2 &C2,
UnaryFunction f) {
// Make sure that our iterators have the same value type.
static_assert(
std::is_same<
typename std::iterator_traits<
typename Container1::iterator
>::value_type,
typename std::iterator_traits<
typename Container2::iterator
>::value_type
>::value,
"Expected both containers to have the same iterator value type");
set_union_for_each(C1.begin(), C1.end(), C2.begin(), C2.end(), f);
}
/// @}
/// A range of iterators.
@@ -573,6 +652,45 @@ void sortUnique(
C.erase(std::unique(C.begin(), C.end()), C.end());
}
/// Returns true if [II, IE) is a sorted and uniqued array. Returns false
/// otherwise.
template <typename IterTy>
inline bool is_sorted_and_uniqued(IterTy II, IterTy IE) {
using RefTy = typename std::iterator_traits<IterTy>::reference;
// The empty list is always sorted and uniqued.
if (II == IE)
return true;
// The list of one element is always sorted and uniqued.
auto LastI = II;
++II;
if (II == IE)
return true;
// Otherwise, until we reach the end of the list...
while (II != IE) {
// If LastI is greater than II then we know that our array is not sorted. If
// LastI equals II, then we know that our array is not unique. If both of
// those are conditions are false, then visit the next iterator element.
if (std::greater_equal<RefTy>()(*LastI, *II)) {
// Return false otherwise.
return false;
}
LastI = II;
++II;
}
// Success!
return true;
}
template <typename Container>
inline bool is_sorted_and_uniqued(const Container &C) {
return is_sorted_and_uniqued(C.begin(), C.end());
}
} // end namespace swift
#endif

View File

@@ -14,8 +14,9 @@
// camelCase names.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_STRINGEXTRAS_HPP
#define SWIFT_BASIC_STRINGEXTRAS_HPP
#ifndef SWIFT_BASIC_STRINGEXTRAS_H
#define SWIFT_BASIC_STRINGEXTRAS_H
#include "swift/Basic/LLVM.h"
#include "swift/Basic/OptionSet.h"

View File

@@ -90,10 +90,10 @@ public:
return Components.empty();
}
/// Parse a _compiler_version build configuration from source code.
/// Parse a version in the form used by the _compiler_version \#if condition.
static Version parseCompilerVersionString(llvm::StringRef VersionString,
SourceLoc Loc,
DiagnosticEngine *Diags);
SourceLoc Loc,
DiagnosticEngine *Diags);
/// Parse a generic version string of the format [0-9]+(.[0-9]+)*
///