/* apple.c:  Functions for Mac OS X as target machine for GNU C compiler.  */

/* Note that the include below means that we can't debug routines in
   i386.c when running on a COFF system.  */

#define MAX_386_STACK_LOCALS 3

#include "i386/i386.c"
#include "apple/openstep.c"

#ifndef FIXED_PIC_REG
int pic86_reg_num = 0;
#endif
 
static rtx
lookup_i386_stack_local (mode, n)
  enum machine_mode mode;
  int n;
{
    if (n < 0 || n >= MAX_386_STACK_LOCALS)
      abort ();

    return  i386_stack_locals[(int) mode][n];
}

void
machopic_output_stub (file, symb, stub)
     FILE *file;
     const char *symb, *stub;
{
  extern int current_machopic_label_num;	/* config/apple/machopic.c  */
  unsigned int length;
  char *binder_name, *symbol_name, lazy_ptr_name[32];
  int label = ++current_machopic_label_num;

  length = strlen(stub);
  binder_name = alloca(length + 32);
  GEN_BINDER_NAME_FOR_STUB(binder_name, stub, length);

  length = strlen(symb);
  symbol_name = alloca(length + 32);
  GEN_SYMBOL_NAME_FOR_SYMBOL(symbol_name, symb, length);

  sprintf (lazy_ptr_name, "L%d$lz", label);

  if (MACHOPIC_PURE)
    machopic_picsymbol_stub_section ();
  else
    machopic_symbol_stub_section ();

  fprintf (file, "%s:\n", stub);
  fprintf (file, "\t.indirect_symbol %s\n", symbol_name);

  if (MACHOPIC_PURE)
    {
      fprintf (file, "\tcall LPC$%d\nLPC$%d:\tpopl %%eax\n", label, label);
      fprintf (file, "\tmovl %s-LPC$%d(%%eax),%%edx\n", lazy_ptr_name, label);
      fprintf (file, "\tjmp %%edx\n");
    }
  else
    {
      fprintf (file, "\tjmp *%s\n", lazy_ptr_name);
    }
  
  fprintf (file, "%s:\n", binder_name);
  
  if (MACHOPIC_PURE)
    {
      fprintf (file, "\tlea %s-LPC$%d(%%eax),%%eax\n", lazy_ptr_name, label);
      fprintf (file, "\tpushl %%eax\n");
    }
  else
    {
      fprintf (file, "\t pushl $%s\n", lazy_ptr_name);
    }

  fprintf (file, "\tjmp dyld_stub_binding_helper\n");

  machopic_lazy_symbol_ptr_section ();
  fprintf (file, "%s:\n", lazy_ptr_name);
  fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
  fprintf (file, "\t.long %s\n", binder_name);
}

i386_finalize_machopic ()
{
  extern int current_function_uses_pic_offset_table;
#ifndef FIXED_PIC_REG
    const int pic_reg = pic86_reg_num;
  rtx first_insn = next_real_insn (get_insns ());

    if (pic_reg >= FIRST_PSEUDO_REGISTER && regno_reg_rtx [pic_reg])
      pic_offset_table_rtx = regno_reg_rtx [pic_reg];
#endif

  if (!current_function_uses_pic_offset_table)
#ifndef FIXED_PIC_REG
    {
#endif
      current_function_uses_pic_offset_table =
	profile_flag || profile_block_flag || get_pool_size()
	  || current_function_uses_const_pool || const_double_used ();

      if (current_function_uses_pic_offset_table)
#ifndef FIXED_PIC_REG
	{
	  if (!first_insn)
	    return;
	  emit_insn_before (gen_rtx (SET, VOIDmode, pic_offset_table_rtx,
				     gen_rtx (PC, VOIDmode)),
			    first_insn);
	  emit_insn_after (gen_rtx (USE, VOIDmode, pic_offset_table_rtx),
			   get_last_insn ());
	}
    }
  else
    {
	if (first_insn)
	  emit_insn_before (gen_rtx (USE, VOIDmode, pic_offset_table_rtx),
			    first_insn);
#endif
      /* This is necessary. */
	if (const_double_used ())
	  emit_insn_after (gen_rtx (USE, VOIDmode, pic_offset_table_rtx),
			   get_last_insn ());
#ifndef FIXED_PIC_REG
    }
#endif
}

