c9ab9ae440
These bits are taken from the FSF anoncvs repo on 1-Feb-2002 08:20 PST.
1142 lines
28 KiB
C
1142 lines
28 KiB
C
/* Functions for generic Darwin as target machine for GNU C compiler.
|
|
Copyright (C) 1989, 1990, 1991, 1992, 1993, 2000, 2001
|
|
Free Software Foundation, Inc.
|
|
Contributed by Apple Computer Inc.
|
|
|
|
This file is part of GNU CC.
|
|
|
|
GNU CC is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU CC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU CC; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "rtl.h"
|
|
#include "regs.h"
|
|
#include "hard-reg-set.h"
|
|
#include "real.h"
|
|
#include "insn-config.h"
|
|
#include "conditions.h"
|
|
#include "insn-flags.h"
|
|
#include "output.h"
|
|
#include "insn-attr.h"
|
|
#include "flags.h"
|
|
#include "tree.h"
|
|
#include "expr.h"
|
|
#include "reload.h"
|
|
#include "function.h"
|
|
#include "ggc.h"
|
|
#include "langhooks.h"
|
|
|
|
#include "darwin-protos.h"
|
|
|
|
extern void machopic_output_stub PARAMS ((FILE *, const char *, const char *));
|
|
|
|
static int machopic_data_defined_p PARAMS ((const char *));
|
|
static int func_name_maybe_scoped PARAMS ((const char *));
|
|
static void update_non_lazy_ptrs PARAMS ((const char *));
|
|
static void update_stubs PARAMS ((const char *));
|
|
|
|
int
|
|
name_needs_quotes (name)
|
|
const char *name;
|
|
{
|
|
int c;
|
|
while ((c = *name++) != '\0')
|
|
if (! ISIDNUM (c))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* flag_pic = 1 ... generate only indirections
|
|
* flag_pic = 2 ... generate indirections and pure code
|
|
*/
|
|
|
|
/* This module assumes that (const (symbol_ref "foo")) is a legal pic
|
|
reference, which will not be changed. */
|
|
|
|
static tree machopic_defined_list;
|
|
|
|
enum machopic_addr_class
|
|
machopic_classify_ident (ident)
|
|
tree ident;
|
|
{
|
|
const char *name = IDENTIFIER_POINTER (ident);
|
|
int lprefix = (((name[0] == '*' || name[0] == '&')
|
|
&& (name[1] == 'L' || (name[1] == '"' && name[2] == 'L')))
|
|
|| ( name[0] == '_'
|
|
&& name[1] == 'O'
|
|
&& name[2] == 'B'
|
|
&& name[3] == 'J'
|
|
&& name[4] == 'C'
|
|
&& name[5] == '_'));
|
|
tree temp;
|
|
|
|
if (name[0] != '!')
|
|
{
|
|
/* Here if no special encoding to be found. */
|
|
if (lprefix)
|
|
{
|
|
const char *name = IDENTIFIER_POINTER (ident);
|
|
int len = strlen (name);
|
|
|
|
if ((len > 5 && !strcmp (name + len - 5, "$stub"))
|
|
|| (len > 6 && !strcmp (name + len - 6, "$stub\"")))
|
|
return MACHOPIC_DEFINED_FUNCTION;
|
|
return MACHOPIC_DEFINED_DATA;
|
|
}
|
|
|
|
for (temp = machopic_defined_list;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
if (ident == TREE_VALUE (temp))
|
|
return MACHOPIC_DEFINED_DATA;
|
|
}
|
|
|
|
if (TREE_ASM_WRITTEN (ident))
|
|
return MACHOPIC_DEFINED_DATA;
|
|
|
|
return MACHOPIC_UNDEFINED;
|
|
}
|
|
|
|
else if (name[1] == 'D')
|
|
return MACHOPIC_DEFINED_DATA;
|
|
|
|
else if (name[1] == 'T')
|
|
return MACHOPIC_DEFINED_FUNCTION;
|
|
|
|
/* It is possible that someone is holding a "stale" name, which has
|
|
since been defined. See if there is a "defined" name (i.e,
|
|
different from NAME only in having a '!D_' or a '!T_' instead of
|
|
a '!d_' or '!t_' prefix) in the identifier hash tables. If so, say
|
|
that this identifier is defined. */
|
|
else if (name[1] == 'd' || name[1] == 't')
|
|
{
|
|
char *new_name;
|
|
new_name = (char *)alloca (strlen (name) + 1);
|
|
strcpy (new_name, name);
|
|
new_name[1] = (name[1] == 'd') ? 'D' : 'T';
|
|
if (maybe_get_identifier (new_name) != NULL)
|
|
return (name[1] == 'd') ? MACHOPIC_DEFINED_DATA
|
|
: MACHOPIC_DEFINED_FUNCTION;
|
|
}
|
|
|
|
for (temp = machopic_defined_list; temp != NULL_TREE; temp = TREE_CHAIN (temp))
|
|
{
|
|
if (ident == TREE_VALUE (temp))
|
|
{
|
|
if (name[1] == 'T')
|
|
return MACHOPIC_DEFINED_FUNCTION;
|
|
else
|
|
return MACHOPIC_DEFINED_DATA;
|
|
}
|
|
}
|
|
|
|
if (name[1] == 't' || name[1] == 'T')
|
|
{
|
|
if (lprefix)
|
|
return MACHOPIC_DEFINED_FUNCTION;
|
|
else
|
|
return MACHOPIC_UNDEFINED_FUNCTION;
|
|
}
|
|
else
|
|
{
|
|
if (lprefix)
|
|
return MACHOPIC_DEFINED_DATA;
|
|
else
|
|
return MACHOPIC_UNDEFINED_DATA;
|
|
}
|
|
}
|
|
|
|
|
|
enum machopic_addr_class
|
|
machopic_classify_name (name)
|
|
const char *name;
|
|
{
|
|
return machopic_classify_ident (get_identifier (name));
|
|
}
|
|
|
|
int
|
|
machopic_ident_defined_p (ident)
|
|
tree ident;
|
|
{
|
|
switch (machopic_classify_ident (ident))
|
|
{
|
|
case MACHOPIC_UNDEFINED:
|
|
case MACHOPIC_UNDEFINED_DATA:
|
|
case MACHOPIC_UNDEFINED_FUNCTION:
|
|
return 0;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int
|
|
machopic_data_defined_p (name)
|
|
const char *name;
|
|
{
|
|
switch (machopic_classify_ident (get_identifier (name)))
|
|
{
|
|
case MACHOPIC_DEFINED_DATA:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
machopic_name_defined_p (name)
|
|
const char *name;
|
|
{
|
|
return machopic_ident_defined_p (get_identifier (name));
|
|
}
|
|
|
|
void
|
|
machopic_define_ident (ident)
|
|
tree ident;
|
|
{
|
|
if (!machopic_ident_defined_p (ident))
|
|
machopic_defined_list =
|
|
tree_cons (NULL_TREE, ident, machopic_defined_list);
|
|
}
|
|
|
|
void
|
|
machopic_define_name (name)
|
|
const char *name;
|
|
{
|
|
machopic_define_ident (get_identifier (name));
|
|
}
|
|
|
|
/* This is a static to make inline functions work. The rtx
|
|
representing the PIC base symbol always points to here. */
|
|
|
|
static char function_base[32];
|
|
|
|
static int current_pic_label_num;
|
|
|
|
char *
|
|
machopic_function_base_name ()
|
|
{
|
|
static const char *name = NULL;
|
|
static const char *current_name;
|
|
|
|
current_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
|
|
|
|
if (name != current_name)
|
|
{
|
|
current_function_uses_pic_offset_table = 1;
|
|
|
|
/* Save mucho space and time. Some of the C++ mangled names are over
|
|
700 characters long! Note that we produce a label containing a '-'
|
|
if the function we're compiling is an Objective-C method, as evinced
|
|
by the incredibly scientific test below. This is because code in
|
|
rs6000.c makes the same ugly test when loading the PIC reg. */
|
|
|
|
++current_pic_label_num;
|
|
if (*current_name == '+' || *current_name == '-')
|
|
sprintf (function_base, "*\"L-%d$pb\"", current_pic_label_num);
|
|
else
|
|
sprintf (function_base, "*L%d$pb", current_pic_label_num);
|
|
|
|
name = current_name;
|
|
}
|
|
|
|
return function_base;
|
|
}
|
|
|
|
static tree machopic_non_lazy_pointers = NULL;
|
|
|
|
/* Return a non-lazy pointer name corresponding to the given name,
|
|
either by finding it in our list of pointer names, or by generating
|
|
a new one. */
|
|
|
|
char *
|
|
machopic_non_lazy_ptr_name (name)
|
|
const char *name;
|
|
{
|
|
char *temp_name;
|
|
tree temp, ident = get_identifier (name);
|
|
|
|
for (temp = machopic_non_lazy_pointers;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
if (ident == TREE_VALUE (temp))
|
|
return IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
}
|
|
|
|
STRIP_NAME_ENCODING (name, name);
|
|
|
|
/* Try again, but comparing names this time. */
|
|
for (temp = machopic_non_lazy_pointers;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
if (TREE_VALUE (temp))
|
|
{
|
|
temp_name = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
STRIP_NAME_ENCODING (temp_name, temp_name);
|
|
if (strcmp (name, temp_name) == 0)
|
|
return IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
}
|
|
}
|
|
|
|
{
|
|
char *buffer;
|
|
tree ptr_name;
|
|
|
|
buffer = alloca (strlen (name) + 20);
|
|
|
|
strcpy (buffer, "&L");
|
|
if (name[0] == '*')
|
|
strcat (buffer, name+1);
|
|
else
|
|
{
|
|
strcat (buffer, "_");
|
|
strcat (buffer, name);
|
|
}
|
|
|
|
strcat (buffer, "$non_lazy_ptr");
|
|
ptr_name = get_identifier (buffer);
|
|
|
|
machopic_non_lazy_pointers
|
|
= tree_cons (ptr_name, ident, machopic_non_lazy_pointers);
|
|
|
|
TREE_USED (machopic_non_lazy_pointers) = 0;
|
|
|
|
return IDENTIFIER_POINTER (ptr_name);
|
|
}
|
|
}
|
|
|
|
static tree machopic_stubs = 0;
|
|
|
|
/* Make sure the GC knows about our homemade lists. */
|
|
|
|
void
|
|
machopic_add_gc_roots ()
|
|
{
|
|
ggc_add_tree_root (&machopic_defined_list, 1);
|
|
ggc_add_tree_root (&machopic_non_lazy_pointers, 1);
|
|
ggc_add_tree_root (&machopic_stubs, 1);
|
|
}
|
|
|
|
/* Return the name of the stub corresponding to the given name,
|
|
generating a new stub name if necessary. */
|
|
|
|
char *
|
|
machopic_stub_name (name)
|
|
const char *name;
|
|
{
|
|
tree temp, ident = get_identifier (name);
|
|
const char *tname;
|
|
|
|
for (temp = machopic_stubs;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
if (ident == TREE_VALUE (temp))
|
|
return IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
tname = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
if (strcmp (name, tname) == 0)
|
|
return IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
/* A library call name might not be section-encoded yet, so try
|
|
it against a stripped name. */
|
|
if (name[0] != '!'
|
|
&& tname[0] == '!'
|
|
&& strcmp (name, tname + 4) == 0)
|
|
return IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
}
|
|
|
|
STRIP_NAME_ENCODING (name, name);
|
|
|
|
{
|
|
char *buffer;
|
|
tree ptr_name;
|
|
int needs_quotes = name_needs_quotes (name);
|
|
|
|
buffer = alloca (strlen (name) + 20);
|
|
|
|
if (needs_quotes)
|
|
strcpy (buffer, "&\"L");
|
|
else
|
|
strcpy (buffer, "&L");
|
|
if (name[0] == '*')
|
|
{
|
|
strcat (buffer, name+1);
|
|
}
|
|
else
|
|
{
|
|
strcat (buffer, "_");
|
|
strcat (buffer, name);
|
|
}
|
|
|
|
if (needs_quotes)
|
|
strcat (buffer, "$stub\"");
|
|
else
|
|
strcat (buffer, "$stub");
|
|
ptr_name = get_identifier (buffer);
|
|
|
|
machopic_stubs = tree_cons (ptr_name, ident, machopic_stubs);
|
|
TREE_USED (machopic_stubs) = 0;
|
|
|
|
return IDENTIFIER_POINTER (ptr_name);
|
|
}
|
|
}
|
|
|
|
void
|
|
machopic_validate_stub_or_non_lazy_ptr (name, validate_stub)
|
|
const char *name;
|
|
int validate_stub;
|
|
{
|
|
char *real_name;
|
|
tree temp, ident = get_identifier (name), id2;
|
|
|
|
for (temp = (validate_stub ? machopic_stubs : machopic_non_lazy_pointers);
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
if (ident == TREE_PURPOSE (temp))
|
|
{
|
|
/* Mark both the stub or non-lazy pointer as well as the
|
|
original symbol as being referenced. */
|
|
TREE_USED (temp) = 1;
|
|
if (TREE_CODE (TREE_VALUE (temp)) == IDENTIFIER_NODE)
|
|
TREE_SYMBOL_REFERENCED (TREE_VALUE (temp)) = 1;
|
|
STRIP_NAME_ENCODING (real_name, IDENTIFIER_POINTER (TREE_VALUE (temp)));
|
|
id2 = maybe_get_identifier (real_name);
|
|
if (id2)
|
|
TREE_SYMBOL_REFERENCED (id2) = 1;
|
|
}
|
|
}
|
|
|
|
/* Transform ORIG, which may be any data source, to the corresponding
|
|
source using indirections. */
|
|
|
|
rtx
|
|
machopic_indirect_data_reference (orig, reg)
|
|
rtx orig, reg;
|
|
{
|
|
rtx ptr_ref = orig;
|
|
|
|
if (! MACHOPIC_INDIRECT)
|
|
return orig;
|
|
|
|
if (GET_CODE (orig) == SYMBOL_REF)
|
|
{
|
|
const char *name = XSTR (orig, 0);
|
|
|
|
if (machopic_data_defined_p (name))
|
|
{
|
|
rtx pic_base = gen_rtx (SYMBOL_REF, Pmode,
|
|
machopic_function_base_name ());
|
|
rtx offset = gen_rtx (CONST, Pmode,
|
|
gen_rtx (MINUS, Pmode, orig, pic_base));
|
|
|
|
#if defined (TARGET_TOC) /* i.e., PowerPC */
|
|
rtx hi_sum_reg = reg;
|
|
|
|
if (reg == NULL)
|
|
abort ();
|
|
|
|
emit_insn (gen_rtx (SET, Pmode, hi_sum_reg,
|
|
gen_rtx (PLUS, Pmode, pic_offset_table_rtx,
|
|
gen_rtx (HIGH, Pmode, offset))));
|
|
emit_insn (gen_rtx (SET, Pmode, reg,
|
|
gen_rtx (LO_SUM, Pmode, hi_sum_reg, offset)));
|
|
|
|
orig = reg;
|
|
#else
|
|
#if defined (HAVE_lo_sum)
|
|
if (reg == 0) abort ();
|
|
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (HIGH, Pmode, offset)));
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (LO_SUM, Pmode, reg, offset)));
|
|
emit_insn (gen_rtx (USE, VOIDmode,
|
|
gen_rtx_REG (Pmode, PIC_OFFSET_TABLE_REGNUM)));
|
|
|
|
orig = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, reg);
|
|
#endif
|
|
#endif
|
|
return orig;
|
|
}
|
|
|
|
ptr_ref = gen_rtx (SYMBOL_REF, Pmode,
|
|
machopic_non_lazy_ptr_name (name));
|
|
|
|
ptr_ref = gen_rtx_MEM (Pmode, ptr_ref);
|
|
RTX_UNCHANGING_P (ptr_ref) = 1;
|
|
|
|
return ptr_ref;
|
|
}
|
|
else if (GET_CODE (orig) == CONST)
|
|
{
|
|
rtx base, result;
|
|
|
|
/* legitimize both operands of the PLUS */
|
|
if (GET_CODE (XEXP (orig, 0)) == PLUS)
|
|
{
|
|
base = machopic_indirect_data_reference (XEXP (XEXP (orig, 0), 0),
|
|
reg);
|
|
orig = machopic_indirect_data_reference (XEXP (XEXP (orig, 0), 1),
|
|
(base == reg ? 0 : reg));
|
|
}
|
|
else
|
|
return orig;
|
|
|
|
if (MACHOPIC_PURE && GET_CODE (orig) == CONST_INT)
|
|
result = plus_constant (base, INTVAL (orig));
|
|
else
|
|
result = gen_rtx (PLUS, Pmode, base, orig);
|
|
|
|
if (RTX_UNCHANGING_P (base) && RTX_UNCHANGING_P (orig))
|
|
RTX_UNCHANGING_P (result) = 1;
|
|
|
|
if (MACHOPIC_JUST_INDIRECT && GET_CODE (base) == MEM)
|
|
{
|
|
if (reg)
|
|
{
|
|
emit_move_insn (reg, result);
|
|
result = reg;
|
|
}
|
|
else
|
|
{
|
|
result = force_reg (GET_MODE (result), result);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
else if (GET_CODE (orig) == MEM)
|
|
XEXP (ptr_ref, 0) = machopic_indirect_data_reference (XEXP (orig, 0), reg);
|
|
/* When the target is i386, this code prevents crashes due to the
|
|
compiler's ignorance on how to move the PIC base register to
|
|
other registers. (The reload phase sometimes introduces such
|
|
insns.) */
|
|
else if (GET_CODE (orig) == PLUS
|
|
&& GET_CODE (XEXP (orig, 0)) == REG
|
|
&& REGNO (XEXP (orig, 0)) == PIC_OFFSET_TABLE_REGNUM
|
|
#ifdef I386
|
|
/* Prevent the same register from being erroneously used
|
|
as both the base and index registers. */
|
|
&& GET_CODE (XEXP (orig, 1)) == CONST
|
|
#endif
|
|
&& reg)
|
|
{
|
|
emit_move_insn (reg, XEXP (orig, 0));
|
|
XEXP (ptr_ref, 0) = reg;
|
|
}
|
|
return ptr_ref;
|
|
}
|
|
|
|
/* Transform TARGET (a MEM), which is a function call target, to the
|
|
corresponding symbol_stub if necessary. Return a new MEM. */
|
|
|
|
rtx
|
|
machopic_indirect_call_target (target)
|
|
rtx target;
|
|
{
|
|
if (GET_CODE (target) != MEM)
|
|
return target;
|
|
|
|
if (MACHOPIC_INDIRECT && GET_CODE (XEXP (target, 0)) == SYMBOL_REF)
|
|
{
|
|
enum machine_mode mode = GET_MODE (XEXP (target, 0));
|
|
const char *name = XSTR (XEXP (target, 0), 0);
|
|
|
|
/* If the name is already defined, we need do nothing. */
|
|
if (name[0] == '!' && name[1] == 'T')
|
|
return target;
|
|
|
|
if (!machopic_name_defined_p (name))
|
|
{
|
|
const char *stub_name = machopic_stub_name (name);
|
|
|
|
XEXP (target, 0) = gen_rtx (SYMBOL_REF, mode, stub_name);
|
|
RTX_UNCHANGING_P (target) = 1;
|
|
}
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
rtx
|
|
machopic_legitimize_pic_address (orig, mode, reg)
|
|
rtx orig, reg;
|
|
enum machine_mode mode;
|
|
{
|
|
rtx pic_ref = orig;
|
|
|
|
if (! MACHOPIC_PURE)
|
|
return orig;
|
|
|
|
/* First handle a simple SYMBOL_REF or LABEL_REF */
|
|
if (GET_CODE (orig) == LABEL_REF
|
|
|| (GET_CODE (orig) == SYMBOL_REF
|
|
))
|
|
{
|
|
/* addr(foo) = &func+(foo-func) */
|
|
rtx pic_base;
|
|
|
|
orig = machopic_indirect_data_reference (orig, reg);
|
|
|
|
if (GET_CODE (orig) == PLUS
|
|
&& GET_CODE (XEXP (orig, 0)) == REG)
|
|
{
|
|
if (reg == 0)
|
|
return force_reg (mode, orig);
|
|
|
|
emit_move_insn (reg, orig);
|
|
return reg;
|
|
}
|
|
|
|
pic_base = gen_rtx (SYMBOL_REF, Pmode, machopic_function_base_name ());
|
|
|
|
if (GET_CODE (orig) == MEM)
|
|
{
|
|
if (reg == 0)
|
|
{
|
|
if (reload_in_progress)
|
|
abort ();
|
|
else
|
|
reg = gen_reg_rtx (Pmode);
|
|
}
|
|
|
|
#ifdef HAVE_lo_sum
|
|
if (GET_CODE (XEXP (orig, 0)) == SYMBOL_REF
|
|
|| GET_CODE (XEXP (orig, 0)) == LABEL_REF)
|
|
{
|
|
rtx offset = gen_rtx (CONST, Pmode,
|
|
gen_rtx (MINUS, Pmode,
|
|
XEXP (orig, 0), pic_base));
|
|
#if defined (TARGET_TOC) /* i.e., PowerPC */
|
|
/* Generating a new reg may expose opportunities for
|
|
common subexpression elimination. */
|
|
rtx hi_sum_reg =
|
|
(reload_in_progress ? reg : gen_reg_rtx (SImode));
|
|
|
|
emit_insn (gen_rtx (SET, Pmode, hi_sum_reg,
|
|
gen_rtx (PLUS, Pmode,
|
|
pic_offset_table_rtx,
|
|
gen_rtx (HIGH, Pmode, offset))));
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (MEM, GET_MODE (orig),
|
|
gen_rtx (LO_SUM, Pmode,
|
|
hi_sum_reg, offset))));
|
|
pic_ref = reg;
|
|
|
|
#else
|
|
emit_insn (gen_rtx (USE, VOIDmode,
|
|
gen_rtx_REG (Pmode, PIC_OFFSET_TABLE_REGNUM)));
|
|
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (HIGH, Pmode,
|
|
gen_rtx (CONST, Pmode, offset))));
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (LO_SUM, Pmode, reg,
|
|
gen_rtx (CONST, Pmode, offset))));
|
|
pic_ref = gen_rtx (PLUS, Pmode,
|
|
pic_offset_table_rtx, reg);
|
|
#endif
|
|
}
|
|
else
|
|
#endif /* HAVE_lo_sum */
|
|
{
|
|
rtx pic = pic_offset_table_rtx;
|
|
if (GET_CODE (pic) != REG)
|
|
{
|
|
emit_move_insn (reg, pic);
|
|
pic = reg;
|
|
}
|
|
#if 0
|
|
emit_insn (gen_rtx (USE, VOIDmode,
|
|
gen_rtx (REG, Pmode, PIC_OFFSET_TABLE_REGNUM)));
|
|
#endif
|
|
|
|
pic_ref = gen_rtx (PLUS, Pmode,
|
|
pic,
|
|
gen_rtx (CONST, Pmode,
|
|
gen_rtx (MINUS, Pmode,
|
|
XEXP (orig, 0),
|
|
pic_base)));
|
|
}
|
|
|
|
#if !defined (TARGET_TOC)
|
|
RTX_UNCHANGING_P (pic_ref) = 1;
|
|
emit_move_insn (reg, pic_ref);
|
|
pic_ref = gen_rtx (MEM, GET_MODE (orig), reg);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef HAVE_lo_sum
|
|
if (GET_CODE (orig) == SYMBOL_REF
|
|
|| GET_CODE (orig) == LABEL_REF)
|
|
{
|
|
rtx offset = gen_rtx (CONST, Pmode,
|
|
gen_rtx (MINUS, Pmode, orig, pic_base));
|
|
#if defined (TARGET_TOC) /* i.e., PowerPC */
|
|
rtx hi_sum_reg;
|
|
|
|
if (reg == 0)
|
|
{
|
|
if (reload_in_progress)
|
|
abort ();
|
|
else
|
|
reg = gen_reg_rtx (SImode);
|
|
}
|
|
|
|
hi_sum_reg = reg;
|
|
|
|
emit_insn (gen_rtx (SET, Pmode, hi_sum_reg,
|
|
gen_rtx (PLUS, Pmode,
|
|
pic_offset_table_rtx,
|
|
gen_rtx (HIGH, Pmode, offset))));
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (LO_SUM, Pmode,
|
|
hi_sum_reg, offset)));
|
|
pic_ref = reg;
|
|
#else
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (HIGH, Pmode, offset)));
|
|
emit_insn (gen_rtx (SET, VOIDmode, reg,
|
|
gen_rtx (LO_SUM, Pmode, reg, offset)));
|
|
pic_ref = gen_rtx (PLUS, Pmode,
|
|
pic_offset_table_rtx, reg);
|
|
#endif
|
|
}
|
|
else
|
|
#endif /* HAVE_lo_sum */
|
|
{
|
|
if (GET_CODE (orig) == REG)
|
|
{
|
|
return orig;
|
|
}
|
|
else
|
|
{
|
|
rtx pic = pic_offset_table_rtx;
|
|
if (GET_CODE (pic) != REG)
|
|
{
|
|
emit_move_insn (reg, pic);
|
|
pic = reg;
|
|
}
|
|
#if 0
|
|
emit_insn (gen_rtx (USE, VOIDmode,
|
|
pic_offset_table_rtx));
|
|
#endif
|
|
pic_ref = gen_rtx (PLUS, Pmode,
|
|
pic,
|
|
gen_rtx (CONST, Pmode,
|
|
gen_rtx (MINUS, Pmode,
|
|
orig, pic_base)));
|
|
}
|
|
}
|
|
}
|
|
|
|
RTX_UNCHANGING_P (pic_ref) = 1;
|
|
|
|
if (GET_CODE (pic_ref) != REG)
|
|
{
|
|
if (reg != 0)
|
|
{
|
|
emit_move_insn (reg, pic_ref);
|
|
return reg;
|
|
}
|
|
else
|
|
{
|
|
return force_reg (mode, pic_ref);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return pic_ref;
|
|
}
|
|
}
|
|
|
|
else if (GET_CODE (orig) == SYMBOL_REF)
|
|
return orig;
|
|
|
|
else if (GET_CODE (orig) == PLUS
|
|
&& (GET_CODE (XEXP (orig, 0)) == MEM
|
|
|| GET_CODE (XEXP (orig, 0)) == SYMBOL_REF
|
|
|| GET_CODE (XEXP (orig, 0)) == LABEL_REF)
|
|
&& XEXP (orig, 0) != pic_offset_table_rtx
|
|
&& GET_CODE (XEXP (orig, 1)) != REG)
|
|
|
|
{
|
|
rtx base;
|
|
int is_complex = (GET_CODE (XEXP (orig, 0)) == MEM);
|
|
|
|
base = machopic_legitimize_pic_address (XEXP (orig, 0), Pmode, reg);
|
|
orig = machopic_legitimize_pic_address (XEXP (orig, 1),
|
|
Pmode, (base == reg ? 0 : reg));
|
|
if (GET_CODE (orig) == CONST_INT)
|
|
{
|
|
pic_ref = plus_constant (base, INTVAL (orig));
|
|
is_complex = 1;
|
|
}
|
|
else
|
|
pic_ref = gen_rtx (PLUS, Pmode, base, orig);
|
|
|
|
if (RTX_UNCHANGING_P (base) && RTX_UNCHANGING_P (orig))
|
|
RTX_UNCHANGING_P (pic_ref) = 1;
|
|
|
|
if (reg && is_complex)
|
|
{
|
|
emit_move_insn (reg, pic_ref);
|
|
pic_ref = reg;
|
|
}
|
|
/* Likewise, should we set special REG_NOTEs here? */
|
|
}
|
|
|
|
else if (GET_CODE (orig) == CONST)
|
|
{
|
|
return machopic_legitimize_pic_address (XEXP (orig, 0), Pmode, reg);
|
|
}
|
|
|
|
else if (GET_CODE (orig) == MEM
|
|
&& GET_CODE (XEXP (orig, 0)) == SYMBOL_REF)
|
|
{
|
|
rtx addr = machopic_legitimize_pic_address (XEXP (orig, 0), Pmode, reg);
|
|
|
|
addr = gen_rtx (MEM, GET_MODE (orig), addr);
|
|
RTX_UNCHANGING_P (addr) = RTX_UNCHANGING_P (orig);
|
|
emit_move_insn (reg, addr);
|
|
pic_ref = reg;
|
|
}
|
|
|
|
return pic_ref;
|
|
}
|
|
|
|
|
|
void
|
|
machopic_finish (asm_out_file)
|
|
FILE *asm_out_file;
|
|
{
|
|
tree temp;
|
|
|
|
for (temp = machopic_stubs;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
const char *sym_name = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
const char *stub_name = IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
char *sym;
|
|
char *stub;
|
|
|
|
if (! TREE_USED (temp))
|
|
continue;
|
|
|
|
/* If the symbol is actually defined, we don't need a stub. */
|
|
if (sym_name[0] == '!' && sym_name[1] == 'T')
|
|
continue;
|
|
|
|
STRIP_NAME_ENCODING (sym_name, sym_name);
|
|
|
|
sym = alloca (strlen (sym_name) + 2);
|
|
if (sym_name[0] == '*' || sym_name[0] == '&')
|
|
strcpy (sym, sym_name + 1);
|
|
else if (sym_name[0] == '-' || sym_name[0] == '+')
|
|
strcpy (sym, sym_name);
|
|
else
|
|
sym[0] = '_', strcpy (sym + 1, sym_name);
|
|
|
|
stub = alloca (strlen (stub_name) + 2);
|
|
if (stub_name[0] == '*' || stub_name[0] == '&')
|
|
strcpy (stub, stub_name + 1);
|
|
else
|
|
stub[0] = '_', strcpy (stub + 1, stub_name);
|
|
|
|
machopic_output_stub (asm_out_file, sym, stub);
|
|
}
|
|
|
|
for (temp = machopic_non_lazy_pointers;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
char *sym_name = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
char *lazy_name = IDENTIFIER_POINTER (TREE_PURPOSE (temp));
|
|
#if 0
|
|
tree decl = lookup_name_darwin (TREE_VALUE (temp));
|
|
#endif
|
|
|
|
if (! TREE_USED (temp))
|
|
continue;
|
|
|
|
if (machopic_ident_defined_p (TREE_VALUE (temp))
|
|
#if 0 /* add back when we have private externs */
|
|
|| (decl && DECL_PRIVATE_EXTERN (decl))
|
|
#endif
|
|
)
|
|
{
|
|
data_section ();
|
|
assemble_align (GET_MODE_ALIGNMENT (Pmode));
|
|
assemble_label (lazy_name);
|
|
assemble_integer (gen_rtx (SYMBOL_REF, Pmode, sym_name),
|
|
GET_MODE_SIZE (Pmode),
|
|
GET_MODE_ALIGNMENT (Pmode), 1);
|
|
}
|
|
else
|
|
{
|
|
machopic_nl_symbol_ptr_section ();
|
|
assemble_name (asm_out_file, lazy_name);
|
|
fprintf (asm_out_file, ":\n");
|
|
|
|
fprintf (asm_out_file, "\t.indirect_symbol ");
|
|
assemble_name (asm_out_file, sym_name);
|
|
fprintf (asm_out_file, "\n");
|
|
|
|
assemble_integer (const0_rtx, GET_MODE_SIZE (Pmode),
|
|
GET_MODE_ALIGNMENT (Pmode), 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
machopic_operand_p (op)
|
|
rtx op;
|
|
{
|
|
if (MACHOPIC_JUST_INDIRECT)
|
|
{
|
|
while (GET_CODE (op) == CONST)
|
|
op = XEXP (op, 0);
|
|
|
|
if (GET_CODE (op) == SYMBOL_REF)
|
|
return machopic_name_defined_p (XSTR (op, 0));
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
while (GET_CODE (op) == CONST)
|
|
op = XEXP (op, 0);
|
|
|
|
if (GET_CODE (op) == MINUS
|
|
&& GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|
|
&& GET_CODE (XEXP (op, 1)) == SYMBOL_REF
|
|
&& machopic_name_defined_p (XSTR (XEXP (op, 0), 0))
|
|
&& machopic_name_defined_p (XSTR (XEXP (op, 1), 0)))
|
|
return 1;
|
|
|
|
#if 0 /*def TARGET_TOC*/ /* i.e., PowerPC */
|
|
/* Without this statement, the compiler crashes while compiling enquire.c
|
|
when targetting PowerPC. It is not known why this code is not needed
|
|
when targetting other processors. */
|
|
else if (GET_CODE (op) == SYMBOL_REF
|
|
&& (machopic_classify_name (XSTR (op, 0))
|
|
== MACHOPIC_DEFINED_FUNCTION))
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This function records whether a given name corresponds to a defined
|
|
or undefined function or variable, for machopic_classify_ident to
|
|
use later. */
|
|
|
|
void
|
|
darwin_encode_section_info (decl)
|
|
tree decl;
|
|
{
|
|
char code = '\0';
|
|
int defined = 0;
|
|
rtx sym_ref;
|
|
const char *orig_str;
|
|
char *new_str;
|
|
size_t len, new_len;
|
|
|
|
if ((TREE_CODE (decl) == FUNCTION_DECL
|
|
|| TREE_CODE (decl) == VAR_DECL)
|
|
&& !DECL_EXTERNAL (decl)
|
|
&& ((TREE_STATIC (decl)
|
|
&& (!DECL_COMMON (decl) || !TREE_PUBLIC (decl)))
|
|
|| (DECL_INITIAL (decl)
|
|
&& DECL_INITIAL (decl) != error_mark_node)))
|
|
defined = 1;
|
|
|
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
|
code = (defined ? 'T' : 't');
|
|
else if (TREE_CODE (decl) == VAR_DECL)
|
|
code = (defined ? 'D' : 'd');
|
|
|
|
if (code == '\0')
|
|
return;
|
|
|
|
sym_ref = XEXP (DECL_RTL (decl), 0);
|
|
orig_str = XSTR (sym_ref, 0);
|
|
len = strlen (orig_str) + 1;
|
|
|
|
if (orig_str[0] == '!')
|
|
{
|
|
/* Already encoded; see if we need to change it. */
|
|
if (code == orig_str[1])
|
|
return;
|
|
/* Yes, tweak a copy of the name and put it in a new string. */
|
|
new_str = alloca (len);
|
|
memcpy (new_str, orig_str, len);
|
|
new_str[1] = code;
|
|
XSTR (sym_ref, 0) = ggc_alloc_string (new_str, len);
|
|
}
|
|
else
|
|
{
|
|
/* Add the encoding. */
|
|
new_len = len + 4;
|
|
new_str = alloca (new_len);
|
|
new_str[0] = '!';
|
|
new_str[1] = code;
|
|
new_str[2] = '_';
|
|
new_str[3] = '_';
|
|
memcpy (new_str + 4, orig_str, len);
|
|
XSTR (sym_ref, 0) = ggc_alloc_string (new_str, new_len);
|
|
}
|
|
/* The non-lazy pointer list may have captured references to the
|
|
old encoded name, change them. */
|
|
if (TREE_CODE (decl) == VAR_DECL)
|
|
update_non_lazy_ptrs (XSTR (sym_ref, 0));
|
|
else
|
|
update_stubs (XSTR (sym_ref, 0));
|
|
}
|
|
|
|
/* Scan the list of non-lazy pointers and update any recorded names whose
|
|
stripped name matches the argument. */
|
|
|
|
static void
|
|
update_non_lazy_ptrs (name)
|
|
const char *name;
|
|
{
|
|
const char *name1, *name2;
|
|
tree temp;
|
|
|
|
STRIP_NAME_ENCODING (name1, name);
|
|
|
|
for (temp = machopic_non_lazy_pointers;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
char *sym_name = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
|
|
if (*sym_name == '!')
|
|
{
|
|
STRIP_NAME_ENCODING (name2, sym_name);
|
|
if (strcmp (name1, name2) == 0)
|
|
{
|
|
IDENTIFIER_POINTER (TREE_VALUE (temp)) = name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Function NAME is being defined, and its label has just been output.
|
|
If there's already a reference to a stub for this function, we can
|
|
just emit the stub label now and we don't bother emitting the stub later. */
|
|
|
|
void
|
|
machopic_output_possible_stub_label (file, name)
|
|
FILE *file;
|
|
const char *name;
|
|
{
|
|
tree temp;
|
|
|
|
|
|
/* Ensure we're looking at a section-encoded name. */
|
|
if (name[0] != '!' || (name[1] != 't' && name[1] != 'T'))
|
|
return;
|
|
|
|
for (temp = machopic_stubs;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
const char *sym_name;
|
|
|
|
sym_name = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
if (sym_name[0] == '!' && sym_name[1] == 'T'
|
|
&& ! strcmp (name+2, sym_name+2))
|
|
{
|
|
ASM_OUTPUT_LABEL (file, IDENTIFIER_POINTER (TREE_PURPOSE (temp)));
|
|
/* Avoid generating a stub for this. */
|
|
TREE_USED (temp) = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scan the list of stubs and update any recorded names whose
|
|
stripped name matches the argument. */
|
|
|
|
static void
|
|
update_stubs (name)
|
|
const char *name;
|
|
{
|
|
const char *name1, *name2;
|
|
tree temp;
|
|
|
|
STRIP_NAME_ENCODING (name1, name);
|
|
|
|
for (temp = machopic_stubs;
|
|
temp != NULL_TREE;
|
|
temp = TREE_CHAIN (temp))
|
|
{
|
|
char *sym_name = IDENTIFIER_POINTER (TREE_VALUE (temp));
|
|
|
|
if (*sym_name == '!')
|
|
{
|
|
STRIP_NAME_ENCODING (name2, sym_name);
|
|
if (strcmp (name1, name2) == 0)
|
|
{
|
|
IDENTIFIER_POINTER (TREE_VALUE (temp)) = name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
machopic_asm_out_constructor (symbol, priority)
|
|
rtx symbol;
|
|
int priority ATTRIBUTE_UNUSED;
|
|
{
|
|
if (flag_pic)
|
|
mod_init_section ();
|
|
else
|
|
constructor_section ();
|
|
assemble_align (POINTER_SIZE);
|
|
assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
|
|
|
|
if (!flag_pic)
|
|
fprintf (asm_out_file, ".reference .constructors_used\n");
|
|
}
|
|
|
|
void
|
|
machopic_asm_out_destructor (symbol, priority)
|
|
rtx symbol;
|
|
int priority ATTRIBUTE_UNUSED;
|
|
{
|
|
if (flag_pic)
|
|
mod_term_section ();
|
|
else
|
|
destructor_section ();
|
|
assemble_align (POINTER_SIZE);
|
|
assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
|
|
|
|
if (!flag_pic)
|
|
fprintf (asm_out_file, ".reference .destructors_used\n");
|
|
}
|