Tue, 04 Jul 2017 09:57:19 +0800
[GC] For weak memory models, more memory barriers are needed for the GC lock-free algorithms.
src/share/vm/utilities/taskqueue.hpp | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/vm/utilities/taskqueue.hpp Tue Jun 20 14:59:09 2017 +0800 1.2 +++ b/src/share/vm/utilities/taskqueue.hpp Tue Jul 04 09:57:19 2017 +0800 1.3 @@ -143,9 +143,23 @@ 1.4 // Internal type for indexing the queue; also used for the tag. 1.5 typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t; 1.6 1.7 +#ifdef MIPS64 1.8 +private: 1.9 +#endif 1.10 // The first free element after the last one pushed (mod N). 1.11 volatile uint _bottom; 1.12 1.13 +#ifdef MIPS64 1.14 +protected: 1.15 + inline uint get_bottom() const { 1.16 + return OrderAccess::load_acquire((volatile juint*)&_bottom); 1.17 + } 1.18 + 1.19 + inline void set_bottom(uint new_bottom) { 1.20 + OrderAccess::release_store(&_bottom, new_bottom); 1.21 + } 1.22 +#endif 1.23 + 1.24 enum { MOD_N_MASK = N - 1 }; 1.25 1.26 class Age { 1.27 @@ -154,11 +168,21 @@ 1.28 Age(const Age& age) { _data = age._data; } 1.29 Age(idx_t top, idx_t tag) { _fields._top = top; _fields._tag = tag; } 1.30 1.31 +#ifndef MIPS64 1.32 Age get() const volatile { return _data; } 1.33 void set(Age age) volatile { _data = age._data; } 1.34 - 1.35 idx_t top() const volatile { return _fields._top; } 1.36 idx_t tag() const volatile { return _fields._tag; } 1.37 +#else 1.38 + Age get() const volatile { 1.39 + size_t res = OrderAccess::load_ptr_acquire((volatile intptr_t*) &_data); 1.40 + return *(Age*)(&res); 1.41 + } 1.42 + 1.43 + void set(Age age) volatile { OrderAccess::release_store_ptr((volatile intptr_t*) &_data, *(size_t*)(&age._data)); } 1.44 + idx_t top() const volatile { return OrderAccess::load_acquire((volatile idx_t*) &(_fields._top)); } 1.45 + idx_t tag() const volatile { return OrderAccess::load_acquire((volatile idx_t*) &(_fields._tag)); } 1.46 +#endif 1.47 1.48 // Increment top; if it wraps, increment tag also. 1.49 void increment() { 1.50 @@ -225,22 +249,41 @@ 1.51 TaskQueueSuper() : _bottom(0), _age() {} 1.52 1.53 // Return true if the TaskQueue contains/does not contain any tasks. 1.54 - bool peek() const { return _bottom != _age.top(); } 1.55 + bool peek() const { 1.56 +#ifdef MIPS64 1.57 + return get_bottom() != _age.top(); 1.58 +#else 1.59 + return _bottom != _age.top(); 1.60 +#endif 1.61 + } 1.62 + 1.63 bool is_empty() const { return size() == 0; } 1.64 1.65 // Return an estimate of the number of elements in the queue. 1.66 // The "careful" version admits the possibility of pop_local/pop_global 1.67 // races. 1.68 uint size() const { 1.69 +#ifdef MIPS64 1.70 + return size(get_bottom(), _age.top()); 1.71 +#else 1.72 return size(_bottom, _age.top()); 1.73 +#endif 1.74 } 1.75 1.76 uint dirty_size() const { 1.77 +#ifdef MIPS64 1.78 + return dirty_size(get_bottom(), _age.top()); 1.79 +#else 1.80 return dirty_size(_bottom, _age.top()); 1.81 +#endif 1.82 } 1.83 1.84 void set_empty() { 1.85 +#ifdef MIPS64 1.86 + set_bottom(0); 1.87 +#else 1.88 _bottom = 0; 1.89 +#endif 1.90 _age.set(0); 1.91 } 1.92 1.93 @@ -292,7 +335,9 @@ 1.94 typedef typename TaskQueueSuper<N, F>::Age Age; 1.95 typedef typename TaskQueueSuper<N, F>::idx_t idx_t; 1.96 1.97 +#ifndef MIPS64 1.98 using TaskQueueSuper<N, F>::_bottom; 1.99 +#endif 1.100 using TaskQueueSuper<N, F>::_age; 1.101 using TaskQueueSuper<N, F>::increment_index; 1.102 using TaskQueueSuper<N, F>::decrement_index; 1.103 @@ -356,7 +401,11 @@ 1.104 void GenericTaskQueue<E, F, N>::oops_do(OopClosure* f) { 1.105 // tty->print_cr("START OopTaskQueue::oops_do"); 1.106 uint iters = size(); 1.107 +#ifdef MIPS64 1.108 + uint index = this->get_bottom(); 1.109 +#else 1.110 uint index = _bottom; 1.111 +#endif 1.112 for (uint i = 0; i < iters; ++i) { 1.113 index = decrement_index(index); 1.114 // tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T, 1.115 @@ -373,18 +422,23 @@ 1.116 bool GenericTaskQueue<E, F, N>::push_slow(E t, uint dirty_n_elems) { 1.117 if (dirty_n_elems == N - 1) { 1.118 // Actually means 0, so do the push. 1.119 +#ifdef MIPS64 1.120 + uint localBot = this->get_bottom(); 1.121 +#else 1.122 uint localBot = _bottom; 1.123 +#endif 1.124 // g++ complains if the volatile result of the assignment is 1.125 // unused, so we cast the volatile away. We cannot cast directly 1.126 // to void, because gcc treats that as not using the result of the 1.127 // assignment. However, casting to E& means that we trigger an 1.128 // unused-value warning. So, we cast the E& to void. 1.129 (void)const_cast<E&>(_elems[localBot] = t); 1.130 +#ifdef MIPS64 1.131 + this->set_bottom(increment_index(localBot)); 1.132 +#else 1.133 OrderAccess::release_store(&_bottom, increment_index(localBot)); 1.134 +#endif 1.135 TASKQUEUE_STATS_ONLY(stats.record_push()); 1.136 -#ifdef MIPS64 1.137 - if (Use3A2000) OrderAccess::fence(); 1.138 -#endif 1.139 return true; 1.140 } 1.141 return false; 1.142 @@ -438,7 +492,11 @@ 1.143 #if !(defined SPARC || defined IA32 || defined AMD64) 1.144 OrderAccess::fence(); 1.145 #endif 1.146 +#ifdef MIPS64 1.147 + uint localBot = this->get_bottom(); 1.148 +#else 1.149 uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom); 1.150 +#endif 1.151 uint n_elems = size(localBot, oldAge.top()); 1.152 if (n_elems == 0) { 1.153 return false; 1.154 @@ -509,9 +567,6 @@ 1.155 if (!taskqueue_t::push(t)) { 1.156 overflow_stack()->push(t); 1.157 TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->size())); 1.158 -#ifdef MIPS64 1.159 - if (Use3A2000) OrderAccess::fence(); 1.160 -#endif 1.161 } 1.162 return true; 1.163 } 1.164 @@ -584,16 +639,10 @@ 1.165 for (uint i = 0; i < 2 * _n; i++) { 1.166 if (steal_best_of_2(queue_num, seed, t)) { 1.167 TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(true)); 1.168 -#ifdef MIPS64 1.169 - if (Use3A2000) OrderAccess::fence(); 1.170 -#endif 1.171 return true; 1.172 } 1.173 } 1.174 TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(false)); 1.175 -#ifdef MIPS64 1.176 - if (Use3A2000) OrderAccess::fence(); 1.177 -#endif 1.178 return false; 1.179 } 1.180 1.181 @@ -695,7 +744,11 @@ 1.182 1.183 template<class E, MEMFLAGS F, unsigned int N> inline bool 1.184 GenericTaskQueue<E, F, N>::push(E t) { 1.185 +#ifdef MIPS64 1.186 + uint localBot = this->get_bottom(); 1.187 +#else 1.188 uint localBot = _bottom; 1.189 +#endif 1.190 assert(localBot < N, "_bottom out of range."); 1.191 idx_t top = _age.top(); 1.192 uint dirty_n_elems = dirty_size(localBot, top); 1.193 @@ -707,11 +760,12 @@ 1.194 // assignment. However, casting to E& means that we trigger an 1.195 // unused-value warning. So, we cast the E& to void. 1.196 (void) const_cast<E&>(_elems[localBot] = t); 1.197 +#ifdef MIPS64 1.198 + this->set_bottom(increment_index(localBot)); 1.199 +#else 1.200 OrderAccess::release_store(&_bottom, increment_index(localBot)); 1.201 +#endif 1.202 TASKQUEUE_STATS_ONLY(stats.record_push()); 1.203 -#ifdef MIPS64 1.204 - if (Use3A2000) OrderAccess::fence(); 1.205 -#endif 1.206 return true; 1.207 } else { 1.208 return push_slow(t, dirty_n_elems); 1.209 @@ -720,7 +774,11 @@ 1.210 1.211 template<class E, MEMFLAGS F, unsigned int N> inline bool 1.212 GenericTaskQueue<E, F, N>::pop_local(volatile E& t) { 1.213 +#ifdef MIPS64 1.214 + uint localBot = this->get_bottom(); 1.215 +#else 1.216 uint localBot = _bottom; 1.217 +#endif 1.218 // This value cannot be N-1. That can only occur as a result of 1.219 // the assignment to bottom in this method. If it does, this method 1.220 // resets the size to 0 before the next call (which is sequential, 1.221 @@ -729,7 +787,11 @@ 1.222 assert(dirty_n_elems != N - 1, "Shouldn't be possible..."); 1.223 if (dirty_n_elems == 0) return false; 1.224 localBot = decrement_index(localBot); 1.225 +#ifdef MIPS64 1.226 + this->set_bottom(localBot); 1.227 +#else 1.228 _bottom = localBot; 1.229 +#endif 1.230 // This is necessary to prevent any read below from being reordered 1.231 // before the store just above. 1.232 OrderAccess::fence();