#include "ppc-reg.h"

#include "ppc-frameinfo.h"
#include "ppc-tdep.h"

#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "symtab.h"
#include "target.h"
#include "gdbcore.h"
#include "symfile.h"
#include "objfiles.h"
#include "command.h"

/* external definitions (should be provided by gdb) */

extern struct obstack frame_cache_obstack;
extern struct frame_info *parse_frame_specification PARAMS ((char *frame_exp));

/* local definitions */


#define	DEFAULT_LR_SAVE 8

/* utility functions */

inline int SIGNED_SHORT (long x)
{
  if (sizeof (short) == 2) {
    return ((short) x);
  } else {
    return (((x & 0xffff) ^ 0x8000) - 0x8000);
  }
}

inline int GET_SRC_REG (long x) 
{
  return (x >> 21) & 0x1f;
}

/* function definitions */

void
ppc_print_boundaries (bounds)
     ppc_function_boundaries *bounds;
{
  if (bounds->prologue_start != INVALID_ADDRESS) {
    printf_filtered 
      (" The function prologue begins at 0x%lx.\n", bounds->prologue_start);
  }
  if (bounds->body_start != INVALID_ADDRESS) {
    printf_filtered 
      (" The function body begins at 0x%lx.\n", bounds->body_start);
  }
  if (bounds->epilogue_start != INVALID_ADDRESS) {
    printf_filtered 
      (" The function epilogue begins at 0x%lx.\n", bounds->epilogue_start);
  }
  if (bounds->function_end != INVALID_ADDRESS) {
    printf_filtered 
      (" The function ends at 0x%lx.\n", bounds->function_end);
  }
}

void
ppc_print_properties (props)
     ppc_function_properties *props;
{
  if (props->frameless) {
    printf_filtered (" No stack frame has been allocated.\n");
  } else {
    printf_filtered (" A stack frame has been allocated.\n");
  }
  if (props->frameptr_reg >= 0) {
    printf_filtered 
      (" The stack pointer has been saved by alloca() in r%d.\n",
       props->frameptr_reg);
  }
  if (props->offset < 0) {
    printf_filtered (" No registers have been saved.\n");
  } else {
    if (props->offset >= 0) {
      printf_filtered 
	(" %d bytes of integer and floating-point registers have been saved:\n",
	 props->offset);
    }
    if (props->saved_gpr >= 0) {
      printf_filtered
	(" General-purpose registers r%d--r%d have been saved at offset 0x%x.\n", 
	 props->saved_gpr, 31, props->gpr_offset);
    } else {
      printf_filtered (" No general-purpose registers have been saved.\n");
    } 
    if (props->saved_fpr >= 0) {
      printf_filtered
	(" Floating-point registers r%d--r%d have been saved at offset 0x%x.\n",
	 props->saved_fpr, 31, props->fpr_offset);
    } else {
      printf_filtered (" No floating-point registers have been saved.\n");
    } 
  }
  if (props->lr_saved) 
    {
      printf_filtered 
        (" The link register has been saved at offset 0x%x.\n", 
	 props->lr_offset);
    }
  else
    {
      if (props->lr_invalid != 0)
	printf_filtered (" The link register is still valid.\n");
      else if (props->lr_reg > -1)
	printf_filtered (" The link register is stored in r%d.\n", props->lr_reg);
      else
	printf_filtered (" I have no idea where the link register is stored.\n");
    }
  
  if (props->cr_saved) {
    printf_filtered 
      (" The condition register has been saved at offset 0x%x.\n",
       props->cr_offset);
  }
}

struct read_memory_unsigned_int_args 
{
  CORE_ADDR addr;
  int len;
  unsigned long ret_val;
};

int wrap_read_memory_unsigned_integer (void *in_args)
{
  struct read_memory_unsigned_int_args *args 
          = (struct read_memory_unsigned_int_args *) in_args;
          
  args->ret_val = read_memory_unsigned_integer (args->addr, args->len);
  return 1;
}

