| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| .. title:: clang-tidy - bugprone-nondeterministic-pointer-iteration-order | ||
|
|
||
| bugprone-nondeterministic-pointer-iteration-order | ||
| ================================================= | ||
|
|
||
| Finds nondeterministic usages of pointers in unordered containers. | ||
|
|
||
| One canonical example is iteration across a container of pointers. | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| { | ||
| int a = 1, b = 2; | ||
| std::unordered_set<int *> UnorderedPtrSet = {&a, &b}; | ||
| for (auto i : UnorderedPtrSet) | ||
| f(i); | ||
| } | ||
| Another such example is sorting a container of pointers. | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| { | ||
| int a = 1, b = 2; | ||
| std::vector<int *> VectorOfPtr = {&a, &b}; | ||
| std::sort(VectorOfPtr.begin(), VectorOfPtr.end()); | ||
| } | ||
| Iteration of a containers of pointers may present the order of different | ||
| pointers differently across different runs of a program. In some cases this | ||
| may be acceptable behavior, in others this may be unexpected behavior. This | ||
| check is advisory for this reason. | ||
|
|
||
| This check only detects range-based for loops over unordered sets and maps. It | ||
| also detects calls sorting-like algorithms on containers holding pointers. | ||
| Other similar usages will not be found and are false negatives. | ||
|
|
||
| Limitations: | ||
|
|
||
| * This check currently does not check if a nondeterministic iteration order is | ||
| likely to be a mistake, and instead marks all such iterations as bugprone. | ||
| * std::reference_wrapper is not considered yet. | ||
| * Only for loops are considered, other iterators can be included in | ||
| improvements. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #ifndef _SIM_ALGORITHM | ||
| #define _SIM_ALGORITHM | ||
|
|
||
| #pragma clang system_header | ||
|
|
||
| namespace std { | ||
|
|
||
| template<class ForwardIt> | ||
| bool is_sorted(ForwardIt first, ForwardIt last); | ||
|
|
||
| template <class RandomIt> | ||
| void nth_element(RandomIt first, RandomIt nth, RandomIt last); | ||
|
|
||
| template<class RandomIt> | ||
| void partial_sort(RandomIt first, RandomIt middle, RandomIt last); | ||
|
|
||
| template<class RandomIt> | ||
| void sort (RandomIt first, RandomIt last); | ||
|
|
||
| template<class RandomIt> | ||
| void stable_sort(RandomIt first, RandomIt last); | ||
|
|
||
| template<class BidirIt, class UnaryPredicate> | ||
| BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p); | ||
|
|
||
| template<class BidirIt, class UnaryPredicate> | ||
| BidirIt stable_partition(BidirIt first, BidirIt last, UnaryPredicate p); | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_ALGORITHM |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #ifndef _SIM_CPP_CONFIG_H | ||
| #define _SIM_CPP_CONFIG_H | ||
|
|
||
| #pragma clang system_header | ||
|
|
||
| typedef unsigned char uint8_t; | ||
|
|
||
| typedef __typeof__(sizeof(int)) size_t; | ||
| typedef __typeof__((char*)0-(char*)0) ptrdiff_t; | ||
|
|
||
| #endif // _SIM_CPP_CONFIG_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| #ifndef _INITIALIZER_LIST | ||
| #define _INITIALIZER_LIST | ||
|
|
||
| #pragma clang system_header | ||
| # | ||
| #include "sim_c++config.h" // size_t | ||
|
|
||
| namespace std { | ||
|
|
||
| template <class _E> | ||
| class initializer_list { | ||
| const _E* __begin_; | ||
| size_t __size_; | ||
|
|
||
| initializer_list(const _E* __b, size_t __s) | ||
| : __begin_(__b), | ||
| __size_(__s) | ||
| {} | ||
|
|
||
| public: | ||
| typedef _E value_type; | ||
| typedef const _E& reference; | ||
| typedef const _E& const_reference; | ||
| typedef size_t size_type; | ||
|
|
||
| typedef const _E* iterator; | ||
| typedef const _E* const_iterator; | ||
|
|
||
| initializer_list() : __begin_(0), __size_(0) {} | ||
|
|
||
| size_t size() const {return __size_;} | ||
| const _E* begin() const {return __begin_;} | ||
| const _E* end() const {return __begin_ + __size_;} | ||
|
|
||
| }; // class initializer_list | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _INITIALIZER_LIST |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #ifndef _SIM_ITERATOR_BASE | ||
| #define _SIM_ITERATOR_BASE | ||
|
|
||
| namespace std { | ||
|
|
||
| struct input_iterator_tag { }; | ||
| struct output_iterator_tag { }; | ||
| struct forward_iterator_tag : public input_iterator_tag { }; | ||
| struct bidirectional_iterator_tag : public forward_iterator_tag { }; | ||
| struct random_access_iterator_tag : public bidirectional_iterator_tag { }; | ||
|
|
||
| template <typename Iterator> struct iterator_traits { | ||
| typedef typename Iterator::difference_type difference_type; | ||
| typedef typename Iterator::value_type value_type; | ||
| typedef typename Iterator::pointer pointer; | ||
| typedef typename Iterator::reference reference; | ||
| typedef typename Iterator::iterator_category iterator_category; | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_ITERATOR_BASE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
|
|
||
| #ifndef _SIM_MAP | ||
| #define _SIM_MAP | ||
|
|
||
| #pragma clang system_header | ||
| #include "sim_stl_pair" | ||
|
|
||
| namespace std { | ||
|
|
||
| template <typename Key, typename Value> | ||
| class map { | ||
| public: | ||
| using value_type = pair<Key, Value>; | ||
| map(); | ||
| map(initializer_list<pair<Key, Value>> initList); | ||
| value_type& operator[](const Key& key); | ||
| value_type& operator[](Key&& key); | ||
| class iterator { | ||
| public: | ||
| iterator(Key *key): ptr(key) {} | ||
| iterator& operator++() { ++ptr; return *this; } | ||
| bool operator!=(const iterator &other) const { return ptr != other.ptr; } | ||
| const Key &operator*() const { return *ptr; } | ||
| private: | ||
| Key *ptr; | ||
| }; | ||
| Key *val; | ||
| iterator begin() const { return iterator(val); } | ||
| iterator end() const { return iterator(val + 1); } | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_MAP |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
|
|
||
| #ifndef _SIM_SET | ||
| #define _SIM_SET | ||
|
|
||
| #pragma clang system_header | ||
| #include "sim_initializer_list" | ||
|
|
||
| namespace std { | ||
|
|
||
| template< class T = void > | ||
| struct less; | ||
|
|
||
| template< class T > | ||
| struct allocator; | ||
|
|
||
| template< class Key > | ||
| struct hash; | ||
|
|
||
| template< | ||
| class Key, | ||
| class Compare = std::less<Key>, | ||
| class Alloc = std::allocator<Key> | ||
| > class set { | ||
| public: | ||
| set(initializer_list<Key> __list) {} | ||
|
|
||
| class iterator { | ||
| public: | ||
| iterator(Key *key): ptr(key) {} | ||
| iterator& operator++() { ++ptr; return *this; } | ||
| bool operator!=(const iterator &other) const { return ptr != other.ptr; } | ||
| const Key &operator*() const { return *ptr; } | ||
| private: | ||
| Key *ptr; | ||
| }; | ||
|
|
||
| Key *val; | ||
| iterator begin() const { return iterator(val); } | ||
| iterator end() const { return iterator(val + 1); } | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_SET |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #ifndef _SIM_STL_PAIR | ||
| #define _SIM_STL_PAIR | ||
|
|
||
| #pragma clang system_header | ||
|
|
||
| #include "sim_type_traits" | ||
|
|
||
| namespace std { | ||
|
|
||
| template <class T1, class T2> | ||
| struct pair { | ||
| T1 first; | ||
| T2 second; | ||
|
|
||
| pair() : first(), second() {} | ||
| pair(const T1 &a, const T2 &b) : first(a), second(b) {} | ||
|
|
||
| template<class U1, class U2> | ||
| pair(const pair<U1, U2> &other) : first(other.first), | ||
| second(other.second) {} | ||
| }; | ||
|
|
||
| template <typename T1, typename T2> | ||
| pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type> | ||
| make_pair(T1 &&, T2 &&) { | ||
| return {}; | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_STL_PAIR | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
|
|
||
| #ifndef _SIM_TYPE_TRAITS | ||
| #define _SIM_TYPE_TRAITS | ||
|
|
||
| #pragma clang system_header | ||
| namespace std { | ||
|
|
||
| template< class T > struct remove_reference {typedef T type;}; | ||
| template< class T > struct remove_reference<T&> {typedef T type;}; | ||
| template< class T > struct remove_reference<T&&> {typedef T type;}; | ||
|
|
||
| template<typename T> typename remove_reference<T>::type&& move(T&& a); | ||
|
|
||
| template< class T > | ||
| using remove_reference_t = typename remove_reference<T>::type; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_TYPE_TRAITS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #ifndef _SIM_UNORDERED_MAP | ||
| #define _SIM_UNORDERED_MAP | ||
|
|
||
| #pragma clang system_header | ||
| #include "sim_initializer_list" | ||
|
|
||
| namespace std { | ||
|
|
||
| template <typename Key, typename Value> | ||
| class unordered_map { | ||
| public: | ||
| using value_type = pair<Key, Value>; | ||
| unordered_map(); | ||
| unordered_map(initializer_list<pair<Key, Value>> initList); | ||
| value_type& operator[](const Key& key); | ||
| value_type& operator[](Key&& key); | ||
| class iterator { | ||
| public: | ||
| iterator(Key *key): ptr(key) {} | ||
| iterator& operator++() { ++ptr; return *this; } | ||
| bool operator!=(const iterator &other) const { return ptr != other.ptr; } | ||
| const Key &operator*() const { return *ptr; } | ||
| private: | ||
| Key *ptr; | ||
| }; | ||
| Key *val; | ||
| iterator begin() const { return iterator(val); } | ||
| iterator end() const { return iterator(val + 1); } | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_UNORDERED_MAP |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| #ifndef _SIM_UNORDERED_SET | ||
| #define _SIM_UNORDERED_SET | ||
|
|
||
| #pragma clang system_header | ||
| #include "sim_initializer_list" | ||
|
|
||
| namespace std { | ||
|
|
||
| template< | ||
| class Key, | ||
| class Hash = std::hash<Key>, | ||
| class Compare = std::less<Key>, | ||
| class Alloc = std::allocator<Key> | ||
| > class unordered_set { | ||
| public: | ||
| unordered_set(initializer_list<Key> __list) {} | ||
|
|
||
| class iterator { | ||
| public: | ||
| iterator(Key *key): ptr(key) {} | ||
| iterator& operator++() { ++ptr; return *this; } | ||
| bool operator!=(const iterator &other) const { return ptr != other.ptr; } | ||
| const Key &operator*() const { return *ptr; } | ||
| private: | ||
| Key *ptr; | ||
| }; | ||
|
|
||
| Key *val; | ||
| iterator begin() const { return iterator(val); } | ||
| iterator end() const { return iterator(val + 1); } | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_UNORDERED_SET |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| #ifndef _SIM_VECTOR | ||
| #define _SIM_VECTOR | ||
|
|
||
| #pragma clang system_header | ||
|
|
||
| #include "sim_iterator_base" | ||
|
|
||
| namespace std { | ||
|
|
||
| template <typename T, typename Ptr, typename Ref> struct __vector_iterator { | ||
| typedef __vector_iterator<T, T *, T &> iterator; | ||
| typedef __vector_iterator<T, const T *, const T &> const_iterator; | ||
|
|
||
| typedef ptrdiff_t difference_type; | ||
| typedef T value_type; | ||
| typedef Ptr pointer; | ||
| typedef Ref reference; | ||
| typedef std::random_access_iterator_tag iterator_category; | ||
|
|
||
| __vector_iterator(const Ptr p = 0) : ptr(p) {} | ||
| __vector_iterator(const iterator &rhs): ptr(rhs.base()) {} | ||
| __vector_iterator<T, Ptr, Ref>& operator++() { ++ ptr; return *this; } | ||
| __vector_iterator<T, Ptr, Ref> operator++(int) { | ||
| auto tmp = *this; | ||
| ++ ptr; | ||
| return tmp; | ||
| } | ||
| __vector_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; } | ||
| __vector_iterator<T, Ptr, Ref> operator--(int) { | ||
| auto tmp = *this; -- ptr; | ||
| return tmp; | ||
| } | ||
| __vector_iterator<T, Ptr, Ref> operator+(difference_type n) { | ||
| return ptr + n; | ||
| } | ||
| friend __vector_iterator<T, Ptr, Ref> operator+( | ||
| difference_type n, | ||
| const __vector_iterator<T, Ptr, Ref> &iter) { | ||
| return n + iter.ptr; | ||
| } | ||
| __vector_iterator<T, Ptr, Ref> operator-(difference_type n) { | ||
| return ptr - n; | ||
| } | ||
| __vector_iterator<T, Ptr, Ref> operator+=(difference_type n) { | ||
| return ptr += n; | ||
| } | ||
| __vector_iterator<T, Ptr, Ref> operator-=(difference_type n) { | ||
| return ptr -= n; | ||
| } | ||
|
|
||
| template<typename U, typename Ptr2, typename Ref2> | ||
| difference_type operator-(const __vector_iterator<U, Ptr2, Ref2> &rhs); | ||
|
|
||
| Ref operator*() const { return *ptr; } | ||
| Ptr operator->() const { return ptr; } | ||
|
|
||
| Ref operator[](difference_type n) { | ||
| return *(ptr+n); | ||
| } | ||
|
|
||
| bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; } | ||
| bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; } | ||
|
|
||
| bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; } | ||
| bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; } | ||
|
|
||
| const Ptr& base() const { return ptr; } | ||
|
|
||
| private: | ||
| Ptr ptr; | ||
| }; | ||
|
|
||
| template<typename T> | ||
| class vector { | ||
| T *_start; | ||
| T *_finish; | ||
| T *_end_of_storage; | ||
|
|
||
| public: | ||
| typedef T value_type; | ||
| typedef size_t size_type; | ||
| typedef __vector_iterator<T, T *, T &> iterator; | ||
| typedef __vector_iterator<T, const T *, const T &> const_iterator; | ||
|
|
||
| vector() : _start(0), _finish(0), _end_of_storage(0) {} | ||
| template <typename InputIterator> | ||
| vector(InputIterator first, InputIterator last); | ||
| vector(const vector &other); | ||
| vector(vector &&other); | ||
| ~vector(); | ||
|
|
||
| size_t size() const { | ||
| return size_t(_finish - _start); | ||
| } | ||
|
|
||
| vector& operator=(const vector &other); | ||
| vector& operator=(vector &&other); | ||
| vector& operator=(std::initializer_list<T> ilist); | ||
|
|
||
| void assign(size_type count, const T &value); | ||
| template <typename InputIterator > | ||
| void assign(InputIterator first, InputIterator last); | ||
| void assign(std::initializer_list<T> ilist); | ||
|
|
||
| void clear(); | ||
|
|
||
| void push_back(const T &value); | ||
| void push_back(T &&value); | ||
| template<class... Args> | ||
| void emplace_back(Args&&... args); | ||
| void pop_back(); | ||
|
|
||
| iterator insert(const_iterator position, const value_type &val); | ||
| iterator insert(const_iterator position, size_type n, | ||
| const value_type &val); | ||
| template <typename InputIterator> | ||
| iterator insert(const_iterator position, InputIterator first, | ||
| InputIterator last); | ||
| iterator insert(const_iterator position, value_type &&val); | ||
| iterator insert(const_iterator position, initializer_list<value_type> il); | ||
|
|
||
| template <class... Args> | ||
| iterator emplace(const_iterator position, Args&&... args); | ||
|
|
||
| iterator erase(const_iterator position); | ||
| iterator erase(const_iterator first, const_iterator last); | ||
|
|
||
| T &operator[](size_t n) { | ||
| return _start[n]; | ||
| } | ||
|
|
||
| const T &operator[](size_t n) const { | ||
| return _start[n]; | ||
| } | ||
|
|
||
| iterator begin() { return iterator(_start); } | ||
| const_iterator begin() const { return const_iterator(_start); } | ||
| const_iterator cbegin() const { return const_iterator(_start); } | ||
| iterator end() { return iterator(_finish); } | ||
| const_iterator end() const { return const_iterator(_finish); } | ||
| const_iterator cend() const { return const_iterator(_finish); } | ||
| T& front() { return *begin(); } | ||
| const T& front() const { return *begin(); } | ||
| T& back() { return *(end() - 1); } | ||
| const T& back() const { return *(end() - 1); } | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| #endif // _SIM_VECTOR |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-nondeterministic-pointer-iteration-order %t -- -- -I%S -std=c++!4 | ||
|
|
||
| #include "Inputs/system-header-simulator/sim_set" | ||
| #include "Inputs/system-header-simulator/sim_unordered_set" | ||
| #include "Inputs/system-header-simulator/sim_map" | ||
| #include "Inputs/system-header-simulator/sim_unordered_map" | ||
| #include "Inputs/system-header-simulator/sim_vector" | ||
| #include "Inputs/system-header-simulator/sim_algorithm" | ||
|
|
||
| template<class T> | ||
| void f(T x); | ||
|
|
||
| void PointerIteration() { | ||
| int a = 1, b = 2; | ||
| std::set<int> OrderedIntSet = {a, b}; | ||
| std::set<int *> OrderedPtrSet = {&a, &b}; | ||
| std::unordered_set<int> UnorderedIntSet = {a, b}; | ||
| std::unordered_set<int *> UnorderedPtrSet = {&a, &b}; | ||
| std::map<int, int> IntMap = { std::make_pair(a,a), std::make_pair(b,b) }; | ||
| std::map<int*, int*> PtrMap = { std::make_pair(&a,&a), std::make_pair(&b,&b) }; | ||
| std::unordered_map<int, int> IntUnorderedMap = { std::make_pair(a,a), std::make_pair(b,b) }; | ||
| std::unordered_map<int*, int*> PtrUnorderedMap = { std::make_pair(&a,&a), std::make_pair(&b,&b) }; | ||
|
|
||
| for (auto i : OrderedIntSet) // no-warning | ||
| f(i); | ||
|
|
||
| for (auto i : OrderedPtrSet) // no-warning | ||
| f(i); | ||
|
|
||
| for (auto i : UnorderedIntSet) // no-warning | ||
| f(i); | ||
|
|
||
| for (auto i : UnorderedPtrSet) | ||
| f(i); | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: iteration of pointers is nondeterministic | ||
|
|
||
| for (auto &i : UnorderedPtrSet) | ||
| f(i); | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: iteration of pointers is nondeterministic | ||
|
|
||
| for (auto &i : IntMap) // no-warning | ||
| f(i); | ||
|
|
||
| for (auto &i : PtrMap) // no-warning | ||
| f(i); | ||
|
|
||
| for (auto &i : IntUnorderedMap) // no-warning | ||
| f(i); | ||
|
|
||
| for (auto &i : PtrUnorderedMap) | ||
| f(i); | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: iteration of pointers is nondeterministic | ||
| } | ||
|
|
||
| bool g (int *x) { return true; } | ||
| bool h (int x) { return true; } | ||
|
|
||
| void PointerSorting() { | ||
| int a = 1, b = 2, c = 3; | ||
| std::vector<int> V1 = {a, b}; | ||
| std::vector<int *> V2 = {&a, &b}; | ||
|
|
||
| std::is_sorted(V1.begin(), V1.end()); // no-warning | ||
| std::nth_element(V1.begin(), V1.begin() + 1, V1.end()); // no-warning | ||
| std::partial_sort(V1.begin(), V1.begin() + 1, V1.end()); // no-warning | ||
| std::sort(V1.begin(), V1.end()); // no-warning | ||
| std::stable_sort(V1.begin(), V1.end()); // no-warning | ||
| std::partition(V1.begin(), V1.end(), h); // no-warning | ||
| std::stable_partition(V1.begin(), V1.end(), h); // no-warning | ||
| std::is_sorted(V2.begin(), V2.end()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| std::nth_element(V2.begin(), V2.begin() + 1, V2.end()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| std::partial_sort(V2.begin(), V2.begin() + 1, V2.end()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| std::sort(V2.begin(), V2.end()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| std::stable_sort(V2.begin(), V2.end()); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| std::partition(V2.begin(), V2.end(), g); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| std::stable_partition(V2.begin(), V2.end(), g); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| ======================= | ||
| Clang SYCL Linker | ||
| ======================= | ||
|
|
||
| .. contents:: | ||
| :local: | ||
|
|
||
| .. _clang-sycl-linker: | ||
|
|
||
| Introduction | ||
| ============ | ||
|
|
||
| This tool works as a wrapper around the SYCL device code linking process. | ||
| The purpose of this tool is to provide an interface to link SYCL device bitcode | ||
| in LLVM IR format, SYCL device bitcode in SPIR-V IR format, and native binary | ||
| objects, and then use the SPIR-V LLVM Translator tool on fully linked device | ||
| objects to produce the final output. | ||
| After the linking stage, the fully linked device code in LLVM IR format may | ||
| undergo several SYCL-specific finalization steps before the SPIR-V code | ||
| generation step. | ||
| The tool will also support the Ahead-Of-Time (AOT) compilation flow. AOT | ||
| compilation is the process of invoking the back-end at compile time to produce | ||
| the final binary, as opposed to just-in-time (JIT) compilation when final code | ||
| generation is deferred until application runtime. | ||
|
|
||
| Device code linking for SYCL offloading has several known quirks that | ||
| make it difficult to use in a unified offloading setting. Two of the primary | ||
| issues are: | ||
| 1. Several finalization steps are required to be run on the fully linked LLVM | ||
| IR bitcode to guarantee conformance to SYCL standards. This step is unique to | ||
| the SYCL offloading compilation flow. | ||
| 2. The SPIR-V LLVM Translator tool is an external tool and hence SPIR-V IR code | ||
| generation cannot be done as part of LTO. This limitation can be lifted once | ||
| the SPIR-V backend is available as a viable LLVM backend. | ||
|
|
||
| This tool has been proposed to work around these issues. | ||
|
|
||
| Usage | ||
| ===== | ||
|
|
||
| This tool can be used with the following options. Several of these options will | ||
| be passed down to downstream tools like 'llvm-link', 'llvm-spirv', etc. | ||
|
|
||
| .. code-block:: console | ||
| OVERVIEW: A utility that wraps around the SYCL device code linking process. | ||
| This enables linking and code generation for SPIR-V JIT targets and AOT | ||
| targets. | ||
| USAGE: clang-sycl-linker [options] | ||
| OPTIONS: | ||
| --arch <value> Specify the name of the target architecture. | ||
| --dry-run Print generated commands without running. | ||
| -g Specify that this was a debug compile. | ||
| -help-hidden Display all available options | ||
| -help Display available options (--help-hidden for more) | ||
| --library-path=<dir> Set the library path for SYCL device libraries | ||
| --device-libs=<value> A comma separated list of device libraries that are linked during the device link | ||
| -o <path> Path to file to write output | ||
| --save-temps Save intermediate results | ||
| --triple <value> Specify the target triple. | ||
| --version Display the version number and exit | ||
| -v Print verbose information | ||
| -spirv-dump-device-code=<dir> Directory to dump SPIR-V IR code into | ||
| -is-windows-msvc-env Specify if we are compiling under windows environment | ||
| -llvm-spirv-options=<value> Pass options to llvm-spirv tool | ||
| --llvm-spirv-path=<dir> Set the system llvm-spirv path | ||
| Example | ||
| ======= | ||
|
|
||
| This tool is intended to be invoked when targeting any of the target offloading | ||
| toolchains. When the --sycl-link option is passed to the clang driver, the | ||
| driver will invoke the linking job of the target offloading toolchain, which in | ||
| turn will invoke this tool. This tool can be used to create one or more fully | ||
| linked device images that are ready to be wrapped and linked with host code to | ||
| generate the final executable. | ||
|
|
||
| .. code-block:: console | ||
| clang-sycl-linker --triple spirv64 --arch native input.bc |