Improve atomic store implementation on hppa-linux.
[official-gcc.git] / libgcc / libgcov-profiler.c
blob249c4f3babbc1ec10eb15bb333b567a9f552e3ce
1 /* Routines required for instrumenting a program. */
2 /* Compile this one with gcc. */
3 /* Copyright (C) 1989-2021 Free Software Foundation, Inc.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 Under Section 7 of GPL version 3, you are granted additional
18 permissions described in the GCC Runtime Library Exception, version
19 3.1, as published by the Free Software Foundation.
21 You should have received a copy of the GNU General Public License and
22 a copy of the GCC Runtime Library Exception along with this program;
23 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 <http://www.gnu.org/licenses/>. */
26 #include "libgcov.h"
27 #if !defined(inhibit_libc)
29 #ifdef L_gcov_interval_profiler
30 /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
31 corresponding counter in COUNTERS. If the VALUE is above or below
32 the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
33 instead. */
35 void
36 __gcov_interval_profiler (gcov_type *counters, gcov_type value,
37 int start, unsigned steps)
39 gcov_type delta = value - start;
40 if (delta < 0)
41 counters[steps + 1]++;
42 else if (delta >= steps)
43 counters[steps]++;
44 else
45 counters[delta]++;
47 #endif
49 #if defined(L_gcov_interval_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
50 /* If VALUE is in interval <START, START + STEPS - 1>, then increases the
51 corresponding counter in COUNTERS. If the VALUE is above or below
52 the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
53 instead. Function is thread-safe. */
55 void
56 __gcov_interval_profiler_atomic (gcov_type *counters, gcov_type value,
57 int start, unsigned steps)
59 gcov_type delta = value - start;
60 if (delta < 0)
61 __atomic_fetch_add (&counters[steps + 1], 1, __ATOMIC_RELAXED);
62 else if (delta >= steps)
63 __atomic_fetch_add (&counters[steps], 1, __ATOMIC_RELAXED);
64 else
65 __atomic_fetch_add (&counters[delta], 1, __ATOMIC_RELAXED);
67 #endif
69 #ifdef L_gcov_pow2_profiler
70 /* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
71 COUNTERS[0] is incremented. */
73 void
74 __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
76 if (value == 0 || (value & (value - 1)))
77 counters[0]++;
78 else
79 counters[1]++;
81 #endif
83 #if defined(L_gcov_pow2_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
84 /* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
85 COUNTERS[0] is incremented. Function is thread-safe. */
87 void
88 __gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value)
90 if (value == 0 || (value & (value - 1)))
91 __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED);
92 else
93 __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED);
95 #endif
97 /* Tries to determine N most commons value among its inputs. */
99 static inline void
100 __gcov_topn_values_profiler_body (gcov_type *counters, gcov_type value,
101 int use_atomic)
103 gcov_topn_add_value (counters, value, 1, use_atomic, 1);
106 #ifdef L_gcov_topn_values_profiler
107 void
108 __gcov_topn_values_profiler (gcov_type *counters, gcov_type value)
110 __gcov_topn_values_profiler_body (counters, value, 0);
112 #endif
114 #if defined(L_gcov_topn_values_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
116 /* Update one value profilers (COUNTERS) for a given VALUE.
118 CAVEAT: Following function is not thread-safe, only total number
119 of executions (COUNTERS[2]) is update with an atomic instruction.
120 Problem is that one cannot atomically update two counters
121 (COUNTERS[0] and COUNTERS[1]), for more information please read
122 following email thread:
123 https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */
125 void
126 __gcov_topn_values_profiler_atomic (gcov_type *counters, gcov_type value)
128 __gcov_topn_values_profiler_body (counters, value, 1);
130 #endif
132 #ifdef L_gcov_indirect_call_profiler_v4
134 /* These two variables are used to actually track caller and callee. Keep
135 them in TLS memory so races are not common (they are written to often).
136 The variables are set directly by GCC instrumented code, so declaration
137 here must match one in tree-profile.c */
139 #if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
140 __thread
141 #endif
142 struct indirect_call_tuple __gcov_indirect_call;
144 /* By default, the C++ compiler will use function addresses in the
145 vtable entries. Setting TARGET_VTABLE_USES_DESCRIPTORS to nonzero
146 tells the compiler to use function descriptors instead. The value
147 of this macro says how many words wide the descriptor is (normally 2).
149 It is assumed that the address of a function descriptor may be treated
150 as a pointer to a function. */
152 /* Tries to determine the most common value among its inputs. */
153 static inline void
154 __gcov_indirect_call_profiler_body (gcov_type value, void *cur_func,
155 int use_atomic)
157 /* If the C++ virtual tables contain function descriptors then one
158 function may have multiple descriptors and we need to dereference
159 the descriptors to see if they point to the same function. */
160 if (cur_func == __gcov_indirect_call.callee
161 || (__LIBGCC_VTABLE_USES_DESCRIPTORS__
162 && *(void **) cur_func == *(void **) __gcov_indirect_call.callee))
163 __gcov_topn_values_profiler_body (__gcov_indirect_call.counters, value,
164 use_atomic);
166 __gcov_indirect_call.callee = NULL;
169 void
170 __gcov_indirect_call_profiler_v4 (gcov_type value, void *cur_func)
172 __gcov_indirect_call_profiler_body (value, cur_func, 0);
175 #if GCOV_SUPPORTS_ATOMIC
176 void
177 __gcov_indirect_call_profiler_v4_atomic (gcov_type value, void *cur_func)
179 __gcov_indirect_call_profiler_body (value, cur_func, 1);
181 #endif
183 #endif
185 #ifdef L_gcov_time_profiler
187 /* Counter for first visit of each function. */
188 gcov_type __gcov_time_profiler_counter ATTRIBUTE_HIDDEN;
190 #endif
192 #ifdef L_gcov_average_profiler
193 /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
194 to saturate up. */
196 void
197 __gcov_average_profiler (gcov_type *counters, gcov_type value)
199 counters[0] += value;
200 counters[1] ++;
202 #endif
204 #if defined(L_gcov_average_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
205 /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
206 to saturate up. Function is thread-safe. */
208 void
209 __gcov_average_profiler_atomic (gcov_type *counters, gcov_type value)
211 __atomic_fetch_add (&counters[0], value, __ATOMIC_RELAXED);
212 __atomic_fetch_add (&counters[1], 1, __ATOMIC_RELAXED);
214 #endif
216 #ifdef L_gcov_ior_profiler
217 /* Bitwise-OR VALUE into COUNTER. */
219 void
220 __gcov_ior_profiler (gcov_type *counters, gcov_type value)
222 *counters |= value;
224 #endif
226 #if defined(L_gcov_ior_profiler_atomic) && GCOV_SUPPORTS_ATOMIC
227 /* Bitwise-OR VALUE into COUNTER. Function is thread-safe. */
229 void
230 __gcov_ior_profiler_atomic (gcov_type *counters, gcov_type value)
232 __atomic_fetch_or (&counters[0], value, __ATOMIC_RELAXED);
234 #endif
237 #endif /* inhibit_libc */