int
safe_read_memory_unsigned_integer (CORE_ADDR addr, int len, unsigned long *val)
{
    struct read_memory_unsigned_int_args args;
    
    args.addr = addr;
    args.len = len;
    
    if (!catch_errors ((catch_errors_ftype *) wrap_read_memory_unsigned_integer,
      &args,"", RETURN_MASK_ERROR))
      {
          return 0;
      }
    *val = args.ret_val;
    return 1;  
}

/* return pc value after skipping a function prologue and also return
   information about a function frame. */

CORE_ADDR
ppc_parse_instructions (start, end, props)
     CORE_ADDR start;
     CORE_ADDR end;
     ppc_function_properties *props;
{
  CORE_ADDR pc;
  int insn_count = 1; /* Some patterns occur in a particular order, so I 
                         keep the instruction count so I can match them
                         with more certainty. */
  int saw_pic_base_setup = 0;
  int lr_reg = -1;  /* temporary cookies that we use to tell us that
		       we have seen the lr moved into a gpr. 
		       * Set to -1 at start.  
		       * Set to the stw instruction for the register we
		       see in the mflr.
		       * Set to 0 when we see the lr get stored on the
		       stack.  */
  int cr_reg = -1;  /* Same as cr_reg but for condition reg. */
  int offset2 = 0;  /* This seems redundant to me, but I am not going
		       to bother to take it out right now.  */

  CHECK_FATAL (props != NULL);
  ppc_clear_function_properties (props);

  CHECK_FATAL (start != INVALID_ADDRESS);
  /* instructions must be word-aligned */
  CHECK_FATAL ((start % 4) == 0);   
  /* instructions must be word-aligned */
  CHECK_FATAL ((end == INVALID_ADDRESS) || (end % 4) == 0);	
  CHECK_FATAL ((end >= start) || (end == INVALID_ADDRESS));

  for (pc = start; (end == INVALID_ADDRESS) || (pc < end); 
       pc += 4, insn_count++) {
    unsigned long op;
    if (!safe_read_memory_unsigned_integer (pc, 4, &op))
      {
        ppc_debug ("ppc_parse_instructions: Got an error reading at 0x%lx",
                   pc);
        /* We got an error reading the PC, so let's get out of here... */
        return pc;
      }
      
    /* This bcl is part of the sequence:
       
       mflr r0 (optional - only if leaf function)
       bcl .+4
       mflr r31
       mtlr r0 (do this if you stored it at the top)
    */
    if ((op & 0xfe000005) == 0x42000005) /* bcl .+4 another relocatable
						 way to access global data */
      {
	props->lr_invalid = pc;
        saw_pic_base_setup = 1;
	continue;
      }
    /* Look at other branch instructions.  There are a couple of MacOS
       X Special purpose routines that are used in function prologues.
       These are:

       * saveWorld: used to be used in user code to set up info for C++ 
         exceptions, though now it is only used in throw itself.
       * saveFP: saves the FP registers AND the lr
       * saveVec: saves the AltiVec registers.
       If the bl is not one of these, we are probably not in a prologue,
       and we should get out...

     */
    else if ((op & 0xfc000003) == 0x48000001) /* bl <FN> */
      {
	/* Look up the pc you are branching to, and make
	   sure it is save_world.  The instruction in the bl
	   is bits 6-31, with the last two bits zeroed, sign extended and
	   added to the pc. */
	
	struct minimal_symbol *msymbol;
	LONGEST branch_target;
	int recognized_fn_in_prolog = 0;
	
        props->lr_invalid = pc;
	branch_target = (op & 0x03fffffc);

	if ((branch_target & 0x02000000) == 0x02000000)
	  {
	    /* Sign extend the address offset */
	    int addrsize = 26; /* The number of significant bits in the address */
	    ULONGEST valmask;
	    valmask = (((ULONGEST) 1) << addrsize) - 1;
	    branch_target &= valmask;
	    if (branch_target & (valmask ^ (valmask >> 1)))
	      {
		branch_target |= ~valmask;
	      }
	    /* branch_target |= 0xfc000000; */
	  }

	branch_target += pc + 4;
	
	msymbol = lookup_minimal_symbol_by_pc (branch_target);
	if (msymbol)
	  {
		if (strcmp (SYMBOL_SOURCE_NAME (msymbol), "save_world") == 0)
		  {
		    /* save_world currently saves all the volatile registers,
		       and saves $lr & $cr on the stack in the usual place.
		       if gcc changes, this needs to be updated as well. */
		    
		    props->frameless = 0;
		    props->frameptr_used = 0;
		    
		    props->lr_saved = pc;
		    props->lr_offset = 8;
		    lr_reg = 0;
		    
		    props->cr_saved = 1;
		    props->cr_offset = 4;
		    cr_reg = 0;
		    
		    props->saved_gpr = 13;
		    props->gpr_offset = -220;
		    
		    props->saved_fpr = 14;
		    props->fpr_offset = -144;
		    
		    recognized_fn_in_prolog = 1;
		  }
		else if (strcmp (SYMBOL_SOURCE_NAME (msymbol), "saveFP") == 0)
		  {
		    unsigned long store_insn;
		    int reg;

		    /* Decode the actual branch target to find the
		       lowest register that is stored: */
		if (!safe_read_memory_unsigned_integer (branch_target, 4, 
                                                            &store_insn))
                       {
                         ppc_debug ("ppc_parse_instructions: Got an error reading at 0x%lx",
                                    pc);
                         /* We got an error reading the PC, 
                            so let's get out of here... */
                         return pc;
                       }
                       
		    reg = GET_SRC_REG (store_insn);
		    if ((props->saved_fpr == -1) 
			|| (props->saved_fpr > reg)) {
		      props->saved_fpr = reg;
		      props->fpr_offset = SIGNED_SHORT (store_insn) 
			+ offset2;
		    }
		    /* The LR also gets saved in saveFP... */
		    props->lr_saved = pc;
		    props->lr_offset = 8;
		    lr_reg = 0;
		    
		    recognized_fn_in_prolog = 1;
		  }
		else if (strcmp (SYMBOL_SOURCE_NAME (msymbol), "saveVec") == 0)
		  {
		    /* FIXME: We can't currently get the AltiVec
		       registers, but we need to save them away for
		       when we can. */

		    recognized_fn_in_prolog = 1;
		  }
	      }
	/* If we didn't recognize this function, we are probably not
	   in the prologue, so let's get out of here... */
	if (!recognized_fn_in_prolog)
	  break;
	else
	  continue;
      }
    /* mflr is used BOTH to store away the real link register,
       and afterwards in
       bcl .+4
       mflr r31
       to set the pic base for globals.  For now, we only care
       about the link register, and not the pic base business.
       The link register store will ALWAYS be to r0, so ignore
       it if it is another register.
    */
    else if ((op & 0xfc1fffff) == 0x7c0802a6) /* mflr Rx */
      {
	int target_reg = (op & 0x03e00000) >> 21;
	if (saw_pic_base_setup)
	  {
	    props->pic_base_reg = target_reg;
	  }
	else
	  {
	    props->lr_reg = target_reg;
	    lr_reg = (op & 0x03e00000) | 0x90010000;
	  }

	continue;
      } 
    else if ((op & 0xfc1fffff) == 0x7c0803a6) /* mtlr Rx */
      {
        /* We see this case when we have moved the lr aside to
           set up the pic base, but have not saved it on the 
           stack because we are a leaf function.  We are now
           moving it back to the lr, so the lr is again valid */

        int target_reg = (op & 0x03e00000) >> 21;
        if (!props->lr_saved 
            && (props->lr_reg > -1))
          {
            props->lr_valid_again = pc;
          }
	continue;
      }
    else if ((op & 0xfc1fffff) == 0x7c000026)  /* mfcr Rx */
      {
	/* Ditto for the cr move, we always use r0 for temp store
	   in the prologue.
	*/
	
	int target_reg = (op & 0x03e000000) >> 21;
	if (target_reg == 0)
	  {
	    cr_reg = (op & 0x03e00000) | 0x90010000;
	  }
	continue;
      } 
    else if ((op & 0xfc1f0000) == 0xd8010000)  /* stfd Rx,NUM(r1) */
      {
	int reg = GET_SRC_REG (op);
	if ((props->saved_fpr == -1) || (props->saved_fpr > reg)) {
	  props->saved_fpr = reg;
	  props->fpr_offset = SIGNED_SHORT (op) + offset2;
	}
	continue;
      } 
    else if (((op & 0xfc1f0000) == 0xbc010000)
	       /* stm Rx, NUM(r1) */
	       || ((op & 0xfc1f0000) == 0x90010000 
		   /* st rx,NUM(r1), rx >= r13 */
		   && (op & 0x03e00000) >= 0x01a00000)) 
      {
	int reg = GET_SRC_REG (op);
	if ((props->saved_gpr == -1) || (props->saved_gpr > reg)) {
	  props->saved_gpr = reg;
	  props->gpr_offset = SIGNED_SHORT (op) + offset2;
	}
	continue;
      }

    /* If we saw a mflr, then we set lr_reg to the stw insn that would 
       match the register mflr moved the lr to.  Otherwise it is 0 or
       -1 so it won't match this... */
    else if ((op & 0xffff0000) == lr_reg) 
      { 
	/* st Rx,NUM(r1) where Rx == lr */
	props->lr_saved = pc;
	props->lr_offset = SIGNED_SHORT (op) + offset2;
	lr_reg = 0;
	continue;
	
      }
    else if ((op & 0xffff0000) == cr_reg) 
      { 
	/* st Rx,NUM(r1) where Rx == cr */
	props->cr_saved = 1;
	props->cr_offset = SIGNED_SHORT (op) + offset2;
	cr_reg = 0;
	continue;
	
      } 
    /* This is moving the stack pointer to the top of the new stack
       when the frame is < 32K */
    else if ((op & 0xffff0000) == 0x94210000) 
      { /* stu r1,NUM(r1) */
	props->frameless = 0;
	props->offset = SIGNED_SHORT (op);
	offset2 = props->offset;
	continue;
	
      } 
    /* The next three instructions are used to set up the stack when
       the frame is >= 32K */
    else if ((op & 0xffff0000) == 0x3c000000) 
      { 
	/* addis 0,0,NUM, (i.e. lis r0, NUM) used for >= 32k frames */
	props->offset = (op & 0x0000ffff) << 16;
	props->frameless = 0;
	continue;
	
      } 
    else if ((op & 0xffff0000) == 0x60000000) 
      { 
	/* ori 0,0,NUM, 2nd half of >= 32k frames */
	props->offset |= (op & 0x0000ffff);
	props->frameless = 0;
	continue;
	
      } 
    else if (op == 0x7c21016e) 
      { /* stwux 1,1,0 */
	props->frameless = 0;
	offset2 = props->offset;
	continue;
      }
    /* Gcc on MacOS X uses r30 for the frame pointer */
    else if (op == 0x7c3e0b78)  /* mr r30, r1 */
      {
	props->frameptr_used = 1;
	props->frameptr_reg = 30;
	continue;
	
      } 
    /* store parameters in stack via frame pointer - Using r31 */
    else if ((props->frameptr_used && props->frameptr_reg == 30) &&
	       ((op & 0xfc1f0000) == 0x901e0000 || /* st rx,NUM(r1) */
		(op & 0xfc1f0000) == 0xd81e0000 || /* stfd Rx,NUM(r1) */
		(op & 0xfc1f0000) == 0xfc1e0000))  /* frsp, fp?,NUM(r1) */
	{
	  continue;

	} 
    /* These ones are not used in Apple's gcc for function prologues
       so far as I can tell.  I am keeping them around since we support
       other compilers, but this way we can tell which bits we need
       to change as we change the ABI that our gcc uses... */

    else if (op == 0x48000005) /* bl .+4 used in -mrelocatable */
      { 
	continue;
      } 
    else if (op == 0x48000004) /* b .+4 (xlc) */
      {
	break;
      } 
    /* load up minimal toc pointer */
    else if ((op >> 22) == 0x20f
	       && ! props->minimal_toc_loaded) /* l r31,... or l r30,... */
      { 
	props->minimal_toc_loaded = 1;
	continue;
	
      } 
    /* store parameters in stack */
    else if ((op & 0xfc1f0000) == 0x90010000 || /* st rx,NUM(r1) */
	       (op & 0xfc1f0000) == 0xd8010000 || /* stfd Rx,NUM(r1) */
	       (op & 0xfc1f0000) == 0xfc010000) /* frsp, fp?,NUM(r1) */
      { 
	continue;
	
      }
    /* store parameters in stack via frame pointer - Using r31 */
    else if ((props->frameptr_used && props->frameptr_reg == 31) &&
	       ((op & 0xfc1f0000) == 0x901f0000 || /* st rx,NUM(r1) */
		(op & 0xfc1f0000) == 0xd81f0000 || /* stfd Rx,NUM(r1) */
		(op & 0xfc1f0000) == 0xfc1f0000))  /* frsp, fp?,NUM(r1) */
	{
	  continue;

	} 
    /* update stack pointer */
    else if (((op & 0xffff0000) == 0x801e0000
		/* lwz 0,NUM(r30), used in V.4 -mrelocatable */
		|| op == 0x7fc0f214)
	       /* add r30,r0,r30, used in V.4 -mrelocatable */
	       && lr_reg == 0x901e0000) 
      {
	continue;
      } 
    else if ((op & 0xffff0000) == 0x3fc00000 
	       /* addis 30,0,foo@ha, used in V.4 -mminimal-toc */
	       || (op & 0xffff0000) == 0x3bde0000)  /* addi 30,30,foo@l */
      {
	continue;
	
      } 
    /* Set up frame pointer */
    else if (op == 0x603f0000	/* oril r31, r1, 0x0 */
	       || op == 0x7c3f0b78)  /* mr r31, r1 */
      {
	props->frameptr_used = 1;
	props->frameptr_reg = 31;
	continue;
	
      } 
    /* Set up frame pointer (this time check for r30)
       Note - I moved the "mr r30, r1" check up to the Apple uses
       these section, just to keep this thing more managable...  */
    else if (op == 0x603e0000)	/* oril r30, r1, 0x0 */
      {
	props->frameptr_used = 1;
	props->frameptr_reg = 30;
	continue;
	
      } 
    /* Another way to set up the frame pointer.  */
    else if ((op & 0xfc1fffff) == 0x38010000) /* addi rX, r1, 0x0 */
      {
	props->frameptr_used = 1;
	props->frameptr_reg = (op & ~0x38010000) >> 21;
	continue;
	
      } 
    /* unknown instruction */
    else 
      {
	break;
      }
  }

  if (props->offset != -1) { props->offset = -props->offset; }
  
  return pc;
}

