diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-20 01:06:22 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-20 01:06:22 +0000 |
commit | c482518380683ee38d14024c1e362a0d681cf967 (patch) | |
tree | e69b4f6d3fee3aced20a41f3fdf543fc1c77fb5d /gnu/usr.bin/gcc/varasm.c | |
parent | 76a62188d0db49c65b696d474c855a799fd96dce (diff) |
FSF GCC version 2.7.2
Diffstat (limited to 'gnu/usr.bin/gcc/varasm.c')
-rw-r--r-- | gnu/usr.bin/gcc/varasm.c | 4064 |
1 files changed, 4064 insertions, 0 deletions
diff --git a/gnu/usr.bin/gcc/varasm.c b/gnu/usr.bin/gcc/varasm.c new file mode 100644 index 00000000000..42b81fa4d86 --- /dev/null +++ b/gnu/usr.bin/gcc/varasm.c @@ -0,0 +1,4064 @@ +/* Output variables, constants and external declarations, for GNU compiler. + Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, 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. */ + + +/* This file handles generation of all the assembler code + *except* the instructions of a function. + This includes declarations of variables and their initial values. + + We also output the assembler code for constants stored in memory + and are responsible for combining constants with the same value. */ + +#include <stdio.h> +#include <setjmp.h> +/* #include <stab.h> */ +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "output.h" +#include "hard-reg-set.h" +#include "regs.h" +#include "defaults.h" +#include "real.h" +#include "bytecode.h" + +#include "obstack.h" +#include "c-pragma.h" + +#ifdef XCOFF_DEBUGGING_INFO +#include "xcoffout.h" +#endif + +#include <ctype.h> + +#ifndef ASM_STABS_OP +#define ASM_STABS_OP ".stabs" +#endif + +/* This macro gets just the user-specified name + out of the string in a SYMBOL_REF. On most machines, + we discard the * if any and that's all. */ +#ifndef STRIP_NAME_ENCODING +#define STRIP_NAME_ENCODING(VAR,SYMBOL_NAME) \ + (VAR) = ((SYMBOL_NAME) + ((SYMBOL_NAME)[0] == '*')) +#endif + +/* File in which assembler code is being written. */ + +extern FILE *asm_out_file; + +/* The (assembler) name of the first globally-visible object output. */ +char *first_global_object_name; + +extern struct obstack *current_obstack; +extern struct obstack *saveable_obstack; +extern struct obstack *rtl_obstack; +extern struct obstack permanent_obstack; +#define obstack_chunk_alloc xmalloc + +/* Number for making the label on the next + constant that is stored in memory. */ + +int const_labelno; + +/* Number for making the label on the next + static variable internal to a function. */ + +int var_labelno; + +/* Carry information from ASM_DECLARE_OBJECT_NAME + to ASM_FINISH_DECLARE_OBJECT. */ + +int size_directive_output; + +/* The last decl for which assemble_variable was called, + if it did ASM_DECLARE_OBJECT_NAME. + If the last call to assemble_variable didn't do that, + this holds 0. */ + +tree last_assemble_variable_decl; + + +#ifdef HANDLE_PRAGMA_WEAK +/* Any weak symbol declarations waiting to be emitted. */ + +struct weak_syms +{ + struct weak_syms *next; + char *name; + char *value; +}; + +static struct weak_syms *weak_decls; +#endif + +/* Nonzero if at least one function definition has been seen. */ + +static int function_defined; + +struct addr_const; +struct constant_descriptor; +struct rtx_const; +struct pool_constant; + +static void bc_make_decl_rtl PROTO((tree, char *, int)); +static char *strip_reg_name PROTO((char *)); +static void bc_output_ascii PROTO((FILE *, char *, int)); +static int contains_pointers_p PROTO((tree)); +static void decode_addr_const PROTO((tree, struct addr_const *)); +static int const_hash PROTO((tree)); +static int compare_constant PROTO((tree, + struct constant_descriptor *)); +static char *compare_constant_1 PROTO((tree, char *)); +static struct constant_descriptor *record_constant PROTO((tree)); +static void record_constant_1 PROTO((tree)); +static tree copy_constant PROTO((tree)); +static void output_constant_def_contents PROTO((tree, int, int)); +static void decode_rtx_const PROTO((enum machine_mode, rtx, + struct rtx_const *)); +static int const_hash_rtx PROTO((enum machine_mode, rtx)); +static int compare_constant_rtx PROTO((enum machine_mode, rtx, + struct constant_descriptor *)); +static struct constant_descriptor *record_constant_rtx PROTO((enum machine_mode, + rtx)); +static struct pool_constant *find_pool_constant PROTO((rtx)); +static int output_addressed_constants PROTO((tree)); +static void bc_assemble_integer PROTO((tree, int)); +static void output_constructor PROTO((tree, int)); + +#ifdef EXTRA_SECTIONS +static enum in_section {no_section, in_text, in_data, in_named, EXTRA_SECTIONS} in_section + = no_section; +#else +static enum in_section {no_section, in_text, in_data, in_named} in_section + = no_section; +#endif + +/* Return a non-zero value if DECL has a section attribute. */ +#define IN_NAMED_SECTION(DECL) \ + ((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \ + && DECL_SECTION_NAME (DECL) != NULL_TREE) + +/* Text of section name when in_section == in_named. */ +static char *in_named_name; + +/* Define functions like text_section for any extra sections. */ +#ifdef EXTRA_SECTION_FUNCTIONS +EXTRA_SECTION_FUNCTIONS +#endif + +/* Tell assembler to switch to text section. */ + +void +text_section () +{ + if (in_section != in_text) + { + if (output_bytecode) + bc_text (); + else + fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); + + in_section = in_text; + } +} + +/* Tell assembler to switch to data section. */ + +void +data_section () +{ + if (in_section != in_data) + { + if (output_bytecode) + bc_data (); + else + { + if (flag_shared_data) + { +#ifdef SHARED_SECTION_ASM_OP + fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP); +#else + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); +#endif + } + else + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); + } + + in_section = in_data; + } +} + +/* Tell assembler to switch to read-only data section. This is normally + the text section. */ + +void +readonly_data_section () +{ +#ifdef READONLY_DATA_SECTION + READONLY_DATA_SECTION (); /* Note this can call data_section. */ +#else + text_section (); +#endif +} + +/* Determine if we're in the text section. */ + +int +in_text_section () +{ + return in_section == in_text; +} + +/* Tell assembler to change to section NAME for DECL. + If DECL is NULL, just switch to section NAME. + If NAME is NULL, get the name from DECL. */ + +void +named_section (decl, name) + tree decl; + char *name; +{ + if (decl != NULL_TREE + && (TREE_CODE (decl) != FUNCTION_DECL && TREE_CODE (decl) != VAR_DECL)) + abort (); + if (name == NULL) + name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); + + if (in_section != in_named || strcmp (name, in_named_name)) + { + in_named_name = name; + in_section = in_named; + +#ifdef ASM_OUTPUT_SECTION_NAME + ASM_OUTPUT_SECTION_NAME (asm_out_file, decl, name); +#else + /* Section attributes are not supported if this macro isn't provided - + some host formats don't support them at all. The front-end should + already have flagged this as an error. */ + abort (); +#endif + } +} + +/* Switch to the section for function DECL. + + If DECL is NULL_TREE, switch to the text section. + ??? It's not clear that we will ever be passed NULL_TREE, but it's + safer to handle it. */ + +void +function_section (decl) + tree decl; +{ + if (decl != NULL_TREE + && DECL_SECTION_NAME (decl) != NULL_TREE) + named_section (decl, (char *) 0); + else + text_section (); +} + +/* Create the rtl to represent a function, for a function definition. + DECL is a FUNCTION_DECL node which describes which function. + The rtl is stored into DECL. */ + +void +make_function_rtl (decl) + tree decl; +{ + char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + if (output_bytecode) + { + if (DECL_RTL (decl) == 0) + DECL_RTL (decl) = bc_gen_rtx (name, 0, (struct bc_label *) 0); + + /* Record that at least one function has been defined. */ + function_defined = 1; + return; + } + + /* Rename a nested function to avoid conflicts. */ + if (decl_function_context (decl) != 0 + && DECL_INITIAL (decl) != 0 + && DECL_RTL (decl) == 0) + { + char *label; + + name = IDENTIFIER_POINTER (DECL_NAME (decl)); + ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); + name = obstack_copy0 (saveable_obstack, label, strlen (label)); + var_labelno++; + } + + if (DECL_RTL (decl) == 0) + { + DECL_RTL (decl) + = gen_rtx (MEM, DECL_MODE (decl), + gen_rtx (SYMBOL_REF, Pmode, name)); + + /* Optionally set flags or add text to the name to record information + such as that it is a function name. If the name is changed, the macro + ASM_OUTPUT_LABELREF will have to know how to strip this information. */ +#ifdef ENCODE_SECTION_INFO + ENCODE_SECTION_INFO (decl); +#endif + } + + /* Record at least one function has been defined. */ + function_defined = 1; +} + +/* Create the DECL_RTL for a declaration for a static or external + variable or static or external function. + ASMSPEC, if not 0, is the string which the user specified + as the assembler symbol name. + TOP_LEVEL is nonzero if this is a file-scope variable. + This is never called for PARM_DECLs. */ + +static void +bc_make_decl_rtl (decl, asmspec, top_level) + tree decl; + char *asmspec; + int top_level; +{ + register char *name = TREE_STRING_POINTER (DECL_ASSEMBLER_NAME (decl)); + + if (DECL_RTL (decl) == 0) + { + /* Print an error message for register variables. */ + if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL) + error ("function declared `register'"); + else if (DECL_REGISTER (decl)) + error ("global register variables not supported in the interpreter"); + + /* Handle ordinary static variables and functions. */ + if (DECL_RTL (decl) == 0) + { + /* Can't use just the variable's own name for a variable + whose scope is less than the whole file. + Concatenate a distinguishing number. */ + if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0) + { + char *label; + + ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); + name = obstack_copy0 (saveable_obstack, label, strlen (label)); + var_labelno++; + } + + DECL_RTL (decl) = bc_gen_rtx (name, 0, (struct bc_label *) 0); + } + } +} + +/* Given NAME, a putative register name, discard any customary prefixes. */ + +static char * +strip_reg_name (name) + char *name; +{ +#ifdef REGISTER_PREFIX + if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX))) + name += strlen (REGISTER_PREFIX); +#endif + if (name[0] == '%' || name[0] == '#') + name++; + return name; +} + +/* Decode an `asm' spec for a declaration as a register name. + Return the register number, or -1 if nothing specified, + or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized, + or -3 if ASMSPEC is `cc' and is not recognized, + or -4 if ASMSPEC is `memory' and is not recognized. + Accept an exact spelling or a decimal number. + Prefixes such as % are optional. */ + +int +decode_reg_name (asmspec) + char *asmspec; +{ + if (asmspec != 0) + { + int i; + + /* Get rid of confusing prefixes. */ + asmspec = strip_reg_name (asmspec); + + /* Allow a decimal number as a "register name". */ + for (i = strlen (asmspec) - 1; i >= 0; i--) + if (! (asmspec[i] >= '0' && asmspec[i] <= '9')) + break; + if (asmspec[0] != 0 && i < 0) + { + i = atoi (asmspec); + if (i < FIRST_PSEUDO_REGISTER && i >= 0) + return i; + else + return -2; + } + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (reg_names[i][0] + && ! strcmp (asmspec, strip_reg_name (reg_names[i]))) + return i; + +#ifdef ADDITIONAL_REGISTER_NAMES + { + static struct { char *name; int number; } table[] + = ADDITIONAL_REGISTER_NAMES; + + for (i = 0; i < sizeof (table) / sizeof (table[0]); i++) + if (! strcmp (asmspec, table[i].name)) + return table[i].number; + } +#endif /* ADDITIONAL_REGISTER_NAMES */ + + if (!strcmp (asmspec, "memory")) + return -4; + + if (!strcmp (asmspec, "cc")) + return -3; + + return -2; + } + + return -1; +} + +/* Create the DECL_RTL for a declaration for a static or external variable + or static or external function. + ASMSPEC, if not 0, is the string which the user specified + as the assembler symbol name. + TOP_LEVEL is nonzero if this is a file-scope variable. + + This is never called for PARM_DECL nodes. */ + +void +make_decl_rtl (decl, asmspec, top_level) + tree decl; + char *asmspec; + int top_level; +{ + register char *name = 0; + int reg_number; + + if (output_bytecode) + { + bc_make_decl_rtl (decl, asmspec, top_level); + return; + } + + reg_number = decode_reg_name (asmspec); + + if (DECL_ASSEMBLER_NAME (decl) != NULL_TREE) + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + + if (reg_number == -2) + { + /* ASMSPEC is given, and not the name of a register. */ + name = (char *) obstack_alloc (saveable_obstack, + strlen (asmspec) + 2); + name[0] = '*'; + strcpy (&name[1], asmspec); + } + + /* For a duplicate declaration, we can be called twice on the + same DECL node. Don't discard the RTL already made. */ + if (DECL_RTL (decl) == 0) + { + DECL_RTL (decl) = 0; + + /* First detect errors in declaring global registers. */ + if (DECL_REGISTER (decl) && reg_number == -1) + error_with_decl (decl, + "register name not specified for `%s'"); + else if (DECL_REGISTER (decl) && reg_number < 0) + error_with_decl (decl, + "invalid register name for `%s'"); + else if ((reg_number >= 0 || reg_number == -3) && ! DECL_REGISTER (decl)) + error_with_decl (decl, + "register name given for non-register variable `%s'"); + else if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL) + error ("function declared `register'"); + else if (DECL_REGISTER (decl) && TYPE_MODE (TREE_TYPE (decl)) == BLKmode) + error_with_decl (decl, "data type of `%s' isn't suitable for a register"); + else if (DECL_REGISTER (decl) + && ! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl)))) + error_with_decl (decl, "register number for `%s' isn't suitable for the data type"); + /* Now handle properly declared static register variables. */ + else if (DECL_REGISTER (decl)) + { + int nregs; +#if 0 /* yylex should print the warning for this */ + if (pedantic) + pedwarn ("ANSI C forbids global register variables"); +#endif + if (DECL_INITIAL (decl) != 0 && top_level) + { + DECL_INITIAL (decl) = 0; + error ("global register variable has initial value"); + } + if (fixed_regs[reg_number] == 0 + && function_defined && top_level) + error ("global register variable follows a function definition"); + if (TREE_THIS_VOLATILE (decl)) + warning ("volatile register variables don't work as you might wish"); + + /* If the user specified one of the eliminables registers here, + e.g., FRAME_POINTER_REGNUM, we don't want to get this variable + confused with that register and be eliminated. Although this + usage is somewhat suspect, we nevertheless use the following + kludge to avoid setting DECL_RTL to frame_pointer_rtx. */ + + DECL_RTL (decl) + = gen_rtx (REG, DECL_MODE (decl), FIRST_PSEUDO_REGISTER); + REGNO (DECL_RTL (decl)) = reg_number; + REG_USERVAR_P (DECL_RTL (decl)) = 1; + + if (top_level) + { + /* Make this register global, so not usable for anything + else. */ + nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl)); + while (nregs > 0) + globalize_reg (reg_number + --nregs); + } + } + /* Specifying a section attribute on an uninitialized variable does not + (and cannot) cause it to be put in the given section. The linker + can only put initialized objects in specific sections, everything + else goes in bss for the linker to sort out later (otherwise the + linker would give a duplicate definition error for each compilation + unit that behaved thusly). So warn the user. */ + else if (TREE_CODE (decl) == VAR_DECL + && DECL_SECTION_NAME (decl) != NULL_TREE + && DECL_INITIAL (decl) == NULL_TREE + && DECL_COMMON (decl) + && ! flag_no_common) + { + warning_with_decl (decl, + "section attribute ignored for uninitialized variable `%s'"); + /* Remove the section name so subsequent declarations won't see it. + We are ignoring it, remember. */ + DECL_SECTION_NAME (decl) = NULL_TREE; + } + + /* Now handle ordinary static variables and functions (in memory). + Also handle vars declared register invalidly. */ + if (DECL_RTL (decl) == 0) + { + /* Can't use just the variable's own name for a variable + whose scope is less than the whole file. + Concatenate a distinguishing number. */ + if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0) + { + char *label; + + ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno); + name = obstack_copy0 (saveable_obstack, label, strlen (label)); + var_labelno++; + } + + if (name == 0) + abort (); + + DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), + gen_rtx (SYMBOL_REF, Pmode, name)); + + /* If this variable is to be treated as volatile, show its + tree node has side effects. If it has side effects, either + because of this test or from TREE_THIS_VOLATILE also + being set, show the MEM is volatile. */ + if (flag_volatile_global && TREE_CODE (decl) == VAR_DECL + && TREE_PUBLIC (decl)) + TREE_SIDE_EFFECTS (decl) = 1; + if (TREE_SIDE_EFFECTS (decl)) + MEM_VOLATILE_P (DECL_RTL (decl)) = 1; + + if (TREE_READONLY (decl)) + RTX_UNCHANGING_P (DECL_RTL (decl)) = 1; + MEM_IN_STRUCT_P (DECL_RTL (decl)) + = AGGREGATE_TYPE_P (TREE_TYPE (decl)); + + /* Optionally set flags or add text to the name to record information + such as that it is a function name. + If the name is changed, the macro ASM_OUTPUT_LABELREF + will have to know how to strip this information. */ +#ifdef ENCODE_SECTION_INFO + ENCODE_SECTION_INFO (decl); +#endif + } + } + /* If the old RTL had the wrong mode, fix the mode. */ + else if (GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl)) + { + rtx rtl = DECL_RTL (decl); + PUT_MODE (rtl, DECL_MODE (decl)); + } +} + +/* Make the rtl for variable VAR be volatile. + Use this only for static variables. */ + +void +make_var_volatile (var) + tree var; +{ + if (GET_CODE (DECL_RTL (var)) != MEM) + abort (); + + MEM_VOLATILE_P (DECL_RTL (var)) = 1; +} + +/* Output alignment directive to align for constant expression EXP. */ + +void +assemble_constant_align (exp) + tree exp; +{ + int align; + + /* Align the location counter as required by EXP's data type. */ + align = TYPE_ALIGN (TREE_TYPE (exp)); +#ifdef CONSTANT_ALIGNMENT + align = CONSTANT_ALIGNMENT (exp, align); +#endif + + if (align > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); +} + +/* Output a string of literal assembler code + for an `asm' keyword used between functions. */ + +void +assemble_asm (string) + tree string; +{ + if (output_bytecode) + { + error ("asm statements not allowed in interpreter"); + return; + } + + app_enable (); + + if (TREE_CODE (string) == ADDR_EXPR) + string = TREE_OPERAND (string, 0); + + fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string)); +} + +#if 0 /* This should no longer be needed, because + flag_gnu_linker should be 0 on these systems, + which should prevent any output + if ASM_OUTPUT_CONSTRUCTOR and ASM_OUTPUT_DESTRUCTOR are absent. */ +#if !(defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER)) +#ifndef ASM_OUTPUT_CONSTRUCTOR +#define ASM_OUTPUT_CONSTRUCTOR(file, name) +#endif +#ifndef ASM_OUTPUT_DESTRUCTOR +#define ASM_OUTPUT_DESTRUCTOR(file, name) +#endif +#endif +#endif /* 0 */ + +/* Record an element in the table of global destructors. + How this is done depends on what sort of assembler and linker + are in use. + + NAME should be the name of a global function to be called + at exit time. This name is output using assemble_name. */ + +void +assemble_destructor (name) + char *name; +{ +#ifdef ASM_OUTPUT_DESTRUCTOR + ASM_OUTPUT_DESTRUCTOR (asm_out_file, name); +#else + if (flag_gnu_linker) + { + /* Now tell GNU LD that this is part of the static destructor set. */ + /* This code works for any machine provided you use GNU as/ld. */ + fprintf (asm_out_file, "%s \"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); + } +#endif +} + +/* Likewise for global constructors. */ + +void +assemble_constructor (name) + char *name; +{ +#ifdef ASM_OUTPUT_CONSTRUCTOR + ASM_OUTPUT_CONSTRUCTOR (asm_out_file, name); +#else + if (flag_gnu_linker) + { + /* Now tell GNU LD that this is part of the static constructor set. */ + /* This code works for any machine provided you use GNU as/ld. */ + fprintf (asm_out_file, "%s \"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); + } +#endif +} + +/* Likewise for entries we want to record for garbage collection. + Garbage collection is still under development. */ + +void +assemble_gc_entry (name) + char *name; +{ +#ifdef ASM_OUTPUT_GC_ENTRY + ASM_OUTPUT_GC_ENTRY (asm_out_file, name); +#else + if (flag_gnu_linker) + { + /* Now tell GNU LD that this is part of the static constructor set. */ + fprintf (asm_out_file, "%s \"___PTR_LIST__\",22,0,0,", ASM_STABS_OP); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); + } +#endif +} + +/* Output assembler code for the constant pool of a function and associated + with defining the name of the function. DECL describes the function. + NAME is the function's name. For the constant pool, we use the current + constant pool data. */ + +void +assemble_start_function (decl, fnname) + tree decl; + char *fnname; +{ + int align; + + /* The following code does not need preprocessing in the assembler. */ + + app_disable (); + + output_constant_pool (fnname, decl); + + function_section (decl); + + /* Tell assembler to move to target machine's alignment for functions. */ + align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align > 0) + { + if (output_bytecode) + BC_OUTPUT_ALIGN (asm_out_file, align); + else + ASM_OUTPUT_ALIGN (asm_out_file, align); + } + +#ifdef ASM_OUTPUT_FUNCTION_PREFIX + ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname); +#endif + +#ifdef SDB_DEBUGGING_INFO + /* Output SDB definition of the function. */ + if (write_symbols == SDB_DEBUG) + sdbout_mark_begin_function (); +#endif + +#ifdef DBX_DEBUGGING_INFO + /* Output DBX definition of the function. */ + if (write_symbols == DBX_DEBUG) + dbxout_begin_function (decl); +#endif + + /* Make function name accessible from other files, if appropriate. */ + + if (TREE_PUBLIC (decl)) + { + if (!first_global_object_name) + { + char *p; + + STRIP_NAME_ENCODING (p, fnname); + first_global_object_name = permalloc (strlen (p) + 1); + strcpy (first_global_object_name, p); + } + +#ifdef ASM_WEAKEN_LABEL + if (DECL_WEAK (decl)) + ASM_WEAKEN_LABEL (asm_out_file, fnname); + else +#endif + if (output_bytecode) + BC_GLOBALIZE_LABEL (asm_out_file, fnname); + else + ASM_GLOBALIZE_LABEL (asm_out_file, fnname); + } + + /* Do any machine/system dependent processing of the function name */ +#ifdef ASM_DECLARE_FUNCTION_NAME + ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl); +#else + /* Standard thing is just output label for the function. */ + if (output_bytecode) + BC_OUTPUT_LABEL (asm_out_file, fnname); + else + ASM_OUTPUT_LABEL (asm_out_file, fnname); +#endif /* ASM_DECLARE_FUNCTION_NAME */ +} + +/* Output assembler code associated with defining the size of the + function. DECL describes the function. NAME is the function's name. */ + +void +assemble_end_function (decl, fnname) + tree decl; + char *fnname; +{ +#ifdef ASM_DECLARE_FUNCTION_SIZE + ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl); +#endif +} + +/* Assemble code to leave SIZE bytes of zeros. */ + +void +assemble_zeros (size) + int size; +{ + if (output_bytecode) + { + bc_emit_const_skip (size); + return; + } + +#ifdef ASM_NO_SKIP_IN_TEXT + /* The `space' pseudo in the text section outputs nop insns rather than 0s, + so we must output 0s explicitly in the text section. */ + if (ASM_NO_SKIP_IN_TEXT && in_text_section ()) + { + int i; + + for (i = 0; i < size - 20; i += 20) + { +#ifdef ASM_BYTE_OP + fprintf (asm_out_file, + "%s 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n", ASM_BYTE_OP); +#else + fprintf (asm_out_file, + "\tbyte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n"); +#endif + } + if (i < size) + { +#ifdef ASM_BYTE_OP + fprintf (asm_out_file, "%s 0", ASM_BYTE_OP); +#else + fprintf (asm_out_file, "\tbyte 0"); +#endif + i++; + for (; i < size; i++) + fprintf (asm_out_file, ",0"); + fprintf (asm_out_file, "\n"); + } + } + else +#endif + if (size > 0) + { + if (output_bytecode) + BC_OUTPUT_SKIP (asm_out_file, size); + else + ASM_OUTPUT_SKIP (asm_out_file, size); + } +} + +/* Assemble an alignment pseudo op for an ALIGN-bit boundary. */ + +void +assemble_align (align) + int align; +{ + if (align > BITS_PER_UNIT) + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); +} + +/* Assemble a string constant with the specified C string as contents. */ + +void +assemble_string (p, size) + char *p; + int size; +{ + register int i; + int pos = 0; + int maximum = 2000; + + if (output_bytecode) + { + bc_emit (p, size); + return; + } + + /* If the string is very long, split it up. */ + + while (pos < size) + { + int thissize = size - pos; + if (thissize > maximum) + thissize = maximum; + + if (output_bytecode) + bc_output_ascii (asm_out_file, p, thissize); + else + { + ASM_OUTPUT_ASCII (asm_out_file, p, thissize); + } + + pos += thissize; + p += thissize; + } +} + +static void +bc_output_ascii (file, p, size) + FILE *file; + char *p; + int size; +{ + BC_OUTPUT_ASCII (file, p, size); +} + +/* Assemble everything that is needed for a variable or function declaration. + Not used for automatic variables, and not used for function definitions. + Should not be called for variables of incomplete structure type. + + TOP_LEVEL is nonzero if this variable has file scope. + AT_END is nonzero if this is the special handling, at end of compilation, + to define things that have had only tentative definitions. + DONT_OUTPUT_DATA if nonzero means don't actually output the + initial value (that will be done by the caller). */ + +void +assemble_variable (decl, top_level, at_end, dont_output_data) + tree decl; + int top_level; + int at_end; + int dont_output_data; +{ + register char *name; + int align; + tree size_tree; + int reloc = 0; + enum in_section saved_in_section; + + last_assemble_variable_decl = 0; + + if (output_bytecode) + return; + + if (GET_CODE (DECL_RTL (decl)) == REG) + { + /* Do output symbol info for global register variables, but do nothing + else for them. */ + + if (TREE_ASM_WRITTEN (decl)) + return; + TREE_ASM_WRITTEN (decl) = 1; + + if (!output_bytecode) + { +#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO) + /* File-scope global variables are output here. */ + if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG) + && top_level) + dbxout_symbol (decl, 0); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && top_level + /* Leave initialized global vars for end of compilation; + see comment in compile_file. */ + && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) + sdbout_symbol (decl, 0); +#endif + } + + /* Don't output any DWARF debugging information for variables here. + In the case of local variables, the information for them is output + when we do our recursive traversal of the tree representation for + the entire containing function. In the case of file-scope variables, + we output information for all of them at the very end of compilation + while we are doing our final traversal of the chain of file-scope + declarations. */ + + return; + } + + /* Normally no need to say anything here for external references, + since assemble_external is called by the language-specific code + when a declaration is first seen. */ + + if (DECL_EXTERNAL (decl)) + return; + + /* Output no assembler code for a function declaration. + Only definitions of functions output anything. */ + + if (TREE_CODE (decl) == FUNCTION_DECL) + return; + + /* If type was incomplete when the variable was declared, + see if it is complete now. */ + + if (DECL_SIZE (decl) == 0) + layout_decl (decl, 0); + + /* Still incomplete => don't allocate it; treat the tentative defn + (which is what it must have been) as an `extern' reference. */ + + if (!dont_output_data && DECL_SIZE (decl) == 0) + { + error_with_file_and_line (DECL_SOURCE_FILE (decl), + DECL_SOURCE_LINE (decl), + "storage size of `%s' isn't known", + IDENTIFIER_POINTER (DECL_NAME (decl))); + TREE_ASM_WRITTEN (decl) = 1; + return; + } + + /* The first declaration of a variable that comes through this function + decides whether it is global (in C, has external linkage) + or local (in C, has internal linkage). So do nothing more + if this function has already run. */ + + if (TREE_ASM_WRITTEN (decl)) + return; + + TREE_ASM_WRITTEN (decl) = 1; + + app_disable (); + + if (! dont_output_data) + { + if (TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST) + goto finish; + + /* This is better than explicit arithmetic, since it avoids overflow. */ + size_tree = size_binop (CEIL_DIV_EXPR, + DECL_SIZE (decl), size_int (BITS_PER_UNIT)); + + if (TREE_INT_CST_HIGH (size_tree) != 0) + { + error_with_decl (decl, "size of variable `%s' is too large"); + goto finish; + } + } + + name = XSTR (XEXP (DECL_RTL (decl), 0), 0); + + /* Handle uninitialized definitions. */ + + /* ANSI specifies that a tentative definition which is not merged with + a non-tentative definition behaves exactly like a definition with an + initializer equal to zero. (Section 3.7.2) + -fno-common gives strict ANSI behavior. Usually you don't want it. + This matters only for variables with external linkage. */ + if ((! flag_no_common || ! TREE_PUBLIC (decl)) + && DECL_COMMON (decl) + && ! dont_output_data + && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node)) + { + int size = TREE_INT_CST_LOW (size_tree); + int rounded = size; + + if (TREE_INT_CST_HIGH (size_tree) != 0) + error_with_decl (decl, "size of variable `%s' is too large"); + /* Don't allocate zero bytes of common, + since that means "undefined external" in the linker. */ + if (size == 0) rounded = 1; + /* Round size up to multiple of BIGGEST_ALIGNMENT bits + so that each uninitialized object starts on such a boundary. */ + rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1; + rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) + * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + +#ifdef DBX_DEBUGGING_INFO + /* File-scope global variables are output here. */ + if (write_symbols == DBX_DEBUG && top_level) + dbxout_symbol (decl, 0); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && top_level + /* Leave initialized global vars for end of compilation; + see comment in compile_file. */ + && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) + sdbout_symbol (decl, 0); +#endif + + /* Don't output any DWARF debugging information for variables here. + In the case of local variables, the information for them is output + when we do our recursive traversal of the tree representation for + the entire containing function. In the case of file-scope variables, + we output information for all of them at the very end of compilation + while we are doing our final traversal of the chain of file-scope + declarations. */ + +#if 0 + if (flag_shared_data) + data_section (); +#endif + if (TREE_PUBLIC (decl)) + { +#ifdef ASM_OUTPUT_SHARED_COMMON + if (flag_shared_data) + ASM_OUTPUT_SHARED_COMMON (asm_out_file, name, size, rounded); + else +#endif + if (output_bytecode) + { + BC_OUTPUT_COMMON (asm_out_file, name, size, rounded); + } + else + { +#ifdef ASM_OUTPUT_ALIGNED_COMMON + ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size, + DECL_ALIGN (decl)); +#else + ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded); +#endif + } + } + else + { +#ifdef ASM_OUTPUT_SHARED_LOCAL + if (flag_shared_data) + ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded); + else +#endif + if (output_bytecode) + { + BC_OUTPUT_LOCAL (asm_out_file, name, size, rounded); + } + else + { +#ifdef ASM_OUTPUT_ALIGNED_LOCAL + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, + DECL_ALIGN (decl)); +#else + ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); +#endif + } + } + goto finish; + } + + /* Handle initialized definitions. */ + + /* First make the assembler name(s) global if appropriate. */ + if (TREE_PUBLIC (decl) && DECL_NAME (decl)) + { + if (!first_global_object_name) + { + char *p; + + STRIP_NAME_ENCODING (p, name); + first_global_object_name = permalloc (strlen (p) + 1); + strcpy (first_global_object_name, p); + } + +#ifdef ASM_WEAKEN_LABEL + if (DECL_WEAK (decl)) + ASM_WEAKEN_LABEL (asm_out_file, name); + else +#endif + ASM_GLOBALIZE_LABEL (asm_out_file, name); + } +#if 0 + for (d = equivalents; d; d = TREE_CHAIN (d)) + { + tree e = TREE_VALUE (d); + if (TREE_PUBLIC (e) && DECL_NAME (e)) + ASM_GLOBALIZE_LABEL (asm_out_file, + XSTR (XEXP (DECL_RTL (e), 0), 0)); + } +#endif + + /* Output any data that we will need to use the address of. */ + if (DECL_INITIAL (decl) == error_mark_node) + reloc = contains_pointers_p (TREE_TYPE (decl)); + else if (DECL_INITIAL (decl)) + reloc = output_addressed_constants (DECL_INITIAL (decl)); + + /* Switch to the proper section for this data. */ + if (IN_NAMED_SECTION (decl)) + named_section (decl, NULL); + else + { + /* C++ can have const variables that get initialized from constructors, + and thus can not be in a readonly section. We prevent this by + verifying that the initial value is constant for objects put in a + readonly section. + + error_mark_node is used by the C front end to indicate that the + initializer has not been seen yet. In this case, we assume that + the initializer must be constant. */ +#ifdef SELECT_SECTION + SELECT_SECTION (decl, reloc); +#else + if (TREE_READONLY (decl) + && ! TREE_THIS_VOLATILE (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl))) + && ! (flag_pic && reloc)) + readonly_data_section (); + else + data_section (); +#endif + } + + /* dbxout.c needs to know this. */ + if (in_text_section ()) + DECL_IN_TEXT_SECTION (decl) = 1; + + /* Record current section so we can restore it if dbxout.c clobbers it. */ + saved_in_section = in_section; + + /* Output the dbx info now that we have chosen the section. */ + +#ifdef DBX_DEBUGGING_INFO + /* File-scope global variables are output here. */ + if (write_symbols == DBX_DEBUG && top_level) + dbxout_symbol (decl, 0); +#endif +#ifdef SDB_DEBUGGING_INFO + if (write_symbols == SDB_DEBUG && top_level + /* Leave initialized global vars for end of compilation; + see comment in compile_file. */ + && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0)) + sdbout_symbol (decl, 0); +#endif + + /* Don't output any DWARF debugging information for variables here. + In the case of local variables, the information for them is output + when we do our recursive traversal of the tree representation for + the entire containing function. In the case of file-scope variables, + we output information for all of them at the very end of compilation + while we are doing our final traversal of the chain of file-scope + declarations. */ + + /* If the debugging output changed sections, reselect the section + that's supposed to be selected. */ + if (in_section != saved_in_section) + { + /* Switch to the proper section for this data. */ +#ifdef SELECT_SECTION + SELECT_SECTION (decl, reloc); +#else + if (TREE_READONLY (decl) + && ! TREE_THIS_VOLATILE (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl))) + && ! (flag_pic && reloc)) + readonly_data_section (); + else + data_section (); +#endif + } + + /* Compute and output the alignment of this data. */ + + align = DECL_ALIGN (decl); + /* In the case for initialing an array whose length isn't specified, + where we have not yet been able to do the layout, + figure out the proper alignment now. */ + if (dont_output_data && DECL_SIZE (decl) == 0 + && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + align = MAX (align, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl)))); + + /* Some object file formats have a maximum alignment which they support. + In particular, a.out format supports a maximum alignment of 4. */ +#ifndef MAX_OFILE_ALIGNMENT +#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT +#endif + if (align > MAX_OFILE_ALIGNMENT) + { + warning_with_decl (decl, + "alignment of `%s' is greater than maximum object file alignment"); + align = MAX_OFILE_ALIGNMENT; + } +#ifdef DATA_ALIGNMENT + /* On some machines, it is good to increase alignment sometimes. */ + align = DATA_ALIGNMENT (TREE_TYPE (decl), align); +#endif +#ifdef CONSTANT_ALIGNMENT + if (DECL_INITIAL (decl)) + align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align); +#endif + + /* Reset the alignment in case we have made it tighter, so we can benefit + from it in get_pointer_alignment. */ + DECL_ALIGN (decl) = align; + + if (align > BITS_PER_UNIT) + { + if (output_bytecode) + BC_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + else + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + } + + /* Do any machine/system dependent processing of the object. */ +#ifdef ASM_DECLARE_OBJECT_NAME + last_assemble_variable_decl = decl; + ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl); +#else + /* Standard thing is just output label for the object. */ + if (output_bytecode) + BC_OUTPUT_LABEL (asm_out_file, name); + else + ASM_OUTPUT_LABEL (asm_out_file, name); +#endif /* ASM_DECLARE_OBJECT_NAME */ + + if (!dont_output_data) + { + if (DECL_INITIAL (decl)) + /* Output the actual data. */ + output_constant (DECL_INITIAL (decl), TREE_INT_CST_LOW (size_tree)); + else + /* Leave space for it. */ + assemble_zeros (TREE_INT_CST_LOW (size_tree)); + } + + finish: +#ifdef XCOFF_DEBUGGING_INFO + /* Unfortunately, the IBM assembler cannot handle stabx before the actual + declaration. When something like ".stabx "aa:S-2",aa,133,0" is emitted + and `aa' hasn't been output yet, the assembler generates a stab entry with + a value of zero, in addition to creating an unnecessary external entry + for `aa'. Hence, we must postpone dbxout_symbol to here at the end. */ + + /* File-scope global variables are output here. */ + if (write_symbols == XCOFF_DEBUG && top_level) + { + saved_in_section = in_section; + + dbxout_symbol (decl, 0); + + if (in_section != saved_in_section) + { + /* Switch to the proper section for this data. */ +#ifdef SELECT_SECTION + SELECT_SECTION (decl, reloc); +#else + if (TREE_READONLY (decl) + && ! TREE_THIS_VOLATILE (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl))) + && ! (flag_pic && reloc)) + readonly_data_section (); + else + data_section (); +#endif + } + } +#else + /* There must be a statement after a label. */ + ; +#endif +} + +/* Return 1 if type TYPE contains any pointers. */ + +static int +contains_pointers_p (type) + tree type; +{ + switch (TREE_CODE (type)) + { + case POINTER_TYPE: + case REFERENCE_TYPE: + /* I'm not sure whether OFFSET_TYPE needs this treatment, + so I'll play safe and return 1. */ + case OFFSET_TYPE: + return 1; + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + tree fields; + /* For a type that has fields, see if the fields have pointers. */ + for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields)) + if (TREE_CODE (fields) == FIELD_DECL + && contains_pointers_p (TREE_TYPE (fields))) + return 1; + return 0; + } + + case ARRAY_TYPE: + /* An array type contains pointers if its element type does. */ + return contains_pointers_p (TREE_TYPE (type)); + + default: + return 0; + } +} + +/* Output text storage for constructor CONSTR. */ + +void +bc_output_constructor (constr, size) + tree constr; + int size; +{ + int i; + + /* Must always be a literal; non-literal constructors are handled + differently. */ + + if (!TREE_CONSTANT (constr)) + abort (); + + /* Always const */ + text_section (); + + /* Align */ + for (i = 0; TYPE_ALIGN (constr) >= BITS_PER_UNIT << (i + 1); i++) + ; + + if (i > 0) + BC_OUTPUT_ALIGN (asm_out_file, i); + + /* Output data */ + output_constant (constr, size); +} + +/* Create storage for constructor CONSTR. */ + +void +bc_output_data_constructor (constr) + tree constr; +{ + int i; + + /* Put in data section */ + data_section (); + + /* Align */ + for (i = 0; TYPE_ALIGN (constr) >= BITS_PER_UNIT << (i + 1); i++); + if (i > 0) + BC_OUTPUT_ALIGN (asm_out_file, i); + + /* The constructor is filled in at runtime. */ + BC_OUTPUT_SKIP (asm_out_file, int_size_in_bytes (TREE_TYPE (constr))); +} + +/* Output something to declare an external symbol to the assembler. + (Most assemblers don't need this, so we normally output nothing.) + Do nothing if DECL is not external. */ + +void +assemble_external (decl) + tree decl; +{ + if (output_bytecode) + return; + +#ifdef ASM_OUTPUT_EXTERNAL + if (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd' + && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl)) + { + rtx rtl = DECL_RTL (decl); + + if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF + && ! SYMBOL_REF_USED (XEXP (rtl, 0))) + { + /* Some systems do require some output. */ + SYMBOL_REF_USED (XEXP (rtl, 0)) = 1; + ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0)); + } + } +#endif +} + +/* Similar, for calling a library function FUN. */ + +void +assemble_external_libcall (fun) + rtx fun; +{ +#ifdef ASM_OUTPUT_EXTERNAL_LIBCALL + if (!output_bytecode) + { + /* Declare library function name external when first used, if nec. */ + if (! SYMBOL_REF_USED (fun)) + { + SYMBOL_REF_USED (fun) = 1; + ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun); + } + } +#endif +} + +/* Declare the label NAME global. */ + +void +assemble_global (name) + char *name; +{ + ASM_GLOBALIZE_LABEL (asm_out_file, name); +} + +/* Assemble a label named NAME. */ + +void +assemble_label (name) + char *name; +{ + if (output_bytecode) + BC_OUTPUT_LABEL (asm_out_file, name); + else + ASM_OUTPUT_LABEL (asm_out_file, name); +} + +/* Output to FILE a reference to the assembler name of a C-level name NAME. + If NAME starts with a *, the rest of NAME is output verbatim. + Otherwise NAME is transformed in an implementation-defined way + (usually by the addition of an underscore). + Many macros in the tm file are defined to call this function. */ + +void +assemble_name (file, name) + FILE *file; + char *name; +{ + char *real_name; + int save_warn_id_clash = warn_id_clash; + + STRIP_NAME_ENCODING (real_name, name); + + /* Don't warn about an identifier name length clash on this name, since + it can be a user symbol suffixed by a number. */ + warn_id_clash = 0; + TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1; + warn_id_clash = save_warn_id_clash; + + if (name[0] == '*') + { + if (output_bytecode) + bc_emit_labelref (name, 0); + else + fputs (&name[1], file); + } + else + { + if (output_bytecode) + BC_OUTPUT_LABELREF (file, name); + else + ASM_OUTPUT_LABELREF (file, name); + } +} + +/* Allocate SIZE bytes writable static space with a gensym name + and return an RTX to refer to its address. */ + +rtx +assemble_static_space (size) + int size; +{ + char name[12]; + char *namestring; + rtx x; + /* Round size up to multiple of BIGGEST_ALIGNMENT bits + so that each uninitialized object starts on such a boundary. */ + int rounded = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) + / (BIGGEST_ALIGNMENT / BITS_PER_UNIT) + * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); + +#if 0 + if (flag_shared_data) + data_section (); +#endif + + ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno); + ++const_labelno; + + namestring = (char *) obstack_alloc (saveable_obstack, + strlen (name) + 2); + strcpy (namestring, name); + + if (output_bytecode) + x = bc_gen_rtx (namestring, 0, (struct bc_label *) 0); + else + x = gen_rtx (SYMBOL_REF, Pmode, namestring); + + if (output_bytecode) + { + BC_OUTPUT_LOCAL (asm_out_file, name, size, rounded); + } + else + { +#ifdef ASM_OUTPUT_ALIGNED_LOCAL + ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT); +#else + ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); +#endif + } + return x; +} + +/* Assemble the static constant template for function entry trampolines. + This is done at most once per compilation. + Returns an RTX for the address of the template. */ + +rtx +assemble_trampoline_template () +{ + char label[256]; + char *name; + int align; + + /* Shouldn't get here */ + if (output_bytecode) + abort (); + + /* By default, put trampoline templates in read-only data section. */ + +#ifdef TRAMPOLINE_SECTION + TRAMPOLINE_SECTION (); +#else + readonly_data_section (); +#endif + + /* Write the assembler code to define one. */ + align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT); + if (align > 0) + ASM_OUTPUT_ALIGN (asm_out_file, align); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LTRAMP", 0); + TRAMPOLINE_TEMPLATE (asm_out_file); + + /* Record the rtl to refer to it. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0); + name + = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); + return gen_rtx (SYMBOL_REF, Pmode, name); +} + +/* Assemble the integer constant X into an object of SIZE bytes. + X must be either a CONST_INT or CONST_DOUBLE. + + Return 1 if we were able to output the constant, otherwise 0. If FORCE is + non-zero, abort if we can't output the constant. */ + +int +assemble_integer (x, size, force) + rtx x; + int size; + int force; +{ + /* First try to use the standard 1, 2, 4, 8, and 16 byte + ASM_OUTPUT... macros. */ + + switch (size) + { +#ifdef ASM_OUTPUT_CHAR + case 1: + ASM_OUTPUT_CHAR (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_SHORT + case 2: + ASM_OUTPUT_SHORT (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_INT + case 4: + ASM_OUTPUT_INT (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_DOUBLE_INT + case 8: + ASM_OUTPUT_DOUBLE_INT (asm_out_file, x); + return 1; +#endif + +#ifdef ASM_OUTPUT_QUADRUPLE_INT + case 16: + ASM_OUTPUT_QUADRUPLE_INT (asm_out_file, x); + return 1; +#endif + } + + /* If we couldn't do it that way, there are two other possibilities: First, + if the machine can output an explicit byte and this is a 1 byte constant, + we can use ASM_OUTPUT_BYTE. */ + +#ifdef ASM_OUTPUT_BYTE + if (size == 1 && GET_CODE (x) == CONST_INT) + { + ASM_OUTPUT_BYTE (asm_out_file, INTVAL (x)); + return 1; + } +#endif + + /* Finally, if SIZE is larger than a single word, try to output the constant + one word at a time. */ + + if (size > UNITS_PER_WORD) + { + int i; + enum machine_mode mode + = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0); + rtx word; + + for (i = 0; i < size / UNITS_PER_WORD; i++) + { + word = operand_subword (x, i, 0, mode); + + if (word == 0) + break; + + if (! assemble_integer (word, UNITS_PER_WORD, 0)) + break; + } + + if (i == size / UNITS_PER_WORD) + return 1; + /* If we output at least one word and then could not finish, + there is no valid way to continue. */ + if (i > 0) + abort (); + } + + if (force) + abort (); + + return 0; +} + +/* Assemble the floating-point constant D into an object of size MODE. */ + +void +assemble_real (d, mode) + REAL_VALUE_TYPE d; + enum machine_mode mode; +{ + jmp_buf output_constant_handler; + + if (setjmp (output_constant_handler)) + { + error ("floating point trap outputting a constant"); +#ifdef REAL_IS_NOT_DOUBLE + bzero ((char *) &d, sizeof d); + d = dconst0; +#else + d = 0; +#endif + } + + set_float_handler (output_constant_handler); + + switch (mode) + { +#ifdef ASM_OUTPUT_BYTE_FLOAT + case QFmode: + ASM_OUTPUT_BYTE_FLOAT (asm_out_file, d); + break; +#endif +#ifdef ASM_OUTPUT_SHORT_FLOAT + case HFmode: + ASM_OUTPUT_SHORT_FLOAT (asm_out_file, d); + break; +#endif +#ifdef ASM_OUTPUT_THREE_QUARTER_FLOAT + case TQFmode: + ASM_OUTPUT_THREE_QUARTER_FLOAT (asm_out_file, d); + break; +#endif +#ifdef ASM_OUTPUT_FLOAT + case SFmode: + ASM_OUTPUT_FLOAT (asm_out_file, d); + break; +#endif + +#ifdef ASM_OUTPUT_DOUBLE + case DFmode: + ASM_OUTPUT_DOUBLE (asm_out_file, d); + break; +#endif + +#ifdef ASM_OUTPUT_LONG_DOUBLE + case XFmode: + case TFmode: + ASM_OUTPUT_LONG_DOUBLE (asm_out_file, d); + break; +#endif + + default: + abort (); + } + + set_float_handler (NULL_PTR); +} + +/* Here we combine duplicate floating constants to make + CONST_DOUBLE rtx's, and force those out to memory when necessary. */ + +/* Chain of all CONST_DOUBLE rtx's constructed for the current function. + They are chained through the CONST_DOUBLE_CHAIN. + A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain. + In that case, CONST_DOUBLE_MEM is either a MEM, + or const0_rtx if no MEM has been made for this CONST_DOUBLE yet. + + (CONST_DOUBLE_MEM is used only for top-level functions. + See force_const_mem for explanation.) */ + +static rtx const_double_chain; + +/* Return a CONST_DOUBLE or CONST_INT for a value specified as a pair of ints. + For an integer, I0 is the low-order word and I1 is the high-order word. + For a real number, I0 is the word with the low address + and I1 is the word with the high address. */ + +rtx +immed_double_const (i0, i1, mode) + HOST_WIDE_INT i0, i1; + enum machine_mode mode; +{ + register rtx r; + int in_current_obstack; + + if (GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT) + { + /* We clear out all bits that don't belong in MODE, unless they and our + sign bit are all one. So we get either a reasonable negative value + or a reasonable unsigned value for this mode. */ + int width = GET_MODE_BITSIZE (mode); + if (width < HOST_BITS_PER_WIDE_INT + && ((i0 & ((HOST_WIDE_INT) (-1) << (width - 1))) + != ((HOST_WIDE_INT) (-1) << (width - 1)))) + i0 &= ((HOST_WIDE_INT) 1 << width) - 1, i1 = 0; + else if (width == HOST_BITS_PER_WIDE_INT + && ! (i1 == ~0 && i0 < 0)) + i1 = 0; + else if (width > 2 * HOST_BITS_PER_WIDE_INT) + /* We cannot represent this value as a constant. */ + abort (); + + /* If this would be an entire word for the target, but is not for + the host, then sign-extend on the host so that the number will look + the same way on the host that it would on the target. + + For example, when building a 64 bit alpha hosted 32 bit sparc + targeted compiler, then we want the 32 bit unsigned value -1 to be + represented as a 64 bit value -1, and not as 0x00000000ffffffff. + The later confuses the sparc backend. */ + + if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width + && (i0 & ((HOST_WIDE_INT) 1 << (width - 1)))) + i0 |= ((HOST_WIDE_INT) (-1) << width); + + /* If MODE fits within HOST_BITS_PER_WIDE_INT, always use a CONST_INT. + + ??? Strictly speaking, this is wrong if we create a CONST_INT + for a large unsigned constant with the size of MODE being + HOST_BITS_PER_WIDE_INT and later try to interpret that constant in a + wider mode. In that case we will mis-interpret it as a negative + number. + + Unfortunately, the only alternative is to make a CONST_DOUBLE + for any constant in any mode if it is an unsigned constant larger + than the maximum signed integer in an int on the host. However, + doing this will break everyone that always expects to see a CONST_INT + for SImode and smaller. + + We have always been making CONST_INTs in this case, so nothing new + is being broken. */ + + if (width <= HOST_BITS_PER_WIDE_INT) + i1 = (i0 < 0) ? ~0 : 0; + + /* If this integer fits in one word, return a CONST_INT. */ + if ((i1 == 0 && i0 >= 0) + || (i1 == ~0 && i0 < 0)) + return GEN_INT (i0); + + /* We use VOIDmode for integers. */ + mode = VOIDmode; + } + + /* Search the chain for an existing CONST_DOUBLE with the right value. + If one is found, return it. */ + + for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r)) + if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1 + && GET_MODE (r) == mode) + return r; + + /* No; make a new one and add it to the chain. + + We may be called by an optimizer which may be discarding any memory + allocated during its processing (such as combine and loop). However, + we will be leaving this constant on the chain, so we cannot tolerate + freed memory. So switch to saveable_obstack for this allocation + and then switch back if we were in current_obstack. */ + + push_obstacks_nochange (); + rtl_in_saveable_obstack (); + r = gen_rtx (CONST_DOUBLE, mode, 0, i0, i1); + pop_obstacks (); + + /* Don't touch const_double_chain in nested function; see force_const_mem. + Also, don't touch it if not inside any function. */ + if (outer_function_chain == 0 && current_function_decl != 0) + { + CONST_DOUBLE_CHAIN (r) = const_double_chain; + const_double_chain = r; + } + + /* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain. + Actual use of mem-slot is only through force_const_mem. */ + + CONST_DOUBLE_MEM (r) = const0_rtx; + + return r; +} + +/* Return a CONST_DOUBLE for a specified `double' value + and machine mode. */ + +rtx +immed_real_const_1 (d, mode) + REAL_VALUE_TYPE d; + enum machine_mode mode; +{ + union real_extract u; + register rtx r; + int in_current_obstack; + + /* Get the desired `double' value as a sequence of ints + since that is how they are stored in a CONST_DOUBLE. */ + + u.d = d; + + /* Detect special cases. */ + + /* Avoid REAL_VALUES_EQUAL here in order to distinguish minus zero. */ + if (!bcmp ((char *) &dconst0, (char *) &d, sizeof d)) + return CONST0_RTX (mode); + /* Check for NaN first, because some ports (specifically the i386) do not + emit correct ieee-fp code by default, and thus will generate a core + dump here if we pass a NaN to REAL_VALUES_EQUAL and if REAL_VALUES_EQUAL + does a floating point comparison. */ + else if (! REAL_VALUE_ISNAN (d) && REAL_VALUES_EQUAL (dconst1, d)) + return CONST1_RTX (mode); + + if (sizeof u == 2 * sizeof (HOST_WIDE_INT)) + return immed_double_const (u.i[0], u.i[1], mode); + + /* The rest of this function handles the case where + a float value requires more than 2 ints of space. + It will be deleted as dead code on machines that don't need it. */ + + /* Search the chain for an existing CONST_DOUBLE with the right value. + If one is found, return it. */ + + for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r)) + if (! bcmp ((char *) &CONST_DOUBLE_LOW (r), (char *) &u, sizeof u) + && GET_MODE (r) == mode) + return r; + + /* No; make a new one and add it to the chain. + + We may be called by an optimizer which may be discarding any memory + allocated during its processing (such as combine and loop). However, + we will be leaving this constant on the chain, so we cannot tolerate + freed memory. So switch to saveable_obstack for this allocation + and then switch back if we were in current_obstack. */ + + push_obstacks_nochange (); + rtl_in_saveable_obstack (); + r = rtx_alloc (CONST_DOUBLE); + PUT_MODE (r, mode); + bcopy ((char *) &u, (char *) &CONST_DOUBLE_LOW (r), sizeof u); + pop_obstacks (); + + /* Don't touch const_double_chain in nested function; see force_const_mem. + Also, don't touch it if not inside any function. */ + if (outer_function_chain == 0 && current_function_decl != 0) + { + CONST_DOUBLE_CHAIN (r) = const_double_chain; + const_double_chain = r; + } + + /* Store const0_rtx in CONST_DOUBLE_MEM since this CONST_DOUBLE is on the + chain, but has not been allocated memory. Actual use of CONST_DOUBLE_MEM + is only through force_const_mem. */ + + CONST_DOUBLE_MEM (r) = const0_rtx; + + return r; +} + +/* Return a CONST_DOUBLE rtx for a value specified by EXP, + which must be a REAL_CST tree node. */ + +rtx +immed_real_const (exp) + tree exp; +{ + return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp))); +} + +/* At the end of a function, forget the memory-constants + previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain. + Also clear out real_constant_chain and clear out all the chain-pointers. */ + +void +clear_const_double_mem () +{ + register rtx r, next; + + /* Don't touch CONST_DOUBLE_MEM for nested functions. + See force_const_mem for explanation. */ + if (outer_function_chain != 0) + return; + + for (r = const_double_chain; r; r = next) + { + next = CONST_DOUBLE_CHAIN (r); + CONST_DOUBLE_CHAIN (r) = 0; + CONST_DOUBLE_MEM (r) = cc0_rtx; + } + const_double_chain = 0; +} + +/* Given an expression EXP with a constant value, + reduce it to the sum of an assembler symbol and an integer. + Store them both in the structure *VALUE. + Abort if EXP does not reduce. */ + +struct addr_const +{ + rtx base; + HOST_WIDE_INT offset; +}; + +static void +decode_addr_const (exp, value) + tree exp; + struct addr_const *value; +{ + register tree target = TREE_OPERAND (exp, 0); + register int offset = 0; + register rtx x; + + while (1) + { + if (TREE_CODE (target) == COMPONENT_REF + && (TREE_CODE (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) + == INTEGER_CST)) + { + offset += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) / BITS_PER_UNIT; + target = TREE_OPERAND (target, 0); + } + else if (TREE_CODE (target) == ARRAY_REF) + { + if (TREE_CODE (TREE_OPERAND (target, 1)) != INTEGER_CST + || TREE_CODE (TYPE_SIZE (TREE_TYPE (target))) != INTEGER_CST) + abort (); + offset += ((TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (target))) + * TREE_INT_CST_LOW (TREE_OPERAND (target, 1))) + / BITS_PER_UNIT); + target = TREE_OPERAND (target, 0); + } + else + break; + } + + switch (TREE_CODE (target)) + { + case VAR_DECL: + case FUNCTION_DECL: + x = DECL_RTL (target); + break; + + case LABEL_DECL: + if (output_bytecode) + /* FIXME: this may not be correct, check it */ + x = bc_gen_rtx (TREE_STRING_POINTER (target), 0, (struct bc_label *) 0); + else + x = gen_rtx (MEM, FUNCTION_MODE, + gen_rtx (LABEL_REF, VOIDmode, + label_rtx (TREE_OPERAND (exp, 0)))); + break; + + case REAL_CST: + case STRING_CST: + case COMPLEX_CST: + case CONSTRUCTOR: + x = TREE_CST_RTL (target); + break; + + default: + abort (); + } + + if (!output_bytecode) + { + if (GET_CODE (x) != MEM) + abort (); + x = XEXP (x, 0); + } + + value->base = x; + value->offset = offset; +} + +/* Uniquize all constants that appear in memory. + Each constant in memory thus far output is recorded + in `const_hash_table' with a `struct constant_descriptor' + that contains a polish representation of the value of + the constant. + + We cannot store the trees in the hash table + because the trees may be temporary. */ + +struct constant_descriptor +{ + struct constant_descriptor *next; + char *label; + char contents[1]; +}; + +#define HASHBITS 30 +#define MAX_HASH_TABLE 1009 +static struct constant_descriptor *const_hash_table[MAX_HASH_TABLE]; + +/* Compute a hash code for a constant expression. */ + +static int +const_hash (exp) + tree exp; +{ + register char *p; + register int len, hi, i; + register enum tree_code code = TREE_CODE (exp); + + if (code == INTEGER_CST) + { + p = (char *) &TREE_INT_CST_LOW (exp); + len = 2 * sizeof TREE_INT_CST_LOW (exp); + } + else if (code == REAL_CST) + { + p = (char *) &TREE_REAL_CST (exp); + len = sizeof TREE_REAL_CST (exp); + } + else if (code == STRING_CST) + p = TREE_STRING_POINTER (exp), len = TREE_STRING_LENGTH (exp); + else if (code == COMPLEX_CST) + return const_hash (TREE_REALPART (exp)) * 5 + + const_hash (TREE_IMAGPART (exp)); + else if (code == CONSTRUCTOR && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE) + { + len = int_size_in_bytes (TREE_TYPE (exp)); + p = (char*) alloca (len); + get_set_constructor_bytes (exp, (unsigned char *) p, len); + } + else if (code == CONSTRUCTOR) + { + register tree link; + + /* For record type, include the type in the hashing. + We do not do so for array types + because (1) the sizes of the elements are sufficient + and (2) distinct array types can have the same constructor. + Instead, we include the array size because the constructor could + be shorter. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + hi = ((HOST_WIDE_INT) TREE_TYPE (exp) & ((1 << HASHBITS) - 1)) + % MAX_HASH_TABLE; + else + hi = ((5 + int_size_in_bytes (TREE_TYPE (exp))) + & ((1 << HASHBITS) - 1)) % MAX_HASH_TABLE; + + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + if (TREE_VALUE (link)) + hi = (hi * 603 + const_hash (TREE_VALUE (link))) % MAX_HASH_TABLE; + + return hi; + } + else if (code == ADDR_EXPR) + { + struct addr_const value; + decode_addr_const (exp, &value); + if (GET_CODE (value.base) == SYMBOL_REF) + { + /* Don't hash the address of the SYMBOL_REF; + only use the offset and the symbol name. */ + hi = value.offset; + p = XSTR (value.base, 0); + for (i = 0; p[i] != 0; i++) + hi = ((hi * 613) + (unsigned)(p[i])); + } + else if (GET_CODE (value.base) == LABEL_REF) + hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13; + + hi &= (1 << HASHBITS) - 1; + hi %= MAX_HASH_TABLE; + return hi; + } + else if (code == PLUS_EXPR || code == MINUS_EXPR) + return const_hash (TREE_OPERAND (exp, 0)) * 9 + + const_hash (TREE_OPERAND (exp, 1)); + else if (code == NOP_EXPR || code == CONVERT_EXPR) + return const_hash (TREE_OPERAND (exp, 0)) * 7 + 2; + + /* Compute hashing function */ + hi = len; + for (i = 0; i < len; i++) + hi = ((hi * 613) + (unsigned)(p[i])); + + hi &= (1 << HASHBITS) - 1; + hi %= MAX_HASH_TABLE; + return hi; +} + +/* Compare a constant expression EXP with a constant-descriptor DESC. + Return 1 if DESC describes a constant with the same value as EXP. */ + +static int +compare_constant (exp, desc) + tree exp; + struct constant_descriptor *desc; +{ + return 0 != compare_constant_1 (exp, desc->contents); +} + +/* Compare constant expression EXP with a substring P of a constant descriptor. + If they match, return a pointer to the end of the substring matched. + If they do not match, return 0. + + Since descriptors are written in polish prefix notation, + this function can be used recursively to test one operand of EXP + against a subdescriptor, and if it succeeds it returns the + address of the subdescriptor for the next operand. */ + +static char * +compare_constant_1 (exp, p) + tree exp; + char *p; +{ + register char *strp; + register int len; + register enum tree_code code = TREE_CODE (exp); + + if (code != (enum tree_code) *p++) + return 0; + + if (code == INTEGER_CST) + { + /* Integer constants are the same only if the same width of type. */ + if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) + return 0; + strp = (char *) &TREE_INT_CST_LOW (exp); + len = 2 * sizeof TREE_INT_CST_LOW (exp); + } + else if (code == REAL_CST) + { + /* Real constants are the same only if the same width of type. */ + if (*p++ != TYPE_PRECISION (TREE_TYPE (exp))) + return 0; + strp = (char *) &TREE_REAL_CST (exp); + len = sizeof TREE_REAL_CST (exp); + } + else if (code == STRING_CST) + { + if (flag_writable_strings) + return 0; + strp = TREE_STRING_POINTER (exp); + len = TREE_STRING_LENGTH (exp); + if (bcmp ((char *) &TREE_STRING_LENGTH (exp), p, + sizeof TREE_STRING_LENGTH (exp))) + return 0; + p += sizeof TREE_STRING_LENGTH (exp); + } + else if (code == COMPLEX_CST) + { + p = compare_constant_1 (TREE_REALPART (exp), p); + if (p == 0) return 0; + p = compare_constant_1 (TREE_IMAGPART (exp), p); + return p; + } + else if (code == CONSTRUCTOR && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE) + { + len = int_size_in_bytes (TREE_TYPE (exp)); + strp = (char*) alloca (len); + get_set_constructor_bytes (exp, (unsigned char *) strp, len); + } + else if (code == CONSTRUCTOR) + { + register tree link; + int length = list_length (CONSTRUCTOR_ELTS (exp)); + tree type; + + if (bcmp ((char *) &length, p, sizeof length)) + return 0; + p += sizeof length; + + /* For record constructors, insist that the types match. + For arrays, just verify both constructors are for arrays. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + type = TREE_TYPE (exp); + else + type = 0; + if (bcmp ((char *) &type, p, sizeof type)) + return 0; + p += sizeof type; + + /* For arrays, insist that the size in bytes match. */ + if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + { + int size = int_size_in_bytes (TREE_TYPE (exp)); + if (bcmp ((char *) &size, p, sizeof size)) + return 0; + p += sizeof size; + } + + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + { + if (TREE_VALUE (link)) + { + if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0) + return 0; + } + else + { + tree zero = 0; + + if (bcmp ((char *) &zero, p, sizeof zero)) + return 0; + p += sizeof zero; + } + } + + return p; + } + else if (code == ADDR_EXPR) + { + struct addr_const value; + decode_addr_const (exp, &value); + strp = (char *) &value.offset; + len = sizeof value.offset; + /* Compare the offset. */ + while (--len >= 0) + if (*p++ != *strp++) + return 0; + /* Compare symbol name. */ + strp = XSTR (value.base, 0); + len = strlen (strp) + 1; + } + else if (code == PLUS_EXPR || code == MINUS_EXPR) + { + p = compare_constant_1 (TREE_OPERAND (exp, 0), p); + if (p == 0) return 0; + p = compare_constant_1 (TREE_OPERAND (exp, 1), p); + return p; + } + else if (code == NOP_EXPR || code == CONVERT_EXPR) + { + p = compare_constant_1 (TREE_OPERAND (exp, 0), p); + return p; + } + + /* Compare constant contents. */ + while (--len >= 0) + if (*p++ != *strp++) + return 0; + + return p; +} + +/* Construct a constant descriptor for the expression EXP. + It is up to the caller to enter the descriptor in the hash table. */ + +static struct constant_descriptor * +record_constant (exp) + tree exp; +{ + struct constant_descriptor *next = 0; + char *label = 0; + + /* Make a struct constant_descriptor. The first two pointers will + be filled in later. Here we just leave space for them. */ + + obstack_grow (&permanent_obstack, (char *) &next, sizeof next); + obstack_grow (&permanent_obstack, (char *) &label, sizeof label); + record_constant_1 (exp); + return (struct constant_descriptor *) obstack_finish (&permanent_obstack); +} + +/* Add a description of constant expression EXP + to the object growing in `permanent_obstack'. + No need to return its address; the caller will get that + from the obstack when the object is complete. */ + +static void +record_constant_1 (exp) + tree exp; +{ + register char *strp; + register int len; + register enum tree_code code = TREE_CODE (exp); + + obstack_1grow (&permanent_obstack, (unsigned int) code); + + switch (code) + { + case INTEGER_CST: + obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); + strp = (char *) &TREE_INT_CST_LOW (exp); + len = 2 * sizeof TREE_INT_CST_LOW (exp); + break; + + case REAL_CST: + obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp))); + strp = (char *) &TREE_REAL_CST (exp); + len = sizeof TREE_REAL_CST (exp); + break; + + case STRING_CST: + if (flag_writable_strings) + return; + + strp = TREE_STRING_POINTER (exp); + len = TREE_STRING_LENGTH (exp); + obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp), + sizeof TREE_STRING_LENGTH (exp)); + break; + + case COMPLEX_CST: + record_constant_1 (TREE_REALPART (exp)); + record_constant_1 (TREE_IMAGPART (exp)); + return; + + case CONSTRUCTOR: + if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE) + { + int nbytes = int_size_in_bytes (TREE_TYPE (exp)); + obstack_grow (&permanent_obstack, &nbytes, sizeof (nbytes)); + obstack_blank (&permanent_obstack, nbytes); + get_set_constructor_bytes + (exp, (unsigned char *) permanent_obstack.next_free, nbytes); + return; + } + else + { + register tree link; + int length = list_length (CONSTRUCTOR_ELTS (exp)); + tree type; + + obstack_grow (&permanent_obstack, (char *) &length, sizeof length); + + /* For record constructors, insist that the types match. + For arrays, just verify both constructors are for arrays. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + type = TREE_TYPE (exp); + else + type = 0; + obstack_grow (&permanent_obstack, (char *) &type, sizeof type); + + /* For arrays, insist that the size in bytes match. */ + if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + { + int size = int_size_in_bytes (TREE_TYPE (exp)); + obstack_grow (&permanent_obstack, (char *) &size, sizeof size); + } + + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + { + if (TREE_VALUE (link)) + record_constant_1 (TREE_VALUE (link)); + else + { + tree zero = 0; + + obstack_grow (&permanent_obstack, + (char *) &zero, sizeof zero); + } + } + } + return; + + case ADDR_EXPR: + { + struct addr_const value; + + decode_addr_const (exp, &value); + /* Record the offset. */ + obstack_grow (&permanent_obstack, + (char *) &value.offset, sizeof value.offset); + /* Record the symbol name. */ + obstack_grow (&permanent_obstack, XSTR (value.base, 0), + strlen (XSTR (value.base, 0)) + 1); + } + return; + + case PLUS_EXPR: + case MINUS_EXPR: + record_constant_1 (TREE_OPERAND (exp, 0)); + record_constant_1 (TREE_OPERAND (exp, 1)); + return; + + case NOP_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + record_constant_1 (TREE_OPERAND (exp, 0)); + return; + + default: + abort (); + } + + /* Record constant contents. */ + obstack_grow (&permanent_obstack, strp, len); +} + +/* Record a list of constant expressions that were passed to + output_constant_def but that could not be output right away. */ + +struct deferred_constant +{ + struct deferred_constant *next; + tree exp; + int reloc; + int labelno; +}; + +static struct deferred_constant *deferred_constants; + +/* Nonzero means defer output of addressed subconstants + (i.e., those for which output_constant_def is called.) */ +static int defer_addressed_constants_flag; + +/* Start deferring output of subconstants. */ + +void +defer_addressed_constants () +{ + defer_addressed_constants_flag++; +} + +/* Stop deferring output of subconstants, + and output now all those that have been deferred. */ + +void +output_deferred_addressed_constants () +{ + struct deferred_constant *p, *next; + + defer_addressed_constants_flag--; + + if (defer_addressed_constants_flag > 0) + return; + + for (p = deferred_constants; p; p = next) + { + output_constant_def_contents (p->exp, p->reloc, p->labelno); + next = p->next; + free (p); + } + + deferred_constants = 0; +} + +/* Make a copy of the whole tree structure for a constant. + This handles the same types of nodes that compare_constant + and record_constant handle. */ + +static tree +copy_constant (exp) + tree exp; +{ + switch (TREE_CODE (exp)) + { + case ADDR_EXPR: + /* For ADDR_EXPR, we do not want to copy the decl whose address + is requested. We do want to copy constants though. */ + if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == 'c') + return build1 (TREE_CODE (exp), TREE_TYPE (exp), + copy_constant (TREE_OPERAND (exp, 0))); + else + return copy_node (exp); + + case INTEGER_CST: + case REAL_CST: + case STRING_CST: + return copy_node (exp); + + case COMPLEX_CST: + return build_complex (copy_constant (TREE_REALPART (exp)), + copy_constant (TREE_IMAGPART (exp))); + + case PLUS_EXPR: + case MINUS_EXPR: + return build (TREE_CODE (exp), TREE_TYPE (exp), + copy_constant (TREE_OPERAND (exp, 0)), + copy_constant (TREE_OPERAND (exp, 1))); + + case NOP_EXPR: + case CONVERT_EXPR: + return build1 (TREE_CODE (exp), TREE_TYPE (exp), + copy_constant (TREE_OPERAND (exp, 0))); + + case CONSTRUCTOR: + { + tree copy = copy_node (exp); + tree list = copy_list (CONSTRUCTOR_ELTS (exp)); + tree tail; + + CONSTRUCTOR_ELTS (copy) = list; + for (tail = list; tail; tail = TREE_CHAIN (tail)) + TREE_VALUE (tail) = copy_constant (TREE_VALUE (tail)); + if (TREE_CODE (TREE_TYPE (exp)) == SET_TYPE) + for (tail = list; tail; tail = TREE_CHAIN (tail)) + TREE_PURPOSE (tail) = copy_constant (TREE_PURPOSE (tail)); + + return copy; + } + + default: + abort (); + } +} + +/* Return an rtx representing a reference to constant data in memory + for the constant expression EXP. + + If assembler code for such a constant has already been output, + return an rtx to refer to it. + Otherwise, output such a constant in memory (or defer it for later) + and generate an rtx for it. + + The TREE_CST_RTL of EXP is set up to point to that rtx. + The const_hash_table records which constants already have label strings. */ + +rtx +output_constant_def (exp) + tree exp; +{ + register int hash; + register struct constant_descriptor *desc; + char label[256]; + char *found = 0; + int reloc; + register rtx def; + + if (TREE_CODE (exp) == INTEGER_CST) + abort (); /* No TREE_CST_RTL slot in these. */ + + if (TREE_CST_RTL (exp)) + return TREE_CST_RTL (exp); + + /* Make sure any other constants whose addresses appear in EXP + are assigned label numbers. */ + + reloc = output_addressed_constants (exp); + + /* Compute hash code of EXP. Search the descriptors for that hash code + to see if any of them describes EXP. If yes, the descriptor records + the label number already assigned. */ + + hash = const_hash (exp) % MAX_HASH_TABLE; + + for (desc = const_hash_table[hash]; desc; desc = desc->next) + if (compare_constant (exp, desc)) + { + found = desc->label; + break; + } + + if (found == 0) + { + /* No constant equal to EXP is known to have been output. + Make a constant descriptor to enter EXP in the hash table. + Assign the label number and record it in the descriptor for + future calls to this function to find. */ + + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + + desc = record_constant (exp); + desc->next = const_hash_table[hash]; + desc->label + = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label)); + const_hash_table[hash] = desc; + } + else + { + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + } + + /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ + + push_obstacks_nochange (); + if (TREE_PERMANENT (exp)) + end_temporary_allocation (); + + def = gen_rtx (SYMBOL_REF, Pmode, desc->label); + + TREE_CST_RTL (exp) + = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def); + RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1; + if (AGGREGATE_TYPE_P (TREE_TYPE (exp))) + MEM_IN_STRUCT_P (TREE_CST_RTL (exp)) = 1; + + pop_obstacks (); + + /* Optionally set flags or add text to the name to record information + such as that it is a function name. If the name is changed, the macro + ASM_OUTPUT_LABELREF will have to know how to strip this information. */ +#ifdef ENCODE_SECTION_INFO + ENCODE_SECTION_INFO (exp); +#endif + + /* If this is the first time we've seen this particular constant, + output it (or defer its output for later). */ + if (found == 0) + { + if (defer_addressed_constants_flag) + { + struct deferred_constant *p; + p = (struct deferred_constant *) xmalloc (sizeof (struct deferred_constant)); + + push_obstacks_nochange (); + suspend_momentary (); + p->exp = copy_constant (exp); + pop_obstacks (); + p->reloc = reloc; + p->labelno = const_labelno++; + p->next = deferred_constants; + deferred_constants = p; + } + else + output_constant_def_contents (exp, reloc, const_labelno++); + } + + return TREE_CST_RTL (exp); +} + +/* Now output assembler code to define the label for EXP, + and follow it with the data of EXP. */ + +static void +output_constant_def_contents (exp, reloc, labelno) + tree exp; + int reloc; + int labelno; +{ + int align; + + if (IN_NAMED_SECTION (exp)) + named_section (exp, NULL); + else + { + /* First switch to text section, except for writable strings. */ +#ifdef SELECT_SECTION + SELECT_SECTION (exp, reloc); +#else + if (((TREE_CODE (exp) == STRING_CST) && flag_writable_strings) + || (flag_pic && reloc)) + data_section (); + else + readonly_data_section (); +#endif + } + + /* Align the location counter as required by EXP's data type. */ + align = TYPE_ALIGN (TREE_TYPE (exp)); +#ifdef CONSTANT_ALIGNMENT + align = CONSTANT_ALIGNMENT (exp, align); +#endif + + if (align > BITS_PER_UNIT) + { + if (!output_bytecode) + { + ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + } + else + { + BC_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT)); + } + } + + /* Output the label itself. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", labelno); + + /* Output the value of EXP. */ + output_constant (exp, + (TREE_CODE (exp) == STRING_CST + ? TREE_STRING_LENGTH (exp) + : int_size_in_bytes (TREE_TYPE (exp)))); + +} + +/* Similar hash facility for making memory-constants + from constant rtl-expressions. It is used on RISC machines + where immediate integer arguments and constant addresses are restricted + so that such constants must be stored in memory. + + This pool of constants is reinitialized for each function + so each function gets its own constants-pool that comes right before it. + + All structures allocated here are discarded when functions are saved for + inlining, so they do not need to be allocated permanently. */ + +#define MAX_RTX_HASH_TABLE 61 +static struct constant_descriptor **const_rtx_hash_table; + +/* Structure to represent sufficient information about a constant so that + it can be output when the constant pool is output, so that function + integration can be done, and to simplify handling on machines that reference + constant pool as base+displacement. */ + +struct pool_constant +{ + struct constant_descriptor *desc; + struct pool_constant *next; + enum machine_mode mode; + rtx constant; + int labelno; + int align; + int offset; +}; + +/* Pointers to first and last constant in pool. */ + +static struct pool_constant *first_pool, *last_pool; + +/* Current offset in constant pool (does not include any machine-specific + header. */ + +static int pool_offset; + +/* Structure used to maintain hash table mapping symbols used to their + corresponding constants. */ + +struct pool_sym +{ + char *label; + struct pool_constant *pool; + struct pool_sym *next; +}; + +static struct pool_sym **const_rtx_sym_hash_table; + +/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true. + The argument is XSTR (... , 0) */ + +#define SYMHASH(LABEL) \ + ((((HOST_WIDE_INT) (LABEL)) & ((1 << HASHBITS) - 1)) % MAX_RTX_HASH_TABLE) + +/* Initialize constant pool hashing for next function. */ + +void +init_const_rtx_hash_table () +{ + const_rtx_hash_table + = ((struct constant_descriptor **) + oballoc (MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *))); + const_rtx_sym_hash_table + = ((struct pool_sym **) + oballoc (MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *))); + bzero ((char *) const_rtx_hash_table, + MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *)); + bzero ((char *) const_rtx_sym_hash_table, + MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *)); + + first_pool = last_pool = 0; + pool_offset = 0; +} + +/* Save and restore status for a nested function. */ + +void +save_varasm_status (p) + struct function *p; +{ + p->const_rtx_hash_table = const_rtx_hash_table; + p->const_rtx_sym_hash_table = const_rtx_sym_hash_table; + p->first_pool = first_pool; + p->last_pool = last_pool; + p->pool_offset = pool_offset; +} + +void +restore_varasm_status (p) + struct function *p; +{ + const_rtx_hash_table = p->const_rtx_hash_table; + const_rtx_sym_hash_table = p->const_rtx_sym_hash_table; + first_pool = p->first_pool; + last_pool = p->last_pool; + pool_offset = p->pool_offset; +} + +enum kind { RTX_DOUBLE, RTX_INT }; + +struct rtx_const +{ +#ifdef ONLY_INT_FIELDS + unsigned int kind : 16; + unsigned int mode : 16; +#else + enum kind kind : 16; + enum machine_mode mode : 16; +#endif + union { + union real_extract du; + struct addr_const addr; + struct {HOST_WIDE_INT high, low;} di; + } un; +}; + +/* Express an rtx for a constant integer (perhaps symbolic) + as the sum of a symbol or label plus an explicit integer. + They are stored into VALUE. */ + +static void +decode_rtx_const (mode, x, value) + enum machine_mode mode; + rtx x; + struct rtx_const *value; +{ + /* Clear the whole structure, including any gaps. */ + + { + int *p = (int *) value; + int *end = (int *) (value + 1); + while (p < end) + *p++ = 0; + } + + value->kind = RTX_INT; /* Most usual kind. */ + value->mode = mode; + + switch (GET_CODE (x)) + { + case CONST_DOUBLE: + value->kind = RTX_DOUBLE; + if (GET_MODE (x) != VOIDmode) + { + value->mode = GET_MODE (x); + bcopy ((char *) &CONST_DOUBLE_LOW (x), + (char *) &value->un.du, sizeof value->un.du); + } + else + { + value->un.di.low = CONST_DOUBLE_LOW (x); + value->un.di.high = CONST_DOUBLE_HIGH (x); + } + break; + + case CONST_INT: + value->un.addr.offset = INTVAL (x); + break; + + case SYMBOL_REF: + case LABEL_REF: + case PC: + value->un.addr.base = x; + break; + + case CONST: + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS) + { + value->un.addr.base = XEXP (x, 0); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + abort (); + value->un.addr.offset = INTVAL (XEXP (x, 1)); + } + else if (GET_CODE (x) == MINUS) + { + value->un.addr.base = XEXP (x, 0); + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + abort (); + value->un.addr.offset = - INTVAL (XEXP (x, 1)); + } + else + abort (); + break; + + default: + abort (); + } + + if (value->kind == RTX_INT && value->un.addr.base != 0) + switch (GET_CODE (value->un.addr.base)) + { + case SYMBOL_REF: + case LABEL_REF: + /* Use the string's address, not the SYMBOL_REF's address, + for the sake of addresses of library routines. + For a LABEL_REF, compare labels. */ + value->un.addr.base = XEXP (value->un.addr.base, 0); + } +} + +/* Given a MINUS expression, simplify it if both sides + include the same symbol. */ + +rtx +simplify_subtraction (x) + rtx x; +{ + struct rtx_const val0, val1; + + decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0); + decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1); + + if (val0.un.addr.base == val1.un.addr.base) + return GEN_INT (val0.un.addr.offset - val1.un.addr.offset); + return x; +} + +/* Compute a hash code for a constant RTL expression. */ + +static int +const_hash_rtx (mode, x) + enum machine_mode mode; + rtx x; +{ + register int hi, i; + + struct rtx_const value; + decode_rtx_const (mode, x, &value); + + /* Compute hashing function */ + hi = 0; + for (i = 0; i < sizeof value / sizeof (int); i++) + hi += ((int *) &value)[i]; + + hi &= (1 << HASHBITS) - 1; + hi %= MAX_RTX_HASH_TABLE; + return hi; +} + +/* Compare a constant rtl object X with a constant-descriptor DESC. + Return 1 if DESC describes a constant with the same value as X. */ + +static int +compare_constant_rtx (mode, x, desc) + enum machine_mode mode; + rtx x; + struct constant_descriptor *desc; +{ + register int *p = (int *) desc->contents; + register int *strp; + register int len; + struct rtx_const value; + + decode_rtx_const (mode, x, &value); + strp = (int *) &value; + len = sizeof value / sizeof (int); + + /* Compare constant contents. */ + while (--len >= 0) + if (*p++ != *strp++) + return 0; + + return 1; +} + +/* Construct a constant descriptor for the rtl-expression X. + It is up to the caller to enter the descriptor in the hash table. */ + +static struct constant_descriptor * +record_constant_rtx (mode, x) + enum machine_mode mode; + rtx x; +{ + struct constant_descriptor *ptr; + char *label; + struct rtx_const value; + + decode_rtx_const (mode, x, &value); + + /* Put these things in the saveable obstack so we can ensure it won't + be freed if we are called from combine or some other phase that discards + memory allocated from function_obstack (current_obstack). */ + obstack_grow (saveable_obstack, &ptr, sizeof ptr); + obstack_grow (saveable_obstack, &label, sizeof label); + + /* Record constant contents. */ + obstack_grow (saveable_obstack, &value, sizeof value); + + return (struct constant_descriptor *) obstack_finish (saveable_obstack); +} + +/* Given a constant rtx X, make (or find) a memory constant for its value + and return a MEM rtx to refer to it in memory. */ + +rtx +force_const_mem (mode, x) + enum machine_mode mode; + rtx x; +{ + register int hash; + register struct constant_descriptor *desc; + char label[256]; + char *found = 0; + rtx def; + + /* If we want this CONST_DOUBLE in the same mode as it is in memory + (this will always be true for floating CONST_DOUBLEs that have been + placed in memory, but not for VOIDmode (integer) CONST_DOUBLEs), + use the previous copy. Otherwise, make a new one. Note that in + the unlikely event that this same CONST_DOUBLE is used in two different + modes in an alternating fashion, we will allocate a lot of different + memory locations, but this should be extremely rare. */ + + /* Don't use CONST_DOUBLE_MEM in a nested function. + Nested functions have their own constant pools, + so they can't share the same values in CONST_DOUBLE_MEM + with the containing function. */ + if (outer_function_chain == 0) + if (GET_CODE (x) == CONST_DOUBLE + && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM + && GET_MODE (CONST_DOUBLE_MEM (x)) == mode) + return CONST_DOUBLE_MEM (x); + + /* Compute hash code of X. Search the descriptors for that hash code + to see if any of them describes X. If yes, the descriptor records + the label number already assigned. */ + + hash = const_hash_rtx (mode, x); + + for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next) + if (compare_constant_rtx (mode, x, desc)) + { + found = desc->label; + break; + } + + if (found == 0) + { + register struct pool_constant *pool; + register struct pool_sym *sym; + int align; + + /* No constant equal to X is known to have been output. + Make a constant descriptor to enter X in the hash table. + Assign the label number and record it in the descriptor for + future calls to this function to find. */ + + desc = record_constant_rtx (mode, x); + desc->next = const_rtx_hash_table[hash]; + const_rtx_hash_table[hash] = desc; + + /* Align the location counter as required by EXP's data type. */ + align = (mode == VOIDmode) ? UNITS_PER_WORD : GET_MODE_SIZE (mode); + if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT) + align = BIGGEST_ALIGNMENT / BITS_PER_UNIT; + + pool_offset += align - 1; + pool_offset &= ~ (align - 1); + + /* If RTL is not being placed into the saveable obstack, make a + copy of X that is in the saveable obstack in case we are being + called from combine or some other phase that discards memory + it allocates. We need only do this if it is a CONST, since + no other RTX should be allocated in this situation. */ + if (rtl_obstack != saveable_obstack + && GET_CODE (x) == CONST) + { + push_obstacks_nochange (); + rtl_in_saveable_obstack (); + + x = gen_rtx (CONST, GET_MODE (x), + gen_rtx (PLUS, GET_MODE (x), + XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1))); + pop_obstacks (); + } + + /* Allocate a pool constant descriptor, fill it in, and chain it in. */ + + pool = (struct pool_constant *) savealloc (sizeof (struct pool_constant)); + pool->desc = desc; + pool->constant = x; + pool->mode = mode; + pool->labelno = const_labelno; + pool->align = align; + pool->offset = pool_offset; + pool->next = 0; + + if (last_pool == 0) + first_pool = pool; + else + last_pool->next = pool; + + last_pool = pool; + pool_offset += GET_MODE_SIZE (mode); + + /* Create a string containing the label name, in LABEL. */ + ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno); + + ++const_labelno; + + desc->label = found + = (char *) obstack_copy0 (saveable_obstack, label, strlen (label)); + + /* Add label to symbol hash table. */ + hash = SYMHASH (found); + sym = (struct pool_sym *) savealloc (sizeof (struct pool_sym)); + sym->label = found; + sym->pool = pool; + sym->next = const_rtx_sym_hash_table[hash]; + const_rtx_sym_hash_table[hash] = sym; + } + + /* We have a symbol name; construct the SYMBOL_REF and the MEM. */ + + def = gen_rtx (MEM, mode, gen_rtx (SYMBOL_REF, Pmode, found)); + + RTX_UNCHANGING_P (def) = 1; + /* Mark the symbol_ref as belonging to this constants pool. */ + CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1; + current_function_uses_const_pool = 1; + + if (outer_function_chain == 0) + if (GET_CODE (x) == CONST_DOUBLE) + { + if (CONST_DOUBLE_MEM (x) == cc0_rtx) + { + CONST_DOUBLE_CHAIN (x) = const_double_chain; + const_double_chain = x; + } + CONST_DOUBLE_MEM (x) = def; + } + + return def; +} + +/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to + the corresponding pool_constant structure. */ + +static struct pool_constant * +find_pool_constant (addr) + rtx addr; +{ + struct pool_sym *sym; + char *label = XSTR (addr, 0); + + for (sym = const_rtx_sym_hash_table[SYMHASH (label)]; sym; sym = sym->next) + if (sym->label == label) + return sym->pool; + + abort (); +} + +/* Given a constant pool SYMBOL_REF, return the corresponding constant. */ + +rtx +get_pool_constant (addr) + rtx addr; +{ + return (find_pool_constant (addr))->constant; +} + +/* Similar, return the mode. */ + +enum machine_mode +get_pool_mode (addr) + rtx addr; +{ + return (find_pool_constant (addr))->mode; +} + +/* Similar, return the offset in the constant pool. */ + +int +get_pool_offset (addr) + rtx addr; +{ + return (find_pool_constant (addr))->offset; +} + +/* Return the size of the constant pool. */ + +int +get_pool_size () +{ + return pool_offset; +} + +/* Write all the constants in the constant pool. */ + +void +output_constant_pool (fnname, fndecl) + char *fnname; + tree fndecl; +{ + struct pool_constant *pool; + rtx x; + union real_extract u; + +#ifdef ASM_OUTPUT_POOL_PROLOGUE + ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset); +#endif + + for (pool = first_pool; pool; pool = pool->next) + { + x = pool->constant; + + /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF) + whose CODE_LABEL has been deleted. This can occur if a jump table + is eliminated by optimization. If so, write a constant of zero + instead. Note that this can also happen by turning the + CODE_LABEL into a NOTE. */ + if (((GET_CODE (x) == LABEL_REF + && (INSN_DELETED_P (XEXP (x, 0)) + || GET_CODE (XEXP (x, 0)) == NOTE))) + || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF + && (INSN_DELETED_P (XEXP (XEXP (XEXP (x, 0), 0), 0)) + || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == NOTE))) + x = const0_rtx; + + /* First switch to correct section. */ +#ifdef SELECT_RTX_SECTION + SELECT_RTX_SECTION (pool->mode, x); +#else + readonly_data_section (); +#endif + +#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY + ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode, + pool->align, pool->labelno, done); +#endif + + if (pool->align > 1) + ASM_OUTPUT_ALIGN (asm_out_file, exact_log2 (pool->align)); + + /* Output the label. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", pool->labelno); + + /* Output the value of the constant itself. */ + switch (GET_MODE_CLASS (pool->mode)) + { + case MODE_FLOAT: + if (GET_CODE (x) != CONST_DOUBLE) + abort (); + + bcopy ((char *) &CONST_DOUBLE_LOW (x), (char *) &u, sizeof u); + assemble_real (u.d, pool->mode); + break; + + case MODE_INT: + case MODE_PARTIAL_INT: + assemble_integer (x, GET_MODE_SIZE (pool->mode), 1); + break; + + default: + abort (); + } + + done: ; + } + + /* Done with this pool. */ + first_pool = last_pool = 0; +} + +/* Find all the constants whose addresses are referenced inside of EXP, + and make sure assembler code with a label has been output for each one. + Indicate whether an ADDR_EXPR has been encountered. */ + +static int +output_addressed_constants (exp) + tree exp; +{ + int reloc = 0; + + switch (TREE_CODE (exp)) + { + case ADDR_EXPR: + { + register tree constant = TREE_OPERAND (exp, 0); + + while (TREE_CODE (constant) == COMPONENT_REF) + { + constant = TREE_OPERAND (constant, 0); + } + + if (TREE_CODE_CLASS (TREE_CODE (constant)) == 'c' + || TREE_CODE (constant) == CONSTRUCTOR) + /* No need to do anything here + for addresses of variables or functions. */ + output_constant_def (constant); + } + reloc = 1; + break; + + case PLUS_EXPR: + case MINUS_EXPR: + reloc = output_addressed_constants (TREE_OPERAND (exp, 0)); + reloc |= output_addressed_constants (TREE_OPERAND (exp, 1)); + break; + + case NOP_EXPR: + case CONVERT_EXPR: + case NON_LVALUE_EXPR: + reloc = output_addressed_constants (TREE_OPERAND (exp, 0)); + break; + + case CONSTRUCTOR: + { + register tree link; + for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link)) + if (TREE_VALUE (link) != 0) + reloc |= output_addressed_constants (TREE_VALUE (link)); + } + break; + + case ERROR_MARK: + break; + } + return reloc; +} + +/* Output assembler code for constant EXP to FILE, with no label. + This includes the pseudo-op such as ".int" or ".byte", and a newline. + Assumes output_addressed_constants has been done on EXP already. + + Generate exactly SIZE bytes of assembler data, padding at the end + with zeros if necessary. SIZE must always be specified. + + SIZE is important for structure constructors, + since trailing members may have been omitted from the constructor. + It is also important for initialization of arrays from string constants + since the full length of the string constant might not be wanted. + It is also needed for initialization of unions, where the initializer's + type is just one member, and that may not be as long as the union. + + There a case in which we would fail to output exactly SIZE bytes: + for a structure constructor that wants to produce more than SIZE bytes. + But such constructors will never be generated for any possible input. */ + +void +output_constant (exp, size) + register tree exp; + register int size; +{ + register enum tree_code code = TREE_CODE (TREE_TYPE (exp)); + rtx x; + + if (size == 0) + return; + + /* Eliminate the NON_LVALUE_EXPR_EXPR that makes a cast not be an lvalue. + That way we get the constant (we hope) inside it. Also, strip off any + NOP_EXPR that converts between two record, union, array, or set types. */ + while ((TREE_CODE (exp) == NOP_EXPR + && (TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0)) + || AGGREGATE_TYPE_P (TREE_TYPE (exp)))) + || TREE_CODE (exp) == NON_LVALUE_EXPR) + exp = TREE_OPERAND (exp, 0); + + /* Allow a constructor with no elements for any data type. + This means to fill the space with zeros. */ + if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0) + { + if (output_bytecode) + bc_emit_const_skip (size); + else + assemble_zeros (size); + return; + } + + switch (code) + { + case CHAR_TYPE: + case BOOLEAN_TYPE: + case INTEGER_TYPE: + case ENUMERAL_TYPE: + case POINTER_TYPE: + case REFERENCE_TYPE: + /* ??? What about (int)((float)(int)&foo + 4) */ + while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR + || TREE_CODE (exp) == NON_LVALUE_EXPR) + exp = TREE_OPERAND (exp, 0); + + if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode, + EXPAND_INITIALIZER), + size, 0)) + error ("initializer for integer value is too complicated"); + size = 0; + break; + + case REAL_TYPE: + if (TREE_CODE (exp) != REAL_CST) + error ("initializer for floating value is not a floating constant"); + + assemble_real (TREE_REAL_CST (exp), + mode_for_size (size * BITS_PER_UNIT, MODE_FLOAT, 0)); + size = 0; + break; + + case COMPLEX_TYPE: + output_constant (TREE_REALPART (exp), size / 2); + output_constant (TREE_IMAGPART (exp), size / 2); + size -= (size / 2) * 2; + break; + + case ARRAY_TYPE: + if (TREE_CODE (exp) == CONSTRUCTOR) + { + output_constructor (exp, size); + return; + } + else if (TREE_CODE (exp) == STRING_CST) + { + int excess = 0; + + if (size > TREE_STRING_LENGTH (exp)) + { + excess = size - TREE_STRING_LENGTH (exp); + size = TREE_STRING_LENGTH (exp); + } + + assemble_string (TREE_STRING_POINTER (exp), size); + size = excess; + } + else + abort (); + break; + + case RECORD_TYPE: + case UNION_TYPE: + if (TREE_CODE (exp) == CONSTRUCTOR) + output_constructor (exp, size); + else + abort (); + return; + + case SET_TYPE: + if (TREE_CODE (exp) == INTEGER_CST) + assemble_integer (expand_expr (exp, NULL_RTX, + VOIDmode, EXPAND_INITIALIZER), + size, 1); + else if (TREE_CODE (exp) == CONSTRUCTOR) + { + unsigned char *buffer = (unsigned char *) alloca (size); + if (get_set_constructor_bytes (exp, buffer, size)) + abort (); + assemble_string ((char *) buffer, size); + } + else + error ("unknown set constructor type"); + return; + } + + if (size > 0) + assemble_zeros (size); +} + +/* Bytecode specific code to output assembler for integer. */ + +static void +bc_assemble_integer (exp, size) + tree exp; + int size; +{ + tree const_part; + tree addr_part; + tree tmp; + + /* FIXME: is this fold() business going to be as good as the + expand_expr() using EXPAND_SUM above in the RTL case? I + hate RMS. + FIXME: Copied as is from BC-GCC1; may need work. Don't hate. -bson */ + + exp = fold (exp); + + while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR) + exp = TREE_OPERAND (exp, 0); + if (TREE_CODE (exp) == INTEGER_CST) + { + const_part = exp; + addr_part = 0; + } + else if (TREE_CODE (exp) == PLUS_EXPR) + { + const_part = TREE_OPERAND (exp, 0); + while (TREE_CODE (const_part) == NOP_EXPR + || TREE_CODE (const_part) == CONVERT_EXPR) + const_part = TREE_OPERAND (const_part, 0); + addr_part = TREE_OPERAND (exp, 1); + while (TREE_CODE (addr_part) == NOP_EXPR + || TREE_CODE (addr_part) == CONVERT_EXPR) + addr_part = TREE_OPERAND (addr_part, 0); + if (TREE_CODE (const_part) != INTEGER_CST) + tmp = const_part, const_part = addr_part, addr_part = tmp; + if (TREE_CODE (const_part) != INTEGER_CST + || TREE_CODE (addr_part) != ADDR_EXPR) + abort (); /* FIXME: we really haven't considered + all the possible cases here. */ + } + else if (TREE_CODE (exp) == ADDR_EXPR) + { + const_part = integer_zero_node; + addr_part = exp; + } + else + abort (); /* FIXME: ditto previous. */ + + if (addr_part == 0) + { + if (size == 1) + { + char c = TREE_INT_CST_LOW (const_part); + bc_emit (&c, 1); + size -= 1; + } + else if (size == 2) + { + short s = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &s, 2); + size -= 2; + } + else if (size == 4) + { + int i = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &i, 4); + size -= 4; + } + else if (size == 8) + { + if (WORDS_BIG_ENDIAN) + { + int i = TREE_INT_CST_HIGH (const_part); + bc_emit ((char *) &i, 4); + i = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &i, 4); + } + else + { + int i = TREE_INT_CST_LOW (const_part); + bc_emit ((char *) &i, 4); + i = TREE_INT_CST_HIGH (const_part); + bc_emit ((char *) &i, 4); + } + size -= 8; + } + } + else + if (size == 4 + && TREE_CODE (TREE_OPERAND (addr_part, 0)) == VAR_DECL) + bc_emit_labelref (IDENTIFIER_POINTER + (DECL_ASSEMBLER_NAME (TREE_OPERAND (addr_part, 0))), + TREE_INT_CST_LOW (const_part)); + else + abort (); /* FIXME: there may be more cases. */ +} + +/* Subroutine of output_constant, used for CONSTRUCTORs + (aggregate constants). + Generate at least SIZE bytes, padding if necessary. */ + +static void +output_constructor (exp, size) + tree exp; + int size; +{ + register tree link, field = 0; + HOST_WIDE_INT min_index = 0; + /* Number of bytes output or skipped so far. + In other words, current position within the constructor. */ + int total_bytes = 0; + /* Non-zero means BYTE contains part of a byte, to be output. */ + int byte_buffer_in_use = 0; + register int byte; + + if (HOST_BITS_PER_WIDE_INT < BITS_PER_UNIT) + abort (); + + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE) + field = TYPE_FIELDS (TREE_TYPE (exp)); + + if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE + && TYPE_DOMAIN (TREE_TYPE (exp)) != 0) + min_index + = TREE_INT_CST_LOW (TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (exp)))); + + /* As LINK goes through the elements of the constant, + FIELD goes through the structure fields, if the constant is a structure. + if the constant is a union, then we override this, + by getting the field from the TREE_LIST element. + But the constant could also be an array. Then FIELD is zero. */ + for (link = CONSTRUCTOR_ELTS (exp); + link; + link = TREE_CHAIN (link), + field = field ? TREE_CHAIN (field) : 0) + { + tree val = TREE_VALUE (link); + tree index = 0; + + /* the element in a union constructor specifies the proper field. */ + + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE) + { + /* if available, use the type given by link */ + if (TREE_PURPOSE (link) != 0) + field = TREE_PURPOSE (link); + } + + if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE) + index = TREE_PURPOSE (link); + + /* Eliminate the marker that makes a cast not be an lvalue. */ + if (val != 0) + STRIP_NOPS (val); + + if (field == 0 || !DECL_BIT_FIELD (field)) + { + /* An element that is not a bit-field. */ + + register int fieldsize; + /* Since this structure is static, + we know the positions are constant. */ + int bitpos = (field ? (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) + / BITS_PER_UNIT) + : 0); + if (index != 0) + bitpos = (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (val))) + / BITS_PER_UNIT + * (TREE_INT_CST_LOW (index) - min_index)); + + /* Output any buffered-up bit-fields preceding this element. */ + if (byte_buffer_in_use) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + byte_buffer_in_use = 0; + } + + /* Advance to offset of this element. + Note no alignment needed in an array, since that is guaranteed + if each element has the proper size. */ + if ((field != 0 || index != 0) && bitpos != total_bytes) + { + if (!output_bytecode) + assemble_zeros (bitpos - total_bytes); + else + bc_emit_const_skip (bitpos - total_bytes); + total_bytes = bitpos; + } + + /* Determine size this element should occupy. */ + if (field) + { + if (TREE_CODE (DECL_SIZE (field)) != INTEGER_CST) + abort (); + if (TREE_INT_CST_LOW (DECL_SIZE (field)) > 100000) + { + /* This avoids overflow trouble. */ + tree size_tree = size_binop (CEIL_DIV_EXPR, + DECL_SIZE (field), + size_int (BITS_PER_UNIT)); + fieldsize = TREE_INT_CST_LOW (size_tree); + } + else + { + fieldsize = TREE_INT_CST_LOW (DECL_SIZE (field)); + fieldsize = (fieldsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT; + } + } + else + fieldsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (exp))); + + /* Output the element's initial value. */ + if (val == 0) + assemble_zeros (fieldsize); + else + output_constant (val, fieldsize); + + /* Count its size. */ + total_bytes += fieldsize; + } + else if (val != 0 && TREE_CODE (val) != INTEGER_CST) + error ("invalid initial value for member `%s'", + IDENTIFIER_POINTER (DECL_NAME (field))); + else + { + /* Element that is a bit-field. */ + + int next_offset = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)); + int end_offset + = (next_offset + TREE_INT_CST_LOW (DECL_SIZE (field))); + + if (val == 0) + val = integer_zero_node; + + /* If this field does not start in this (or, next) byte, + skip some bytes. */ + if (next_offset / BITS_PER_UNIT != total_bytes) + { + /* Output remnant of any bit field in previous bytes. */ + if (byte_buffer_in_use) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + byte_buffer_in_use = 0; + } + + /* If still not at proper byte, advance to there. */ + if (next_offset / BITS_PER_UNIT != total_bytes) + { + assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes); + total_bytes = next_offset / BITS_PER_UNIT; + } + } + + if (! byte_buffer_in_use) + byte = 0; + + /* We must split the element into pieces that fall within + separate bytes, and combine each byte with previous or + following bit-fields. */ + + /* next_offset is the offset n fbits from the beginning of + the structure to the next bit of this element to be processed. + end_offset is the offset of the first bit past the end of + this element. */ + while (next_offset < end_offset) + { + int this_time; + int shift; + HOST_WIDE_INT value; + int next_byte = next_offset / BITS_PER_UNIT; + int next_bit = next_offset % BITS_PER_UNIT; + + /* Advance from byte to byte + within this element when necessary. */ + while (next_byte != total_bytes) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + byte = 0; + } + + /* Number of bits we can process at once + (all part of the same byte). */ + this_time = MIN (end_offset - next_offset, + BITS_PER_UNIT - next_bit); + if (BYTES_BIG_ENDIAN) + { + /* On big-endian machine, take the most significant bits + first (of the bits that are significant) + and put them into bytes from the most significant end. */ + shift = end_offset - next_offset - this_time; + /* Don't try to take a bunch of bits that cross + the word boundary in the INTEGER_CST. */ + if (shift < HOST_BITS_PER_WIDE_INT + && shift + this_time > HOST_BITS_PER_WIDE_INT) + { + this_time -= (HOST_BITS_PER_WIDE_INT - shift); + shift = HOST_BITS_PER_WIDE_INT; + } + + /* Now get the bits from the appropriate constant word. */ + if (shift < HOST_BITS_PER_WIDE_INT) + { + value = TREE_INT_CST_LOW (val); + } + else if (shift < 2 * HOST_BITS_PER_WIDE_INT) + { + value = TREE_INT_CST_HIGH (val); + shift -= HOST_BITS_PER_WIDE_INT; + } + else + abort (); + byte |= (((value >> shift) + & (((HOST_WIDE_INT) 1 << this_time) - 1)) + << (BITS_PER_UNIT - this_time - next_bit)); + } + else + { + /* On little-endian machines, + take first the least significant bits of the value + and pack them starting at the least significant + bits of the bytes. */ + shift = (next_offset + - TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))); + /* Don't try to take a bunch of bits that cross + the word boundary in the INTEGER_CST. */ + if (shift < HOST_BITS_PER_WIDE_INT + && shift + this_time > HOST_BITS_PER_WIDE_INT) + { + this_time -= (HOST_BITS_PER_WIDE_INT - shift); + shift = HOST_BITS_PER_WIDE_INT; + } + + /* Now get the bits from the appropriate constant word. */ + if (shift < HOST_BITS_PER_INT) + value = TREE_INT_CST_LOW (val); + else if (shift < 2 * HOST_BITS_PER_WIDE_INT) + { + value = TREE_INT_CST_HIGH (val); + shift -= HOST_BITS_PER_WIDE_INT; + } + else + abort (); + byte |= (((value >> shift) + & (((HOST_WIDE_INT) 1 << this_time) - 1)) + << next_bit); + } + next_offset += this_time; + byte_buffer_in_use = 1; + } + } + } + if (byte_buffer_in_use) + { + ASM_OUTPUT_BYTE (asm_out_file, byte); + total_bytes++; + } + if (total_bytes < size) + assemble_zeros (size - total_bytes); +} + +/* Output asm to handle ``#pragma weak'' */ +void +handle_pragma_weak (what, name, value) + enum pragma_state what; + char *name, *value; +{ +#ifdef HANDLE_PRAGMA_WEAK + if (what == ps_name || what == ps_value) + { + struct weak_syms *weak = + (struct weak_syms *)permalloc (sizeof (struct weak_syms)); + weak->next = weak_decls; + weak->name = permalloc (strlen (name) + 1); + strcpy (weak->name, name); + + if (what != ps_value) + weak->value = NULL_PTR; + + else + { + weak->value = permalloc (strlen (value) + 1); + strcpy (weak->value, value); + } + + weak_decls = weak; + } + else if (! (what == ps_done || what == ps_start)) + warning ("malformed `#pragma weak'"); +#endif /* HANDLE_PRAGMA_WEAK */ +} + +/* Declare DECL to be a weak symbol. */ + +void +declare_weak (decl) + tree decl; +{ + if (! TREE_PUBLIC (decl)) + error_with_decl (decl, "weak declaration of `%s' must be public"); + else if (TREE_ASM_WRITTEN (decl)) + error_with_decl (decl, "weak declaration of `%s' must precede definition"); + else if (SUPPORTS_WEAK) + DECL_WEAK (decl) = 1; +} + +/* Emit any pending weak declarations. */ + +void +weak_finish () +{ +#ifdef HANDLE_PRAGMA_WEAK + if (HANDLE_PRAGMA_WEAK) + { + struct weak_syms *t; + for (t = weak_decls; t; t = t->next) + { + ASM_WEAKEN_LABEL (asm_out_file, t->name); + if (t->value) + ASM_OUTPUT_DEF (asm_out_file, t->name, t->value); + } + } +#endif +} + +void +assemble_alias (decl, target) + tree decl, target; +{ +#ifdef ASM_OUTPUT_DEF + char *name; + + make_decl_rtl (decl, (char*)0, 1); + name = XSTR (XEXP (DECL_RTL (decl), 0), 0); + + /* Make name accessible from other files, if appropriate. */ + + if (TREE_PUBLIC (decl)) + { +#ifdef ASM_WEAKEN_LABEL + if (DECL_WEAK (decl)) + ASM_WEAKEN_LABEL (asm_out_file, name); + else +#endif + if (output_bytecode) + BC_GLOBALIZE_LABEL (asm_out_file, name); + else + ASM_GLOBALIZE_LABEL (asm_out_file, name); + } + + ASM_OUTPUT_DEF (asm_out_file, name, IDENTIFIER_POINTER (target)); + TREE_ASM_WRITTEN (decl) = 1; +#else + warning ("alias definitions not supported in this configuration"); +#endif +} |