Gregs.c revision fa2871946af3c3d696efacbbc529d834020d8b67
1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2001-2005 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26#include "offsets.h"
27#include "regs.h"
28#include "unwind_i.h"
29
30static inline ia64_loc_t
31linux_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
32{
33#if !defined(UNW_LOCAL_ONLY) || defined(__linux)
34  unw_word_t addr = c->sigcontext_addr, flags, tmp_addr;
35  int i;
36
37  if (ia64_get_abi_marker (c) == ABI_MARKER_LINUX_SIGTRAMP
38      || ia64_get_abi_marker (c) == ABI_MARKER_OLD_LINUX_SIGTRAMP)
39    {
40      switch (reg)
41	{
42	case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
43	case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
44	  /* Linux sigcontext contains the NaT bit of scratch register
45	     N in bit position N of the sc_nat member. */
46	  *nat_bitnr = (reg - UNW_IA64_NAT);
47	  addr += LINUX_SC_NAT_OFF;
48	  break;
49
50	case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
51	case UNW_IA64_GR +  8 ... UNW_IA64_GR + 31:
52	  addr += LINUX_SC_GR_OFF + 8 * (reg - UNW_IA64_GR);
53	  break;
54
55	case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
56	  addr += LINUX_SC_FR_OFF + 16 * (reg - UNW_IA64_FR);
57	  return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
58
59	case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
60	  if (ia64_get (c, IA64_LOC_ADDR (addr + LINUX_SC_FLAGS_OFF, 0),
61			&flags) < 0)
62	    return IA64_NULL_LOC;
63
64	  if (!(flags & IA64_SC_FLAG_FPH_VALID))
65	    {
66	      /* initialize fph partition: */
67	      tmp_addr = addr + LINUX_SC_FR_OFF + 32*16;
68	      for (i = 32; i < 128; ++i, tmp_addr += 16)
69		if (ia64_putfp (c, IA64_LOC_ADDR (tmp_addr, 0),
70				unw.read_only.f0) < 0)
71		  return IA64_NULL_LOC;
72	      /* mark fph partition as valid: */
73	      if (ia64_put (c, IA64_LOC_ADDR (addr + LINUX_SC_FLAGS_OFF, 0),
74			    flags | IA64_SC_FLAG_FPH_VALID) < 0)
75		return IA64_NULL_LOC;
76	    }
77
78	  addr += LINUX_SC_FR_OFF + 16 * (reg - UNW_IA64_FR);
79	  return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
80
81	case UNW_IA64_BR + 0: addr += LINUX_SC_BR_OFF + 0; break;
82	case UNW_IA64_BR + 6: addr += LINUX_SC_BR_OFF + 6*8; break;
83	case UNW_IA64_BR + 7: addr += LINUX_SC_BR_OFF + 7*8; break;
84	case UNW_IA64_AR_RSC: addr += LINUX_SC_AR_RSC_OFF; break;
85	case UNW_IA64_AR_CSD: addr += LINUX_SC_AR_CSD_OFF; break;
86	case UNW_IA64_AR_SSD: addr += LINUX_SC_AR_SSD_OFF; break;
87	case UNW_IA64_AR_CCV: addr += LINUX_SC_AR_CCV; break;
88
89	default:
90	  if (unw_is_fpreg (reg))
91	    return IA64_FPREG_LOC (c, reg);
92	  else
93	    return IA64_REG_LOC (c, reg);
94	}
95      return IA64_LOC_ADDR (addr, 0);
96    }
97  else
98    {
99      int is_nat = 0;
100
101      if ((unsigned) (reg - UNW_IA64_NAT) < 128)
102	{
103	  is_nat = 1;
104	  reg -= (UNW_IA64_NAT - UNW_IA64_GR);
105	}
106      if (ia64_get_abi_marker (c) == ABI_MARKER_LINUX_INTERRUPT)
107	{
108	  switch (reg)
109	    {
110	    case UNW_IA64_BR + 6 ... UNW_IA64_BR + 7:
111	      addr += LINUX_PT_B6_OFF + 8 * (reg - (UNW_IA64_BR + 6));
112	      break;
113
114	    case UNW_IA64_AR_CSD: addr += LINUX_PT_CSD_OFF; break;
115	    case UNW_IA64_AR_SSD: addr += LINUX_PT_SSD_OFF; break;
116
117	    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 11:
118	      addr += LINUX_PT_R8_OFF + 8 * (reg - (UNW_IA64_GR + 8));
119	      break;
120
121	    case UNW_IA64_IP: addr += LINUX_PT_IIP_OFF; break;
122	    case UNW_IA64_CFM: addr += LINUX_PT_IFS_OFF; break;
123	    case UNW_IA64_AR_UNAT: addr += LINUX_PT_UNAT_OFF; break;
124	    case UNW_IA64_AR_PFS: addr += LINUX_PT_PFS_OFF; break;
125	    case UNW_IA64_AR_RSC: addr += LINUX_PT_RSC_OFF; break;
126	    case UNW_IA64_AR_RNAT: addr += LINUX_PT_RNAT_OFF; break;
127	    case UNW_IA64_AR_BSPSTORE: addr += LINUX_PT_BSPSTORE_OFF; break;
128	    case UNW_IA64_PR: addr += LINUX_PT_PR_OFF; break;
129	    case UNW_IA64_BR + 0: addr += LINUX_PT_B0_OFF; break;
130	    case UNW_IA64_GR + 1: addr += LINUX_PT_R1_OFF; break;
131	    case UNW_IA64_GR + 12: addr += LINUX_PT_R12_OFF; break;
132	    case UNW_IA64_GR + 13: addr += LINUX_PT_R13_OFF; break;
133	    case UNW_IA64_AR_FPSR: addr += LINUX_PT_FPSR_OFF; break;
134	    case UNW_IA64_GR + 15: addr += LINUX_PT_R15_OFF; break;
135	    case UNW_IA64_GR + 14: addr += LINUX_PT_R14_OFF; break;
136	    case UNW_IA64_GR + 2: addr += LINUX_PT_R2_OFF; break;
137	    case UNW_IA64_GR + 3: addr += LINUX_PT_R3_OFF; break;
138
139	    case UNW_IA64_GR + 16 ... UNW_IA64_GR + 31:
140	      addr += LINUX_PT_R16_OFF + 8 * (reg - (UNW_IA64_GR + 16));
141	      break;
142
143	    case UNW_IA64_AR_CCV: addr += LINUX_PT_CCV_OFF; break;
144
145	    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 11:
146	      addr += LINUX_PT_F6_OFF + 16 * (reg - (UNW_IA64_FR + 6));
147	      return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
148
149	    default:
150	      if (unw_is_fpreg (reg))
151		return IA64_FPREG_LOC (c, reg);
152	      else
153		return IA64_REG_LOC (c, reg);
154	    }
155	}
156      else if (ia64_get_abi_marker (c) == ABI_MARKER_OLD_LINUX_INTERRUPT)
157	{
158	  switch (reg)
159	    {
160	    case UNW_IA64_GR +  1 ... UNW_IA64_GR + 3:
161	      addr += LINUX_OLD_PT_R1_OFF + 8 * (reg - (UNW_IA64_GR + 1));
162	      break;
163
164	    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 11:
165	      addr += LINUX_OLD_PT_R8_OFF + 8 * (reg - (UNW_IA64_GR + 8));
166	      break;
167
168	    case UNW_IA64_GR + 16 ... UNW_IA64_GR + 31:
169	      addr += LINUX_OLD_PT_R16_OFF + 8 * (reg - (UNW_IA64_GR + 16));
170	      break;
171
172	    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 9:
173	      addr += LINUX_OLD_PT_F6_OFF + 16 * (reg - (UNW_IA64_FR + 6));
174	      return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
175
176	    case UNW_IA64_BR + 0: addr += LINUX_OLD_PT_B0_OFF; break;
177	    case UNW_IA64_BR + 6: addr += LINUX_OLD_PT_B6_OFF; break;
178	    case UNW_IA64_BR + 7: addr += LINUX_OLD_PT_B7_OFF; break;
179
180	    case UNW_IA64_AR_RSC: addr += LINUX_OLD_PT_RSC_OFF; break;
181	    case UNW_IA64_AR_CCV: addr += LINUX_OLD_PT_CCV_OFF; break;
182
183	    default:
184	      if (unw_is_fpreg (reg))
185		return IA64_FPREG_LOC (c, reg);
186	      else
187		return IA64_REG_LOC (c, reg);
188	    }
189	}
190      if (is_nat)
191	{
192	  /* For Linux pt-regs structure, bit number is determined by
193	     the UNaT slot number (as determined by st8.spill) and the
194	     bits are saved wherever the (primary) UNaT was saved.  */
195	  *nat_bitnr = ia64_unat_slot_num (addr);
196	  return c->loc[IA64_REG_PRI_UNAT_MEM];
197	}
198      return IA64_LOC_ADDR (addr, 0);
199    }
200#endif
201  return IA64_NULL_LOC;
202}
203
204static inline ia64_loc_t
205hpux_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
206{
207#if !defined(UNW_LOCAL_ONLY) || defined(__hpux)
208  return IA64_LOC_UC_REG (reg, c->sigcontext_addr);
209#else
210  return IA64_NULL_LOC;
211#endif
212}
213
214HIDDEN ia64_loc_t
215ia64_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
216{
217  if (c->sigcontext_addr)
218    {
219      if (ia64_get_abi (c) == ABI_LINUX)
220	return linux_scratch_loc (c, reg, nat_bitnr);
221      else if (ia64_get_abi (c) ==  ABI_HPUX)
222	return hpux_scratch_loc (c, reg, nat_bitnr);
223      else
224	return IA64_NULL_LOC;
225    }
226  else
227    return IA64_REG_LOC (c, reg);
228}
229
230static inline int
231update_nat (struct cursor *c, ia64_loc_t nat_loc, unw_word_t mask,
232	    unw_word_t *valp, int write)
233{
234  unw_word_t nat_word;
235  int ret;
236
237  ret = ia64_get (c, nat_loc, &nat_word);
238  if (ret < 0)
239    return ret;
240
241  if (write)
242    {
243      if (*valp)
244	nat_word |= mask;
245      else
246	nat_word &= ~mask;
247      ret = ia64_put (c, nat_loc, nat_word);
248    }
249  else
250    *valp = (nat_word & mask) != 0;
251  return ret;
252}
253
254static int
255access_nat (struct cursor *c,
256	    ia64_loc_t nat_loc, ia64_loc_t reg_loc, uint8_t nat_bitnr,
257	    unw_word_t *valp, int write)
258{
259  unw_word_t mask = 0;
260  unw_fpreg_t tmp;
261  int ret;
262
263  if (IA64_IS_FP_LOC (reg_loc))
264    {
265      /* NaT bit is saved as a NaTVal.  This happens when a general
266	 register is saved to a floating-point register.  */
267      if (write)
268	{
269	  if (*valp)
270	    {
271	      if (ia64_is_big_endian (c))
272		ret = ia64_putfp (c, reg_loc, unw.nat_val_be);
273	      else
274		ret = ia64_putfp (c, reg_loc, unw.nat_val_le);
275	    }
276	  else
277	    {
278	      unw_word_t *src, *dst;
279	      unw_fpreg_t tmp;
280
281	      ret = ia64_getfp (c, reg_loc, &tmp);
282	      if (ret < 0)
283		return ret;
284
285	      /* Reset the exponent to 0x1003e so that the significand
286		 will be interpreted as an integer value.  */
287	      src = (unw_word_t *) &unw.int_val_be;
288	      dst = (unw_word_t *) &tmp;
289	      if (!ia64_is_big_endian (c))
290		++src, ++dst;
291	      *dst = *src;
292
293	      ret = ia64_putfp (c, reg_loc, tmp);
294	    }
295	}
296      else
297	{
298	  ret = ia64_getfp (c, reg_loc, &tmp);
299	  if (ret < 0)
300	    return ret;
301
302	  if (ia64_is_big_endian (c))
303	    *valp = (memcmp (&tmp, &unw.nat_val_be, sizeof (tmp)) == 0);
304	  else
305	    *valp = (memcmp (&tmp, &unw.nat_val_le, sizeof (tmp)) == 0);
306	}
307      return ret;
308    }
309
310  if ((IA64_IS_REG_LOC (nat_loc)
311       && (unsigned) (IA64_GET_REG (nat_loc) - UNW_IA64_NAT) < 128)
312      || IA64_IS_UC_LOC (reg_loc))
313    {
314      if (write)
315	return ia64_put (c, nat_loc, *valp);
316      else
317	return ia64_get (c, nat_loc, valp);
318    }
319
320  if (IA64_IS_NULL_LOC (nat_loc))
321    {
322      /* NaT bit is not saved. This happens if a general register is
323	 saved to a branch register.  Since the NaT bit gets lost, we
324	 need to drop it here, too.  Note that if the NaT bit had been
325	 set when the save occurred, it would have caused a NaT
326	 consumption fault.  */
327      if (write)
328	{
329	  if (*valp)
330	    return -UNW_EBADREG;	/* can't set NaT bit */
331	}
332      else
333	*valp = 0;
334      return 0;
335    }
336
337  mask = (unw_word_t) 1 << nat_bitnr;
338  return update_nat (c, nat_loc, mask, valp, write);
339}
340
341HIDDEN int
342tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
343		 int write)
344{
345  ia64_loc_t loc, reg_loc, nat_loc;
346  unw_word_t nat, mask, val;
347  int ret, readonly = 0;
348  uint8_t nat_bitnr;
349
350  switch (reg)
351    {
352      /* frame registers: */
353
354    case UNW_IA64_BSP:
355      if (write)
356	return -UNW_EREADONLYREG;
357      *valp = c->bsp;
358      return 0;
359
360    case UNW_REG_SP:
361      if (write)
362	return -UNW_EREADONLYREG;
363      *valp = c->sp;
364      return 0;
365
366    case UNW_REG_IP:
367      if (write)
368	{
369	  c->ip = *valp;	/* also update the IP cache */
370	  if (c->pi_valid && (*valp < c->pi.start_ip || *valp >= c->pi.end_ip))
371	    c->pi_valid = 0;	/* new IP outside of current proc */
372	}
373      loc = c->loc[IA64_REG_IP];
374      break;
375
376      /* preserved registers: */
377
378    case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7:
379      loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_GR + 4))];
380      break;
381
382    case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7:
383      loc = c->loc[IA64_REG_NAT4 + (reg - (UNW_IA64_NAT + 4))];
384      reg_loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_NAT + 4))];
385      nat_bitnr = c->nat_bitnr[reg - (UNW_IA64_NAT + 4)];
386      return access_nat (c, loc, reg_loc, nat_bitnr, valp, write);
387
388    case UNW_IA64_AR_BSP:	loc = c->loc[IA64_REG_BSP]; break;
389    case UNW_IA64_AR_BSPSTORE:	loc = c->loc[IA64_REG_BSPSTORE]; break;
390    case UNW_IA64_AR_PFS:	loc = c->loc[IA64_REG_PFS]; break;
391    case UNW_IA64_AR_RNAT:	loc = c->loc[IA64_REG_RNAT]; break;
392    case UNW_IA64_AR_UNAT:	loc = c->loc[IA64_REG_UNAT]; break;
393    case UNW_IA64_AR_LC:	loc = c->loc[IA64_REG_LC]; break;
394    case UNW_IA64_AR_FPSR:	loc = c->loc[IA64_REG_FPSR]; break;
395    case UNW_IA64_BR + 1:	loc = c->loc[IA64_REG_B1]; break;
396    case UNW_IA64_BR + 2:	loc = c->loc[IA64_REG_B2]; break;
397    case UNW_IA64_BR + 3:	loc = c->loc[IA64_REG_B3]; break;
398    case UNW_IA64_BR + 4:	loc = c->loc[IA64_REG_B4]; break;
399    case UNW_IA64_BR + 5:	loc = c->loc[IA64_REG_B5]; break;
400
401    case UNW_IA64_CFM:
402      if (write)
403	c->cfm = *valp;	/* also update the CFM cache */
404      loc = c->cfm_loc;
405      break;
406
407    case UNW_IA64_PR:
408      /*
409       * Note: broad-side access to the predicates is NOT rotated
410       * (i.e., it is done as if CFM.rrb.pr == 0.
411       */
412      if (write)
413	{
414	  c->pr = *valp;		/* update the predicate cache */
415	  return ia64_put (c, c->loc[IA64_REG_PR], *valp);
416	}
417      else
418	return ia64_get (c, c->loc[IA64_REG_PR], valp);
419
420    case UNW_IA64_GR + 32 ... UNW_IA64_GR + 127:	/* stacked reg */
421      reg = rotate_gr (c, reg - UNW_IA64_GR);
422      if (reg < 0)
423	return -UNW_EBADREG;
424      ret = ia64_get_stacked (c, reg, &loc, NULL);
425      if (ret < 0)
426	return ret;
427      break;
428
429    case UNW_IA64_NAT + 32 ... UNW_IA64_NAT + 127:	/* stacked reg */
430      reg = rotate_gr (c, reg - UNW_IA64_NAT);
431      if (reg < 0)
432	return -UNW_EBADREG;
433      ret = ia64_get_stacked (c, reg, &loc, &nat_loc);
434      if (ret < 0)
435	return ret;
436      assert (!IA64_IS_REG_LOC (loc));
437      mask = (unw_word_t) 1 << rse_slot_num (IA64_GET_ADDR (loc));
438      return update_nat (c, nat_loc, mask, valp, write);
439
440    case UNW_IA64_AR_EC:
441      if ((ret = ia64_get (c, c->ec_loc, &val)) < 0)
442	return ret;
443
444      if (write)
445	{
446	  val = ((val & ~((unw_word_t) 0x3f << 52)) | ((*valp & 0x3f) << 52));
447	  return ia64_put (c, c->ec_loc, val);
448	}
449      else
450	{
451	  *valp = (val >> 52) & 0x3f;
452	  return 0;
453	}
454
455      /* scratch & special registers: */
456
457    case UNW_IA64_GR + 0:
458      if (write)
459	return -UNW_EREADONLYREG;
460      *valp = 0;
461      return 0;
462
463    case UNW_IA64_GR + 1:				/* global pointer */
464      if (write)
465	return -UNW_EREADONLYREG;
466
467      /* ensure c->pi is up-to-date: */
468      if ((ret = ia64_make_proc_info (c)) < 0)
469	return ret;
470      *valp = c->pi.gp;
471      return 0;
472
473    case UNW_IA64_NAT + 0:
474    case UNW_IA64_NAT + 1:				/* global pointer */
475      if (write)
476	return -UNW_EREADONLYREG;
477      *valp = 0;
478      return 0;
479
480    case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
481    case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
482      loc = ia64_scratch_loc (c, reg, &nat_bitnr);
483      if (!(IA64_IS_REG_LOC (loc) || IA64_IS_UC_LOC (loc)
484	    || IA64_IS_FP_LOC (loc)))
485	{
486	  /* We're dealing with a NaT bit stored in memory.  */
487	  mask = (unw_word_t) 1 << nat_bitnr;
488
489	  if ((ret = ia64_get (c, loc, &nat)) < 0)
490	    return ret;
491
492	  if (write)
493	    {
494	      if (*valp)
495		nat |= mask;
496	      else
497		nat &= ~mask;
498	      ret = ia64_put (c, loc, nat);
499	    }
500	  else
501	    *valp = (nat & mask) != 0;
502	  return ret;
503	}
504      break;
505
506    case UNW_IA64_GR + 15 ... UNW_IA64_GR + 18:
507      mask = 1 << (reg - (UNW_IA64_GR + 15));
508      if (write)
509	{
510	  c->eh_args[reg - (UNW_IA64_GR + 15)] = *valp;
511	  c->eh_valid_mask |= mask;
512	  return 0;
513	}
514      else if ((c->eh_valid_mask & mask) != 0)
515	{
516	  *valp = c->eh_args[reg - (UNW_IA64_GR + 15)];
517	  return 0;
518	}
519      else
520	loc = ia64_scratch_loc (c, reg, NULL);
521      break;
522
523    case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
524    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 14:
525    case UNW_IA64_GR + 19 ... UNW_IA64_GR + 31:
526    case UNW_IA64_BR + 0:
527    case UNW_IA64_BR + 6:
528    case UNW_IA64_BR + 7:
529    case UNW_IA64_AR_RSC:
530    case UNW_IA64_AR_CSD:
531    case UNW_IA64_AR_SSD:
532    case UNW_IA64_AR_CCV:
533      loc = ia64_scratch_loc (c, reg, NULL);
534      break;
535
536    default:
537      Debug (1, "bad register number %d\n", reg);
538      return -UNW_EBADREG;
539    }
540
541  if (write)
542    {
543      if (readonly)
544	return -UNW_EREADONLYREG;
545      return ia64_put (c, loc, *valp);
546    }
547  else
548    return ia64_get (c, loc, valp);
549}
550
551HIDDEN int
552tdep_access_fpreg (struct cursor *c, int reg, unw_fpreg_t *valp,
553		   int write)
554{
555  ia64_loc_t loc;
556
557  switch (reg)
558    {
559    case UNW_IA64_FR + 0:
560      if (write)
561	return -UNW_EREADONLYREG;
562      *valp = unw.read_only.f0;
563      return 0;
564
565    case UNW_IA64_FR + 1:
566      if (write)
567	return -UNW_EREADONLYREG;
568
569      if (ia64_is_big_endian (c))
570	*valp = unw.read_only.f1_be;
571      else
572	*valp = unw.read_only.f1_le;
573      return 0;
574
575    case UNW_IA64_FR + 2: loc = c->loc[IA64_REG_F2]; break;
576    case UNW_IA64_FR + 3: loc = c->loc[IA64_REG_F3]; break;
577    case UNW_IA64_FR + 4: loc = c->loc[IA64_REG_F4]; break;
578    case UNW_IA64_FR + 5: loc = c->loc[IA64_REG_F5]; break;
579
580    case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31:
581      loc = c->loc[IA64_REG_F16 + (reg - (UNW_IA64_FR + 16))];
582      break;
583
584    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
585      loc = ia64_scratch_loc (c, reg, NULL);
586      break;
587
588    case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
589      reg = rotate_fr (c, reg - UNW_IA64_FR) + UNW_IA64_FR;
590      loc = ia64_scratch_loc (c, reg, NULL);
591      break;
592
593    default:
594      Debug (1, "bad register number %d\n", reg);
595      return -UNW_EBADREG;
596    }
597
598  if (write)
599    return ia64_putfp (c, loc, *valp);
600  else
601    return ia64_getfp (c, loc, valp);
602}
603