void
ppc_clear_function_boundaries_request (request)
     ppc_function_boundaries_request *request;
{
  request->min_start = INVALID_ADDRESS;
  request->max_end = INVALID_ADDRESS;

  request->contains_pc = INVALID_ADDRESS;

  request->prologue_start = INVALID_ADDRESS;
  request->body_start = INVALID_ADDRESS;
  request->epilogue_start = INVALID_ADDRESS;
  request->function_end = INVALID_ADDRESS;
}

void
ppc_clear_function_boundaries (boundaries)
     ppc_function_boundaries *boundaries;
{
  boundaries->prologue_start = INVALID_ADDRESS;
  boundaries->body_start = INVALID_ADDRESS;
  boundaries->epilogue_start = INVALID_ADDRESS;
  boundaries->function_end = INVALID_ADDRESS;
}

void
ppc_clear_function_properties (properties)
     ppc_function_properties *properties;
{
  properties->offset = -1;

  properties->saved_gpr = -1;
  properties->saved_fpr = -1;
  properties->gpr_offset = 0;
  properties->fpr_offset = 0;

  properties->frameptr_reg = -1;
  properties->frameptr_used = 0;

  properties->frameless = 1;

  properties->lr_saved = 0;
  properties->lr_offset = -1;
  properties->lr_reg    = -1;
  properties->lr_invalid  = 0;
  properties->lr_valid_again = INVALID_ADDRESS;

  properties->cr_saved = 0;
  properties->cr_offset = -1;

  properties->minimal_toc_loaded = 0;
  properties->pic_base_reg = 0;
}


