]>
Commit | Line | Data |
---|---|---|
203b91b9 RS |
1 | /* More subroutines needed by GCC output code on some machines. */ |
2 | /* Compile this one with gcc. */ | |
e09d24ff R |
3 | /* Copyright (C) 1989, 92, 93, 94, 95, 96, 97, 98, 1999, 2000 |
4 | Free Software Foundation, Inc. | |
203b91b9 RS |
5 | |
6 | This file is part of GNU CC. | |
7 | ||
8 | GNU CC is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2, or (at your option) | |
11 | any later version. | |
12 | ||
13 | GNU CC is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with GNU CC; see the file COPYING. If not, write to | |
a35311b0 RK |
20 | the Free Software Foundation, 59 Temple Place - Suite 330, |
21 | Boston, MA 02111-1307, USA. */ | |
203b91b9 | 22 | |
d5c88b0a RK |
23 | /* As a special exception, if you link this library with other files, |
24 | some of which are compiled with GCC, to produce an executable, | |
25 | this library does not by itself cause the resulting executable | |
26 | to be covered by the GNU General Public License. | |
203b91b9 RS |
27 | This exception does not however invalidate any other reasons why |
28 | the executable file might be covered by the GNU General Public License. */ | |
29 | ||
30 | /* It is incorrect to include config.h here, because this file is being | |
31 | compiled for the target, and hence definitions concerning only the host | |
32 | do not apply. */ | |
33 | ||
0dadecf6 | 34 | #include "tconfig.h" |
2e39bdbe | 35 | #include "tsystem.h" |
2467749d | 36 | |
bfe655f9 | 37 | #include "machmode.h" |
daefd78b | 38 | #include "defaults.h" |
203b91b9 RS |
39 | |
40 | /* Don't use `fancy_abort' here even if config.h says to use it. */ | |
41 | #ifdef abort | |
42 | #undef abort | |
43 | #endif | |
44 | ||
956d6950 JL |
45 | /* In a cross-compilation situation, default to inhibiting compilation |
46 | of routines that use libc. */ | |
47 | ||
eff0f7ac | 48 | #if defined(CROSS_COMPILE) && !defined(inhibit_libc) |
956d6950 JL |
49 | #define inhibit_libc |
50 | #endif | |
51 | ||
299b83b7 | 52 | #include "libgcc2.h" |
203b91b9 RS |
53 | \f |
54 | #if defined (L_negdi2) || defined (L_divdi3) || defined (L_moddi3) | |
55 | #if defined (L_divdi3) || defined (L_moddi3) | |
56 | static inline | |
57 | #endif | |
996ed075 JJ |
58 | DWtype |
59 | __negdi2 (DWtype u) | |
203b91b9 | 60 | { |
996ed075 JJ |
61 | DWunion w; |
62 | DWunion uu; | |
203b91b9 RS |
63 | |
64 | uu.ll = u; | |
65 | ||
66 | w.s.low = -uu.s.low; | |
996ed075 | 67 | w.s.high = -uu.s.high - ((UWtype) w.s.low > 0); |
203b91b9 RS |
68 | |
69 | return w.ll; | |
70 | } | |
71 | #endif | |
72 | \f | |
37ef1054 RK |
73 | /* Unless shift functions are defined whith full ANSI prototypes, |
74 | parameter b will be promoted to int if word_type is smaller than an int. */ | |
203b91b9 | 75 | #ifdef L_lshrdi3 |
996ed075 JJ |
76 | DWtype |
77 | __lshrdi3 (DWtype u, word_type b) | |
203b91b9 | 78 | { |
996ed075 | 79 | DWunion w; |
b799cfc3 | 80 | word_type bm; |
996ed075 | 81 | DWunion uu; |
203b91b9 RS |
82 | |
83 | if (b == 0) | |
84 | return u; | |
85 | ||
86 | uu.ll = u; | |
87 | ||
996ed075 | 88 | bm = (sizeof (Wtype) * BITS_PER_UNIT) - b; |
203b91b9 RS |
89 | if (bm <= 0) |
90 | { | |
91 | w.s.high = 0; | |
996ed075 | 92 | w.s.low = (UWtype)uu.s.high >> -bm; |
203b91b9 RS |
93 | } |
94 | else | |
95 | { | |
996ed075 JJ |
96 | UWtype carries = (UWtype)uu.s.high << bm; |
97 | w.s.high = (UWtype)uu.s.high >> b; | |
98 | w.s.low = ((UWtype)uu.s.low >> b) | carries; | |
203b91b9 RS |
99 | } |
100 | ||
101 | return w.ll; | |
102 | } | |
103 | #endif | |
104 | ||
105 | #ifdef L_ashldi3 | |
996ed075 JJ |
106 | DWtype |
107 | __ashldi3 (DWtype u, word_type b) | |
203b91b9 | 108 | { |
996ed075 | 109 | DWunion w; |
b799cfc3 | 110 | word_type bm; |
996ed075 | 111 | DWunion uu; |
203b91b9 RS |
112 | |
113 | if (b == 0) | |
114 | return u; | |
115 | ||
116 | uu.ll = u; | |
117 | ||
996ed075 | 118 | bm = (sizeof (Wtype) * BITS_PER_UNIT) - b; |
203b91b9 RS |
119 | if (bm <= 0) |
120 | { | |
121 | w.s.low = 0; | |
996ed075 | 122 | w.s.high = (UWtype)uu.s.low << -bm; |
203b91b9 RS |
123 | } |
124 | else | |
125 | { | |
996ed075 JJ |
126 | UWtype carries = (UWtype)uu.s.low >> bm; |
127 | w.s.low = (UWtype)uu.s.low << b; | |
128 | w.s.high = ((UWtype)uu.s.high << b) | carries; | |
203b91b9 RS |
129 | } |
130 | ||
131 | return w.ll; | |
132 | } | |
133 | #endif | |
134 | ||
135 | #ifdef L_ashrdi3 | |
996ed075 JJ |
136 | DWtype |
137 | __ashrdi3 (DWtype u, word_type b) | |
203b91b9 | 138 | { |
996ed075 | 139 | DWunion w; |
b799cfc3 | 140 | word_type bm; |
996ed075 | 141 | DWunion uu; |
203b91b9 RS |
142 | |
143 | if (b == 0) | |
144 | return u; | |
145 | ||
146 | uu.ll = u; | |
147 | ||
996ed075 | 148 | bm = (sizeof (Wtype) * BITS_PER_UNIT) - b; |
203b91b9 RS |
149 | if (bm <= 0) |
150 | { | |
151 | /* w.s.high = 1..1 or 0..0 */ | |
996ed075 | 152 | w.s.high = uu.s.high >> (sizeof (Wtype) * BITS_PER_UNIT - 1); |
203b91b9 RS |
153 | w.s.low = uu.s.high >> -bm; |
154 | } | |
155 | else | |
156 | { | |
996ed075 | 157 | UWtype carries = (UWtype)uu.s.high << bm; |
203b91b9 | 158 | w.s.high = uu.s.high >> b; |
996ed075 | 159 | w.s.low = ((UWtype)uu.s.low >> b) | carries; |
203b91b9 RS |
160 | } |
161 | ||
162 | return w.ll; | |
163 | } | |
164 | #endif | |
165 | \f | |
aa66bd06 | 166 | #ifdef L_ffsdi2 |
996ed075 JJ |
167 | DWtype |
168 | __ffsdi2 (DWtype u) | |
aa66bd06 | 169 | { |
996ed075 | 170 | DWunion uu, w; |
aa66bd06 RS |
171 | uu.ll = u; |
172 | w.s.high = 0; | |
173 | w.s.low = ffs (uu.s.low); | |
174 | if (w.s.low != 0) | |
de6cbba6 | 175 | return w.ll; |
aa66bd06 RS |
176 | w.s.low = ffs (uu.s.high); |
177 | if (w.s.low != 0) | |
178 | { | |
996ed075 | 179 | w.s.low += BITS_PER_UNIT * sizeof (Wtype); |
de6cbba6 | 180 | return w.ll; |
aa66bd06 | 181 | } |
de6cbba6 | 182 | return w.ll; |
aa66bd06 RS |
183 | } |
184 | #endif | |
185 | \f | |
203b91b9 | 186 | #ifdef L_muldi3 |
996ed075 JJ |
187 | DWtype |
188 | __muldi3 (DWtype u, DWtype v) | |
203b91b9 | 189 | { |
996ed075 JJ |
190 | DWunion w; |
191 | DWunion uu, vv; | |
203b91b9 RS |
192 | |
193 | uu.ll = u, | |
194 | vv.ll = v; | |
195 | ||
196 | w.ll = __umulsidi3 (uu.s.low, vv.s.low); | |
996ed075 JJ |
197 | w.s.high += ((UWtype) uu.s.low * (UWtype) vv.s.high |
198 | + (UWtype) uu.s.high * (UWtype) vv.s.low); | |
203b91b9 RS |
199 | |
200 | return w.ll; | |
201 | } | |
202 | #endif | |
203 | \f | |
3904131a | 204 | #ifdef L_udiv_w_sdiv |
ce13d15f | 205 | #if defined (sdiv_qrnnd) |
996ed075 JJ |
206 | UWtype |
207 | __udiv_w_sdiv (UWtype *rp, UWtype a1, UWtype a0, UWtype d) | |
431b1ee0 | 208 | { |
996ed075 JJ |
209 | UWtype q, r; |
210 | UWtype c0, c1, b1; | |
431b1ee0 | 211 | |
996ed075 | 212 | if ((Wtype) d >= 0) |
431b1ee0 | 213 | { |
996ed075 | 214 | if (a1 < d - a1 - (a0 >> (W_TYPE_SIZE - 1))) |
431b1ee0 TG |
215 | { |
216 | /* dividend, divisor, and quotient are nonnegative */ | |
217 | sdiv_qrnnd (q, r, a1, a0, d); | |
218 | } | |
219 | else | |
220 | { | |
221 | /* Compute c1*2^32 + c0 = a1*2^32 + a0 - 2^31*d */ | |
996ed075 | 222 | sub_ddmmss (c1, c0, a1, a0, d >> 1, d << (W_TYPE_SIZE - 1)); |
431b1ee0 TG |
223 | /* Divide (c1*2^32 + c0) by d */ |
224 | sdiv_qrnnd (q, r, c1, c0, d); | |
225 | /* Add 2^31 to quotient */ | |
996ed075 | 226 | q += (UWtype) 1 << (W_TYPE_SIZE - 1); |
431b1ee0 TG |
227 | } |
228 | } | |
229 | else | |
230 | { | |
231 | b1 = d >> 1; /* d/2, between 2^30 and 2^31 - 1 */ | |
232 | c1 = a1 >> 1; /* A/2 */ | |
996ed075 | 233 | c0 = (a1 << (W_TYPE_SIZE - 1)) + (a0 >> 1); |
431b1ee0 TG |
234 | |
235 | if (a1 < b1) /* A < 2^32*b1, so A/2 < 2^31*b1 */ | |
236 | { | |
237 | sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */ | |
238 | ||
239 | r = 2*r + (a0 & 1); /* Remainder from A/(2*b1) */ | |
240 | if ((d & 1) != 0) | |
241 | { | |
242 | if (r >= q) | |
243 | r = r - q; | |
244 | else if (q - r <= d) | |
245 | { | |
246 | r = r - q + d; | |
247 | q--; | |
248 | } | |
249 | else | |
250 | { | |
251 | r = r - q + 2*d; | |
252 | q -= 2; | |
253 | } | |
254 | } | |
255 | } | |
256 | else if (c1 < b1) /* So 2^31 <= (A/2)/b1 < 2^32 */ | |
257 | { | |
258 | c1 = (b1 - 1) - c1; | |
259 | c0 = ~c0; /* logical NOT */ | |
260 | ||
261 | sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */ | |
262 | ||
263 | q = ~q; /* (A/2)/b1 */ | |
264 | r = (b1 - 1) - r; | |
265 | ||
266 | r = 2*r + (a0 & 1); /* A/(2*b1) */ | |
267 | ||
268 | if ((d & 1) != 0) | |
269 | { | |
270 | if (r >= q) | |
271 | r = r - q; | |
272 | else if (q - r <= d) | |
273 | { | |
274 | r = r - q + d; | |
275 | q--; | |
276 | } | |
277 | else | |
278 | { | |
279 | r = r - q + 2*d; | |
280 | q -= 2; | |
281 | } | |
282 | } | |
283 | } | |
284 | else /* Implies c1 = b1 */ | |
285 | { /* Hence a1 = d - 1 = 2*b1 - 1 */ | |
286 | if (a0 >= -d) | |
287 | { | |
288 | q = -1; | |
289 | r = a0 + d; | |
290 | } | |
291 | else | |
292 | { | |
293 | q = -2; | |
294 | r = a0 + 2*d; | |
295 | } | |
296 | } | |
297 | } | |
298 | ||
299 | *rp = r; | |
300 | return q; | |
301 | } | |
ce13d15f RK |
302 | #else |
303 | /* If sdiv_qrnnd doesn't exist, define dummy __udiv_w_sdiv. */ | |
996ed075 JJ |
304 | UWtype |
305 | __udiv_w_sdiv (UWtype *rp __attribute__ ((__unused__)), | |
306 | UWtype a1 __attribute__ ((__unused__)), | |
307 | UWtype a0 __attribute__ ((__unused__)), | |
308 | UWtype d __attribute__ ((__unused__))) | |
081f5e7e KG |
309 | { |
310 | return 0; | |
311 | } | |
ce13d15f | 312 | #endif |
431b1ee0 TG |
313 | #endif |
314 | \f | |
536bfcd0 RK |
315 | #if (defined (L_udivdi3) || defined (L_divdi3) || \ |
316 | defined (L_umoddi3) || defined (L_moddi3)) | |
317 | #define L_udivmoddi4 | |
318 | #endif | |
319 | ||
203b91b9 | 320 | #ifdef L_udivmoddi4 |
ab495388 | 321 | static const UQItype __clz_tab[] = |
203b91b9 RS |
322 | { |
323 | 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, | |
324 | 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, | |
325 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, | |
326 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, | |
327 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
328 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
329 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
330 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, | |
331 | }; | |
332 | ||
536bfcd0 RK |
333 | #if (defined (L_udivdi3) || defined (L_divdi3) || \ |
334 | defined (L_umoddi3) || defined (L_moddi3)) | |
335 | static inline | |
336 | #endif | |
996ed075 JJ |
337 | UDWtype |
338 | __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) | |
203b91b9 | 339 | { |
996ed075 JJ |
340 | DWunion ww; |
341 | DWunion nn, dd; | |
342 | DWunion rr; | |
343 | UWtype d0, d1, n0, n1, n2; | |
344 | UWtype q0, q1; | |
345 | UWtype b, bm; | |
203b91b9 RS |
346 | |
347 | nn.ll = n; | |
348 | dd.ll = d; | |
349 | ||
350 | d0 = dd.s.low; | |
351 | d1 = dd.s.high; | |
352 | n0 = nn.s.low; | |
353 | n1 = nn.s.high; | |
354 | ||
355 | #if !UDIV_NEEDS_NORMALIZATION | |
356 | if (d1 == 0) | |
357 | { | |
358 | if (d0 > n1) | |
359 | { | |
360 | /* 0q = nn / 0D */ | |
361 | ||
362 | udiv_qrnnd (q0, n0, n1, n0, d0); | |
363 | q1 = 0; | |
364 | ||
365 | /* Remainder in n0. */ | |
366 | } | |
367 | else | |
368 | { | |
369 | /* qq = NN / 0d */ | |
370 | ||
371 | if (d0 == 0) | |
372 | d0 = 1 / d0; /* Divide intentionally by zero. */ | |
373 | ||
374 | udiv_qrnnd (q1, n1, 0, n1, d0); | |
375 | udiv_qrnnd (q0, n0, n1, n0, d0); | |
376 | ||
377 | /* Remainder in n0. */ | |
378 | } | |
379 | ||
380 | if (rp != 0) | |
381 | { | |
382 | rr.s.low = n0; | |
383 | rr.s.high = 0; | |
384 | *rp = rr.ll; | |
385 | } | |
386 | } | |
387 | ||
388 | #else /* UDIV_NEEDS_NORMALIZATION */ | |
389 | ||
390 | if (d1 == 0) | |
391 | { | |
392 | if (d0 > n1) | |
393 | { | |
394 | /* 0q = nn / 0D */ | |
395 | ||
396 | count_leading_zeros (bm, d0); | |
397 | ||
398 | if (bm != 0) | |
399 | { | |
400 | /* Normalize, i.e. make the most significant bit of the | |
401 | denominator set. */ | |
402 | ||
403 | d0 = d0 << bm; | |
996ed075 | 404 | n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); |
203b91b9 RS |
405 | n0 = n0 << bm; |
406 | } | |
407 | ||
408 | udiv_qrnnd (q0, n0, n1, n0, d0); | |
409 | q1 = 0; | |
410 | ||
411 | /* Remainder in n0 >> bm. */ | |
412 | } | |
413 | else | |
414 | { | |
415 | /* qq = NN / 0d */ | |
416 | ||
417 | if (d0 == 0) | |
418 | d0 = 1 / d0; /* Divide intentionally by zero. */ | |
419 | ||
420 | count_leading_zeros (bm, d0); | |
421 | ||
422 | if (bm == 0) | |
423 | { | |
424 | /* From (n1 >= d0) /\ (the most significant bit of d0 is set), | |
425 | conclude (the most significant bit of n1 is set) /\ (the | |
426 | leading quotient digit q1 = 1). | |
427 | ||
428 | This special case is necessary, not an optimization. | |
996ed075 | 429 | (Shifts counts of W_TYPE_SIZE are undefined.) */ |
203b91b9 RS |
430 | |
431 | n1 -= d0; | |
432 | q1 = 1; | |
433 | } | |
434 | else | |
435 | { | |
436 | /* Normalize. */ | |
437 | ||
996ed075 | 438 | b = W_TYPE_SIZE - bm; |
203b91b9 RS |
439 | |
440 | d0 = d0 << bm; | |
441 | n2 = n1 >> b; | |
442 | n1 = (n1 << bm) | (n0 >> b); | |
443 | n0 = n0 << bm; | |
444 | ||
445 | udiv_qrnnd (q1, n1, n2, n1, d0); | |
446 | } | |
447 | ||
0f41302f | 448 | /* n1 != d0... */ |
203b91b9 RS |
449 | |
450 | udiv_qrnnd (q0, n0, n1, n0, d0); | |
451 | ||
452 | /* Remainder in n0 >> bm. */ | |
453 | } | |
454 | ||
455 | if (rp != 0) | |
456 | { | |
457 | rr.s.low = n0 >> bm; | |
458 | rr.s.high = 0; | |
459 | *rp = rr.ll; | |
460 | } | |
461 | } | |
462 | #endif /* UDIV_NEEDS_NORMALIZATION */ | |
463 | ||
464 | else | |
465 | { | |
466 | if (d1 > n1) | |
467 | { | |
468 | /* 00 = nn / DD */ | |
469 | ||
470 | q0 = 0; | |
471 | q1 = 0; | |
472 | ||
473 | /* Remainder in n1n0. */ | |
474 | if (rp != 0) | |
475 | { | |
476 | rr.s.low = n0; | |
477 | rr.s.high = n1; | |
478 | *rp = rr.ll; | |
479 | } | |
480 | } | |
481 | else | |
482 | { | |
483 | /* 0q = NN / dd */ | |
484 | ||
485 | count_leading_zeros (bm, d1); | |
486 | if (bm == 0) | |
487 | { | |
488 | /* From (n1 >= d1) /\ (the most significant bit of d1 is set), | |
489 | conclude (the most significant bit of n1 is set) /\ (the | |
490 | quotient digit q0 = 0 or 1). | |
491 | ||
492 | This special case is necessary, not an optimization. */ | |
493 | ||
494 | /* The condition on the next line takes advantage of that | |
495 | n1 >= d1 (true due to program flow). */ | |
496 | if (n1 > d1 || n0 >= d0) | |
497 | { | |
498 | q0 = 1; | |
499 | sub_ddmmss (n1, n0, n1, n0, d1, d0); | |
500 | } | |
501 | else | |
502 | q0 = 0; | |
503 | ||
504 | q1 = 0; | |
505 | ||
506 | if (rp != 0) | |
507 | { | |
508 | rr.s.low = n0; | |
509 | rr.s.high = n1; | |
510 | *rp = rr.ll; | |
511 | } | |
512 | } | |
513 | else | |
514 | { | |
996ed075 | 515 | UWtype m1, m0; |
203b91b9 RS |
516 | /* Normalize. */ |
517 | ||
996ed075 | 518 | b = W_TYPE_SIZE - bm; |
203b91b9 RS |
519 | |
520 | d1 = (d1 << bm) | (d0 >> b); | |
521 | d0 = d0 << bm; | |
522 | n2 = n1 >> b; | |
523 | n1 = (n1 << bm) | (n0 >> b); | |
524 | n0 = n0 << bm; | |
525 | ||
526 | udiv_qrnnd (q0, n1, n2, n1, d1); | |
527 | umul_ppmm (m1, m0, q0, d0); | |
528 | ||
529 | if (m1 > n1 || (m1 == n1 && m0 > n0)) | |
530 | { | |
531 | q0--; | |
532 | sub_ddmmss (m1, m0, m1, m0, d1, d0); | |
533 | } | |
534 | ||
535 | q1 = 0; | |
536 | ||
537 | /* Remainder in (n1n0 - m1m0) >> bm. */ | |
538 | if (rp != 0) | |
539 | { | |
540 | sub_ddmmss (n1, n0, n1, n0, m1, m0); | |
541 | rr.s.low = (n1 << b) | (n0 >> bm); | |
542 | rr.s.high = n1 >> bm; | |
543 | *rp = rr.ll; | |
544 | } | |
545 | } | |
546 | } | |
547 | } | |
548 | ||
549 | ww.s.low = q0; | |
550 | ww.s.high = q1; | |
551 | return ww.ll; | |
552 | } | |
553 | #endif | |
554 | ||
555 | #ifdef L_divdi3 | |
996ed075 JJ |
556 | DWtype |
557 | __divdi3 (DWtype u, DWtype v) | |
203b91b9 | 558 | { |
b799cfc3 | 559 | word_type c = 0; |
996ed075 JJ |
560 | DWunion uu, vv; |
561 | DWtype w; | |
203b91b9 RS |
562 | |
563 | uu.ll = u; | |
564 | vv.ll = v; | |
565 | ||
566 | if (uu.s.high < 0) | |
567 | c = ~c, | |
568 | uu.ll = __negdi2 (uu.ll); | |
569 | if (vv.s.high < 0) | |
570 | c = ~c, | |
571 | vv.ll = __negdi2 (vv.ll); | |
572 | ||
996ed075 | 573 | w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); |
203b91b9 RS |
574 | if (c) |
575 | w = __negdi2 (w); | |
576 | ||
577 | return w; | |
578 | } | |
579 | #endif | |
580 | ||
581 | #ifdef L_moddi3 | |
996ed075 JJ |
582 | DWtype |
583 | __moddi3 (DWtype u, DWtype v) | |
203b91b9 | 584 | { |
b799cfc3 | 585 | word_type c = 0; |
996ed075 JJ |
586 | DWunion uu, vv; |
587 | DWtype w; | |
203b91b9 RS |
588 | |
589 | uu.ll = u; | |
590 | vv.ll = v; | |
591 | ||
592 | if (uu.s.high < 0) | |
593 | c = ~c, | |
594 | uu.ll = __negdi2 (uu.ll); | |
595 | if (vv.s.high < 0) | |
596 | vv.ll = __negdi2 (vv.ll); | |
597 | ||
598 | (void) __udivmoddi4 (uu.ll, vv.ll, &w); | |
599 | if (c) | |
600 | w = __negdi2 (w); | |
601 | ||
602 | return w; | |
603 | } | |
604 | #endif | |
605 | ||
606 | #ifdef L_umoddi3 | |
996ed075 JJ |
607 | UDWtype |
608 | __umoddi3 (UDWtype u, UDWtype v) | |
203b91b9 | 609 | { |
996ed075 | 610 | UDWtype w; |
203b91b9 RS |
611 | |
612 | (void) __udivmoddi4 (u, v, &w); | |
613 | ||
614 | return w; | |
615 | } | |
616 | #endif | |
617 | ||
618 | #ifdef L_udivdi3 | |
996ed075 JJ |
619 | UDWtype |
620 | __udivdi3 (UDWtype n, UDWtype d) | |
203b91b9 | 621 | { |
996ed075 | 622 | return __udivmoddi4 (n, d, (UDWtype *) 0); |
203b91b9 RS |
623 | } |
624 | #endif | |
625 | \f | |
626 | #ifdef L_cmpdi2 | |
4be7c28f | 627 | word_type |
996ed075 | 628 | __cmpdi2 (DWtype a, DWtype b) |
203b91b9 | 629 | { |
996ed075 | 630 | DWunion au, bu; |
203b91b9 RS |
631 | |
632 | au.ll = a, bu.ll = b; | |
633 | ||
634 | if (au.s.high < bu.s.high) | |
635 | return 0; | |
636 | else if (au.s.high > bu.s.high) | |
637 | return 2; | |
996ed075 | 638 | if ((UWtype) au.s.low < (UWtype) bu.s.low) |
203b91b9 | 639 | return 0; |
996ed075 | 640 | else if ((UWtype) au.s.low > (UWtype) bu.s.low) |
203b91b9 RS |
641 | return 2; |
642 | return 1; | |
643 | } | |
644 | #endif | |
645 | ||
646 | #ifdef L_ucmpdi2 | |
4be7c28f | 647 | word_type |
996ed075 | 648 | __ucmpdi2 (DWtype a, DWtype b) |
203b91b9 | 649 | { |
996ed075 | 650 | DWunion au, bu; |
203b91b9 RS |
651 | |
652 | au.ll = a, bu.ll = b; | |
653 | ||
996ed075 | 654 | if ((UWtype) au.s.high < (UWtype) bu.s.high) |
203b91b9 | 655 | return 0; |
996ed075 | 656 | else if ((UWtype) au.s.high > (UWtype) bu.s.high) |
203b91b9 | 657 | return 2; |
996ed075 | 658 | if ((UWtype) au.s.low < (UWtype) bu.s.low) |
203b91b9 | 659 | return 0; |
996ed075 | 660 | else if ((UWtype) au.s.low > (UWtype) bu.s.low) |
203b91b9 RS |
661 | return 2; |
662 | return 1; | |
663 | } | |
664 | #endif | |
665 | \f | |
eaa4b44c | 666 | #if defined(L_fixunstfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128) |
996ed075 JJ |
667 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
668 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
ab495388 | 669 | |
996ed075 | 670 | DWtype |
37ef1054 | 671 | __fixunstfdi (TFtype a) |
ab495388 RS |
672 | { |
673 | TFtype b; | |
996ed075 | 674 | UDWtype v; |
ab495388 RS |
675 | |
676 | if (a < 0) | |
677 | return 0; | |
678 | ||
679 | /* Compute high word of result, as a flonum. */ | |
680 | b = (a / HIGH_WORD_COEFF); | |
996ed075 | 681 | /* Convert that to fixed (but not to DWtype!), |
ab495388 | 682 | and shift it into the high word. */ |
996ed075 | 683 | v = (UWtype) b; |
ab495388 RS |
684 | v <<= WORD_SIZE; |
685 | /* Remove high part from the TFtype, leaving the low part as flonum. */ | |
686 | a -= (TFtype)v; | |
996ed075 | 687 | /* Convert that to fixed (but not to DWtype!) and add it in. |
ab495388 RS |
688 | Sometimes A comes out negative. This is significant, since |
689 | A has more bits than a long int does. */ | |
690 | if (a < 0) | |
996ed075 | 691 | v -= (UWtype) (- a); |
ab495388 | 692 | else |
996ed075 | 693 | v += (UWtype) a; |
ab495388 RS |
694 | return v; |
695 | } | |
696 | #endif | |
697 | ||
eaa4b44c | 698 | #if defined(L_fixtfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128) |
996ed075 | 699 | DWtype |
37ef1054 | 700 | __fixtfdi (TFtype a) |
ab495388 RS |
701 | { |
702 | if (a < 0) | |
703 | return - __fixunstfdi (-a); | |
704 | return __fixunstfdi (a); | |
705 | } | |
706 | #endif | |
707 | ||
eaa4b44c | 708 | #if defined(L_fixunsxfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96) |
996ed075 JJ |
709 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
710 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
e0799b34 | 711 | |
996ed075 | 712 | DWtype |
37ef1054 | 713 | __fixunsxfdi (XFtype a) |
e0799b34 RS |
714 | { |
715 | XFtype b; | |
996ed075 | 716 | UDWtype v; |
e0799b34 RS |
717 | |
718 | if (a < 0) | |
719 | return 0; | |
720 | ||
721 | /* Compute high word of result, as a flonum. */ | |
722 | b = (a / HIGH_WORD_COEFF); | |
996ed075 | 723 | /* Convert that to fixed (but not to DWtype!), |
e0799b34 | 724 | and shift it into the high word. */ |
996ed075 | 725 | v = (UWtype) b; |
e0799b34 RS |
726 | v <<= WORD_SIZE; |
727 | /* Remove high part from the XFtype, leaving the low part as flonum. */ | |
728 | a -= (XFtype)v; | |
996ed075 | 729 | /* Convert that to fixed (but not to DWtype!) and add it in. |
e0799b34 RS |
730 | Sometimes A comes out negative. This is significant, since |
731 | A has more bits than a long int does. */ | |
732 | if (a < 0) | |
996ed075 | 733 | v -= (UWtype) (- a); |
e0799b34 | 734 | else |
996ed075 | 735 | v += (UWtype) a; |
e0799b34 RS |
736 | return v; |
737 | } | |
738 | #endif | |
739 | ||
eaa4b44c | 740 | #if defined(L_fixxfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96) |
996ed075 | 741 | DWtype |
37ef1054 | 742 | __fixxfdi (XFtype a) |
e0799b34 RS |
743 | { |
744 | if (a < 0) | |
745 | return - __fixunsxfdi (-a); | |
746 | return __fixunsxfdi (a); | |
747 | } | |
748 | #endif | |
749 | ||
203b91b9 | 750 | #ifdef L_fixunsdfdi |
996ed075 JJ |
751 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
752 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
203b91b9 | 753 | |
996ed075 | 754 | DWtype |
37ef1054 | 755 | __fixunsdfdi (DFtype a) |
203b91b9 | 756 | { |
ab495388 | 757 | DFtype b; |
996ed075 | 758 | UDWtype v; |
203b91b9 RS |
759 | |
760 | if (a < 0) | |
761 | return 0; | |
762 | ||
763 | /* Compute high word of result, as a flonum. */ | |
764 | b = (a / HIGH_WORD_COEFF); | |
996ed075 | 765 | /* Convert that to fixed (but not to DWtype!), |
203b91b9 | 766 | and shift it into the high word. */ |
996ed075 | 767 | v = (UWtype) b; |
203b91b9 | 768 | v <<= WORD_SIZE; |
ab495388 RS |
769 | /* Remove high part from the DFtype, leaving the low part as flonum. */ |
770 | a -= (DFtype)v; | |
996ed075 | 771 | /* Convert that to fixed (but not to DWtype!) and add it in. |
203b91b9 RS |
772 | Sometimes A comes out negative. This is significant, since |
773 | A has more bits than a long int does. */ | |
774 | if (a < 0) | |
996ed075 | 775 | v -= (UWtype) (- a); |
203b91b9 | 776 | else |
996ed075 | 777 | v += (UWtype) a; |
203b91b9 RS |
778 | return v; |
779 | } | |
780 | #endif | |
781 | ||
782 | #ifdef L_fixdfdi | |
996ed075 | 783 | DWtype |
37ef1054 | 784 | __fixdfdi (DFtype a) |
203b91b9 RS |
785 | { |
786 | if (a < 0) | |
787 | return - __fixunsdfdi (-a); | |
788 | return __fixunsdfdi (a); | |
789 | } | |
790 | #endif | |
791 | ||
792 | #ifdef L_fixunssfdi | |
996ed075 JJ |
793 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
794 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
203b91b9 | 795 | |
996ed075 | 796 | DWtype |
ab495388 | 797 | __fixunssfdi (SFtype original_a) |
203b91b9 | 798 | { |
ab495388 | 799 | /* Convert the SFtype to a DFtype, because that is surely not going |
203b91b9 | 800 | to lose any bits. Some day someone else can write a faster version |
ab495388 RS |
801 | that avoids converting to DFtype, and verify it really works right. */ |
802 | DFtype a = original_a; | |
803 | DFtype b; | |
996ed075 | 804 | UDWtype v; |
203b91b9 RS |
805 | |
806 | if (a < 0) | |
807 | return 0; | |
808 | ||
809 | /* Compute high word of result, as a flonum. */ | |
810 | b = (a / HIGH_WORD_COEFF); | |
996ed075 | 811 | /* Convert that to fixed (but not to DWtype!), |
203b91b9 | 812 | and shift it into the high word. */ |
996ed075 | 813 | v = (UWtype) b; |
203b91b9 | 814 | v <<= WORD_SIZE; |
ab495388 RS |
815 | /* Remove high part from the DFtype, leaving the low part as flonum. */ |
816 | a -= (DFtype)v; | |
996ed075 | 817 | /* Convert that to fixed (but not to DWtype!) and add it in. |
203b91b9 RS |
818 | Sometimes A comes out negative. This is significant, since |
819 | A has more bits than a long int does. */ | |
820 | if (a < 0) | |
996ed075 | 821 | v -= (UWtype) (- a); |
203b91b9 | 822 | else |
996ed075 | 823 | v += (UWtype) a; |
203b91b9 RS |
824 | return v; |
825 | } | |
826 | #endif | |
827 | ||
828 | #ifdef L_fixsfdi | |
996ed075 | 829 | DWtype |
ab495388 | 830 | __fixsfdi (SFtype a) |
203b91b9 RS |
831 | { |
832 | if (a < 0) | |
833 | return - __fixunssfdi (-a); | |
834 | return __fixunssfdi (a); | |
835 | } | |
836 | #endif | |
837 | ||
eaa4b44c | 838 | #if defined(L_floatdixf) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96) |
996ed075 JJ |
839 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
840 | #define HIGH_HALFWORD_COEFF (((UDWtype) 1) << (WORD_SIZE / 2)) | |
841 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
e0799b34 RS |
842 | |
843 | XFtype | |
996ed075 | 844 | __floatdixf (DWtype u) |
e0799b34 RS |
845 | { |
846 | XFtype d; | |
e0799b34 | 847 | |
996ed075 | 848 | d = (Wtype) (u >> WORD_SIZE); |
e0799b34 RS |
849 | d *= HIGH_HALFWORD_COEFF; |
850 | d *= HIGH_HALFWORD_COEFF; | |
996ed075 | 851 | d += (UWtype) (u & (HIGH_WORD_COEFF - 1)); |
e0799b34 | 852 | |
e5e809f4 | 853 | return d; |
e0799b34 RS |
854 | } |
855 | #endif | |
856 | ||
eaa4b44c | 857 | #if defined(L_floatditf) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128) |
996ed075 JJ |
858 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
859 | #define HIGH_HALFWORD_COEFF (((UDWtype) 1) << (WORD_SIZE / 2)) | |
860 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
ab495388 RS |
861 | |
862 | TFtype | |
996ed075 | 863 | __floatditf (DWtype u) |
ab495388 RS |
864 | { |
865 | TFtype d; | |
ab495388 | 866 | |
996ed075 | 867 | d = (Wtype) (u >> WORD_SIZE); |
ab495388 RS |
868 | d *= HIGH_HALFWORD_COEFF; |
869 | d *= HIGH_HALFWORD_COEFF; | |
996ed075 | 870 | d += (UWtype) (u & (HIGH_WORD_COEFF - 1)); |
ab495388 | 871 | |
e5e809f4 | 872 | return d; |
ab495388 RS |
873 | } |
874 | #endif | |
875 | ||
203b91b9 | 876 | #ifdef L_floatdidf |
996ed075 JJ |
877 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
878 | #define HIGH_HALFWORD_COEFF (((UDWtype) 1) << (WORD_SIZE / 2)) | |
879 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
203b91b9 | 880 | |
ab495388 | 881 | DFtype |
996ed075 | 882 | __floatdidf (DWtype u) |
203b91b9 | 883 | { |
ab495388 | 884 | DFtype d; |
203b91b9 | 885 | |
996ed075 | 886 | d = (Wtype) (u >> WORD_SIZE); |
203b91b9 RS |
887 | d *= HIGH_HALFWORD_COEFF; |
888 | d *= HIGH_HALFWORD_COEFF; | |
996ed075 | 889 | d += (UWtype) (u & (HIGH_WORD_COEFF - 1)); |
203b91b9 | 890 | |
e5e809f4 | 891 | return d; |
203b91b9 RS |
892 | } |
893 | #endif | |
894 | ||
895 | #ifdef L_floatdisf | |
996ed075 JJ |
896 | #define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) |
897 | #define HIGH_HALFWORD_COEFF (((UDWtype) 1) << (WORD_SIZE / 2)) | |
898 | #define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) | |
899 | #define DI_SIZE (sizeof (DWtype) * BITS_PER_UNIT) | |
cac896d8 RK |
900 | |
901 | /* Define codes for all the float formats that we know of. Note | |
902 | that this is copied from real.h. */ | |
903 | ||
904 | #define UNKNOWN_FLOAT_FORMAT 0 | |
905 | #define IEEE_FLOAT_FORMAT 1 | |
906 | #define VAX_FLOAT_FORMAT 2 | |
907 | #define IBM_FLOAT_FORMAT 3 | |
908 | ||
909 | /* Default to IEEE float if not specified. Nearly all machines use it. */ | |
910 | #ifndef HOST_FLOAT_FORMAT | |
911 | #define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT | |
912 | #endif | |
913 | ||
914 | #if HOST_FLOAT_FORMAT == IEEE_FLOAT_FORMAT | |
d9e1ab8d RK |
915 | #define DF_SIZE 53 |
916 | #define SF_SIZE 24 | |
cac896d8 RK |
917 | #endif |
918 | ||
919 | #if HOST_FLOAT_FORMAT == IBM_FLOAT_FORMAT | |
d9e1ab8d RK |
920 | #define DF_SIZE 56 |
921 | #define SF_SIZE 24 | |
cac896d8 RK |
922 | #endif |
923 | ||
924 | #if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT | |
d9e1ab8d RK |
925 | #define DF_SIZE 56 |
926 | #define SF_SIZE 24 | |
d9e1ab8d | 927 | #endif |
203b91b9 | 928 | |
ab495388 | 929 | SFtype |
996ed075 | 930 | __floatdisf (DWtype u) |
203b91b9 | 931 | { |
56b03d5f RS |
932 | /* Do the calculation in DFmode |
933 | so that we don't lose any of the precision of the high word | |
934 | while multiplying it. */ | |
935 | DFtype f; | |
203b91b9 | 936 | |
d9e1ab8d RK |
937 | /* Protect against double-rounding error. |
938 | Represent any low-order bits, that might be truncated in DFmode, | |
939 | by a bit that won't be lost. The bit can go in anywhere below the | |
940 | rounding position of the SFmode. A fixed mask and bit position | |
941 | handles all usual configurations. It doesn't handle the case | |
942 | of 128-bit DImode, however. */ | |
943 | if (DF_SIZE < DI_SIZE | |
944 | && DF_SIZE > (DI_SIZE - DF_SIZE + SF_SIZE)) | |
945 | { | |
996ed075 JJ |
946 | #define REP_BIT ((UWtype) 1 << (DI_SIZE - DF_SIZE)) |
947 | if (! (- ((DWtype) 1 << DF_SIZE) < u | |
948 | && u < ((DWtype) 1 << DF_SIZE))) | |
d9e1ab8d | 949 | { |
996ed075 | 950 | if ((UWtype) u & (REP_BIT - 1)) |
d9e1ab8d RK |
951 | u |= REP_BIT; |
952 | } | |
953 | } | |
996ed075 | 954 | f = (Wtype) (u >> WORD_SIZE); |
203b91b9 RS |
955 | f *= HIGH_HALFWORD_COEFF; |
956 | f *= HIGH_HALFWORD_COEFF; | |
996ed075 | 957 | f += (UWtype) (u & (HIGH_WORD_COEFF - 1)); |
203b91b9 | 958 | |
e5e809f4 | 959 | return (SFtype) f; |
203b91b9 RS |
960 | } |
961 | #endif | |
962 | ||
eaa4b44c | 963 | #if defined(L_fixunsxfsi) && LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96 |
3f3d2ec8 JW |
964 | /* Reenable the normal types, in case limits.h needs them. */ |
965 | #undef char | |
966 | #undef short | |
967 | #undef int | |
968 | #undef long | |
969 | #undef unsigned | |
970 | #undef float | |
971 | #undef double | |
c07e26bd RK |
972 | #undef MIN |
973 | #undef MAX | |
a99598c9 | 974 | #include <limits.h> |
e0799b34 | 975 | |
996ed075 | 976 | UWtype |
37ef1054 | 977 | __fixunsxfsi (XFtype a) |
e0799b34 RS |
978 | { |
979 | if (a >= - (DFtype) LONG_MIN) | |
996ed075 JJ |
980 | return (Wtype) (a + LONG_MIN) - LONG_MIN; |
981 | return (Wtype) a; | |
e0799b34 RS |
982 | } |
983 | #endif | |
984 | ||
203b91b9 | 985 | #ifdef L_fixunsdfsi |
3f3d2ec8 JW |
986 | /* Reenable the normal types, in case limits.h needs them. */ |
987 | #undef char | |
988 | #undef short | |
989 | #undef int | |
990 | #undef long | |
991 | #undef unsigned | |
992 | #undef float | |
993 | #undef double | |
c07e26bd RK |
994 | #undef MIN |
995 | #undef MAX | |
a99598c9 | 996 | #include <limits.h> |
203b91b9 | 997 | |
996ed075 | 998 | UWtype |
37ef1054 | 999 | __fixunsdfsi (DFtype a) |
203b91b9 | 1000 | { |
ab495388 | 1001 | if (a >= - (DFtype) LONG_MIN) |
996ed075 JJ |
1002 | return (Wtype) (a + LONG_MIN) - LONG_MIN; |
1003 | return (Wtype) a; | |
203b91b9 RS |
1004 | } |
1005 | #endif | |
1006 | ||
1007 | #ifdef L_fixunssfsi | |
3f3d2ec8 JW |
1008 | /* Reenable the normal types, in case limits.h needs them. */ |
1009 | #undef char | |
1010 | #undef short | |
1011 | #undef int | |
1012 | #undef long | |
1013 | #undef unsigned | |
1014 | #undef float | |
1015 | #undef double | |
c07e26bd RK |
1016 | #undef MIN |
1017 | #undef MAX | |
a99598c9 | 1018 | #include <limits.h> |
203b91b9 | 1019 | |
996ed075 | 1020 | UWtype |
ab495388 | 1021 | __fixunssfsi (SFtype a) |
203b91b9 | 1022 | { |
ab495388 | 1023 | if (a >= - (SFtype) LONG_MIN) |
996ed075 JJ |
1024 | return (Wtype) (a + LONG_MIN) - LONG_MIN; |
1025 | return (Wtype) a; | |
203b91b9 RS |
1026 | } |
1027 | #endif | |
1028 | \f | |
ab495388 RS |
1029 | /* From here on down, the routines use normal data types. */ |
1030 | ||
1031 | #define SItype bogus_type | |
1032 | #define USItype bogus_type | |
1033 | #define DItype bogus_type | |
1034 | #define UDItype bogus_type | |
1035 | #define SFtype bogus_type | |
1036 | #define DFtype bogus_type | |
996ed075 JJ |
1037 | #undef Wtype |
1038 | #undef UWtype | |
1039 | #undef HWtype | |
1040 | #undef UHWtype | |
1041 | #undef DWtype | |
1042 | #undef UDWtype | |
ab495388 RS |
1043 | |
1044 | #undef char | |
1045 | #undef short | |
1046 | #undef int | |
1047 | #undef long | |
1048 | #undef unsigned | |
1049 | #undef float | |
1050 | #undef double | |
9bd23d2c RS |
1051 | \f |
1052 | #ifdef L__gcc_bcmp | |
1053 | ||
1054 | /* Like bcmp except the sign is meaningful. | |
9faa82d8 | 1055 | Result is negative if S1 is less than S2, |
9bd23d2c RS |
1056 | positive if S1 is greater, 0 if S1 and S2 are equal. */ |
1057 | ||
1058 | int | |
299b83b7 | 1059 | __gcc_bcmp (const unsigned char *s1, const unsigned char *s2, size_t size) |
9bd23d2c RS |
1060 | { |
1061 | while (size > 0) | |
1062 | { | |
78e33213 | 1063 | unsigned char c1 = *s1++, c2 = *s2++; |
9bd23d2c RS |
1064 | if (c1 != c2) |
1065 | return c1 - c2; | |
1066 | size--; | |
1067 | } | |
1068 | return 0; | |
1069 | } | |
ab495388 | 1070 | |
9bd23d2c RS |
1071 | #endif |
1072 | \f\f | |
2e06e616 RK |
1073 | #ifdef L__dummy |
1074 | void | |
3e7d8ef1 | 1075 | __dummy (void) {} |
2e06e616 RK |
1076 | #endif |
1077 | ||
203b91b9 RS |
1078 | #ifdef L_varargs |
1079 | #ifdef __i860__ | |
600032fc | 1080 | #if defined(__svr4__) || defined(__alliant__) |
203b91b9 RS |
1081 | asm (" .text"); |
1082 | asm (" .align 4"); | |
1083 | ||
27d21d32 | 1084 | /* The Alliant needs the added underscore. */ |
203b91b9 RS |
1085 | asm (".globl __builtin_saveregs"); |
1086 | asm ("__builtin_saveregs:"); | |
27d21d32 RS |
1087 | asm (".globl ___builtin_saveregs"); |
1088 | asm ("___builtin_saveregs:"); | |
1089 | ||
1090 | asm (" andnot 0x0f,%sp,%sp"); /* round down to 16-byte boundary */ | |
203b91b9 RS |
1091 | asm (" adds -96,%sp,%sp"); /* allocate stack space for reg save |
1092 | area and also for a new va_list | |
1093 | structure */ | |
1094 | /* Save all argument registers in the arg reg save area. The | |
1095 | arg reg save area must have the following layout (according | |
1096 | to the svr4 ABI): | |
1097 | ||
1098 | struct { | |
1099 | union { | |
1100 | float freg[8]; | |
1101 | double dreg[4]; | |
1102 | } float_regs; | |
1103 | long ireg[12]; | |
1104 | }; | |
1105 | */ | |
1106 | ||
1107 | asm (" fst.q %f8, 0(%sp)"); /* save floating regs (f8-f15) */ | |
1108 | asm (" fst.q %f12,16(%sp)"); | |
1109 | ||
1110 | asm (" st.l %r16,32(%sp)"); /* save integer regs (r16-r27) */ | |
1111 | asm (" st.l %r17,36(%sp)"); | |
1112 | asm (" st.l %r18,40(%sp)"); | |
1113 | asm (" st.l %r19,44(%sp)"); | |
1114 | asm (" st.l %r20,48(%sp)"); | |
1115 | asm (" st.l %r21,52(%sp)"); | |
1116 | asm (" st.l %r22,56(%sp)"); | |
1117 | asm (" st.l %r23,60(%sp)"); | |
1118 | asm (" st.l %r24,64(%sp)"); | |
1119 | asm (" st.l %r25,68(%sp)"); | |
1120 | asm (" st.l %r26,72(%sp)"); | |
1121 | asm (" st.l %r27,76(%sp)"); | |
1122 | ||
1123 | asm (" adds 80,%sp,%r16"); /* compute the address of the new | |
1124 | va_list structure. Put in into | |
1125 | r16 so that it will be returned | |
1126 | to the caller. */ | |
1127 | ||
1128 | /* Initialize all fields of the new va_list structure. This | |
1129 | structure looks like: | |
1130 | ||
1131 | typedef struct { | |
1132 | unsigned long ireg_used; | |
1133 | unsigned long freg_used; | |
1134 | long *reg_base; | |
1135 | long *mem_ptr; | |
1136 | } va_list; | |
1137 | */ | |
1138 | ||
1139 | asm (" st.l %r0, 0(%r16)"); /* nfixed */ | |
1140 | asm (" st.l %r0, 4(%r16)"); /* nfloating */ | |
1141 | asm (" st.l %sp, 8(%r16)"); /* __va_ctl points to __va_struct. */ | |
1142 | asm (" bri %r1"); /* delayed return */ | |
1143 | asm (" st.l %r28,12(%r16)"); /* pointer to overflow args */ | |
1144 | ||
24e4939e | 1145 | #else /* not __svr4__ */ |
6aadf9c2 RS |
1146 | #if defined(__PARAGON__) |
1147 | /* | |
1148 | * we'll use SVR4-ish varargs but need SVR3.2 assembler syntax, | |
1149 | * and we stand a better chance of hooking into libraries | |
1150 | * compiled by PGI. [andyp@ssd.intel.com] | |
1151 | */ | |
1152 | asm (" .text"); | |
1153 | asm (" .align 4"); | |
1154 | asm (".globl __builtin_saveregs"); | |
1155 | asm ("__builtin_saveregs:"); | |
1156 | asm (".globl ___builtin_saveregs"); | |
1157 | asm ("___builtin_saveregs:"); | |
1158 | ||
1159 | asm (" andnot 0x0f,sp,sp"); /* round down to 16-byte boundary */ | |
1160 | asm (" adds -96,sp,sp"); /* allocate stack space for reg save | |
1161 | area and also for a new va_list | |
1162 | structure */ | |
1163 | /* Save all argument registers in the arg reg save area. The | |
1164 | arg reg save area must have the following layout (according | |
1165 | to the svr4 ABI): | |
1166 | ||
1167 | struct { | |
1168 | union { | |
1169 | float freg[8]; | |
1170 | double dreg[4]; | |
1171 | } float_regs; | |
1172 | long ireg[12]; | |
1173 | }; | |
1174 | */ | |
1175 | ||
1176 | asm (" fst.q f8, 0(sp)"); | |
1177 | asm (" fst.q f12,16(sp)"); | |
1178 | asm (" st.l r16,32(sp)"); | |
1179 | asm (" st.l r17,36(sp)"); | |
1180 | asm (" st.l r18,40(sp)"); | |
1181 | asm (" st.l r19,44(sp)"); | |
1182 | asm (" st.l r20,48(sp)"); | |
1183 | asm (" st.l r21,52(sp)"); | |
1184 | asm (" st.l r22,56(sp)"); | |
1185 | asm (" st.l r23,60(sp)"); | |
1186 | asm (" st.l r24,64(sp)"); | |
1187 | asm (" st.l r25,68(sp)"); | |
1188 | asm (" st.l r26,72(sp)"); | |
1189 | asm (" st.l r27,76(sp)"); | |
1190 | ||
1191 | asm (" adds 80,sp,r16"); /* compute the address of the new | |
1192 | va_list structure. Put in into | |
1193 | r16 so that it will be returned | |
1194 | to the caller. */ | |
1195 | ||
1196 | /* Initialize all fields of the new va_list structure. This | |
1197 | structure looks like: | |
1198 | ||
1199 | typedef struct { | |
1200 | unsigned long ireg_used; | |
1201 | unsigned long freg_used; | |
1202 | long *reg_base; | |
1203 | long *mem_ptr; | |
1204 | } va_list; | |
1205 | */ | |
1206 | ||
1207 | asm (" st.l r0, 0(r16)"); /* nfixed */ | |
1208 | asm (" st.l r0, 4(r16)"); /* nfloating */ | |
1209 | asm (" st.l sp, 8(r16)"); /* __va_ctl points to __va_struct. */ | |
1210 | asm (" bri r1"); /* delayed return */ | |
1211 | asm (" st.l r28,12(r16)"); /* pointer to overflow args */ | |
1212 | #else /* not __PARAGON__ */ | |
203b91b9 RS |
1213 | asm (" .text"); |
1214 | asm (" .align 4"); | |
1215 | ||
1216 | asm (".globl ___builtin_saveregs"); | |
1217 | asm ("___builtin_saveregs:"); | |
1218 | asm (" mov sp,r30"); | |
1219 | asm (" andnot 0x0f,sp,sp"); | |
1220 | asm (" adds -96,sp,sp"); /* allocate sufficient space on the stack */ | |
1221 | ||
1222 | /* Fill in the __va_struct. */ | |
1223 | asm (" st.l r16, 0(sp)"); /* save integer regs (r16-r27) */ | |
1224 | asm (" st.l r17, 4(sp)"); /* int fixed[12] */ | |
1225 | asm (" st.l r18, 8(sp)"); | |
1226 | asm (" st.l r19,12(sp)"); | |
1227 | asm (" st.l r20,16(sp)"); | |
1228 | asm (" st.l r21,20(sp)"); | |
1229 | asm (" st.l r22,24(sp)"); | |
1230 | asm (" st.l r23,28(sp)"); | |
1231 | asm (" st.l r24,32(sp)"); | |
1232 | asm (" st.l r25,36(sp)"); | |
1233 | asm (" st.l r26,40(sp)"); | |
1234 | asm (" st.l r27,44(sp)"); | |
1235 | ||
1236 | asm (" fst.q f8, 48(sp)"); /* save floating regs (f8-f15) */ | |
1237 | asm (" fst.q f12,64(sp)"); /* int floating[8] */ | |
1238 | ||
1239 | /* Fill in the __va_ctl. */ | |
1240 | asm (" st.l sp, 80(sp)"); /* __va_ctl points to __va_struct. */ | |
1241 | asm (" st.l r28,84(sp)"); /* pointer to more args */ | |
1242 | asm (" st.l r0, 88(sp)"); /* nfixed */ | |
1243 | asm (" st.l r0, 92(sp)"); /* nfloating */ | |
1244 | ||
1245 | asm (" adds 80,sp,r16"); /* return address of the __va_ctl. */ | |
1246 | asm (" bri r1"); | |
1247 | asm (" mov r30,sp"); | |
1248 | /* recover stack and pass address to start | |
1249 | of data. */ | |
6aadf9c2 | 1250 | #endif /* not __PARAGON__ */ |
24e4939e | 1251 | #endif /* not __svr4__ */ |
203b91b9 RS |
1252 | #else /* not __i860__ */ |
1253 | #ifdef __sparc__ | |
b335c2cc TW |
1254 | asm (".global __builtin_saveregs"); |
1255 | asm ("__builtin_saveregs:"); | |
203b91b9 RS |
1256 | asm (".global ___builtin_saveregs"); |
1257 | asm ("___builtin_saveregs:"); | |
b1166fae RS |
1258 | #ifdef NEED_PROC_COMMAND |
1259 | asm (".proc 020"); | |
b335c2cc | 1260 | #endif |
203b91b9 RS |
1261 | asm ("st %i0,[%fp+68]"); |
1262 | asm ("st %i1,[%fp+72]"); | |
1263 | asm ("st %i2,[%fp+76]"); | |
1264 | asm ("st %i3,[%fp+80]"); | |
1265 | asm ("st %i4,[%fp+84]"); | |
1266 | asm ("retl"); | |
1267 | asm ("st %i5,[%fp+88]"); | |
b1166fae RS |
1268 | #ifdef NEED_TYPE_COMMAND |
1269 | asm (".type __builtin_saveregs,#function"); | |
1270 | asm (".size __builtin_saveregs,.-__builtin_saveregs"); | |
1271 | #endif | |
203b91b9 RS |
1272 | #else /* not __sparc__ */ |
1273 | #if defined(__MIPSEL__) | defined(__R3000__) | defined(__R2000__) | defined(__mips__) | |
1274 | ||
1275 | asm (" .text"); | |
6003a6bf JL |
1276 | #ifdef __mips16 |
1277 | asm (" .set nomips16"); | |
1278 | #endif | |
203b91b9 RS |
1279 | asm (" .ent __builtin_saveregs"); |
1280 | asm (" .globl __builtin_saveregs"); | |
1281 | asm ("__builtin_saveregs:"); | |
1282 | asm (" sw $4,0($30)"); | |
1283 | asm (" sw $5,4($30)"); | |
1284 | asm (" sw $6,8($30)"); | |
1285 | asm (" sw $7,12($30)"); | |
1286 | asm (" j $31"); | |
1287 | asm (" .end __builtin_saveregs"); | |
0f41302f | 1288 | #else /* not __mips__, etc. */ |
3bd4f3b8 | 1289 | |
299b83b7 | 1290 | void * __attribute__ ((__noreturn__)) |
3e7d8ef1 | 1291 | __builtin_saveregs (void) |
203b91b9 RS |
1292 | { |
1293 | abort (); | |
1294 | } | |
3bd4f3b8 | 1295 | |
203b91b9 RS |
1296 | #endif /* not __mips__ */ |
1297 | #endif /* not __sparc__ */ | |
1298 | #endif /* not __i860__ */ | |
1299 | #endif | |
1300 | \f | |
1301 | #ifdef L_eprintf | |
c74d5583 | 1302 | #ifndef inhibit_libc |
bba2431c | 1303 | |
203b91b9 RS |
1304 | #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ |
1305 | #include <stdio.h> | |
1306 | /* This is used by the `assert' macro. */ | |
1307 | void | |
37ef1054 | 1308 | __eprintf (const char *string, const char *expression, |
10c301ac | 1309 | unsigned int line, const char *filename) |
203b91b9 RS |
1310 | { |
1311 | fprintf (stderr, string, expression, line, filename); | |
1312 | fflush (stderr); | |
1313 | abort (); | |
1314 | } | |
bba2431c RS |
1315 | |
1316 | #endif | |
203b91b9 RS |
1317 | #endif |
1318 | ||
1319 | #ifdef L_bb | |
203b91b9 | 1320 | |
92832bb5 | 1321 | /* Structure emitted by -a */ |
203b91b9 RS |
1322 | struct bb |
1323 | { | |
92832bb5 MM |
1324 | long zero_word; |
1325 | const char *filename; | |
1326 | long *counts; | |
1327 | long ncounts; | |
1328 | struct bb *next; | |
1329 | const unsigned long *addresses; | |
1330 | ||
1331 | /* Older GCC's did not emit these fields. */ | |
1332 | long nwords; | |
1333 | const char **functions; | |
1334 | const long *line_nums; | |
1335 | const char **filenames; | |
90b4a764 | 1336 | char *flags; |
203b91b9 RS |
1337 | }; |
1338 | ||
92832bb5 MM |
1339 | #ifdef BLOCK_PROFILER_CODE |
1340 | BLOCK_PROFILER_CODE | |
1341 | #else | |
c7544ff7 | 1342 | #ifndef inhibit_libc |
92832bb5 MM |
1343 | |
1344 | /* Simple minded basic block profiling output dumper for | |
9faa82d8 | 1345 | systems that don't provide tcov support. At present, |
92832bb5 MM |
1346 | it requires atexit and stdio. */ |
1347 | ||
ebd41309 | 1348 | #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ |
92832bb5 | 1349 | #include <stdio.h> |
b077f3ac | 1350 | char *ctime (); |
203b91b9 | 1351 | |
8b7677be | 1352 | #include "gbl-ctors.h" |
65f7a653 | 1353 | #include "gcov-io.h" |
ac957f13 | 1354 | #include <string.h> |
e09d24ff R |
1355 | #ifdef TARGET_HAS_F_SETLKW |
1356 | #include <fcntl.h> | |
1357 | #include <errno.h> | |
1358 | #endif | |
92832bb5 | 1359 | |
7e6f1890 | 1360 | static struct bb *bb_head; |
92832bb5 | 1361 | |
e8f38d1a DN |
1362 | static int num_digits (long value, int base) __attribute__ ((const)); |
1363 | ||
92832bb5 MM |
1364 | /* Return the number of digits needed to print a value */ |
1365 | /* __inline__ */ static int num_digits (long value, int base) | |
203b91b9 | 1366 | { |
92832bb5 MM |
1367 | int minus = (value < 0 && base != 16); |
1368 | unsigned long v = (minus) ? -value : value; | |
1369 | int ret = minus; | |
203b91b9 | 1370 | |
92832bb5 MM |
1371 | do |
1372 | { | |
1373 | v /= base; | |
1374 | ret++; | |
1375 | } | |
1376 | while (v); | |
1377 | ||
1378 | return ret; | |
203b91b9 RS |
1379 | } |
1380 | ||
92832bb5 MM |
1381 | void |
1382 | __bb_exit_func (void) | |
1383 | { | |
65f7a653 | 1384 | FILE *da_file, *file; |
92832bb5 | 1385 | long time_value; |
65f7a653 DE |
1386 | int i; |
1387 | ||
1388 | if (bb_head == 0) | |
1389 | return; | |
1390 | ||
1391 | i = strlen (bb_head->filename) - 3; | |
1392 | ||
1393 | if (!strcmp (bb_head->filename+i, ".da")) | |
1394 | { | |
1395 | /* Must be -fprofile-arcs not -a. | |
1396 | Dump data in a form that gcov expects. */ | |
1397 | ||
1398 | struct bb *ptr; | |
1399 | ||
1400 | for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) | |
1401 | { | |
e09d24ff R |
1402 | int firstchar; |
1403 | ||
1404 | /* Make sure the output file exists - | |
1405 | but don't clobber exiting data. */ | |
1406 | if ((da_file = fopen (ptr->filename, "a")) != 0) | |
1407 | fclose (da_file); | |
1408 | ||
1409 | /* Need to re-open in order to be able to write from the start. */ | |
1410 | da_file = fopen (ptr->filename, "r+b"); | |
1411 | /* Some old systems might not allow the 'b' mode modifier. | |
1412 | Therefore, try to open without it. This can lead to a race | |
1413 | condition so that when you delete and re-create the file, the | |
1414 | file might be opened in text mode, but then, you shouldn't | |
1415 | delete the file in the first place. */ | |
1416 | if (da_file == 0) | |
1417 | da_file = fopen (ptr->filename, "r+"); | |
1418 | if (da_file == 0) | |
1419 | { | |
1420 | fprintf (stderr, "arc profiling: Can't open output file %s.\n", | |
1421 | ptr->filename); | |
1422 | continue; | |
1423 | } | |
1424 | ||
1425 | /* After a fork, another process might try to read and/or write | |
1426 | the same file simultanously. So if we can, lock the file to | |
1427 | avoid race conditions. */ | |
1428 | #if defined (TARGET_HAS_F_SETLKW) | |
1429 | { | |
1430 | struct flock s_flock; | |
1431 | ||
1432 | s_flock.l_type = F_WRLCK; | |
1433 | s_flock.l_whence = SEEK_SET; | |
1434 | s_flock.l_start = 0; | |
1435 | s_flock.l_len = 1; | |
1436 | s_flock.l_pid = getpid (); | |
1437 | ||
1438 | while (fcntl (fileno (da_file), F_SETLKW, &s_flock) | |
1439 | && errno == EINTR); | |
1440 | } | |
1441 | #endif | |
1442 | ||
1443 | /* If the file is not empty, and the number of counts in it is the | |
1444 | same, then merge them in. */ | |
1445 | firstchar = fgetc (da_file); | |
1446 | if (firstchar == EOF) | |
1447 | { | |
1448 | if (ferror (da_file)) | |
1449 | { | |
1450 | fprintf (stderr, "arc profiling: Can't read output file "); | |
1451 | perror (ptr->filename); | |
1452 | } | |
1453 | } | |
1454 | else | |
65f7a653 DE |
1455 | { |
1456 | long n_counts = 0; | |
65f7a653 | 1457 | |
e09d24ff R |
1458 | if (ungetc (firstchar, da_file) == EOF) |
1459 | rewind (da_file); | |
65f7a653 DE |
1460 | if (__read_long (&n_counts, da_file, 8) != 0) |
1461 | { | |
1462 | fprintf (stderr, "arc profiling: Can't read output file %s.\n", | |
1463 | ptr->filename); | |
1464 | continue; | |
1465 | } | |
1466 | ||
1467 | if (n_counts == ptr->ncounts) | |
1468 | { | |
1469 | int i; | |
1470 | ||
1471 | for (i = 0; i < n_counts; i++) | |
1472 | { | |
1473 | long v = 0; | |
65f7a653 DE |
1474 | |
1475 | if (__read_long (&v, da_file, 8) != 0) | |
1476 | { | |
1477 | fprintf (stderr, "arc profiling: Can't read output file %s.\n", | |
1478 | ptr->filename); | |
1479 | break; | |
1480 | } | |
1481 | ptr->counts[i] += v; | |
1482 | } | |
1483 | } | |
1484 | ||
65f7a653 DE |
1485 | } |
1486 | ||
e09d24ff R |
1487 | rewind (da_file); |
1488 | ||
956d6950 | 1489 | /* ??? Should first write a header to the file. Preferably, a 4 byte |
65f7a653 DE |
1490 | magic number, 4 bytes containing the time the program was |
1491 | compiled, 4 bytes containing the last modification time of the | |
1492 | source file, and 4 bytes indicating the compiler options used. | |
1493 | ||
1494 | That way we can easily verify that the proper source/executable/ | |
1495 | data file combination is being used from gcov. */ | |
1496 | ||
1497 | if (__write_long (ptr->ncounts, da_file, 8) != 0) | |
1498 | { | |
1499 | ||
1500 | fprintf (stderr, "arc profiling: Error writing output file %s.\n", | |
1501 | ptr->filename); | |
1502 | } | |
1503 | else | |
1504 | { | |
1505 | int j; | |
1506 | long *count_ptr = ptr->counts; | |
1507 | int ret = 0; | |
1508 | for (j = ptr->ncounts; j > 0; j--) | |
1509 | { | |
1510 | if (__write_long (*count_ptr, da_file, 8) != 0) | |
1511 | { | |
1512 | ret=1; | |
1513 | break; | |
1514 | } | |
1515 | count_ptr++; | |
1516 | } | |
1517 | if (ret) | |
1518 | fprintf (stderr, "arc profiling: Error writing output file %s.\n", | |
1519 | ptr->filename); | |
1520 | } | |
1521 | ||
1522 | if (fclose (da_file) == EOF) | |
1523 | fprintf (stderr, "arc profiling: Error closing output file %s.\n", | |
1524 | ptr->filename); | |
1525 | } | |
1526 | ||
1527 | return; | |
1528 | } | |
1529 | ||
1530 | /* Must be basic block profiling. Emit a human readable output file. */ | |
1531 | ||
1532 | file = fopen ("bb.out", "a"); | |
92832bb5 MM |
1533 | |
1534 | if (!file) | |
1535 | perror ("bb.out"); | |
1536 | ||
1537 | else | |
1538 | { | |
1539 | struct bb *ptr; | |
1540 | ||
1541 | /* This is somewhat type incorrect, but it avoids worrying about | |
1542 | exactly where time.h is included from. It should be ok unless | |
90b4a764 | 1543 | a void * differs from other pointer formats, or if sizeof (long) |
92832bb5 MM |
1544 | is < sizeof (time_t). It would be nice if we could assume the |
1545 | use of rationale standards here. */ | |
1546 | ||
90b4a764 | 1547 | time ((void *) &time_value); |
92832bb5 MM |
1548 | fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value)); |
1549 | ||
1550 | /* We check the length field explicitly in order to allow compatibility | |
1551 | with older GCC's which did not provide it. */ | |
1552 | ||
0f41302f | 1553 | for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) |
92832bb5 MM |
1554 | { |
1555 | int i; | |
3e7d8ef1 | 1556 | int func_p = (ptr->nwords >= (long) sizeof (struct bb) |
65f7a653 DE |
1557 | && ptr->nwords <= 1000 |
1558 | && ptr->functions); | |
92832bb5 MM |
1559 | int line_p = (func_p && ptr->line_nums); |
1560 | int file_p = (func_p && ptr->filenames); | |
65f7a653 | 1561 | int addr_p = (ptr->addresses != 0); |
92832bb5 MM |
1562 | long ncounts = ptr->ncounts; |
1563 | long cnt_max = 0; | |
1564 | long line_max = 0; | |
1565 | long addr_max = 0; | |
1566 | int file_len = 0; | |
1567 | int func_len = 0; | |
1568 | int blk_len = num_digits (ncounts, 10); | |
1569 | int cnt_len; | |
1570 | int line_len; | |
1571 | int addr_len; | |
1572 | ||
1573 | fprintf (file, "File %s, %ld basic blocks \n\n", | |
1574 | ptr->filename, ncounts); | |
1575 | ||
1576 | /* Get max values for each field. */ | |
1577 | for (i = 0; i < ncounts; i++) | |
1578 | { | |
1579 | const char *p; | |
1580 | int len; | |
1581 | ||
1582 | if (cnt_max < ptr->counts[i]) | |
1583 | cnt_max = ptr->counts[i]; | |
1584 | ||
3e7d8ef1 | 1585 | if (addr_p && (unsigned long) addr_max < ptr->addresses[i]) |
92832bb5 MM |
1586 | addr_max = ptr->addresses[i]; |
1587 | ||
1588 | if (line_p && line_max < ptr->line_nums[i]) | |
1589 | line_max = ptr->line_nums[i]; | |
1590 | ||
1591 | if (func_p) | |
1592 | { | |
1593 | p = (ptr->functions[i]) ? (ptr->functions[i]) : "<none>"; | |
1594 | len = strlen (p); | |
1595 | if (func_len < len) | |
1596 | func_len = len; | |
1597 | } | |
1598 | ||
1599 | if (file_p) | |
1600 | { | |
1601 | p = (ptr->filenames[i]) ? (ptr->filenames[i]) : "<none>"; | |
1602 | len = strlen (p); | |
1603 | if (file_len < len) | |
1604 | file_len = len; | |
1605 | } | |
1606 | } | |
1607 | ||
1608 | addr_len = num_digits (addr_max, 16); | |
1609 | cnt_len = num_digits (cnt_max, 10); | |
1610 | line_len = num_digits (line_max, 10); | |
1611 | ||
1612 | /* Now print out the basic block information. */ | |
1613 | for (i = 0; i < ncounts; i++) | |
1614 | { | |
1615 | fprintf (file, | |
65f7a653 | 1616 | " Block #%*d: executed %*ld time(s)", |
92832bb5 | 1617 | blk_len, i+1, |
65f7a653 DE |
1618 | cnt_len, ptr->counts[i]); |
1619 | ||
1620 | if (addr_p) | |
1621 | fprintf (file, " address= 0x%.*lx", addr_len, | |
1622 | ptr->addresses[i]); | |
92832bb5 MM |
1623 | |
1624 | if (func_p) | |
3cca99e8 | 1625 | fprintf (file, " function= %-*s", func_len, |
92832bb5 MM |
1626 | (ptr->functions[i]) ? ptr->functions[i] : "<none>"); |
1627 | ||
1628 | if (line_p) | |
1d42e1b7 | 1629 | fprintf (file, " line= %*ld", line_len, ptr->line_nums[i]); |
92832bb5 MM |
1630 | |
1631 | if (file_p) | |
3cca99e8 | 1632 | fprintf (file, " file= %s", |
92832bb5 MM |
1633 | (ptr->filenames[i]) ? ptr->filenames[i] : "<none>"); |
1634 | ||
1635 | fprintf (file, "\n"); | |
1636 | } | |
1637 | ||
1638 | fprintf (file, "\n"); | |
1639 | fflush (file); | |
1640 | } | |
1641 | ||
1642 | fprintf (file, "\n\n"); | |
1643 | fclose (file); | |
1644 | } | |
1645 | } | |
1646 | ||
1647 | void | |
1648 | __bb_init_func (struct bb *blocks) | |
1649 | { | |
1650 | /* User is supposed to check whether the first word is non-0, | |
0f41302f | 1651 | but just in case.... */ |
92832bb5 MM |
1652 | |
1653 | if (blocks->zero_word) | |
1654 | return; | |
1655 | ||
92832bb5 MM |
1656 | /* Initialize destructor. */ |
1657 | if (!bb_head) | |
c063dc98 | 1658 | atexit (__bb_exit_func); |
92832bb5 MM |
1659 | |
1660 | /* Set up linked list. */ | |
1661 | blocks->zero_word = 1; | |
1662 | blocks->next = bb_head; | |
1663 | bb_head = blocks; | |
1664 | } | |
1665 | ||
e09d24ff R |
1666 | /* Called before fork or exec - write out profile information gathered so |
1667 | far and reset it to zero. This avoids duplication or loss of the | |
1668 | profile information gathered so far. */ | |
1669 | void | |
1670 | __bb_fork_func (void) | |
1671 | { | |
1672 | struct bb *ptr; | |
1673 | ||
1674 | __bb_exit_func (); | |
1675 | for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) | |
1676 | { | |
1677 | long i; | |
1678 | for (i = ptr->ncounts - 1; i >= 0; i--) | |
1679 | ptr->counts[i] = 0; | |
1680 | } | |
1681 | } | |
1682 | ||
90b4a764 RK |
1683 | #ifndef MACHINE_STATE_SAVE |
1684 | #define MACHINE_STATE_SAVE(ID) | |
1685 | #endif | |
1686 | #ifndef MACHINE_STATE_RESTORE | |
1687 | #define MACHINE_STATE_RESTORE(ID) | |
1688 | #endif | |
1689 | ||
0f41302f | 1690 | /* Number of buckets in hashtable of basic block addresses. */ |
90b4a764 RK |
1691 | |
1692 | #define BB_BUCKETS 311 | |
1693 | ||
0f41302f | 1694 | /* Maximum length of string in file bb.in. */ |
90b4a764 RK |
1695 | |
1696 | #define BBINBUFSIZE 500 | |
1697 | ||
90b4a764 RK |
1698 | struct bb_edge |
1699 | { | |
1700 | struct bb_edge *next; | |
1701 | unsigned long src_addr; | |
1702 | unsigned long dst_addr; | |
1703 | unsigned long count; | |
1704 | }; | |
1705 | ||
1706 | enum bb_func_mode | |
1707 | { | |
1708 | TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2 | |
1709 | }; | |
1710 | ||
1711 | struct bb_func | |
1712 | { | |
1713 | struct bb_func *next; | |
1714 | char *funcname; | |
1715 | char *filename; | |
1716 | enum bb_func_mode mode; | |
1717 | }; | |
1718 | ||
1719 | /* This is the connection to the outside world. | |
1720 | The BLOCK_PROFILER macro must set __bb.blocks | |
0f41302f | 1721 | and __bb.blockno. */ |
90b4a764 RK |
1722 | |
1723 | struct { | |
1724 | unsigned long blockno; | |
1725 | struct bb *blocks; | |
1726 | } __bb; | |
1727 | ||
1728 | /* Vars to store addrs of source and destination basic blocks | |
0f41302f | 1729 | of a jump. */ |
90b4a764 RK |
1730 | |
1731 | static unsigned long bb_src = 0; | |
1732 | static unsigned long bb_dst = 0; | |
1733 | ||
0f41302f MS |
1734 | static FILE *bb_tracefile = (FILE *) 0; |
1735 | static struct bb_edge **bb_hashbuckets = (struct bb_edge **) 0; | |
1736 | static struct bb_func *bb_func_head = (struct bb_func *) 0; | |
90b4a764 RK |
1737 | static unsigned long bb_callcount = 0; |
1738 | static int bb_mode = 0; | |
1739 | ||
0f41302f | 1740 | static unsigned long *bb_stack = (unsigned long *) 0; |
90b4a764 RK |
1741 | static size_t bb_stacksize = 0; |
1742 | ||
1743 | static int reported = 0; | |
1744 | ||
1745 | /* Trace modes: | |
1746 | Always : Print execution frequencies of basic blocks | |
1747 | to file bb.out. | |
1748 | bb_mode & 1 != 0 : Dump trace of basic blocks to file bbtrace[.gz] | |
1749 | bb_mode & 2 != 0 : Print jump frequencies to file bb.out. | |
1750 | bb_mode & 4 != 0 : Cut call instructions from basic block flow. | |
1751 | bb_mode & 8 != 0 : Insert return instructions in basic block flow. | |
1752 | */ | |
1753 | ||
1754 | #ifdef HAVE_POPEN | |
1755 | ||
1756 | /*#include <sys/types.h>*/ | |
1757 | #include <sys/stat.h> | |
1758 | /*#include <malloc.h>*/ | |
1759 | ||
0f41302f | 1760 | /* Commands executed by gopen. */ |
90b4a764 RK |
1761 | |
1762 | #define GOPENDECOMPRESS "gzip -cd " | |
1763 | #define GOPENCOMPRESS "gzip -c >" | |
1764 | ||
1765 | /* Like fopen but pipes through gzip. mode may only be "r" or "w". | |
1766 | If it does not compile, simply replace gopen by fopen and delete | |
0f41302f | 1767 | '.gz' from any first parameter to gopen. */ |
90b4a764 RK |
1768 | |
1769 | static FILE * | |
37ef1054 | 1770 | gopen (char *fn, char *mode) |
90b4a764 RK |
1771 | { |
1772 | int use_gzip; | |
1773 | char *p; | |
1774 | ||
1775 | if (mode[1]) | |
0f41302f | 1776 | return (FILE *) 0; |
90b4a764 RK |
1777 | |
1778 | if (mode[0] != 'r' && mode[0] != 'w') | |
0f41302f | 1779 | return (FILE *) 0; |
90b4a764 RK |
1780 | |
1781 | p = fn + strlen (fn)-1; | |
db3cf6fb MS |
1782 | use_gzip = ((p[-1] == '.' && (p[0] == 'Z' || p[0] == 'z')) |
1783 | || (p[-2] == '.' && p[-1] == 'g' && p[0] == 'z')); | |
90b4a764 RK |
1784 | |
1785 | if (use_gzip) | |
1786 | { | |
1787 | if (mode[0]=='r') | |
1788 | { | |
1789 | FILE *f; | |
0f41302f MS |
1790 | char *s = (char *) malloc (sizeof (char) * strlen (fn) |
1791 | + sizeof (GOPENDECOMPRESS)); | |
90b4a764 RK |
1792 | strcpy (s, GOPENDECOMPRESS); |
1793 | strcpy (s + (sizeof (GOPENDECOMPRESS)-1), fn); | |
1794 | f = popen (s, mode); | |
1795 | free (s); | |
1796 | return f; | |
1797 | } | |
1798 | ||
1799 | else | |
1800 | { | |
1801 | FILE *f; | |
0f41302f MS |
1802 | char *s = (char *) malloc (sizeof (char) * strlen (fn) |
1803 | + sizeof (GOPENCOMPRESS)); | |
90b4a764 RK |
1804 | strcpy (s, GOPENCOMPRESS); |
1805 | strcpy (s + (sizeof (GOPENCOMPRESS)-1), fn); | |
1806 | if (!(f = popen (s, mode))) | |
1807 | f = fopen (s, mode); | |
1808 | free (s); | |
1809 | return f; | |
1810 | } | |
1811 | } | |
1812 | ||
1813 | else | |
1814 | return fopen (fn, mode); | |
1815 | } | |
1816 | ||
1817 | static int | |
37ef1054 | 1818 | gclose (FILE *f) |
90b4a764 RK |
1819 | { |
1820 | struct stat buf; | |
1821 | ||
920b13cc | 1822 | if (f != 0) |
90b4a764 RK |
1823 | { |
1824 | if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode)) | |
1825 | return pclose (f); | |
1826 | ||
1827 | return fclose (f); | |
1828 | } | |
1829 | return 0; | |
1830 | } | |
1831 | ||
1832 | #endif /* HAVE_POPEN */ | |
1833 | ||
0f41302f | 1834 | /* Called once per program. */ |
90b4a764 RK |
1835 | |
1836 | static void | |
3e7d8ef1 | 1837 | __bb_exit_trace_func (void) |
90b4a764 RK |
1838 | { |
1839 | FILE *file = fopen ("bb.out", "a"); | |
1840 | struct bb_func *f; | |
90b4a764 RK |
1841 | struct bb *b; |
1842 | ||
1843 | if (!file) | |
1844 | perror ("bb.out"); | |
1845 | ||
1846 | if (bb_mode & 1) | |
1847 | { | |
1848 | if (!bb_tracefile) | |
1849 | perror ("bbtrace"); | |
1850 | else | |
1851 | #ifdef HAVE_POPEN | |
1852 | gclose (bb_tracefile); | |
1853 | #else | |
1854 | fclose (bb_tracefile); | |
1855 | #endif /* HAVE_POPEN */ | |
1856 | } | |
1857 | ||
0f41302f | 1858 | /* Check functions in `bb.in'. */ |
90b4a764 RK |
1859 | |
1860 | if (file) | |
1861 | { | |
1862 | long time_value; | |
1863 | const struct bb_func *p; | |
1864 | int printed_something = 0; | |
1865 | struct bb *ptr; | |
1866 | long blk; | |
1867 | ||
0f41302f | 1868 | /* This is somewhat type incorrect. */ |
90b4a764 RK |
1869 | time ((void *) &time_value); |
1870 | ||
0f41302f | 1871 | for (p = bb_func_head; p != (struct bb_func *) 0; p = p->next) |
90b4a764 | 1872 | { |
0f41302f | 1873 | for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) |
90b4a764 | 1874 | { |
51723711 | 1875 | if (!ptr->filename || (p->filename != (char *) 0 && strcmp (p->filename, ptr->filename))) |
90b4a764 RK |
1876 | continue; |
1877 | for (blk = 0; blk < ptr->ncounts; blk++) | |
1878 | { | |
1879 | if (!strcmp (p->funcname, ptr->functions[blk])) | |
1880 | goto found; | |
1881 | } | |
1882 | } | |
1883 | ||
1884 | if (!printed_something) | |
1885 | { | |
1886 | fprintf (file, "Functions in `bb.in' not executed during basic block profiling on %s\n", ctime ((void *) &time_value)); | |
1887 | printed_something = 1; | |
1888 | } | |
1889 | ||
1890 | fprintf (file, "\tFunction %s", p->funcname); | |
1891 | if (p->filename) | |
1892 | fprintf (file, " of file %s", p->filename); | |
1893 | fprintf (file, "\n" ); | |
1894 | ||
1895 | found: ; | |
1896 | } | |
1897 | ||
1898 | if (printed_something) | |
1899 | fprintf (file, "\n"); | |
1900 | ||
1901 | } | |
1902 | ||
1903 | if (bb_mode & 2) | |
1904 | { | |
1905 | if (!bb_hashbuckets) | |
1906 | { | |
1907 | if (!reported) | |
1908 | { | |
1909 | fprintf (stderr, "Profiler: out of memory\n"); | |
1910 | reported = 1; | |
1911 | } | |
1912 | return; | |
1913 | } | |
1914 | ||
1915 | else if (file) | |
1916 | { | |
1917 | long time_value; | |
1918 | int i; | |
1919 | unsigned long addr_max = 0; | |
1920 | unsigned long cnt_max = 0; | |
1921 | int cnt_len; | |
1922 | int addr_len; | |
1923 | ||
1924 | /* This is somewhat type incorrect, but it avoids worrying about | |
1925 | exactly where time.h is included from. It should be ok unless | |
1926 | a void * differs from other pointer formats, or if sizeof (long) | |
1927 | is < sizeof (time_t). It would be nice if we could assume the | |
1928 | use of rationale standards here. */ | |
1929 | ||
1930 | time ((void *) &time_value); | |
1931 | fprintf (file, "Basic block jump tracing"); | |
1932 | ||
1933 | switch (bb_mode & 12) | |
1934 | { | |
1935 | case 0: | |
1936 | fprintf (file, " (with call)"); | |
1937 | break; | |
1938 | ||
1939 | case 4: | |
0f41302f | 1940 | /* Print nothing. */ |
90b4a764 RK |
1941 | break; |
1942 | ||
1943 | case 8: | |
1944 | fprintf (file, " (with call & ret)"); | |
1945 | break; | |
1946 | ||
1947 | case 12: | |
1948 | fprintf (file, " (with ret)"); | |
1949 | break; | |
1950 | } | |
1951 | ||
1952 | fprintf (file, " finished on %s\n", ctime ((void *) &time_value)); | |
1953 | ||
1954 | for (i = 0; i < BB_BUCKETS; i++) | |
1955 | { | |
1956 | struct bb_edge *bucket = bb_hashbuckets[i]; | |
1957 | for ( ; bucket; bucket = bucket->next ) | |
1958 | { | |
1959 | if (addr_max < bucket->src_addr) | |
1960 | addr_max = bucket->src_addr; | |
1961 | if (addr_max < bucket->dst_addr) | |
1962 | addr_max = bucket->dst_addr; | |
1963 | if (cnt_max < bucket->count) | |
1964 | cnt_max = bucket->count; | |
1965 | } | |
1966 | } | |
1967 | addr_len = num_digits (addr_max, 16); | |
1968 | cnt_len = num_digits (cnt_max, 10); | |
1969 | ||
1970 | for ( i = 0; i < BB_BUCKETS; i++) | |
1971 | { | |
1972 | struct bb_edge *bucket = bb_hashbuckets[i]; | |
1973 | for ( ; bucket; bucket = bucket->next ) | |
1974 | { | |
78a0d70c ZW |
1975 | fprintf (file, |
1976 | "Jump from block 0x%.*lx to block 0x%.*lx executed %*lu time(s)\n", | |
90b4a764 RK |
1977 | addr_len, bucket->src_addr, |
1978 | addr_len, bucket->dst_addr, | |
1979 | cnt_len, bucket->count); | |
1980 | } | |
1981 | } | |
1982 | ||
1983 | fprintf (file, "\n"); | |
1984 | ||
1985 | } | |
1986 | } | |
1987 | ||
1988 | if (file) | |
1989 | fclose (file); | |
1990 | ||
0f41302f | 1991 | /* Free allocated memory. */ |
90b4a764 RK |
1992 | |
1993 | f = bb_func_head; | |
1994 | while (f) | |
1995 | { | |
1996 | struct bb_func *old = f; | |
1997 | ||
1998 | f = f->next; | |
1999 | if (old->funcname) free (old->funcname); | |
2000 | if (old->filename) free (old->filename); | |
2001 | free (old); | |
2002 | } | |
2003 | ||
2004 | if (bb_stack) | |
2005 | free (bb_stack); | |
2006 | ||
2007 | if (bb_hashbuckets) | |
2008 | { | |
2009 | int i; | |
2010 | ||
2011 | for (i = 0; i < BB_BUCKETS; i++) | |
2012 | { | |
2013 | struct bb_edge *old, *bucket = bb_hashbuckets[i]; | |
2014 | ||
2015 | while (bucket) | |
2016 | { | |
2017 | old = bucket; | |
2018 | bucket = bucket->next; | |
2019 | free (old); | |
2020 | } | |
2021 | } | |
2022 | free (bb_hashbuckets); | |
2023 | } | |
2024 | ||
2025 | for (b = bb_head; b; b = b->next) | |
2026 | if (b->flags) free (b->flags); | |
2027 | } | |
2028 | ||
0f41302f | 2029 | /* Called once per program. */ |
90b4a764 RK |
2030 | |
2031 | static void | |
3e7d8ef1 | 2032 | __bb_init_prg (void) |
90b4a764 | 2033 | { |
90b4a764 RK |
2034 | FILE *file; |
2035 | char buf[BBINBUFSIZE]; | |
2036 | const char *p; | |
2037 | const char *pos; | |
2038 | enum bb_func_mode m; | |
c5c76735 | 2039 | int i; |
90b4a764 | 2040 | |
90b4a764 | 2041 | /* Initialize destructor. */ |
c063dc98 | 2042 | atexit (__bb_exit_func); |
90b4a764 RK |
2043 | |
2044 | if (!(file = fopen ("bb.in", "r"))) | |
2045 | return; | |
2046 | ||
78a0d70c | 2047 | while(fgets (buf, BBINBUFSIZE, file) != 0) |
90b4a764 | 2048 | { |
78a0d70c ZW |
2049 | i = strlen (buf); |
2050 | if (buf[i] == '\n') | |
2051 | buf[i--] = '\0'; | |
2052 | ||
90b4a764 RK |
2053 | p = buf; |
2054 | if (*p == '-') | |
2055 | { | |
2056 | m = TRACE_OFF; | |
2057 | p++; | |
2058 | } | |
2059 | else | |
2060 | { | |
2061 | m = TRACE_ON; | |
2062 | } | |
2063 | if (!strcmp (p, "__bb_trace__")) | |
2064 | bb_mode |= 1; | |
2065 | else if (!strcmp (p, "__bb_jumps__")) | |
2066 | bb_mode |= 2; | |
2067 | else if (!strcmp (p, "__bb_hidecall__")) | |
2068 | bb_mode |= 4; | |
2069 | else if (!strcmp (p, "__bb_showret__")) | |
2070 | bb_mode |= 8; | |
2071 | else | |
2072 | { | |
0f41302f | 2073 | struct bb_func *f = (struct bb_func *) malloc (sizeof (struct bb_func)); |
90b4a764 RK |
2074 | if (f) |
2075 | { | |
2076 | unsigned long l; | |
2077 | f->next = bb_func_head; | |
51723711 | 2078 | if ((pos = strchr (p, ':'))) |
90b4a764 | 2079 | { |
0f41302f | 2080 | if (!(f->funcname = (char *) malloc (strlen (pos+1)+1))) |
90b4a764 RK |
2081 | continue; |
2082 | strcpy (f->funcname, pos+1); | |
2083 | l = pos-p; | |
0f41302f | 2084 | if ((f->filename = (char *) malloc (l+1))) |
90b4a764 RK |
2085 | { |
2086 | strncpy (f->filename, p, l); | |
2087 | f->filename[l] = '\0'; | |
2088 | } | |
2089 | else | |
0f41302f | 2090 | f->filename = (char *) 0; |
90b4a764 RK |
2091 | } |
2092 | else | |
2093 | { | |
0f41302f | 2094 | if (!(f->funcname = (char *) malloc (strlen (p)+1))) |
90b4a764 RK |
2095 | continue; |
2096 | strcpy (f->funcname, p); | |
0f41302f | 2097 | f->filename = (char *) 0; |
90b4a764 RK |
2098 | } |
2099 | f->mode = m; | |
2100 | bb_func_head = f; | |
2101 | } | |
2102 | } | |
2103 | } | |
2104 | fclose (file); | |
2105 | ||
2106 | #ifdef HAVE_POPEN | |
2107 | ||
2108 | if (bb_mode & 1) | |
2109 | bb_tracefile = gopen ("bbtrace.gz", "w"); | |
2110 | ||
2111 | #else | |
2112 | ||
2113 | if (bb_mode & 1) | |
2114 | bb_tracefile = fopen ("bbtrace", "w"); | |
2115 | ||
2116 | #endif /* HAVE_POPEN */ | |
2117 | ||
2118 | if (bb_mode & 2) | |
2119 | { | |
2120 | bb_hashbuckets = (struct bb_edge **) | |
2121 | malloc (BB_BUCKETS * sizeof (struct bb_edge *)); | |
2122 | if (bb_hashbuckets) | |
c5c76735 JL |
2123 | /* Use a loop here rather than calling bzero to avoid having to |
2124 | conditionalize its existance. */ | |
2125 | for (i = 0; i < BB_BUCKETS; i++) | |
2126 | bb_hashbuckets[i] = 0; | |
90b4a764 RK |
2127 | } |
2128 | ||
2129 | if (bb_mode & 12) | |
2130 | { | |
2131 | bb_stacksize = 10; | |
2132 | bb_stack = (unsigned long *) malloc (bb_stacksize * sizeof (*bb_stack)); | |
2133 | } | |
2134 | ||
c063dc98 JM |
2135 | /* Initialize destructor. */ |
2136 | atexit (__bb_exit_trace_func); | |
90b4a764 RK |
2137 | } |
2138 | ||
0f41302f | 2139 | /* Called upon entering a basic block. */ |
90b4a764 RK |
2140 | |
2141 | void | |
3e7d8ef1 | 2142 | __bb_trace_func (void) |
90b4a764 RK |
2143 | { |
2144 | struct bb_edge *bucket; | |
2145 | ||
2146 | MACHINE_STATE_SAVE("1") | |
2147 | ||
2148 | if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF))) | |
2149 | goto skip; | |
2150 | ||
2151 | bb_dst = __bb.blocks->addresses[__bb.blockno]; | |
2152 | __bb.blocks->counts[__bb.blockno]++; | |
2153 | ||
2154 | if (bb_tracefile) | |
2155 | { | |
2156 | fwrite (&bb_dst, sizeof (unsigned long), 1, bb_tracefile); | |
2157 | } | |
2158 | ||
2159 | if (bb_hashbuckets) | |
2160 | { | |
2161 | struct bb_edge **startbucket, **oldnext; | |
2162 | ||
db3cf6fb MS |
2163 | oldnext = startbucket |
2164 | = & bb_hashbuckets[ (((int) bb_src*8) ^ (int) bb_dst) % BB_BUCKETS ]; | |
90b4a764 RK |
2165 | bucket = *startbucket; |
2166 | ||
2167 | for (bucket = *startbucket; bucket; | |
2168 | oldnext = &(bucket->next), bucket = *oldnext) | |
2169 | { | |
db3cf6fb MS |
2170 | if (bucket->src_addr == bb_src |
2171 | && bucket->dst_addr == bb_dst) | |
90b4a764 RK |
2172 | { |
2173 | bucket->count++; | |
2174 | *oldnext = bucket->next; | |
2175 | bucket->next = *startbucket; | |
2176 | *startbucket = bucket; | |
2177 | goto ret; | |
2178 | } | |
2179 | } | |
2180 | ||
2181 | bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge)); | |
2182 | ||
2183 | if (!bucket) | |
2184 | { | |
2185 | if (!reported) | |
2186 | { | |
2187 | fprintf (stderr, "Profiler: out of memory\n"); | |
2188 | reported = 1; | |
2189 | } | |
2190 | } | |
2191 | ||
2192 | else | |
2193 | { | |
2194 | bucket->src_addr = bb_src; | |
2195 | bucket->dst_addr = bb_dst; | |
2196 | bucket->next = *startbucket; | |
2197 | *startbucket = bucket; | |
2198 | bucket->count = 1; | |
2199 | } | |
2200 | } | |
2201 | ||
2202 | ret: | |
2203 | bb_src = bb_dst; | |
2204 | ||
2205 | skip: | |
2206 | ; | |
2207 | ||
2208 | MACHINE_STATE_RESTORE("1") | |
2209 | ||
2210 | } | |
2211 | ||
0f41302f | 2212 | /* Called when returning from a function and `__bb_showret__' is set. */ |
90b4a764 RK |
2213 | |
2214 | static void | |
3e7d8ef1 | 2215 | __bb_trace_func_ret (void) |
90b4a764 RK |
2216 | { |
2217 | struct bb_edge *bucket; | |
2218 | ||
2219 | if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF))) | |
2220 | goto skip; | |
2221 | ||
2222 | if (bb_hashbuckets) | |
2223 | { | |
2224 | struct bb_edge **startbucket, **oldnext; | |
2225 | ||
db3cf6fb MS |
2226 | oldnext = startbucket |
2227 | = & bb_hashbuckets[ (((int) bb_dst * 8) ^ (int) bb_src) % BB_BUCKETS ]; | |
90b4a764 RK |
2228 | bucket = *startbucket; |
2229 | ||
2230 | for (bucket = *startbucket; bucket; | |
2231 | oldnext = &(bucket->next), bucket = *oldnext) | |
2232 | { | |
db3cf6fb MS |
2233 | if (bucket->src_addr == bb_dst |
2234 | && bucket->dst_addr == bb_src) | |
90b4a764 RK |
2235 | { |
2236 | bucket->count++; | |
2237 | *oldnext = bucket->next; | |
2238 | bucket->next = *startbucket; | |
2239 | *startbucket = bucket; | |
2240 | goto ret; | |
2241 | } | |
2242 | } | |
2243 | ||
2244 | bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge)); | |
2245 | ||
2246 | if (!bucket) | |
2247 | { | |
2248 | if (!reported) | |
2249 | { | |
2250 | fprintf (stderr, "Profiler: out of memory\n"); | |
2251 | reported = 1; | |
2252 | } | |
2253 | } | |
2254 | ||
2255 | else | |
2256 | { | |
2257 | bucket->src_addr = bb_dst; | |
2258 | bucket->dst_addr = bb_src; | |
2259 | bucket->next = *startbucket; | |
2260 | *startbucket = bucket; | |
2261 | bucket->count = 1; | |
2262 | } | |
2263 | } | |
2264 | ||
2265 | ret: | |
2266 | bb_dst = bb_src; | |
2267 | ||
2268 | skip: | |
2269 | ; | |
2270 | ||
2271 | } | |
2272 | ||
0f41302f | 2273 | /* Called upon entering the first function of a file. */ |
90b4a764 RK |
2274 | |
2275 | static void | |
37ef1054 | 2276 | __bb_init_file (struct bb *blocks) |
90b4a764 RK |
2277 | { |
2278 | ||
2279 | const struct bb_func *p; | |
2280 | long blk, ncounts = blocks->ncounts; | |
2281 | const char **functions = blocks->functions; | |
2282 | ||
2283 | /* Set up linked list. */ | |
2284 | blocks->zero_word = 1; | |
2285 | blocks->next = bb_head; | |
2286 | bb_head = blocks; | |
2287 | ||
2288 | blocks->flags = 0; | |
db3cf6fb MS |
2289 | if (!bb_func_head |
2290 | || !(blocks->flags = (char *) malloc (sizeof (char) * blocks->ncounts))) | |
90b4a764 RK |
2291 | return; |
2292 | ||
2293 | for (blk = 0; blk < ncounts; blk++) | |
2294 | blocks->flags[blk] = 0; | |
2295 | ||
2296 | for (blk = 0; blk < ncounts; blk++) | |
2297 | { | |
2298 | for (p = bb_func_head; p; p = p->next) | |
2299 | { | |
db3cf6fb MS |
2300 | if (!strcmp (p->funcname, functions[blk]) |
2301 | && (!p->filename || !strcmp (p->filename, blocks->filename))) | |
90b4a764 RK |
2302 | { |
2303 | blocks->flags[blk] |= p->mode; | |
2304 | } | |
2305 | } | |
2306 | } | |
2307 | ||
2308 | } | |
2309 | ||
0f41302f | 2310 | /* Called when exiting from a function. */ |
90b4a764 RK |
2311 | |
2312 | void | |
3e7d8ef1 | 2313 | __bb_trace_ret (void) |
90b4a764 RK |
2314 | { |
2315 | ||
2316 | MACHINE_STATE_SAVE("2") | |
2317 | ||
2318 | if (bb_callcount) | |
2319 | { | |
2320 | if ((bb_mode & 12) && bb_stacksize > bb_callcount) | |
2321 | { | |
2322 | bb_src = bb_stack[bb_callcount]; | |
2323 | if (bb_mode & 8) | |
2324 | __bb_trace_func_ret (); | |
2325 | } | |
2326 | ||
2327 | bb_callcount -= 1; | |
2328 | } | |
2329 | ||
2330 | MACHINE_STATE_RESTORE("2") | |
2331 | ||
2332 | } | |
2333 | ||
0f41302f | 2334 | /* Called when entering a function. */ |
90b4a764 RK |
2335 | |
2336 | void | |
37ef1054 | 2337 | __bb_init_trace_func (struct bb *blocks, unsigned long blockno) |
90b4a764 RK |
2338 | { |
2339 | static int trace_init = 0; | |
2340 | ||
2341 | MACHINE_STATE_SAVE("3") | |
2342 | ||
2343 | if (!blocks->zero_word) | |
2344 | { | |
2345 | if (!trace_init) | |
2346 | { | |
2347 | trace_init = 1; | |
2348 | __bb_init_prg (); | |
2349 | } | |
2350 | __bb_init_file (blocks); | |
2351 | } | |
2352 | ||
2353 | if (bb_callcount) | |
2354 | { | |
2355 | ||
2356 | bb_callcount += 1; | |
2357 | ||
2358 | if (bb_mode & 12) | |
2359 | { | |
2360 | if (bb_callcount >= bb_stacksize) | |
2361 | { | |
2362 | size_t newsize = bb_callcount + 100; | |
2363 | ||
2364 | bb_stack = (unsigned long *) realloc (bb_stack, newsize); | |
2365 | if (! bb_stack) | |
2366 | { | |
2367 | if (!reported) | |
2368 | { | |
2369 | fprintf (stderr, "Profiler: out of memory\n"); | |
2370 | reported = 1; | |
2371 | } | |
2372 | bb_stacksize = 0; | |
2373 | goto stack_overflow; | |
2374 | } | |
2375 | bb_stacksize = newsize; | |
2376 | } | |
2377 | bb_stack[bb_callcount] = bb_src; | |
2378 | ||
2379 | if (bb_mode & 4) | |
2380 | bb_src = 0; | |
2381 | ||
2382 | } | |
2383 | ||
2384 | stack_overflow:; | |
2385 | ||
2386 | } | |
2387 | ||
2388 | else if (blocks->flags && (blocks->flags[blockno] & TRACE_ON)) | |
2389 | { | |
2390 | bb_callcount = 1; | |
2391 | bb_src = 0; | |
2392 | ||
2393 | if (bb_stack) | |
2394 | bb_stack[bb_callcount] = bb_src; | |
2395 | } | |
2396 | ||
2397 | MACHINE_STATE_RESTORE("3") | |
2398 | } | |
2399 | ||
c7544ff7 RS |
2400 | #endif /* not inhibit_libc */ |
2401 | #endif /* not BLOCK_PROFILER_CODE */ | |
2402 | #endif /* L_bb */ | |
203b91b9 | 2403 | \f |
203b91b9 RS |
2404 | #ifdef L_shtab |
2405 | unsigned int __shtab[] = { | |
2406 | 0x00000001, 0x00000002, 0x00000004, 0x00000008, | |
2407 | 0x00000010, 0x00000020, 0x00000040, 0x00000080, | |
2408 | 0x00000100, 0x00000200, 0x00000400, 0x00000800, | |
2409 | 0x00001000, 0x00002000, 0x00004000, 0x00008000, | |
2410 | 0x00010000, 0x00020000, 0x00040000, 0x00080000, | |
2411 | 0x00100000, 0x00200000, 0x00400000, 0x00800000, | |
2412 | 0x01000000, 0x02000000, 0x04000000, 0x08000000, | |
2413 | 0x10000000, 0x20000000, 0x40000000, 0x80000000 | |
2414 | }; | |
2415 | #endif | |
2416 | \f | |
2417 | #ifdef L_clear_cache | |
2418 | /* Clear part of an instruction cache. */ | |
2419 | ||
2420 | #define INSN_CACHE_PLANE_SIZE (INSN_CACHE_SIZE / INSN_CACHE_DEPTH) | |
2421 | ||
2422 | void | |
139fa6f8 MM |
2423 | __clear_cache (char *beg __attribute__((__unused__)), |
2424 | char *end __attribute__((__unused__))) | |
203b91b9 | 2425 | { |
e1178973 KKT |
2426 | #ifdef CLEAR_INSN_CACHE |
2427 | CLEAR_INSN_CACHE (beg, end); | |
2428 | #else | |
203b91b9 RS |
2429 | #ifdef INSN_CACHE_SIZE |
2430 | static char array[INSN_CACHE_SIZE + INSN_CACHE_PLANE_SIZE + INSN_CACHE_LINE_WIDTH]; | |
7e6f1890 | 2431 | static int initialized; |
203b91b9 | 2432 | int offset; |
b6422cca RS |
2433 | void *start_addr |
2434 | void *end_addr; | |
3e7d8ef1 | 2435 | typedef (*function_ptr) (void); |
203b91b9 RS |
2436 | |
2437 | #if (INSN_CACHE_SIZE / INSN_CACHE_LINE_WIDTH) < 16 | |
2438 | /* It's cheaper to clear the whole cache. | |
2439 | Put in a series of jump instructions so that calling the beginning | |
2440 | of the cache will clear the whole thing. */ | |
2441 | ||
2442 | if (! initialized) | |
2443 | { | |
2444 | int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1) | |
2445 | & -INSN_CACHE_LINE_WIDTH); | |
2446 | int end_ptr = ptr + INSN_CACHE_SIZE; | |
2447 | ||
2448 | while (ptr < end_ptr) | |
2449 | { | |
2450 | *(INSTRUCTION_TYPE *)ptr | |
2451 | = JUMP_AHEAD_INSTRUCTION + INSN_CACHE_LINE_WIDTH; | |
2452 | ptr += INSN_CACHE_LINE_WIDTH; | |
2453 | } | |
0f41302f | 2454 | *(INSTRUCTION_TYPE *) (ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION; |
203b91b9 RS |
2455 | |
2456 | initialized = 1; | |
2457 | } | |
2458 | ||
2459 | /* Call the beginning of the sequence. */ | |
2460 | (((function_ptr) (((int) array + INSN_CACHE_LINE_WIDTH - 1) | |
2461 | & -INSN_CACHE_LINE_WIDTH)) | |
2462 | ()); | |
2463 | ||
2464 | #else /* Cache is large. */ | |
2465 | ||
2466 | if (! initialized) | |
2467 | { | |
2468 | int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1) | |
2469 | & -INSN_CACHE_LINE_WIDTH); | |
2470 | ||
2471 | while (ptr < (int) array + sizeof array) | |
2472 | { | |
2473 | *(INSTRUCTION_TYPE *)ptr = RETURN_INSTRUCTION; | |
2474 | ptr += INSN_CACHE_LINE_WIDTH; | |
2475 | } | |
2476 | ||
2477 | initialized = 1; | |
2478 | } | |
2479 | ||
2480 | /* Find the location in array that occupies the same cache line as BEG. */ | |
2481 | ||
2482 | offset = ((int) beg & -INSN_CACHE_LINE_WIDTH) & (INSN_CACHE_PLANE_SIZE - 1); | |
2483 | start_addr = (((int) (array + INSN_CACHE_PLANE_SIZE - 1) | |
2484 | & -INSN_CACHE_PLANE_SIZE) | |
2485 | + offset); | |
2486 | ||
2487 | /* Compute the cache alignment of the place to stop clearing. */ | |
2488 | #if 0 /* This is not needed for gcc's purposes. */ | |
2489 | /* If the block to clear is bigger than a cache plane, | |
2490 | we clear the entire cache, and OFFSET is already correct. */ | |
2491 | if (end < beg + INSN_CACHE_PLANE_SIZE) | |
2492 | #endif | |
2493 | offset = (((int) (end + INSN_CACHE_LINE_WIDTH - 1) | |
2494 | & -INSN_CACHE_LINE_WIDTH) | |
2495 | & (INSN_CACHE_PLANE_SIZE - 1)); | |
2496 | ||
2497 | #if INSN_CACHE_DEPTH > 1 | |
2498 | end_addr = (start_addr & -INSN_CACHE_PLANE_SIZE) + offset; | |
2499 | if (end_addr <= start_addr) | |
2500 | end_addr += INSN_CACHE_PLANE_SIZE; | |
2501 | ||
2502 | for (plane = 0; plane < INSN_CACHE_DEPTH; plane++) | |
2503 | { | |
2504 | int addr = start_addr + plane * INSN_CACHE_PLANE_SIZE; | |
2505 | int stop = end_addr + plane * INSN_CACHE_PLANE_SIZE; | |
2506 | ||
2507 | while (addr != stop) | |
2508 | { | |
2509 | /* Call the return instruction at ADDR. */ | |
2510 | ((function_ptr) addr) (); | |
2511 | ||
2512 | addr += INSN_CACHE_LINE_WIDTH; | |
2513 | } | |
2514 | } | |
2515 | #else /* just one plane */ | |
2516 | do | |
2517 | { | |
2518 | /* Call the return instruction at START_ADDR. */ | |
2519 | ((function_ptr) start_addr) (); | |
2520 | ||
2521 | start_addr += INSN_CACHE_LINE_WIDTH; | |
2522 | } | |
2523 | while ((start_addr % INSN_CACHE_SIZE) != offset); | |
2524 | #endif /* just one plane */ | |
2525 | #endif /* Cache is large */ | |
2526 | #endif /* Cache exists */ | |
e1178973 | 2527 | #endif /* CLEAR_INSN_CACHE */ |
203b91b9 RS |
2528 | } |
2529 | ||
2530 | #endif /* L_clear_cache */ | |
2531 | \f | |
2532 | #ifdef L_trampoline | |
2533 | ||
2534 | /* Jump to a trampoline, loading the static chain address. */ | |
2535 | ||
b27d2bd5 | 2536 | #if defined(WINNT) && ! defined(__CYGWIN__) && ! defined (_UWIN) |
e3367a77 | 2537 | |
3e7d8ef1 KG |
2538 | long |
2539 | getpagesize (void) | |
f5ea9817 RK |
2540 | { |
2541 | #ifdef _ALPHA_ | |
2542 | return 8192; | |
2543 | #else | |
2544 | return 4096; | |
2545 | #endif | |
2546 | } | |
2547 | ||
d7ebf9ea | 2548 | #ifdef __i386__ |
e4b15106 RK |
2549 | extern int VirtualProtect (char *, int, int, int *) __attribute__((stdcall)); |
2550 | #endif | |
2551 | ||
272e2587 RK |
2552 | int |
2553 | mprotect (char *addr, int len, int prot) | |
f5ea9817 RK |
2554 | { |
2555 | int np, op; | |
2556 | ||
272e2587 RK |
2557 | if (prot == 7) |
2558 | np = 0x40; | |
2559 | else if (prot == 5) | |
2560 | np = 0x20; | |
2561 | else if (prot == 4) | |
2562 | np = 0x10; | |
2563 | else if (prot == 3) | |
2564 | np = 0x04; | |
2565 | else if (prot == 1) | |
2566 | np = 0x02; | |
2567 | else if (prot == 0) | |
2568 | np = 0x01; | |
f5ea9817 RK |
2569 | |
2570 | if (VirtualProtect (addr, len, np, &op)) | |
2571 | return 0; | |
2572 | else | |
2573 | return -1; | |
f5ea9817 RK |
2574 | } |
2575 | ||
b27d2bd5 | 2576 | #endif /* WINNT && ! __CYGWIN__ && ! _UWIN */ |
f5ea9817 | 2577 | |
203b91b9 RS |
2578 | #ifdef TRANSFER_FROM_TRAMPOLINE |
2579 | TRANSFER_FROM_TRAMPOLINE | |
2580 | #endif | |
2581 | ||
c1381fd3 KKT |
2582 | #if defined (NeXT) && defined (__MACH__) |
2583 | ||
2584 | /* Make stack executable so we can call trampolines on stack. | |
2585 | This is called from INITIALIZE_TRAMPOLINE in next.h. */ | |
c5df463e RK |
2586 | #ifdef NeXTStep21 |
2587 | #include <mach.h> | |
2588 | #else | |
2589 | #include <mach/mach.h> | |
2590 | #endif | |
c1381fd3 KKT |
2591 | |
2592 | void | |
37ef1054 | 2593 | __enable_execute_stack (char *addr) |
c1381fd3 KKT |
2594 | { |
2595 | kern_return_t r; | |
2596 | char *eaddr = addr + TRAMPOLINE_SIZE; | |
2597 | vm_address_t a = (vm_address_t) addr; | |
2598 | ||
2599 | /* turn on execute access on stack */ | |
2600 | r = vm_protect (task_self (), a, TRAMPOLINE_SIZE, FALSE, VM_PROT_ALL); | |
2601 | if (r != KERN_SUCCESS) | |
2602 | { | |
2603 | mach_error("vm_protect VM_PROT_ALL", r); | |
2604 | exit(1); | |
2605 | } | |
2606 | ||
2607 | /* We inline the i-cache invalidation for speed */ | |
2608 | ||
2609 | #ifdef CLEAR_INSN_CACHE | |
2610 | CLEAR_INSN_CACHE (addr, eaddr); | |
2611 | #else | |
2612 | __clear_cache ((int) addr, (int) eaddr); | |
2613 | #endif | |
2614 | } | |
2615 | ||
2616 | #endif /* defined (NeXT) && defined (__MACH__) */ | |
2617 | ||
203b91b9 RS |
2618 | #ifdef __convex__ |
2619 | ||
2620 | /* Make stack executable so we can call trampolines on stack. | |
2621 | This is called from INITIALIZE_TRAMPOLINE in convex.h. */ | |
2622 | ||
2623 | #include <sys/mman.h> | |
2624 | #include <sys/vmparam.h> | |
2625 | #include <machine/machparam.h> | |
2626 | ||
2627 | void | |
3e7d8ef1 | 2628 | __enable_execute_stack (void) |
203b91b9 RS |
2629 | { |
2630 | int fp; | |
2631 | static unsigned lowest = USRSTACK; | |
2632 | unsigned current = (unsigned) &fp & -NBPG; | |
2633 | ||
2634 | if (lowest > current) | |
2635 | { | |
2636 | unsigned len = lowest - current; | |
2637 | mremap (current, &len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE); | |
2638 | lowest = current; | |
2639 | } | |
2640 | ||
0f41302f | 2641 | /* Clear instruction cache in case an old trampoline is in it. */ |
203b91b9 RS |
2642 | asm ("pich"); |
2643 | } | |
2644 | #endif /* __convex__ */ | |
b335c2cc | 2645 | |
db87ec0b | 2646 | #ifdef __sysV88__ |
0c8ae3d3 | 2647 | |
0f41302f | 2648 | /* Modified from the convex -code above. */ |
0c8ae3d3 RK |
2649 | |
2650 | #include <sys/param.h> | |
2651 | #include <errno.h> | |
2652 | #include <sys/m88kbcs.h> | |
2653 | ||
2654 | void | |
3e7d8ef1 | 2655 | __enable_execute_stack (void) |
0c8ae3d3 RK |
2656 | { |
2657 | int save_errno; | |
2658 | static unsigned long lowest = USRSTACK; | |
2659 | unsigned long current = (unsigned long) &save_errno & -NBPC; | |
2660 | ||
2661 | /* Ignore errno being set. memctl sets errno to EINVAL whenever the | |
2662 | address is seen as 'negative'. That is the case with the stack. */ | |
2663 | ||
2664 | save_errno=errno; | |
2665 | if (lowest > current) | |
2666 | { | |
2667 | unsigned len=lowest-current; | |
2668 | memctl(current,len,MCT_TEXT); | |
2669 | lowest = current; | |
2670 | } | |
2671 | else | |
2672 | memctl(current,NBPC,MCT_TEXT); | |
2673 | errno=save_errno; | |
2674 | } | |
2675 | ||
db87ec0b | 2676 | #endif /* __sysV88__ */ |
0c8ae3d3 | 2677 | |
c85f7c16 JL |
2678 | #ifdef __sysV68__ |
2679 | ||
2680 | #include <sys/signal.h> | |
2681 | #include <errno.h> | |
2682 | ||
2683 | /* Motorola forgot to put memctl.o in the libp version of libc881.a, | |
2684 | so define it here, because we need it in __clear_insn_cache below */ | |
3698f44e MH |
2685 | /* On older versions of this OS, no memctl or MCT_TEXT are defined; |
2686 | hence we enable this stuff only if MCT_TEXT is #define'd. */ | |
c85f7c16 | 2687 | |
3698f44e | 2688 | #ifdef MCT_TEXT |
c85f7c16 JL |
2689 | asm("\n\ |
2690 | global memctl\n\ | |
2691 | memctl:\n\ | |
2692 | movq &75,%d0\n\ | |
2693 | trap &0\n\ | |
2694 | bcc.b noerror\n\ | |
2695 | jmp cerror%\n\ | |
2696 | noerror:\n\ | |
2697 | movq &0,%d0\n\ | |
2698 | rts"); | |
3698f44e | 2699 | #endif |
c85f7c16 JL |
2700 | |
2701 | /* Clear instruction cache so we can call trampolines on stack. | |
2702 | This is called from FINALIZE_TRAMPOLINE in mot3300.h. */ | |
2703 | ||
2704 | void | |
3e7d8ef1 | 2705 | __clear_insn_cache (void) |
c85f7c16 | 2706 | { |
3698f44e | 2707 | #ifdef MCT_TEXT |
c85f7c16 JL |
2708 | int save_errno; |
2709 | ||
2710 | /* Preserve errno, because users would be surprised to have | |
2711 | errno changing without explicitly calling any system-call. */ | |
2712 | save_errno = errno; | |
2713 | ||
2714 | /* Keep it simple : memctl (MCT_TEXT) always fully clears the insn cache. | |
2715 | No need to use an address derived from _start or %sp, as 0 works also. */ | |
2716 | memctl(0, 4096, MCT_TEXT); | |
2717 | errno = save_errno; | |
3698f44e | 2718 | #endif |
c85f7c16 JL |
2719 | } |
2720 | ||
2721 | #endif /* __sysV68__ */ | |
2722 | ||
b335c2cc TW |
2723 | #ifdef __pyr__ |
2724 | ||
98126ed6 | 2725 | #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ |
b335c2cc TW |
2726 | #include <stdio.h> |
2727 | #include <sys/mman.h> | |
2728 | #include <sys/types.h> | |
2729 | #include <sys/param.h> | |
2730 | #include <sys/vmmac.h> | |
2731 | ||
2732 | /* Modified from the convex -code above. | |
0f41302f | 2733 | mremap promises to clear the i-cache. */ |
b335c2cc TW |
2734 | |
2735 | void | |
3e7d8ef1 | 2736 | __enable_execute_stack (void) |
b335c2cc TW |
2737 | { |
2738 | int fp; | |
2739 | if (mprotect (((unsigned int)&fp/PAGSIZ)*PAGSIZ, PAGSIZ, | |
2740 | PROT_READ|PROT_WRITE|PROT_EXEC)) | |
2741 | { | |
2742 | perror ("mprotect in __enable_execute_stack"); | |
2743 | fflush (stderr); | |
2744 | abort (); | |
2745 | } | |
2746 | } | |
2747 | #endif /* __pyr__ */ | |
7d41c411 RK |
2748 | |
2749 | #if defined (sony_news) && defined (SYSTYPE_BSD) | |
2750 | ||
2751 | #include <stdio.h> | |
2752 | #include <sys/types.h> | |
2753 | #include <sys/param.h> | |
2754 | #include <syscall.h> | |
2755 | #include <machine/sysnews.h> | |
2756 | ||
2757 | /* cacheflush function for NEWS-OS 4.2. | |
2758 | This function is called from trampoline-initialize code | |
2759 | defined in config/mips/mips.h. */ | |
2760 | ||
2761 | void | |
37ef1054 | 2762 | cacheflush (char *beg, int size, int flag) |
7d41c411 RK |
2763 | { |
2764 | if (syscall (SYS_sysnews, NEWS_CACHEFLUSH, beg, size, FLUSH_BCACHE)) | |
2765 | { | |
2766 | perror ("cache_flush"); | |
2767 | fflush (stderr); | |
2768 | abort (); | |
2769 | } | |
2770 | } | |
2771 | ||
2772 | #endif /* sony_news */ | |
203b91b9 RS |
2773 | #endif /* L_trampoline */ |
2774 | \f | |
cae21ae8 | 2775 | #ifndef __CYGWIN__ |
203b91b9 RS |
2776 | #ifdef L__main |
2777 | ||
2778 | #include "gbl-ctors.h" | |
c06cff95 RS |
2779 | /* Some systems use __main in a way incompatible with its use in gcc, in these |
2780 | cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to | |
2781 | give the same symbol without quotes for an alternative entry point. You | |
0f41302f | 2782 | must define both, or neither. */ |
c06cff95 RS |
2783 | #ifndef NAME__MAIN |
2784 | #define NAME__MAIN "__main" | |
2785 | #define SYMBOL__MAIN __main | |
2786 | #endif | |
203b91b9 | 2787 | |
fe1fd353 JM |
2788 | #ifdef INIT_SECTION_ASM_OP |
2789 | #undef HAS_INIT_SECTION | |
2790 | #define HAS_INIT_SECTION | |
2791 | #endif | |
2792 | ||
2793 | #if !defined (HAS_INIT_SECTION) || !defined (OBJECT_FORMAT_ELF) | |
31cf0144 JM |
2794 | |
2795 | /* Some ELF crosses use crtstuff.c to provide __CTOR_LIST__, but use this | |
2796 | code to run constructors. In that case, we need to handle EH here, too. */ | |
2797 | ||
2798 | #ifdef EH_FRAME_SECTION | |
2799 | #include "frame.h" | |
2800 | extern unsigned char __EH_FRAME_BEGIN__[]; | |
2801 | #endif | |
2802 | ||
203b91b9 RS |
2803 | /* Run all the global destructors on exit from the program. */ |
2804 | ||
2805 | void | |
3e7d8ef1 | 2806 | __do_global_dtors (void) |
203b91b9 | 2807 | { |
89cf554b RS |
2808 | #ifdef DO_GLOBAL_DTORS_BODY |
2809 | DO_GLOBAL_DTORS_BODY; | |
2810 | #else | |
b40b9d93 MS |
2811 | static func_ptr *p = __DTOR_LIST__ + 1; |
2812 | while (*p) | |
2813 | { | |
2814 | p++; | |
2815 | (*(p-1)) (); | |
2816 | } | |
89cf554b | 2817 | #endif |
bf279c4e | 2818 | #if defined (EH_FRAME_SECTION) && !defined (HAS_INIT_SECTION) |
a4ebb0e6 GRK |
2819 | { |
2820 | static int completed = 0; | |
2821 | if (! completed) | |
2822 | { | |
2823 | completed = 1; | |
2824 | __deregister_frame_info (__EH_FRAME_BEGIN__); | |
2825 | } | |
2826 | } | |
31cf0144 | 2827 | #endif |
203b91b9 | 2828 | } |
68d69835 | 2829 | #endif |
203b91b9 | 2830 | |
fe1fd353 | 2831 | #ifndef HAS_INIT_SECTION |
203b91b9 RS |
2832 | /* Run all the global constructors on entry to the program. */ |
2833 | ||
203b91b9 | 2834 | void |
3e7d8ef1 | 2835 | __do_global_ctors (void) |
203b91b9 | 2836 | { |
31cf0144 JM |
2837 | #ifdef EH_FRAME_SECTION |
2838 | { | |
2839 | static struct object object; | |
2840 | __register_frame_info (__EH_FRAME_BEGIN__, &object); | |
2841 | } | |
2842 | #endif | |
203b91b9 | 2843 | DO_GLOBAL_CTORS_BODY; |
a218d5ba | 2844 | atexit (__do_global_dtors); |
203b91b9 | 2845 | } |
fe1fd353 | 2846 | #endif /* no HAS_INIT_SECTION */ |
203b91b9 | 2847 | |
fe1fd353 | 2848 | #if !defined (HAS_INIT_SECTION) || defined (INVOKE__main) |
203b91b9 RS |
2849 | /* Subroutine called automatically by `main'. |
2850 | Compiling a global function named `main' | |
2851 | produces an automatic call to this function at the beginning. | |
2852 | ||
2853 | For many systems, this routine calls __do_global_ctors. | |
2854 | For systems which support a .init section we use the .init section | |
2855 | to run __do_global_ctors, so we need not do anything here. */ | |
2856 | ||
2857 | void | |
c06cff95 | 2858 | SYMBOL__MAIN () |
203b91b9 RS |
2859 | { |
2860 | /* Support recursive calls to `main': run initializers just once. */ | |
7e6f1890 | 2861 | static int initialized; |
203b91b9 RS |
2862 | if (! initialized) |
2863 | { | |
2864 | initialized = 1; | |
2865 | __do_global_ctors (); | |
2866 | } | |
2867 | } | |
fe1fd353 | 2868 | #endif /* no HAS_INIT_SECTION or INVOKE__main */ |
203b91b9 RS |
2869 | |
2870 | #endif /* L__main */ | |
cae21ae8 | 2871 | #endif /* __CYGWIN__ */ |
203b91b9 | 2872 | \f |
ad38743d | 2873 | #ifdef L_ctors |
203b91b9 RS |
2874 | |
2875 | #include "gbl-ctors.h" | |
2876 | ||
2877 | /* Provide default definitions for the lists of constructors and | |
657be7af JL |
2878 | destructors, so that we don't get linker errors. These symbols are |
2879 | intentionally bss symbols, so that gld and/or collect will provide | |
2880 | the right values. */ | |
203b91b9 RS |
2881 | |
2882 | /* We declare the lists here with two elements each, | |
657be7af JL |
2883 | so that they are valid empty lists if no other definition is loaded. |
2884 | ||
2885 | If we are using the old "set" extensions to have the gnu linker | |
2886 | collect ctors and dtors, then we __CTOR_LIST__ and __DTOR_LIST__ | |
2887 | must be in the bss/common section. | |
2888 | ||
2889 | Long term no port should use those extensions. But many still do. */ | |
b335c2cc | 2890 | #if !defined(INIT_SECTION_ASM_OP) && !defined(CTOR_LISTS_DEFINED_EXTERNALLY) |
657be7af | 2891 | #if defined (ASM_OUTPUT_CONSTRUCTOR) || defined (USE_COLLECT2) |
d15d0264 RS |
2892 | func_ptr __CTOR_LIST__[2] = {0, 0}; |
2893 | func_ptr __DTOR_LIST__[2] = {0, 0}; | |
657be7af JL |
2894 | #else |
2895 | func_ptr __CTOR_LIST__[2]; | |
2896 | func_ptr __DTOR_LIST__[2]; | |
2897 | #endif | |
b335c2cc | 2898 | #endif /* no INIT_SECTION_ASM_OP and not CTOR_LISTS_DEFINED_EXTERNALLY */ |
ad38743d RS |
2899 | #endif /* L_ctors */ |
2900 | \f | |
2901 | #ifdef L_exit | |
2902 | ||
2903 | #include "gbl-ctors.h" | |
203b91b9 | 2904 | |
8b7677be | 2905 | #ifdef NEED_ATEXIT |
8b7677be | 2906 | |
f75e8946 | 2907 | #ifndef ON_EXIT |
203b91b9 | 2908 | |
8b7677be RK |
2909 | # include <errno.h> |
2910 | ||
920b13cc | 2911 | static func_ptr *atexit_chain = 0; |
8b7677be RK |
2912 | static long atexit_chain_length = 0; |
2913 | static volatile long last_atexit_chain_slot = -1; | |
2914 | ||
c063dc98 JM |
2915 | int |
2916 | atexit (func_ptr func) | |
8b7677be RK |
2917 | { |
2918 | if (++last_atexit_chain_slot == atexit_chain_length) | |
2919 | { | |
2920 | atexit_chain_length += 32; | |
2921 | if (atexit_chain) | |
a25cea96 RK |
2922 | atexit_chain = (func_ptr *) realloc (atexit_chain, atexit_chain_length |
2923 | * sizeof (func_ptr)); | |
8b7677be | 2924 | else |
a25cea96 RK |
2925 | atexit_chain = (func_ptr *) malloc (atexit_chain_length |
2926 | * sizeof (func_ptr)); | |
8b7677be RK |
2927 | if (! atexit_chain) |
2928 | { | |
2929 | atexit_chain_length = 0; | |
2930 | last_atexit_chain_slot = -1; | |
2931 | errno = ENOMEM; | |
2932 | return (-1); | |
2933 | } | |
2934 | } | |
2935 | atexit_chain[last_atexit_chain_slot] = func; | |
2936 | return (0); | |
2937 | } | |
8b7677be | 2938 | |
3e7d8ef1 KG |
2939 | extern void _cleanup (void); |
2940 | extern void _exit (int) __attribute__ ((__noreturn__)); | |
203b91b9 RS |
2941 | |
2942 | void | |
37ef1054 | 2943 | exit (int status) |
203b91b9 | 2944 | { |
8b7677be RK |
2945 | if (atexit_chain) |
2946 | { | |
2947 | for ( ; last_atexit_chain_slot-- >= 0; ) | |
2948 | { | |
2949 | (*atexit_chain[last_atexit_chain_slot + 1]) (); | |
920b13cc | 2950 | atexit_chain[last_atexit_chain_slot + 1] = 0; |
8b7677be RK |
2951 | } |
2952 | free (atexit_chain); | |
920b13cc | 2953 | atexit_chain = 0; |
8b7677be | 2954 | } |
203b91b9 RS |
2955 | #ifdef EXIT_BODY |
2956 | EXIT_BODY; | |
2957 | #else | |
2958 | _cleanup (); | |
2959 | #endif | |
2960 | _exit (status); | |
2961 | } | |
2962 | ||
f75e8946 | 2963 | #else /* ON_EXIT */ |
bceb30e7 | 2964 | |
c063dc98 JM |
2965 | /* Simple; we just need a wrapper for ON_EXIT. */ |
2966 | int | |
2967 | atexit (func_ptr func) | |
bceb30e7 | 2968 | { |
c063dc98 | 2969 | return ON_EXIT (func); |
bceb30e7 | 2970 | } |
c063dc98 | 2971 | |
f75e8946 | 2972 | #endif /* ON_EXIT */ |
c063dc98 | 2973 | #endif /* NEED_ATEXIT */ |
203b91b9 RS |
2974 | |
2975 | #endif /* L_exit */ | |
2976 | \f | |
ad912eec | 2977 | #ifdef L_eh |
6adb4e3a | 2978 | |
f24af81b | 2979 | #include "gthr.h" |
6adb4e3a | 2980 | |
154bba13 | 2981 | /* Shared exception handling support routines. */ |
6adb4e3a | 2982 | |
e976b8b2 | 2983 | void |
3e7d8ef1 | 2984 | __default_terminate (void) |
e976b8b2 MS |
2985 | { |
2986 | abort (); | |
2987 | } | |
2988 | ||
3e7d8ef1 KG |
2989 | void (*__terminate_func)(void) __attribute__ ((__noreturn__)) = |
2990 | __default_terminate; | |
e976b8b2 MS |
2991 | |
2992 | void | |
3e7d8ef1 | 2993 | __terminate (void) |
e976b8b2 MS |
2994 | { |
2995 | (*__terminate_func)(); | |
2996 | } | |
2997 | ||
ca55abae JM |
2998 | void * |
2999 | __throw_type_match (void *catch_type, void *throw_type, void *obj) | |
3000 | { | |
3001 | #if 0 | |
3002 | printf ("__throw_type_match (): catch_type = %s, throw_type = %s\n", | |
3003 | catch_type, throw_type); | |
3004 | #endif | |
3005 | if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0) | |
3006 | return obj; | |
3007 | return 0; | |
3008 | } | |
3009 | ||
3010 | void | |
3e7d8ef1 | 3011 | __empty (void) |
ca55abae JM |
3012 | { |
3013 | } | |
3014 | \f | |
154bba13 | 3015 | |
9a0d1e1b AM |
3016 | /* Include definitions of EH context and table layout */ |
3017 | ||
3018 | #include "eh-common.h" | |
43566944 | 3019 | #ifndef inhibit_libc |
7ac2148b | 3020 | #include <stdio.h> |
43566944 | 3021 | #endif |
154bba13 | 3022 | |
154bba13 TT |
3023 | /* Allocate and return a new EH context structure. */ |
3024 | ||
3e7d8ef1 | 3025 | #if __GTHREADS |
154bba13 | 3026 | static void * |
3e7d8ef1 | 3027 | new_eh_context (void) |
154bba13 | 3028 | { |
d0b9a143 MS |
3029 | struct eh_full_context { |
3030 | struct eh_context c; | |
3031 | void *top_elt[2]; | |
3032 | } *ehfc = (struct eh_full_context *) malloc (sizeof *ehfc); | |
3033 | ||
3034 | if (! ehfc) | |
154bba13 TT |
3035 | __terminate (); |
3036 | ||
d0b9a143 | 3037 | memset (ehfc, 0, sizeof *ehfc); |
154bba13 | 3038 | |
d0b9a143 | 3039 | ehfc->c.dynamic_handler_chain = (void **) ehfc->top_elt; |
154bba13 | 3040 | |
d0b9a143 MS |
3041 | /* This should optimize out entirely. This should always be true, |
3042 | but just in case it ever isn't, don't allow bogus code to be | |
3043 | generated. */ | |
3044 | ||
3045 | if ((void*)(&ehfc->c) != (void*)ehfc) | |
3046 | __terminate (); | |
3047 | ||
3048 | return &ehfc->c; | |
154bba13 TT |
3049 | } |
3050 | ||
154bba13 TT |
3051 | static __gthread_key_t eh_context_key; |
3052 | ||
3053 | /* Destructor for struct eh_context. */ | |
3054 | static void | |
3055 | eh_context_free (void *ptr) | |
3056 | { | |
f24af81b | 3057 | __gthread_key_dtor (eh_context_key, ptr); |
154bba13 TT |
3058 | if (ptr) |
3059 | free (ptr); | |
3060 | } | |
3061 | #endif | |
3062 | ||
3063 | /* Pointer to function to return EH context. */ | |
3064 | ||
3e7d8ef1 KG |
3065 | static struct eh_context *eh_context_initialize (void); |
3066 | static struct eh_context *eh_context_static (void); | |
154bba13 | 3067 | #if __GTHREADS |
3e7d8ef1 | 3068 | static struct eh_context *eh_context_specific (void); |
154bba13 TT |
3069 | #endif |
3070 | ||
3e7d8ef1 | 3071 | static struct eh_context *(*get_eh_context) (void) = &eh_context_initialize; |
154bba13 TT |
3072 | |
3073 | /* Routine to get EH context. | |
3074 | This one will simply call the function pointer. */ | |
3075 | ||
3076 | void * | |
3e7d8ef1 | 3077 | __get_eh_context (void) |
154bba13 TT |
3078 | { |
3079 | return (void *) (*get_eh_context) (); | |
3080 | } | |
3081 | ||
3082 | /* Get and set the language specific info pointer. */ | |
3083 | ||
3084 | void ** | |
3e7d8ef1 | 3085 | __get_eh_info (void) |
154bba13 TT |
3086 | { |
3087 | struct eh_context *eh = (*get_eh_context) (); | |
0776059e | 3088 | return &eh->info; |
154bba13 TT |
3089 | } |
3090 | \f | |
d9d5c9de BS |
3091 | #ifdef DWARF2_UNWIND_INFO |
3092 | static int dwarf_reg_size_table_initialized = 0; | |
7e259f25 | 3093 | static char dwarf_reg_size_table[DWARF_FRAME_REGISTERS]; |
d9d5c9de BS |
3094 | |
3095 | static void | |
3e7d8ef1 | 3096 | init_reg_size_table (void) |
d9d5c9de BS |
3097 | { |
3098 | __builtin_init_dwarf_reg_size_table (dwarf_reg_size_table); | |
3099 | dwarf_reg_size_table_initialized = 1; | |
3100 | } | |
3101 | #endif | |
3102 | ||
154bba13 TT |
3103 | #if __GTHREADS |
3104 | static void | |
3e7d8ef1 | 3105 | eh_threads_initialize (void) |
154bba13 TT |
3106 | { |
3107 | /* Try to create the key. If it fails, revert to static method, | |
3108 | otherwise start using thread specific EH contexts. */ | |
3109 | if (__gthread_key_create (&eh_context_key, &eh_context_free) == 0) | |
3110 | get_eh_context = &eh_context_specific; | |
3111 | else | |
3112 | get_eh_context = &eh_context_static; | |
3113 | } | |
3114 | #endif /* no __GTHREADS */ | |
3115 | ||
3116 | /* Initialize EH context. | |
3117 | This will be called only once, since we change GET_EH_CONTEXT | |
3118 | pointer to another routine. */ | |
3119 | ||
3120 | static struct eh_context * | |
3e7d8ef1 | 3121 | eh_context_initialize (void) |
154bba13 TT |
3122 | { |
3123 | #if __GTHREADS | |
3124 | ||
3125 | static __gthread_once_t once = __GTHREAD_ONCE_INIT; | |
754d1a92 TT |
3126 | /* Make sure that get_eh_context does not point to us anymore. |
3127 | Some systems have dummy thread routines in their libc that | |
3128 | return a success (Solaris 2.6 for example). */ | |
3129 | if (__gthread_once (&once, eh_threads_initialize) != 0 | |
3130 | || get_eh_context == &eh_context_initialize) | |
f24af81b TT |
3131 | { |
3132 | /* Use static version of EH context. */ | |
3133 | get_eh_context = &eh_context_static; | |
3134 | } | |
d9d5c9de BS |
3135 | #ifdef DWARF2_UNWIND_INFO |
3136 | { | |
3137 | static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT; | |
3138 | if (__gthread_once (&once_regsizes, init_reg_size_table) != 0 | |
3139 | || ! dwarf_reg_size_table_initialized) | |
3140 | init_reg_size_table (); | |
3141 | } | |
3142 | #endif | |
154bba13 TT |
3143 | |
3144 | #else /* no __GTHREADS */ | |
3145 | ||
3146 | /* Use static version of EH context. */ | |
3147 | get_eh_context = &eh_context_static; | |
3148 | ||
d9d5c9de BS |
3149 | #ifdef DWARF2_UNWIND_INFO |
3150 | init_reg_size_table (); | |
3151 | #endif | |
3152 | ||
154bba13 TT |
3153 | #endif /* no __GTHREADS */ |
3154 | ||
3155 | return (*get_eh_context) (); | |
3156 | } | |
3157 | ||
3158 | /* Return a static EH context. */ | |
3159 | ||
3160 | static struct eh_context * | |
3e7d8ef1 | 3161 | eh_context_static (void) |
154bba13 | 3162 | { |
c3cad221 JL |
3163 | static struct eh_context eh; |
3164 | static int initialized; | |
d0b9a143 MS |
3165 | static void *top_elt[2]; |
3166 | ||
c3cad221 JL |
3167 | if (! initialized) |
3168 | { | |
3169 | initialized = 1; | |
3170 | memset (&eh, 0, sizeof eh); | |
3171 | eh.dynamic_handler_chain = top_elt; | |
3172 | } | |
3173 | return &eh; | |
154bba13 TT |
3174 | } |
3175 | ||
3176 | #if __GTHREADS | |
3177 | /* Return a thread specific EH context. */ | |
3178 | ||
3179 | static struct eh_context * | |
3e7d8ef1 | 3180 | eh_context_specific (void) |
154bba13 TT |
3181 | { |
3182 | struct eh_context *eh; | |
3183 | eh = (struct eh_context *) __gthread_getspecific (eh_context_key); | |
3184 | if (! eh) | |
3185 | { | |
3186 | eh = new_eh_context (); | |
3187 | if (__gthread_setspecific (eh_context_key, (void *) eh) != 0) | |
3188 | __terminate (); | |
3189 | } | |
3190 | ||
3191 | return eh; | |
3192 | } | |
3193 | #endif __GTHREADS | |
3194 | \f | |
ca55abae JM |
3195 | /* Support routines for setjmp/longjmp exception handling. */ |
3196 | ||
e976b8b2 MS |
3197 | /* Calls to __sjthrow are generated by the compiler when an exception |
3198 | is raised when using the setjmp/longjmp exception handling codegen | |
3199 | method. */ | |
3200 | ||
154bba13 | 3201 | #ifdef DONT_USE_BUILTIN_SETJMP |
6e6a07d2 | 3202 | extern void longjmp (void *, int); |
154bba13 | 3203 | #endif |
e976b8b2 MS |
3204 | |
3205 | /* Routine to get the head of the current thread's dynamic handler chain | |
154bba13 | 3206 | use for exception handling. */ |
e976b8b2 MS |
3207 | |
3208 | void *** | |
3e7d8ef1 | 3209 | __get_dynamic_handler_chain (void) |
e976b8b2 | 3210 | { |
154bba13 | 3211 | struct eh_context *eh = (*get_eh_context) (); |
0776059e | 3212 | return &eh->dynamic_handler_chain; |
e976b8b2 MS |
3213 | } |
3214 | ||
3215 | /* This is used to throw an exception when the setjmp/longjmp codegen | |
3216 | method is used for exception handling. | |
3217 | ||
154bba13 TT |
3218 | We call __terminate if there are no handlers left. Otherwise we run the |
3219 | cleanup actions off the dynamic cleanup stack, and pop the top of the | |
3220 | dynamic handler chain, and use longjmp to transfer back to the associated | |
3221 | handler. */ | |
e976b8b2 MS |
3222 | |
3223 | void | |
3e7d8ef1 | 3224 | __sjthrow (void) |
e976b8b2 | 3225 | { |
154bba13 TT |
3226 | struct eh_context *eh = (*get_eh_context) (); |
3227 | void ***dhc = &eh->dynamic_handler_chain; | |
e976b8b2 MS |
3228 | void *jmpbuf; |
3229 | void (*func)(void *, int); | |
3230 | void *arg; | |
3e7d8ef1 KG |
3231 | /* The cleanup chain is one word into the buffer. Get the cleanup chain. */ |
3232 | void ***cleanup = (void***)&(*dhc)[1]; | |
e976b8b2 MS |
3233 | |
3234 | /* If there are any cleanups in the chain, run them now. */ | |
3235 | if (cleanup[0]) | |
3236 | { | |
3237 | double store[200]; | |
3238 | void **buf = (void**)store; | |
3239 | buf[1] = 0; | |
3240 | buf[0] = (*dhc); | |
3241 | ||
3242 | /* try { */ | |
6e6a07d2 | 3243 | #ifdef DONT_USE_BUILTIN_SETJMP |
e976b8b2 | 3244 | if (! setjmp (&buf[2])) |
6e6a07d2 MS |
3245 | #else |
3246 | if (! __builtin_setjmp (&buf[2])) | |
3247 | #endif | |
e976b8b2 MS |
3248 | { |
3249 | *dhc = buf; | |
3250 | while (cleanup[0]) | |
3251 | { | |
3252 | func = (void(*)(void*, int))cleanup[0][1]; | |
3253 | arg = (void*)cleanup[0][2]; | |
3254 | ||
3255 | /* Update this before running the cleanup. */ | |
3256 | cleanup[0] = (void **)cleanup[0][0]; | |
3257 | ||
3258 | (*func)(arg, 2); | |
3259 | } | |
3260 | *dhc = buf[0]; | |
3261 | } | |
3262 | /* catch (...) */ | |
3263 | else | |
3264 | { | |
3265 | __terminate (); | |
3266 | } | |
3267 | } | |
3268 | ||
3269 | /* We must call terminate if we try and rethrow an exception, when | |
3270 | there is no exception currently active and when there are no | |
3271 | handlers left. */ | |
d0b9a143 | 3272 | if (! eh->info || (*dhc)[0] == 0) |
e976b8b2 MS |
3273 | __terminate (); |
3274 | ||
3275 | /* Find the jmpbuf associated with the top element of the dynamic | |
3276 | handler chain. The jumpbuf starts two words into the buffer. */ | |
3277 | jmpbuf = &(*dhc)[2]; | |
3278 | ||
3279 | /* Then we pop the top element off the dynamic handler chain. */ | |
3280 | *dhc = (void**)(*dhc)[0]; | |
3281 | ||
3282 | /* And then we jump to the handler. */ | |
3283 | ||
6e6a07d2 | 3284 | #ifdef DONT_USE_BUILTIN_SETJMP |
e976b8b2 | 3285 | longjmp (jmpbuf, 1); |
6e6a07d2 MS |
3286 | #else |
3287 | __builtin_longjmp (jmpbuf, 1); | |
e976b8b2 MS |
3288 | #endif |
3289 | } | |
3290 | ||
3291 | /* Run cleanups on the dynamic cleanup stack for the current dynamic | |
3292 | handler, then pop the handler off the dynamic handler stack, and | |
3293 | then throw. This is used to skip the first handler, and transfer | |
3294 | control to the next handler in the dynamic handler stack. */ | |
3295 | ||
3296 | void | |
3e7d8ef1 | 3297 | __sjpopnthrow (void) |
e976b8b2 | 3298 | { |
154bba13 TT |
3299 | struct eh_context *eh = (*get_eh_context) (); |
3300 | void ***dhc = &eh->dynamic_handler_chain; | |
e976b8b2 MS |
3301 | void (*func)(void *, int); |
3302 | void *arg; | |
3e7d8ef1 KG |
3303 | /* The cleanup chain is one word into the buffer. Get the cleanup chain. */ |
3304 | void ***cleanup = (void***)&(*dhc)[1]; | |
e976b8b2 MS |
3305 | |
3306 | /* If there are any cleanups in the chain, run them now. */ | |
3307 | if (cleanup[0]) | |
3308 | { | |
3309 | double store[200]; | |
3310 | void **buf = (void**)store; | |
3311 | buf[1] = 0; | |
3312 | buf[0] = (*dhc); | |
3313 | ||
3314 | /* try { */ | |
6e6a07d2 | 3315 | #ifdef DONT_USE_BUILTIN_SETJMP |
e976b8b2 | 3316 | if (! setjmp (&buf[2])) |
6e6a07d2 MS |
3317 | #else |
3318 | if (! __builtin_setjmp (&buf[2])) | |
3319 | #endif | |
e976b8b2 MS |
3320 | { |
3321 | *dhc = buf; | |
3322 | while (cleanup[0]) | |
3323 | { | |
3324 | func = (void(*)(void*, int))cleanup[0][1]; | |
3325 | arg = (void*)cleanup[0][2]; | |
3326 | ||
3327 | /* Update this before running the cleanup. */ | |
3328 | cleanup[0] = (void **)cleanup[0][0]; | |
3329 | ||
3330 | (*func)(arg, 2); | |
3331 | } | |
3332 | *dhc = buf[0]; | |
3333 | } | |
3334 | /* catch (...) */ | |
3335 | else | |
3336 | { | |
3337 | __terminate (); | |
3338 | } | |
3339 | } | |
3340 | ||
3341 | /* Then we pop the top element off the dynamic handler chain. */ | |
3342 | *dhc = (void**)(*dhc)[0]; | |
3343 | ||
3344 | __sjthrow (); | |
3345 | } | |
ca55abae JM |
3346 | \f |
3347 | /* Support code for all exception region-based exception handling. */ | |
3348 | ||
bf71cd2e AM |
3349 | int |
3350 | __eh_rtime_match (void *rtime) | |
3351 | { | |
3352 | void *info; | |
3353 | __eh_matcher matcher; | |
3354 | void *ret; | |
3355 | ||
3356 | info = *(__get_eh_info ()); | |
3357 | matcher = ((__eh_info *)info)->match_function; | |
7ac2148b AM |
3358 | if (! matcher) |
3359 | { | |
43566944 | 3360 | #ifndef inhibit_libc |
7ac2148b | 3361 | fprintf (stderr, "Internal Compiler Bug: No runtime type matcher."); |
43566944 | 3362 | #endif |
7ac2148b AM |
3363 | return 0; |
3364 | } | |
bf71cd2e | 3365 | ret = (*matcher) (info, rtime, (void *)0); |
7ac2148b | 3366 | return (ret != NULL); |
bf71cd2e AM |
3367 | } |
3368 | ||
ca55abae JM |
3369 | /* This value identifies the place from which an exception is being |
3370 | thrown. */ | |
3371 | ||
ca55abae JM |
3372 | #ifdef EH_TABLE_LOOKUP |
3373 | ||
3374 | EH_TABLE_LOOKUP | |
e976b8b2 | 3375 | |
ca55abae JM |
3376 | #else |
3377 | ||
d6f4ec51 | 3378 | #ifdef DWARF2_UNWIND_INFO |
ad912eec | 3379 | |
48b24bcd AM |
3380 | /* Return the table version of an exception descriptor */ |
3381 | ||
3382 | short | |
3383 | __get_eh_table_version (exception_descriptor *table) | |
3384 | { | |
3385 | return table->lang.version; | |
3386 | } | |
3387 | ||
3388 | /* Return the originating table language of an exception descriptor */ | |
3389 | ||
3390 | short | |
3391 | __get_eh_table_language (exception_descriptor *table) | |
3392 | { | |
3393 | return table->lang.language; | |
3394 | } | |
3395 | ||
ca55abae JM |
3396 | /* This routine takes a PC and a pointer to the exception region TABLE for |
3397 | its translation unit, and returns the address of the exception handler | |
3398 | associated with the closest exception table handler entry associated | |
3399 | with that PC, or 0 if there are no table entries the PC fits in. | |
3400 | ||
3401 | In the advent of a tie, we have to give the last entry, as it represents | |
3402 | an inner block. */ | |
3403 | ||
a1622f83 AM |
3404 | static void * |
3405 | old_find_exception_handler (void *pc, old_exception_table *table) | |
3406 | { | |
3407 | if (table) | |
3408 | { | |
3409 | int pos; | |
3410 | int best = -1; | |
3411 | ||
3412 | /* We can't do a binary search because the table isn't guaranteed | |
3413 | to be sorted from function to function. */ | |
3414 | for (pos = 0; table[pos].start_region != (void *) -1; ++pos) | |
3415 | { | |
3416 | if (table[pos].start_region <= pc && table[pos].end_region > pc) | |
3417 | { | |
3418 | /* This can apply. Make sure it is at least as small as | |
3419 | the previous best. */ | |
3420 | if (best == -1 || (table[pos].end_region <= table[best].end_region | |
3421 | && table[pos].start_region >= table[best].start_region)) | |
3422 | best = pos; | |
3423 | } | |
3424 | /* But it is sorted by starting PC within a function. */ | |
3425 | else if (best >= 0 && table[pos].start_region > pc) | |
3426 | break; | |
3427 | } | |
3428 | if (best != -1) | |
3429 | return table[best].exception_handler; | |
3430 | } | |
3431 | ||
3432 | return (void *) 0; | |
3433 | } | |
3434 | ||
e6cfb550 AM |
3435 | /* find_exception_handler finds the correct handler, if there is one, to |
3436 | handle an exception. | |
3437 | returns a pointer to the handler which controlled should be transferred | |
3438 | to, or NULL if there is nothing left. | |
3439 | Parameters: | |
3440 | PC - pc where the exception originates. If this is a rethrow, | |
3441 | then this starts out as a pointer to the exception table | |
3442 | entry we wish to rethrow out of. | |
3443 | TABLE - exception table for the current module. | |
3444 | EH_INFO - eh info pointer for this exception. | |
3445 | RETHROW - 1 if this is a rethrow. (see incoming value of PC). | |
3446 | CLEANUP - returned flag indicating whether this is a cleanup handler. | |
3447 | */ | |
ca55abae | 3448 | static void * |
e6cfb550 AM |
3449 | find_exception_handler (void *pc, exception_descriptor *table, |
3450 | __eh_info *eh_info, int rethrow, int *cleanup) | |
ca55abae | 3451 | { |
e6cfb550 AM |
3452 | |
3453 | void *retval = NULL; | |
3454 | *cleanup = 1; | |
ca55abae JM |
3455 | if (table) |
3456 | { | |
e6cfb550 | 3457 | int pos = 0; |
9a0d1e1b AM |
3458 | /* The new model assumed the table is sorted inner-most out so the |
3459 | first region we find which matches is the correct one */ | |
3460 | ||
9a0d1e1b AM |
3461 | exception_table *tab = &(table->table[0]); |
3462 | ||
3463 | /* Subtract 1 from the PC to avoid hitting the next region */ | |
e6cfb550 AM |
3464 | if (rethrow) |
3465 | { | |
3466 | /* pc is actually the region table entry to rethrow out of */ | |
3467 | pos = ((exception_table *) pc) - tab; | |
3468 | pc = ((exception_table *) pc)->end_region - 1; | |
3469 | ||
3470 | /* The label is always on the LAST handler entry for a region, | |
3471 | so we know the next entry is a different region, even if the | |
3472 | addresses are the same. Make sure its not end of table tho. */ | |
3473 | if (tab[pos].start_region != (void *) -1) | |
3474 | pos++; | |
3475 | } | |
3476 | else | |
3477 | pc--; | |
9a0d1e1b AM |
3478 | |
3479 | /* We can't do a binary search because the table is in inner-most | |
3480 | to outermost address ranges within functions */ | |
e6cfb550 | 3481 | for ( ; tab[pos].start_region != (void *) -1; pos++) |
9a0d1e1b AM |
3482 | { |
3483 | if (tab[pos].start_region <= pc && tab[pos].end_region > pc) | |
3484 | { | |
3485 | if (tab[pos].match_info) | |
3486 | { | |
e6cfb550 | 3487 | __eh_matcher matcher = eh_info->match_function; |
9a0d1e1b AM |
3488 | /* match info but no matcher is NOT a match */ |
3489 | if (matcher) | |
3490 | { | |
e6cfb550 AM |
3491 | void *ret = (*matcher)((void *) eh_info, |
3492 | tab[pos].match_info, table); | |
3493 | if (ret) | |
3494 | { | |
3495 | if (retval == NULL) | |
3496 | retval = tab[pos].exception_handler; | |
3497 | *cleanup = 0; | |
3498 | break; | |
3499 | } | |
9a0d1e1b AM |
3500 | } |
3501 | } | |
3502 | else | |
e6cfb550 AM |
3503 | { |
3504 | if (retval == NULL) | |
3505 | retval = tab[pos].exception_handler; | |
3506 | } | |
9a0d1e1b AM |
3507 | } |
3508 | } | |
ca55abae | 3509 | } |
e6cfb550 | 3510 | return retval; |
ca55abae | 3511 | } |
d6f4ec51 | 3512 | #endif /* DWARF2_UNWIND_INFO */ |
ca55abae JM |
3513 | #endif /* EH_TABLE_LOOKUP */ |
3514 | \f | |
0776059e | 3515 | #ifdef DWARF2_UNWIND_INFO |
ca55abae JM |
3516 | /* Support code for exception handling using static unwind information. */ |
3517 | ||
3518 | #include "frame.h" | |
3519 | ||
3520 | /* This type is used in get_reg and put_reg to deal with ABIs where a void* | |
3521 | is smaller than a word, such as the Irix 6 n32 ABI. We cast twice to | |
3522 | avoid a warning about casting between int and pointer of different | |
3523 | sizes. */ | |
3524 | ||
3525 | typedef int ptr_type __attribute__ ((mode (pointer))); | |
3526 | ||
71038426 RH |
3527 | #ifdef INCOMING_REGNO |
3528 | /* Is the saved value for register REG in frame UDATA stored in a register | |
3529 | window in the previous frame? */ | |
3530 | ||
3531 | /* ??? The Sparc INCOMING_REGNO references TARGET_FLAT. This allows us | |
3532 | to use the macro here. One wonders, though, that perhaps TARGET_FLAT | |
3533 | compiled functions won't work with the frame-unwind stuff here. | |
3534 | Perhaps the entireity of in_reg_window should be conditional on having | |
3535 | seen a DW_CFA_GNU_window_save? */ | |
3536 | #define target_flags 0 | |
3537 | ||
3538 | static int | |
3539 | in_reg_window (int reg, frame_state *udata) | |
3540 | { | |
3541 | if (udata->saved[reg] == REG_SAVED_REG) | |
3542 | return INCOMING_REGNO (reg) == reg; | |
3543 | if (udata->saved[reg] != REG_SAVED_OFFSET) | |
3544 | return 0; | |
3545 | ||
3546 | #ifdef STACK_GROWS_DOWNWARD | |
3547 | return udata->reg_or_offset[reg] > 0; | |
3548 | #else | |
3549 | return udata->reg_or_offset[reg] < 0; | |
3550 | #endif | |
3551 | } | |
3552 | #else | |
3e7d8ef1 KG |
3553 | static inline int |
3554 | in_reg_window (int reg __attribute__ ((__unused__)), | |
3555 | frame_state *udata __attribute__ ((__unused__))) | |
3556 | { | |
3557 | return 0; | |
3558 | } | |
71038426 RH |
3559 | #endif /* INCOMING_REGNO */ |
3560 | ||
3561 | /* Get the address of register REG as saved in UDATA, where SUB_UDATA is a | |
ca55abae JM |
3562 | frame called by UDATA or 0. */ |
3563 | ||
71038426 RH |
3564 | static word_type * |
3565 | get_reg_addr (unsigned reg, frame_state *udata, frame_state *sub_udata) | |
ca55abae | 3566 | { |
71038426 RH |
3567 | while (udata->saved[reg] == REG_SAVED_REG) |
3568 | { | |
3569 | reg = udata->reg_or_offset[reg]; | |
3570 | if (in_reg_window (reg, udata)) | |
3571 | { | |
3572 | udata = sub_udata; | |
3573 | sub_udata = NULL; | |
3574 | } | |
3575 | } | |
ca55abae | 3576 | if (udata->saved[reg] == REG_SAVED_OFFSET) |
71038426 | 3577 | return (word_type *)(udata->cfa + udata->reg_or_offset[reg]); |
ca55abae JM |
3578 | else |
3579 | abort (); | |
3580 | } | |
3581 | ||
71038426 RH |
3582 | /* Get the value of register REG as saved in UDATA, where SUB_UDATA is a |
3583 | frame called by UDATA or 0. */ | |
3584 | ||
3585 | static inline void * | |
3586 | get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata) | |
3587 | { | |
3588 | return (void *)(ptr_type) *get_reg_addr (reg, udata, sub_udata); | |
3589 | } | |
3590 | ||
ca55abae JM |
3591 | /* Overwrite the saved value for register REG in frame UDATA with VAL. */ |
3592 | ||
71038426 | 3593 | static inline void |
ca55abae JM |
3594 | put_reg (unsigned reg, void *val, frame_state *udata) |
3595 | { | |
71038426 | 3596 | *get_reg_addr (reg, udata, NULL) = (word_type)(ptr_type) val; |
ca55abae JM |
3597 | } |
3598 | ||
2f3ca9e7 JM |
3599 | /* Copy the saved value for register REG from frame UDATA to frame |
3600 | TARGET_UDATA. Unlike the previous two functions, this can handle | |
3601 | registers that are not one word large. */ | |
3602 | ||
3603 | static void | |
3604 | copy_reg (unsigned reg, frame_state *udata, frame_state *target_udata) | |
3605 | { | |
71038426 RH |
3606 | word_type *preg = get_reg_addr (reg, udata, NULL); |
3607 | word_type *ptreg = get_reg_addr (reg, target_udata, NULL); | |
3608 | ||
d9d5c9de | 3609 | memcpy (ptreg, preg, dwarf_reg_size_table [reg]); |
2f3ca9e7 JM |
3610 | } |
3611 | ||
71038426 | 3612 | /* Retrieve the return address for frame UDATA. */ |
ca55abae JM |
3613 | |
3614 | static inline void * | |
3615 | get_return_addr (frame_state *udata, frame_state *sub_udata) | |
3616 | { | |
3617 | return __builtin_extract_return_addr | |
3618 | (get_reg (udata->retaddr_column, udata, sub_udata)); | |
3619 | } | |
3620 | ||
3621 | /* Overwrite the return address for frame UDATA with VAL. */ | |
3622 | ||
3623 | static inline void | |
3624 | put_return_addr (void *val, frame_state *udata) | |
3625 | { | |
3626 | val = __builtin_frob_return_addr (val); | |
3627 | put_reg (udata->retaddr_column, val, udata); | |
3628 | } | |
3629 | ||
3630 | /* Given the current frame UDATA and its return address PC, return the | |
3631 | information about the calling frame in CALLER_UDATA. */ | |
3632 | ||
3633 | static void * | |
3634 | next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata) | |
3635 | { | |
3636 | caller_udata = __frame_state_for (pc, caller_udata); | |
3637 | if (! caller_udata) | |
3638 | return 0; | |
3639 | ||
3640 | /* Now go back to our caller's stack frame. If our caller's CFA register | |
3641 | was saved in our stack frame, restore it; otherwise, assume the CFA | |
3642 | register is SP and restore it to our CFA value. */ | |
3643 | if (udata->saved[caller_udata->cfa_reg]) | |
3644 | caller_udata->cfa = get_reg (caller_udata->cfa_reg, udata, 0); | |
3645 | else | |
3646 | caller_udata->cfa = udata->cfa; | |
3647 | caller_udata->cfa += caller_udata->cfa_offset; | |
3648 | ||
3649 | return caller_udata; | |
3650 | } | |
3651 | ||
e6cfb550 AM |
3652 | /* Hook to call before __terminate if only cleanup handlers remain. */ |
3653 | void | |
3e7d8ef1 | 3654 | __unwinding_cleanup (void) |
ca55abae | 3655 | { |
e6cfb550 | 3656 | } |
ca55abae | 3657 | |
e6cfb550 AM |
3658 | /* throw_helper performs some of the common grunt work for a throw. This |
3659 | routine is called by throw and rethrows. This is pretty much split | |
3660 | out from the old __throw routine. An addition has been added which allows | |
3661 | for a dummy call to a routine __unwinding_cleanup() when there are nothing | |
3662 | but cleanups remaining. This allows a debugger to examine the state | |
3663 | at which the throw was executed, before any cleanups, rather than | |
51980de6 JM |
3664 | at the terminate point after the stack has been unwound. |
3665 | ||
3666 | EH is the current eh_context structure. | |
3667 | PC is the address of the call to __throw. | |
3668 | MY_UDATA is the unwind information for __throw. | |
3669 | OFFSET_P is where we return the SP adjustment offset. */ | |
ca55abae | 3670 | |
e6cfb550 | 3671 | static void * |
3e7d8ef1 KG |
3672 | throw_helper (struct eh_context *eh, void *pc, frame_state *my_udata, |
3673 | long *offset_p) | |
e6cfb550 | 3674 | { |
51980de6 | 3675 | frame_state ustruct2, *udata = &ustruct2; |
e6cfb550 AM |
3676 | frame_state ustruct; |
3677 | frame_state *sub_udata = &ustruct; | |
3678 | void *saved_pc = pc; | |
3679 | void *handler; | |
3e7d8ef1 KG |
3680 | void *handler_p = 0; |
3681 | void *pc_p = 0; | |
e6cfb550 AM |
3682 | frame_state saved_ustruct; |
3683 | int new_eh_model; | |
3684 | int cleanup = 0; | |
3685 | int only_cleanup = 0; | |
3686 | int rethrow = 0; | |
3687 | int saved_state = 0; | |
51980de6 | 3688 | long args_size; |
e6cfb550 AM |
3689 | __eh_info *eh_info = (__eh_info *)eh->info; |
3690 | ||
3691 | /* Do we find a handler based on a re-throw PC? */ | |
3692 | if (eh->table_index != (void *) 0) | |
3693 | rethrow = 1; | |
3694 | ||
51980de6 JM |
3695 | memcpy (udata, my_udata, sizeof (*udata)); |
3696 | ||
e6cfb550 | 3697 | handler = (void *) 0; |
ca55abae JM |
3698 | for (;;) |
3699 | { | |
3700 | frame_state *p = udata; | |
3701 | udata = next_stack_level (pc, udata, sub_udata); | |
3702 | sub_udata = p; | |
3703 | ||
3704 | /* If we couldn't find the next frame, we lose. */ | |
3705 | if (! udata) | |
3706 | break; | |
3707 | ||
a1622f83 | 3708 | if (udata->eh_ptr == NULL) |
e6cfb550 | 3709 | new_eh_model = 0; |
a1622f83 | 3710 | else |
e6cfb550 | 3711 | new_eh_model = (((exception_descriptor *)(udata->eh_ptr))-> |
a1622f83 AM |
3712 | runtime_id_field == NEW_EH_RUNTIME); |
3713 | ||
e6cfb550 AM |
3714 | if (rethrow) |
3715 | { | |
3716 | rethrow = 0; | |
3717 | handler = find_exception_handler (eh->table_index, udata->eh_ptr, | |
3718 | eh_info, 1, &cleanup); | |
3719 | eh->table_index = (void *)0; | |
3720 | } | |
a1622f83 | 3721 | else |
e6cfb550 AM |
3722 | if (new_eh_model) |
3723 | handler = find_exception_handler (pc, udata->eh_ptr, eh_info, | |
3724 | 0, &cleanup); | |
3725 | else | |
3726 | handler = old_find_exception_handler (pc, udata->eh_ptr); | |
3727 | ||
3728 | /* If we found one, we can stop searching, if its not a cleanup. | |
3729 | for cleanups, we save the state, and keep looking. This allows | |
3730 | us to call a debug hook if there are nothing but cleanups left. */ | |
ca55abae | 3731 | if (handler) |
1b528097 JM |
3732 | { |
3733 | if (cleanup) | |
3734 | { | |
3735 | if (!saved_state) | |
3736 | { | |
3737 | saved_ustruct = *udata; | |
3738 | handler_p = handler; | |
3739 | pc_p = pc; | |
3740 | saved_state = 1; | |
3741 | only_cleanup = 1; | |
3742 | } | |
3743 | } | |
3744 | else | |
3745 | { | |
3746 | only_cleanup = 0; | |
3747 | break; | |
3748 | } | |
3749 | } | |
ca55abae | 3750 | |
6020d360 JM |
3751 | /* Otherwise, we continue searching. We subtract 1 from PC to avoid |
3752 | hitting the beginning of the next region. */ | |
3753 | pc = get_return_addr (udata, sub_udata) - 1; | |
ca55abae JM |
3754 | } |
3755 | ||
e6cfb550 AM |
3756 | if (saved_state) |
3757 | { | |
3758 | udata = &saved_ustruct; | |
3759 | handler = handler_p; | |
3760 | pc = pc_p; | |
3761 | if (only_cleanup) | |
3762 | __unwinding_cleanup (); | |
3763 | } | |
3764 | ||
ca55abae JM |
3765 | /* If we haven't found a handler by now, this is an unhandled |
3766 | exception. */ | |
e6cfb550 AM |
3767 | if (! handler) |
3768 | __terminate(); | |
ca55abae | 3769 | |
9a0d1e1b | 3770 | eh->handler_label = handler; |
9a0d1e1b | 3771 | |
51980de6 JM |
3772 | args_size = udata->args_size; |
3773 | ||
154bba13 | 3774 | if (pc == saved_pc) |
ca55abae JM |
3775 | /* We found a handler in the throw context, no need to unwind. */ |
3776 | udata = my_udata; | |
3777 | else | |
3778 | { | |
3779 | int i; | |
ca55abae JM |
3780 | |
3781 | /* Unwind all the frames between this one and the handler by copying | |
3782 | their saved register values into our register save slots. */ | |
3783 | ||
3784 | /* Remember the PC where we found the handler. */ | |
3785 | void *handler_pc = pc; | |
3786 | ||
3787 | /* Start from the throw context again. */ | |
154bba13 | 3788 | pc = saved_pc; |
ca55abae JM |
3789 | memcpy (udata, my_udata, sizeof (*udata)); |
3790 | ||
3791 | while (pc != handler_pc) | |
3792 | { | |
3793 | frame_state *p = udata; | |
3794 | udata = next_stack_level (pc, udata, sub_udata); | |
3795 | sub_udata = p; | |
3796 | ||
7e259f25 | 3797 | for (i = 0; i < DWARF_FRAME_REGISTERS; ++i) |
d1485032 | 3798 | if (i != udata->retaddr_column && udata->saved[i]) |
ca55abae | 3799 | { |
ca55abae JM |
3800 | /* If you modify the saved value of the return address |
3801 | register on the SPARC, you modify the return address for | |
3802 | your caller's frame. Don't do that here, as it will | |
3803 | confuse get_return_addr. */ | |
3804 | if (in_reg_window (i, udata) | |
3805 | && udata->saved[udata->retaddr_column] == REG_SAVED_REG | |
3806 | && udata->reg_or_offset[udata->retaddr_column] == i) | |
3807 | continue; | |
2f3ca9e7 | 3808 | copy_reg (i, udata, my_udata); |
ca55abae JM |
3809 | } |
3810 | ||
6020d360 | 3811 | pc = get_return_addr (udata, sub_udata) - 1; |
ca55abae JM |
3812 | } |
3813 | ||
ca55abae JM |
3814 | /* But we do need to update the saved return address register from |
3815 | the last frame we unwind, or the handler frame will have the wrong | |
3816 | return address. */ | |
3817 | if (udata->saved[udata->retaddr_column] == REG_SAVED_REG) | |
3818 | { | |
3819 | i = udata->reg_or_offset[udata->retaddr_column]; | |
3820 | if (in_reg_window (i, udata)) | |
f3447109 | 3821 | copy_reg (i, udata, my_udata); |
ca55abae | 3822 | } |
ca55abae | 3823 | } |
e6cfb550 AM |
3824 | /* udata now refers to the frame called by the handler frame. */ |
3825 | ||
51980de6 JM |
3826 | /* We adjust SP by the difference between __throw's CFA and the CFA for |
3827 | the frame called by the handler frame, because those CFAs correspond | |
3828 | to the SP values at the two call sites. We need to further adjust by | |
3829 | the args_size of the handler frame itself to get the handler frame's | |
3830 | SP from before the args were pushed for that call. */ | |
3831 | #ifdef STACK_GROWS_DOWNWARD | |
3832 | *offset_p = udata->cfa - my_udata->cfa + args_size; | |
3833 | #else | |
3834 | *offset_p = my_udata->cfa - udata->cfa - args_size; | |
3835 | #endif | |
3836 | ||
e6cfb550 AM |
3837 | return handler; |
3838 | } | |
3839 | ||
3840 | ||
3841 | /* We first search for an exception handler, and if we don't find | |
3842 | it, we call __terminate on the current stack frame so that we may | |
3843 | use the debugger to walk the stack and understand why no handler | |
3844 | was found. | |
3845 | ||
3846 | If we find one, then we unwind the frames down to the one that | |
3847 | has the handler and transfer control into the handler. */ | |
3848 | ||
3849 | /*extern void __throw(void) __attribute__ ((__noreturn__));*/ | |
3850 | ||
3851 | void | |
3e7d8ef1 | 3852 | __throw (void) |
e6cfb550 AM |
3853 | { |
3854 | struct eh_context *eh = (*get_eh_context) (); | |
3855 | void *pc, *handler; | |
51980de6 JM |
3856 | long offset; |
3857 | ||
3858 | /* XXX maybe make my_ustruct static so we don't have to look it up for | |
3859 | each throw. */ | |
e6cfb550 AM |
3860 | frame_state my_ustruct, *my_udata = &my_ustruct; |
3861 | ||
3862 | /* This is required for C++ semantics. We must call terminate if we | |
3863 | try and rethrow an exception, when there is no exception currently | |
3864 | active. */ | |
3865 | if (! eh->info) | |
3866 | __terminate (); | |
3867 | ||
3868 | /* Start at our stack frame. */ | |
3869 | label: | |
51980de6 JM |
3870 | my_udata = __frame_state_for (&&label, my_udata); |
3871 | if (! my_udata) | |
e6cfb550 AM |
3872 | __terminate (); |
3873 | ||
3874 | /* We need to get the value from the CFA register. */ | |
51980de6 | 3875 | my_udata->cfa = __builtin_dwarf_cfa (); |
e6cfb550 AM |
3876 | |
3877 | /* Do any necessary initialization to access arbitrary stack frames. | |
3878 | On the SPARC, this means flushing the register windows. */ | |
3879 | __builtin_unwind_init (); | |
3880 | ||
3881 | /* Now reset pc to the right throw point. */ | |
3882 | pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; | |
3883 | ||
51980de6 | 3884 | handler = throw_helper (eh, pc, my_udata, &offset); |
e6cfb550 AM |
3885 | |
3886 | /* Now go! */ | |
3887 | ||
51980de6 | 3888 | __builtin_eh_return ((void *)eh, offset, handler); |
e6cfb550 AM |
3889 | |
3890 | /* Epilogue: restore the handler frame's register values and return | |
3891 | to the stub. */ | |
3892 | } | |
3893 | ||
3894 | /*extern void __rethrow(void *) __attribute__ ((__noreturn__));*/ | |
3895 | ||
3896 | void | |
3e7d8ef1 | 3897 | __rethrow (void *index) |
e6cfb550 AM |
3898 | { |
3899 | struct eh_context *eh = (*get_eh_context) (); | |
3900 | void *pc, *handler; | |
51980de6 JM |
3901 | long offset; |
3902 | ||
3903 | /* XXX maybe make my_ustruct static so we don't have to look it up for | |
3904 | each throw. */ | |
e6cfb550 AM |
3905 | frame_state my_ustruct, *my_udata = &my_ustruct; |
3906 | ||
3907 | /* This is required for C++ semantics. We must call terminate if we | |
3908 | try and rethrow an exception, when there is no exception currently | |
3909 | active. */ | |
3910 | if (! eh->info) | |
3911 | __terminate (); | |
3912 | ||
3913 | /* This is the table index we want to rethrow from. The value of | |
3914 | the END_REGION label is used for the PC of the throw, and the | |
3915 | search begins with the next table entry. */ | |
3916 | eh->table_index = index; | |
3917 | ||
3918 | /* Start at our stack frame. */ | |
3919 | label: | |
51980de6 JM |
3920 | my_udata = __frame_state_for (&&label, my_udata); |
3921 | if (! my_udata) | |
e6cfb550 AM |
3922 | __terminate (); |
3923 | ||
3924 | /* We need to get the value from the CFA register. */ | |
51980de6 | 3925 | my_udata->cfa = __builtin_dwarf_cfa (); |
e6cfb550 AM |
3926 | |
3927 | /* Do any necessary initialization to access arbitrary stack frames. | |
3928 | On the SPARC, this means flushing the register windows. */ | |
3929 | __builtin_unwind_init (); | |
3930 | ||
3931 | /* Now reset pc to the right throw point. */ | |
3932 | pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; | |
3933 | ||
51980de6 | 3934 | handler = throw_helper (eh, pc, my_udata, &offset); |
ca55abae | 3935 | |
71038426 | 3936 | /* Now go! */ |
9a0d1e1b | 3937 | |
51980de6 | 3938 | __builtin_eh_return ((void *)eh, offset, handler); |
ca55abae JM |
3939 | |
3940 | /* Epilogue: restore the handler frame's register values and return | |
3941 | to the stub. */ | |
3942 | } | |
0776059e | 3943 | #endif /* DWARF2_UNWIND_INFO */ |
ca55abae | 3944 | |
ad912eec | 3945 | #endif /* L_eh */ |
efc955c7 JM |
3946 | \f |
3947 | #ifdef L_pure | |
2c62c124 JM |
3948 | #ifndef inhibit_libc |
3949 | /* This gets us __GNU_LIBRARY__. */ | |
3950 | #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ | |
3951 | #include <stdio.h> | |
3952 | ||
3953 | #ifdef __GNU_LIBRARY__ | |
3954 | /* Avoid forcing the library's meaning of `write' on the user program | |
3955 | by using the "internal" name (for use within the library) */ | |
3956 | #define write(fd, buf, n) __write((fd), (buf), (n)) | |
3957 | #endif | |
3958 | #endif /* inhibit_libc */ | |
3959 | ||
efc955c7 | 3960 | #define MESSAGE "pure virtual method called\n" |
2c62c124 | 3961 | |
efc955c7 | 3962 | void |
3e7d8ef1 | 3963 | __pure_virtual (void) |
efc955c7 | 3964 | { |
2c62c124 | 3965 | #ifndef inhibit_libc |
efc955c7 | 3966 | write (2, MESSAGE, sizeof (MESSAGE) - 1); |
2c62c124 | 3967 | #endif |
4f2905fb | 3968 | __terminate (); |
efc955c7 JM |
3969 | } |
3970 | #endif |