#ifndef FIXED_PIC_REG
static int replace_count, inside_set_rtx;
static
rtx
replace_machopic86_base_refs (rtx x, rtx repl)
{
  register enum rtx_code code;
  register int i;
  register char *fmt;

  if (x == 0)
    return x;

  code = GET_CODE (x);
  switch (code)
    {
    case SCRATCH:
    case PC:
    case CC0:
    case CONST_INT:
    case CONST_DOUBLE:
    case CONST:
    case SYMBOL_REF:
    case LABEL_REF:
    case SUBREG:
      return x;

    case REG:
      /* Only switch registers if we're inside a "(set ...)" rtx.  */
      if (REGNO (x) == pic86_reg_num && inside_set_rtx)
      {
	++replace_count;
	return repl;
      }
      return x;

    case SET:
      ++inside_set_rtx;
      SET_SRC (x) = replace_machopic86_base_refs (SET_SRC (x), repl);
      --inside_set_rtx;
      return x;
    }

  fmt = GET_RTX_FORMAT (code);
  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
    {
      if (fmt[i] == 'e')
        XEXP (x, i) = replace_machopic86_base_refs (XEXP (x, i), repl);
      else
      if (fmt[i] == 'E')
        {
          register int j;
          for (j = 0; j < XVECLEN (x, i); j++)
            XVECEXP (x, i, j) = 
		replace_machopic86_base_refs (XVECEXP (x, i, j), repl);
        }
    }
  return x;
}


/* fixup_machopic386_pic_base_refs fixes up COUNT insns beginning with
   INSNS so that references to the pic_offset_table_rtx pseudo are
   replaced with the real thing.

   Only SET insns are affected.

   This is because the compiler can't handle reloading this pseudo
   for reasons mightily obscure, but I think caused by the fact that
   "(reg:SI 21)" appears from nowhere and has no mem or const equivalences.

   Note that passing a negative COUNT will fixup all instructions.  */

void
fixup_machopic386_pic_base_refs (rtx insns, int count)
{

  /* Replace all occurrences of "(reg:SI 21)" -- the PIC base register --
     if a register or memory location has been allocated for it.
     I am at a loss as to why the dratted compiler can't do this itself.  */

  if (1 && flag_pic && current_function_uses_pic_offset_table
     && regno_reg_rtx[pic86_reg_num] 
     && ((  GET_CODE (regno_reg_rtx[pic86_reg_num]) != REG
	   || /* it is REG, but a different register */
            REGNO (regno_reg_rtx[pic86_reg_num]) != pic86_reg_num)
        || (reg_renumber && reg_renumber[pic86_reg_num] >= 0)))
  {
    rtx replacement;
    int output_hdr = 0;

    if (reg_renumber && reg_renumber[pic86_reg_num] >= 0)
      replacement = gen_rtx_REG(SImode, reg_renumber[pic86_reg_num]);
    else
      replacement = regno_reg_rtx[pic86_reg_num];

    for (; insns != 0 && count--; insns = next_real_insn (insns))    
    {
      replace_count = 0;
      inside_set_rtx = 0;
      insns = replace_machopic86_base_refs (insns, replacement);
      if (replace_count && getenv("REPLACED_INSNS") != 0)
      {
	if (!output_hdr)
	{
	  fprintf (stderr, "## %s -- replace (reg:SI %d) with ",
			current_function_name, pic86_reg_num);
	  debug_rtx (replacement);
	  output_hdr = 1;
	}
        debug_rtx (insns);
	fprintf(stderr, "\n##\n");
      }
    }   
  }     
}
#endif	/* FIXED_PIC_REG */

/* Returns 1 if OP is a

     (const:SI (minus:SI (symbol_ref:SI ("symbol_name"))
			 (symbol_ref:SI ("name_of_pic_base_label"))

   i.e., a MACHOPIC pic-base-offset constant.

   This is only needed by a hacked pattern in i386.md, and does not
   apply if the PIC base reg is fixed.  Returning 0 here in this case
   effectively disables that pattern.  */

int
machopic_symbolic_operand (op, mode)
     register rtx op;
     enum machine_mode mode ATTRIBUTE_UNUSED;
{
#ifndef FIXED_PIC_REG
  if (GET_CODE (op) == CONST)
    {
      op = XEXP (op, 0);
      if (GET_CODE (op) == MINUS)
	   return (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
		   && GET_CODE (XEXP (op, 1)) == SYMBOL_REF);
    }
#endif
  return 0;
}