int
ppc_find_function_boundaries (request, reply)
     ppc_function_boundaries_request *request;
     ppc_function_boundaries *reply;
{
  ppc_function_properties props;

  CHECK_FATAL (request != NULL);
  CHECK_FATAL (reply != NULL);

  if (request->prologue_start != INVALID_ADDRESS) {
    reply->prologue_start = request->prologue_start;
  } else if (request->contains_pc != INVALID_ADDRESS) {
    reply->prologue_start = get_pc_function_start (request->contains_pc);
    if (reply->prologue_start == 0) { return -1; }
  }

  CHECK_FATAL (reply->prologue_start != INVALID_ADDRESS);

  if ((reply->prologue_start % 4) != 0) { return -1; }
  reply->body_start = ppc_parse_instructions (reply->prologue_start, 
					      INVALID_ADDRESS, &props);
  return 0;
}

int
ppc_frame_cache_boundaries (frame, retbounds)
     struct frame_info *frame;
     struct ppc_function_boundaries *retbounds;
{
  if (!frame->extra_info->bounds) {

    if (ppc_is_dummy_frame (frame)) {

      frame->extra_info->bounds = (struct ppc_function_boundaries *)
	obstack_alloc (&frame_cache_obstack, sizeof (ppc_function_boundaries));
      CHECK_FATAL (frame->extra_info->bounds != NULL);
      
      frame->extra_info->bounds->prologue_start = INVALID_ADDRESS;
      frame->extra_info->bounds->body_start = INVALID_ADDRESS;
      frame->extra_info->bounds->epilogue_start = INVALID_ADDRESS;
      frame->extra_info->bounds->function_end = INVALID_ADDRESS;

    } else {

      ppc_function_boundaries_request request;
      ppc_function_boundaries lbounds;
      int ret;
  
      ppc_clear_function_boundaries (&lbounds);
      ppc_clear_function_boundaries_request (&request);

      request.contains_pc = frame->pc;
      ret = ppc_find_function_boundaries (&request, &lbounds);
      if (ret != 0) { return ret; }
    
      frame->extra_info->bounds = (struct ppc_function_boundaries *)
	obstack_alloc (&frame_cache_obstack, sizeof (ppc_function_boundaries));
      CHECK_FATAL (frame->extra_info->bounds != NULL);
      memcpy (frame->extra_info->bounds, &lbounds, sizeof (ppc_function_boundaries));
    }
  }

  if (retbounds != NULL) { 
    memcpy (retbounds, frame->extra_info->bounds, sizeof (ppc_function_boundaries));
  }

  return 0;
}

/* ppc_frame_cache_properties - 
 * This function creates the ppc_function_properties
 * bit of the extra_info structure of FRAME, if it is not already
 * present.  If RETPROPS is not NULL, it copies the properties
 * to retprops (provided by caller).
 * 
 * Returns: 0 on success */

int
ppc_frame_cache_properties (frame, retprops)
     struct frame_info *frame;
     struct ppc_function_properties *retprops;
{
  /* FIXME: I have seen a couple of cases where this function gets
     called with a frame whose extra_info has not been set yet.  It is
     always when the stack is mauled, and you try to run "up" or some
     other such command, so I am not sure we can really recover well,
     but at some point it might be good to look at this more
     closely. */

  if (!frame->extra_info)
    {
      return 1;
    }

  if (! frame->extra_info->props) 
    {
      
      if (ppc_is_dummy_frame (frame)) 
	{
	  
	  ppc_function_properties *props;
	  
	  frame->extra_info->props = (struct ppc_function_properties *)
	    obstack_alloc (&frame_cache_obstack, sizeof (ppc_function_properties));
	  CHECK_FATAL (frame->extra_info->props != NULL);
	  ppc_clear_function_properties (frame->extra_info->props);
	  props = frame->extra_info->props;
	  
	  props->offset = 0;
	  props->saved_gpr = -1;
	  props->saved_fpr = -1;
	  props->gpr_offset = -1;
	  props->fpr_offset = -1;
	  props->frameless = 0;
	  props->frameptr_used = 0;
	  props->frameptr_reg = -1;
	  props->lr_saved = 1;
	  props->lr_offset = DEFAULT_LR_SAVE;
	  props->cr_saved = 0;
	  props->cr_offset = -1;
	  props->minimal_toc_loaded = 0;
	  
	} 
      else 
	{
	  
	  int ret;
	  ppc_function_properties lprops;
	  
	  ppc_clear_function_properties (&lprops);
	  
	  ret = ppc_frame_cache_boundaries (frame, NULL);
	  if (ret != 0) { return ret; }
	  
	  if ((frame->extra_info->bounds->prologue_start % 4) != 0) { return -1; }
	  if ((frame->pc % 4) != 0) { return -1; }
	  ppc_parse_instructions (frame->extra_info->bounds->prologue_start, 
				  frame->extra_info->bounds->function_end, &lprops);
	  frame->extra_info->props = (struct ppc_function_properties *)
	    obstack_alloc (&frame_cache_obstack, sizeof (ppc_function_properties));
	  CHECK_FATAL (frame->extra_info->props != NULL);
	  
	  /* ppc_parse_instructions sometimes gets the stored pc location
	     wrong.  However, we KNOW that for all frames but frame 0
	     the pc is on the stack.  So force that here. */
	  
	  if (frame->next != NULL && !lprops.lr_saved)
	    {
	      lprops.lr_offset = 8;
	      /* This is bogus, just needs to be BEFORE where we are
		 to get the rest of the code right. */
	      lprops.lr_saved = frame->pc - 8;
	      lprops.lr_reg = LR_REGNUM;
	      lprops.lr_invalid = 0;
	      ppc_debug ("ppc_frame_cache_properties: %s\n",
			 "thought a non-leaf frame didn't save the lr, correcting...");
	    }
	  
	  memcpy (frame->extra_info->props, &lprops,
		  sizeof (ppc_function_properties));
	}
    } 
  else if (frame->next != NULL && !frame->extra_info->props->lr_saved)
    {
      /* We tried to correct this error from ppc_parse_instructions above,
	 but the frame hadn't been fully set yet.  So we will do it again
	 here... */
      ppc_function_properties *props = frame->extra_info->props;
      
      props->lr_offset = 8;
      /* This is bogus, just needs to be BEFORE where we are
	 to get the rest of the code right. */
      props->lr_saved = frame->pc - 8;
      props->lr_reg = LR_REGNUM;
      props->lr_invalid = 0;
      ppc_debug ("ppc_frame_cache_properties: %s\n",
		 "thought a non-leaf frame didn't save the lr, correcting...");
      
    }

  if (retprops != NULL) {
    memcpy (retprops, frame->extra_info->props, sizeof (ppc_function_properties));
  }
  
  return 0;    
}
