Page MenuHomeFreeBSD

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
This document is not UTF8. It was detected as ISO-8859-1 (Latin 1) and converted to UTF8 for display.
Index: head/contrib/gcc/ChangeLog
===================================================================
--- head/contrib/gcc/ChangeLog (revision 52750)
+++ head/contrib/gcc/ChangeLog (revision 52751)
@@ -1,17061 +1,17241 @@
+Sun Oct 24 23:54:10 PDT 1999 Jeff Law (law@cygnus.com)
+
+ * gcc-2.95.2 Released.
+
+Mon Oct 25 00:43:05 1999 Jeffrey A Law (law@cygnus.com)
+
+ * loop.c (note_set_pseudo_multiple_uses_retval): New variable.
+ (note_set_pseudo_multiple_uses): New function.
+ (check_dbra_loop): Use not_set_pseudo_multiple_uses to determine
+ if a pseudo set in the loop exit is used elsewhere.
+
+Wed Oct 20 10:46:41 1999 Richard Earnshaw (rearnsha@arm.com)
+
+ * jump.c (jump_optimize_1): More accurately detect casesi insns.
+
+Wed Oct 20 22:57:58 1999 Jeffrey A Law (law@cygnus.com)
+
+ * toplev.c (main): Do not turn on strict aliasing by default.
+ * invoke.texi: Corresponding changes.
+
+ * sparc.md (movsf_const_intreg): If splitting, length must be > 1.
+ (movdf_const_intreg_sp64): Similarly.
+
+ * local-alloc.c (update_equiv_regs): Check the correct insn
+ for pre-existing REG_EQUIV notes.
+
+Tue Oct 19 02:03:00 1999 Jeffrey A Law (law@cygnus.com)
+
+ Revert this change. Gavin's patch to operand_equal_p is a better fix.
+ * fold-const.c (fold_range_test): Do not try to fold the range
+ test if the rhs or lhs has side effects.
+
+ Fri Sep 17 15:05:27 1999 Gavin Romig-Koch <gavin@cygnus.com>
+ * fold-const.c (operand_equal_p): Pay attention to side effects.
+
+ * reg-stack.c (stack_result): Aggregates are not returned in
+ stack registers.
+
+Mon Oct 18 01:41:35 1999 Jeffrey A Law (law@cygnus.com)
+
+ * combine.c (get_last_value): If the last set of a register
+ is after subst_low_cuid, then we can not use it to determine
+ the register's last value.
+
+Sat Oct 16 15:20:15 1999 Jeffrey A Law (law@cygnus.com)
+
+ Sat Oct 16 00:07:01 1999 Richard Henderson <rth@cygnus.com>
+ * gcse.c (hash_expr_1): Add in MEM_ALIAS_SET.
+ (expr_equiv_p): Reject memories with different alias sets.
+
+Wed Oct 13 01:46:14 1999 Jeffrey A Law (law@cygnus.com)
+
+ Wed Oct 13 01:44:29 1999 Carol LePage <carolo@hal.com>
+ * configure.in (sparc-hal-solaris2*): Fix xm_file, xm_defines,
+ float_format and thread_file definitions.
+ * configure: Rebuilt.
+
+Tue Oct 12 17:09:38 1999 David Edelsohn <edelsohn@gnu.org>
+
+ * collect2.c (main): Do preliminary link on AIX if rflag.
+
+Mon Oct 11 23:35:19 1999 Jeffrey A Law (law@cygnus.com)
+
+ Fri Sep 3 09:14:32 1999 Marc Espie <espie@tetto.liafa.jussieu.fr>
+ * tlink.c (scan_linker_output): Skip the initial underscore in
+ a mangled name if appropriate.
+
+ Fri Aug 27 19:19:43 CEST 1999 Marc Espie <espie@cvs.openbsd.org>
+ * config/openbsd.h (SET_ASM_OP): Define.
+
+ Mon Oct 4 08:54:12 1999 Alexandre Oliva <oliva@lsd.ic.unicamp.br>
+ * cppinit.c (is_idchar initializer): Loosen tests to accept
+ gcc 2.>6 and >2.
+
+ Tue Jun 29 01:37:53 1999 Jeffrey A Law (law@cygnus.com)
+ * pa.h (CPP_SPEC): Conditionally add -D__STDC_EXT__ to the cpp
+ command line.
+
+ Thu Sep 2 20:08:23 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
+ * regmove.c (fixup_match_1): Don't change an unchanging register.
+ (stable_but_for_p): Renamed to:
+ (stable_and_no_regs_but_for_p). Reject unchanging registers too.
+ Changed all callers.
+
+ Tue Aug 17 22:06:11 1999 Jan Hubicka <hubicka@freesoft.cz>
+ * haifa-sched.c (insn_unit): Fix typo on out of range test.
+ * sched.c (insn_unit): Likewise.
+
+Sun Oct 10 20:58:27 1999 David Edelsohn <edelsohn@gnu.org>
+
+ * rs6000.md (scc patterns): Disable most SImode variants if
+ TARGET_POWERPC64.
+ * rs6000.c (expand_block_move): Fix gen_movsi typos.
+
+ Thu Oct 7 23:06:50 1999 Richard Henderson <rth@cygnus.com>
+ * rs6000.md (fix_truncdfsi2_internal+1): Emit a clobber
+ before the fctiwz insn.
+
+ Thu Oct 7 00:36:17 1999 Diego Novillo <dnovillo@cygnus.com>
+ * config/rs6000/rs6000.c (secondary_reload_class): For TARGET_ELF
+ make sure that HIGH instructions are copied into BASE_REGS.
+
+Thu Sep 30 05:40:34 1999 Richard Earnshaw <rearnsha@arm.com>
+
+ * c-lang.c (finish_file case ndef ASM_OUTPUT_{CON,DE}STRUCTOR):
+ Correctly build argument list to constructor and destructor functions.
+
+Wed Sep 22 17:55:31 1999 David Edelsohn <edelsohn@gnu.org>
+
+ * rs6000.c (expand_block_move): DImode loads and stores require
+ word-aligned displacements. Increment address registers with
+ adddi3 on 64-bit platform. Use TARGET_POWERPC64 not TARGET_64BIT.
+
+Wed Sep 22 07:07:44 1999 Alexandre Oliva <oliva@lsd.ic.unicamp.br>
+
+ * config/i386/sol2.h (PREFERRED_DEBUGGING_TYPE): Use stabs.
+ (ASM_SPEC): Moved from sol2dbg.h. Added work-around for gas.
+ * config/i386/sol2dbg.h: Removed.
+ * config/i386/sol2gas.h: New file, to enable gas work-around.
+ * configure.in: Use i386/sol2gas.h on Solaris/x86 --with-gas.
+ Don't use sol2dbg.h.
+ * configure: Rebuilt.
+
+Wed Sep 15 21:20:38 1999 Mark Mitchell <mark@codesourcery.com>
+
+ * c-typeck.c (qualify_type): Merge qualifiers from both types.
+
+Wed Sep 15 10:07:27 1999 Scott Bambrough <scottb@netwinder.org>
+
+ * config/arm/linux-elf.h: define NO_IMPLICIT_EXTERN_C
+
+Tue Sep 7 16:50:59 1999 David Edelsohn <edelsohn@gnu.org>
+
+ * rs6000.h (ASM_FILE_START): Specify complete filename, including
+ path, in .file directive.
+
+Tue Sep 7 01:27:21 1999 Jeffrey A Law (law@cygnus.com)
+
+ Thu Sep 2 00:06:43 1999 Jeffrey A Law (law@cygnus.com)
+ * fold-const.c (fold_range_test): Do not try to fold the range
+ test if the rhs or lhs has side effects.
+
+ Sun Aug 29 03:27:23 1999 Scott Weikart <scott@igc.apc.org>
+ * fix-header.c (main): Do not pass a null pointer to strcmp.
+
+ Thu Aug 19 14:42:38 1999 Mike Stump <mrs@wrs.com>
+ Mark Mitchell <mark@codesourcery.com>
+ * c-common.c (c_get_alias_set): Fix support for pointers and
+ references.
+
+ Fri Aug 27 01:03:48 1999 Jim Kingdon <http://developer.redhat.com>
+ with much help from Jeffrey A Law and Richard Henderson
+ * i386.md: In the 6 insns which call output_fix_trunc,
+ earlyclobber operands[0].
+
+ Fri Aug 27 01:01:51 1999 Philip Blundell <pb@nexus.co.uk>
+ * jump.c (duplicate_loop_exit_test): Call reg_scan_update after
+ creating new registers.
+
+ Fri Aug 27 15:35:24 1999 Jeffrey A Law (law@cygnus.com)
+ * cse.c (fold_rtx): Work around bug in Sun V5.0 compilers.
+ * pa.c (emit_move_sequence): Do not stop on SUBREG_WORD of an
+ operand.
+
+ Tue Aug 31 11:51:06 1999 Jim Kingdon <http://developer.redhat.com>
+ * i386.c (output_strlen_unroll): Don't write xops[7]
+ label if it wasn't set.
+
+ Fri Aug 27 09:36:17 1999 Andreas Schwab <schwab@suse.de>
+ * function.c (assign_stack_temp_for_type): Fix change of Mar 5 for
+ the fact that ALIGN is measured in bits, not bytes.
+
+ Wed Aug 25 14:00:18 1999 Jason Merrill <jason@yorick.cygnus.com>
+ * c-common.c (combine_strings): Always set TREE_CONSTANT.
+
+Wed Aug 18 01:16:43 1999 David S. Miller <davem@redhat.com>
+
+ * config/sparc/sparc.c (sparc_block_profiler): Fix typo in Aug 2
+ change.
+
Mon Aug 16 01:29:24 PDT 1999 Jeff Law (law@cygnus.com)
* gcc-2.95.1 Released.
1999-08-13 Michael Meissner <meissner@cygnus.com>
* Makefile.in (GCC_FOR_TARGET): Move -B./ after the tooldir -B.
Fri Aug 13 01:42:24 1999 Jeffrey A Law (law@cygnus.com)
Tue Aug 3 00:03:41 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fixincludes: Fix the return type of bsearch, char* -> void*.
* fixinc/inclhack.def: Likewise.
Fri Aug 13 01:29:57 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* dwarfout.c (fundamental_type_code): Return FT_boolean for
INTEGER_TYPE with precision==1, it's __java_boolean.
1999-08-11 Richard Earnshaw (rearnsha@arm.com)
* emit-rtl.c (mark_reg_pointer): Don't increase the alignment of
a register that is already known to be a pointer.
1999-08-11 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/inclhack.tpl: Only install assert.h conditionally.
* fixinc/inclhack.sh: Regenerated.
* fixinc/fixincl.sh: Regenerated.
Wed Aug 11 00:34:22 1999 Joe Buck <jbuck@synopsys.com>
* invoke.texi: s/GNU CC/GCC/ for consistency with gcc.texi.
Fix documentation of -ansi flag to describe its C++ behavior.
Remove bogus reference to GCC 2.9.
Tue Aug 10 22:40:36 1999 Jeffrey A Law (law@cygnus.com)
Thu Aug 5 22:27:15 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* config/sh/lib1funcs.asm (___movstrSI0): Change or r0,r0,r0 to nop.
(___mulsi3): Use '!' comment character.
Sat Aug 7 00:06:30 1999 Jeffrey A Law (law@cygnus.com)
* gcc.texi: Update bug reporting text.
Tue Jul 20 17:07:54 1999 Richard Henderson <rth@cygnus.com>
* rs6000.h (struct rs6000_args): Add sysv_gregno.
* rs6000.c (init_cumulative_args): Init sysv_gregno.
(function_arg_boundary): Align DFmode.
(function_arg_advance): Restructure for ABI_V4; use sysv_gregno
to get fp reg and stack overflow correct.
(function_arg): Likewise.
(function_arg_pass_by_reference): True for TFmode for ABI_V4.
(setup_incoming_varargs): Restructure for ABI_V4; use
function_arg_advance to skip final named argument.
(expand_builtin_saveregs): Properly unskip the last integer arg
when doing varargs. Adjust overflow location calculation.
* ginclude/va-ppc.h (struct __va_list_tag): Make gpr and fpr
explicitly unsigned.
(__VA_FP_REGSAVE): Use new OFS argument instead of AP->fpr directly.
(__VA_GP_REGSAVE): Similarly.
(__va_longlong_p): Delete.
(__va_arg_type_violation): New declaration.
(va_arg): Restructure. Flag promotion errors. Align double.
TFmode passed by reference.
* rs6000.md (movdi_32+1): Use GEN_INT after arithmetic
in the HOST_BITS_PER_WIDE_INT > 32 case.
1999-08-6 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* reg-stack.c (change_stack) Fixed problem with negative array index.
Fri Aug 6 20:41:08 1999 Jeffrey A Law (law@cygnus.com)
Mon Jul 19 15:09:29 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (arithmetic, logical, and shift Rc combiner patterns):
Disable patterns performing SImode comparisons with SImode values
if TARGET_POWERPC64 and instruction does not sign-extend or does
not mask to narrower than SImode, i.e. where bit 31 and bit 63 may
differ for signed quantities.
(indirect_jump): Add expander to choose RTL based on TARGET_64BIT.
(tablejump): Patterns contingent on TARGET_64BIT not TARGET_POWERPC64.
(decrement_and_branch_on_count): Add 64-bit variant.
Thu Aug 5 02:40:42 1999 Jeffrey A Law (law@cygnus.com)
* gcc.c: Update URLs and mail addresses.
* gcc.texi: Likewise.
Thu Aug 5 01:14:13 1999 Daniel Jacobowitz <drow@false.org>
* rs6000.c (current_file_function_operand): Return zero for
weak functions.
(rs6000_encode_section_info): Do not set SYMBOL_REF_FLAG for
weak symbols.
* rs6000.h (ENCODE_SECTION_NIFO): Do not set SYMBOL_REF_FLAG
for weak symbols.
Thu Aug 5 00:56:30 1999 Geoffrey Keating <geoffk@cygnus.com>
* rs6000.c (rs6000_stack_info): For ABI_V4/ABI_SOLARIS -fpic, always
allocate space in the stack frame for the PIC register.
Thu Aug 5 00:20:47 1999 Jeffrey A Law (law@cygnus.com)
* m68k.md (xordi3, anddi3): These patterns are not available on
the coldfire.
Wed Aug 4 23:39:20 1999 Mark Mitchell <mark@codesourcery.com>
* real.c (GET_REAL): Don't violate ANSI/ISO aliasing rules.
(PUT_REAL): Likewise.
Wed Aug 4 02:15:32 1999 Richard Henderson <rth@cygnus.com>
* jump.c (delete_insn): Delete the addr_vec when deleting a tablejump.
Wed Aug 4 01:08:44 1999 Jeffrey A Law (law@cygnus.com)
* flow.c (delete_unreachable_blocks): Do not call merge_blocks
or tidy_fallthru_edge if the last insn in the block is not
an unconditional jump or a simple conditional jump.
Tue Aug 3 03:51:20 1999 Jeffrey A Law (law@cygnus.com)
* cse.c (cse_insn): Fix dumb thinko in last change.
Mon Aug 2 23:45:45 1999 Hans-Peter Nilsson <hp@bitrange.com>
* dwarf2out.c (add_location_or_const_value_attribute): Correct
test for sizes of passed and declared parameter types.
Mon Aug 2 12:45:09 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (override_options): Don't force ALPHA_TP_PROG for ev6.
Mon Aug 2 01:34:22 1999 Jeffrey A Law (law@cygnus.com)
* fix-header.c (main): When testing for CONTINUED, use string
equality, not pointer equality.
Mon Aug 2 01:27:24 1999 Dan Nicolaescu <dann@ics.uci.edu>
* sparc.c (sparc_block_profiler): Use the %g2 register, not %o0.
Sun Aug 1 22:46:42 1999 Jeffrey A Law (law@cygnus.com)
* cse.c (cse_insn): Fix loop which deletes insns after a jump
that has become an unconditional jump.
* m68k.c (output_function_prologue): Fix typo in CPU32 case.
(output_function_epilogue): Similarly.
Tue Jul 20 12:37:30 1999 Hans-Peter Nilsson <hp@bitrange.com>
* dwarf2out.c (output_abbrev_section): Terminate with a zero.
Thu Jul 15 15:40:09 1999 Jim Wilson <wilson@cygnus.com>
* tree.c (build_type_attribute_variant): Move current_obstack restore
after build_qualified_type call.
Fri Jun 4 03:20:40 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.c (fixup_addr_diff_vecs): Emit braf reference label.
(braf_label_ref_operand): Delete.
* sh.h (PREDICATE_CODES): Remove braf_label_ref_operand.
* sh.md (casesi_jump_2): Operand1 is now the inside of a
label_ref, and has no predicate.
The patten has a predicate to guard against invalid substitutions.
(dummy_jump): Delete.
(casesi): Update use of casesi_jump_2.
Thu Jul 31 12:34:45 1999 Joe Buck <jbuck@synopsys.com>
* gcc.texi: Use terms "GNU Compiler Collection" and "GCC".
Also update copyright.
Wed Jul 28 21:39:31 PDT 1999 Jeff Law (law@cygnus.com)
* gcc-2.95 Released.
* verison.c: No longer a prerelease.
Wed Jul 28 13:49:03 1999 Jeffrey A Law (law@cygnus.com)
* README: Update.
Sun Jul 25 21:40:33 1999 Jeffrey A Law (law@cygnus.com)
* gcc.texi: More changes related to list conversion.
* invoke.texi: Likewise.
Sat Jul 17 23:58:24 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (insv, extzv): Remove SImode dependence in named
patterns. Explicitly generate DImode RTL if PowerPC64 and
operand is DImode.
(insvdi): Reverse start and size in instruction template.
1999-07-17 Alexandre Oliva <oliva@dcc.unicamp.br>
* gcc.texi: Update e-mail addresses and URLs to gcc.gnu.org.
Removed paragraph about compression of files and size limitation,
duplicated in the FAQ. Use gcc-patches for posting patches.
* gcc.c (main): Updated URL with bug reporting instructions to
gcc.gnu.org. Removed e-mail address.
* system.h (abort): Likewise.
1999-07-17 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (stmp-multilib-sub): Make the files extracted
from $(LIBGCC1) writable.
Fri Jul 16 01:39:57 1999 Jeffrey A Law (law@cygnus.com)
* m68k.c (output_function_prologue): Fix computation of save mask
when generating PIC code.
1999-07-12 Joseph S. Myers <jsm28@cam.ac.uk>
* invoke.texi: Typo fixes.
Wed Jul 14 23:28:06 1999 Jeffrey A Law (law@cygnus.com)
* emit-rtl.c (gen_realpart): Issue an error for cases GCC can not
handle at this time instead of silently generating incorrect code.
(gen_imagpart): Likewise.
* reload.c (find_reloads): Emit a USE for a pseudo register without
a hard register if we could not create an optional reload for the
pseudo.
Wed Jul 14 01:57:39 1999 Richard Henderson <rth@cygnus.com>
* regclass.c (scan_one_insn): Notice subregs that change the
size of their operand.
(record_reg_classes): Use that to obey CLASS_CANNOT_CHANGE_SIZE.
Wed Jul 14 01:37:06 1999 Jeffrey A Law (law@cygnus.com)
* configure.in (alpha*-*-*): Include alpha/t-ieee.
* configure: Rebuilt.
* alpha/t-ieee: New file.
Tue Jul 13 10:44:14 1999 Jeffrey A Law (law@cygnus.com)
Wed Jun 16 20:29:00 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* cse.c (cse_insn): Don't put hard register source into tables for
the last insn of a libcall.
* rs6000.c (find_addr_reg): Do not select r0 as an address
register.
Tue Jul 13 00:46:18 1999 Philippe De Muyter <phdm@macqel.be>
* m68k/x-mot3300 (XCFLAGS): List of big files now includes `cse.o'.
Mon Jul 12 23:39:08 1999 Jeffrey A Law (law@cygnus.com)
* rs6000.md (movsf): Do not force easy FP constants into memory.
Sun Jul 11 11:21:24 1999 Jason Merrill <jason@yorick.cygnus.com>
* toplev.c (main): Don't complain about saying -gdwarf.
Fri Jul 9 03:51:52 1999 Jeffrey A Law (law@cygnus.com)
* version.c: Drop "gcc-" prefix from version #.
Thu Jul 8 19:15:51 1999 Jim Wilson <wilson@cygnus.com>
* unroll.c (unroll_loops): Don't delete named CODE_LABEL or
NOTE_INSN_DELETED_LABEL note.
Thu Jul 8 14:18:46 1999 Richard Henderson <rth@cygnus.com>
* m68k.c (output_function_prologue): Add pic register to mask
if live and flag_pic.
(output_function_epilogue): Likewise.
Thu Jul 8 10:28:25 1999 Craig Burley <craig@jcb-sc.com>
* invoke.texi (DEC Alpha Options): Put @end table at
beginning of line, to avoid confusing texi2html.
Wed Jul 7 02:00:04 1999 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* reload1.c (gen_reload): When synthesizing a 3 operand add
sequence, improve test for when to reload OP1 into the reload
register instead of OP0.
Wed Jul 7 01:38:03 1999 Jim Wilson <wilson@cygnus.com>
* unroll.c (unroll_loop): Don't delete NOTE_INSN_DELETED_LABEL notes.
1999-07-07 Manfred Hollstein <mhollstein@cygnus.com>
* m88k/dguxbcs.h (CPP_SPEC): Add missing \ in multi-line
string literal.
Wed Jul 7 01:16:43 1999 Richard Henderson <rth@cygnus.com>
* ginclude/varargs.h (__builtin_va_alist_t): New typedef.
(va_dcl): Use __builtin_va_alist_t.
Wed Jul 7 01:13:31 1999 Jason Merrill <jason@yorick.cygnus.com>
* dwarf2out.c (gen_struct_or_union_type_die): Only remember types
on the permanent_obstack.
* dwarfout.c (output_type): Likewise.
Fri Jul 2 03:05:13 1999 Jeffrey A Law (law@cygnus.com)
* dwarfout.c (field_byte_offset): Correctly compute the object's
byte offset for the first bit of a field which crosses an alignment
boundary on a !BYTES_BIG_ENDIAN target.
Fri Jul 2 01:36:36 1999 Robert Lipe <robertlipe@usa.net>
* fixinc.svr4: Fix <arpa/inet.h> by deleting protos for htons and
ntohs.
Fri Jul 2 00:46:47 1999 Richard Henderson <rth@cygnus.com>
Jeff Law <law@cygnus.com>
* ginclude/varargs.h (va_dcl): Use word_mode for type of
__builtin_va_list.
* except.c: Include intl.h.
(expand_eh_return): Set current_function_cannot_inline.
(save_eh_status, restore_eh_status): Twiddle eh_return_stub_label.
* function.h (struct function): Add eh_return_stub_label.
* flow.c (delete_unreachable_blocks): Don't merge across EH edges.
* Makefile.in (except.o): Depend on intl.h.
Fri Jul 2 00:04:23 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (movdf_hardfloat32): Handle PRE_INC and PRE_DEC the
same as offsettable in cases 1 and 2.
1999-07-01 Mark Kettenis <kettenis@gnu.org>
* config/i386/gnu.h (CPP_SPEC): Define __PIC__ and __pic__ if
-fPIC or -fpic is specified.
Wed Jun 30 23:56:01 1999 Jeffrey A Law (law@cygnus.com)
* expr.c (emit_block_move): Use copy_to_mode_reg for
!TARGET_MEM_FUNCTIONS case too.
Tue Jun 29 01:37:53 1999 Jeffrey A Law (law@cygnus.com)
* mips.md (leasi, leadi): New patterns.
* expr.c (emit_block_move): Properly handle case where one of the
block move arguments has a queued increment or decrement.
(clear_storage): Similarly. Fix formatting goof.
Mon Jun 28 05:32:09 1999 Jeffrey A Law (law@cygnus.com)
* m68k.h (CONDITIONAL_REGISTER_USAGE): Define for !SUN_FPA
case. Also make the PIC register call_used.
* m68k.h (FINALIZE_PIC): Delete.
* m68k.c (finalize_pic): Delete.
* m68k.h (CONDITIONAL_REGISTER_USAGE): Make the PIC register fixed
when -fpic/-fPIC.
Mon Jun 28 05:16:35 1999 Richard Henderson <rth@cygnus.com>
* m68k.h (PREFERRED_RELOAD_CLASS): Don't force any FP const_doubles
to memory.
Mon Jun 28 04:07:27 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (output_toc): Always use hex values for floating-point
constants. Store single-precision values in upper-half of TOC
entry in 64-bit mode.
* rs6000.md (floatsidf2, floatunssidf2): Add !TARGET_POWERPC64
to final constraints.
(fix_truncdfsi2 splitter): Change pattern matching fctiwz.
(fctiwz): Improve accuracy of RTL for pattern.
* rs6000.c (print_operand, case 'L'): Use plus_constant_for_output.
* expmed.c (expand_divmod): Ensure unsigned value fits in reg_note.
Fri Jun 25 06:06:37 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (MASK_SUPPORT_ARCH, MASK_CPU_EV5, MASK_CPU_EV6): Define
such that MASK_SUPPORT_ARCH is not negative.
Fri Jun 25 05:35:44 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (verify_dominator): Properly handle ADDR_VEC and
ADDR_DIFF_VEC insns that appear inside loops.
Thu Jun 24 22:54:05 1999 David Edelsohn <edelsohn@gnu.org>
Jeff Law <law@cygnus.com>
* rs6000.md (movdf_hardfloat32): Revert previous patch.
Handle LO_SUM the same as offsettable in cases 1 and 2.
* rs6000.c (find_addr_reg): Revert previous patch.
Thu Jun 24 22:43:12 1999 Philippe De Muyter <phdm@macqel.be>
* system.h (strstr): New external function declaration.
* acconfig.h (NEED_DECLARATION_STRSTR): New define slot.
* configure.in (GCC_NEED_DECLARATIONS): Check for strstr.
* config.in, configure: Rebuilt.
1999-06-24 Tom Tromey <tromey@cygnus.com>
* gcc.c (main): Read user-specified specs files after computing
additional startfile_prefixes.
1999-06-24 Bruce Korb <ddsinc09@ix.netcom.com>
*fixinc/inclhack.def(end_else_label): combined else_label
and endif_label and fixed the sed expression.
*fixinc/{fixincl.x|inclhack.sh}: regen
Tue Jun 22 01:58:18 1999 Jeffrey A Law (law@cygnus.com)
* rs6000.md (movdf_hardfloat32): Use %X instead of always emitting
'x' when handling non-offsettable addresses
Tue Jun 22 00:20:05 1999 Richard Earnshaw (rearnsha@arm.com)
* final.c (shorten_branches): Don't try to split an insn that has
been deleted.
Mon Jun 21 23:32:17 1999 Jeffrey A Law (law@cygnus.com)
Thu Jun 17 15:07 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixincludes: ISCNTL patch
Mon Jun 21 22:15:50 1999 Jeffrey A Law (law@cygnus.com)
* rs6000.c (find_addr_reg): Handle LO_SUM addresses.
Mon Jun 21 22:14:05 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (movdf_hardfloat32): Fix typo.
Mon Jun 21 20:10:42 1999 Richard Henderson <rth@cygnus.com>
* collect2.c (main): Log frame table count.
(GCC_OK_SYMBOL) [ECOFF]: Accept stGlobal.
(scan_prog_file) [COFF]: Handle frame tables.
* alpha/alpha.h (UNALIGNED_SHORT_ASM_OP): Define.
(UNALIGNED_INT_ASM_OP, UNALIGNED_DOUBLE_INT_ASM_OP): Define.
* alpha/elf.h: Undef them again.
* alpha/vms.h: Remove their definitions.
1999-06-21 Jakub Jelinek <jj@ultra.linux.cz>
* real.c (ereal_from_double): Fix for 64-bit big endian hosts.
* emit-rtl.c (gen_lowpart_common): Add case for hosts where double
fits in HOST_WIDE_INT and one uses union to access a long constant
as double.
Mon Jun 21 17:18:25 1999 Richard Henderson <rth@cygnus.com>
* sparc.c (sparc_override_options): Don't allow profiling for
code models other than medlow.
(sparc_function_profiler): New function from old FUNCTION_PROFILER
macro. Use ASM_GENERATE_INTERNAL_LABEL and MCOUNT_FUNCTION.
(sparc_function_block_profiler): Likewise. Use user_label_prefix.
(sparc_block_profiler): Likewise.
(sparc_function_block_profiler_exit): Likewise.
* sparc.h (FUNCTION_PROFILER): Call new sparc.c function.
(FUNCTION_BLOCK_PROFILER): Likewise.
(BLOCK_PROFILER): Likewise.
(FUNCTION_BLOCK_PROFILER_EXIT): Likewise.
(MCOUNT_FUNCTION): New.
* sparc/pbd.h (FUNCTION_PROFILER): Delete.
(FUNCTION_BLOCK_PROFILER, BLOCK_PROFILER): Delete.
* sparc/sun4o3.h (FUNCTION_PROFILER): Delete.
(MCOUNT_FUNCTION): New.
* sparc/sysv4.h (FUNCTION_BLOCK_PROFILER): Delete.
(BLOCK_PROFILER): Delete.
(MCOUNT_FUNCTION): New.
Mon Jun 21 06:22:21 1999 Mark Elbrecht <snowball3@bigfoot.com>
* i386/djgpp.h (LIB_SPEC): New.
(STARTFILE_SPEC): New.
* i386/xm-djgpp.h (NO_SYS_SIGLIST): Deleted. Now obsolete.
Mon Jun 21 06:19:33 1999 Philippe De Muyter <phdm@macqel.be>
* fixinc/Makefile.in (gnu-regex.o): Do not define STDC_HEADERS in
compiler flags.
* system.h (WSTOPSIG): New macro.
Mon Jun 21 05:33:15 1999 Mumit Khan <khan@xraylith.wisc.edu>
* c-pragma.c (push_alignment): Don't ignore alignments greater than
4 bytes.
(insert_pack_attributes): Take into account member natural
alignment.
* i386/winnt.c (exports_head): New static variable.
(i386_pe_record_exported_symbol): New function.
(i386_pe_asm_file_end): Use.
* i386/cygwin.h (ASM_OUTPUT_COMMON): Record the exported
symbols to be emitted at end of assembly.
(ASM_DECLARE_OBJECT_NAME): Likewise.
(ASM_DECLARE_FUNCTION_NAME): Likewise.
* i386/uwin.h (CPP_SPEC): Use -idirafter instead -iprefix and
-iwithprefix.
Mon Jun 21 05:17:00 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (movdf_hardfloat32): Use worst case insn length
attributes for cases 1 and 2.
* rs6000.c (find_addr_reg): New function.
* rs6000.h (find_addr_reg): Declare.
(offsettable_addr_operand): Delete.
* rs6000.md (movdf_hardfloat32): Handle non-offsettable loads
from and stores to GPRs.
Mon Jun 21 04:44:31 1999 Jeffrey A Law (law@cygnus.com)
* sparc.h (LEGITIMIZE_RELOAD_ADDRESS): Fix paren error introduced
in last change.
Sun Jun 20 17:24:35 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (sched_analyze): Don't clear reg_last_uses on calls.
Sat Jun 19 22:52:55 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (sched_analyze): Mark call-user regs as clobbered
instead of set.
Sat Jun 19 05:40:07 1999 Philip Blundell <pb@nexus.co.uk>
* arm.c (arm_reload_in_hi): Invert sense of test on BYTES_BIG_ENDIAN.
Sat Jun 19 05:25:05 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.h (CONDITIONAL_REGISTER_USAGE): If flag_pic, never use
PIC_OFFSET_TABLE_REGNUM for general alloaction.
(INITIAL_ELIMINATION_OFFSET): Count the fact that the PIC register
must be stacked if it is used for PIC accesses.
* arm.c (use_return_insn): Handle PIC register specially.
(output_return_instruction): Likewise.
(output_func_{prologue,epilogue}): Likewise.
(output_expand_prologue): Likewise.
* arm.c (arm_override_options): Remove warning about PIC code
not being supported.
Fri Jun 18 15:44:18 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_expand_block_move): Use get_insns rather than
gen_sequence as argument to emit_no_conflict_block.
Fri Jun 18 06:48:30 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fixinc/inclhack.def (ioctl_fix_ctrl): Fix the definition of _*ISCTRL().
* fixinc/{fixincl.x,inclhack.sh}: regen
Thu Jun 17 13:28:30 1999 David O'Brien <obrien@FreeBSD.org>
* i386/freebsd-elf.h (LINK_SPEC): Fix typo.
* i386/freebsd-elf.h (FUNCTION_PROFILER): labels are not needed and
the reference to `mcount' was not correct for the ELF on FreeBSD.
Thu Jun 17 02:54:30 1999 Jeffrey A Law (law@cygnus.com)
* invoke.texi (ia32 options): Fix typo.
* emit-rtl.c (operand_subword): Tighten checks for when it is safe
to safe to extract a subword out of a REG.
Wed Jun 16 10:33:02 1999 Jason Merrill <jason@yorick.cygnus.com>
* dwarfout.c (add_incomplete_type): New fn.
(output_type): Call it.
(retry_incomplete_types): New fn.
(dwarfout_finish): Call it.
From Eric Raskin <ehr@listworks.com>:
(output_type): Output types for bases.
Tue Jun 15 01:55:20 1999 David O'Brien <obrien@FreeBSD.org>
* i386/freebsd-elf.h (LINK_SPEC): clean up the linking library
specifications and make it realistic.
(LIB_SPEC): Likewise.
Mon Jun 14 03:55:40 1999 Jeffrey A Law (law@cygnus.com)
* configure.in (rs6000-ibm-aix4.3*, powerpc-ibm-aix4.3*): Do not
require a sub-version #.
* configure: Rebuilt.
1999-06-14 Robert Lipe (robertlipe@usa.net)
* svr4.h (DWARF2_DEBUGGING_INFO): Check for redefinition.
1999-06-14 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* gcc.texi: Mention gcc 2.95 instead of egcs 1.00.
Sun Jun 13 01:08:02 1999 Mark Mitchell <mark@codesourcery.com>
* invoke.texi (node Option Summary): Add -fpermissive flag.
Sat Jun 12 03:40:42 1999 Jeffrey A Law (law@cygnus.com)
* sparc.h (LEGITIMIZE_RELOAD_ADDRESS): Do nothing with operands
that require PIC code sequences.
Fri Jun 11 03:17:51 1999 Jeffrey A Law (law@cygnus.com)
* Makefile.in (libgcc2): Pass MAYBE_USE_COLLECT2 as an argument.
* libgcc2.c (__CTOR_LIST, __DTOR_LIST); Do not provide
initializers is some circumstances.
* fixinc/inclhack.def (endif_label): Add additional selector for
more bogus stuff after #endif statements.
* fixinc/inclhack.sh, fixinc/fixincl.x: Rebuilt.
Thu Jun 10 20:45:27 1999 Mumit Khan <khan@xraylith.wisc.edu>
* i386/cygwin.h (SET_ASM_OP): Define.
Thu Jun 10 20:37:57 1999 Mumit Khan <khan@xraylith.wisc.edu>
* reg-stack.c (stack_reg_life_analysis): Find all the RETURN insns.
Thu Jun 10 14:47:59 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/inclhack.def(sun_auth_proto): We do not know how to
test for the presence of valid prototypes. Delete bypass expr.
(ioctl_fix_ctrl): Correct the selection expression.
* fixinc/inclhack.def(no_double_slash): fixed quoting rules
* fixinc/inclhack.sh: regen
* fixinc/fixincl.x: regen
Thu Jun 10 01:22:59 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (strength_reduce): Disable biv->giv translations and
giv recombination. For the release branch only.
Wed Jun 9 15:57:57 1999 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* rs6000.md (movsi_got_internal_mem): Delete.
* rs6000.h (CONDITIONAL_REGISTER_USAGE): Mark PIC_OFFSET_TABLE_REGNUM.
(GOT_TOC_REGNUM): Delete.
(PIC_OFFSET_TABLE_REGNUM): Define.
(FINALIZE_PIC): Disable.
* rs6000.c (rs6000_got_register): New code for fixed pic register.
(rs6000_replace_regno): Delete.
(rs6000_finalize_pic): Likewise.
(output_prolog): Handle PIC_OFFSET_TABLE_REGNUM.
Wed Jun 9 19:44:26 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (loop_insn_first_p): Don't compare LUIDs when P
is a note; use <= for the compare; advance P while it is
a NOTE.
Wed Jun 9 13:12:24 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/inclhack.def (no_double_slash): Fix quoting for test.
* fixinc/inclhack.sh, fixinc/fixincl.x, fixinc/fixincl.sh; Rebuilt.
* varasm.c (remove_from_pending_weak_list): Verify t->name
is non-NULL before passing it to strcmp.
Wed Jun 9 23:01:17 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* invoke.texi: Add C4x invocation docs.
Wed Jun 9 22:42:49 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (TARGET_EXPOSE_LDP, LEGITIMIZE_RELOAD_ADDRESS):
Define new macros.
* config/c4x/c4x.c (c4x_emit_move_sequence, src_operand): Use
TARGET_EXPOSE_LDP.
(c4x_legitimize_reload_address): New function.
* config/c4x/c4x.md: Update docs.
Wed Jun 9 06:50 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/inclhack.def(sun_auth_proto): bypass the patch if
the typed arguments are not part of a comment
(ioctl_fix_ctrl): Added a purpose comment
* fixinc/fixincl.x: regenerate
* fixinc/inclhack.sh: regenerate
Wed Jun 9 04:14:48 1999 Jeffrey A Law (law@cygnus.com)
* fixincludes: Avoid removing '.'.
* fixinc/fixinc.svr4: Likewise.
* fixinc/fixinc.winnt: Likewise.
* fixinc/inclhack.tpl: Likewise.
* fixinc/fixincl.sh, fixinc/inclhack.sh: Rebuilt.
Wed Jun 9 03:55:34 1999 Jim Wilson <wilson@cygnus.com>
* configure.in (rs6000-ibm-aix4.[12]*): Change rx6000 to rs6000.
* configure: Regenerate.
* configure.in (rs6000-ibm-aix4.[12]*): Delete use of aix41-gld.h.
Add use of x-aix41-gld.
1999-06-09 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fixinc/inclhack.def (sun_catmacro): Escape parens in the select
pattern.
* fixinc/fixincl.x, fixinc/inclhack.sh: Rebuilt.
Wed Jun 9 03:10:34 1999 Mumit Khan <khan@xraylith.wisc.edu>
* c-pragma.c (handle_pragma_token): Handle `#pragma pack()'
correctly.
Tue Jun 8 05:47:48 1999 Richard Earnshaw (rearnsha@arm.com)
* optabs.c (expand_cmplxdiv_wide): Use expand_abs to get the absolute
values.
Mon Jun 7 22:30:37 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/inclhack.def (bad_lval): Remove bogus selector.
* fixinc/inclhack.sh, fixinc/fixincl.x, fixinc/fixincl.sh; Rebuilt.
* fixinc/inclhack.def (avoid_bool): Also catch
"typedef [unsigned] int bool".
* fixinc/inclhack.sh, fixinc/fixincl.x, fixinc/fixincl.sh: Rebuilt.
* m68k/x-hp3bsd44: Delete obsolete and incorrect file.
* configure.in (m68k-hp-bsd4.4): No longer use x-hp3bsd44.
* configure: Rebuilt.
Mon Jun 7 22:05:03 1999 Mark Kettenis <kettenis@gnu.org>
* config/i386/gnu.h: Include <gnu.h> right after <i386/linux.h>,
such that we can override its definitions if necessary.
(CPP_SPEC): New define. Support processor specific predefines via
%(cpp_cpu).
(CC1_SPEC): New define. Support processor specific compiler
options via %(cc1_cpu).
(STARTFILE_SPEC): New define. Use crt0.o instead of crt1.o for
-static.
1999-06-07 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fixinc/inclhack.def (math_gcc_ifndefs): Insert whitespace
between sed's -e flag, and the open-quote following it.
* fixinc/fixincl.x, fixinc/fixincl.sh: Rebuilt.
Mon Jun 7 20:34:20 1999 Robert Lipe <robertlipe@usa.net>
Jeffrey A Law (law@cygnus.com)
* varasm.c (assemble_start_function): Remove the function
from the pending weak decls list when we define a function.
(assemble_variable): Similarly for variables.
(weak_finish): Ignore items on the list with a NULL name.
(remove_from_ending_weak_list); New function to "remove" an item
from the pending weak declarations list.
Mon Jun 7 19:27:07 1999 Jerry Quinn <jquinn@nortelnetworks.com>
* pa.md (fmpyfadd, fmpynfadd, fnegabs): New patterns.
Sun Jun 6 11:58:34 1999 Jakub Jelinek <jj@ultra.linux.cz>
* sparc.md (abstf2): This should be an expand.
(split after abstf2_notv9): Fix mode.
(abstf2_hq_v9): New pattern.
(abstf2_v9): Only use when no hard quad.
(absdf2_v9): Fix if target is not the same as source.
(ashrsi3_extend, ashrsi3_extend2, lshrsi3_extend, lshrsi3_extend2):
Add correct output constraints.
Sat Jun 5 17:04:16 1999 Craig Burley <craig@jcb-sc.com>
From Dave Love to egcs-patches on 20 May 1999 17:38:38 +0100:
* invoke.texi: Clarify text vis-a-vis Intel CPUs.
Fri Jun 4 13:30:27 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* alpha/osf.h (CPP_SUBTARGET_SPEC): Handle -threads.
(LIB_SPEC): Likewise.
Link with -lprof1_r for -g/-pg.
1999-06-04 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* loop.c (check_dbra_loop): Fix change of Jan 19.
Fri Jun 4 00:12:40 1999 Marc Espie <espie@cvs.openbsd.org>
* freebsd-elf.h (SWITCH_TAKES_ARG): Redefine, not define.
(STARTFILE_SPEC): Define, override the svr4.h version.
(ENDFILE_SPEC): Likewise.
Thu Jun 3 23:58:55 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/inclhack.def (limits_ifndefs): Also apply to sys/limits.h
* fixinc/fixincl.x: Regenerated.
* fixinc/inclhack.sh: Regenerated.
Thu Jun 3 07:48 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/inclhack.def(Io_Def_Quotes): corrected sed expression
* fixinc/fixincl.x: regenerate
* fixinc/inclhack.sh: regenerate
Thu Jun 3 22:27:50 1999 Robert Lipe <robertlipe@usa.net>
* i386/udk.h (LINK_SPEC): Correct linker search path for
system libraries.
Thu Jun 3 02:15:07 1999 Jason Merrill <jason@yorick.cygnus.com>
* dwarf2out.c (add_incomplete_type): New fn.
(gen_struct_or_union_type_die): Call it.
(retry_incomplete_types): New fn.
(dwarf2out_finish): Call it.
Thu Jun 3 01:19:03 1999 Jeffrey A Law (law@cygnus.com)
* gcse.c (insert_insn_end_bb): Correct placement of insns when the
current block starts with a CODE_LABEL and ends with a CALL and
we can not find all the argument setup instructions for the CALL.
Wed Jun 2 12:25:55 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (override_options): Thinko in last patch.
* alpha/osf.h (CPP_SUBTARGET_SPEC): Define.
(LIB_SPEC): Recognize -pthread.
Wed Jun 2 07:07 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/fixincl.c(global def): Add FD_SHELL_SCRIPT to mark
fixes that need "file=xxx\n" prepended before invocation
(start_fixer - new): starting the fixer process is complex enough
to warrent its own routine. It prepends the "file=xxx\n" stuff.
(process): uses the new routine; omit usage of putenv()
* fixinc/fixincl.tpl: mark shell scripts with FD_SHELL_SCRIPT
* fixinc/fixincl.x: regenerate
Wed Jun 2 06:36:14 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.md (zero_extendqidi2): Don't allow operand1 to be a memory
reference. Temporary work-around for problems with constant
pool handling.
Wed Jun 2 02:40:43 1999 Jeffrey A Law (law@cygnus.com)
* README, configure.in, gcc.1, gcc.texi: Update name (egcs -> gcc)
and version #s (1.1 -> 2.95) as needed.
* README.g77: Kill way out of date file in the toplevel directory.
Wed Jun 2 00:52:34 1999 David O'Brien <obrien@FreeBSD.org>
* configure.in (i[34567]86-*-freebsdelf): Don't include linux.h,
i386/freebsd-elf.h no longer requires it. Instead include svr4.h.
* configure: Rebuilt.
* i386/freebsd-elf.h (DEFAULT_VTABLE_THUNKS): Define.
(ASM_COMMENT_START, ASM_APP_ON, ASM_APP_OFF, SET_ASM_OP): Likewise.
(PREFERRED_DEBUGGING_TYPE, WCHAR_UNSIGNED): Likewise.
(SWITCH_TAKES_ARG): Likewise.
* i386/freebsd.h: Remove FREEBSD_NATIVE support.
* config/t-freebsd: Moved from config/i386/ so it can used for all
FreeBSD targets.
Mon May 31 02:22:55 1999 Philippe De Muyter <phdm@macqel.be>
* m68k/x-mot3300 (XCFLAGS): Fixed to match stb.o, not f/stb.o.
Wed Jun 2 00:08:34 1999 Robert Lipe <robertlipe@usa.net>
* configure.in (i[34567]86-*-udk*): Install headers with cpio.
* configure: Rebuilt.
Wed Jun 2 00:49:00 EDT 1999 John Wehle (john@feith.com)
* flow.c (mark_regs_live_at_end, insn_dead_p,
mark_set_1, mark_used_regs): Only give FRAME_POINTER_REGNUM
and HARD_FRAME_POINTER_REGNUM special treatment if reload
hasn't run or the frame pointer is needed.
* haifa-sched.c (attach_deaths): Likewise.
* sched.c (attach_deaths): Likewise.
Thu May 27 22:06:52 1999 Mark Mitchell <mark@codesourcery.com>
* cccp.c (handle_directive): Handle backslash-newlines in quoted
strings correctly.
Mon May 31 22:42:02 1999 Jeffrey A Law (law@cygnus.com)
* Remove this patch (from the branch only)
Wed May 26 09:53:05 1999 Mark Mitchell <mark@codesourcery.com>
* fold-const.c (fold): STRIP_NOPS when deciding whether or not
something is a candidate for optimize_bit_field_compare.
Mon May 31 15:23:23 1999 Richard Henderson <rth@cygnus.com>
* alpha.md (reload_*_help): New patterns and splitters.
(reload_*): Use them.
(mov[qh]i): Likewise.
Mon May 31 09:36:11 1999 Cort Dougan <cort@cs.nmt.edu>
* rs6000/linux.h (LINK_SPEC): Use emulation elf32ppclinux.
Sat May 29 19:08:10 1999 Philip Blundell <philb@gnu.org>
* config/arm/aout.h (ASM_OUTPUT_ALIGN): Only define if not already
defined.
* config/arm/elf.h (ASM_OUTPUT_ALIGN): Define.
(MAX_OFILE_ALIGNMENT): Likewise.
Mon May 31 00:45:14 1999 Jeffrey A Law (law@cygnus.com)
* jump.c (jump_optimize_1): Only set CAN_REACH_END if
calculate_can_reach_end returns nonzero.
* Makefile.in (CFLAGS): Remove warning flags.
(WARN_CFLAGS): Disable.
* configure.in (native gas tests): Search for an assembler in the
same manner that the installed compiler will.
* configure: Rebuilt.
* tm.texi (MD_EXEC_PREFIX): Note need to update configure.in too.
* alias.c (find_base_term): Improve handling of addresses
constructed from binary operations.
Sun May 30 14:43:37 1999 Robert Lipe <robertlipe@usa.net>
* fixincl.c: Replace local include scheme with #includes of
gansidecl.h and system.h.
* procopen.c: Likewise.
* server.c: Likewise.
Sun May 30 14:18:40 1999 Jeffrey A Law (law@cygnus.com)
* function.h (cleanup_label, frame_offset): Declare.
(tail_recursion_label, tail_recursion_reentry): Likewise.
(arg_pointer_save_area, rtl_expr_chain): Likewise.
* stmt.c (cleanup_label, frame_offset): Delete extern declarations.
(tail_recursion_label, tail_recursion_reentry): Likewise.
(arg_pointer_save_area, rtl_expr_chain): Likewise.
Fri May 28 03:47:03 1999 Eric Raskin (ehr@listworks.com)
* i386/t-dgux (EXTRA_PARTS): Add crti.o.
(crti.o): Add build rule and dependencies.
Fri May 28 03:07:10 1999 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* rs6000/sysv4.h (CC1_SPEC): Add support for -profile
(LIB_LINUX_SPEC): Likewise.
(LIB_LINUX_SPEC): Add support for -pthread
(CPP_OS_LINUX_SPEC): Likewise.
(CPP_SYSV_SPEC): Avoid redefinitions if both -fpic and -fPIC are
specified
* rs6000.c (output_mi_thunk): Enable full support again.
Thu May 27 13:04:52 1999 H.J. Lu (hjl@gnu.org)
* i386.c (output_fp_cc0_set): Don't check the JUMP_INSN code for
conditional move.
(notice_update_cc, output_float_compare): Enable TARGET_CMOVE support.
(output_float_compare, output_fp_cc0_set): Fix the FLOAT comparison
for IEEE math and CC_FCOMI.
(put_jump_code): No IEEE if CC_FCOMI is set.
1999-05-27 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* fold-const.c (fold_truthop): Make the field reference unsigned
when converting a single bit compare.
Thu May 27 03:07:13 1999 Philip Blundell <pb@nexus.co.uk>
Based on patch by Scott Bambrough and Pat Beirne:
* config/arm/arm.c (making_const_table): New variable.
* config/arm/arm.h (making_const_table): Declare.
(OUTPUT_INT_ADDR_CONST): Mark symbols as position independent if
appropriate.
* config/arm/arm.md (consttable_4, consttable_8, consttable_end):
Keep track of when we are building the constant table.
Thu May 27 02:52:55 1999 Jeffrey A Law (law@cygnus.com)
* varasm.c (STRIP_NAME_ENCODING): Remove default definition.
* output.h (STRIP_NAME_ENCODING): Strip '*' like the old varasm
version did.
Thu May 27 02:40:48 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Don't do biv->giv conversion on constants.
Thu May 27 02:09:27 1999 Jeffrey A Law (law@cygnus.com)
* reload.c (push_reload): Do not call remove_address_replacements
when presented with identical optional reloads.
Wed May 26 14:18:05 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (MASK_FIX, TARGET_FIX): New.
(MASK_*): Reorganize constants.
(CPP_AM_FIX_SPEC): New.
(TARGET_SWITCHES): Add FIX.
(EXTRA_SPECS): Likewise.
(CPP_CPU_EV6_SPEC): Use FIX, not CIX.
(SECONDARY_MEMORY_NEEDED): Likewise.
(REGISTER_MOVE_COST): Likewise.
* alpha.c (override_options): Add FIX support. Always use
ALPHA_TP_PROG for ev6.
* alpha.md (sqrt and mov[sd]i patterns): Use FIX, not CIX.
* alpha/elf.h (ASM_FILE_START): Look at FIX too.
* configure.in (target_cpu_default2) [ev6]: Use FIX, not CIX.
Wed May 26 09:53:05 1999 Mark Mitchell <mark@codesourcery.com>
* fold-const.c (fold): STRIP_NOPS when deciding whether or not
something is a candidate for optimize_bit_field_compare.
Wed May 26 03:54:33 1999 Melissa O'Neill <oneill@cs.sfu.ca>
* fixinc/fixincl.c: (WIFSIGNALED): Define if not already defined.
(WTERMSIG, WIFEXITED, WEXITSTATUS, WIFSTOPPED, WSTOPSIG): Likewise.
(S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP): Likewise.
(S_IROTH, S_IWOTH, S_IXOTH, S_IRWXU, S_IRWXG, S_IRWXO): Likewise.
Wed May 26 02:19:31 1999 Philip Blundell <pb@nexus.co.uk>
* arm.h (NEED_PLT_GOT): Fix mistake in last change.
(GOT_PCREL): New macro. Define to 1 if not already defined.
* arm/elf.h (GOT_PCREL): Define to 0.
* arm.c (arm_finalize_pic): Take into account the setting of
GOT_PCREL.
Tue May 25 14:06:06 1999 Jeffrey A Law (law@cygnus.com)
* output.h (STRIP_NAME_ENCODING): Provide default definition.
* dwarf2out.c (ASM_NAME_TO_STRING): Use STRIP_NAME_ENCODING.
* flow.c (mark_set_1): Do not record BLKmode stores as dead
store elimination candidates.
Mon May 24 14:34:31 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (strength_reduce): Do not clear NOT_EVERY_ITERATION at the
last CODE_LABEL in a loop if we have previously passed a jump
to the top of the loop.
Mon May 24 01:02:58 1999 Mark Mitchell <mark@codesourcery.com>
* stmt.c (expand_end_bindings): Ignore any elements of VARS that
are not VAR_DECLs.
Sun May 23 20:31:16 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (strength_reduce): Grow reg_single_usage as needed.
Sun May 23 10:13:20 1999 David O'Brien <obrien@FreeBSD.org>
* i386/freebsd-elf.h (LINK_SPEC): Change -static to -Bstatic.
Also remove a useless comment.
Sun May 23 10:05:23 1999 Jerry Quinn <jquinn@nortelnetworks.com>
* pa.md (negdf2,negsf2): Use fneg instead of fsub on pa 2.0.
Sat May 22 01:27:49 1999 Mark Mitchell <mark@codesourcery.com>
* expr.h (lang_expand_constant): Guard with #ifdef TREE_CODE.
Thu May 20 10:00:42 1999 Stephen L Moshier <moshier@world.std.com>
* Makefile.in (GCC_FOR_TARGET): Add -I$(build_tooldir)/include.
Thu May 20 09:58:57 1999 Jan Hubicka <hubicka@freesoft.cz>
* function.c (assign_stack_local): Align stack slot propertly.
(assign_outer_stack_local): Likewise.
Thu May 20 10:38:43 1999 Mark Mitchell <mark@codesourcery.com>
* expr.h (lang_expand_constant): Declare.
* toplev.c (lang_expand_constant): Define it.
* varasm.c (output_constant): Use it.
Thu May 20 11:28:53 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* optabs.c (expand_cmplxdiv_straight, expand_cmplxdiv_wide):
Change function definitions to K&R style.
Thu May 20 08:15:00 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/fixincl.c(main): we must not ignore SIGCLD now.
Thu May 20 07:06:39 1999 Alexandre Oliva <aoliva@acm.org>
* fixinc/Makefile.in(gnu-regex.o): add $(INCLUDES) to compile options
* fixinc/fixincl.c(wait_for_pid): K&R-ify arguments
(several places): omit static initialization
(process): use single fd, since only the read fd is used
* fixinc/gnu-regex.c: define 'const' away, if not supported
* fixinc/procopen.c(several places): omit static initialization
* fixinc/server.c: define 'volitile' away, if not supported
1999-05-20 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* config/dbxcoff.h (DBX_OUTPUT_MAIN_SOURCE_FILE_END): Use
asm_fprintf and %L to generate the label name.
* config/dbxelf.h (DBX_OUTPUT_MAIN_SOURCE_FILE_END): Likewise.
(ASM_OUTPUT_SOURCE_LINE): Correct generation of internal labels.
Thu May 20 01:40:55 1999 Jeffrey A Law (law@cygnus.com)
* jump.c (can_reverse_comparison_p): Do not abort if the comparison
insn for a conditional jump can not be found.
Wed May 19 23:58:58 1999 Jeffrey A Law (law@cygnus.com)
* mips.h (ENCODE_SECTION_INFO): Do not perform GP optimizations
on variables in specific sections other than .sbss and .sdata.
Tue May 18 11:20:48 1999 Mark Mitchell <mark@codesourcery.com>
* stmt.c (expand_return): Call start_cleanup_deferral and
end_cleanup_deferral around conditional code.
Wed May 19 03:10:08 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/fixincl.tpl: Avoid depending on ANSI C features for
filename lists. Utilizes new AutoGen function "krstr".
* fixinc/fixincl.x: Rebuilt.
Wed May 19 02:47:11 1999 Jan Hubicka (hubicka@freesoft.cz)
* i386.c (output_float_compare): Avoid GNU-C extensions.
Wed May 19 00:50:24 1999 Jeffrey A Law (law@cygnus.com)
* version.c: Bump version to gcc-2.95 prerelease.
Tue May 18 03:53:37 1999 Craig Burley <craig@jcb-sc.com>
Improve open-coding of complex divide:
* flags.h: Declare new front-end-malleable flag.
* toplev.c: Define new flag.
* optabs.c (expand_cmplxdiv_straight): New function to do original
open-coding.
(expand_cmplxdiv_wide): New function to do new open-coding,
from Toon Moene, with changes (call to emit_barrier, dropping
of spurious `ok = 1;', plus the obvious `break;' -> `return 0;').
(expand_binop): A bit of spacing fixing, while at it.
Use new functions instead of inlining the open-coding code.
Tue May 18 00:51:46 1999 Krister Walfridsson <cato@df.lth.se>
* configure.in (arm*-*-netbsd*): Use collect2.
(i[34567]86-*-netbsd*): Likewise.
(m68k*-*-netbsd*): Likewise.
(ns32k-*-netbsd*): Likewise.
(sparc-*-netbsd*): Likewise.
(vax-*-netbsd*): Likewise.
* configure: Rebuilt.
Tue May 18 00:21:34 1999 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cppspec.c: Insert -no-gcc into command line unless -gcc was
given by user.
* gcc.c (default_compilers): Define __GNUC__ and
__GNUC_MINOR__ only if -no-gcc was not given.
* objc/lang-specs.h: Likewise.
* cpp.texi: Document -x and -std options; explain that -lang
is no longer supported. Minor related corrections.
Mon May 17 23:56:39 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* Makefile.in (stmp-fixproto): Pass location of mkinstalldirs to
fixproto.
* fixproto: Avoid unportable constructs such as `basename' and
`mkdir -p'. Use mkinstalldirs from the environment if `mkdir -p'
fails.
* fixinc/fixincl.c: Remove #error, it is not portable.
Mon May 17 23:50:41 1999 Marc Espie <espie@cvs.openbsd.org>
* collect2.c (main): Fix typo in COLLECT2_HOST_INITIALIZATION.
Mon May 17 19:45:41 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* fixinc/fixincl.c (process): Wait for children from chain_open()
to avoid creating zombies.
* fixinc/inclhack.tpl: Removed no-op pipe.
* fixinc/inclhack.sh fixinc/fixincl.sh: regenerate
Mon May 17 07:23:34 1999 Mark Mitchell <mark@codesourcery.com>
* tree.def (TYPE_NONCOPIED_PARTS): Revise documentation to match
reality.
* expr.c (init_noncopied_parts): Don't generate initializers for
parts that don't need them.
Mon May 17 02:56:35 PDT 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Oct 31 05:08:34 CET 1998 Jan Hubicka (hubicka@freesoft.cz)
* reg-stack.c: Do not emit pop insns after cc0 setter.
(emit_pop_insn): Do not emit insn in case WHEN is NULL.
(compare_for_stack_reg): Update REG_DEAD note and
do not emit push insn.
* i386.c: (output_float_compare): Handle new REG_DEAD notes.
Mon May 17 01:57:37 1999 David Daney <daney@ibw.com.ni>
* i386/sol2.h (LINK_SPEC): Do not pass "-z text" to the linker
if -mimpure-text.
1999-05-17 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* m68k.c (standard_68881_constant_p): Don't accept -0.0 as valid
68881 constant.
* fold-const.c (fold_truthop): When converting a one-bit
comparison don't sign extend the constant.
* cse.c (cse_insn): Copy SRC_CONST before putting it in the
REG_EQUAL note.
1999-05-17 Mike Stump <mrs@wrs.com>
* rs6000/vxppc.h (CPP_SPEC): Fix support for vararg functions.
Sat May 15 14:22:40 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/hackshell.tpl: Fix mis-applied patch.
* fixinc/inclhack.sh: Regenerated.
Thu May 13 21:05:55 1999 Mark Kettenis <kettenis@gnu.org>
* fixinc/mkfixinc.sh: Add the Hurd (*-*-gnu*) to the list of
targets that do not need any fixes.
Sat May 15 14:12:38 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (set_lo_sum+2): New splitter to load large
const_ints.
Sat May 15 14:09:08 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (decrement_and_branch_on_count): Disabled.
(doloop_begin, doloop_end): New patterns.
(*rptb_init): Added extra operands.
Fri May 14 21:31:36 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (*umulqi3_highpart_clobber): Fix operand 2
constraints order.
1999-05-14 Ulrich Drepper <drepper@cygnus.com>
* fixinc/fixinc.x86-linux-gnu (FD_ZERO): Fix operand numbers in
asm input operands.
Thu May 13 15:34:18 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (mask_constant): Delete.
(mask_operand): Move mask_constant() body to here.
* rs6000.h (mask_constant): Delete declaration.
* rs6000.md (nabsdi2): Reverse subtraction in splitter.
Thu May 13 02:25:01 1999 Jeffrey A Law (law@cygnus.com)
* cpp.texi: Fix some typos.
Thu May 13 01:49:55 1999 Graham Stott <GrahamS@RCP.co.uk>
* loop.c (maybe_eliminate_biv): Check regno against
max_reg_before_loop.
* i386.c (memory_address_info): Correct the scale
factor test.
Thu May 13 01:31:19 1999 Nick Burrett <nick.burrett@btinternet.com>
* arm.md (nop): Backout Apr 27 change. Ensure REGISTER_PREFIX is
applied to each register.
* aof.h (ASM_FILE_START): Define register `r0'.
1999-05-12 20:22 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* configure.in: Make --enable-cpp and --with-cpp-install-dir
documented options. Enable the cpp driver by default.
* configure: Rebuilt.
Wed May 12 18:08:48 1999 David Edelsohn <edelsohn@gnu.org>
Richard Henderson <rth@cygnus.com>
* rs6000.c (print_operand) [w]: Calculate signed constant more clearly.
(rs6000_allocate_stack_space): Print as hexadecimal value.
* rs6000.h (CONST_OK_FOR_LETTER_P): 'L' checks for a signed,
16-bit shifted constant. Fix typo for 'P'.
(EXTRA_CONSTARINT): 'T' checks for a 32-bit mask operand.
* rs6000.md (movsi, addsi3_internal1, movdi, adddi3_internal1):
Use 'L' for shifted constant.
(anddi3_internal3): Fix typo.
(32-bit mask patterns): Use 'T'.
Wed May 12 07:30:31 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/fixincl.c(quoted_file_exists): new procedure to ensure that
a file exists before trying to copy it into the destination
(extract_quoted_files): use that routine.
Wed May 12 07:27:31 1999 Craig Burley <craig@jcb-sc.com>
Allow front end (like g77's) to override maintenance of errno:
* expr.c (expand_builtin): Bother with errno only if
flag_errno_math.
* flags.h: Declare flag_errno_math.
* toplev.c: Define flag_errno_math.
Tue May 11 23:55:49 1999 Jeffrey A Law (law@cygnus.com)
* fixproto: Change "mkdir" calls to "mkdir -p"
* fixinc/inclhack.def (io_def_quotes): Consistently allow multiple
whitespace characters between the "define" and the name of the macro.
* fixinc/fixincl.x, fixinc/inclhack.sh: Rebuilt.
Tue May 11 20:46:37 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_expand_block_move): Handle TImode registers
used with ADDRESSOF.
(alpha_expand_block_clear): Handle ADDRESSOF specially.
1999-05-11 Ulrich Drepper <drepper@cygnus.com>
* fixinc/fixinc.x86-linux-gnu (FD_ZERO): Remove unneccessary
memory output operand which irritates gcc.
Tue May 11 11:45:16 1999 Dave Brolley <brolley@cygnus.com>
* toplev.c (documented_lang_options): Add -MD, -MMD, -M and -MM for
cpplib-enabled compilers.
Tue May 11 11:34:56 1999 Vladimir Makarov <vmakarov@tofu.to.cygnus.com>
* config/sparc/sparc.h (GO_IF_LEGITIMATE_ADDRESS): Add parentheses
around &&.
Mon May 10 13:51:24 1999 Nick Clifton <nickc@cygnus.com>
* tm.texi (FUNCTION_ARG): Stack element of PARALLEL must come
first.
Tue May 11 01:32:01 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/inclhack.def (sun_auth_proto): Apply to all targets.
(sysz_stdlib_for_sun): Similarly.
* fixinc/fixincl.x, fixinc/inclhack.sh: Rebuilt.
Mon May 10 20:34:10 1999 Jim Wilson <wilson@cygnus.com>
* config/mips/elf.h (UNIQUE_SECTION_P): Undef.
* config/mips/elf64.h (UNIQUE_SECTION_P): Undef.
* config/mips/mips.h (UNIQUE_SECTION_P): Define to 0.
1999-05-10 18:21 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* cppfiles.c (initialize_input_buffer): New function.
(finclude): Call it, if pfile->input_buffer is NULL. Accept
any character device as an input file.
(read_and_prescan): Use pfile->input_buffer and
pfile->input_speccase.
* cppinit.c (cpp_cleanup): Free pfile->input_buffer and
pfile->input_speccase.
* cpplib.h (cpp_reader): Add input_buffer, input_speccase, and
input_buffer_len members. Use memcpy in CPP_PUTS_Q.
* cppmain.c: Buffer output in the token_buffer; throttle
number of calls to fwrite; check for errors from fwrite.
1999-05-10 18:21 -0400 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cppspec.c: Treat two non-option arguments as input and
output file. Three or more non-option args is an error.
Clean up.
* gcc.c (default_compilers): Pass -$ to the preprocessor.
* objc/lang-specs.h: Likewise.
Mon May 10 12:59:20 1999 Jeffrey A Law (law@cygnus.com)
* optabs.c (emit_cmp_and_jump_insns): Handle the case where both
operands to the comparison are constants.
Mon May 10 07:28:10 1999 Bruce Korb <autogen@autogen.freeservers.com>
* fixinc/inclhack.def(arm_norcroft_hint): check before fixing
(no_double_slash): portability
(math_exception): added reminder comment
Mon May 10 01:28:10 1999 Craig Burley <craig@jcb-sc.com>
From Fri May 7 9:31:41 1999 Donn Terry (donn@interix.com):
* varasm.c (mark_constant_pool): Add some transitive closure.
Sun May 9 22:51:04 1999 Craig Burley <craig@jcb-sc.com>
Fix gcc.dg/990506-0.c:
* c-typeck.c (require_complete_type): Handle ERROR_MARK input.
Sun May 9 13:19:12 1999 Jeffrey A Law (law@cygnus.com)
* gcse.c (cprop_insn): Do not try to simplify a simple jump.
Sun May 9 11:12:19 1999 Philip Blundell <bp@nexus.co.uk>
* config/arm/arm.h (ASM_OUTPUT_MI_THUNK): Add (PLT) to branch if
necessary. Reported by jim@federated.com.
Sat May 8 23:05:35 1999 Jeffrey A Law (law@cygnus.com)
* pa.h (PRINT_OPERAND_ADDRESS): Output "%r0", not "r0" for the
base register in an absolute memory address.
* pa.md (conditional moves): Avoid using immediate zero for
register zero.
Sat May 8 06:23:21 1999 Philip Blundell <pb@nexus.co.uk>
Based on patch by Scott Bambrough:
* config/arm/arm.h (NEED_PLT_GOT): New macro. Set to 0 if not
already defined.
* config/arm/elf.h (NEED_PLT_GOT): Define to flag_pic.
* config/arm/arm.md (call_symbol, call_value_symbol et al.): If
NEED_PLT_GOT is true, add explicit "(PLT)" to generated branches.
* config/arm/arm.c (output_func_epilogue,
output_return_instruction): Likewise for calls to abort.
Sat May 8 01:57:58 1999 Donn Terry (donn@interix.com)
* calls.c (rtx_for_function_call): Extend function pointer being
passed to chkr_check_exec_libfunc, if needed.
Sat May 8 01:51:50 1999 David Edelsohn <edelsohn@gnu.org>
* ginclude/stdarg.h (__va_rounded_size): Use long type for
rounding on AIX.
* ginclude/varargs.h: Likewise.
Sat May 8 01:47:20 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* invoke.texi: Remove duplicates in the description of -d
letters. Fix use of @item vs. @itemx.
Sat May 8 01:43:02 1999 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* rs6000.h (RS6000_VARARGS_OFFSET): Die die die.
(CUMULATIVE_ARGS): Remove varargs_offset; update commentary.
* rs6000.c (setup_incoming_varargs): Fix typo last change.
(init_cumulative_args): Remove varargs_offset references.
* rs6000/linux.h (NO_IMPLICIT_EXTERN_C): Define.
(MD_EXEC_PREFIX, MD_STARTFILE_PREFIX): Undefine.
Sat May 8 01:34:19 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* reload1.c (gen_mode_int): New function.
(reload_cse_move2add): Use it to generate the new constants.
Sat May 8 01:25:09 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* varasm.c (output_constant): Do nothing if -fsyntax-only.
Fri May 7 19:10:15 1999 Vladimir Makarov <vmakarov@tofu.to.cygnus.com>
* sparc.h (GO_IF_LEGITIMATE_ADDRESS): Prohibit REG+REG addressing
for TFmode when there are no instructions which accept REG+REG
instructions.
Fri May 7 12:38:54 1999 Jim Wilson <wilson@cygnus.com>
* mips/elf64.h (MAKE_DECL_ONE_ONLY, UNIQUE_SECTION_P): Define.
* mips/mips.c (mips_select_rtx_section): When TARGET_MIPS16, use
function_section instead of text_section.
* mips/mips.h (ENCODE_SECTION_INFO): Add check for UNIQUE_SECTION_P
in TARGET_MIPS16 STRING_CST handling.
Fri May 7 09:54:11 1999 Nick Clifton <nickc@cygnus.com>
Patch from: Nick Burrett <nick.burrett@btinternet.com>
* arm.c (arm_poke_function_name): New function to implement
-mpoke-function-name.
* aof.h (ASM_DECLARE_FUNCTION_NAME): Call it.
* aout.h (ASM_DECLARE_FUNCTION_NAME): Likewise.
* elf.h (ASM_DECLARE_FUNCTION_NAME): Likewise.
* arm.h: Prototype it.
(TARGET_SWITCHES): Add `no-poke-function-name'.
Fri May 7 14:19:31 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* fixinc/server.c (load_data): Cast text_size to long, adapt
format.
* fixinc/server.c (read_pipe_timeout): Declare volatile, modified
in signal handler.
(sig_handler): Add debug code.
* fixinc/server.c (run_shell): Don't \-escape cd, it breaks the
Ultrix V4.3 /bin/sh.
* fixinc/server.c (def_args): Use static instead of STATIC to
avoid redefinition error from linker iff DEBUG.
* fixinc/hackshell.tpl: Don't strip trailing directory from
DESTDIR - that is already done
* fixinc/fixincl.c (run_compiles): fix memory leak
Thu May 6 20:34:00 1999 Mark Mitchell <mark@codesourcery.com>
* resource.c (mark_referenced_resources): Make volatil
monotonically increasing.
(mark_set_resources): Likewise.
Thu May 6 20:02:33 1999 Fred Fish <fnf@be.com>
* rs6000/xm-beos.h (HAVE_VPRINTF): Don't redefine if already defined.
(HAVE_PUTENV, HAVE_ATEXIT, HAVE_RENAME): Likewise.
Wed May 5 20:28:32 1999 Jason Merrill <jason@yorick.cygnus.com>
* install.texi (Header Dirs): s/GPLUS/GPLUSPLUS/.
Wed May 5 23:44:15 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* unroll.c (copy_loop_body): Don't copy VTOP notes from copy_notes_from.
Wed May 5 16:26:13 1999 Vladimir Makarov <vmakarov@tofu.to.cygnus.com>
* function.c (purge_addressof_replacements): Rename into
purge_bitfield_addressof_replacements.
(purge_addressof_replacements): New variable.
(purge_addressof_1): Add code for changing addressof in notes for
field values which are extracted by usage MEM with narrower mode.
(purge_addressof): Initialize purge_bitfield_addressof_replacements.
Wed May 5 07:40:02 1999 Nick Clifton <nickc@cygnus.com>
Patch from: Nick Burrett <nick.burrett@btinternet.com>
* config/arm/arm.h (ARM_MCOUNT_NAME): Define.
(FUNCTION_PROFILER): Remove assembler dialect dependency and use
ARM_MCOUNT_NAME.
(TRAMPOLINE_TEMPLATE): Remove assembler dialect dependency.
* config/arm/aof.h (ARM_MCOUNT_NAME): Define.
1999-05-05 09:58 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* gcc.c (default_compilers): Fix brace nesting bug.
* objc/lang-specs.h: Use %i, not %g.mi, for the input file
when processing an .mi file.
Tue May 4 13:17:55 1999 Mark Mitchell <mark@codesourcery.com>
* resource.c (mark_set_resources): Handle UNSPEC_VOLATILE,
ASM_INPUT, TRAP_IF, and ASM_OPERANDS just like in
mark_referenced_resources.
Mon May 3 22:38:41 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000/aix43.h (SUBTARGET_OVERRIDE_OPTIONS): Change non-PowerPC
and AIX64 combination to warning. Add warning for disabling
PowerPC64 support when using 64-bit mode.
(LIB_SPEC): Do not link with libg.a in 64-bit mode.
(LINK_SPEC): Do not export libg.exp symbols in 64-bit mode.
* rs6000/rs6000.h (MY_ISCOFF): Treat import/export files as valid
XCOFF files.
(read_only_data_section, private_data_section,
read_only_private_data_section): Always align CSECTs to doubleword
boundary regardless of mode.
(TEXT_SECTION_ASM_OP): Align text CSECT on doubleword boundary in
64-bit mode.
(DATA_SECTION_ASM_OP): Always align CSECT to doubleword boundary.
(ASM_OUTPUT_LOCAL): Use rounded size in 64-bit mode to
maintain doublword alignment.
Mon May 3 14:45:23 1999 Jeffrey A Law (law@cygnus.com)
* mn10200.md (btst insns): btst does not leave cc0 in a useable
state for redundant tst eliminatino.
* mn10300.md (btst insns): Likewise.
Mon May 3 16:14:32 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* mips.h (Pmode): Revert Oct 14th change which added a cast.
Sun May 2 14:02:21 1999 Mark Mitchell <mark@codesourcery.com>
* tree.h (struct tree_decl): Add comdat_flag.
(DECL_COMDAT): Define it.
* toplev.c (wrapup_global_declarations): Don't output a
DECL_COMDAT function just because it's public.
Sun May 2 15:16:42 1999 Joseph S. Myers <jsm28@cam.ac.uk>
* pdp11.h (TARGET_SWITCHES): Fix error in previous change.
(ASSEMBLER_DIALECT): Define.
(CONDITIONAL_REGISTER_USAGE): Rename floating point registers if
required for the UNIX assembler.
(ASM_OUTPUT_INT): Remove. The compiler will synthesise it.
(ASM_OUTPUT_ADDR_VEC_PROLOGUE): Remove.
(ASM_OPEN_PAREN, ASM_CLOSE_PAREN): Change to "[" and "]".
(TRAMPOLINE_TEMPLATE): Use ASM_OUTPUT_SHORT.
* pdp11.c (output_addr_const_pdp11): Copy of output_addr_const
adapted to output constants in octal.
* pdp11.c, pdp11.h, pdp11.md: Use output_addr_const_pdp11 instead
of output_addr_const. Output constants in octal. Use assembler
dialect alternatives where DEC and UNIX assemblers use different
instruction names.
Sun May 2 01:15:06 PDT 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Fri Apr 30 13:55:43 1999 Richard Henderson <rth@cygnus.com>
* va-ppc.h (__va_start_common): Let __builtin_saveregs do the work.
* rs6000.c (expand_builtin_saveregs): For V4, initialize a private
va_list struct, and return a pointer to it.
(setup_incoming_varargs): V4 save area based off virtual_stack_vars
instead of frame_pointer.
Thu Apr 29 23:02:22 1999 Mark Mitchell <mark@codesourcery.com>
* emit-rtl.c (start_sequence): Expand comments.
(start_sequence_for_rtl_expr): Likewise.
(push_to_sequence): Likewise.
(end_sequence): Likewise.
* expr.c (inhibit_defer_pop): Likewise.
* expr.h (inhibit_defer_pop): Likewise.
(NO_DEFER_POP): Likewise.
(OK_DEFER_POP): Likewise.
Thu Apr 29 22:13:46 1999 Robert Lipe <robertlipe@usa.net>
* configure.in (i?86-UnixWare7*-sysv): Set thread_file to 'posix'
--enable-threads[={yes,pthreads,posix}] is passed as a command
line parameter to configure.
* config/i386/sysv5.h (LIB_SPEC): Add support for '-pthread'.
(CPP_SPEC): Likewise.
Thu Apr 29 17:23:59 1999 Richard Henderson <rth@cygnus.com>
* emit-rtl.c (operand_subword): Religiously mask and sign-extend
from 32-bits to HOST_WIDE_INT.
Thu Apr 29 15:58:52 1999 Robert Lipe <robertlipe@usa.net>
* fixinc/regex.c, fixinc/regex.h: Removed. Replace with...
* fixinc/gnu-regex.c, fixinc/gnu-regex.h: Imported from GDB 4.18.
* fixinc/Makefile.in (OBJ, HDR): Handle name changes from above.
(gnu-regex.o): Define REGEX_MALLOC to avoid memory leak.
* fixinc/fixincl.c: new regex.h header name
* Makefile.in: new regex.[ch] file names
Thu Apr 29 12:53:33 1999 Richard Henderson <rth@cygnus.com>
* calls.c (emit_call_1): Pass rounded_stack_size to emit_call
instead of the unrounded size.
1999-04-28 14:40 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/mkfixinc.sh: Makesure the result shell script is writable
Wed Apr 28 10:36:39 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* config/m68k/m68k.md (cmpsi+1): Use cmp.w when comparing a 16 bit
constant with an address register.
Wed Apr 28 00:14:41 PDT 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Tue Apr 27 19:50:25 EDT 1999 Andrew MacLeod <amacleod@cygnus.com>
* rtl.h (REG_EH_REGION): Update comment to indicate a value of -1
indicates no throw and no nonlocal gotos.
* optabs.c (emit_libcall_block): Emit REG_EH_REGION with a value
of -1 instead of 0 to indicate a nonlocal goto won't happen either.
* flow.c (count_basic_blocks, find_basic_blocks_1): Ignore libcall
blocks, look for REG_EH_REGION note exclusively.
(make_edges): Check for REG_EH_REGION > 0 for specified handlers.
Tue Apr 27 15:33:42 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (read_only_data_section, private_data_section,
read_only_private_data_section, toc_section): Align CSECT on
doubleword boundary for 64-bit target.
(DATA_SECTION_ASM_OP): Likewise.
* rs6000.c (rs6000_stack_info): Leaf procedure stack limit is 288.
Tue Apr 27 20:19:47 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (insv): Use copy_addr_to_reg.
* final.c (insn_lengths_max_uid): New variable.
(init_insn_lengths, shorten_branches): Set it.
(get_attr_lengths): Test insn uid against insn_lengths_max_uid.
1999-04-27 08:32 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* expr.c (emit_move_insn_1): Abort if MODE argument is invalid.
(compare): Punt if TREE_OPERAND (exp, 0) is an ERROR_MARK.
Tue Apr 27 01:33:43 1999 Jeffrey A Law (law@cygnus.com)
* Makefile.in (ORDINARY_FLAGS_TO_PASS): Renmaed from FLAGS_TO_PASS.
Remove "CC".
(FLAGS_TO_PASS): New variable.
Tue Apr 27 00:36:44 1999 Nick Burrett <nick.burrett@btinternet.com>
* arm.md (nop): Output instruction using output_asm_insn to fix
assembler dialect problems.
Mon Apr 26 23:55:50 1999 Robert Lipe <robertlipe@usa.net>
* Makefile.in (fixinc.sh): Fix dependencies.
* fixinc/inclhack.def (avoid_bool): Enable match if typedefs are
prepended by spaces.
(sco5_stat_wrappers): New fix. Make sys/stat.h C++ safe.
* fixinc/fixincl.sh, fixinc/fixincl.x, fixinc/inclhack.sh: Rebuilt.
Mon Apr 26 23:28:54 1999 Mumit Khan <khan@xraylith.wisc.edu>
Donn Terry <donn@interix.com>
* function.c (put_var_into_stack): Change ptr_mode to Pmode
in setup for chkr_set_right_libfunc calls.
(assign_params): Likewise.
* expr.c (emit_push_insn): Change ptr_mode to Pmode in
setup for chkr_copy_bitmap_libfunc and chkr_set_right_libfunc calls.
(expand_assignment): Change ptr_mode to Pmode in
setup for chkr_add_libfunc and chkr_copy_bitmap_libfunc.
(store_expr): Change ptr_mode to Pmode in
setup for chkr_add_libfunc and chkr_copy_bitmap_libfunc.
(expand_expr): Change ptr_mode to Pmode in
setup for chkr_check_addr_libfunc.
(expand_builtin): Change ptr_mode to Pmode in
setup for chkr_check_str_libfunc, chkr_copy_bitmap_libfunc and
chkr_check_addr_libfunc.
* calls.c (rtx_for_function_call): Change ptr_mode to Pmode in
setup for chkr_check_exec_libfunc.
(expand_call): Change ptr_mode to Pmode in
setup for chkr_set_right_libfunc.
(expand_call): Change ptr_mode to Pmode in
setup for chkr_set_right_libfunc.
(store_one_arg): Change ptr_mode to Pmode in
setup for chkr_set_right_libfunc.
* c-parse.in (absdcl1): Allow attributes in explicit typespecs.
(%expect): Update.
* c-parse.y: Regenerate.
* c-parse.c: Likewise.
* objc/objc-parse.c: Likewise.
* objc/objc-parse.y: Likewise.
Mon Apr 26 21:17:41 1999 Jason Merrill <jason@yorick.cygnus.com>
* c-pragma.c (push_alignment): Don't ignore alignments greater than
4 bytes.
(handle_pragma_token): Likewise.
* c-pragma.c: Support for #pragma pack (push, <id>, <n>).
(struct align_stack): Add id field.
(push_alignment, pop_alignment): Take id parameter.
(handle_pragma_token): Add necessary states.
* c-pragma.h (enum pragma_state): Add necessary states.
Tue Apr 27 13:58:23 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (*cmpqf, *cmpqf_noov, *cmpqi_test,
*cmpqi_test_noov): Remove ? modifier from constraints list.
(*smulqi3_highpart_clobber, *umulqi3_highpart_clobber): Swap
output strings to match new constraint ordering.
1999-04-26 19:16 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* cpphash.c (dump_definition): New function.
* cpphash.h: Prototype it.
* cpplib.c (handle_directive): Don't output anything here.
Streamline.
(pass_thru_directive): Take a length, not a pointer to the
end. All callers changed.
(do_define): Handle -dD, -dN, -g3 entirely here. Streamline.
(do_include): Handle -dI here.
(do_ident): Correct to match cccp.
(do_pragma): Copy the pragma through here.
(do_assert, do_unassert): Tidy.
* cppinit.c (cpp_finish): If -dM was specified, walk the macro
hash table and call dump_definition on all the entries.
* cppmain.c: cpp_finish may produce output.
Mon Apr 26 15:27:33 1999 Mark Mitchell <mark@codesourcery.com>
* toplev.c (compile_file): Move call to check_global_declarations
after output_exception_table to restore behavior as it was before
1999-04-22 change.
1999-04-26 10:50 -0700 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/fixincl.c: Improve the handling of child process exits
* fixinc/server.[ch]: Export the interface for shutting down
the server process
* fixinc/inclhack.tpl: Remove unnecessary character quote
* fixinc/fixincl.sh, fixinc/inclhack.sh: Regenerate
Mon Apr 26 10:41:42 EDT 1999 Andrew MacLeod <amacleod@cygnus.com>
* alpha.md (builtin_setjmp_receiver): Use a label_ref instead of
a code label.
1999-04-26 09:47 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* rtl.texi: Document the rtl classes and their relation to
formats.
Mon Apr 26 01:02:38 1999 Richard Henderson <rth@cygnus.com>
* alpha.md (fix_trunc patterns): Use reg_no_subreg_operand on op0
for less work in reload.
(movsf and movdf patterns): Put fp reg alternatives first.
Mon Apr 26 01:55:56 1999 Marc Espie <espie@cvs.openbsd.org>
* configure.in (openbsd): Factorize xmake_file.
(ix86 openbsd): Trim obsolete comment.
(vax openbsd): Fix typo.
* configure: Rebuilt.
Mon Apr 26 01:30:59 1999 Donn Terry <donn@interix.com>
* expr.c (expand_assignment): Improve test for pointer type.
Mon Apr 26 00:26:18 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (print_operand_address): Account for the subreg word.
Mon Apr 26 01:08:36 1999 Toshiyasu Morita (tm@netcom.com)
* fold-const.c (make_range): Always initialize arg0 and arg1.
(fold): Similarly for alt0 and alt1.
* function.c (fixup_var_refs_insns): Initialize insn_list.
(instantiate_virtual_regs_1): Initialize offset.
* optabs.c (expand_binop): Initialize carry_in, carry_out, op0_xhigh
and op1_xhigh.
* stmt.c (expand_end_case): Initialize minval and maxval.
Mon Apr 26 01:02:34 1999 Nathan Sidwell <nathan@acm.org>
* toplev.c (report_error_function): Reorder file stack and
function name printing. Ignore FILE parameter.
Mon Apr 26 00:58:54 1999 Jerry Quinn <jquinn@nortelnetworks.com>
* pa.h (architecture_type): New enum.
(pa_arch_string, pa_arch): Declare.
(MASK_PA_10, MASK_PA_20): New flags.
(TARGET_SWITCHES): Add pa-risc-2-0. Update docs for PA1.0 codegen.
(TARGET_OPTIONS): Add -march= option.
* pa.c (pa_arch, pa_arch_string): Define.
(override_options): Set them.
* pa/pa-hpux10.h (ASM_FILE_START): Output LEVEL 2.0 asm directive for
2.0 architecture.
* invoke.texi (Option Summary, HPPA Options): Document new
architecture flags.
* pa/pa-hpux.h, pa/pa-hpux10.h, pa/pa-hpux9.h, pa/pa-osf.h, pa.h,
pa.c, pa.md, configure.in, configure: Replace TARGET_SNAKE by
TARGET_PA_11 and MASK_SNAKE by MASK_PA_11.
Mon Apr 26 00:28:25 1999 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* flags.h (inline_max_insns): Declare.
* integrate.c (inline_max_insns): New variable.
(function_cannot_inline_p): Use it.
* toplev.c (main): Add the flag -finline-limit-n.
(display_help): Document -finline-limit-n.
* invoke.texi: Document -finline-limit-n
Sun Apr 25 23:03:32 1999 Richard Henderson <rth@cygnus.com>
* stmt.c (expand_asm_operands): Reload in-out reg-only memory operands.
Sun Apr 25 13:06:13 1999 Richard Henderson <rth@cygnus.com>
* function.c (assign_parms/STACK_BYTES): Revert last change,
and that of 19 Nov.
Sun Apr 25 12:30:50 1999 Richard Henderson <rth@cygnus.com>
* calls.c (emit_call_1): New arg rounded_stack_size; update callers.
Update pending_stack_adjust based on this value.
(compute_argument_block_size): Include pending_stack_adjust in
PREFERRED_STACK_BOUNDARY alignment.
* function.c (assign_parms): Don't round to PREFERRED_STACK_BOUNDARY.
Sun Apr 25 14:38:10 EDT 1999 John Wehle (john@feith.com)
* stupid.c (stupid_mark_refs): Generate a REG_UNUSED note
for a register which is clobbered even if the register
was used by an earlier instruction.
* i386.md (fix_truncsfdi2, fix_truncdfdi2,
fix_truncxfdi2): Don't bother with the gen_reg_RTX.
(fix_truncsfsi2, fix_truncsfdi2, fix_truncdfsi2,
fix_truncdfdi2, fix_truncxfsi2, fix_truncxfdi2): Update
operand constraints and modes.
* i386.c (output_fix_trunc): Use HImode register to avoid
memory stalls. Call output_move_double instead of output_to_reg.
(output_to_reg): Remove.
* i386.h: Likewise.
* i386.md (negsf2, negdf2, negxf2): Set the type
attribute to fpop.
Sat Apr 24 23:15:57 1999 Donn Terry (donn@interix.com)
* alpha.md (call_value_nt): Correct subscripts.
Sat Apr 24 20:49:20 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (PRINT_OPERAND_ADDRESS): Break out to ...
* alpha.c (print_operand_address): here. Handle subregs.
Fri Apr 23 22:35:41 EDT 1999 John Wehle (john@feith.com)
* acconfig.h (HAVE_GAS_FILDS_FISTS): Add.
* configure.in: Check assembler instructions.
* configure: Rebuild.
* config.in: Likewise.
* i386.md (floathisf2, floathidf2, floathixf2): New patterns.
* i386.c (print_operand): Use the proper suffix for a 387 HImode
operand. Abort if a 387 operand has an unsupported size.
Fri Apr 23 16:57:40 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_write_verstamp): Mark `file' unused.
* alpha.h (FUNCTION_VALUE): Use gen_rtx_REG not gen_rtx.
(LIBCALL_VALUE): Likewise.
(GO_IF_LEGITIMATE_SIMPLE_ADDRESS): Handle normal subregs.
Fri Apr 23 14:57:33 1999 Donn Terry <donn@interix.com>
* alpha32.h (INITIALIZE_TRAMPOLINE): Get offsets right.
* alpha.c (alpha_initialize_trampoline): Add covert_memory_address
calls as needed.
Fri Apr 23 14:36:47 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_expand_prologue): Don't negate frame size
for use with subq.
Fri Apr 23 09:43:18 1999 Nick Clifton <nickc@cygnus.com>
* print-rtl.c (print_rtx): Display LABEL_NUSES for labels.
Thu Apr 22 23:08:37 1999 Mark Mitchell <mark@codesourcery.com>
* toplev.h (wrapup_global_declarations): Declare.
(check_global_declarations): Likewise.
* toplev.c (wrapup_global_declarations): New function, split out
from ...
(check_global_declarations): Likewise...
(compile_file): Here.
Thu Apr 22 22:34:41 1999 Richard Henderson <rth@cygnus.com>
* c-parse.in (expr_no_commas): Verify we've an expr before
calling C_SET_EXP_ORIGINAL_CODE.
Thu Apr 22 22:22:15 EDT 1999 John Wehle (john@feith.com)
* toplev.c (rest_of_compilation): Always set
current_function_uses_only_leaf_regs appropriately.
Thu Apr 22 14:39:43 1999 Mumit Khan <khan@xraylith.wisc.edu>
* i386/xm-cygwin.h (HAVE_BCOPY): Delete unneeded macro.
(HAVE_BZERO): Likewise.
(HAVE_BCMP): Likewise.
(HAVE_RINDEX): Likewise.
(HAVE_INDEX): Likewise.
(DIR_SEPARATOR_2): Define.
(GET_ENV_PATH_LIST): Turn path lists into POSIX.
(PATH_SEPARATOR): Use ':'.
1999-04-22 Bruce Korb <ddsinc09@ix.netcom.com>
* configure.in: enable disabling of fast fixincludes
* configure: regenerate
1999-04-21 14:55 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* gen-protos.c: #undef abort after including system.h.
Delete defns of fancy_abort and fatal.
* fix-header.c: Delete defn of fancy_abort.
Wed Apr 21 12:09:38 1999 Mumit Khan <khan@xraylith.wisc.edu>
* cccp.c (simplify_filename): Always preserve leading double slash.
Wed Apr 21 18:15:55 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md: Add new peepholes to remove redundant loads.
Wed Apr 21 17:41:29 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (binary patterns): Reorder alternatives
so that two operand instructions are chosen before three operand
instructions.
Tue Apr 20 23:38:58 1999 Nathan Sidwell <nathan@acm.org>
* objc/Make-lang.in (objc-parse.c): Put BISON parameters in correct
order.
* Makefile.in (c-parse.c): Put BISON parameters in correct
order.
Tue Apr 20 16:38:11 1999 Richard Henderson <rth@cygnus.com>
* alpha.md (nt_lda): New pattern.
* alpha.c (alpha_expand_prologue): Use it for large frames
under windows nt.
Tue Apr 20 17:57:14 1999 Catherine Moore <clm@cygnus.com>
* config/arm/arm.md (movhi): Add check for odd offset.
Tue Apr 20 13:14:58 EDT 1999 John Wehle (john@feith.com)
* i386.c (output_move_double): Abort if a non-offsettable
memory operand is encountered. Delete unused code.
(find_addr_reg): Remove.
Mon Apr 19 21:13:02 1999 Craig Burley <craig@jcb-sc.com>
* tree.def (BLOCK): Fix typo in comment.
1999-04-19 14:51 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* cpplib.c (output_line_command): Drop CONDITIONAL argument.
We can omit unnecessary line commands if file_change ==
same_file and pfile->lineno != 0. All callers changed.
(cpp_get_token [case '\n']): Don't bump pfile->lineno if
CPP_OPTIONS (pfile)->no_line_commands is set.
* cpplib.h: Fix prototype of output_line_command.
1999-04-18 17:46 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* cppfiles.c (find_position, read_and_prescan): Use `unsigned
long' variables consistently to count line and column numbers.
Sun Apr 18 15:50:33 EDT 1999 John Wehle (john@feith.com)
* output.h (current_function_is_leaf,
current_function_uses_only_leaf_regs): Declare.
* function.c (current_function_is_leaf,
current_function_uses_only_leaf_regs): Define.
(init_function_start): Initialize current_function_is_leaf
and current_function_uses_only_leaf_regs.
* final.c (leaf_function): Don't define.
(final_start_function): Replace uses of leaf_function with
current_function_uses_only_leaf_regs.
* toplev.c (rest_of_compilation): Set current_function_is_leaf
prior to invoking local register allocation.
(rest_of_compilation): Replace uses of leaf_function with
current_function_uses_only_leaf_regs.
* dbxout.c (dbxout_symbol, dbxout_parms): Likewise.
* dwarf2out.c (add_location_or_const_vaule_attribute): Likewise.
* dwarfout.c (add_location_or_const_value_attribute): Likewise.
* sdbout.c (sdbout_symbol): Likewise.
* sparc.h (FUNCTION_PROLOGUE, FUNCTION_EPILOGUE): Likewise.
* sparc.c (eligible_for_epilogue_delay, output_return,
sparc_return_peephole_ok): Likewise.
* sparc.md (leaf_function attribute, untyped_return): Likewise.
* i386.c (ix86_compute_frame_size): Don't align the stack
for leaf functions which don't allocate any stack slots.
* tm.texi: Update documentation.
Sun Apr 18 02:15:09 PDT 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Apr 18 00:08:45 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (GO_IF_LEGITIMATE_SIMPLE_ADDRESS): Correct last change --
make sure FP_BASE_P registers are only used with an integer.
Sat Apr 17 22:54:17 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (REG_OK_FP_BASE_P): New macro.
(GO_IF_LEGITIMATE_SIMPLE_ADDRESS): Use it.
* alpha.md (adddi3+1): New insn to handle large constants off
the soft frame pointer.
(adddi3+2): Don't split soft frame pointer or arg pointer additions.
Sun Apr 18 17:24:10 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (legitimize_operands): Use rtx_cost
to determine if it is worthwhile forcing a constant into a register.
* config/c4x/c4x.h (CONST_COSTS): An integer value of 255 or 65535
used with a logical and or an integer value of 16 or 24 used with
a right shift has zero cost on the C40.
Sat Apr 17 21:30:11 1999 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* gcse.c (compute_local_properties): If setp is nonzero, clear
TRANSP instead of setting it to all ones.
Sat Apr 17 21:10:10 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.c (i386_preferred_stack_boundary_string): New global variable.
(i386_preferred_stack_boundary): New global variable.
(override_functions): Set it. Tidy option setting code.
* i386.h (TARGET_OPTIONS): New command line option.
(i386_preferred_stack_boundary_string): Declare it.
(i386_preferred_stack_boundary): Likewise.
(PREFERRED_STACK_BOUNDARY): Use i386_preferred_stack_boundary.
Sat Apr 17 19:22:38 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.c (k6_cost): Take into account the decoding time.
Sat Apr 17 19:13:22 1999 Donn Terry <donn@interix.com>
* i386.h (PRINT_OPERAND_PUNCT_VALID_P): Allow _.
* i386.c (print_operand): New %_ operator.
(load_pic_register): Proper number of leading _ in GOT literal.
* i386.md (prologue_get_pc_and_set_got): Likewise.
* i386/unix.h (ASM_OUTPUT_MI_THUNK): Likewise.
Sat Apr 17 19:13:07 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_expand_prologue): Use gen_adddi3 instead of
emit_move_insn+plus_constant. For NT, don't use the stack probe
loop pointer to allocate stack space.
* alpha.md (adddi3): Always use lda to set the stack pointer.
1999-04-17 20:11 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* c-aux-info.c, emit-rtl.c, explow.c, expmed.c, gcse.c,
haifa-sched.c, optabs.c, reorg.c, resource.c, sched.c: Include
toplev.h for real declaration of trim_filename.
* Makefile.in: Update dependencies.
Sat Apr 17 14:36:19 1999 Craig Burley <craig@jcb-sc.com>
* tree.c (chainon): Check for circularity only if
ENABLE_CHECKING is defined.
1999-04-17 10:15 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* cccp.c: Make fatal non-static.
Sat Apr 17 23:47:24 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (*andqi3_255_clobber,*andqi3_65535_clobber):
New logical and patterns using C40 bit-field insert instructions.
(*lshrqi3_24_clobber,*ashrqi3_24_clobber,*lshrqi3_16_clobber,
*ashrqi3_16_clobber): New shift patterns using C40 bit-field insert
instructions.
1999-04-16 22:44 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* system.h: Always prototype abort. Prototype fatal. Define
abort to call fatal, not fprintf/exit. Define a stub macro
for trim_filename.
* toplev.c: Define DIR_SEPARATOR. (trim_filename): New
function.
* toplev.h: Prototype trim_filename, and #undef system.h's stub.
* gcc.c, genattr.c, genattrtab.c, gencodes.c, genconfig.c,
genemit.c, genextract.c, genflags.c, genopinit.c, genoutput.c,
genpeep.c, genrecog.c: Make fatal non-static.
* gcov.c, gengenrtl.c, protoize.c: #undef abort after
including system.h.
* config/i386/dgux.h, config/m68k/xm-amix.h: Remove stale code
relating to abort.
Sat Apr 17 11:25:44 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (mulqf3_clrqf_clobber, mulqi3_clrqi_clobber):
New patterns to support parallel multiply and load of zero.
Fri Apr 16 01:23:47 1999 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (valid_machine_attribute): If we're modifying the
FUNCTION_TYPE within a POINTER_TYPE and we don't get a decl,
update the POINTER_TYPE.
Fri Apr 16 00:19:31 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.c (x86_adjust_cost): Move break statement to correct place.
Thu Apr 15 23:17:33 1999 Jerry Quinn <jquinn@nortelnetworks.com>
* pa.h (HAVE_PRE_INCREMENT, HAVE_POST_INCREMENT,
HAVE_PRE_DECREMENT, HAVE_POST_DECREMENT): Fix pa_cpu value from
8000 to PROCESSOR_8000.
Thu Apr 15 20:46:57 1999 Donn Terry (donn@interix.com)
* expr.c (expand_assignment): Force pointers to proper mode if
POINTERS_EXTEND_UNSIGNED is defined.
* xm-alpha.h (alloca.h): Add Interix to list of special machines
that don't like alloca.h, pending using autoconf results.
* except.c (start_catch_hadler): Be sure rtime_address is Pmode
if POINTERS_EXTEND_UNSIGNED.
* except.c (expand_eh_return): Force pointers to proper mode if
POINTERS_EXTEND_UNSIGNED.
Thu Apr 15 23:13:35 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h: Tweaked comment formatting.
* config/c4x/c4x.c: Likewise.
Thu Apr 15 02:45:19 1999 Mumit Khan <khan@xraylith.wisc.edu>
* aclocal.m4 (GCC_FUNC_MKDIR_TAKES_ONE_ARG): Define.
* configure.in: Use.
* configure: Rebuilt.
* acconfig.h (MKDIR_TAKES_ONE_ARG): Add.
* config.in: Rebuilt.
* system.h: Use.
Thu Apr 15 01:03:21 1999 Jan Hubicka <hubicka@freesoft.cz>
Jeff Law <law@cygnus.com>
* i386.md (QImode add pattern): Support lea instruction.
(HImode add pattern): Likewise.
* i386.md (ashlsi patterns): Call output_ashl instead of output_ashlsi3.
(ashlqi): Use expander, separate LEA and SAL / ADD patterns; call
output_ashl.
(ashlhi): Likewise.
* i386.h (output_ashl): Renamed from output_ashlsi3.
* i386.c (output_ashl): Likewise; support HImode and QImode operands
as well.
* i386.md (notsi, nothi, xorsi, xorhi, and xorqi patterns): Call
memory_address_displacement_length instead of memory_address_length.
* i386.c (memory_address_info): Renamed from memory_address_length.
Accept new argument DISP_LENGTH. All callers changed. If DISP_LENGTH,
then return the displacement length. Else return length of the
entire memory address. Handle MULT case correctly.
* i386.h (memory_address_info): Update declaration.
* i386.md (memory_bit_test): Fix paren error.
Wed Apr 14 21:29:18 1999 Andrew Haley <aph@cygnus.com>
* flow.c: (make_edges): Always make edges from a basic block
to its exception handlers, even if the block ends with a jump.
1999-04-14 23:26 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* graph.c (node_data): Return void. Ignore result of
print_rtl_single. Change caller to match.
* integrate.c (subst_constants): Initialize op0_mode to an
invalid mode, and abort before use if it's still invalid.
(Can only happen if the RTX_CLASS, RTX_FORMAT tables are corrupted.)
* objc/objc-act.c (get_objc_string_decl,
build_selector_translation_table, generate_protocol_list,
synth_id_with_class_suffix, build_keyword_selector,
build_selector_expr, gen_declarator): Abort when the tree
structure is corrupted.
Wed Apr 14 19:57:49 1999 Jeffrey A Law (law@cygnus.com)
* configure.in (alpha interix): Use symbolic names to set
target_cpu_default.
* configure: Rebuilt.
* explow.c (allocate_dynamic_stack_space): Undo last change. Use
convert_memory_address instead.
Wed Apr 14 19:42:02 1999 Donn Terry (donn@interix.com)
* alpha/lib1funcs.asm: New file.
* alpha/t-interix (lib1funcs.asm): Add to build.
* explow.c (allocate_dynamic_stack_space): Correctly convert TARGET
to Pmode.
Wed Apr 14 14:26:36 1999 John Wehle (john@feith.com)
* i386.md (truncxfdf): Output the template supplied
by output_move_double with the correct operands.
* i386.md (extendsfdf, extendsfxf, extenddfxf): Use
output_float_extend instead specifying '#' as the template.
* i386.c (output_float_extend): Define.
* i386.h (output_float_extend): Declare.
Wed Apr 14 10:48:03 1999 Catherine Moore <clm@cygnus.com>
* config/mips/elf.h, config/mips/elf64.h
(CTORS_SECTION_ASM_OP): Define.
(DTORS_SECTION_ASM_OP): Define.
(EXTRA_SECTIONS): Define.
(INVOKE__main): Define.
(NAME__MAIN): Define.
(SYMBOL__MAIN): Define.
(EXTRA_SECTIONS_FUNCTIONS): Define.
(SECTION_FUNCTION_TEMPLATE): Define.
(ASM_OUTPUT_CONSTRUCTOR): Define.
(ASM_OUTPUT_DESTRUCTOR): Define.
(CTOR_LIST_BEGIN): Define.
(CTOR_LIST_END): Define.
(DTOR_LIST_BEGIN): Define.
(DTOR_LIST_END): Define.
(LIB_SPEC): Define.
(STARTFILE_SPEC): Define.
(ENDFILE_SPEC): Define.
* config/mips/linux.h: Undefine all of the above.
* config/mips/rtems64.h: Likewise.
* config/mips/t-r3900: Likewise.
* config/mips/t-elf: New file.
* config/mips/vxworks.h: New file.
* configure.in (mips-wrs-vxworks): Use mips/vxworks.h.
(mips*-*-*elf*): Use t-elf instead of t-ecoff.
* configure: Regenerate.
Wed Apr 14 09:59:38 1999 Richard Henderson <rth@cygnus.com>
* reload1.c (emit_reload_insns): Also find equivalent mems
for subregs of pseudos.
* alpha.c (aligned_memory_operand): Recognize the output of
LEGITIMIZE_RELOAD_ADDRESS. Examine reg_equiv_memory_loc in
the event of a pseudo.
(unaligned_memory_operand): Likewise. Don't otherwise accept
completely illegal addresses.
(normal_memory_operand): Likewise. Handle subregs of pseudos.
(get_aligned_mem): Revert previous change. Abort if we don't have a
mem. During reload, call find_replacement on all illegal memories.
(get_unaligned_address): Likewise.
* alpha.h (SECONDARY_INPUT_RELOAD_CLASS): Use !aligned_memory_operand
instead of unaligned_memory_operand.
* alpha.md: Revert extra argument to get_aligned_mem.
(reload_inqi): Use any_memory_operand in constraints. Abort if
we're not given some sort of mem.
(reload_inhi): Likewise.
(reload_outqi, reload_outhi): Likewise.
Wed Apr 14 09:39:20 1999 Richard Henderson <rth@cygnus.com>
* i386.md (neghi): Use the whole register when widening the op.
1999-04-14 12:37 -0400 Zack Weinberg <zack@rabi.columbia.edu>
* cpperror.c, cppexp.c, cpplib.c: Never call abort.
* cpphash.c: Only call abort when we detect corruption of the
malloc arena.
* cppmain.c: Don't define fatal or fancy_abort.
Wed Apr 14 09:19:39 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.c (x86_adjust_cost): Agi stall takes 1 cycle on Pentium, fst
requires value to be ready one extra cycle.
Wed Apr 14 11:28:34 1999 Dave Brolley <brolley@cygnus.com>
* config/i386/i386.c (memory_address_length): Add missing parenthesis.
Wed Apr 14 13:59:27 1999 Martin von Loewis <loewis@informatik.hu-berlin.de>
* extend.texi (Deprecated Features): New node.
* invoke.texi (-Wdeprecated): Document.
Wed Apr 14 00:18:22 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.md (SImode logical compare): Avoid outputing non-pariable testw
and testl on Pentium.
(register and memory bit tests): Likewise.
(setcc, normal and reversed conditional branches): Use shorter
sequence for testing flags stored in EAX.
* i386.md (xorsi3): Do not output NOT instrctions on Pentium.
(xorqi3): Likewise.
(xorhi3): Likewise.
(notsi2): Likewise.
(notqi2): Likewise.
(nothi2): Likewise; do not output prefixed opcodes when possible.
* i386.md (neghi2): Do not output prefixed opcode when possible.
(ashlhi3): Likewise.
Wed Apr 14 00:08:46 1999 Richard Henderson <rth@cygnus.com>
* i386.c (memory_address_length): New function.
* i386.h (memory_address_length): Declare it.
Tue Apr 13 22:52:04 1999 Donn Terry (donn@interix.com)
Martin Heller (Ing.-Buero_Heller@t-online.de)
* configure.in (interix Alpha): Add.
(winnt Alpha): Use alpha32.h
(interix i386): Parallel Alpha32.
* configure: Rebuilt.
* config/interix.h: Move common elements from i386-interix.h.
* config/i386/i386-interix.h: Delete same.
* config/alpha/alpha-interix.h: New file.
* config/alpha/alpha32.h: New file, part fron win-nt.h.
* config/alpha/win-nt.h: Deletions (-> alpha32.h).
* config/alpha/interix.h: New file
* config/alpha/alpha.md (interix): Comment.
* config/alpha/xm-alpha-interix.h: New file.
* config/alpha/t-interix: New file.
* fixinc/mkfixinc.sh (interix/Alpha): Add.
1999-04-13 Mike Stump <mrs@wrs.com>
* i386/vxi386.h (CPP_CPU_SPEC): Define appropriately for vxworks.
(CPP_PREDEFINES, LIB_SPEC, STARTFILE_SPEC, ENDFILE_SPEC): likewise.
Tue Apr 13 21:01:36 1999 Jason Merrill <jason@yorick.cygnus.com>
* c-common.c (default_valid_lang_attribute): New fn.
(valid_lang_attribute): New callback ptr.
(decl_attributes): Call it. Move init_priority support into
C++ frontend.
Tue Apr 13 17:47:14 1999 John Wehle (john@feith.com)
* i386.md (movdi): Add splitter.
Wed Apr 14 10:04:27 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (storeqf_int, storeqf_int_clobber, loadqf_int,
loadqf_int_clobber): Add new patterns with corresponding splitters
to handle moves of floating point values into and out of intager
registers by using memory.
* config/c4x/c4x.c (c4x_check_legit_addr): Disallow PRE_INC for modes
other than QFmode and QImode.
(mixed_subreg_operand): New function.
(c4x_emit_move_sequence): If moving a floating point value into or
out of an integer register, use the new patterns storeqf_int_clobber
or loadqf_int_clobber.
(reg_imm_operand, *_reg_operand): Call reg_operand instead of
register_operand.
(reg_operand, src_operand): Disallow operand if it satisifes
mixed_subreg_operand.
* config/c4x/c4x.h (mixed_subreg_operand): Add prototype.
Tue Apr 13 14:49:13 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.c (agi_dependent): Handle push operation more correctly.
Tue Apr 13 14:45:17 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.md (anddi3): Add % constraint.
(iordi3, xordi3): Likewise.
Tue Apr 13 14:29:58 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.md (extendhisi2): Output mov instead of cw instruction for K6
to improve decoding bandwidth.
* i386.md (extendhiqi2): Likewise.
Tue Apr 13 14:26:31 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.md (movsf_push): Handle memory to memory case too, new splitter.
(movdf_push, movxf_push): Likewise.
(movsf_push_memory, movdf_push_memory, movxf_push_memory): Remove.
Tue Apr 13 14:14:06 1999 Jan Hubicka <hubicka@freesoft.cz>
* i386.md: Do not output mov %0,reg on AMD K6.
Tue Apr 13 12:14:07 1999 Dave Brolley <brolley@cygnus.com>
* cppinit.c (cpp_start_read): Fix buffer overwrite.
* Makefile.in (cppinit.o): Typo in dependencies.
Tue Apr 13 05:04:59 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.h (function prototypes for arm.c): Ifdef these out if
HAVE_CONFIG_H is not defined.
Tue Apr 13 02:11:11 1999 Jeffrey A Law (law@cygnus.com)
* pa.c: Avoid Using immediate zero for register zero.
* pa.md: Likewise.
* pa.c (print_operand, case 'f'): New case for FP register or 0.0.
(print_operand, case 'r'): Use %r0 for zero value.
* pa.md (move patterns, fcmp patterns): Use new %f output arg.
* pa.c: Use a register name, not a raw immediate in branch,
compare/clear, sub, subb, uaddcm and vshd instructions.
* pa.md: Likewise.
* pa.md, pa.h, ee.asm, ee_fp.asm, lib2funcs.asm: Likewise.
* pa.c: Use a register name, not a raw immediate in "bv" instructions.
* pa.md, pa.h, ee.asm, ee_fp.asm, lib2funcs.asm: Likewise.
* pa.c: Remove space register specification in memory addresses,
except where it is actually needed.
* pa.md, pa.h, ee.asm, ee_fp.asm, lib2funcs.asm: Likewise.
Mon Apr 12 23:34:35 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Apr 12 14:58:30 1999 Jan Hubicka <hubicka@freesoft.cz>
* reg-stack.c (check_stack_regs_mentioned): Remove variable SIZE.
Mon Apr 12 19:15:17 1999 Daniel Jacobowitz <dan@debian.org>
* rs6000/sysv4.h (CPP_OS_LINUX_SPEC): Add missing backslash.
Mon Apr 12 19:11:38 1999 Mumit Khan <khan@xraylith.wisc.edu>
* i386/cygwin.h (SUBTARGET_SWITCHES): Add -mconsole; fix
-mno-nop-fun-dllimport and minor doc fixes.
(STARTFILE_SPEC): Cygwin DLLs don't have dllcrt0.
(LINK_SPEC): Add -mconsole support.
* i386/mingw32.h (LIB_SPEC): Make libraries consistent with
Cygwin.
(LINK_SPEC): Remove. Use Cygwin's version.
(MATH_LIBRARY): Make it null.
* i386/crtdll.h (MATH_LIBRARY): Likewise.
Fri Apr 12 15:00:52 1999 Stan Cox <scox@cygnus.com>
* c-decl.c (c_decode_option, start_decl, start_function,
finish_function) : Recognize -Wno-main so we can avoid warnings.
1999-04-12 Zack Weinberg <zack@rabi.columbia.edu>
* cpphash.c (collect_expansion, macroexpand,
push_macro_expansion): Make the escape character in macro
buffers '\r', not '@'. Remove code to protect literal
occurences of the escape character; '\r' cannot appear
in a macro buffer unless we put it there.
* cpplib.c (skip_comment, copy_comment, cpp_skip_hspace,
copy_rest_of_line, cpp_get_token, parse_string,
parse_assertion): '\r' might be a backslash-newline marker, or
it might be a macro escape marker, depending on
CPP_BUFFER (pfile)->has_escapes. '@' is not a special
character.
* cpplib.h: Update commentary.
Mon Apr 12 09:30:03 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.h (target_fp_name, structure_size_string, arm_cpu_select):
Const-ify.
* arm.c (target_fp_name, structure_size_string): Const-ify.
* arm.md (reload_inhi, reload_outhi): Make the scratch DImode.
* arm.c (arm_reload_in_hi): Handle cases when the input is still
a pseudo, make use of scratch registers for reloading the address
as appropriate.
(arm_reload_outhi): Similarly for when the output is still a pseudo.
* riscix.h (SUBTARGET_SWITCHES): Document.
1999-04-12 Bruce Korb <ddsinc09@ix.netcom.com>
* fixincludes:
make fixincludes behave like the scripts in fixinc/
* Makefile.in( stmp-fixinc ):
ensure the SHELL value is that of the make
* fixincl/inclhack.tpl:
the file name lists ought to be restricted to "*.h" anyway
C++ files may be named .../[a-z]++/... also
Adding copyright year and attribution to output
* fixincl/inclhack.def:
fixed broken expression
Clarify a some comments
* fixincl/fixincl.tpl:
Clarify a some comments
Remove dead template text
Correct the counting of regular expressions
Mon Apr 12 03:07:44 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (aligned_memory_operand): Handle out of range stack slots.
Take a new SCRATCH argument for the occasion. Update all callers.
(get_unaligned_address): Abort on out of range stack slots.
* alpha.md (adddi3 splitter): Check s_p_rtx not REGNO.
(reload_inqi): Check for aligned mems before unaligned.
(reload_inhi): Likewise.
Mon Apr 12 03:11:30 1999 Jeffrey A Law (law@cygnus.com)
* flow.c (flow_delete_insn): If we delete a CODE_LABEL, also remove
it from the nonlocal_goto_handler_labels list.
* jump.c (delete_insn): Likewise.
(jump_optimize_1): Also recompute LABEL_NUSES when we are just
marking labels.
* rtl.h (remove_node_from_expr_list): Declare.
* rtlanal.c (remove_node_from_expr_list): New function.
Mon Apr 12 02:37:02 1999 Jan Hubicka <hubicka@freesoft.cz>
* reg-stack.c: Update comment, include varray.h.
(stack_regs_mentioned_data): New global variable.
(check_stack_regs_mentioned): New function.
(stack_regs_mentioned): New function.
(reg_to_stack): Initialize and free stack_regs_mentioned_data,
use stack_regs_mentioned.
(record_asm_reg_life): Change insn type cache for changed insn.
(record_reg_life): Do not change the insn mode.
(emit_pop_insn): Likewise.
(emit_swap_insn): Likewise.
(move_for_stack_reg): Likewise.
(stack_reg_life_analysis): Use stack_regs_mentioned.
(emit_swap_insn): Likewise.
(subst_stack_regs): Likewise.
(convert_regs): Likewise.
* jump.c (find_cross_jump): Use stack_regs_mentioned.
* rtl.h (stack_regs_mentioned): Declare.
Mon Apr 12 00:57:10 1999 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* integrate.c (INTEGRATE_THRESHOLD): Sync it with the comment.
Sun Apr 11 10:24:18 1999 Mark Mitchell <mark@codesourcery.com>
* rtl.h (rtx_def): Update documentation for jump and call.
Sun Apr 11 07:43:44 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* jump.c (jump_optimize_1): Make the definition static to match
the prototype.
Sat Apr 10 22:51:53 1999 Jan Hubicka <hubicka@limax.paru.cas.cz>
* flow.c (life_analysis): New parameter remove_dead_code.
(life_analysis_1): Likewise.
(propagate_block): Likewise; use it.
* output.h: Update prototype.
* toplev.c: Update calls to life_analysis.
Sat Apr 10 22:12:12 1999 Jan Hubicka <hubicka@freesoft.cz>
* recog.c (constrain_operands): Ignore unary operators when
matching operands. Recognize '5'..'9' as well.
Sat Apr 10 21:53:02 1999 Philipp Thomas (kthomas@gwdg.de)
Richard Henderson <rth@cygnus.com>
* configure.in: Set target_cpu_default2 for target_alias k6.
* i386.h (TARGET_SWITCHES): Remove no-<cpu> entries.
(CC1_CPU_SPEC): Likewise.
(CPP_CPU_DEFAULT_SPEC): Streamline definition. Add K6 version.
(CPP_K6_SPEC): New.
(CPP_CPU_SPEC): Add K6 variant.
(EXTRA_SPECS): Likewise.
Fri Apr 9 11:29:17 1999 Richard Henderson <rth@cygnus.com>
* flow.c (merge_blocks_nomove): Rewrite to properly handle two
blocks that vanish entirely during merging.
Sat Apr 10 20:09:55 1999 John Wehle (john@feith.com)
* i386.md (floatsisf2, floatdisf2, floatsidf2, floatdidf2,
floatsixf2, floatdixf2, movsicc, movhicc, movsfcc, movdfcc,
movxfcc, movdicc): Remove unused register constraints from
the splitters.
* i386.md (fixuns_truncsfsi2, fixuns_truncdfsi2,
fixuns_truncxfsi2): Delete.
* reg-stack.c (delete_insn_for_stacker): Ensure that
the only side effects of a PARALLEL are clobbers.
(subst_stack_regs): Handle subst_stack_regs_pat deleting
a PARALLEL.
* i386.md (extendsfdf2, extenddfxf2,
extendsfxf2): Rewrite using a splitter.
* i386.c (output_op_from_reg): Remove.
* i386.h: Likewise.
Sat Apr 10 13:09:18 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (di_operand): Allow SUBREGs as well.
(soft_df_operand): Allow SUBREGs as well.
Sat Apr 10 06:14:31 1999 Jan Hubicka <hubicka@paru.cas.cz>
* extend.texi (Assembler Instructions with C Expression Operands):
Document the i386 floating point operands.
1999-04-10 Mike Stump <mrs@wrs.com>
* configure.in (*-*-vxworks): Add vxWorks thread support for all
vxWorks targets.
* configure.in (thumb-wrs-xvworks): Add vxWorks support for thumb.
* configure: Rebuilt.
Sat Apr 10 06:04:50 1999 Donn Terry (donn@interix.com)
* i386/t-interix: Use mostly system headers unchanged.
Use system assert.h
* fixinc/fixinc.interix: Ditto (make almost no-op).
* config/x-interix.h (_ALL_SOURCE): add -D
* config/x-interix.h (crti.o): Delete dependency.
* config/xm-interix.h (ONLY_INT_FIELDS): Define only when bootstrapping.
* i386/xm-i386-interix.h: New file.
* i386/interix.h (ASM_OUTPUT_LIMITED_STRING): Fix warnings.
* i386/i386-interix.h: Renamed from interix.h.
* configure.in (interix): Use new files.
* configure: Rebuilt.
Sat Apr 10 05:25:28 1999 Daniel Jacobowitz <dan@debian.org>
* rs6000/sysv4.h (CPP_OS_LINUX_SPEC): Fix conditions
for -Dunix and -Dlinux, and remove duplicate definition.
Change -Asystem(linux) to -Asystem(posix).
(CPP_OS_SOLARIS_SPEC): Fix conditions for -Dunix, -Dsun,
-DSVR4, -D__EXTENSIONS__.
* rs6000/linux.h (CPP_PREDEFINES): Remove -Dunix,
-Dlinux, -Asystem(linux), and -Asystem(unix).
Sat Apr 10 05:14:50 1999 Mark Elbrecht <snowball3@usa.net>
* i386/djgpp.h (SET_ASM_OP): Define.
* cccp.c (DIR_SEPARATOR): Move to the top of the file.
(is_dir_separator): New function.
(simplify_filename): Use it.
* collect2.c (find_a_file): Use HAVE_DOS_BASED_FILE_SYSTEM in place
of the DIR_SEPARATOR test.
Consider any file starting with a drivename to be absolute.
If the absolute filename test fails and EXECUTABLE_SUFFIX is
defined, append EXECUTABLE_SUFFIX to the file and try again.
* cppinit.c (base_name): Use HAVE_DOS_BASED_FILE_SYSTEM
in place of __MSDOS__ and _WIN32.
* cppfiles.c (simplify_pathname): Likewise.
* gcc.c (IS_DIR_SEPARATOR): Define new macro. Returns true if a
character is a directory separator.
(find_a_file): Use it.
(convert_filename): Likewise.
(process_command): Likewise.
(do_spec_1): Likewise.
(is_directory): Likewise.
(main): Likewise.
* prefix.c (IS_DIR_SEPARATOR): Define. Tests whether a character is
a directory separator.
(translate_name): Use it.
(update_path): Change DIR_SEPARATOR_2 to DIR_SEPARATOR. Fix
warning in block where '/' is changed to DIR_SEPARATOR.
* i386/xm-djgpp.h (DIR_SEPARATOR): Set to '/'.
(DIR_SEPARATOR_2): New macro. Set to '\'.
(HAVE_DOS_BASED_FILESYS): Define.
* i386/xm-mingw32.h: Updated copyright. Set
DIR_SEPARATOR_2 to '/'. Define HAVE_DOS_BASED_FILE_SYSTEM.
* i386/xm-os2.h: Likewise.
* winnt/xm-winnt.h: Likewise.
* i386/xm-dos.h: Likewise. Add copyright.
1999-04-10 Joseph S. Myers <jsm28@cam.ac.uk>
* pdp11.h (TARGET_SWITCHES): Add option to vary assembler syntax.
(TARGET_DEFAULT): Possibly use UNIX syntax.
(TARGET_UNIX_ASM, TARGET_UNIX_ASM_DEFAULT): New macros.
(REGISTER_NAMES): Use "r5" instead of "fp".
(ASM_OUTPUT_ALIGN): Use ".even" directive, and abort for any
greater alignment.
* 2bsd.h (TARGET_UNIX_ASM_DEFAULT): Default to UNIX assembler
syntax for 2BSD.
* pdp11.c (output_ascii): Use working syntax for ".byte".
(print_operand_address): Use "*" instead of "@" when using UNIX
assembler syntax.
Sat Apr 10 03:50:12 1999 Jeffrey A Law (law@cygnus.com)
* rtl.h (rebuild_jump_labels): Declare.
* jump.c (jump_optimize_1): Renamed from jump_optimize. Make static.
Add new argument MARK_LABELS_ONLY. Quit after mark_all_labels if
requested.
(jump_optimize, rebuild_jump_labels): New wrapper functions for
jump_optimize_1.
* toplev.c (rest_of_compilation): Use rebuild_jump_labels instead of
running the entire jump optimizer.
* rtl.h (local_alloc): Returns an integer now.
* local-alloc.c (recorded_label_ref): New file scoped variable.
(local_alloc): Initialize recorded_label_ref to zero. Return its
value when local allocation has completed.
(update_equiv_regs); If we create an equivalence for a LABEL_REF,
set recorded_label_ref.
* toplev.c (rest_of_compilation): Run the jump optimizer after
register allocation and reloading if needed.
Fri Apr 9 21:02:57 1999 Krister Walfridsson (cato@df.lth.se)
* i386/gas.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Fix typo.
* i386/freebsd-elf.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Likewise.
1999-04-09 Zack Weinberg <zack@rabi.columbia.edu>
* cpphash.c (special_symbol): When expanding __LINE__, use the
top file buffer, not the top buffer.
Fri Apr 9 13:41:04 1999 Jim Wilson <wilson@cygnus.com>
* Makefile.in (check-g++, check-gcc, check-g77, check-objc): Add
cd .. to TCL_LIBRARY command.
Fri Apr 9 13:04:52 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/unknown-elf.h (SUBTARGET_CPU_DEFAULT): Only define if
not already specified.
Fri Apr 9 11:18:55 1999 Jason Merrill <jason@yorick.cygnus.com>
* c-common.c (decl_attributes, A_INIT_PRIORITY): Allow arrays
of classes, too.
Fri Apr 9 10:40:10 1999 Kaveh R. Ghazi <ghazi@snafu.rutgers.edu>
* rs6000.c (rs6000_override_options, ptt, rs6000_file_start,
rs6000_float_const, rs6000_replace_regno, debug_stack_info,
rs6000_output_load_toc_table, output_prolog, output_epilog):
Const-ify a char*.
(output_mi_thunk): Likewise. Mark parameter `thunk_fndecl' with
ATTRIBUTE_UNUSED. Hide unused variables `r0', `sp', `toc',
`schain', `r12', `buf' and `labelno'.
(output_ascii): Const-ify a char*.
(rs6000_gen_section_name): Initialize variable `last_period'.
(rs6000_adjust_priority): Mark parameter `insn' with
ATTRIBUTE_UNUSED.
(rs6000_trampoline_template, rs6000_dll_import_ref,
rs6000_longcall_ref, rs6000_encode_section_info): Const-ify a char*.
* rs6000.h (offsettable_mem_operand, optimization_options): Add
prototypes.
* rs6000.md (movdi, define_split): Cast a value to HOST_WIDE_INT
when comparing against one.
Thu Apr 8 19:20:18 1999 Jeffrey A Law (law@cygnus.com)
* expr.c (expand_expr, case ARRAY_REF, COMPONENT_REF, BIT_FIELD_REF):
Do not try to optimize a aggregate address which has VOIDmode.
Mirrors March 23 change to expand_assignment.
* flow.c (delete_unreachable_blocks): Do not require EDGE_FALLTHRU
for an edge when tidying an edge which connects consecutive basic
blocks.
* flow.c (can_delete_label_p): Do not convert a label into a
deleted label here.
* cse.c (flush_hash_table): New function.
(cse_insn): Flush the hash table when we encounter a volatile asm.
(cse_basic_block): Use flush_hash_table instead of doing it
inline.
* reload1.c (reload_cse_regs_1): Flush known register values if
we encounter a volatile asm.
* loop.c (strength_reduce): Re-enable Joern's loop improvements.
Thu Apr 8 09:37:40 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (arm_print_operand): Undo previous change -
always print large constants in decimal.
Thu Apr 8 10:22:23 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* configure.in (host_xm_file, build_xm_file): Include hwint.h.
Use case statements instead of "if test -a ... -a ... -a ..."
* machmode.h: Don't define HOST_WIDE_INT, etc. Wrap use of
HOST_WIDE_INT in #ifdef.
* mips.h: Include hwint.h instead of providing definitions for
HOST_WIDE_INT, etc. Wrap uses of HOST_WIDE_INT in #ifdef.
Thu Apr 8 06:16:14 1999 John Wehle (john@feith.com)
* i386.md (truncdfsf2, truncxfsf2,
truncxfdf2): Rewrite using a splitter.
Thu Apr 8 01:26:05 1999 Arg Haas (ahaas@neosoft.com)
Jeffrey A Law (law@cygnus.com)
* freebsd-elf.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Avoid ambiguous
else statement.
* gas.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Likewise.
* linux.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Likewise.
* openbsd.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Likewise.
Wed Apr 7 22:40:19 1999 Jim Wilson <wilson@cygnus.com>
* i960/i960.c (i960_function_prologue): Don't save static chain
pointer.
* i960/i960.h (STACK_CHAIN_REGNUM): Change from r3 to g12.
(TRAMPOLINE_TEMPLATE): Likewise.
(FRAME_POINTER_REQUIRED): Check current_function_has_nonlocal_goto.
* i960/i960.md (nonlocal_goto): Rewrite.
Tue Apr 6 17:49:49 1999 Philip Blundell <pb@nexus.co.uk>
* config/arm/lib1funcs.asm: Test for __ELF__ not __elf__.
Wed Apr 7 14:07:34 1999 Jeffrey A Law (law@cygnus.com)
* h8300.c (h8300_adjust_insn_length): Also avoid recognizing
ADDR_VEC and ADDR_DIFF_VEC insns.
* h8300.c (h8300_adjust_insn_length): Avoid trying to recognize
USE, CLOBBER or SEQUENCE insns.
* unroll.c (unroll_loop): For HAVE_cc0 machines, adjust copy_end_luid
to account for the uncopied insn that sets cc0 at the end of the loop.
* unroll.c (copy_loop_body): Always ensure at least two insns
are in the copied loop.
Wed Apr 7 14:52:18 1999 Catherine Moore <clm@cygnus.com>
* config/mips/elf.h (MAKE_DECL_ONE_ONLY): Define.
(UNIQUE_SECTION_P): Define.
1999-04-07 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/inclhack.tpl & fixincl.tpl:
Remove dynamic content from generated files
Wed Apr 7 13:16:22 1999 John Wehle (john@feith.com)
* i386.c (output_move_memory): Remove.
* i386.h: Likewise.
* i386.md (movsi, movhi, movstricthi, movqi, movstrictqi,
movsf, movdf, movxf, movdi): Check no_new_pseudos instead
of (reload_in_progress | reload_completed).
Wed Apr 7 03:16:45 1999 Richard Henderson <rth@cygnus.com>
* alpha.c (reg_no_subreg_operand): New function.
* alpha.h (PREDICATE_CODES): Add it.
* alpha.md (floatdi?f patterns): Use it for op1.
* alpha.c (alpha_end_function): Don't flag weak functions.
Wed Apr 7 02:11:55 1999 Richard Henderson <rth@cygnus.com>
* expr.c (expand_builtin) [BUILT_IN_RETURN_ADDRESS]: Use
copy_to_mode_reg; don't force constants into a register.
Tue Apr 6 22:55:25 1999 Richard Henderson <rth@cygnus.com>
* toplev.c (compile_file): Typo flow_dump -> flow2_dump.
1999-04-06 Joseph S. Myers <jsm28@cam.ac.uk>
* pdp11.c (simple_memory_operand): Add default case in switch.
* pdp11.h (TARGET_SWITCHES): Add help strings.
(NOTICE_UPDATE_CC): Don't include excess argument to format.
(ASM_OUTPUT_DOUBLE_INT): Remove.
Tue Apr 6 22:09:40 1999 Richard Henderson <rth@cygnus.com>
* expr.c (expand_builtin_setjmp): Put setjmp return label on
nonlocal_goto_handler_labels for flow.
Tue Apr 6 22:05:21 1999 Jan Hubicka <hubicka@paru.cas.cz>
Richard Henderson <rth@cygnus.com>
* flow.c (verify_flow_info): New function.
(find_basic_blocks): Call it if ENABLE_CHECKING.
(merge_blocks): Don't merge if there are non-deletable labels.
* toplev.c (fatal_insn): Allow a printf-style arg list.
* toplev.h (fatal_insn): Update prototype.
Tue Apr 6 16:18:58 1999 Jan Hubicka <hubicka@paru.cas.cz>
* flow.c (split_edge) update correctly flow graph, disable
EDGE_CRITICAL flag on the split edge, update NUSES for new label.
Tue Apr 6 15:47:51 1999 Richard Henderson <rth@cygnus.com>
* emit-rtl.c (gen_rtx_CONST_DOUBLE): Use XWINT not XINT.
Clear third and following slots, if they exist.
Tue Apr 6 15:45:28 1999 Richard Henderson <rth@cygnus.com>
* flow.c (create_basic_block): Make sure the bb note is in the block.
(can_delete_note_p): Rename from delete_note_p.
(delete_insn_chain): Preserve undeleteable labels too.
(tidy_fallthru_edge): Use next_real_insn instead of confusing
inline code.
1999-04-06 Zack Weinberg <zack@rabi.columbia.edu>
* cppexp.c (parse_charconst): Initialize c.
(cpp_parse_expr): Initialize rprio.
* cppfiles.c (merge_include_chains): Initialize prev.
(finclude): Set fp->line_base to fp->buf before returning.
* cpphash.c (macroexpand): Initialize token.
* cppspec.c (lang_specific_driver): Change suff to
const char *const *.
1999-04-06 Zack Weinberg <zack@rabi.columbia.edu>
* cppinit.c (install_predefs): Delete function.
(cpp_start_read): Don't call install_predefs.
(cpp_handle_option): Remove case 'u' and all refs to
opts->inhibit_predefs.
(print_help): Don't mention -undef.
(initialize_builtins): Define __HAVE_BUILTIN_SETJMP__, to
match cccp.
* cpplib.h (struct cpp_options): Remove inhibit_predefs
member.
* cccp.c (predefs): Delete variable.
(main): Remove case 'u' in argument parse loop,
'inhibit_predefs' variable, and the code block that would
process CPP_PREDEFINES.
(initialize_builtins): Don't define __OBJC__, the driver will
do that.
* gcc.c (default_compilers): Remove -undef from all specs that
invoke a C preprocessor.
* ch/lang-specs.h: Likewise.
* cp/lang-specs.h: Likewise.
* f/lang-specs.h: Likewise.
* objc/lang-specs.h: Likewise.
Mon Apr 5 11:55:31 1999 Donn Terry (donn@interix.com)
* Makefile.in (SUBDIR_FLAGS_TO_PASS): Fix misapplied patch.
Mon Apr 5 11:51:38 1999 Jeffrey A Law (law@cygnus.com)
* m68k.md (movdf): Hide GPR sources & destinations from regclass.
Mon Apr 5 09:54:42 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Apr 5 05:55:15 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* fixincl.tpl: Separate "-e" from its argument, a la
the Sat Apr 3 17:05:13 1999 fix.
* genfixes: Ensure that the server shell is _NOT_ csh.
Mon Apr 5 03:52:30 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Apr 5 04:47:14 1999 Jeffrey A Law (law@cygnus.com)
* i386.c (x86_double_with_add): Turn off for Pentium and PPro.
(small_shift_operand, output_ashlsi3): New functions.
* i386.h (small_shift_operand, output_ashlsi3): Declare.
* i386.md (ashlsi3): Simplify ahlsi3 patterns. Remove splitters
that are no longer needed.
Sun Apr 4 04:05:04 1999 Jeffrey A Law (law@cygnus.com)
* stmt.c (expand_loop_end): When copying the loop exit test,
do not walk into a nested loop.
Sun Apr 4 00:14:54 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/hackshell.tpl: Skip links to directories, to avoid
removing them.
* fixinc/inclhack.tpl: Likewise.
* fixinc/fixinc.sh, fixinc/fixincl.x, fixinc/inclhack.sh: Rebuilt.
Sat Apr 3 23:46:13 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (addsi3, iorsi3, xorsi3, adddi3, iordi3, xordi3,
movsi_got, movsi, movsf): Use no_new_pseudos.
* rs6000.c (rs6000_got_register): Likewise.
(offsettable_mem_opereand): Use || not |.
Sat Apr 3 22:02:56 1999 Jeffrey A Law (law@cygnus.com)
* acconfig.h (ENABLE_CHECKING): Remove redundant #undef.
* config.in: Rebuilt.
Sat Apr 3 16:22:59 1999 Toshiyasu Morita (tm@netcom.com)
* gcc.texi: Add info on regmove pass.
* regmove.c (fixup_match_1): Consistently evaluate
HAVE_POST_INCREMENT and HAVE_POST_DECREMENT.
Sat Apr 3 19:21:05 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* configure.in (DEFAULT_LINKER, DEFAULT_ASSEMBLER): Use grep
instead of test and sed to check whether they're GNU programs.
* configure: Rebuilt.
Sat Apr 3 17:57:35 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* Makefile.in (install-headers-tar, install-headers-cpio): Avoid
problems with CDPATH.
Reported by Ralf Canis <canis@bigfoot.com>
Sat Apr 3 13:50:16 1999 Jeffrey A Law (law@cygnus.com)
* fixinc.x86-linux-gnu: Deleted.
Sat Apr 3 17:05:13 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* inclhack.tpl: Insert spaces between `sed -e' and '...'.
Reported by Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fixinc/fixincl.sh, fixinc/fixincl.x, fixinc/inclhack.sh: Regen.
Sat Apr 3 14:54:46 1999 Craig Burley <craig@jcb-sc.com>
* tree.def (BLOCK): Fix typo in comment.
Sat Apr 3 00:53:29 1999 John Wehle (john@feith.com)
* i386.md (floatsisf2, floatdisf2, floatsidf2, floatdidf2,
floatsixf2, floatdixf2): Rewrite using a splitter.
Fri Apr 2 17:36:10 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (arm_print_operand): Print large constants in
hex rather than decimal.
Fri Apr 2 17:23:58 1999 Nick Clifton <nickc@cygnus.com>
* print-rtl.c (print_rtx): Use both HOST_WIDE_INT_PRINT_DEC
and HOST_WIDE_INT_PRINT_HEX to display constants.
1999-04-02 Zack Weinberg <zack@rabi.columbia.edu>
* config/i386/i386.h: Document all TARGET_SWITCHES or add
explicit null initializer.
* config/i386/cygwin.h: Document all SUBTARGET_SWITCHES.
* config/i386/dgux.h: Likewise.
* config/i386/osf1elf.h: Likewise.
* config/i386/win32.h: Likewise.
* config/i386/osfrose.h: Likewise. Drop obsolete -mno-ident option.
Fri Apr 2 17:49:44 1999 Toshiyasu Morita <tm@netcom.com>
* regmove.c (fixup_match_1): Remove now useless if (0).
Sat Apr 3 11:37:20 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* tm.texi (USE_LOAD_POST_DECREMENT, USE_LOAD_PRE_DECREMENT,
USE_STORE_POST_DECREMENT, USE_STORE_PRE_DECREMENT): Document.
(USE_LOAD_POST_INCREMENT, USE_LOAD_PRE_INCREMENT,
USE_STORE_POST_INCREMENT, USE_STORE_PRE_INCREMENT): Fix documentation.
* rtl.h (USE_LOAD_POST_DECREMENT, USE_LOAD_PRE_DECREMENT,
USE_STORE_POST_DECREMENT, USE_STORE_PRE_DECREMENT,
USE_LOAD_POST_INCREMENT, USE_LOAD_PRE_INCREMENT,
USE_STORE_POST_INCREMENT, USE_STORE_PRE_INCREMENT): Provide default
definition.
* expr.c (USE_LOAD_POST_INCREMENT, USE_LOAD_PRE_INCREMENT,
USE_STORE_POST_INCREMENT, USE_STORE_PRE_INCREMENT): Delete default
definition.
Fri Apr 2 16:03:05 1999 Jeffrey A Law (law@cygnus.com)
* fixinc.dgux, fixinc.interix, fixinc.irix, fixinc.ptx: Deleted.
* fixinc.sco, fixinc.svr4, fixinc.winnt, fixinc.wrap: Likewise.
Fri Apr 2 15:46:25 1999 Donn Terry (donn@interix.com)
* configure.in: Set and substitute quoted_cc_set_by_configure.
* configure: Rebuilt.
* Makefile.in (SUBDIR_FLAGS_TO_PASS): Fix quoting problem with ``.
Fri Apr 2 14:35:45 1999 Stan Cox <scox@cygnus.com>
* config/i386/cygwin.h (CPP_SPEC): Use mingw_include_path instead
of a hardcoded path for -mno-cygwin.
(mingw_include_path): New.
1999-04-02 Joseph S. Myers <jsm28@cam.ac.uk>
* pdp11.c: Include "recog.h".
(output_function_prologue): Remove unused variables `nregs', `i',
`offset'.
(output_function_epilogue): Remove unused variables
`may_call_alloca', `nregs', `regno', `adjust_fp'.
(output_ascii): Mark as returning void.
(print_operand_address: Likewise.
(simple_memory_operand): Remove unused variables `plus0', `plus1',
`offset'.
* pdp11.h: Declare functions `arith_operand',
`const_immediate_operand', `expand_shift_operand',
`legitimate_address_p', `notice_update_cc_on_set', `output_ascii',
`output_function_epilogue', `output_function_prologue',
`print_operand_address', `register_move_cost',
`simple_memory_operand'.
(HARD_REGNO_MODE_OK): Parenthesize `REGNO' arg.
(REGNO_REG_CLASS): Likewise.
* pdp11.md: Add explicit `int' to `static count' (in two places).
(addhi3): Add explicit braces to avoid ambiguous else.
(addqi3): Likewise.
(ashlhi3): Likewise.
Fri Apr 2 14:17:10 1999 Jerry James <jerry@cs.ucsb.edu>
* gcc/invoke.texi: Add documentation for additional supported
MIPS CPU types, options -mips16 and -mentry, and ABI and ISA
defaults.
Fri Apr 2 14:12:06 1999 John Wehle (john@feith.com)
* i386.md: Delete floating point compare, add, subtract,
multiply, and divide patterns which allowed integer
operands.
* i386.c (output_387_binary_op): Delete unused code.
(output_float_compare): Likewise.
Fri Apr 2 11:53:37 1999 John Wehle (john@feith.com)
* i386.md (movsf+1, movdf+1, movxf+1): Update constraints
so that SECONDARY_MEMORY_RELOAD is used. Remove dead code.
1999-04-02 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/mkfixinc.sh: Added support for x86-interix.
* fixinc/fixinc.interix: Fixincludes script, slight changes
from ./fixinc.interix. Untested (needs interix box).
* fixinc/inclhack.def:
Complete the change to the 'fixinc.tmp' file.
Fixed regex for finding C++ headers.
* fixincl.x, fixincl.sh, inclhack.sh: Regenerate.
Fri Apr 2 11:36:12 1999 Jan Hubicka (hubicka@paru.cas.cz)
* i386.c (print_operand_address, case REG): Do not use ESI addressing
mode for the K6.
* i386.c (print_operand_address, case MULT): Use more efficient
encoding (mult (reg) (const_int 2)).
Thu Apr 1 17:01:50 1999 Richard Henderson <rth@cygnus.com>
Move over patch from Bernd Schmidt from GC branch:
* emit-rtl.c (gen_rtx_CONST_DOUBLE): New function.
(gen_rtx): Call it. Tidy cases.
* rtl.h (gen_rtx_CONST_DOUBLE): Prototype it.
* gengenrtl.c: Add commentary.
(special_rtx): Also match CONST_DOUBLE.
(gencode): Emit call to memset instead of bzero.
Fri Apr 2 12:58:26 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (ashlhi3, lshrhi3, ashrhi3): Force operand 1
into a register if shift count not constant.
(ashlhi3_reg, lshrhi3_reg, ashrhi3_reg): Ensure that operand 1
is a register.
Fri Apr 2 12:19:17 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (*db): Enable pattern if TARGET_LOOP_UNSIGNED
is non-zero.
(movstrqi_small, movstrqi_large, *cmpstrqi): Add + modifier to address
register constraints.
(*movhi_clobber+1): Modify splitter pattern to handle destination
register that is used in the source address.
(*xorhi3_clobber): Replace AND with XOR in call to legitimize_operands.
Fri Apr 2 12:16:15 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h: Added more comments.
Fri Apr 2 11:58:22 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_emit_move_sequence): Force invalid QImode
constants into memory if we get called directly from gen_move_insn
rather than emit_move_insn.
(c4x_legitimize_address): Fix up LABEL_REF addresses.
Thu Apr 1 12:04:05 1999 Jim Wilson <wilson@cygnus.com>
* expr.c (store_field): When check direct_store, assume all complex
modes can be directly stored.
1999-04-01 Bruce Korb <ddsinc09@ix.netcom.com>
* fixinc/genfixes: New shell script that runs autogen
to create the generated files.
1999-04-01 Manfred Hollstein <manfred@s-direktnet.de>
* Makefile.in (cppmain$(exeext)): Depend on intl.o. Link in intl.o.
Thu Apr 1 03:48:34 1999 H.J. Lu (hjl@gnu.org)
* i386.c (output_fp_conditional_move): Abort for LT, LE, GE, and GT
signed integer comparisons.
* i386.c (output_int_conditional_move): Use "enum rtx_code" for code
type.
* i386.c (notice_update_cc): No need to check the INT mode for
conditional moves since FLOAT conditional moves don't affect cc0.
Thu Apr 1 02:17:18 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/inclhack.def (zzz_ki_syscalls, zzz_time): Fix trigger
string to only match on hpux11.
* fixinc/fixincl.x, fixinc/inclhack.sh, fixinc/fixinc.sh: Rebuilt.
Thu Apr 1 01:09:27 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* fixinc/hackshell.tpl: Complete transition to fixinc.tmp.
* fixinc/inclhack.sh: Rebuilt.
* fixinc/inclhack.def: Fix typos in c_asm.h fix.
* fixinc/fixincl.x, fixinc/inclhack.sh, fixinc/fixinc.sh: Rebuilt.
Wed Mar 31 17:20:11 1999 Jeffrey A Law (law@cygnus.com)
* toplev.c (rest_of_compilation): Allow dbr_schedule to write to
the dump file too.
Wed Mar 31 12:32:43 1999 Richard Henderson <rth@cygnus.com>
* flow.c (find_basic_blocks): New argument `do_cleanup'.
Conditionally call delete_unreachable_blocks.
(free_basic_block_vars): Zero ENTRY/EXIT data.
(allocate_for_life_analysis): Kill. Split into...
(allocate_bb_life_data, allocate_reg_life_data): ... new functions.
(life_analysis_1): Update.
* gcse.c (gcse_main): Update find_basic_blocks call.
* toplev.c (rest_of_compilation): Likewise.
* stupid.c (stupid_life_analysis): Update life data calls.
* rtl.h, output.h: Update prototypes.
Wed Mar 31 12:10:00 1999 Bruce Korb <ddsinc09@ix.netcom.com>
* inclhack.def (several): Added spaces in tests to ensure
correct shell syntax. Added c_asm.h fix from fixincludes.
Also corrected the corrected fix to C++ comments :-}
* inclhack.tpl: Changed method of traversing symlink trees
so that file name matching will work correctly.
* fixincl.c, hackshell.tpl: Fallout from above.
* fixincl.x, inclhack.sh, fixincl.sh: Rebuilt.
Tue Mar 30 10:43:49 1999 Philip Blundell <pb@nexus.co.uk>
* config/arm/aout.h (DBX_DEBUGGING_INFO): Avoid redefinition if
dbxelf.h was previously included.
(CPP_APCS_PC_DEFAULT_SPEC): No need to undefine.
* config/arm/linux-elf.h (FP_DEFAULT): Correctly override the
definition from arm.h.
Wed Mar 31 10:33:37 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (c-gperf.h): Generate using gperf language 'C', not
'KR-C', so gperf uses the `const' keyword on strings.
* c-parse.gperf (resword): Const-ify a char*.
Wed Mar 31 01:49:31 1999 Ian Lance Taylor <ian@zembu.com>
* t-rtems (LIMITS_H_TEST, LIBGCC2_INCLUDES): Define.
Wed Mar 31 00:50:48 1999 Jeffrey A Law (law@cygnus.com)
* system.h (STDERR_FILENO): Fix typo.
* inclhack.def (bool): Also fix bogus bool in curses_colr/curses.h.
* fixincl.x, inclhack.sh, fixincl.sh: Rebuilt.
Tue Mar 30 20:51:40 1999 Mark Mitchell <mark@codesourcery.com>
* alias.c (alias_set_compare): Remove.
(record_alias_subset): Use splay_tree_compare_ints instead of
alias_set_compare.
(init_alias_once): Likewise.
* cse.c: Include splay-tree.h.
(reg_qty): Remove.
(reg_tick): Likewise.
(reg_table): Likewise.
(cse_reg_info): New structure.
(cse_reg_info_free_list): New variable.
(cse_reg_info_tree): Likewise.
(cached_regno): Likewise.
(cached_cse_reg_info): Likewise.
(all_minus_one): Remove.
(consec_ints): Likewise.
(GET_CSE_REG_INFO): New macro.
(REG_TICK): Likewise. Use throughout instead of reg_tick.
(REG_IN_TABLE): Likewise. Use throughout instead of reg_in_table.
(REG_QTY): Likewise. Use throughout instead of reg_qty.
(get_cse_reg_info): New function.
(free_cse_reg_info): Likewise.
(new_basic_block): Reinitialize cse_reg_info_tree instead of
reg_tick, all_minus_one, and consec_ints.
* Makefile.in (cse.o): Depend on splay-tree.h
Tue Mar 30 13:19:36 1999 Jason Merrill <jason@yorick.cygnus.com>
* libgcc2.c (throw_helper): Just return the SP offset, rather than
a whole udata. Include args_size in the offset.
(__throw, __rethrow): Adjust.
Tue Mar 30 11:39:27 1999 Craig Burley <craig@jcb-sc.com>
* extend.texi (Extended Asm): Delete spurious `b' before
`@end example', which was confusing texi2html.
Tue Mar 30 00:26:34 1999 Jason Merrill <jason@yorick.cygnus.com>
* dwarf2out.c (output_line_info): Don't emit redundant info.
Do start a new row if the file changes and the line # doesn't.
Mon Mar 29 15:48:39 1999 Jason Merrill <jason@yorick.cygnus.com>
* invoke.texi (Invoking G++, C++ Dialect Options): Update.
Mon Mar 29 15:05:39 1999 Richard Henderson <rth@cygnus.com>
* except.c (start_dynamic_handler): Force jmp_buf address to
and operand before moving to memory.
Mon Mar 29 15:11:10 1999 Craig Burley <craig@jcb-sc.com>
* invoke.texi (Code Gen Options): Attempt to clarify
-fcheck-memory-usage. Minor edits to -fprefix-function-name.
Mon Mar 29 20:52:47 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (maybe_eliminate_biv): For libcalls that set a giv, skip to
end of libcall.
Mon Mar 29 20:35:49 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (mulsi3): Tag an extra REG_EQUAL note to the middle insn.
Mon Mar 29 11:50:34 1999 Jerry Quinn <jquinn@nortelnetworks.com>
* pa.h (HAVE_PRE_INCREMENT): Disable when optimizing for a PA8000
class machine.
(HAVE_PRE_DECREMENT, HAVE_POST_INCREMENT): Likewise.
(HAVE_POST_DECREMENT): Likewise.
Mon Mar 29 08:24:43 1999 Bruce Korb <korb@datadesign.com>
* fixinc/mkfixinc.sh: Fix portability problems with old shells.
* fixinc/README: Updated for release announcement
Sun Mar 28 20:26:55 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* recog.h (insn_outfun, insn_operand_predicate): Add prototype
arguments.
* rtl.h (note_stores): Likewise.
* rtlanal.c (note_stores): Likewise.
Sun Mar 28 15:34:28 1999 Richard Henderson <rth@cygnus.com>
* varasm.c (output_constant_pool): Always mark the constant pool.
Sun Mar 28 16:09:01 1999 Jerry Quinn <jquinn@nortelnetworks.com>
* pa.md (pa7100LCshiftmem, pa7100LCalu): Change simultaneity. Use
shift/mem ops in pa7100LCalu.
* pa.c (pa_adjust_cost): Don't do cost adjustments on pa8000.
(pa_reorg): Don't call pa_combine_instructions on pa8000.
Sun Mar 28 15:27:26 1999 Jeffrey A Law (law@cygnus.com)
* reload1.c (reload): Remove accidental code duplication.
Sun Mar 28 12:22:12 1999 Robert Lipe (robertlipe@usa.net)
* i386/sysv5.h: New file to describe UnixWare7/SVR5.
* configure.in (i?86-UnixWare7*-sysv): Use it.
* i386/udk.h: Use sysv5.h. Now uses Dwarf-2.
Sun Mar 28 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Mar 28 00:44:27 1999 Jeffrey A Law (law@cygnus.com)
* sdbout.c (sdbout_symbol): Do not call build_pointer_type, build
one on the fly and do not cache the result.
* gcc.cps, cpp.cps: Delete unwanted files.
Sat Mar 27 23:37:40 1999 John Wehle (john@feith.com)
* i386.md (movdicc+3, movdicc+4): Rewrite using split_di.
* i386.c (output_int_conditional_move): Delete unused code.
Sat Mar 27 21:17:36 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000/{aix41.h,aix43.h} (ASM_CPU_SPEC): Add 604e.
Sat Mar 27 16:13:50 1999 Jeffrey A Law (law@cygnus.com)
* flow.c (mark_used_regs): Improve handling of ASMs.
1999-03-26 Zack Weinberg <zack@rabi.columbia.edu>
* Makefile.in (xcpp, cppspec.o): New targets.
(CPP_INSTALL_NAME): New macro.
(install-cpp): Install xcpp. Use CPP_INSTALL_NAME.
(all.build, start.encap): Build xcpp.
* cppspec.c: New file, implements argument filtering for a
user-visible C preprocessor.
* cpp.sh: Removed.
Fri Mar 26 20:41:46 1999 Jim Wilson <wilson@cygnus.com>
* Makefile.in (stmp-fixinc): Use tooldir instead of gcc_tooldir.
Fri Mar 26 16:02:37 1999 Nick Clifton <nickc@cygnus.com>
* configure.in (arm-*-vxworks*): Just include arm/vxarm.h.
* configure: Regenerate.
* config/arm/vxarm.h: Define SUBTARGET_CPU_DEFAULT before
including arm/coff.h
1999-02-16 Scott Bambrough <scottb@corelcomputer.com>
* configure.in (arm*-*-linux-gnu*): Set thread_file to 'posix' if
--enable-threads[={yes,pthreads,posix}] is passed as a command
line parameter to configure.
* configure: Regenerate.
* gcc/config/arm/t-linux (TARGET_LIBGCC2_CFLAGS): Include -fPIC.
Fri Mar 26 19:42:19 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (combine_givs): Fix index into can_combine when doing
benefit adjustment for remaining givs when having combined a giv.
Fri Mar 26 11:38:01 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/t-arm-elf (EXTRA_MULTILIB_PARTS): Define.
Fri Mar 26 10:48:27 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/linux-elf.h: Include dbxelf.h
Fri Mar 26 10:43:47 1999 Nick Clifton <nickc@cygnus.com>
* config/svr4.h: Include new header file dbxelf.h.
(DBX_DEBUGGING_INFO): Remove definition.
(DBX_USE_BINCL): Remove definition.
(DBX_BLOCKS_FUNCTION_RELATIVE): Remove definition.
(ASM_IDENTIFY_GCC): Remove definition.
(ASM_IDENTIFY_GCC_AFTER_SOURCE): Remove definition.
(ASM_OUTPUT_SOURCE_LINE): Remove definition.
(DBX_FUNCTION_FIRST): Remove definition.
(DBX_OUTPUT_MAIN_SOURCE_FILE_END): Remove definition.
* config/elfos.h: Include new header file dbxelf.h.
(DBX_DEBUGGING_INFO): Remove definition.
(DBX_BLOCKS_FUNCTION_RELATIVE): Remove definition.
(ASM_IDENTIFY_GCC): Remove definition.
(ASM_IDENTIFY_GCC_AFTER_SOURCE): Remove definition.
(ASM_OUTPUT_SOURCE_LINE): Remove definition.
(DBX_FUNCTION_FIRST): Remove definition.
* config/dbxelf.h: New header file.
(DBX_DEBUGGING_INFO): Define.
(DBX_BLOCKS_FUNCTION_RELATIVE): Define.
(DBX_FUNCTION_FIRST): Define.
(DBX_USE_BINCL): Define.
(DBX_CONTIN_LENGTH): Define.
(ASM_IDENTIFY_GCC): Define.
(ASM_IDENTIFY_GCC_AFTER_SOURCE): Define.
(ASM_OUTPUT_SOURCE_LINE): Define.
(DBX_OUTPUT_MAIN_SOURCE_FILE_END): Define.
Fri Mar 26 01:59:15 1999 "Charles M. Hannum" <root@ihack.net>
* fold-const.c (fold_truthop): Optimize bitfield references with
different masks as long as their size and bit position are the same.
* fold-const.c (fold_truthop): Build a type for both the lhs and
rhs and use it appropriately.
* fold-const.c (fold_truthop): Mask the lhs and rhs after merging
adjacent bitfield references.
* fold-const.c (fold_truthop): Verify that the lhs and rhs are
in the same bit position when optimizing bitfield references
which have the same mask.
Thu Mar 25 22:53:27 1999 Martin von Löwis <loewis@informatik.hu-berlin.de>
* gcc.texi (Copy Assignment): New node.
1999-03-25 Zack Weinberg <zack@rabi.columbia.edu>
* gcc.c: Compile unconditionally all code formerly dependent
on #ifdef LANG_SPECIFIC_DRIVER.
* gccspec.c: New file with stub lang_specific_driver,
lang_specific_pre_link.
* Makefile.in: Link gccspec.o into xgcc. Add rule to compile
Thu Mar 25 21:08:02 1999 Jason Merrill <jason@yorick.cygnus.com>
* gcc.texi (Temporaries): Update.
Thu Mar 25 16:53:53 1999 Richard Henderson <rth@cygnus.com>
* combine.c (distribute_notes): Place REG_LABEL also where
REG_EQUAL indicates.
Thu Mar 25 12:46:37 1999 Jim Wilson <wilson@cygnus.com>
* a29k/a29k.h (TARGET_SWITCHES): Add doc strings.
* i960/i960.h (TARGET_SWITCHES): Add doc strings.
* invoke.texi (a29k): Add documentation for -mno-multm option.
Thu Mar 25 14:04:54 1999 Andrew MacLeod <amacleod@cygnus.com>
* rtl.texi (RTX_FRAME_RELATED_P): Add documentation.
* rtl.h (struct rtx_def): Update comment for frame_related field.
(set_unique_reg_note): Declare prototype.
* dwarf2out.c (dwarf2out_frame_debug_expr): Split out from
'dwarf2out_frame_debug' to handle only expressions, and process
component parts of a PARALLEL expression.
(dwarf2out_frame_debug): Process insns only, and call
new function 'dwarf2out_frame_debug_expr' for patterns.
* emit-rtl.c (set_unique_reg_note): New function to add a reg note,
but if there is an existing one, delete it first.
* expmed.c (expand_mult, expand_divmod): Use set_unique_reg_note.
* optabs.c (add_equal_note, expand_binop): Use set_unique_reg_note.
(emit_no_conflict_block, emit_libcall_block): Use set_unique_reg_note.
(expand_fix): Use set_unique_reg_note.
Thu Mar 25 11:47:49 1999 Art Haas <ahaas@neosoft.com>
* tlink.c (symbol_hash_newfunc): Remove redundant call to
hash_newfunc.
(file_hash_newfunc, demangled_hash_newfunc): Likewise.
Thu Mar 25 10:05:56 1999 Richard Henderson <rth@cygnus.com>
* i386.h (PREFERRED_STACK_BOUNDARY): Set to 128.
1999-03-25 Philip Blundell <pb@nexus.co.uk>
Based on patch from Jim Studt <jim@federated.com>:
* config/arm/linux-elf.h (STARTFILE_SPEC, ENDFILE_SPEC): Copy
definitions from config/linux.h.
(DBX_BLOCKS_FUNCTION_RELATIVE): Define to 1.
Thu Mar 25 02:12:42 1999 Finn Hakansson <finn@axis.com>
* loop.c (strength_reduce): Correct a comment.
* rtl.h (MEM_COPY_ATTRIBUTES): Remove unnecessary ending backslash.
Thu Mar 25 02:02:13 1999 Axel Thimm <Axel.Thimm@physik.fu-berlin.de>
* Makefile.in (RANLIB_TEST): Improve test.
Thu Mar 25 01:15:33 1999 Donn Terry <donn@interix.com>
* combine.c (force_to_mode, case PLUS): Use sign extended mask
when masking the low bits out of a constant.
Tue Mar 23 15:45:25 1999 Richard Earnshaw (rearnsha@arm.com)
Jeff Law <law@cygnus.com>
* fold-const.c (make_range): If orig_type is unset, set it as soon
as we know the type. Remove now unnecessary set of orig_type for
conversions.
Wed Mar 24 23:27:25 1999 Mark Elbrecht <snowball3@usa.net>
Jeff Law <law@cygnus.com>
* system.h (STDIN_FILENO): Provide default definition if one is not
provided by the system header files.
(STDOUT_FILENO, STDERR_FILENO): Likewise.
* i386/xm-djgpp.h (COLLECT2_HOST_INITIALIZATION): New macro.
* collect2.c (main): Use it.
(pexecute_pid): New variable. Holds return value from call to pexecute.
(collect2_execute): Rework to use pexecute instead of fork.
(collect2_wait): Use pwait() instead of wait().
* i386/djgpp.h: Fix typo.
Wed Mar 24 23:24:30 1999 Jeffrey A Law (law@cygnus.com)
* fixinc/mkfixinc.sh: Recognize cygwin* instead of only
cygwin32.
Wed Mar 24 15:44:12 1999 Nick Clifton <nickc@cygnus.com>
* config/m32r/m32r.c (init_idents): Accept both NAME and __NAME__
versions of attribute names and values.
(m32r_valid_machine_decl_attribute): Likewise.
(m32r_encode_section_info): Likewise.
Wed Mar 24 21:42:15 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (choose_reload_regs): If output-reloading for a
simple move insn, try to inherit an equivalence for the input.
1999-02-24 Mike Stump <mrs@wrs.com>
* arm/aout.h (DBX_OUTPUT_MAIN_SOURCE_FILENAME): Fix quoting.
1999-03-24 Jim Blandy <jimb@zwingli.cygnus.com>
* libgcc2.c (__CTOR_LIST__, __DTOR_LIST__): Initialize on all
platforms.
Wed Mar 24 01:35:01 1999 Geoff Keating <geoffk@ozemail.com.au>
* fold-const.c (fold): Recognize a rotate by an unsigned amount.
Tue Mar 23 23:32:14 1999 Jeffrey A Law (law@cygnus.com)
* pa.md (rotlsi3): New expander. Synthesize a variable rotate
left using a variable rotate right. Provide anonymous pattern for
rotate left by a constant value.
* expr.c (expand_assignment): Do not try to optimize a aggregate
address which has VOIDmode.
Tue Mar 23 22:51:48 1999 Mumit Khan <khan@xraylith.wisc.edu>
Donn Terry <donn@interix.com>
* protoize.c (abspath): Preserve multiple leading slashes for
_WIN32 and Interix.
1999-01-23 Mike Stump <mrs@wrs.com>
* arm/vxarm.h: Split out vxWorks support into separate headerfile
and vxify.
* arm/arm.c (cpu_defaults): Allow arm710 as default.
* configure.in: Split out vxWorks support for Arm.
* configure: Rebuilt.
Tue Mar 23 11:20:03 1999 Per Bothner <bothner@cygnus.com>
* tree.c (first_rtl_op, has_cleanups): Handle GOTO_SUBROUTINE_EXPR.
Tue Mar 23 09:00:39 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/riscix1.h (SUBTARGET_SWITCHES): Add doc string.
* config/arm/riscix1-1.h (SUBTARGET_SWITCHES): Add doc string.
Tue Mar 23 07:50:20 1999 Mark Mitchell <mark@codesourcery.com>
* function.c: Include hash.h.
(insns_for_mem_entry): New struct.
(put_reg_into_stack): Take an optional hash-table mapping MEMs to
the INSNs that use them.
(fixup_var_refs): Likewise.
(put_addressof_into_stack): Likewise.
(purge_addressof_1): Likewise. Keep the hash-table up to date if
we add new instructions.
(fixup_var_refs_insns): Use it to avoid searching the entire
instruction chain.
(insns_for_mem_newfunc): New function.
(insns_for_mem_comp): Likewise.
(insns_for_mem_walk): Likewise.
(compute_insns_for_mem): Likewise.
(pop_function_context_from): Pass NULL for the hash-table.
(put_var_into_stack): Likewise.
(gen_mem_addressof): Likewise.
(flush_addressof): Likewise.
(purge_addressof): Call compute_insns_for_mem to pre-compute the
hash table.
* Makefile.in (OBJS): Include hash.o.
(function.o): Depend on hash.h.
Tue Mar 23 00:39:14 1999 Jeffrey A Law (law@cygnus.com)
* i386/openbsd.h (TARGET_DEFAULT): Use symbolic names instead of
numbers.
* i386/netbsd.h, i386/freebsd.h: Likewise.
* crtstuff.c: Use ANSI function definitions. Fix minor whitespace
problems.
* i386/openbsd.h (TARGET_DEFAULT): Define.
* configure.in: Do not set TARGET_CPU_DEFAULT for x86 OpenBSD
configurations.
* configure: Rebuilt.
Tue Mar 23 00:39:10 1999 John Wehle (john@feith.com)
* i386/freebsd.h (TARGET_DEFAULT): Define instead
of TARGET_CPU_DEFAULT.
* i386/netbsd.h (TARGET_DEFAULT): Likewise.
Mon Mar 22 23:52:01 1999 Mumit Khan <khan@xraylith.wisc.edu>
Donn Terry <donn@interix.com>
* sdbout.c (syms.h): Don't include on Interix.
* toplev.c (main): No sbrk on Interix.
* configure.in: Add i386-pc-interix support.
* configure: Regenerate.
* fixinc.interix: New file.
* config/interix.h: New file.
* config/x-interix: New file.
* config/xm-interix.h: New file.
* i386/interix.h: New file.
* i386/interix.c: New file.
* i386/t-interix: New file.
Mon Mar 22 23:41:49 1999 Jeffrey A Law (law@cygnus.com)
* i386.h (PREFERRED_STACK_BOUNDARY): Define.
Mon Mar 22 23:41:31 1999 John Wehle (john@feith.com)
* i386.c (ix86_compute_frame_size): New function.
(ix86_prologue, ix86_epilogue): Use it.
* i386.h (INITIAL_ELIMINATION_OFFSET): Likewise.
* reload1.c: Provide default for PREFERRED_STACK_BOUNDARY.
Mon Mar 22 18:06:59 1999 Jim Wilson <wilson@cygnus.com>
* mips/mips.h (TARGET_SWITCHES, TARGET_OPTIONS): Add option doc
strings.
* mips/abi64.h (SUBTARGET_TARGET_OPTIONS): Likewise.
Mon Mar 22 16:18:27 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/elf.h (VALID_MACHINE_DECL_ATTRIBUTE): Do not bother
passing ATTRIBUTES to arm_valid_machine_decl_attribute.
* config/arm/coff.h (VALID_MACHINE_DECL_ATTRIBUTE): Do not bother
passing ATTRIBUTES to arm_valid_machine_decl_attribute.
* config/arm/arm.h (DEFAULT_RTX_COSTS): Do not bother passing
OUTER_CODE to arm_rtx_costs - it is not used.
(arm_compare_fp): Delete declaration.
(FINAL_PRESCAN_INSN): Do not bother passing OPVEC or NOPERANDS to
arm_final_prescan_insn - they are not used.
(const_ok_for_op): Remove prototype.
(arm_rtx_costs): Fix prototype.
(arm_valid_machine_decl_attribute): Fix prototype.
(final_prescan_insn): Fix prototype.
* config/arm/arm.md: Remove references to arm_compare_fp.
* config/arm/arm.c (arm_compare_fp): Delete.
(const_ok_for_op): Make function static. Add prototype. Remove
mode parameter - it is unused.
(arm_rtx_costs): Remove outer_code parameter.
(reload_memory_operand): Declare mode parameter unused.
(power_of_two_operand): Declare mode parameter unused.
(equality_operator): Declare mode parameter unused.
(load_multiple_operation): Declare mode parameter unused.
(store_multiple_operation): Declare mode parameter unused.
(multi_register_push): Declare mode parameter unused.
(arm_valid_machine_decl_attribute): Remove attributes parameter -
it is unused.
(select_dominance_cc_mode): Remove op parameter - it is unused.
(gen_compare_reg): Remove fp parameter - it is unused.
(final_prescan_insn): Remove opvec and noperands parameters - they
are unused.
Mon Mar 22 14:35:28 1999 Nick Clifton <nickc@cygnus.com>
* tm.texi (MD_SCHED_INIT): Add missing closing parenthesis.
Mon Mar 22 22:24:30 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_as_needed): Set reload_is_output_reload /
reload_has_output_reload for auto_inc expressions that could be
reloaded. Call forget_old_reloads for REG_INC notes.
Mon Mar 22 21:51:57 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* cse.c (cse_insn): Don't change the result register of a libcall.
Mon Mar 22 21:08:59 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* rtl.h (shallow_copy_rtx): Declare.
* rtl.c (shallow_copy_rtx): New function.
* reload.c (find_reloads_toplev): Use shallow_copy_rtx instead of
copy_rtx.
Mon Mar 22 10:44:33 1999 Vladimir Makarov <vmakarov@tofu.to.cygnus.com>
* config/h8300/h8300.md (adjust_length): New attribute.
(modhi3+1, andsi3+1, iorsi3+1, extzv+1, extzv+2): Change insn
default value of attribute "adjust_length" onto "no".
* config/h8300/h8300.c (h8300_adjust_insn_length): Adjust
length only if the attribute "adjust_length" value is "yes".
Use 0 if the shift is negative.
* final.c (shorten_branches): Check insn length after its
adjusting.
Sun Mar 21 17:33:48 1999 Jeffrey A Law (law@cygnus.com)
* i860.h (TARGET_SWITCHES): Add documentation for default case.
* i860/paragon.h (TARGET_SWITCHES): Add documentation for default case.
* i370.h (TARGET_SWITCHES): Add documentation for default case.
* fx80.h (TARGET_SWITCHES): Add documentation for default case.
* elxsi.h (TARGET_SWITCHES): Add documentation for default case.
* clipper.h (TARGET_SWITCHES): Add documentation for default case.
* 1750a.h (TARGET_SWITCHES): Add documentation for default case.
* pa.h (TARGET_SWITCHES): Add documentation for default case.
(TARGET_OPTIONS): Likewise for default case.
* mn10300.h (TARGET_SWITCHES): Add documentation for default case.
* h8300.h (TARGET_SWITCHES): Add documentation for default case.
* gcse.c (dump_hash_table): Fix whitespace in declaration.
(compute_transpout): Renamed from pre_compute_transpout.
(compute_pre_*): Deleted.
(pre_expr_reaches_here_p): New argument, CHECK_PRE_COMP. All
callers changed.
(insert_insn_end_bb): Renamed from pre_insert_insn.
(pre_*): Delete unused variables. Only leave local properties and
global redundant/optimal computation points.
(alloc_pre_mem, free_pre_mem): Corresponding changes.
(compute_pre_data): Simplify and call pre_lcm to run the lazy
code motion dataflow analysis.
(pre_insert, pre_insert_copies, pre_delete): Revamp to use LCM
based redundant and optimal computation points.
* basic-block.h (pre_lcm, pre_rev_lcm): Declare.
* toplev.c (main): A debug option without a level defaults to
level 2.
Sun Mar 21 12:13:01 1999 Nick Clifton <nickc@cygnus.com>
* flow.c (can_delete_label_p): Do not allow user specified
labels to be deleted.
* dwarf2out.c (gen_label_die): Generate addresses for deleted
(programmer specified) labels.
* dwarfout.c (output_label_die): Generate addresses for deleted
(programmer specified) labels.
1999-03-21 Manfred Hollstein <manfred@s-direktnet.de>
* Makefile.in (xgcc$(exeext)): Add intl.o to list of files to be
linked with.
Sun Mar 21 01:15:03 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Mar 20 22:26:23 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* sparc.h (TARGET_SWITCHES): Add null description to default case.
Sat Mar 20 21:46:06 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-lex.c (yylex): Remove unused variable `bytes'.
* flow.c (print_rtl_with_bb): Cast the return value of alloca.
* function.c (assign_parms): Wrap variable `varargs_setup' in
macro SETUP_INCOMING_VARARGS.
(thread_prologue_and_epilogue_insns): Mark parameter `f' with
ATTRIBUTE_UNUSED.
* local-alloc.c (no_equiv): Likewise for parameter `store'.
* sched.c (schedule_insns): Remove unused variables `insn' and `next'.
* tlink.c (symbol_hash_newfunc, symbol_hash_lookup,
file_hash_newfunc, file_hash_lookup, demangled_hash_newfunc,
demangled_hash_lookup, symbol_push, symbol_pop, file_push,
file_pop, tlink_init, tlink_execute, frob_extension,
obstack_fgets, tfgets, pfgets, freadsym, read_repo_file,
maybe_tweak, recompile_files, read_repo_files,
demangle_new_symbols, scan_linker_output): Add static prototype.
(symbol_hash_newfunc, file_hash_newfunc, demangled_hash_newfunc):
Make the third argument a `hash_table_key'.
* toplev.c (debug_start_source_file): Mark parameter `filename'
with ATTRIBUTE_UNUSED.
Sun Mar 21 02:28:21 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* tm.texi (Varargs): Don't split argument of @item across lines.
* invoke.texi: Fix use of @item vs @itemx.
Sun Mar 21 09:59:54 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (TARGET_SWITCHES): Add null description to
default case.
Sat Mar 20 23:33:54 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.c (check_dbra_loop): Fix debug message.
Sat Mar 20 15:54:35 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (decrement_and_branch_on_count): Emit rptb_end
pattern instead of decrement_and_branch_until_zero pattern.
Sat Mar 20 11:39:58 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (TARGET_SWITCHES): Add documentation.
* config/c4x/c4x.h (TARGET_OPTIONS): Add documentation.
Fri Mar 19 23:26:29 1999 Martin von Löwis <loewis@informatik.hu-berlin.de>
* expr.c (expand_expr): Handle ERROR_MARK much earlier.
Fri Mar 19 15:28:38 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c (create_definition): Cast to U_CHAR* when assigning to one.
* cppfiles.c (read_and_prescan): Likewise.
Start a #define in column 0.
* cpplib.c (cpp_define): Cast to U_CHAR* when assigning to one.
(cpp_push_buffer): Likewise for cpp_buffer*.
(do_include): Change the type of `fbeg' and `fend' to unsigned char*.
(do_endif): Cast to char* when assigning to one.
(do_assert): Likewise.
(do_unassert): Likewise.
(cpp_read_check_assertion): Change the type of `name' to U_CHAR*.
Don't do unnecessary cast to char* anymore.
* genrecog.c (make_insn_sequence): Cast to char** when assigning
to one. Cast the first argument of bzero to PTR.
* loop.c (strength_reduce): Remove unused variable `note'.
* reload1.c (new_insn_chain): Cast to struct insn_chain* when
assigning to one.
* rtl.c (copy_rtx): Use memcpy instead of bcopy.
Fri Mar 19 11:19:31 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* calls.c (initialize_argument_information): Mark parameters
`num_actuals' and `n_named_args' with ATTRIBUTE_UNUSED.
* dbxout.c (dbxout_start_new_source_file): Likewise for parameter
`filename'.
(dbxout_finish): Likewise for parameters `file' and `filename'.
(dbxout_prepare_symbol): Likewise for parameter `decl'.
(dbxout_begin_function): Likewise.
* explow.c (hard_function_value): Likewise for parameter `func'.
* function.c (locate_and_pad_parm): Likewise for parameter `fndecl'.
* expmed.c (expand_divmod): Omit unused argument to `expand_abs'.
* expr.c (expand_expr): Likewise.
* expr.h (expand_abs): Delete unused argument from prototype.
* optabs.c (expand_abs): Remove unused parameter `unsignedp'.
* sdbout.c (sdbout_init): Mark parameter `syms' with ATTRIBUTE_UNUSED.
(sdbout_end_block): Likewise for parameter `n'.
* toplev.c (debug_define): Likewise for parameters `lineno' and
`buffer'.
(debug_undef): Likewise.
* varasm.c (named_section): Likewise for parameter 'reloc'.
(assemble_external): Likewise for parameter `decl'.
(assemble_alias): Likewise for parameter `target'.
Fri Mar 19 01:54:30 1999 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* toplev.c (read_integral_parameter): Constify. Better control of
error messages.
(main): Use read_integral_parameter to set optimize, id_clash_len,
larger_than_size, and the debugging level.
* toplev.h (read_integral_parameter): Update prototype.
Fri Mar 19 01:42:05 1999 Zack Weinberg <zack@rabi.phys.columbia.edu>
* system.h: Use putc_unlocked, fputc_unlocked, and
fputs_unlocked only if putc_unlocked has a prototype already.
Prototype fputs_unlocked if necessary.
* configure.in: Check for prototypes of putc_unlocked and
fputs_unlocked.
* acconfig.h: Updated.
* config.in, configure: Rebuilt.
Fri Mar 19 02:45:12 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* Makefile.in (INTL_TARGETS): New macro.
($(INTL_TARGETS)): Depend on generated sources; drop dependencies
on cp/parse.c and objc/objc-parse.c.
($(srcdir)/cp/parse.c): Move to cp/Make-lang.in.
* objc/Make-lang.in ($(INTL_TARGETS)): Depend on objc/objc-parse.c.
Thu Mar 18 22:28:53 1999 Jeffrey A Law (law@cygnus.com)
* i860.h (TARGET_SWITCHES): Add documentation.
* i860/paragon.h (TARGET_SWITCHES): Add documentation.
* i370.h (TARGET_SWITCHES): Add documentation.
* fx80.h (TARGET_SWITCHES): Add documentation.
* elxsi.h (TARGET_SWITCHES): Add documentation.
* clipper.h (TARGET_SWITCHES): Add documentation.
* 1750a.h (TARGET_SWITCHES): Add documentation.
* pa.h (TARGET_SWITCHES): Add documentation.
(TARGET_OPTIONS): Likewise.
* mn10300.h (TARGET_SWITCHES): Add documentation.
* h8300.h (TARGET_SWITCHES): Add documentation.
Thu Mar 18 15:58:26 1999 Nick Clifton <nickc@cygnus.com>
* loop.c (strength_reduce): Do not perform pseudo replacements
if the loop contains volatile memory references.
Thu Mar 18 19:09:50 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload.c (find_reloads_toplev): When processing X recursively,
don't alter it destructively except by filling in constants.
Thu Mar 18 10:14:18 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c (default_include): Initialize structure members.
(pass_thru_directive): Change the type of 'keyword_length' to int.
(main): Cast `bindtextdomain' and `textdomain' to (void).
* collect2.c (main): Likewise.
* cppmain.c (main): Likewise.
* gcc.c (main): Likewise.
* gcov.c (main): Likewise.
* protoize.c (main): Likewise.
* toplev.c (main): Likewise.
1999-03-18 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/mips.c (mips_explicit_type_size_string): Correct
its type.
Thu Mar 18 01:24:25 1999 Jeffrey A Law (law@cygnus.com)
* configure.in: Use "exit 1", not "exit (1)".
* configure: Rebuilt.
Wed Mar 17 23:17:42 1999 Mark Kettenis <kettenis@gnu.org>
* config/t-gnu (SYSTEM_HEADER_DIR): New variable. Set to
`/include' in order to find the system's limits.h.
Wed Mar 17 23:00:18 1999 Robert Lipe <robertlipe@usa.net>
* fixinc/fixincl.c: Include auto-host.h instead of config.h.
* fixinc/procopen.c: Likewise.
* fixinc/regex.c: Likewise.
* fixinc/server.c: Likewise.
Wed Mar 17 22:46:13 1999 Mark Elbrecht <snowball3@usa.net.
* config/i386/go32.h: Delete.
* config/i386/djgpp.h: New. Renamed from go32.h.
Added -DDJGPP=2 to CPP_PREDEFINES.
* config/i386/go32-rtems.h: Delete.
* config/i386/djgpp-rtems.h: New. Renamed from go32-rtems.h.
Added -DDJGPP=2 to CPP_PREDEFINES.
* config/i386/xm-go32.h: Delete.
* config/i386/xm-djgpp.h: New. Renamed from xm-go32.h.
* config/i386/x-go32: Delete.
* config/i386/x-djgpp: New. Renamed from x-go32.
* config/i386/t-go32: Delete.
* config/i386/t-djgpp: New. Renamed from t-go32.
* configure.in(pc-msdosdjgpp): Set xm_file to i386/xm-djgpp.h.
Set tm_file to i386/djgpp.h. Set tmake_file to i386/t-djgpp.
Set xmake_file to i386/x-djgpp.
(*-go32-msdos, *-go32*): Remove entries. Warn that GO32/DJGPP V1.X
is now unsupported and *-pc-msdosdjgpp for DJGPP V2.X should be
used instead.
* configure: Rebuilt.
* gcc.c (process_command): Dump link_command_spec too.
Wed Mar 17 20:38:08 1999 Jerry Quinn <jquinn@nortelnetworks.com>
Jeff Law <law@cygnus.com>
* pa.md: Add real PA8000 scheduling information.
* pa.h (processor_type): Add PROCESSOR_8000 symbol.
(ISSUE_RATE): Revamp, including PA8000 support.
* pa.c (override_options): Add 8000 as -mschedule= option.
Do not call strcmp if pa_cpu_string is null.
* pa.md (attr cpu): Add 8000.
* invoke.texi: Add documentation for PA8000 scheduling.
Wed Mar 17 18:20:24 1999 David S. Miller <davem@redhat.com>
* config/sparc/sparc.h (TARGET_SWITCHES, TARGET_OPTIONS):
Add descriptions.
* config/sparc/sp64-elf.h (SUBTARGET_SWITCHES): Likewise.
* config/sparc/splet.h (SUBTARGET_SWITCHES): Likewise.
Wed Mar 17 14:51:19 1999 Richard Henderson <rth@cygnus.com>
* flow.c (compute_immediate_dominators): New function.
* basic-block.h (compute_immediate_dominators): Declare it.
* alpha.h (HARD_REGNO_MODE_OK): Allow only 4 and 8 byte unit modes
in FP regs.
(MODES_TIEABLE_P): Define asymmetricly wrt modes illegal in FP regs.
Wed Mar 17 14:41:41 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/aout.h (ASM_GENERATE_INTERNAL_LABEL): Fix compile
time warning.
* config/arm/arm.md: Fix various compile time warnings.
* config/arm/arm.h: Fix various compile time warnings. Add
function prototypes.
* config/arm/arm.c: Fix various compile time warnings.
(arm_override_options): Reorganize to separate tuning from
targetting.
(bit_count): New function: Return a count of the number of bits
set in a word.
Wed Mar 17 21:29:12 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (eliminate_regs): Don't keep REG_DEAD notes around for
things that were eliminated.
Wed Mar 17 12:16:26 1999 Richard Henderson <rth@cygnus.com>
* function.c (fixup_var_refs_1): First try moving the expression
directly into a register. Don't separate cc0 setter and user.
Wed Mar 17 11:20:29 1999 Dave Brolley <brolley@cygnus.com>
* cppfiles.c (PIPE_BUF): #define PIPE_BUF if not defined already.
Wed Mar 17 09:25:06 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-lex.c: Don't include setjmp.h.
(parse_float): New static function.
(pf_args): New struct.
(yylex): Use them in call to `do_float_handler'.
1999-03-16 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* cexp.y (yyerror): Call verror to get a useful error message.
* cexp.c: Rebuilt.
* .gdbinit: Move command to put breakpoint at abort to end of file
so that gdb does not bail out early.
Tue Mar 16 15:30:19 1999 Nick Clifton <nickc@cygnus.com>
* rtl.h: Rename prototype for free_bb_memory to free_bb_mem.
Tue Mar 16 23:40:09 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (movsi_i): Move t/r alternative after r/rI alternative.
Tue Mar 16 13:44:50 1999 Jim Wilson <wilson@cygnus.com>
* mn10200/mn10200.md (addsi3, subsi3, ashlsi3, lshrsi3, ashrsi3):
Delete emit_library_call_value declaration.
1999-03-16 Zack Weinberg <zack@rabi.columbia.edu>
* cppfiles.c (read_and_prescan): Map backslash-newline to '\r'
(which cannot otherwise appear in the processed buffer) and
move it out of tokens that it appears in the middle of.
Improve performance.
(find_position): New function.
* cpplib.c: \r (one character) indicates backslash
newline, not \\\n (two characters). It cannot appear in the
middle of a token. Call CPP_BUMP_LINE (pfile) whenever
parsing moves past \n or \r. Increment pfile->lineno whenever
a \n is placed into token_buffer. Only one mark can exist at
a time, and CPP_BUMP_LINE must not be used while it is
active. It is automatically cleared by cpp_pop_buffer and
parse_goto_mark. \r is not in is_hor_space or is_space.
(NEWLINE_FIX, NEWLINE_FIX1, adjust_position,
update_position, count_newlines, parse_move_mark): Removed.
(parse_string, copy_comment): New functions.
(parse_name): Returns void.
(parse_set_mark, parse_clear_mark, parse_goto_mark): Take only
one argument, a cpp_reader *. Change for new marking scheme.
(skip_comment): Handle CHILL line comments too. Second
argument is now first character of comment marker; all callers
changed. Issue error for unterminated block comment here.
(cpp_skip_hspace): Recognize CHILL comments.
(copy_rest_of_line): Likewise. Call skip_comment and
parse_string directly, don't go through cpp_get_token. Emit
"/**/" for block comments if -traditional (create_definition
needs this).
(do_define): Don't play with put_out_comments.
(cpp_push_buffer): Initialize ->mark to -1.
(cpp_buf_line_and_col): Just read out the values in the buffer
structure.
(output_line_command): Use cpp_buf_line_and_col. Fix
formatting. Remove stale code.
(cpp_get_token): Break out string parsing code to
parse_string. Use skip_comment for CHILL comments too. Use
copy_comment for put_out_comments instead of dinking with
marks. Remove stale code. Don't call output_line_command
unless it's necessary.
* cpplib.h (parse_marker): Removed.
(struct cpp_buffer): Line_base is now a unsigned char *; add
`mark' [long], remove `marks' [struct parse_marker *].
(parse_set_mark, parse_clear_mark, parse_goto_mark): Update
prototypes.
(CPP_BUMP_LINE, CPP_BUMP_BUFFER_LINE): New macros.
* cppinit.c (is_hor_space, is_space): '\r' is not considered
whitespace.
* cppexp.c (cpp_parse_expression): Use cpp_skip_hspace, not
SKIP_WHITE_SPACE.
* cpphash.c (macarg): Disable line commands while expanding.
Tue Mar 16 11:30:19 1999 Gavin Romig-Koch <gavin@cygnus.com>
* c-lex.c (yylex) : Remove warning for integer literals being
larger than the largest target int. Add warning for integer
literal being larger than than its chosen type.
Tue Mar 16 10:53:17 1999 Gavin Romig-Koch <gavin@cygnus.com>
* invoke.texi: Add -mlong32 documentation.
* config/mips/mips.h (mips_explicit_type_size_string): New.
(TARGET_SWITCHES): Add 'long32'.
(TARGET_OPTIONS): Add 'explicit-type-size'.
(CC1_SPECS): Set -mexplicit-type-size.
(LONG_MAX_SPEC): Change a use of 'no-long64' to 'long32'.
* config/mips/abi64.h (LONG_MAX_SPEC): Same. Add 'mabi=32'.
* config/mips/mips.c (mips_explicit_type_size_string): New.
(override_options): Use it.
* config/mips/osfrose.h (CC1_SPECS): Set -mexplicit-type-size.
* config/mips/mips.h (SUBTARGET_CPP_SIZE_SPEC):
Pointer size now depends on both size longs and size of GP
registers.
Tue Mar 16 10:22:22 1999 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/iris.h (CTORS_SECTION_ASM_OP,DTORS_SECTION_ASM_OP,
dtors_section): Use Pmode == DImode rather than TARGET_LONG64.
* config/mips/mips.c (override_options): Allow -mlong64 and
-mint64 with -mips2 or less.
* config/mips/mips.h (MASK_LONG64): Fix comment.
(POINTER_SIZE): Use Pmode == DImode rather than TARGET_LONG64.
(Pmode): Make Pmode the smaller of longs or gp registers.
* invoke.texi: Note the new size for pointers.
Mon Mar 15 22:45:25 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (ASM_OUTPUT_{DOUBLE,FLOAT}): Always generate IEEE 754
bit-pattern directly.
(ASM_OUTPUT_REG_{PUSH,POP}): Delete.
* rs6000.c (first_reg_to_save): If profiling and context needed,
allocate a reg to save static chain for all ABIs. For AIX
profiling, calculate parameter registers to save based on need.
(output_function_profiler): Save and restore static chain around
profile call for all ABIs.
1999-03-15 Zack Weinberg <zack@rabi.columbia.edu>
* cppinit.c: Instead of one pending list, keep separate lists
for each category of pending option: -D/-U, -A, -include,
-imacros. Move the four partial include-path lists into the
pending block. Use head and tail pointers so we don't ever
have to reverse the lists.
(cpp_start_read): Break out blocks of code to their own
functions: install_predefs and initialize_dependency_output.
Use path_include for C_INCLUDE_PATH and friends as well as
CPATH. Remove include_defaults gunk. Warn about the
combination of -lang-chill and -trigraphs. Optimize string
bashing. Walk each pending list once, deallocating as we go.
(append_include_chain): Brought over from cppfiles.c. Mark
dirs as system include dirs if and only if appending to
system include path. If opts->verbose, print a notice when a
dir is dropped from the include path because it doesn't
exist. Fix memory leak: this function is not supposed to copy
its DIR argument.
(nreverse_pending, push_pending): Removed.
(APPEND): New macro for adding to pending lists.
(path_include): Can now add to any partial include path.
(base_name): Bring over from cccp.c.
(cpp_options_init): Allocate the pending block.
(cpp_handle_option): Add --version. Exit after --help. Fix
formatting. Order -ifoo options by frequency of usage.
(install_predefs): New function, simplified version of code
that was in cpp_start_read.
(initialize_dependency_output): Likewise. Understand OBJECT_SUFFIX.
* cppfiles.c (simplify_pathname): Export.
(merge_include_chains): Don't nreverse the lists. If
opts->verbose, print a notice when a duplicate dir is detected
and dropped from the include path.
(finclude): Fix excessive cleverness in setting
fp->system_header_p.
(actual_directory): Set x->sysp from
CPP_BUFFER (pfile)->system_header_p so that one system header
may include another with "".
(deps_output): Fix double adjustment of deps_size which would
cause all dependencies after the first two lines to be lost.
* cpplib.c (cpp_unassert): New function.
* cpplib.h: Lay out struct cpp_pending here. Adjust
prototypes. Add include_prefix_len to struct cpp_options.
Mon Mar 15 16:01:52 1999 Jim Wilson <wilson@cygnus.com>
* config/misp/mips.h (REGISTER_MOVE_COST): Make the cost of moving
from HI/LO/HILO/MD into general registers the same as for one
of moving general registers to HI/LO/HILO/MD.
Mon Mar 15 12:39:38 1999 Nick Clifton <nickc@cygnus.com>
* config/m32r/m32r.c (init_idents): New function. Initialize
static tree nodes for m32r specific attribute identifiers. Remove
leading and trailing double underscores from the attribute names.
(m32r_valid_machine_decl_attribute): Call init_idents.
(m32r_encode_section_info): Call init_idents.
Mon Mar 15 10:20:20 1999 Mark Mitchell <mark@markmitchell.com>
* reload.c (find_reloads): Add a REG_LABEL note if we substitute a
LABEL_REF for something else.
Mon Mar 15 08:24:17 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fold-const.c (exact_real_inverse): Move variable `float_error'
into the scope where it is used.
(const_binop_1): New static function.
(cb_args): New struct.
(const_binop): Use them in call to `do_float_handler'.
(fold_convert_1): New static function.
(fc_args): New struct.
(fold_convert): Use them in call to `do_float_handler'.
Mon Mar 15 22:50:18 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* rtlanal.c (auto_inc_p): New function.
* rtl.h (auto_inc_p): Prototype it.
* reload1.c (add_auto_inc_notes): New function.
(reload): Strip REG_INC notes and call add_auto_inc_notes
for each insn to restore them correctly.
1999-03-15 Manfred Hollstein <manfred@s-direktnet.de>
* fixinc/Makefile.in (procopen.o): List the actual
dependencies.
Sun Mar 14 16:22:10 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cse.c (check_fold_consts): New static function.
(cfc_args): New struct.
(simplify_relational_operation): Use them in call to
`do_float_handler'.
* toplev.c (do_float_handler): New function to wrap calls to
setjmp/set_float_handler.
* toplev.h (do_float_handler): Add extern prototype.
* tree.c (build_real_from_int_cst_1): New static function.
(brfic_args): New struct.
(build_real_from_int_cst): Use them in call to
`do_float_handler'.
Sun Mar 14 01:15:06 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Mar 13 17:37:18 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (sched_analyze_1): Only clear reg_last_uses on a SET.
Sat Mar 13 11:36:16 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.c (arm_split_constant): Don't try to force a constant to
memory after arm_reorg has run.
(after_arm_reorg): New static variable.
(arm_reorg): Set it.
(output_func_epilogue): Clear it.
Fri Mar 12 20:26:32 1999 David Edelsohn <edelsohn@gnu.org>
* configure.in ({rs6000,powerpc}-ibm-aix*): Set float_format to none.
* configure: Rebuilt.
Fri Mar 12 20:45:30 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* unroll.c (loop_iterations): Don't return a final value for EQ
comparison loops.
Fri Mar 12 12:35:01 1999 Jim Wilson <wilson@cygnus.com>
* reload1.c (calculate_needs_all_insns): When ignore equivalence
setting insn, clear need_elim, need_reload, and need_operand_change.
Fri Mar 12 07:54:43 1999 Bruce Korb <korb@datadesign.com>
* fixinc/fixinc.*: Some changes from the fixincl-branch
were not applied (??!!). Corrected.
* fixinc/Makefile.in: Same thing.
Fri Mar 12 00:51:43 1999 Jeffrey A Law (law@cygnus.com)
* expr.c (expand_expr): Allow a CALL_EXPR with a mode wider than
MAX_INTEGER_COMPUTATION_MODE.
Thu Mar 11 14:00:58 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (HARD_REGNO_MODE_OK): Disallow QI/HImode in fp regs.
(MODES_TIEABLE_P): Update.
* alpha.md (ev5_e0): Conflict loads and stores.
Thu Mar 11 13:55:52 1999 Richard Henderson <rth@cygnus.com>
* machmode.h (smallest_mode_for_size): Prototype.
* stor-layout.c (smallest_mode_for_size): Remove static.
Thu Mar 11 21:25:59 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Don't do biv increment -> DEST_REG giv
conversion if we don't know the lifetime.
Thu Mar 11 20:37:59 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload.1c (delete_address_reloads_1): Check for reloads of
CURRENT_INSN even if it sets DST.
Thu Mar 11 10:29:50 1999 Jason Merrill <jason@yorick.cygnus.com>
* dwarf2out.c (add_AT_lbl_offset): Rename from add_AT_section_offset.
(print_die, size_of_die, value_format, output_die): Adjust.
Thu Mar 11 10:27:42 1999 Robert Lipe <robertlipe@usa.net>
* dwarf2out.c (TEXT_SECTION_LABEL, DEBUG_LINE_SECTION_LABEL,
DEBUG_INFO_SECTION_LABEL, ABBREV_SECTION_LABEL,
text_section_label, debug_line_section_label,
debug_info_section_label, abbrev_section_label): New.
(output_compilation_unit_header): Emit label associated
with section instead of section name itself.
(out_pubnames, output_aranges, output_line_info,
dwarf2out_finish): Likewise.
(dwarf2out_init): Build internal label names for sections
from static labels.
Thu Mar 11 17:28:32 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (mulsi3): End mul.l sequence with a no-op move.
Thu Mar 11 08:52:02 1999 Bruce Korb <korb@datadesign.com>
* Makefile.in: Activated fixinc/mkfixinc.sh.
* configure.in: Activated fixinc/mkfixinc.sh.
Thu Mar 11 01:38:02 1999 Mumit Khan <khan@xraylith.wisc.edu>
* cppfiles.c (INO_T_EQ): Handle UWIN.
* c-common.c (decl_attributes): Flag unrecognized attribute
functions as warnings instead of as errors.
Support for i386-pc-uwin.
* i386/uwin.h: New file.
* i386/xm-uwin.h: New file.
* i386/t-uwin: New file.
* i386/uwin.asm: New file.
* configure.in (i[3456]86-*-uwin*): Define.
Add Workaround for vfork bug when hosted on uwin.
* configure: Regenerate.
* cccp.c (INO_T_EQ): Undefine. UWIN has inodes.
(absolute_filename): UWIN uses POSIX pathnames only.
* libgcc2.c (getpagesize): Do not define for UWIN.
(mprotect): Likewise.
* protoize.c (dirent.h): Conditionally include.
(fputc): Prototype only if it's not a macro.
Wed Mar 10 02:49:04 1999 Jason Merrill <jason@yorick.cygnus.com>
* configure.in: Remove init_priority stuff.
1999-03-11 Colin Smith <colin@wrs.com>
* sdbout.c (plain_type_1): Make boolean types work better with sdb.
Thu Mar 11 00:20:52 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* gcc.texi: Update bug reporting instructions to match
current ezmlm list reality.
Wed Mar 10 23:11:19 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gcc.c (print_file_name, print_prog_name, spec_machine,
read_specs, set_spec, lookup_compiler, build_search_list,
putenv_from_prefixes, find_a_file, record_temp_file,
delete_if_ordinary, handle_braces, do_spec, do_spec_1, find_file,
is_directory, validate_switches, used_arg, default_arg,
pfatal_with_name, perror_with_name, pfatal_pexecute, fatal, error,
notice, add_preprocessor_option, add_assembler_option,
add_linker_option, process_command, execute,
unused_prefix_warnings, clear_args, fatal_error,
lang_specific_driver, user_specs, compiler, link_command_spec,
option_map, translate_options, make_temp_file, temp_name,
programname, path_prefix, machine_suffix, just_machine_suffix,
gcc_exec_prefix, standard_exec_prefix, standard_exec_prefix_1,
md_exec_prefix, md_startfile_prefix, md_startfile_prefix_1,
standard_startfile_prefix, standard_startfile_prefix_1,
standard_startfile_prefix_2, tooldir_base_prefix, tooldir_prefix,
multilib_dir, temp_filename, temp_file, command, switchstr,
infile, outfiles, input_filename, input_basename, input_suffix,
check_live_switch, main): Qualify a char* with the `const' keyword.
Wed Mar 10 20:28:29 1999 Jeffrey A Law (law@cygnus.com)
* lcm.c: New file.
* Makefile.in (OBJS): Add lcm.o.
(lcm.o): Add dependencies.
* gcse.c (compute_pre_local_properties): Delete.
(compute_pre_data): Use compute_local_properties instead of
compute_pre_local_properties.
* gcse.c: More comments, whitespace and similar fixes.
(dump_cuid_table, maybe_set_rd_gen, dump_cprop_data): Delete.
(dump_pre_data, compute_cprop_local_properties): Likewise.
(one_classic_gcse_pass): Lose unused argument. All callers changed.
(compute_hash_table, compute_expr_hash_table): Likewise.
(compute_set_hash_table, one_pre_gcse_pass, mark_call): Likewise.
(cprop_insn, cprop, one_cprop_pass): Add new argument ALTER_JUMPS.
All callers changed. Only alter jumps if ALTER_JUMPS is nonzero.
Lose unused argument.
(gcse_main): Always run a cprop pass after finishing global cse.
(compute_local_properties): New function.
(hash_scan_pat, hash_scan_insn): No longer call maybe_set_rd_gen.
(compute_cprop_data): Use compute_local_properties.
* gcse.c: Update various comments.
(current_function_calls_longjmp): Delete declaration.
* gcse.c (run_jump_opt_after_gcse): New variable.
(gcse_main): Returns an integer.
(hash_scan_set): Record initializations from CONST_DOUBLEs too.
(try_replace_reg): Update some comments.
(cprop_insn): Allow propagation into some JUMP_INSNs too.
* rtl.h (gcse_main): Update prototype.
* toplev.c (rest_of_compilation): If gcse_main returns nonzero,
then run a jump optimization pass.
* jump.c (delete_barrier_successors): Delete nop jumps too.
Wed Mar 10 19:04:31 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.c (fp_arith_reg_operand): Actually test if reg is suitable
for FP arithmetic. Changed caller.
* sh.md (subsf3, subsf_i): Use fp_arith_reg_operand.
Wed Mar 10 18:56:31 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (choose_reload_regs): When inheriting from the frame
pointer, don't clobber it.
Wed Mar 10 08:01:52 1999 Bruce Korb <korb@datadesign.com>
* fixinc/fixinc.*: Resync-ed with the files in this
directory.
* fixinc/mkfixinc.sh: The machine case elements were
out-of-order. (the ix86-*-linux-gnu* entry needed to
be earlier).
Wed Mar 10 00:01:24 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_combine_note_store): Fix calculation of number
of affected registers.
Tue Mar 9 15:48:15 1999 Richard Henderson <rth@cygnus.com>
* flow.c (tidy_fallthru_edge): Be more careful finding the last
BARRIER of a list. Delete the cc0 setter as well as a cond jump.
Tue Mar 9 15:26:02 1999 Hans-Peter Nilsson <hp@bitrange.com>
* i386.md (ashlsi3 splitter): Fix typo in last change.
Tue Mar 9 11:35:20 1999 Richard Henderson <rth@cygnus.com>
* reg-stack.c (stack_reg_life_analysis): Use returnjump_p
instead of an explicit test for RETURN.
Tue Mar 9 09:33:16 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (toplev.o): Depend on $(BASIC_BLOCK_H).
* toplev.c: Include basic-block.h.
Tue Mar 9 02:08:17 1999 Jeffrey A Law (law@cygnus.com)
* calls.c (load_register_parameters): New function.
(expand_call): Use it.
* calls.c (expand_call): Slightly reorganize code.
* calls.c (compute_argument_addresses): New function.
(rtx_for_function_call): New function.
(expand_call): Use them.
* i386.md (zero_extendhisi2): Split into an expander and anonymous
pattern. Add new anonymous pattern for use when optimizing for
size or for the PPro.
(zero_extendqihi2, zero_extendqisi2): Likewise.
Mon Mar 8 23:43:47 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (sched_analyze_1): Fix last change -- add clobber
dependencies to sets in the non-hard-reg case too.
Mon Mar 8 18:55:21 1999 Marc Espie <espie@cvs.openbsd.org>
* config/openbsd.h (HANDLE_SYSV_PRAGMA): Define.
Mon Mar 8 16:04:44 1999 Jim Wilson <wilson@cygnus.com>
* local-alloc.c (combine_regs): Don't combine if we have a hard reg
for which CLASS_LIKELY_SPILLED_P is true.
* unroll.c (loop_iterations): Only call loop_find_equiv_value if we
have a REG or SUBREG.
Mon Mar 8 15:27:42 1999 Jeffrey A Law (law@cygnus.com)
* i386.md (ashlsi3): Revise comments. Provide new anonymous
pattern for Pentium and PPro/PII. Reverse constraints in
generic ashlsi3 anonymous pattern.
* calls.c (initialize_argument_info): Accept a pointer to
CUMULATIVE_ARGS.
(expand_call): Pass the address of CUMULATIVE_ARGS.
* rs6000/xm-sysv4.h (HOST_BITS_PER_LONGLONG): Remove #if 0.
* mn10300.h (CASE_DROPS_THROUGH): Delete.
* mn10200.h (CASE_DROPS_THROUGH): Delete.
* h8300.h (CASE_DROPS_THROUGH): Delete.
* flow.c (merge_blocks_nomove): For HAVE_cc0 targets, make sure
to also delete the cc0 setter when deleting a conditional branch
to the next block.
Mon Mar 8 18:47:11 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* regmove.c (copy_src_to_dest): New argument max_old_uid.
Mon Mar 8 08:23:00 1999 Bruce Korb <korb@datadesign.com>
* ChangeLog: Merged entries from fixincl-branch.
Sun Mar 7 11:48:56 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (ENCODE_BLOCKAGE): Don't shift unit too far.
(print_exp): Special case addition of a constant.
(print_value) [CONST_INT]: Use HOST_WIDE_INT_PRINT_HEX.
Sun Mar 7 11:21:02 1999 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (reg_last_clobbers): New.
(reg_pending_clobbers, bb_reg_last_clobbers): New.
(compute_block_backward_dependences): Allocate memory for them.
(schedule_region): Likewise.
(sched_analyze_1): Clobbers don't interfere with one another.
They do interfere with sets ...
(sched_analyze_2): ... and uses.
(sched_analyze): Likewise.
(sched_analyze_insn): Update reg_last_clobbers appropriately.
Sun Mar 7 08:30:37 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gmon-sol2.c: Include config.h and system.h. Don't redundantly
include system header files.
(sccsid): Remove.
(moncontrol, monstartup, _mcleanup, internal_mcount): Prototype.
(_mcleanup): Add the `const' keyword to a char*.
(internal_mcount): Declare `etext' as a char[] not a function.
Cast `etext' to char* when calling `monstartup'.
* sparc.c (frame_base_name, save_regs, restore_regs,
build_big_number, sparc_cmodel_string, sparc_align_loops_string,
sparc_align_jumps_string, sparc_align_funcs_string, code_model,
cpu_default, cpu_table, output_function_prologue,
output_function_epilogue, output_return,
sparc_flat_output_function_prologue, ultra_code_names,
sparc_flat_output_function_epilogue): Constify a char*.
(hypersparc_adjust_cost): Add a default case in a switch.
* sparc.h (sparc_cmodel_string, OVERRIDE_OPTIONS,
sparc_cpu_select, sparc_align_loops_string,
sparc_align_jumps_string, sparc_align_funcs_string,
output_return): Constify a char*.
* sparc.md (movdi): Change the comparison of HOST_BITS_PER_WIDE_INT
so that we check "== 32", instead of "!= 64". Cast a value to
HOST_WIDE_INT when comparing against one. Hide the declaration
for variable `chain'.
Sun Mar 7 08:05:27 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* system.h (const, inline): Move the stage2 handling of these
keywords-as-macros from here...
* gansidecl.h (const, inline): ...to here.
Sun Mar 7 02:44:15 1999 Richard Henderson <rth@cygnus.com>
* recog.c (push_operand, pop_operand): VOIDmode needn't match modes.
Sun Mar 7 01:58:47 1999 Richard Henderson <rth@cygnus.com>
* cse.c (canon_hash): Never reject hard regs in CCmode.
Sun Mar 7 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Mar 6 17:18:44 1999 Richard Earnshaw (rearnsha@arm.com)
Richard Henderson <rth@cygnus.com>
* flow.c (make_edges): Handle casesi that jump to default branch.
If CASE_DROPS_THROUGH, force fallthru to block after casesi.
Sat Mar 6 07:49:23 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-aux-info.c (data_type, affix_data_type, gen_decl,
gen_formal_list_for_type, gen_formal_list_for_func_def, gen_type):
Qualify a char* with the `const' keyword.
* c-common.c (declare_hidden_char_array, add_attribute, if_elt,
declare_function_name, decl_attributes, format_char_info,
check_format_info, binary_op_error): Likewise.
* cexp.y (yyerror, error, pedwarn, warning, token): Likewise.
* gcse.c (dump_hash_table): Likewise.
* integrate.c (function_cannot_inline_p): Likewise.
* optabs.c: Include insn-config.h earlier.
(init_libfuncs, init_integral_libfuncs, init_floating_libfuncs):
Qualify a char* with the `const' keyword.
* real.c (asctoe24, asctoe53, asctoe64, asctoe113, asctoe,
asctoeg, mtherr, ereal_atof): Likewise.
* real.h (ereal_atof): Likewise.
* sbitmap.c (dump_sbitmap_vector): Likewise.
* sbitmap.h (dump_sbitmap_vector): Likewise.
* stmt.c (nesting, n_occurrences, expand_start_case): Likewise.
* toplev.c (rest_of_compilation): Likewise.
* tree.h (function_cannot_inline_p, expand_start_case): Likewise.
Fri Mar 5 23:16:42 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (ASM_OUTPUT_REG_{PUSH,POP}): Add 64-bit support and do
not overwrite AIX link register save area.
Fri Mar 5 23:08:01 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload.c (find_reloads_subreg_address): Actually create the USE
for the register, not the new memory location.
Fri Mar 5 21:41:07 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (emit_reload_insns): If pseudo that can't be replaced
with its equivalent constant, fall back to reload_in.
Fri Mar 5 13:20:39 1999 Richard Henderson <rth@cygnus.com>
* Makefile.in: Delete .flow2 debugging files.
Fri Mar 5 11:36:11 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (arm_override_options): Change default target
cpu selection so that enabling TARGET_APCS_32 does not override
default target CPU.
Fri Mar 5 19:26:23 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.h (SLOW_BYTE_ACCESS): Define to 1.
(BOOL_TYPE_SIZE): Define.
Fri Mar 5 02:14:54 1999 John Wehle (john@feith.com)
* function.c (assign_stack_temp_for_type): Abort
if mode == Blkmode and align is less than
BIGGEST_ALIGNMENT / BITS_PER_UNIT.
(assign_stack_temp_for_type): Round the size parameter
passed to assign_stack_local instead of size itself.
Thu Mar 4 15:00:35 1999 Richard Henderson <rth@cygnus.com>
* flow.c (delete_unreachable_blocks): Mark blocks as they
are put on to the worklist, not as they are taken off.
Thu Mar 4 00:05:44 1999 Jeffrey A Law (law@cygnus.com)
* function.c (current_function_has_computed_jump): Remove duplicate
definition.
Wed Mar 3 19:09:11 1999 Jim Wilson <wilson@cygnus.com>
* m68k/m68020-elf.h (INIT_SECTION_ASM_OP, FINI_SECTION_ASM_OP): Undef.
(STARTFILE_SPEC, ENDFILE_SPEC): Define to empty string.
* sparc/elf.h (MULDI3_LIBCALL, DIVDI3_LIBCALL, UDIVDI3_LIBCALL,
MODDI3_LIBCALL, UMODDI3_LIBCALL, STDC_0_IN_SYSTEM_HEADERS): Undef.
(INIT_SUBTARGET_OPTABS): Define to empty.
Wed Mar 3 00:00:37 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.c (force_into): New function.
(expand_block_move): Use it.
Tue Mar 2 10:39:43 1999 Nick Clifton <nickc@cygnus.com>
* cccp.c (struct default_include): Add 'included' field.
(main): Set 'included' field when a default include directory
is added to the chain. If -v is specified list all default
include directories which do not get appended to the chain.
Tue Mar 2 09:24:10 1999 Nick Clifton <nickc@cygnus.com>
* configure.in (gxx_include_dir): Rename to
gcc_gxx_include_dir in order to prevent it being overridden by
a top level Makefile.
(gcc_tooldir): If $exec_prefix != $prefix then use the
difference between the two as the basis for gcc_tooldir.
* configure: Rebuild.
* Makefile.in: Rename gxx_include_dir to gcc_gxx_include_dir.
Tue Mar 2 16:45:31 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* unroll.c (copy_loop_body): Don't make extra copies of
NOTE_INSN_LOOP_CONT notes.
Tue Mar 2 07:44:56 1999 Mark Mitchell <mark@markmitchell.com>
* tree.c (save_tree_status): Don't treat functions with no context
as nested.
Tue Mar 2 09:37:05 1999 Robert Lipe <robertlipe@usa.net>
* Makefile.in (MAKEINFO): Use makeinfo built from sibling
tree when available.
Tue Mar 2 10:12:48 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* alpha.c (alpha_cpu_name, alpha_cpu_string, alpha_tp_string,
alpha_fprm_string, alpha_fptm_string, alpha_mlat_string,
current_function_file): Add the `const' keyword.
(normal_memory_operand): Mark parameter `mode' with
ATTRIBUTE_UNUSED.
(alpha_expand_unaligned_load): Add a default case to a switch.
* alpha.h (alpha_cpu_string, alpha_fprm_string, alpha_fptm_string,
alpha_tp_string, alpha_mlat_string): Add the `const' keyword.
(normal_memory_operand): Add prototype.
* alpha.md: Cast an expression to `unsigned HOST_WIDE_INT' when
comparing against one.
Tue Mar 2 10:00:21 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* mips.c (abort_with_insn): Make function static, add a prototype,
constify 2nd parameter and mark with ATTRIBUTE_NORETURN.
(current_function_file, mips_cpu_string, mips_isa_string,
mips_abi_string, mips_no_mips16_string, mips_entry_string,
mips_move_1word, mips_move_2words, output_block_move, load_store,
override_options, make_temp_file, mips16_fp_args): Qualify a char*
with the `const' keyword.
* mips.h (current_function_file, mips_cpu_string, mips_isa_string,
mips_abi_string, mips_entry_string, mips_no_mips16_string,
mips_move_1word, mips_move_2words, output_block_move): Likewise.
(abort_with_insn): Remove extern prototype.
* mips.md: Qualify a char* with the `const' keyword.
Remove many unused variables named `label'.
Tue Mar 2 01:27:52 1999 H.J. Lu (hjl@gnu.org)
* Makefile.in (cpp_install_dir, INSTALL_CPP, UNINSTALL_CPP): New
variables.
(install-cpp, uninstall-cpp): New targets.
(install-normal): Depend on $(INSTALL_CPP).
(uninstall): Depend on $(UNINSTALL_CPP).
* configure.in (cpp_install_dir): New, substitute.
(tmake_file): Added t-install-cpp for --enable-cpp.
* configure: Rebuilt.
* cpp.sh: New cpp script.
* config/t-install-cpp: New target fragment.
Tue Mar 2 01:40:01 1999 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
Jeffrey A Law (law@cygnus.com)
* cse.c (fold_rtx): Update comments for (const (minus (label) (label)))
case.
(cse_insn): Avoid creating a bogus REG_EQUAL note for
(const (minus (label) (label)))
(record_jump_cond): Fix mismatched paren in comment.
Tue Mar 2 01:07:12 1999 Dan Nicolaescu <dann@godzilla.ics.uci.edu>
* final.c (end_final): There are 11 words in the "main header"
structure, not 10.
Tue Mar 2 00:09:18 1999 Marc Espie <espie@cvs.openbsd.org>
* extend.texi: Reference __extension__ in the index.
Mon Mar 1 19:09:32 1999 Jim Wilson <wilson@cygnus.com>
* Makefile.in (CROSS_FLOAT_H): Delete.
(FLOAT_H): Use float_h_file.
(rest.cross, stmp-int-hdrs): Delete gfloat.h dependency.
(gfloat.h): Delete.
(stmp-int-hdrs): Use FLOAT_H instead of gfloat.h.
(mostlyclean): Delete gloat.h reference.
(install-cross-rest, install-float-h-cross, stmp-headers): Update
comments.
* configure.in (sparcv9-*-solaris2*): Set float_format to none.
(sparc-*-solaris2*): Set float_format to none for 2.5 and higher.
(float_h_file): Set from float_format. Substitute into Makefile.in.
(float_format): No longer substitute into Makefile.in.
* cross-make (FLOAT_H): Delete.
* config/mips/t-cross64 (FLOAT_H): Delete.
* configure: Rebuilt.
Mon Mar 1 16:36:18 1999 Jeffrey A Law (law@cygnus.com)
* mips.md (div_trap_normal, div_trap_mips16): Require the dependent
insn to be an INSN before looking at its pattern.
Mon Mar 1 15:03:51 1999 Jim Wilson <wilson@cygnus.com>
* config/m68k/lb1sf68.asm (udivsi3): Change jmi to jcs. Fix comments.
* config/m68k/m68k.h (LEGITIMATE_INDEX_REG_P): Reject SIGN_EXTEND of
HImode reg when TARGET_5200.
Mon Mar 1 21:44:30 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
From Toshiyasu Morita:
* sh.h (CACHE_LOG): SH2 has cache, too.
Mon Mar 1 14:23:36 1999 Catherine Moore <clm@cygnus.com>
* toplev.c (compile_file): Disable -ffunction-sections and
debugging warning if the object format is elf.
Mon Mar 1 11:46:25 1999 Vladimir N. Makarov <vmakarov@cygnus.com>
* config/h8300/h8300.c (print_operand): Use 16 bit addressing
when the data in 8-bit area and can not be addressed by 8-bit.
Sun Feb 28 16:40:00 1999 Richard Henderson <rth@cygnus.com>
* flow.c (create_basic_block): Disregard integrated bb notes.
Sun Feb 28 15:57:06 1999 Richard Henderson <rth@cygnus.com>
* sparc.md (blockage, nonlocal_goto_receiver): Set length to 0.
Sun Feb 28 14:47:53 1999 Arturo Montes <mitosys@colomsat.com.co>
* config/i386/t-sco5gas (crti.o): New target.
Sun Feb 28 15:10:17 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (elf_high, movsi_got, *movsi_got_internal,
*movsi_got_internal_mem, GOT splitter, movdf_hardfloat32,
movdf_softfloat32, movdf_hardfloat64, movdf_softfloat64,
load_multiple, allocate_stack, call_indirect_aix32,
call_indirect_aix64, call_value_indirect_aix32,
call_value_indirect_aix64, call_indirect_nt,
call_value_indirect_nt): Use gpc_reg_operand instead of
register_operand.
Sun Feb 28 15:10:17 1999 Michael Meissner <meissner@cygnus.com>
* rs6000.md (one_cmplsi2, andsi3, iorsi3, xorsi3, *eqvsi3,
*andcsi3, *iorcsi3, *nandsi3, *norsi3): Add alternatives to use CR
other than cr0.
* rs6000.c (and{,64}_operand): If the user did -ffixed-cr0, don't
allow andi. or andis. which always set cr0.
Sun Feb 28 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Feb 28 02:00:38 1999 Jeffrey A Law (law@cygnus.com)
* invoke.texi: Update information for PA scheduling.
Sat Feb 27 23:21:47 1999 Jerry Quinn <jquinn@nortelnetworks.com>
Mike Stump <mrs@wrs.com>
* pa.c (override_options): Change default to 7100LC.
* pa.h (REG_ALLOC_ORDER): Change order to allocate left half of
float regs before right half of float regs.
Sat Feb 27 22:48:38 1999 H.J. Lu (hjl@gnu.org)
Jeffrey A Law (law@cygnus.com)
* frame.h: Update some comments.
* defaults.h (TARGET_ATTRIBUTE_WEAK): Define.
* crtstuff.c (__register_frame_info, __deregister_frame_info): Declare
using TARGET_WEAK_ATTRIBUTE.
(__do_global_dtors_aux): Check if __deregister_frame_info is
zero before calling it.
(__do_global_dtors): Likewise.
(frame_dummy): Check if __register_frame_info is zero before
calling it.
(__frame_dummy): Likewise.
Sat Feb 27 19:18:24 1999 Jeffrey A Law (law@cygnus.com)
* SERVICE: Update from the FSF.
Sat Feb 27 14:31:22 1999 Arturo Montes <mitosys@colomsat.com.co>
* config/i386/t-sco5 (crti.o): New target.
* config/i386/sco5.h (STARTFILE_SPEC): Include crti.o when
linking -shared.
* configure.in (i[34567]86-*-sco3.2v5*): Add crti.o.
Sat Feb 27 01:12:40 1999 Jeffrey A Law (law@cygnus.com)
* md.texi (prologue,epilogue): Document named patterns.
Fri Feb 26 19:31:25 1999 Dave Love <fx@gnu.org>
* md.texi, invoke.texi: Fix unterminated @xrefs.
Fri Feb 26 15:33:45 1999 Richard Henderson <rth@cygnus.com>
* genattrtab.c (simplify_knowing): Fix uninitialized read
in Feb 21 change.
* genextract.c (main): Clear recog_operands before extracting.
Fri Feb 26 02:24:57 1999 Jeffrey A Law (law@cygnus.com)
* c-pragma.c (add_weak); Delete. Moved into...
* varasm.c (add_weak): New external function.
(declare_weak): If HANDLE_PRAGMA_WEAK, then add the function to
the list of weak functions.
* c-pragma (add_weak): Declare.
Thu Feb 25 23:43:59 1999 Richard Henderson <rth@cygnus.com>
Flow rewrite to use basic block structures and edge lists:
* basic-block.h (x_basic_block_head, x_basic_block_end): Kill.
(basic_block_computed_jump_target, basic_block_live_at_start): Kill.
(struct edge_def): New.
(struct basic_block_def): New.
(basic_block_info): New.
(BLOCK_HEAD, BLOCK_END): Update.
(ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR): New.
(uid_block_number): Kill.
(basic_block_for_insn, BLOCK_FOR_INSN): New.
(BLOCK_NUM): Update.
* flow.c (XNMALLOC): Kill.
(max_uid_for_flow): Kill.
(uid_block_number): Kill.
(uid_volatile): Turn into a bitmap.
(SET_INSN_VOLATILE): New.
(basic_block_info): New.
(entry_exit_blocks): New.
(x_basic_block_head, x_basic_block_end): Kill.
(basic_block_computed_jump_target, basic_block_live_at_start): Kill.
(flow_int_list_blocks, basic_block_succ, basic_block_pred): Kill.
(basic_block_loop_depth): Kill.
(basic_block_for_insn): New.
(find_basic_blocks): Split out initial block counting into
count_basic_blocks. Call functions split out of find_basic_blocks_1.
(count_basic_blocks): New.
(find_basic_blocks_1): Split out edge recognition, unreachable
block deletion.
(create_basic_block): New.
(compute_bb_for_insn): New.
(clear_edges): New.
(free_bb_memory): Kill.
(add_edge, add_edge_to_label): Kill.
(mark_label_ref): Kill.
(make_edges): Rewrite to use edge lists.
(make_edge, make_label_edge): New.
(mark_critical_edges): New.
(split_edge, insert_insn_on_edge): New.
(commit_one_edge_insertion, commit_edge_insertions): New.
(delete_unreachable_blocks): Rewrite to use edge lists.
Split out EH region manipulation into delete_eh_regions.
Call tidy_fallthru_edge and merge_blocks.
(delete_eh_regions): New.
(delete_note_p): New.
(delete_insn_chain): New.
(delete_block): Split out code into delete_insn_chain and
tidy_fallthru_edge. Update edge lists.
(expunge_block): New.
(flow_delete_insn): New?
(can_delete_label_p): New?
(merge_blocks_nomove, merge_blocks): New.
(tidy_fallthru_edge): New.
(calculate_loop_depth): New.
(life_analysis): Allocate and free uid_volatile.
(free_basic_block_vars): Update for new structures.
(record_volatile_insns): Use SET_INSN_VOLATILE.
(mark_regs_live_at_end): Tidy EXIT_IGNORE_STACK usage.
(mark_used_regs): Likewise.
(life_analysis_1): Use bb global_live_at_start, global_live_at_end,
local_set regsets. Use bb->aux to store new_live_at_end. Begin
life propagation from EXIT_BLOCK rather than last block. Clear
regs_ever_live after mark_regs_live_at_end.
(allocate_for_life_analysis): Update for new structures.
(propagate_block): Split out loop depth calculation to
calculate_loop_depth.
(regno_uninitialized): Use bb->global_live_at_start.
(regno_clobbered_at_setjmp): Likewise.
(dump_bb_data): Likewise.
(find_auto_inc): Use BLOCK_FOR_INSN instead of BLOCK_NUM.
(dump_flow_info): Update for new structures.
(dump_edge_info): New.
(print_rtl_with_bb): Update for new structures.
(compute_preds_succs): Do no work -- convert edge lists.
(set_block_for_insn): From corpse of old set_block_num.
(set_block_num): Call it.
* rtl.c (note_insn_name): Add NOTE_INSN_BASIC_BLOCK.
* rtl.h (rtunion_def): Add bb entry.
(NOTE_BASIC_BLOCK): New.
(NOTE_INSN_BASIC_BLOCK): New.
* varray.h (varray_data_tag): Add bb entry.
(VARRAY_BB_INIT, VARRAY_BB): New.
* emit-rtl.c (emit_label_before): New.
* except.c (expand_rethrow): Delete insns following the call to
rethrow. Put the REG_EH_RETHROW on the call.
* jump.c (returnjump_p, returnjump_p_1): New.
* expr.h (nonlocal_goto_handler_labels): New declaration.
* function.c (nonlocal_goto_handler_labels): Define it.
(push_function_context_to): Save it.
(pop_function_context_from): Restore it.
(init_function_start): Clear it.
(nonlocal_label_rtx_list): Kill.
* function.h (struct function): Add storage space for it.
* stmt.c (expand_nl_handler_label): Return the new label.
(expand_nl_goto_receivers): Collect a list of them in
nonlocal_goto_handler_labels.
* Makefile.in (print-rtl.o): Depend on basic-block.h.
(flow.o): Depend on insn-flags.h.
* function.c (thread_prologue_and_epilogue_insns): Do not
half-heartedly update bb structures.
* toplev.c: Add flow2 dump as -dw.
(rest_of_compilation): Finish .greg before flow2.
* graph.c (draw_edge): Handle class 3.
(print_rtl_graph_with_bb): Make abnormal edges red class 2,
change non-fall-thru but adjacent to green class 3. Update
to use new structures.
* print-rtl.c (print_rtx): Handle NOTE_INSN_BASIC_BLOCK.
* reg-stack.c (BLOCK_NUM): Convert to function. Abort if
block_number is -1.
(reg_to_stack): Initialize block_num to -1.
* combine.c (set_nonzero_bits_and_sign_copies): Update reference
to basic_block_live_at_start to bb->global_live_at_start.
(try_combine): Likewise.
(reg_dead_at_p): Likewise.
* global.c (global_conflicts): Likewise.
Handle stack regs on all abnormal edges, not just computed jumps.
(mark_elimination): Update reference to basic_block_live_at_start.
(build_insn_chain): Likewise.
* haifa-sched.c (haifa_edge): Rename from edge for conflict.
(is_cfg_nonregular): Look at nonlocal_goto_handler_labels instead
of nonlocal_label_rtx_list.
(check_live_1): Update reference to basic_block_live_at_start.
(update_live_1): Likewise.
(find_pre_sched_live): Likewise.
(find_post_sched_live): Likewise.
* local-alloc.c (update_equiv_regs): Likewise.
(block_alloc): Likewise.
* reload1.c (reload, reload_combine): Likewise.
* regmove.c (mark_flags_life_zones): Likewise.
* resource.c (mark_target_live_regs): Likewise.
* sched.c (schedule_block): Likewise.
* regclass.c (regset_release_memory): Don't free
basic_block_live_at_start.
* unroll.c (copy_loop_body): Don't duplicate NOTE_INSN_BASIC_BLOCK.
Thu Feb 25 21:32:34 1999 Jason Merrill <jason@yorick.cygnus.com>
* fixinc.wrap: Also handle struct queue in sys/stream.h.
* fixinc.svr4: Likewise.
* dwarf2out.c (scope_die_for): Set scope_die to comp_unit_die
rather than asserting it.
Thu Feb 25 23:33:06 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cppexp.c (left_shift, right_shift, parse_charconst, COMPARE,
cpp_parse_expr): Replace uses of long/HOST_BITS_PER_LONG with
HOST_WIDEST_INT/HOST_BITS_PER_WIDEST_INT.
* Makefile.in (cppmain.o, cpplib.o, cpphash.o, cppalloc.o,
cpperror.o, cppexp.o, cppfiles.o, cppinit.o, fix-header.o,
scan-decls.o): Don't depend on machmode.h.
* cppexp.c: Don't define CHAR_BIT or HOST_BITS_PER_WIDE_INT anymore.
Replace all instances of HOST_WIDE_INT with HOST_WIDEST_INT.
* cppfiles.c: Likewise.
* cpplib.c: Likewise.
* cpplib.h: Likewise. Also don't include machmode.h anymore.
Thu Feb 25 18:46:26 1999 Richard Henderson <rth@cygnus.com>
* gcc.c (default_compilers): Define __FAST_MATH__ when appropriate.
* objc/lang-specs.h: Likewise.
Thu Feb 25 16:19:43 1999 Jeffrey A Law (law@cygnus.com)
* pa.md (call patterns): Lose unused argument to output_call.
* print-rtl.c (print_rtl): Print /j and /c for the jump/call flags.
1999-02-25 Zack Weinberg <zack@rabi.columbia.edu>
* cpphash.c (install): Rename to cpp_install, add cpp_reader*
first argument. All callers changed.
(hashtab): Removed.
(cpp_lookup, cpp_install): Change all refs to hashtab to
pfile->hashtab.
(cpp_hash_cleanup): Removed.
* cpphash.h: Adjust prototypes.
* cpplib.h (struct cpp_reader): Add hashtab pointer.
* cppinit.c (cpp_reader_init): Also allocate space for the
hashtab.
(cpp_cleanup): Delete all macros and free the hashtab.
Thu Feb 25 21:52:54 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.h (PASS_IN_REG_P): For TARGET_HITACHI, don't pass structures
in registers.
* expr.h (PRETEND_OUTGOING_VARARGS_NAMED): Provide default definition.
* function.c (assign_parms): Honour PRETEND_OUTGOING_VARARGS_NAMED.
* calls.c (expand_call): Likewise.
* sh.c (sh_expand_prologue): For TARGET_HITACHI, don't push varargs /
stdarg arguments.
* sh.h (CPP_SPEC): Add -D__HITACHI__ for -mhitachi.
(FUNCTION_ARG): For TARGET_HITACHI, don't pass unnamed
arguments in registers.
(PRETEND_OUTGOING_VARARGS_NAMED): Define.
* va-sh.h (entire file): If __HITACHI__ is defined, use sh[123]
flavor varargs.
Thu Feb 25 14:32:40 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cse.c (dump_class): Revert last change and make the prototype
extern.
Thu Feb 25 19:13:42 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* rtl.h (insn_first_p): Don't declare.
* rtlanal.c (insn_first_p): Delete.
* loop.c (loop_insn_first_p): Faster implementation.
Thu Feb 25 10:44:35 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.h (TARGET_SWITCHES): Delete deprecated switches -m[236].
(TARGET_3, TARGET_6): Delete.
(ARM_FLAG_ARM[36]): Delete.
(CPP_CPU_ARCH_SPEC): No need to handle -m[236] any more.
(CC1_SPEC): Don't expand -m[236] into new equivalents.
(CPP_APCS_PC_SPEC): No need to handle -m[236] any more.
* arm.c (arm_override_options): Delete warnings about deprecated
options -m[236].
* arm.c (arm_finalize_pic): Build the label into the special pic
adjustment insn instead of issuing it separately.
* arm.md (pic_add_dot_plus_eight): Rework to contain the label
that is needed.
* arm.md (*zeroextractqi_compare0_scratch): Delete.
(*ne_zeroextractsi): New pattern.
Thu Feb 25 18:40:06 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* stmt.c (expand_end_loop): Grok code emitted by
expand_exit_loop_if_false.
Thu Feb 25 10:17:32 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c (return_in_memory): Float fields in unions
force a return in memory.
(load_multiple_sequence): Add comment explaining why two LDR
instructions can be better than an LDMIA instruction.
* config/arm/arm.h (TARGET_SHORT_BY_BYTES): Add comment
describing the real meaning of this option.
(FIXED_REGISTERS): Default r10 to not-fixed.
(CALL_USED_REGISTERS): Default r10 to not-call-used.
(SUBTARGET_CONDITIONAL_REGISTER_USAGE): If not defined, define
as empty.
(CONDITIONAL_REGISTER_USAGE): Fix r10 if TARGET_APCS_STACK is
true. Invoke SUBTARGET_CONDITIONAL_REGISTER_USAGE after
performing other checks.
* config/arm/arm.md (zero_extendhisi2): Undo previous change.
(extendhisi2): Undo previous change.
Also add comments describing why TARGET_SHORT_BY_BYTES can be
ignored for armv4(t) architectures.
* config/arm/riscix.h (SUBTARGET_CONDITIONAL_REGISTER_USAGE):
Define to fix r10.
* config/arm/riscix1-1.h
(SUBTARGET_CONDITIONAL_REGISTER_USAGE): Define to fix r10.
Thu Feb 25 12:09:04 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cse.c (dump_class): Make the function definition static to match
the prototype.
Wed Feb 24 17:47:28 1999 Jim Wilson <wilson@cygnus.com>
* dbxout.c (gstab.h): Use if CROSS_COMPILE.
* dwarf2out.c (add_location_or_const_value_attribute): Add big
endian correction for parms passed in regs but living on the stack.
Wed Feb 24 14:03:54 1999 Jeffrey A Law (law@cygnus.com)
* calls.c (initialize_argument_information): New function extracted
from expand_call.
(expand_call): Use initialize_argument_information. Remove variables
which are no longer used due to cleanups.
* calls.c (compute_argument_block_size): New function, extracted from
expand_calls.
(expand_calls): Use compute_argument_block_size. Delete
original_args_size, use unadjusted_args_size instead.
* calls.c (precompute_arguments): New function, extracted from
expand_call.
(expand_call): Use precompute_arguments.
* calls.c (finalize_must_preallocate): New function, extracted from
expand_call.
(expand_call): Use finalize_must_preallocate.
* calls.c (store_one_arg): Mark "variable_size" as possibly unused.
* regclass.c (record_reg_classes, case 'p'): Set classes appropriately.
An alternative always fails if it needs a pseudo and no suitable
register class can be found.
Wed Feb 24 19:47:56 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.h (loop_insn_first_p): Declare.
* loop.c (loop_insn_first_p): No longer static.
* unroll.c (iteration_info): Fix comparison to
reg_iv_type->num_elements.
Before accessing reg_biv_class, check index against
max_reg_before_loop.
Fix and enable code for giv iterators.
(loop_iterations): Compare with reg_iv_type->num_elements instead
of with max_reg_before_loop.
Wed Feb 24 19:17:11 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* unroll.c (unroll_loop): Avoid out-of-bounds index for local_regno.
Wed Feb 24 11:26:41 1999 Vladimir N. Makarov <vmakarov@cygnus.com>
* config/sparc/sparc.h (CONDITIONAL_REGISTER_USAGE): Don't use
PIC_OFFSET_TABLE_REGNUM for register allocation when -fPIC.
Tue Feb 23 16:24:19 1999 Marc Lehmann <pcg@goof.com>
* config/i386/i386.md: Fix typo.
Mon Feb 22 19:36:33 1999 Andrew Cagney <cagney@b1.cygnus.com>
* config/mips/mips.c (mips_debugger_offset): When TARGET_MIPS16 &&
frame_pointer_needed adjust frame size.
(function_prologue): Don't MIPS16 .mask GPOFFSET. Already adjusted
in .frame pseudo-op.
Frm Jim Wilson <wilson@cygnus.com>:
* mips.c (function_prologue): Adjust frame size in .frame pseudo-op
when TARGET_MIPS16 && frame_pointer_needed.
1999-02-22 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.h: Add TARGET_CPU_strongarm1100.
Add -mno-sched command line switch to disable scheduling of
instructions into the function's prologue.
(enum processor_type): Remove.
(TARGET_OPTIONS): Add "fpe=" option to match documentation.
(struct arm_cpu_select): Replace 'set_tune_p' and 'set_arch_p'
fields with 'processors' field.
(CONDITIONAL_REGISTER_USAGE): Allow r10 to be used if stack
checking is not enabled.
(RETURN_IN_MEMORY): Always call arm_return_in_memory.
* config/arm/arm.c (arm_cpu): Remove.
(tune_flags): Remove.
(arm_is_strong): New variable: true iff the target processor is a
StrongARM.
(arm_is_6_or_7): New variable: true iff the target processor is an
ARM6 or and ARM7.
(arm_select): Fields reorganised.
(struct processors): processor_type field removed.
(all_procs): Remove.
(all_cores): New array: Definitions of all known ARM cpu cores.
(all_architectures): New array: Definitions of all known ARM
architectures.
(streq): New macro.
(FL_SCHED): New processor flag: processor required load
scheduling.
(FL_STRONG): New processor flag: processor is a StrongARM.
(arm_override_options): Reorganized to make code clearer.
(use_return_insn): Test for "not (TARGET_APCS and
frame_pointer_needed)".
(arm_return_in_memory): Improve handling of structures.
* config/arm/arm.md: Remove "cpu" attribute. Replace with
"is_strongarm" and "is_arm_6_or_7" attributes.
(zero_extendhisi2): Check for TARGET_SHORT_BY_BYTES before
arm_arch4.
(extendhisi2): Check for TARGET_SHORT_BY_BYTES before arm_arch4.
* invoke.texi (ARM Options): Document -mtune= and -mfp= options.
1999-02-22 Philip Blundell <philb@gnu.org>
* config/arm/linux-gas.h (INITIALIZE_TRAMPOLINE): Replace default
definition with one including cache synchronization.
(CLEAR_INSN_CACHE): Correct syscall number and enable definition.
Move definition of inhibit_libc to...
* config/arm/xm-linux.h: ... here.
* config/arm/t-linux: Disable multilib configurations since the
only effect for most people is to cause builds to fail.
* config/arm/elf.h (ASM_FILE_START): Add .file directive.
(ASM_SPEC): Translate -mapcs-float to -mfloat for the assembler.
* config/arm/linux-elf.h (DEFAULT_VTABLE_THUNKS): Define.
(HANDLE_SYSV_PRAGMA): Likewise.
(LIB_SPEC): Copy definition from generic Linux files.
(LIBGCC_SPEC): Include -lfloat if -msoft-float was given.
(FP_DEFAULT): Set to SOFT3 on 32-bit targets.
(DWARF2_DEBUGGING_INFO): Define.
(PREFERRED_DEBUGGING_TYPE): Define as DBX_DEBUG.
Mon Feb 22 16:54:18 1999 Andrew MacLeod <amacleod@cygnus.com>
* loop.c (libcall_other_regs): Make extern.
* rtl.h (find_last_value): Add parameter to prototype.
(libcall_other_reg): Add extern declaration.
* rtlanal.c (find_last_value): Add another parameter to allow
a definition using a hardware register to be found as well.
Mon Feb 22 13:33:47 1999 Mark Mitchell <mark@markmitchell.com>
* cse.c (dump_class): New function.
(invalidate_memory): Fix typo in comment.
* function.c (temp_slot): Add an alias set field.
(assign_stack_temp): Only reuse slots if they will have the
same alias set as before.
(combine_temp_slots): Don't combine if -fstrict-aliasing;
that's unsafe.
* rtl.c (copy_rtx): Copy all the flags (in particular,
MEM_SCALAR_P).
Mon Feb 22 14:13:23 1999 Vladimir N. Makarov <vmakarov@cygnus.com>
* configure.in (i[34567]86-*-linux-gnu*,
i[34567]86-*-linux-gnulibc1, i[34567]86-*-linux-gnuaout*,
i[34567]86-*-linux-gnuoldld*): Use fixinc.x86-linux-gnu as
fixincludes.
* configure: Rebuilt.
* fixinc.x86-linux-gnu: New script for fixing asm-statements bug
on x86 linux.
* fixinc/fixinc.x86-linux-gnu: Copy of the previous one.
* fixinc/mkfixinc.sh (i[34567]86-*-linux-gnu*,
i[34567]86-*-linux-gnulibc1, i[34567]86-*-linux-gnuaout*,
i[34567]86-*-linux-gnuoldld*): Use fixinc.x86-linux-gnu as
fixincludes.
Mon Feb 22 08:55:05 1999 Ovidiu Predescu <ovidiu@cup.hp.com>
* objc/objc-act.c (encode_type): Temporary revert to the old
behavior of encoding types as the new one seems to break the
encoding of bitfields.
Mon Feb 22 11:40:44 1999 Craig Burley <craig@jcb-sc.com>
Sat Feb 20 09:59:36 1999 Craig Burley <craig@jcb-sc.com>
* Makefile.in (all.internal, all.cross): Depend on `doc'
target, to ensure docs get made before installation.
Decrease spurious warnings from -fsyntax-only:
* stmt.c (expand_expr_stmt): Expand expr even when -fsyntax-only.
Mon Feb 22 10:55:00 1999 Gavin Romig-Koch <gavin@cygnus.com>
* c-lex.c (yylex): Replace warning about integer constants being
larger than long-longs, with a warning about integer constants
being larger than the largest target integer.
Mon Feb 22 08:35:38 1999 Craig Burley <craig@jcb-sc.com>
Fix -fsyntax-only ICEs:
* varasm.c (assemble_zeros, assemble_variable,
output_constant_def): Do nothing when -fsyntax-only.
Fri Feb 19 18:18:56 1999 Don Bowman <don@pixstream.com>
* configure.in (mips*-*-vxworks*): Enable gthreads vxworks support.
* configure: Rebuilt.
Sun Feb 21 20:34:44 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Feb 21 20:35:10 1999 Jeffrey A Law (law@cygnus.com)
* config/aoutos.h (ASM_OUTPUT_CONSTRUCTOR): Delete.
(ASM_OUTPUT_DESTRUCTOR, ASM_OUTPUT_GC_ENTRY): Likewise.
* tm.texi: Update docs for constructors and destructors.
Sun Feb 21 17:11:18 1999 Richard Henderson <rth@cygnus.com>
* genattrtab.c (check_attr_value): Allow negative const_int if
negative_ok. Accept integral arithmetic operators. Accept
direct references to other attributes. Accept symbol_ref in
non-constant attributes.
(max_attr_value): Add new argument `unknownp'. Update all callers.
(or_attr_value): Likewise.
(simplify_knowing): Don't optimize if max_attr_value unknown.
(write_length_unit_log): Likewise with or_attr_value.
(find_and_mark_used_attributes): Don't fallthru case.
(write_attr_set): Pass thru all non-cond expressions.
(write_attr_value): Handle symbol_ref, attr, and arithmetic.
Sun Feb 21 13:16:44 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* regmove.c (discover_flags_reg): Use word_mode instead of SImode.
Sun Feb 21 13:15:40 1999 Richard Henderson <rth@cygnus.com>
* regmove.c (discover_flags_reg): Remove cc0 code.
(mark_flags_life_zones) [HAVE_cc0]: Force use of cc0; bail if
a potential flags register was identified.
Sat Feb 20 16:16:07 1998 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* rs6000.md (scc plus ltu): Fix typo in last change.
Sat Feb 20 09:08:44 1999 Richard Earnshaw (rearnsha@arm.com)
* xm-arm.h (HOST_BITS_PER_LONGLONG): Define.
Fri Feb 19 23:02:02 1999 Richard Henderson <rth@cygnus.com>
* regmove.c (discover_flags_reg): New function.
(flags_set_1, mark_flags_life_zones): New functions.
(regmove_optimize): Call them.
(fixup_match_1): Use insn modes rather than sets_cc0_p.
Fri Feb 19 22:47:01 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* rtlanal.c (insn_first_p): Fix return value for insn == reference.
* loop.c (strength_reduce, check_final_value, check_dbra_loop):
Use loop_insn_first_p.
Fri Feb 19 15:49:26 1999 Michael Meissner <meissner@cygnus.com>
David Edelsohn <edelsohn@gnu.org>
* rs6000.md (scc plus eq): Fix output template.
(scc plus ltu): Fix output template and collapse variants
correcting early clobbers.
(scc plus geu): Fix output template.
(scc plus gt): Fix output template.
(scc plus gtu): Fix output template and collapse variants.
Fri Feb 19 15:43:59 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cppinit.c (print_help): Remove unescaped newline in string.
Fri Feb 19 19:55:06 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Check for intervening jumps when
converting biv increment to giv.
Thu Feb 18 16:36:58 1999 Per Bothner <bothner@cygnus.com>
* tree.def (TRY_FINALLY_EXPR, GOTO_SUBROUTINE_EXPR): New tree nodes,
* expr.c (expand_expr): Support new tree nodes.
Fri Feb 19 10:17:56 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* config/m68k/m68k.c (m68k_align_loops_string,
m68k_align_jumps_string, m68k_align_funcs_string): Add const.
* config/m68k/m68k.h (m68k_align_loops_string,
m68k_align_jumps_string, m68k_align_funcs_string): Likewise.
Thu Feb 18 23:28:35 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* bitmap.c (bitmap_print): Qualify a char* with the `const' keyword.
* bitmap.h (bitmap_print): Likewise.
* c-decl.c (builtin_function, grokdeclarator, grokfield): Likewise.
* c-lang.c (build_objc_string): Likewise.
* c-lex.c (yyerror, extend_token_buffer): Likewise. Don't include
limits.h or ctype.h. Remove unused variable `p'.
* c-lex.h (yyerror): Qualify a char* with the `const' keyword.
* c-pragma.c (handle_pragma_token): Likewise.
* c-pragma.h (handle_pragma_token): Likewise.
* c-tree.h (build_objc_string, builtin_function, grokfield,
build_indirect_ref, lvalue_or_else, readonly_warning, error_init,
pedwarn_init): Likewise.
* c-typeck.c (convert_for_assignment, warn_for_assignment,
push_string, warning_init, incomplete_type_error,
build_indirect_ref, lvalue_or_else, readonly_warning,
build_c_cast, spelling, push_member_name, print_spelling,
error_init, pedwarn_init, start_init): Likewise.
* objc/objc-act.c (build_objc_string): Likewise.
* print-tree.c (print_node_brief, print_node): Likewise.
* tree.h (lvalue_or_else, print_node, print_node_brief): Likewise.
Thu Feb 18 20:44:21 1999 David Edelsohn <edelsohn@gnu.org>
* regclass.c (record_reg_classes): Correctly handle 'p' constraint.
Thu Feb 18 19:59:37 1999 Marc Espie <espie@cvs.openbsd.org>
* configure.in :Handle OpenBSD platforms.
* configure: Rebuilt.
* config/openbsd.h: New file.
* config/xm-openbsd.h: New file.
* config/t-openbsd: New file.
* config/t-openbsd-thread: New file.
Thu Feb 18 18:47:09 1999 Jeffrey A Law (law@cygnus.com)
* function.c (assign_stack_temp_for_type): Round SIZE before calling
assign_stack_local for BLKmode slots.
Fri Feb 19 01:45:06 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): For derived givs, replace the
giv this was derived from with its new_reg.
(recombine_givs): Don't set new_reg for derived giv.
And don't print it, print SUM instead.
Thu Feb 18 15:52:49 1999 Jim Wilson <wilson@cygnus.com>
* m68kelf.h (ASM_RETURN_CASE_JUMP): Add 5200 support.
1999-02-18 Zack Weinberg <zack@rabi.columbia.edu>
* cpplib.c: Kill define of STDC_VALUE. Don't include output.h
or prefix.h. Change CPP_IS_MACRO_BUFFER to not refer to
macro_cleanup.
(GET_ENV_PATH_LIST, PATH_SEPARATOR, STANDARD_INCLUDE_DIR,
predefs, SIZE_TYPE, PTRDIFF_TYPE, WCHAR_TYPE,
CPP_WCHAR_TYPE, USER_LABEL_PREFIX, REGISTER_PREFIX, struct
cpp_pending, version_string, struct default_include,
include_defaults_array, path_include, cpp_options_init,
dump_special_to_buffer, initialize_builtins, cpp_start_read,
cpp_reader_init, nreverse_pending, push_pending, print_help,
cpp_handle_option, cpp_handle_options, cpp_finish,
cpp_cleanup): Move to cppinit.c.
(macro_cleanup, struct arglist, collect_expansion,
create_definition, compare_defs, comp_def_part, ARG_BASE,
struct argdata, macarg, change_newlines, timestamp,
monthnames, special_symbol, unsafe_chars, macroexpand,
push_macro_expansion): Move to cpphash.c.
(quote_string, check_macro_name, cpp_expand_to_buffer,
output_line_command, cpp_undef): Export.
(null_underflow, null_cleanup, handle_directive): Make static.
* cpplib.h: Prototype now-exported functions. Adjust decls of
syntax tables so we can include cpplib.h in cppinit.c.
* cpphash.h: Prototype all functions exported by cpphash.c.
* cppinit.c: Make syntax tables initialized data if possible
(uses GCC designated-initializer extension).
* cppexp.c: Make cpp_lex static.
* Makefile.in: Move -D switches for the various include dirs
from cpplib.o rule to cppinit.o rule. Adjust dependencies.
Thu Feb 18 13:15:56 1999 Marc Espie <espie@cvs.openbsd.org>
* alpha/openbsd.h: New file.
* alpha/xm-openbsd.h: New file.
* sparc/openbsd.h: New file.
* sparc/xm-openbsd.h: New file.
* m68k/openbsd.h: New file.
* m68k/xm-openbsd.h: New file.
* i386/openbsd.h: New file, originally from netbsd.
* i386/xm-openbsd.h: New file.
1999-02-17 Zack Weinberg <zack@rabi.columbia.edu>
* Makefile.in: Correct dependencies for cpplib object files.
Wed Feb 17 14:04:18 1999 Michael Meissner <meissner@cygnus.com>
* rs6000.md ({add,sub}si3 `.'): Add alternatives to use CR other
than cr0.
Wed Feb 17 16:59:28 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Don't move giv insn for biv turned giv
below scan_start.
Wed Feb 17 10:56:24 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* tree.c (tree_node_kind_names, print_obstack_name,
get_identifier, maybe_get_identifier, build_string,
build_expr_wfl, is_attribute_p, lookup_attribute,
print_obstack_statistics, get_file_function_name_long, tree_check,
tree_class_check, expr_check): Qualify a char* with the `const'
keyword.
* tree.h (get_identifier, maybe_get_identifier, build_string,
build_expr_wfl, is_attribute_p, lookup_attribute,
print_obstack_statistics, print_obstack_name, tree_check,
tree_class_check, expr_check): Likewise.
Tue Feb 16 21:29:38 1999 Jeffrey A Law (law@cygnus.com)
* i386/freebsd-elf.h, i386/gas.h, i386/linux.h: Fix minor spacing
errors.
* calls.c (store_one_arg): Mark any slots used for the argument
as in-use immediately after we're done saving any slots which
will be overwritten by this argument.
Tue Feb 16 21:02:07 1999 Anton Hartl <toni@devsoft.com>
* rs6000.md (call_value): Fix typo.
Wed Feb 17 01:29:07 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Calculate maybe_dead before
calling recombine_givs.
Wed Feb 17 00:43:12 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Dump biv increment -> giv conversions.
Tue Feb 16 15:31:39 1999 Ovidiu Predescu <ovidiu@cup.hp.com>
* objc/objc-act.c (encode_type): Encode the type instead of
encoding the mode of the type (patch from Richard Frith-Macdonald
<richard@brainstorm.co.uk>).
Tue Feb 16 10:53:51 1999 Richard Earnshaw (rearnsha@arm.com)
* config/arm/arm.md (*zeroextractqi_compare0_scratch): Re-add load
instruction killed in previous change. Simplify mask generation.
(*zeroextractsi_compare0_scratch): Simplify mask generation.
Tue Feb 16 09:52:26 1999 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.md (zeroextractqi_compare0_scratch): Ensure that
bitfield does not overflow a byte boundary.
Tue Feb 16 01:37:33 1999 Charles G Waldman <cgw@alum.mit.edu>
* c-common.c (shorten_compare): Get the min/max value from the
underlying type of an enumeration, not the enumerated type itself.
Mon Feb 15 23:04:48 1999 Jeffrey A Law (law@cygnus.com)
* jump.c: Include insn-attr.h.
(delete_computation): If reload has completed and insn scheduling
after reload is enabled, then do not depend on REG_DEAD notes.
* Makefile.in (jump.o): Depend on insn-attr.h.
Mon Feb 15 16:57:38 1999 Richard Henderson <rth@cygnus.com>
* i386.md (addsi3): Allow lea for any constant_p.
1999-02-15 Zack Weinberg <zack@rabi.columbia.edu>
* toplev.c (documented_lang_options): Remove -fident and
-fnoident, which are now handled by the language independent
option parser.
1999-02-15 Zack Weinberg <zack@rabi.columbia.edu>
* c-common.c (UNGETC [USE_CPPLIB=1]): Do nothing if c is EOF.
* c-lex.c: Likewise.
* cpplib.c (cpp_push_buffer, cpp_pop_buffer): Use a linked
list in malloced memory for the buffer stack.
(cpp_get_token): Don't pop the last buffer off the stack.
Calls after CPP_EOF has been returned produce CPP_EOF with no
state change.
(cpp_finish): Pop last buffer here.
(do_line): Don't free ip->last_nominal_fname if it is equal to
ip->fname.
(special_symbol): If a T_CONST is the empty string, push a
single `@ ' escape instead.
(macroexpand): Special symbol buffers have escapes too.
* cpplib.h (struct cpp_buffer): Remove unused fields, add prev
buffer pointer.
(struct cpp_reader): Remove buffer_stack. Add
buffer_stack_depth.
(CPP_PREV_BUFFER, CPP_NULL_BUFFER): Buffer stack is now a
linked list.
Mon Feb 15 14:44:53 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c: Don't define HOST_WIDE_INT. Replace all occurrences of
WIDE_INT with WIDEST_INT.
* cexp.y: Likewise.
Don't define unsigned_HOST_WIDE_INT, CHAR_BIT or
HOST_BITS_PER_WIDE_INT. Replace occurrences of PRINTF_PROTO_1()
style with PVPROTO() ATTRIBUTE_PRINTF_1 style macros. Replace
occurrences of "unsigned_HOST" with "unsigned HOST". Provide a
definition of variable `c89' when compiling a test binary and set it.
* system.h: Don't define the PRINTF_PROTO_* macros.
Mon Feb 15 11:33:51 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (mark_loop_jump): Handle LO_SUM. If we encounter something
we do not understand, mark the loop and containing loops as invalid.
Mon Feb 15 00:40:45 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* alias.c (init_alias_analysis): Avoid self-referential value
when setting reg_known_value from REG_EQUAL notes.
Sun Feb 14 23:12:10 1999 Richard Henderson <rth@cygnus.com>
* i386.c (legitimate_address_p): Verify modes of base and index.
Sun Feb 14 23:01:28 1999 Richard Henderson <rth@cygnus.com>
* i386.c (legitimate_pic_address_disp_p): Remove static.
* i386.h (LEGITIMATE_PIC_OPERAND_P): Use it instead of
open-coding cases.
Sun Feb 14 21:03:28 1999 Jeffrey A Law (law@cygnus.com)
* except.c (start_catch_handler): Use emit_cmp_and_jump_insns.
* explow.c (probe_stack_range): Likewise.
* expmed.c (do_cmp_and_jump): Likewise.
* expr.c (store_expr, expand_expr, expand_builtin): Likewise.
(do_tablejump): Likewise.
* stmt.c (expand_expr_stmt, expand_end_case): Likewise.
(do_jump_if_equal, emit_case_nodes): Likewise.
* optabs.c (emit_cmp_and_jump_insns): Clarify comments. If UNSIGNEDP,
then convert comparison to an unsigned code before emitting the jump.
(expand_float, expand_fix): Use emit_cmp_and_jump_insns.
Sun Feb 14 02:24:15 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Feb 14 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Feb 14 00:45:50 1999 Jeffrey A Law (law@cygnus.com)
* loop.c: Disable recent loop changes. Temporary as Joern
continues to fix problems.
Sat Feb 13 23:29:42 1999 Richard Henderson <rth@cygnus.com>
* loop.c (combine_givs_used_by_other): Delete.
(combine_givs_benefit_from): Delete.
(combine_givs): Deny combination of givs only used once. Simplify
code with the death of combine_givs_benefit_from.
Sun Feb 14 11:24:05 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.c (scan_loop): Call reg_in_basic_block_p before
loop_reg_used_before_p.
Sat Feb 13 05:32:00 1999 Richard Earnshaw (rearnsha@arm.com)
* arm.md: Use gen_rtx_FOO instead of gen_rtx (FOO, ...).
* arm.h: Likewise.
* arm.c: Likewise.
* arm.h (TARGET_OPTIONS): Reformat for clarity.
(GO_IF_LEGITIMATE_ADDRESS): When generating PIC, references to symbols
in the constant pool aren't valid.
(LEGITIMATE_PIC_OPERAND_P): Likewise.
* arm.c: Include "system.h", not stdio.h and string.h.
Fri Feb 12 13:06:28 1999 Jim Wilson <wilson@cygnus.com>
* stmt.c (expand_return): Return if optimize_tail_recursion succeeded.
(optimize_tail_recursion): Change return type from void to int.
Add return statements.
* tree.h (optimize_tail_recursion): Change prototype to match.
Fri Feb 12 21:09:51 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload.c (find_reloads_subreg_address): New function, broken out of
find_reloads_toplev.
(find_reloads_toplev, find_reloads_address_1): Use it.
Fri Feb 12 13:20:52 1999 Jeffrey A Law (law@cygnus.com)
* h8300.md (zero_extendhisi2 H8/300 variant): Correctly handle
extending a CONST_INT.
* h8300.md (peephole for combining memrefs): Delete incorrect peephole.
Fri Feb 12 18:29:11 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (loop_insn_first_p, biv_elimination_giv_has_0_offset):
New functions.
(maybe_eliminate_biv_1): Use biv_elimination_giv_has_0_offset.
Fri Feb 12 16:56:10 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (load_mems): Don't guess how to do a load / store, use
emit_move_insn.
Fri Feb 12 09:24:26 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* system.h: Provide a definition for HOST_WIDEST_INT, etc.
Fri Feb 12 23:37:26 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_address_cost): Revert 9 Feb change.
Fri Feb 12 00:51:26 1999 Jeffrey A Law (law@cygnus.com)
* reload.c (find_reloads_address_1): Fix handling of an autoincremented
pseudo which is homed in the stack.
* mips.c (save_restore_insns): Fix loop to save/restore FP registers.
(compute_frame_size): Change loop over FP regs to be consistent
with the loop in save_restore_insns.
Thu Feb 11 17:38:40 1999 Jim Wilson <wilson@cygnus.com>
* i960/i960.h (OVERRIDE_OPTIONS): Warn if -mlong-double-64 is used.
(LONG_DOUBLE_TYPE_SIZE): Undef then unconditionally define to 96.
Thu Feb 11 15:11:35 1999 Jeffrey A Law (law@cygnus.com)
* mn10200.md (bset); Re-enable.
Thu Feb 11 15:20:49 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (is_sfunc): New attribute.
* sh.h (INSN_SETS_ARE_DELAYED, INSN_REFERENCES_ARE_DELAYED): Use it.
Thu Feb 11 01:06:49 1999 Nathan Sidwell <nathan@acm.org>
* fold-const.c (range_binop): Take account of the bounded nature
of fixed length arithmetic when comparing unbounded ranges.
Thu Feb 11 00:08:17 1999 John Wehle (john@feith.com)
* function.c (assign_stack_temp_for_type): Clear best_p
when an exact match is found.
* i386.h (LOCAL_ALIGNMENT): Define.
* function.c (assign_stack_local, assign_outer_stack_local): Use it.
(assign_stack_temp_for_type): New function based on assign_stack_temp.
(assign_stack_temp): Call it.
(assign_temp): Use assign_stack_temp_for_type, not assign_stack_temp.
* stmt.c: Use assign_temp, not assign_stack_temp.
* tm.texi: Document LOCAL_ALIGNMENT.
Wed Feb 10 23:28:28 1999 Jeffrey A Law (law@cygnus.com)
* reorg.c: Finish deleting half-deleted comment.
Wed Feb 10 17:12:21 1999 Jim Wilson <wilson@cygnus.com>
* emit-rtl.c (operand_subword): Sign extend REAL_VALUE_TO_TARGET_SINGLE
result.
* final.c (split_double): Sign extend REAL_VALUE_TO_TARGET_DOUBLE
result.
* real.c (endian): Delete sign extension code.
* config/m32r/m32r.md (movsf_insn+1): REAL_VALUE_TO_TARGET_SINGLE call
replaced with operand_subword call.
Wed Feb 10 15:16:39 1999 Richard Henderson <rth@cygnus.com>
* alpha.md (cmov compound patterns): Delete. Jump can now
create the correct constructs in the first place.
Wed Feb 10 11:03:22 1999 Richard Henderson <rth@cygnus.com>
* configure.in (alphaev6*): Fix typo in target_cpu_default2.
Wed Feb 10 13:59:18 1999 Dave Brolley <brolley@cygnus.com>
* mbchar.c (local_mb_cur_max): Handle the case where MB_CUR_MAX is 0.
Wed Feb 10 10:35:05 1999 Jim Wilson <wilson@cygnus.com>
* tmp-emsgids.c: Delete.
Wed Feb 10 09:57:08 1999 Mark Mitchell <mark@markmitchell.com>
* rtlanal.c (for_each_rtx): Fix declaration to conform to GNU
coding standards.
Wed Feb 10 10:09:41 1999 Jeffrey A Law (law@cygnus.com)
* mn10200.md (bset, bclr): Operand 0 is a read/write operand.
* reload1.c (reload_combine_note_store): Second argument is no
longer unused/ignored. Handle multi-register hard regs.
(move2add_note_store): Simplify.
Wed Feb 10 10:05:23 1999 Mumit Khan <khan@xraylith.wisc.edu>
* collect2.c (collect_execute): Remove cygwin-specific code.
Tue Feb 9 17:27:29 1999 Nathan Sidwell <nathan@acm.org>
* system.h (_, N_): Remove dummy i18n macros.
* protoize.c: Move inclusion of intl.h to after system.h.
* cexp.y: Include intl.h.
* cexp.c: Rebuilt.
Tue Feb 9 16:52:22 1999 Mumit Khan <khan@xraylith.wisc.edu>
* i386/cygwin.h (SUBTARGET_OVERRIDE_OPTIONS): New macro to ignore
fpic/fPIC for windows32 targets.
* i386/xm-cygwin.h (GET_ENV_PATH_LIST): Replace '\\' in windows32
paths with '/'.
* i386/mingw32.h (CPP_SPEC): Define.
(CPP_PREDEFINES): Add MINGW32 version id.
* i386/crtdll.h (CPP_PREDEFINES): Likewise.
* Makefile.in (collect2$(exeext)): Delete redundant dependency and
add missing exeext to target.
* gcc.c (convert_filename): Handle null filename argument.
Wed Feb 10 15:46:10 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (*movhf_noclobber, *movhi_noclobber): Use
m constraint instead of QT.
1999-02-09 Brendan Kehoe <brendan@cygnus.com>
* cpplib.c (special_symbol): Move IP to be declared in function
scope, rather than individual case statements.
1999-02-09 Zack Weinberg <zack@rabi.columbia.edu>
* cppfiles.c (finclude): Handle pipes properly under old BSD
derivatives.
1999-02-09 Melissa O'Neill <oneill@cs.sfu.ca>
* system.h: Provide fallback definitions for S_ISCHR,
S_ISSOCK, S_ISFIFO, O_NONBLOCK, and O_NOCTTY.
1999-02-09 Zack Weinberg <zack@rabi.columbia.edu>
* cpplib.c (do_define): Allow redefining __STDC__ with -D.
1999-02-09 Jim Blandy <jimb@zwingli.cygnus.com>
* configure.in: For PowerPC configurations, accept "401", "ec603e",
"740", and "750" as valid arguments to --with-cpu.
* configure: Rebuilt.
Tue Feb 9 00:00:14 1999 Mark Kettenis <kettenis@gnu.org>
* configure.in (i[34567]86-*gnu*): Set float_format to i386.
* configure: Rebuilt.
Mon Feb 8 22:38:24 1999 Jeffrey A Law (law@cygnus.com)
* rs6000.md: Revert "alternate use of crs if cr0 not available"
patches from 01-22-1999, 01-24-1999, 01-26-1999, and 02-08-1999.
Mon Feb 8 21:36:44 1999 Richard Henderson <rth@cygnus.com>
* output.h (current_function_has_computed_jump): Rename from
current_function_addresses_labels.
* function.h (struct function): Likewise for addresses_labels member.
* rtl.h (FUNCTION_FLAGS_HAS_COMPUTED_JUMP): Likewise.
* function.c (current_function_has_computed_jump): Likewise.
Update all references.
* integrate.c (function_cannot_inline_p):
Test current_function_has_computed_jump instead of addresses_labels.
(initialize_for_inline): Likewise save.
(output_inline_function): Likewise restore.
* expr.c (expand_expr): Don't reference addresses_labels variables.
* stmt.c (expand_computed_goto): Set has_computed_jump.
1999-02-08 Michael Meissner <meissner@cygnus.com>
This is being installed only to get it into the repository to help
with the revert, resubmit & review process for the massive rs6000.md
changes.
* rs6000.md (andsi3_internal1 splitter): Don't split if using the
rlwinm instruction.
(anddi3_internal1): Likewise.
(andsi3_internal{2,3}): Correct some insn lengths.
(anddi3*): Restore missing TARGET_POWERPC64, and don't emit old
mnemonics.
Mon Feb 8 21:31:06 1999 Richard Henderson <rth@cygnus.com>
* loop.c (reg_single_usage): New file-scope variable ...
(scan_loop): ... moved out of here. Always initialize.
Test loop_has_call instead of reg_single_usage not zero.
Free reg_single_usage after strength reduction.
(count_loop_regs_set): Assume single_usage non-zero.
(combine_givs_used_by_other): Test reg_single_usage.
(load_mems_and_recount_loop_regs_set): Remove reg_single_usage
as a parameter. Assume non-zero.
1999-02-08 Zack Weinberg <zack@midnite.ec.rhno.columbia.edu>
* cpplib.c (special_symbol): Rewrite. Don't copy things
multiple times. Handle __STDC__ specially. T_CONST
indicates a constant /string/. Don't handle T_*_TYPE and
T_SPEC_DEFINED. Use cpp_buf_line_and_col instead of
adjust_position. Determine the file buffer only if needed.
(initialize_builtins): Handle __SIZE_TYPE__,
__PTRDIFF_TYPE__, __WCHAR_TYPE__, __USER_LABEL_PREFIX__, and
__REGISTER_PREFIX__ with T_CONST special hashtab entries.
Don't provide __OBJC__; the driver does that. Provide
__STDC_VERSION__, using T_CONST. Use T_STDC for
__STDC__. Give install the length of all symbols defined.
(eval_if_expression): Drop code to insert and remove the
"defined" special symbol.
* cpplib.h: Remove SELF_DIR_DUMMY (no longer used). Remove
T_*_TYPE and T_SPEC_DEFINED from enum node_type; add T_STDC.
* cpphash.c (install): Drop the `ivalue' parameter. Constify
the `value' parameter. All callers changed.
* cpphash.h (install): Change prototype to match.
(union hashval): Remove `ival' member.
* cppexp.c (cpp_lex): Handle `defined' here.
Mon Feb 8 17:29:42 1999 Jeffrey A Law (law@cygnus.com)
* pa.h (EXTRA_CONSTRAINT): Fix comment.
Mon Feb 8 18:57:45 1999 Vladimir N. Makarov <vmakarov@cygnus.com>
* c-typeck.c (check_init_type_bitfields): Use nonincremental
initialization of unions whose first member is a bitfield.
Remove unnecessary code for checking the declaration mode
after DECL_C_BIT_FIELD.
* varasm.c (output_constructor): Additional comment about the
constructor of bitfield union initialization.
Tue Feb 9 11:55:04 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (*movhi_stik): New pattern.
(movhi): Allow some immediate constants to be directly
stored in memory.
Tue Feb 9 11:34:15 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (all call patterns): Add constraints "Ur".
(call, call_value): Force address into a register if not valid
for a call instruction.
(load_immed_address): Emit a USE of the SYMBOL_REF that is
forced into memory.
* config/c4x/c4x.c (c4x_print_operand): Fix 'C' and 'U' modifiers.
Tue Feb 9 11:08:41 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (call_address_operand, symbolic_address_operand):
Rename from call_operand and symbolic_operand respectively. All
callers changed.
* config/c4x/c4x.md (call_address_operand, symbolic_address_operand):
Likewise.
* config/c4x/c4x.h (call_address_operand, symbolic_address_operand):
Likewise.
(PREDICATE_CODES): Allow CONST, LABEL_REF for call_address_operand.
Tue Feb 9 10:52:27 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_legitimize_address): Don't generate a
LO_SUM address for HImode or HFmode but instead force address into
a register so that it is offsettable.
(c4x_emit_move_sequence): Handle LO_SUM immediate address.
Tue Feb 9 10:46:42 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_address_cost): Return cost of 1 for
REG+REG addressing if strength reduction enabled.
Tue Feb 9 10:10:31 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/t-c4x (LIBGCC2_CFLAGS): Delete.
(TARGET_LIBGCC2_CFLAGS): Define.
1999-02-08 Nick Clifton <nickc@cygnus.com>
* config/v850/v850.md: Replace \\n\\t with \\;
* config/v850/v850.md: Enforce TARGET_LONG_CALLS option.
* config/v850/v850.c (construct_restore_jr, construct_save_jarl):
Enforce TARGET_LONG_CALLS option.
Mon Feb 8 11:43:07 1999 Donn Terry <donn@interix.com>
* real.c (PUT_REAL) [XFmode]: Zero the balance of the structure.
Mon Feb 8 11:37:24 1999 Marc Espie (espie@cvs.openbsd.org)
* m88k/t-luna-gas: Remove bash dependency.
Mon Feb 8 11:34:44 1999 Graham <grahams@rcp.co.uk>
* collect2.c (xrealloc): Fix typo in last change.
Mon Feb 8 09:13:38 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Feb 7 22:18:42 1999 Robert Lipe <robertlipe@usa.net>
* tree.h (TYPE_CHECK): Make it clear to the preprocessor
that we do not want macro replacement within a character constant.
(TYPE_CHECK1): Likewise.
Sun Feb 7 15:37:10 1999 Jason Merrill <jason@yorick.cygnus.com>
* tree.h (DECL_P): New macro.
Sun Feb 7 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Feb 6 18:14:46 1999 Jeffrey A Law (law@cygnus.com)
* mn10300.md (reload_insi): Do not earlyclobber the output operand.
* README.g77, gcc.c, gcc.texi: Update email addresses.
* invoke.texi system.h: Likewise.
Sat Feb 6 11:04:08 1999 Jim Wilson <wilson@cygnus.com>
* unroll.c (find_splittable_givs): After express_from, call replace_rtx
to convert dest_reg to new_reg.
Sat Feb 6 10:31:35 1999 Jeffrey A Law (law@cygnus.com)
* reload1.c (reload_combine_note_store): Be more careful with
STRICT_LOW_PART, ZERO_EXTRACT and SIGN_EXTRACT.
(move2add_note_store): Likewise.
Sat Feb 6 10:18:01 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cppfiles.c (read_and_prescan): Cast the result of `xrealloc' to
U_CHAR* when assigning to one. Ensure the values of a ?: operator
have the same type.
* cppinit.c (initialize_char_syntax): Use K&R function definition.
Sat Feb 6 11:17:03 1999 Richard Earnshaw <rearnsha@arm.com>
Support for ARM9
* config/arm/arm.c (all_procs): Add arm9 and arm9tdmi.
* config/arm/arm.h ((TARGET_CPU_arm9, TARGET_CPUD_arm9tdmi): Define.
(TARGET_CPU_DEFAULT): Rework to support ARM9.
(CPP_CPU_ARCH_SPEC): Likewise.
(enum processor_type): Likewise.
* config/arm/arm.md (attr cpu): Add arm9.
General scheduling changes
* config/arm/arm.c (MAX_INSNS_SKIPPED): Delete.
(max_insns_skipped): New variable.
(arm_override_options): If generating hard floating point code for
the FPA, emit code for version 3.
When optimizing for space, don't synthesize constants.
Reword several flags based on the requested processor and optimization
level.
(use_return_insn): New argument iscond, all callers changed. Don't
use a return insn if it will be conditional and that would be
expensive; eg on StrongARM.
(arm_adjust_cost): Anti- and output- dependencies normally have no
cost.
(load_multiple_sequence): Newer ARMs don't benefit from ldm if
the sequence is short.
(final_prescan_insn): Use max_insns_skipped instead of
MAX_INSNS_SKIPPED. Note whether we will make a return instruction
conditional, and aviod this if it would be expensive.
* config/arm/arm.md (scheduling attributes and function units):
Rewrite to better describe ARM8, 9 and StrongARM.
* config/arm/arm.md (*movhi_insn_littleend): Make op0 predicate
s_register_operand.
(*ifcompare_plus_move): Use arm_rhs_operand in place of
arm_rhsm_operand. Rework constraints.
(*if_plus_move): Likewise.
(*ifcompare_move_plus): Likewise.
(*if_move_plus): Likewise.
(*ifcompre_arith_move): Likewise.
(*if_arith_move): Likewise.
(*ifcompare_move_arith): Likewise.
(*if_move_arith): Likewise.
* config/arm/xm-netbsd.h: Don't include arm/xm-arm.h.
1999-02-05 Michael Meissner <meissner@cygnus.com>
* loop.c (check_dbra_loop): A store using an address giv for which
we have no life information is not reversible.
Fri Feb 5 17:08:01 1999 Dave Brolley <brolley@cygnus.com>
* function.c (fixup_var_refs): Scan catch_clauses too.
Fri Feb 5 11:49:49 1999 Benjamin Kosnik <bkoz@loony.cygnus.com>
* c-common.c (decl_attributes): Fix reserved space for init_priority.
* tree.h (MAX_RESERVED_INIT_PRIORITY): New macro.
Fri Feb 5 12:37:05 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (strength_reduce): Clear not_every_iteration when
passing the NOTE_INSN_LOOP_CONT note.
* haifa-sched.c (add_dependence): Do not add a dependency on a
note.
Fri Feb 5 10:55:43 1999 Nick Clifton <nickc@cygnus.com>
* recog.c (split_block_insns): Only call update_flow_info if
instruction scheduling is enabled.
1999-02-05 Zack Weinberg <zack@rabi.columbia.edu>
* Makefile.in (gen-protos): Use libcpp.a like everyone else.
Fri Feb 5 07:09:29 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (first_loop_store_insn): New file-scope variable.
(prescan_loop): Set it.
(check_dbra_loop): Check if a store depends on a register
that is set after the store.
Fri Feb 5 06:55:15 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* unroll.c (entire file): Remove tabs / spaces at end of lines.
Replace spaces with tabs where appropriate.
Thu Feb 4 15:12:41 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (scan_loop): New argument loop_cont. Changed caller.
(strength_reduce): New argument loop_cont. Changed caller.
Before clearing not_every_iteration after a label, check if
we are not already past LOOP_CONT.
1999-02-04 Zack Weinberg <zack@rabi.columbia.edu>
* cpperror.c (cpp_print_containing_files): Fix formatting
bug induced by merge.
1999-02-04 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (initialize_char_syntax): Move to cppinit.c.
(cpp_define): Remove redundant syntax checks.
(make_assertion): Rename cpp_assert, remove redundant syntax
checks, export.
(cpp_options_init): Don't init things to zero twice.
(cpp_expand_to_buffer): Use memcpy, not a char-by-char loop.
(do_include): Kill excessively verbose #import warning that
snuck back in in the gcc2 merge.
(convert_string): Removed.
(do_line): Rewrite with simple last-name-used cache instead of
private hashtable.
(cpp_start_read): Call initialize_char_syntax here, not...
(cpp_reader_init): ...here.
(cpp_handle_options): Support the -std switch.
* cpplib.h (cpp_buffer): Add last_nominal_fname member.
(cpp_options): Add c9x flag.
Declare all the is_* tables and trigraph table here, as const.
Prototype cpp_assert and initialize_char_syntax.
* cppinit.c: New file.
* cppfiles.c (read_and_prescan): Optimize.
* Makefile.in (LIBCPP_OBJS): Add cppinit.o.
Thu Feb 4 10:46:30 1999 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/mips.md ([u]divmodsi4,[u]divmoddi4,[u]divsi3,[u]divdi3,
[u]modsi3,[u]moddi3) : Don't copy the "zero" argument to a register
before calling gen_div_trap.
Wed Feb 3 21:56:27 1999 Jeffrey A Law (law@cygnus.com)
* configure.in (hppa1.1-*-*, hppa2*-*): Use symbolic value rather
than numeric value for target_cpu_default..
* configure: Rebuilt.
Wed Feb 3 21:55:56 1999 Marc Espie <Marc.Espie@liafa.jussieu.fr>
* Makefile.in (xgcc$(exeext)): Remove choose-temp, pexecute and
mkstemp. Get them from libiberty.
(COLLECT2_OBJS): Similarly for choose-temp, cplus-dem and mkstemp.
(PROTO_OBJS): Similarly for choose-temp, getopt, getopt1 and pexecute.
(cplus-dem.o, pexecute.o, choose-temp.o): Remove build rules.
(mkstemp.o, getopt1.o, getopt.o): Likewise.
* pa-gas.h (TARGET_DEFAULT): Use symbolic values rather than numeric
values.
* pa-hpux.h (LINK_SPEC): Likewise.
* pa-hpux10.h (LINK_SPEC): Likewise.
* pa-hpux9.h (LINK_SPEC): Likewise.
* pa-osf.h (LINK_SPEC): Likewise.
* pa-pro.h (TARGET_DEFAULT): Likewise.
* pa1.h (TARGET_DEFAULT): Likewise.
* pa.h (MASK_*): New defines.
(TARGET_*): Use symbolic values rather than numeric values.
(TARGET_SWITCHES): Likewise.
(TARGET_DEFAULT): Likewise.
(CPP_SPEC): Likewise.
Wed Feb 3 21:07:38 1999 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload1.c (reload_cse_regs_1): Undo Jan 16 patch.
* reload.c (find_reusable_reload): New function, broken out of
push_reload. Add code to verify that none of the involved
outputs are subject to earlyclobbers.
(push_reload): Break out new function find_reusable_reload.
Delete "register" keyword for IN, OUT args.
Wed Feb 3 15:51:04 1999 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/mips.c (true_reg_or_0_operand) : New function.
* config/mips/mips.h (PREDICATE_CODES): Add true_reg_or_0_operand.
* config/mips/mips.md (div_trap,div_trap_normal,div_trap_mips16):
Use true_reg_or_0_operand for div_trap.
Wed Feb 3 20:44:59 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.h (express_from): Declare.
(struct induction): Replace derived flag with derived_from pointer.
* loop.c (strength_reduce, record_giv, recombine_givs): Likewise.
(express_from): No longer static.
* unroll.c (find_splittable_givs): Replace derived with derived_from.
When processing an address giv with which another giv has been
combined that has also been derived from a third giv, handle like
having combined with the third giv.
Set splittable_regs_updates appropriately for derived givs.
Wed Feb 3 15:26:58 1999 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/mips.md (div_trap_mips16): Remove nop's after branches.
Wed Feb 3 11:56:23 1999 Jeffrey A Law (law@cygnus.com)
* pa.c (insn_sets_and_refs_are_delayed): New function.
* pa.h (INSN_SETS_ARE_DELAYED): Use it.
(INSN_REFERENCES_ARE_DELAYED): Likewise.
Wed Feb 3 06:24:49 1999 Richard Earnshaw (rearnsha@arm.com)
* config/arm/t-arm-elf (LIBGCC2_CFLAGS): Delete.
* config/arm/t-linux (LIBGCC2_CFLAGS): Delete.
(TARGET_LIBGCC2_CFLAGS): Define.
(LIBGCC2_DEBUG_CFLAGS): Define.
* config/arm/t-netbsd: Likewise.
* config/arm/t-semi: Likewise.
* config/arm/t-semiaof: Likewise.
* config/arm/t-riscix: Likewise.
Wed Feb 3 10:59:07 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* config/m68k/m68k.c (print_operand_address): When printing a
SYMBOL_REF that ends in `.<letter>' put parentheses around it.
Tue Feb 2 23:38:35 1999 David O'Brien <obrien@FreeBSD.org>
* i386/freebsd*.h now allows '$' in label names and does not use the
PCC struct return method.
Tue Feb 2 22:38:23 1999 Jim Wilson <wilson@cygnus.com>
* Makefile.in: Change all uses of AR to AR_FOR_TARGET. Change all uses
of HOST_AR to AR. Likewise for AR_FLAGS, RANLIB, and RANLIB_TEST.
(RANLIB_TEST): Test to see if ranlib exists. Only test absolute file
names if host == target.
(HOST_AR, HOST_AR_FLAGS, HOST_RANLIB, HOST_RANLIB_TEST): Delete.
(AR_FLAGS_FOR_TARGET): Renamed from AR_FOR_TARGET_FLAGS.
(AR, AR_FLAGS, OLDAR, OLDAR_FLAGS, RANLIB, RANLIB_TEST): Delete rules
setting them to *_FOR_TARGET.
* cross-make (AR, AR_FLAGS, OLDAR, OLDAR_FLAGS, RANLIB, RANLIB_TEST):
Delete.
Tue Feb 2 22:38:19 1999 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* toplev.h (read_integral_parameter): Declare.
* toplev.c (read_integral_parameter): New function.
Fri Jan 29 21:00:56 1999 Bob Manson <manson@charmed.cygnus.com>
* resource.c, resource.h: New files.
* Makefile.in (OBJS): Add it.
* haifa-sched.c (regno_use_in): Moved to rtlanal.c.
(split_block_insns): Moved to recog.c.
(update_flow_info): Make public.
* rtl.h: Declare them.
* reorg.c: Moved the functions dealing with computing resource
usage to resource.c.
* sched.c (regno_use_in): Moved to rtlanal.c.
(update_flow_info): Make public.
(schedule_insns): Use split_block_insns.
* recog.c (split_block_insns): New function.
Tue Feb 2 22:03:26 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000/linux.h (LINK_START_DEFAULT_SPEC): Delete, unused.
(LINK_OS_DEFAULT_SPEC): Delete, unused.
Tue Feb 2 20:29:34 1999 Catherine Moore <clm@cygnus.com>
* configure.in (arm-*-oabi): Support.
* configure: Regenerate.
* config/arm/unknown-elf-oabi.h: New file.
Tue Feb 2 19:43:59 1999 Jeffrey A Law (law@cygnus.com)
* i386.md (ashlsi3): Turn into a define_expand an anonymous pattern.
Make the anonymous pattern match when ! optimize_size.
(ashlsi3 size optimizer): New pattern.
* intl/Makefile.in (uninstall): Add missing "; \".
Tue Feb 2 18:21:23 1999 Stan Cox <scox@cygnus.com>
* sparc.h (TARGET_CPU_sparc86x): Added. TARGET_CPU_sparclite86x
synonym.
Tue Feb 2 20:24:11 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (loop_optimize): Fix value max_uid_for_loop is reset
to after find_and_verify_loops call.
Tue Feb 2 19:48:29 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* (recombine_givs): Don't use a giv that's likely to be dead to
derive others.
* loop.c (recombine_givs): Fix test for lifetime overlaps / loop
wrap around when deriving givs.
Mon Feb 1 20:00:40 1999 Richard Henderson <rth@cygnus.com>
* recog.c (check_asm_operands): Treat indeterminate operand ok
results as success. Try harder to resolve a matching constraint.
* stmt.c (expand_asm_operands): Recognize when an output operand's
constraint does not allow memory. Treat indeterminate operand ok
results as failure. Try harder to resolve a matching constraint.
Mon Feb 1 15:00:02 1999 Ken Raeburn <raeburn@cygnus.com>
Use varrays for constant-equivalence data:
* varray.h (struct const_equiv_data): New type.
(union varray_data_tag): New element const_equiv.
(VARRAY_CONST_EQUIV_INIT, VARRAY_CONST_EQUIV): New macros.
(VARRAY_SIZE): New macro, returns number of elements.
* integrate.h: Include varray.h.
(struct inline_remap): Replace const_equiv_map, const_age_map and
const_equiv_map_size with a const_equiv_varray element.
(MAYBE_EXTEND_CONST_EQUIV_VARRAY): New macro; grows varray if
needed.
(SET_CONST_EQUIV_DATA): New macro; sets rtx and age fields
simultaneously, growing the varray if needed.
* integrate.c (global_const_equiv_map,
global_const_equiv_map_size): Deleted, replaced by....
(global_const_equiv_varray): New variable.
(expand_inline_function): References changed.
* integrate.h: Update declarations.
* integrate.c (process_reg_parm, expand_inline_function,
copy_rtx_and_substitute, try_constants, subst_constants,
mark_stores): Use varray allocation and accessor macros, new
integrate.h macros, and global_const_equiv_varray. Don't
conditionalize non-NULL stores on array size; instead, expand the
array as needed.
* unroll.c (unroll_loop): Likewise.
* unroll.c (unroll_loop): Initialize const_equiv_varray element to
zero. After allocating varray, always exit through bottom of
function, where it can be deallocated if needed. Don't explicitly
reallocate const_equiv_map storage; instead, just ensure the
varray has been initialized, and update the global reference.
Mon Feb 1 09:40:25 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* system.h (inline, const): Handle these for stage2 (and later) gcc.
* dwarf2out.c (inline): Don't define.
* dwarfout.c (inline): Likewise.
Sun Jan 31 22:04:37 1999 Richard Henderson <rth@cygnus.com>
* loop.c (recombine_givs): Dump recombination and derivation data.
Sun Jan 31 20:34:29 1999 Zack Weinberg <zack@rabi.columbia.edu>
* flags.h: Declare flag_no_ident.
* toplev.c: Define flag_no_ident. Process -f(no-)ident here.
* c-tree.h: Don't declare flag_no_ident.
* c-decl.c: Don't define flag_no_ident. Don't process
-f(no-)ident switches here.
* config/elfos.h (ASM_FILE_END): Output final .ident directive
only if !flag_no_ident.
* config/ptx4.h: Likewise.
* config/svr4.h: Likewise.
* config/alpha/elf.h: Likewise.
* config/arm/linux-elf.h: Likewise.
* config/i386/sco5.h: Likewise.
* config/i860/fx2800.h: Likewise.
* config/mips/gnu.h: Likewise.
* config/i386/osfrose.h: Likewise.
* gcc.c (C specs): Map -Qn to -fno-ident.
* objc/lang-specs.h: Likewise.
Mon Feb 1 10:52:07 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* configure.in: Don't remove loop.o and unroll.o when
enable-haifa is selected.
* configure: Rebuilt.
Sun Jan 31 13:22:02 1999 John Wehle (john@feith.com)
* i386.md (movsicc, movhicc, movsfcc, movdfcc,
movxfcc, movdicc): Delete unconstrained alternatives.
* i386.c (output_fp_conditional_move,
output_int_conditional_move): Delete unused case.
Sun Jan 31 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Jan 31 00:52:37 1999 Richard Henderson <rth@cygnus.com>
* alpha.md (mov patterns): Emit the assembler aliases mov and fmov
instead of bis and cpys. Combine alternatives where possible.
Sat Jan 30 23:14:13 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gcov.c (fnotice): Add missing FILE* parameter.
(function_summary): Fix format specifiers in calls to `fnotice'.
(output_data): Likewise.
* toplev.c (fnotice): Constify char* parameter.
* toplev.h (fnotice): Add prototype.
Wrap prototype with BUFSIZ to protect FILE* usage.
Sun Jan 31 15:33:09 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (RTX_COSTS): Explicitly define c4x costs.
Sat Jan 30 08:27:23 1999 Jeffrey A Law (law@cygnus.com)
* combine.c (distribute_notes): Handle REG_EH_REGION notes.
* alias.c (fixed_scalar_and_varying_struct_p): Add "static" to
function definition.
(aliases_everything_p, write_dependence_p):Likewise.
* install.texi: Fix merge lossages.
* cccp.c (main): Only call setlocale (LC_MESSAGES, ...) if LC_MESSAGES
is defined.
* collect2.c (main): Likewise.
* cppmain.c (main): Likewise.
* gcc.c (main): Likewise.
* gcov.c (main): Likewise.
* protoize.c (main): Likewise.
* toplev.c (main): Likewise.
* pa.md (parallel shift and shiftadd): Mark output of shift as an
earlyclobber.
* loop.c: Disable recent loop changes. Temporary as Joern
continues to fix problems.
Sat Jan 30 03:24:37 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Size reg_map according to reg_iv_type.
Fri Jan 29 18:26:07 1999 Dave Brolley <brolley@cygnus.com>
* emit-rtl.c (remove_insn): New function.
* rtl.h (remove_insn): Add prototype.
* function.c (reposition_prologue_and_epilogue_notes): Call remove_insn.
Fri Jan 29 22:34:41 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (recombine_givs): Don't try to derive givs that have combined.
Fri Jan 29 15:00:39 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* toplev.c (notice, fnotice): Check ANSI_PROTOTYPES, not __STDC__,
when declaring arguments and calling va_arg() to initialize them.
* collect2.c (notice): Likewise.
* loop.c (find_life_end): Use PROTO() macro in the prototype.
Fri Jan 29 14:36:11 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* collect2.c (error): Fix typo in declaration.
* cpperror.c (cpp_message): Likewise.
* cpplib.c (cpp_warning): Likewise.
* cpplib.h (cpp_notice): Use PVPROTO not VPROTO, also add
ATTRIBUTE_PRINTF_1.
* toplev.c (error): Fix typo in declaration.
Fri Jan 29 15:44:13 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Fix HAVE_cc0 handling when scanning
forward from cont dominator.
Fri Jan 29 07:10:27 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c (eprint_string): Constify a char*.
(notice): Likewise. Use PVPROTO not VPROTO, add ATTRIBUTE_PRINTF_1.
(vnotice): Constify a char*.
(error): Likewise. Use PVPROTO not VPROTO, add ATTRIBUTE_PRINTF_1.
(verror): Constify a char*.
(warning): Likewise. Use PVPROTO not VPROTO, add ATTRIBUTE_PRINTF_1.
(vwarning): Constify a char*.
(error_with_line): Likewise. Use PVPROTO not VPROTO, add
ATTRIBUTE_PRINTF_2.
(verror_with_line): Constify a char*.
(vwarning_with_line): Likewise.
(warning_with_line): Likewise. Use PVPROTO not VPROTO, add
ATTRIBUTE_PRINTF_2.
(pedwarn): Constify a char*. Use PVPROTO not VPROTO, add
ATTRIBUTE_PRINTF_1.
(pedwarn_with_line): Likewise with ATTRIBUTE_PRINTF_2.
(pedwarn_with_file_and_line): Likewise with ATTRIBUTE_PRINTF_4.
Also correct typo in parameter name declaration.
(make_assertion): Constify a char*.
(quote_string_for_make): Likewise.
(deps_output): Likewise.
(fatal): Likewise. Use PVPROTO not VPROTO, add
ATTRIBUTE_PRINTF_1. Use ATTRIBUTE_NORETURN not an explicit
"__attribute__ ((noreturn))".
(fancy_abort): Likewise for ATTRIBUTE_NORETURN.
(pfatal_with_name): Likewise.
(pipe_closed): Likewise.
(memory_full): Likewise.
Fri Jan 29 00:14:55 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): Grow set_in_loop / n_times_set /
may_not_optimize to proper size when converting biv increments
into givs.
If necessary, reallocate reg_iv_type / reg_iv_info before calling
recombine_givs.
Thu Jan 28 23:24:08 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (recombine_givs): New parameter unroll_p. If set, don't
generate complex adds. Changed caller.
Don't generate adds that cost more than the original one.
(strength_reduce): Warning fixes.
Thu Jan 28 09:41:11 1999 Jeffrey A Law (law@cygnus.com)
* configure.in (hppa1.0-hp-hpux10*): Use t-pa.
* configure: Rebuilt.
Wed Jan 27 23:39:53 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* rtl.h (insn_first_p, no_jumps_between_p): Declare.
* rtlanal.c (insn_first_p, no_jumps_between_p): New function.
* loop.h (varray.h): Include.
(struct induction): Change combined_with to unsigned.
New members derived, ix and last_use.
(reg_iv_type, reg_iv_info): Now varray_type. All references changed.
(REG_IV_TYPE, REG_IV_INFO): Define.
(first_increment_giv, last_increment_giv): Declare.
* loop.c (loop_number_loop_cont): New static variable.
(loop_number_cont_dominator): Likewise.
(reg_iv_type, reg_iv_info): Now varray_type.
(first_increment_giv, last_increment_giv): New variables.
(compute_luids, verify_dominator, find_life_end): New functions.
(cmp_recombine_givs_stats, recombine_givs): Likewise.
(loop_optimize): Allocate loop_number_loop_cont and
loop_number_cont_dominator. Use compute_luids.
(find_and_verify_loops): Initialize loop_number_loop_cont and
loop_number_cont_dominator.
(strength_reduce): Try to find bivs that can be expressed as givs
of another biv, and to convert biv increments into givs.
Call recombine_givs. Handle derived givs.
(record_biv): New argument location. All callers changed.
(record_giv): Initialize derived and last_use fields.
(basic_induction_var): New argument location. All callers changed.
(combine_givs): Don't combine a DEST_REG giv with a DEST_ADDR giv.
Increment combined_with instead of setting to 1.
* unroll.c (derived_regs): New static variable.
(unroll_loop): Initialize it.
Allocate local_regno according to max_reg_num.
(copy_loop_body): Cope with derived givs.
(find_splittable_givs): Check for Givs made from biv increments.
Set derived_regs for givs.
* Makefile.in (stmt.o, loop.o, unroll.o): Depend on loop.h .
Wed Jan 27 19:31:36 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* function.c (purge_addressof_1): Handle case when a register
has been used in a wider mode.
1999-01-27 Bruce Korb <autogen@linuxbox.com>
* fixinc/fixincl.c, fixinc/server.[ch]:
Removed the last of the capitalized variable and proc names.
* fixinc/server.c: Removed the process open code.
* fixinc/procopen.c: New file containing the proc open code.
* fixinc/inclhack.tpl: Added code to bypass a readability test
when a file is not present. A problem on some systems.
* fixinc/inclhack.sh, fixinc/fixincl.sh: Regenerated.
Wed Jan 27 11:58:18 1999 Dave Brolley <brolley@cygnus.com>
* cpplib.h (cpp_notice): Add prototype.
Wed Jan 27 02:20:48 1999 Jeffrey A Law (law@cygnus.com)
* Merge gcc2 snapshot 19980929.
* cccp.c (PRINTF_PROTO): Remove.
(PRINTF_PROTO_{1,2,3,4}: Likewise.
* cexp.y: Likewise.
* system.h: Add PRINTF_PROTO and PRINTF_PROTO_{1,2,3,4}.
* fix-header.c (cpp_file_lin_for_message): Delete. In libcpp.
(cpp_print_containing_files, v_cpp_message, cpp_message): Likewise.
(cpp_fatal, cpp-Pfatal_with_name): Likewise.
* gen-protos.c (hashf): Delete in cpphash.o.
* gen-protos.c (hashf): Delete in cpphash.o.
* expr.c: Do not merge SAVE_STACKAREA_MODE changes.
* expmed.c: Likewise.
* rs6000.md: Likewise.
* rs6000.c, rs6000.md: Do not merge formatting changes yet.
Wed Jan 27 01:13:42 1999 Richard Henderson <rth@cygnus.com>
* rs6000.c (input_operand): Don't expect CONST around CONSTANT_P_RTX.
* rs6000.md (movsi, movdi): Likewise.
Tue Jan 26 13:31:38 1999 Jim Wilson <wilson@cygnus.com>
* function.c (expand_function_end): Pass arg_pointer_save_area to
validize_mem before using it. Emit code into a sequence.
Tue Jan 26 13:41:38 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (doz + set cr and or + set cr patterns): Add missing
'#' to split patterns. Correct indentation of some new patterns.
1999-01-26 Zack Weinberg <zack@midnite.ec.rhno.columbia.edu>
* cppfiles.c (safe_read): Deleted.
(read_and_prescan): New function, replaces safe_read, converts
and/or warns about trigraphs, silently converts odd line
terminators (\r, \n\r, \r\n). Warns about no newline at EOF.
(finclude): Use read_and_prescan; turn off nonblocking mode on
the input descriptor; remove file-size-examination and
no-newline-at-EOF gunk which is longer necessary; be more
careful about checking that we've been handed a legitimate
file to read (only real files, pipes, and ttys are acceptable).
* cpplib.h (cpp_options): Rename no_trigraphs flag to
`trigraphs' and invert its sense.
(trigraph_table): Declare.
(cpp_warning_with_line): Prototype.
* cpplib.c: Remove all references to trigraph_pcp. Define
trigraph_table; initialize it in initialize_char_syntax. Open
files in nonblocking mode. s/no_trigraphs/trigraphs/
throughout, and invert sense. Put cpp_warning_with_line back
in and export it.
Tue Jan 26 23:21:49 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (COUNTER_REGS): New register class.
* config/c4x/c4x.md (*rptb_init): Change constraints.
(rptb_end): Emit alternate looping instructions if
RC register not allocated for loop counter.
(decrement_and_branch_on_count): Allow other registers
for loop counter.
1999-01-25 Zack Weinberg <zack@rabi.columbia.edu>
* cppexp.c (struct arglist): Removed.
(parse_number): Use HOST_WIDE_INT for the accumulator.
Allow two `l' suffixes unless C89. Clean up. Make static.
(parse_charconst): New function broken out of cpp_lex.
Code cleaned up drastically. Don't use a token_buffer.
(token_buffer): Removed.
(cpp_lex): Don't call parse_number on a constant string.
Use parse_charconst.
(cpp_parse_expr): Properly handle an ERROR op returned by
cpp_lex.
1999-01-25 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c: Don't include signal.h, sys/times.h, or
sys/resource.h. Don't declare localtime.
(macroexpand): Handle special symbols here.
(push_macro_expansion): Chop off the trailing '@ ' if possible
here.
(cpp_get_token): Don't do either of the above two things here.
Move `string' label just after case '"' so that wide strings
don't crash the preprocessor.
Sun Jan 24 20:13:45 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (left shift + set cr patterns): Add missing '#' to
split patterns.
(move register + set cr pattern): Likewise.
(movdi, !TARGET_POWERPC64 splitters): Add back in Jan. 15th patch,
inadvertently deleted.
Sun Jan 24 08:07:59 1999 Jeffrey A Law (law@cygnus.com)
* stmt.c (stmt_loop_nest_empty): New function.
* tree.h (stmt_loop_nest_empty): Declare it.
* rtl.def (CALL_PLACEHOLDER): New rtx code.
Sun Jan 24 21:24:43 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_emit_move_sequence, c4x_encode_section_info):
New functions.
(c4x_check_legit_addr): Remove USE and PLUS, allow
LO_SUM, and disable SYMBOL_REF, LABEL_REF, and CONST cases.
(c4x_legitimize_address): Penalize SYMBOL_REF, LABEL_REF, and
CONST cases. Add LO_SUM.
(c4x_print_operand): Modified 'C' and 'R' cases for calls.
Added 'U' case. Remove dependence on SYMBOL_REF_FLAG.
(c4x_print_operand_address): Handle LO_SUM.
(c4x_scan_for_ldp): Delete. Hooray!
(c4x_process_after_reload): Remove call to c4x_scan_for_ldp.
Split all insns.
(c4x_immed_int_constant): Renamed from c4x_int_constant. All callers
changed.
(c4x_immed_float_constant): Renamed from c4x_float_constant. All
callers changed.
(c4x_T_constraint): Allow LO_SUM, disable SYMBOL_REF, LABEL_REF,
and CONST.
(c4x_U_constraint, symbolic_operand): New functions.
(src_operand): Allow 'I' constants in HImode. Allow LO_SUM,
disable SYMBOL_REF, LABEL_REF, and CONST.
(lsrc_operand, tsrc_operand): Call src_operand instead of
general_operand.
(c4x_operand_subword): Update comments.
* config/c4x/c4x.c (TARGET_LOAD_ADDRESS): New macro.
(LEGITIMATE_CONSTANT_P): Allow SYMBOL_REF, LABEL_REF, CONST,
plus HIGH and LO_SUM for the C40.
(ENCODE_SECTION_INFO): Define macro.
(symbolic_operand, c4x_U_constraint, c4x_emit_move_sequence): New
prototypes.
(PREDICATE_CODES): Add symbolic_operand.
* config/c4x/c4x.md (movqi, movgqf, movhi, movhi): Call
c4x_emit_move_sequence.
(floatunsqiqf2, fixuns_truncqfqi2): Rework emitted RTL
to avoid symbol references.
(all patterns with g constraint): Replace 'g' constraint with 'rIm'.
(set_high): Renamed from set_high_use.
(set_lo_sum): Renamed from set_ior_lo_use.
(all call patterns): Make MEM explicit in call address operands.
Modified output templates to use 'U' modifier.
Sun Jan 24 01:15:05 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Jan 23 22:34:57 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* final.c (bb_str): Qualify a char* with the keyword `const'.
(add_bb_string, final_scan_insn, output_asm_insn): Likewise.
* fix-header.c (read_scan_file): Likewise.
* genoutput.c (output_epilogue, process_template): Likewise.
* local-alloc.c (requires_inout, block_alloc): Likewise.
* output.h (output_asm_insn, assemble_string): Likewise.
* recog.c (recog_constraints, check_asm_operands,
decode_asm_operands, extract_insn, preprocess_constraints,
constrain_operands): Likewise.
* recog.h (operand_alternative, recog_constraints, insn_template,
insn_outfun, insn_operand_constraint, insn_name): Likewise.
* regclass.c (record_reg_classes, scan_one_insn): Likewise.
* regmove.c (find_matches): Likewise.
* reload.c (alternative_allows_memconst): Likewise.
* reload1.c (constraint_accepts_reg_p,
reload_cse_simplify_operands): Likewise.
* rtl.h (decode_asm_operands): Likewise.
* scan.h (fn_decl): Likewise.
* varasm.c (assemble_string): Likewise.
Sat Jan 23 01:37:36 1999 Jeffrey A Law (law@cygnus.com)
* configure.in (gcc_tooldir): Handle case where exec_prefix has
not been explicitly set.
* configure: Rebuilt.
* fold-const.c (lshift_double): Mark 'prec' arguments as possibly
unused.
* bitmap.h (bitmap_head_def): Make indx field unsigned.
* configure.in (gcc_tooldir): When not making a relative gcc_tooldir,
use $exec_prefix/$target_alias for gcc_tooldir.
* configure: Rebuilt.
Fri Jan 22 11:48:56 1999 Richard Henderson <rth@cygnus.com>
* cppp.c (xrealloc): Fix typo last change.
* cppalloc.c, gcc.c, genattr.c, genattrtab.c, gencodes.c: Likewise.
* genconfig.c, genemit.c, genextract.c, genflags.c: Likewise.
* genopinit.c, genoutput.c, genpeep.c, genrecog.c: Likewise.
1999-01-22 Michael Meissner <meissner@cygnus.com>
* rs6000.h (CR0_REGNO_P): New macro to test if cr0.
(CR_REGNO_NOT_CR0_P): New macro to test if cr, but not cr0.
(PREDICATE_CODES): Add cc_reg_not_cr0_operand.
(cc_reg_not_cr0_operand): Add declaration.
* rs6000.c (cc_reg_not_cr0_operand): Return true if register is a
pseudo register, or a control register that is not CR0.
* rs6000.md (all combiner patterns building . instructions): For
all `.' instructions that do something and set cr0, add an
alternative that does the operation, and then sets a different
flag, in order to avoid using the costly mcrf instruction and also
allow cr0 to be clobbered in asm statements. Also fix a few
patterns that used the wrong register.
* rs6000.h (rs6000_cpu_select): Make string, names be const char *.
(rs6000_debug_name): Make const char *, not char *.
* sysv4.h (rs6000_{abi,sdata}_name): Make const char *.
* rs6000.c (rs6000_{debug,abi,sdata}_name): Make const char *.
(rs6000_select): Use const char * in casts.
Fri Jan 22 07:43:01 1999 Jeffrey A Law (law@cygnus.com)
* Makefile.in (gcc_tooldir): Move before first reference.
Let autoconf substitute in a value.
* configure.in (gcc_tooldir): Only use a relative path to the
tool directory if $exec_prefix == $prefix.
* configure: Rebuilt.
* Makefile.in (tooldir): Replace with gcc_tooldir.
Thu Jan 21 23:21:57 1999 Jeffrey A Law (law@cygnus.com)
* m68k.md (ashldi_const): Disable for !TARGET_5200. Fix indentation.
(ashldi3 expander): Similarly. Update comments.
(ashrdi_const, lshrdi_const): Fix indentation.
(ashrdi3, lshrdi3): Fix indentation. Update comments.
Thu Jan 21 21:53:36 1999 Richard Henderson <rth@cygnus.com>
* emit-rtl.c (try_split): Don't try to split non-instructions.
Thu Jan 21 23:47:30 1999 Andrew MacLeod <amacleod@cygnus.com>
* expr.c (emit_push_insn): Fix dumb typo.
Thu Jan 21 20:24:02 1999 Richard Henderson <rth@cygnus.com>
* rs6000.h (LEGITIMIZE_RELOAD_ADDRESS): Recognize and accept
transformations that we have performed earlier.
* alpha.h (LEGITIMIZE_RELOAD_ADDRESS): Likewise.
* alpha.md (prologue_stack_probe_loop): Don't do our own label
handling, call gen_label_rtx instead.
Thu Jan 21 17:45:18 1999 Richard Henderson <rth@cygnus.com>
* configure.in ({rs6000|powerpc}-ibm-aix4.[12]*): Add missing `then'.
* cccp.c (xrealloc): Call malloc given a NULL old pointer.
* collect2.c, cppalloc.c, gcc.c, genattr.c, genattrtab.c: Likewise.
* gencodes.c, genconfig.c, genemit.c, genextract.c: Likewise.
* genflags.c, genopinit.c, genoutput.c, genpeep.c: Likewise.
* genrecog.c, mips-tfile.c, protoize.c: Likewise.
Thu Jan 21 19:44:55 1999 Michael Meissner <meissner@cygnus.com>
* configure.in ({rs6000|powerpc}-ibm-aix4.[12]*): If
--with-gnu-ld, use x-aix41-gld instead of x-aix41 to suppress
adding -Wl,-bbigtoc to BOOT_LDFLAGS.
* configure: Regenerate.
* config/rs6000/x-aix41-gld: New file, don't set BOOT_LDFLAGS.
Thu Jan 21 15:48:03 1999 Dave Brolley <brolley@cygnus.com>
* cppexp.c (cpp_lex): Allocate token_buffer dynamically.
Thu Jan 21 14:18:04 1999 Andrew MacLeod <amacleod@cygnus.com>
* expr.c (MOVE_BY_PIECES_P): Define condition for deciding to use
move_by_pieces.
(MOVE_MAX_PIECES): Define maximum number of bytes to move at once.
(USE_LOAD_POST_INCREMENT, USE_LOAD_PRE_DECREMENT): Define defaults.
(USE_STORE_POST_INCREMENT, USE_STORE_PRE_DECREMENT): Define defaults.
(move_by_pieces): Use new macros.
(emit_block_move): Use new macros.
(clear_by_pieces): Use new macros.
(clear_storage): Use new macros.
(emit_push_insn): Use new macros.
(expand_expr): Use new macros.
* config/sh/sh.h (USE_LOAD_POST_INCREMENT, USE_LOAD_PRE_DECREMENT):
Define.
(USE_STORE_POST_INCREMENT, USE_STORE_PRE_DECREMENT): Define.
(MOVE_BY_PIECES_P): Define based on alignment and TARGET_SMALLCODE.
(MOVE_MAX_PIECES): Move 8 bytes on SH4.
* tm.texi(MOVE_BY_PIECES_P, MOVE_MAX_PIECES, USE_LOAD_POST_INCREMENT,
USE_LOAD_PRE_DECREMENT, USE_STORE_POST_INCREMENT,
USE_STORE_PRE_DECREMENT): Describe new macros.
Thu Jan 21 14:13:31 1999 Vladimir N. Makarov <vmakarov@cygnus.com>
* varasm.c (output_constant_pool): Use floor_log2 instead of
exact_log2 for ASM_OUTPUT_ALIGN.
* stor-layout.c (layout_type): Do machine-dependent extra alignment.
* emit-rtl.c (operand_subword): Handle case when a subword outside
the operand.
* tm.texi (ROUND_TYPE_{SIZE,ALIGN}): More accurate descriptions of
the macros.
Thu Jan 21 01:59:30 1999 Richard Henderson <rth@cygnus.com>
* cse.c (fold_rtx): Revert 29 Dec change.
(cse_insn): Revert 12 Jan change.
* expr.c (expand_builtin): Don't emit CONST around CONSTANT_P_RTX.
* regclass.c (reg_scan_mark_refs): Revert 29 Dec change.
* rtl.def: Likewise.
* rtl.h (CONSTANT_P): Likewise.
* expr.c (emit_move_insn): Never try to flush CONSTANT_P_RTX
to memory.
* recog.c (immediate_operand): Accept CONSTANT_P_RTX.
* alpha.c (input_operand): Likewise.
* c4x.c (const_operand): Likewise.
* explow.c (allocate_dynamic_stack_space): Use register_operand
instead of arith_operand, which does not exist.
* 1750a.h: Fix comment closure.
* a29k.c (a29k_set_memflags): Fix typo in 19 Jan change.
* arc.md (one_cmplsi2_set_cc_insn): Fix set mode mismatch.
* arm.h (TARGET_SWITCHES): Fix typo.
* i370.md (anon mult and div patterns): Fix set mode mismatch.
* i860.c (output_delayed_branch): Fix operands to constrain_operands.
(output_delay_insn): Likewise.
* m88k.md (anon rotate insns): Fix set mode mismatch.
(anon BLKmode moves): Commonize and fix set mode mismatches.
* ns32k.md (udivmoddi[shq]i4_internal): Fix mode mismatch.
* romp.md (movdf): Fix typo.
Thu Jan 21 00:29:35 1999 Nathan Sidwell <nathan@acm.org>
* Makefile.in (install-common): Remove extraneous chmod for gcov
install.
Wed Jan 20 18:15:08 1999 Dave Brolley <brolley@cygnus.com>
* function.c (assign_parms): Save and restore setting of
TREE_USED (parm).
Wed Jan 20 12:51:42 1999 Mark Mitchell <mark@markmitchell.com>
* arm.md: Use MEM_COPY_ATTRIBUTES where appropriate throughout.
Pass MEM_SCALAR_P to arm_gen_store_multiple where appropriate.
Tue Jan 19 21:20:52 1999 Richard Henderson <rth@cygnus.com>
* recog.c (pop_operand): New function.
* recog.h (pop_operand): Declare it.
* genrecog.c (preds): Define it.
* expr.c (do_jump_for_compare): Handle conditional branch expanders
emitting multiple jump instructions.
* jump.c (condjump_label): New function.
* rtl.h (condjump_label): Declare it.
Tue Jan 19 21:08:20 1999 Richard Henderson <rth@cygnus.com>
* expr.c (emit_move_insn_1): Revert 17 Dec change. Don't emit
clobber during or after reload.
Tue Jan 19 16:56:03 1999 Richard Henderson <rth@cygnus.com>
* genoutput.c (name_for_index): New function.
(scan_operands, validate_insn_alternatives): Use it.
* genrecog.c (insn_name_ptr_size): New variable.
(make_insn_sequence): Fill in insn_name_ptr.
(merge_trees): Use it.
Tue Jan 19 16:37:36 1999 Richard Henderson <rth@cygnus.com>
* i386/isc.h (TARGET_DEFAULT): Define symbolicly.
* i386/isccoff.h, i386/next.h, i386/sco.h, i386/sco5.h: Likewise.
* i386/scodbx.h, i386/sequent.h, i386.unix.h: Likewise.
Tue Jan 19 15:00:10 1999 Jeffrey A Law (law@cygnus.com)
* loop.c (NUM_STORES): Delete.
(loop_store_mems): Turn into an EXPR_LIST of MEMs.
(prescan_loop): Properly initialize loop_mems_idx.
(note_addr_stored): Simplify using list structure instead of
fixed sized array.
(invariant_p, check_dbra_loop, load_mems): Similarly.
* flow.c (invalidate_from_autoinc): New function.
(mark_set_1, mark_used_regs): Use it.
* Makefile.in (protoize.o, unprotoize.o): Depend on Makefile.
1999-01-19 Vladimir N. Makarov <vmakarov@cygnus.com>
* invoke.texi (-mlong-double-64): New option description.
1999-01-19 Jim Wilson <wilson@cygnus.com>
* libgcc2.c: Change all uses of LONG_DOUBLE_TYPE_SIZE to
LIBGCC2_LONG_DOUBLE_TYPE_SIZE.
(LIBGCC2_LONG_DOUBLE_TYPE_SIZE): New. Set to LONG_DOUBLE_TYPE_SIZE
if not defined.
* i960/i960.h (MULTILIB_DEFAULTS): Define to mnumerics.
(CPP_SPECS): Add -mlong-double-64 support.
(TARGET_FLAG_LONG_DOUBLE_64, TARGET_LONG_DOUBLE_64): New.
(TARGET_SWITCHES): Add -mlong-double-64 support.
(LONG_DOUBLE_TYPE_SIZE): Likewise.
(LIBGCC2_LONG_DOUBLE_TYPE_SIZE): Define.
* i960/vx960-coff.h (MULTILIB_DEFAULTS): Define to msoft-float.
(CPP_SPECS): Add -mlong-double-64 support.
* i960/t-960bare (MULTILIB_OPTIONS): Add mlong-double-64.
(MULTILIB_DIRNAMES): Add ld64.
* i960/t-vxworks960 (MULTILIB_OPTIONS, MULTILIB_DIRNAMES): Likewise.
Tue Jan 19 11:54:04 1999 Jason Merrill <jason@yorick.cygnus.com>
* calls.c (expand_call): Strip a TARGET_EXPR if we're passing by
invisible reference.
Tue Jan 19 14:51:36 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (offsettable_addr_operand): Delete.
(offsettable_mem_operand): New function.
* rs6000.h (PREDICATE_CODES): Reflect function change.
(RS6000_SAVE_TOC): Represent address as MEM.
* win-nt.h (RS6000_SAVE_TOC): Same.
* rs6000.md (indirect calls): Change offsettable address parameter
to offsettable memory parameter.
Tue Jan 19 10:24:53 1999 Mark Mitchell <mark@markmitchell.com>
* rtl.h (rtx_def): Update documentation.
(MEM_IN_STRUCT_P): Likewise.
(MEM_SCALAR_P): New macro.
(MEM_COPY_ATTRIBUTES): Likewise.
(MEM_SET_IN_STRUCT_P): Likewise.
* rtl.texi (MEM_SCALAR_P): Document.
* alias.c (canon_rtx): Use MEM_COPY_ATTRIBUTES.
(fixed_scalar_and_varying_struct_p): New function. Use
MEM_SCALAR_P rather than !MEM_IN_STRUCT_P.
(aliases_everything_p): Likewise.
(true_dependence): Use them.
(write_dependence_p): New function, containing code common to
anti_dependence and output_dependence.
(anti_dependence): Use it.
(output_dependence): Likewise.
* calls.c (save_fixed_argument_area): Don't clear
MEM_IN_STRUCT_P.
(expand_call): Use MEM_SET_IN_STRUCT_P.
(emit_library_call): Don't clear MEM_IN_STRUCT_P.
(emit_library_call_value): Likewise.
(store_one_arg): Use MEM_SET_IN_STRUCT_P.
* combine.c (simplify_rtx): Use MEM_COPY_ATTRIBUTES.
(make_extraction): Likewise.
(simplify_shift_const): Likewise.
(gen_lowpart_for_combine): Likewise.
* cse.c (gen_lowpart_if_possible): Use MEM_COPY_ATTRIBUTES.
* emit-rtl.c (operand_subword): Likewise.
(change_address): Likewise.
* explow.c (stabilize): Use MEM_COPY_ATTRIBUTES.
* expr.c (protect_from_queue): Use MEM_COPY_ATTRIBUTES.
(emit_group_store): Use MEM_SET_IN_STRUCT_P.
(copy_blkmode_from_reg): Likewise.
(store_field): Likewise.
(expand_expr): Remove bogus guesswork setting MEM_IN_STRUCT_P
heuristically. Use MEM_SET_IN_STRUCT_P.
(get_memory_rtx): Likewise.
* final.c (alter_subreg): Use MEM_COPY_ATTRIBUTES.
* function.c (assign_stack_temp): Clear MEM_SCALAR_P and
MEM_ALIAS_SET on newly returned MEMs.
(assign_temp): Use MEM_SET_IN_STRUCT_P.
(put_reg_into_stack): Likewise.
(fixup_var_refs1): Use MEM_COPY_ATTRIBUTES.
(gen_mem_addressof): Use MEM_SET_IN_STRUCT_P.
(assign_parms): Likewise.
(expand_function): Likewise.
* integrate.c (expand_inline_function): Likewise.
(copy_rtx_and_substitute): Use MEM_COPY_ATTRIBUTES.
* loop.c (note_addr_stored): Remove check on MEM_IN_STRUCT_P.
* optabs.c (gen_move_insn): Use MEM_COPY_ATTRIBUTES.
* print-rtl.c (print_rtx): Print /f for frame_related.
* recog.c (validate_replace_rtx_1): Use MEM_COPY_ATTRIBUTES.
* reload1.c (reload): Copy MEM_SCALAR_P as well.
* stmt.c (expand_decl): Use MEM_SET_IN_STRUCT_P.
(expand_anon_union_decl): Use MEM_COPY_ATTRIBUTES.
* varasm.c (make_decl_rtl): Use MEM_SET_IN_STRUCT_P.
(output_constant_def): Likewise.
* a29k.c (a29k_set_memflags_1): Take scalar_p.
Set MEM_SCALAR_P.
(a29k_set_memflags): Use it.
* alpha.c (get_aligned_mem): Use MEM_COPY_ATTRIBUTES.
* c4x.c (c4x_scan_for_ld): Likewise.
* h8300.c (fix_bit_operand): Likewise.
* m88k.c (legitimize_address): Likewise.
(block_move_loop): Likewise.
(block_move_no_loop): Likewise.
(block_move_sequence): Likewise.
(m88k_builtin_saveregs): Use MEM_SET_IN_STRUCT_P.
* mips/abi64.h (SETUP_INCOMING_VARARGS): Likewise.
* rs6000.c (expand_block_move_insn): Use MEM_COPY_ATTRIBUTES.
* sh.c (sh_builtin_saveregs): Use MEM_SET_IN_STRUCT_P.
* arm.h (arm_gen_load_multiple): Take scalar_p.
(arm_store_load_multiple): Likewise.
* arm.c (arm_gen_load_multiple): Likewise.
(arm_gen_store_multiple): Likewise.
(arm_gen_movstrqi): Treat MEM_SCALAR_P like MEM_IN_STRUCT_P.
Tue Jan 19 12:30:37 1999 Andrew MacLeod <amacleod@cygnus.com>
* optabs.c (emit_libcall_block): Add a REG_EH_REGION reg note to all
calls within a libcall block to indicate no throws are possible.
* flow.c (find_basic_blocks, find_basic_blocks_1): Don't look for
libcall blocks. Don't add edges to exception handlers if we see
a REG_EH_REGION note with a value of 0.
(make_edges): Override active_eh_region vector if the call has a note
indicating the call does not throw.
1999-01-19 Vladimir N. Makarov <vmakarov@cygnus.com>
* config/rs6000/sysv4.h (CC1_SPEC): Fix correct numbers of {}.
Tue Jan 19 06:26:30 1999 Jeffrey A Law (law@cygnus.com)
* Makefile.in (cccp.o, cpplib.o): Depend on Makefile.
Mon Jan 18 09:56:41 1999 Jason Merrill <jason@yorick.cygnus.com>
* invoke.texi (C++ Dialect Options): Document -fno-rtti.
1999-01-18 Vladimir N. Makarov <vmakarov@cygnus.com>
* invoke.texi (-mcpu=740, -mcpu=750): New options.
(-m(no-)multiple, -m(no-)string): Describe cases for PPC740 &
PPC750.
1999-01-18 Michael Meissner <meissner@cygnus.com>
* rs6000.h ({ASM,CPP}_CPU_SPEC): Add support for all machines
supported with -mcpu=xxx.
(processor_type): Add PROCESSOR_PPC750.
(ADJUST_PRIORITY): Call rs6000_adjust_priority.
(RTX_COSTS): Supply costs for 750 multiply/divide operations.
(rs6000_adjust_priority): Add declaration.
* rs6000.c (rs6000_override_options): -mcpu={750,740} now sets the
processor type as 750, not 603. Allow -mmultiple and -mstring on
little endian 750 systems.
(rs6000_adjust_priority): Stub for now.
(get_issue_rate): The PowerPC 750 can issue 2 instructions/cycle.
* rs6000.md (function/cpu attributes): Add initial ppc750 support.
* sysv4.h (STRICT_ALIGNMENT): Don't force strict alignment if
little endian.
(CC1_SPEC): Pass -mstrict-align if little endian, and not
overridden.
(CC1_ENDIAN_{LITTLE,BIG,DEFAULT}_SPEC): Endian specific configs.
(SUBTARGET_EXTRA_SPECS): Add cc1 endian specs.
* {sysv4,eabi}le.h (CC1_ENDIAN_DEFAULT_SPEC): Override, default is
little endian.
* t-ppcgas (MULTILIB_*): Delete obsolete Solaris multilibs.
Mon Jan 18 12:03:08 1999 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/mips.md (div_trap): Split div_trap_mips16
from div_trap.
(div_trap_normal,div_trap_mips16): Correct the length attributes.
Mon Jan 18 11:48:28 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cpplib.c (special_symbol): Qualify a char* with the `const' keyword.
Instead of writing to const char *buf directly, use a non-const
variable `wbuf' to allocate and write a string, then set buf = wbuf.
* cppulp.c (user_label_prefix): Qualify a char* with the `const'
keyword.
* dyn-string.c (dyn_string_append): Likewise.
* dyn-string.h (dyn_string_append): Likewise.
* final.c (end_final, output_operand_lossage, asm_fprintf): Likewise.
* output.h (end_final, output_operand_lossage, asm_fprintf,
named_section, decode_reg_name, make_decl_rtl, user_label_prefix):
Likewise.
* profile.c (init_branch_prob): Likewise.
* toplev.c (set_target_switch, vmessage,
v_message_with_file_and_line, v_message_with_decl,
v_error_with_file_and_line, v_error_with_decl, v_error_for_asm,
verror, vfatal, v_warning_with_file_and_line, v_warning_with_decl,
v_warning_for_asm, vwarning, vpedwarn, v_pedwarn_with_decl,
v_pedwarn_with_file_and_line, vsorry, v_really_sorry,
open_dump_file, dump_rtl, clean_dump_file,
print_version, print_single_switch, print_switch_values,
dump_base_name, debug_args, lang_independent_options,
user_label_prefix, documented_lang_options, target_switches,
target_options, print_time, pfatal_with_name, fatal_io_error,
fatal_insn, default_print_error_function, print_error_function,
report_error_function, error_with_file_and_line, error_with_decl,
error_for_asm, error, fatal, warning_with_file_and_line,
warning_with_decl, warning_for_asm, warning, pedwarn,
pedwarn_with_decl, pedwarn_with_file_and_line, sorry,
really_sorry, botch, output_quoted_string, output_file_directive,
open_dump_file, rest_of_decl_compilation, display_help, main):
Likewise.
* toplev.h (print_time, fatal, fatal_io_error, pfatal_with_name,
fatal_insn, warning, error, pedwarn, pedwarn_with_file_and_line,
warning_with_file_and_line, error_with_file_and_line, sorry,
really_sorry, default_print_error_function, report_error_function,
rest_of_decl_compilation, pedwarn_with_decl, warning_with_decl,
error_with_decl, error_for_asm, warning_for_asm, output_quoted_string,
output_file_directive, botch): Likewise.
* tree.h (make_decl_rtl): Likewise.
* varasm.c (strip_reg_name, named_section, decode_reg_name,
make_decl_rtl): Likewise.
Mon Jan 18 11:35:49 1999 Gavin Romig-Koch <gavin@cygnus.com>
* Makefile.in (TCL_LIBRARY): Use 'cd' to find the library
directory logically rather than physically.
Mon Jan 18 09:05:37 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* loop.c (insert_bct): Hide the definition of variables
`increment_direction', `compare_direction', `add_iteration' and
`loop_var_mode'.
* recog.c (mode_dependent_address_p): Mark parameter `addr' with
ATTRIBUTE_UNUSED. Mark label `win' with ATTRIBUTE_UNUSED_LABEL.
(mode_independent_operand): Mark label `lose' with
ATTRIBUTE_UNUSED_LABEL.
* regclass.c (n_occurrences): Remove prototype and definition.
* reload.c (find_reloads_address_1): Mark variable `tem' with
ATTRIBUTE_UNUSED.
* reload1.c (reload): Cast the first two arguments of `bcopy' to PTR.
* sbitmap.c (sbitmap_copy): Likewise.
* scan-decls.c (scan_decls): Hide label `handle_comma'.
* toplev.c (output_lang_identify): Mark prototype with
ATTRIBUTE_UNUSED.
* tree.c (make_node): Cast the first argument of `bzero' to PTR.
(make_tree_vec): Likewise.
(build1): Likewise.
* varasm.c (assemble_static_space): Mark variable `tem' with
ATTRIBUTE_UNUSED.
Mon Jan 18 04:28:36 1999 Nathan Sidwell <nathan@acm.org>
* Makefile.in (GCOV_INSTALL_NAME): New macro.
(install-common): Use it.
(uninstall): Use it.
(uninstall): Use correct names for protoize and unprotoize.
Mon Jan 18 03:52:56 1999 Christian Bruel <Christian.Bruel@st.com>
Jeffrey A Law (law@cygnus.com)
* flow.c (last_mem_set): Delete variable. References removed.
(mem_set_list): New variable.
(life_analysis): Initialize and finalize alias analysis.
(propagate_block); Initialize mem_set_list. Clear for CALL_INSNs.
(insn_dead_p): For a store to memory, search the entire mem_set_list
for a match.
(mark_set_1): Kill entries on the mem_set_list for aliased writes or
changes to their addresses. Add new entries to the mem_set_list for
memory writes writes.
(mark_used_regs): Kill entries on the mem_set_list which may be
referenced by a load operation.
Mon Jan 18 01:01:02 1999 Jeffrey A Law (law@cygnus.com)
* alias.c (base_alias_check): Add missing return for differing
symbols case.
Mon Jan 18 00:36:13 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* mips-tdump.c (print_file_desc): Handle unknown filenames and
missing local symbols.
Sun Jan 17 21:04:31 1999 Richard Henderson <rth@cygnus.com>
* jump.c (rtx_renumbered_equal_p): Special case CODE_LABEL.
* system.h (bcopy): Implement with memmove not memcpy.
Sun Jan 17 19:23:20 1999 Jeffrey A Law (law@cygnus.com)
* Makefile.in (cppulp.o): Add dependencies.
* i386.md (integer conditional moves): Add missing earlyclobbers.
* regmove.c (optimize_reg_copy_1): Undo Aug 18 change. Update
REG_N_CALLS_CROSSED and REG_LIVE_LENGH if and only if we change
where a register is live.
Sun Jan 17 03:20:47 1999 H.J. Lu (hjl@gnu.org)
* reg-stack.c (subst_stack_regs_pat): Abort if the destination
of a FP conditional move is not on the FP register stack.
Sun Jan 17 01:15:04 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Jan 16 23:40:33 1999 Jeffrey A Law (law@cygnus.com)
* reload1.c (reload_cse_regs_1): Do not call
reload_cse_simplify_operands for an insn with asm operands.
* cccp.c (print_help): Fix typos.
* cpplib.c (print_help): Fix typos.
* toplev.c (f_optiosn): Fix typos.
(documented_lang_options): Fix typos.
Sat Jan 16 21:48:17 1999 Marc Espie (Marc.Espie@openbsd.org)
* gcc.c (do_spec_1): Fix obvious typo.
Sat Jan 16 19:31:07 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-decl.c (duplicate_decls): If `warn_traditional', warn when
a non-static function declaration follows a static one.
* invoke.texi (-Wtraditional): Document the extra check now done
by this flag.
Sat Jan 16 15:13:46 1999 Jeffrey A Law (law@cygnus.com)
* pa.md (shadd): Create shadd insns, even if the result of the shift is
needed without the addition.
Sat Jan 16 10:48:16 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (movdf, movsf): Temporary workaround for no_new_pseudos lossage.
Fri Jan 15 23:44:37 1999 Richard Henderson <rth@cygnus.com>
* sparc.c (sparc_issue): Add hypersparc/sparclite86x entries.
Fri Jan 15 22:30:04 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (CONST_OK_FOR_LETTER_P): Do not assume 32-bit CONST_INT.
* rs6000.c (u_short_cint_operand, add_operand, logical_operand,
non_add_cint_operand, non_logical_cint_operand): Likewise.
(get_issue_rate): Add CPU_PPC604E case.
* rs6000.md (movdi, !TARGET_POWERPC64 splitters): Handle 64-bit hosts.
Fri Jan 15 18:42:12 1999 Richard Henderson <rth@cygnus.com>
* expr.c (queued_subexp_p): Make public.
* expr.h (queued_subexp_p): Declare it.
* recog.c (asm_operand_ok): New function.
(check_asm_operands): Use it. After reload, use constrain_operands
instead.
* recog.h (asm_operand_ok): Declare it.
* stmt.c (expand_asm_operands): Use it to try harder to make
asms initially satisfy their constraints.
Fri Jan 15 17:43:59 1999 Jeffrey A. Law <law@rtl.cygnus.com>
* sparc.h (LEGITIMIZE_RELOAD_ADDRESS): Do not create
(mem (lo_sum (...)) for TFmode unless TARGET_V9.
Sat Jan 16 12:47:15 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (not_repeat_reg): Allow ldp instruction
in delay slot of RPTBD.
Sat Jan 16 12:26:40 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/libgcc.S (___divhi3, ___modhi3): Fix long long
divide and modulo sign problem.
Fri Jan 15 11:02:31 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* unroll.c (loop_iterations): Return 0 if the last loop insn
is not a jump insn or if the loop has multiple back edges.
1999-01-15 Manfred Hollstein <manfred@s-direktnet.de>
* configure.in (fixinc_defs): Do not define for m[68]8k-motorola-sysv{,3};
it's working properly now. Remove comment saying "see m68k-motorola-sysv
as an example".
* configure: Regenerate using autoconf.
* fixinc/fixincl.c (main): Do not ignore SIGCHLD.
Thu Jan 14 22:38:41 1999 Jeffrey A Law (law@cygnus.com)
* unroll.c (find_splittable_givs): For a DEST_ADDR giv, do not share
a register with another DEST_ADDR giv if the address is not valid.
* pa.c (hppa_expand_epilogue): Save and restore the static chain
around the call to mcount.
* h8300.h (ASM_OUTPUT_LABELREF): Use asm_fprintf, not fprintf.
* stmt.c (expand_end_case): Use emit_cmp_and_jump_insns to avoid
generating non-canonical rtl.
1999-01-14 Vladimir N. Makarov <vmakarov@cygnus.com>
* config/i960/i960.c (i960_output_move_double_zero,
i960_output_move_quad_zero): New functions for moving zeros.
(i960_output_move_double, i960_output_move_quad): Additional code
for situation when moving unaligned register group.
* config/i960/i960.h (i960_output_move_double_zero,
i960_output_move_quad_zero): The function definitions.
* config/i960/i960.md (movdi+1, movti+1): Usage of the functions.
1999-01-13 Vladimir N. Makarov <vmakarov@cygnus.com>
* config/i960/i960.c (i960_function_prologue): New code (optimal
solution) for saving global registers in local registers.
(form_reg_groups, reg_group_compare, split_reg_group): New
functions used by the code.
(reg_group): New structure definition for the new code.
1999-01-13 Manfred Hollstein <manfred@s-direktnet.de>
* fixinc/fixincl.c (create_file): Pass file creation mask as
third parameter to "open". Use O_TRUNC flag to open instead of
explicitly unlink'ing the file.
(process): and forget about the "chmod" stuff.
Wed Jan 13 20:12:37 1999 Richard Henderson <rth@cygnus.com>
* integrate.c (expand_inline_function): Recognize (mem (addressof))
and substitute. Copy the return value from there into a new pseudo.
Wed Jan 13 16:47:00 1999 Catherine Moore <clm@cygnus.com>
* config/arm.c (output_func_epilogue): Check TARGET_ABORT_NORETURN
before generating a call to abort for volatile functions.
* config/arm.h (ARM_FLAG_ABORT_NORETURN): Define.
(TARGET_ABORT_NORETURN): Define.
(abort-on-noreturn): New option.
Thu Jan 14 13:52:42 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (in_annul_slot_3): Correctly allow unarycc
and binarycc operations in 3rd annulled delay slot!
Wed Jan 13 16:16:44 1999 Catherine Moore <clm@cygnus.com>
* config/arm.c (output_func_epilogue): Check TARGET_ABORT_NORETURN
before generating a call to abort for volatile functions.
* config/arm.h (ARM_FLAG_ABORT_NORETURN): Define.
(TARGET_ABORT_NORETURN): Define.
(abort-on-noreturn): New option.
Wed Jan 13 13:30:08 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c (xstrdup): Renamed from `savestring'. All callers changed.
Remove prototype which we get from libiberty.h.
* collect2.c (xstrdup): Likewise.
* genextract.c (xstrdup): Likewise for `copystr'.
(mybzero): Remove it and use `memset' instead.
* genoutput.c (mybcopy, mybzero): Remove these. All callers changed
to use `memcpy' and `memset' instead.
* genrecog.c (xstrdup): Renamed from `copystr'. All callers
changed. Remove prototype.
(mybcopy, mybzero): Remove these and use memcpy/memset.
Wed Jan 13 00:59:04 1999 Jeffrey A Law (law@cygnus.com)
* mips.h (LOAD_EXTEND_OP): Correct for SImode and CCmode moves when
generating code for TARGET_64BIT.
Tue Jan 12 14:05:37 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (print_operand, cases 'm' and 'M'): Do not depend on
HOST_WIDE_INT word-size.
(rs6000_stack_info): Remove redundant alignment of fpmem.
Tue Jan 12 14:05:37 1999 Richard Henderson <rth@cygnus.com>
* rs6000.c (short_cint_operand): Remove CONSTANT_P_RTX handling.
(u_short_cint_operand, reg_or_cint_operand, logical_operand): Likewise.
(input_operand): Adjust CONSTANT_P_RTX handling.
* rs6000.h (PREDICATE_CODES): Remove CONSTANT_P_RTX references.
* rs6000.md (movsi): Adjust CONSTANT_P_RTX handling.
(movhi, movqi): Remove CONSTANT_P_RTX handling.
(movdi): Adjust CONSTANT_P_RTX handling.
1999-01-12 Manfred Hollstein <manfred@s-direktnet.de>
* configure: Regenerate using autoconf.
* fixinc/Makefile.in (INCLUDES): Add -I$(srcdir)/../../include.
* fixinc/fixincl.c (SIGCHLD): Use SIGCLD on (very) old systems.
(process): "fchmod" isn't available on all systems, use "chmod"
instead.
* fixinc/server.c: Add #include <sys/types.h>.
(STDIN_FILENO): Add default definition if no include file defines
it already.
(STDOUT_FILENO): Likewise.
Tue Jan 12 10:23:24 1999 Stan Cox <scox@cygnus.com>
* mips.md (call_value_internal3c): New pattern for -mips16 -mlong-calls.
1999-01-12 Manfred Hollstein <manfred@s-direktnet.de>
* m68k/mot3300.h (ADD_MISSING_POSIX, ADD_MISSING_XOPEN): Define to
ensure all prototypes necessary for building libio will be available.
* m68k/xm-mot3300.h (ADD_MISSING_POSIX, ADD_MISSING_XOPEN): Remove
definitions here as they are not host specific.
* m88k/sysv3.h, m88k/xm-sysv3.h: Likewise.
Tue Jan 12 02:53:46 1999 Richard Henderson <rth@cygnus.com>
* cse.c (cse_insn): Never prefer (const (constant_p_rtx)).
Tue Jan 12 02:36:10 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Tue Jan 12 01:30:19 1999 Richard Henderson <rth@cygnus.com>
* rtl.c (rtx_alloc): Use memset instead of inline loop.
* recog.h (recog_op_alt): Declare extern.
Tue Jan 12 00:23:31 1999 Richard Henderson <rth@cygnus.com>
* function.c (purge_addressof_1): If the note accesses a mem+addressof
in a wider mode than any replacement, adjust the cached replacement.
Cache trivial substitutions as well.
Tue Jan 12 00:06:00 1999 Richard Henderson <rth@cygnus.com>
* Makefile.in (OBJECTS): Add sbitmap.o.
(BASIC_BLOCK_H): Add sbitmap.h.
* basic-block.h: Move simple bitmap code to sbitmap.h.
* flow.c: Move simple bitmap code to sbitmap.c.
* sbitmap.h, sbitmap.c: New files.
Mon Jan 11 23:51:50 1999 Richard Henderson <rth@cygnus.com>
* alpha.h (TARGET_SWITCHES): Document switches.
(TARGET_OPTIONS): Likewise.
* alpha/elf.h (ASM_FINISH_DECLARE_OBJECT): Use HOST_WIDE_INT_PRINT_DEC.
Mon Jan 11 22:54:14 1999 Richard Henderson <rth@cygnus.com>
* tree.c (new_alias_set): Return zero if !flag_strict_aliasing.
Mon Jan 11 22:36:01 1999 Richard Henderson <rth@cygnus.com>
* basic-block.h (basic_block_head): Rename to x_basic_block_head.
(basic_block_end): Rename to x_basic_block_end.
(BLOCK_HEAD, BLOCK_END): Update.
* caller-save.c: Change basic_block_head/end references to
BLOCK_HEAD/END.
* combine.c, flow.c, function.c, gcse.c, global.c: Likewise.
* graph.c, haifa-sched.c, local-alloc.c, regclass.c: Likewise.
* regmove.c, reload1.c, reorg.c, sched.c: Likewise.
Sat Jan 9 23:54:09 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gcc.c (xstrerror): Renamed from my_strerror. All callers
changed. Remove prototype since we get that from libiberty.h.
* protoize.c (xstrerror): Likewise.
Sat Jan 9 23:22:04 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gcc.c (read_specs): Ensure format specifiers match their arguments.
Sat Jan 9 20:04:24 1999 Richard Henderson <rth@cygnus.com>
* tree.c (copy_node): Oops. That would be copy not zero
in that last change.
Sun Jan 10 15:35:41 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c: Include system.h.
(c4x_caller_save_map): Disable caller save for RC.
(c4x_optimization_options): Disable scheduling before reload.
(valid_parallel_load_store) : Define return type as int.
Remove unused variable regs.
* config/c4x/c4x.h (REGISTER_MOVE_COST): Make independent of register
class.
* config/c4x/c4x.md (rotlqi3, rotrqi3): Fix up emitted RTL to
handle rotations.
(*db, decrement_and_branch_until_zero): Fix up constraints
to keep reload happy.
Sat Jan 9 18:35:29 1999 Richard Henderson <rth@cygnus.com>
* tree.c (make_node): Call bzero instead of inline clear.
(copy_node, make_tree_vec, build1): Likewise.
(get_identifier): Call strlen instead of inline count.
(maybe_get_identifier): Likewise.
Sun Jan 10 14:04:51 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (in_annul_slot_3): Allow unarycc and binarycc
operations in 3rd annulled delay slot.
(*lshrqi3_const_set): Disallow c constraint for operand0.
(modhi3+1, modhi3+2): Set attribute type to multi.
* config/c4x/c4x.c (c4x_S_constraint): Removed space in middle of
!= operator.
Sat Jan 9 11:44:55 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gansidecl.h: Allow attribute unused on labels only when we are
version 2.93 or higher. Not all versions of 2.92 have this feature.
* version.c: Bump minor number to 93.
Fri Jan 8 10:51:13 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* config/m68k/m68k.h: Declare output_function_epilogue.
* recog.h: Declare next_insn_tests_no_inequality.
Fri Jan 8 01:43:53 1999 Jeffrey A Law (law@cygnus.com)
* stmt.c (optimize_tail_recursion): New function, extracted from ...
(expand_return): Use optimize_tail_recursion.
* tree.h (optimize_tail_recursion): Declare.
* toplev.c (compile_file): Move call to output_func_start_profiler
to after the loop to emit deferred functions.
Thu Jan 7 19:52:53 1999 Gerald Pfeifer <pfeifer@dbai.tuwien.ac.at>
* system.h (abort): Supply more detailed information on how to
report an Internal Compiler Error.
Thu Jan 7 09:25:58 1999 Bruce Korb (korb@datadesign.com)
* fixinc/fixincl.c (*): More decapitalization of variables
plus some explanatory comments.
* fixinc/Makefile.in fixinc/mkfixinc.sh:
When the fixincl program does not work for a certain system,
we substitute a shell script. Added user commentary when
this happens.
Thu Jan 7 11:26:17 1999 Mark Mitchell <mark@markmitchell.com>
* calls.c (store_unaligned_arguments_into_pseudos): Use xmalloc to
allocate memory that will live beyond this function.
(expand_call): Free it here.
Thu Jan 7 03:08:17 1999 Richard Henderson <rth@cygnus.com>
* sparc.h (PREFERRED_RELOAD_CLASS): Select GENERAL_REGS for
integer data not destined for fp regs.
(LEGITIMIZE_RELOAD_ADDRESS): New.
Thu Jan 7 03:03:42 1999 Stan Cox <scox@cygnus.com>
Richard Henderson <rth@cygnus.com>
Support for Hypersparc and Sparclite86x:
* sparc.h (TARGET_CPU_hypersparc, TARGET_CPU_sparclite86x): New.
(CPP_CPU32_DEFAULT_SPEC): Fix up for the new targets.
(ASM_CPU32_DEFAULT_SPEC): Likewise.
(TARGET_CPU_DEFAULT): Likewise.
(enum processor_type): Likewise.
(CPP_ENDIAN_SPEC): Handle little endian data.
(LIBGCC2_WORDS_BIG_ENDIAN): Likewise.
(ADJUST_COST): Call sparc_adjust_cost.
* sparc.c (sparc_override_options): Fix up for the new targets.
(supersparc_adjust_cost): Make static.
(hypersparc_adjust_cost): New.
(ultrasparc_adjust_cost): Make static.
(sparc_adjust_cost): New.
* sparc.md (attr cpu): Add hypersparc and sparclite86x.
(function_unit): Add hypersparc scheduling rules.
* configure.in (with_cpu handler): Recognize hypersparc.
Thu Jan 7 23:54:05 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c: Added space after negation operator.
* config/c4x/c4x.h: Likewise.
* config/c4x/c4x.md: Likewise.
Thu Jan 7 23:39:27 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_preferred_reload_class): Always return class.
Thu Jan 7 00:29:25 1999 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* combine.c (num_sign_bit_copies): In NEG, MULT, DIV and MOD cases,
when a test can't be performed due to limited width of
HOST_BITS_PER_WIDE_INT, use the more conservative approximation.
Fix UDIV case for cases where the first operand has the highest bit
set.
Thu Jan 7 00:01:38 1999 Lutz Vieweg <lkv@mania.robin.de>
* pa.h (reg_class): Add FPUPPER_REGS.
(REG_CLASS_NAMES): Similarly.
(REG_CLASS_CONTENTS): Similarly.
(REGNO_REG_CLASS): Handle FPUPPER_REGS.
(FP_REG_CLASS_P): Likewise.
(REG_CLASS_FROM_LETTER): Similarly.
(CLASS_MAX_NREGS): Similarly.
1999-01-06 Brendan Kehoe <brendan@cygnus.com>
* fixincludes: For HP/UX 10.20, also look in curses_colr/curses.h
for a typedef of bool. Make sure to have a copy of the file is
in place before we look to fix it. Fix typo in variable name to
FILE.
Wed Jan 6 07:51:05 1999 Richard Henderson <rth@cygnus.com>
* expr.c (expand_builtin) [case BUILT_IN_CONSTANT_P]: Use
value_mode for the return mode.
Wed Jan 6 17:55:19 1999 Robert Lipe <robertlipe@usa.net>
* configure.in: New flag --with-dwarf2. If set, enables DWARF-2
debugging as default.
* config/tm-dwarf2.h: New file.
Wed Jan 6 16:08:54 1999 Jeffrey A Law (law@cygnus.com)
* h8300.h (ASM_OUTPUT_LABELREF): Define.
* pa.h (DONT_RECORD_EQUIVALENCE): Kill.
* local-alloc.c (update_equiv_regs): Corresponding changes.
* tm.texi (DONT_RECORD_EQUIVALENCE): Kill.
* calls.c (special_function_p): Push alloca test inside the large
conditional which excludes functions not at file scope or not
extern.
* calls.c (special_function_p): New function broken out of
expand_call.
(precompute_register_parameters): Likewise.
(store_one_arg): Likewise.
(store_unaligned_argumetns_into_pseudos): Likewise.
(save_fixed_argument_area): Likewise.
(restore_fixed_argument_area): Likewise.
(expand_call): Corresponding changes.
Thu Jan 7 00:12:24 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (addqi3): If the destination operand is
a hard register other than an extended precision register,
emit addqi3_noclobber.
(*addqi3_noclobber_reload): New pattern added so that reload
will recognize a store of a pseudo, equivalent to the sum
of the frame pointer and a constant, as an add insn.
1999-01-06 Manfred Hollstein <manfred@s-direktnet.de>
* fixinc/fixincl.c: Re-indent according to the GNU standards.
fixinc/server.c: Likewise.
fixinc/server.h: Likewise.
Wed Jan 6 10:43:29 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* config/m68k/m68k.c (const_uint32_operand): Remove CONSTANT_P_RTX
handling.
(const_sint32_operand): Likewise.
Wed Jan 6 09:44:51 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* toplev.h: In addition to checking _JBLEN, also check if `setjmp'
is a macro when deciding if we can use `jmp_buf' in prototypes.
Wed Jan 6 03:18:53 1999 Mark Elbrecht <snowball3@usa.net>
* configure.in (pc-msdosdjgpp): Set x_make to x-go32.
* configure: Rebuilt.
* i386/xm-go32.h: Define LIBSTDCXX.
* i386/x-go32: New.
* i386/go32.h (MD_EXEC_PREFIX): Define.
(FILE_NAME_ABSOLUTE_P): Define.
(LINK_COMMAND_SPEC): Define.
Wed Jan 6 02:23:36 1999 "Charles M. Hannum" <root@ihack.net>
* expr.c (store_expr): If the lhs is a memory location pointed
to be a postincremented (or postdecremented) pointer, always
force the rhs to be evaluated into a pseudo.
Wed Jan 6 00:54:21 1999 Geoff Keating <geoffk@ozemail.com.au>
* real.c (mtherr): Print more reasonable warning messages.
Tue Jan 5 21:57:42 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (gcc.o, prefix.o, cccp.o, cpplib.o): Depend on prefix.h.
* cccp.c: Include prefix.h, don't prototype prefix.c functions.
(new_include_prefix): Constify char* parameters.
* cppfiles.c (read_name_map): Likewise.
(append_include_chain): Likewise. Also, use a writable char* copy
of parameter `dir' which we then modify, rather than using the
parameter itself to store the new writable string.
(remap_filename): Constify some variables. Also, use a writable
char* to store an allocated string which we will be modifying.
* cpplib.c: Include prefix.h, don't prototype prefix.c functions.
(cpp_start_read): Constify variable `str'.
* cpplib.h (append_include_chain): Constify a char* parameter.
* gcc.c Include prefix.h, don't prototype prefix.c functions.
(add_prefix, save_string): Constify char* parameters.
(fatal, error): Add ATTRIBUTE_PRINTF_1 to prototypes.
* prefix.c: Include prefix.h.
(get_key_value, translate_name, save_string, update_path,
set_std_prefix): Constify various char* parameters and variables.
(save_string): Use xmalloc, not malloc.
(translate_name): Use a writable temporary variable to create and
modify a string before setting it to a const char*.
* prefix.h: New file to prototype functions exported from prefix.c.
Tue Jan 5 08:52:18 1999 Bruce Korb (korb@datadesign.com)
* fixinc/fixincl.c (various): Added debug code so
Manfred can trace the processing.
* fixinc/inclhack.def (sys/utsname.h): Provide forward declaration of
struct utsname on Ultrix V4.[35].
* fixinc/{fixincl.x|fixincl.sh|inclhack.sh} : Regenerated.
Mon Jan 4 15:37:30 1999 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (skip_if_group): Split out the logic that handles
directive recognition to its own function. Don't use
parse markers; use a bare pointer into the buffer. Use
copy/skip_rest_of_line instead of doing it by hand. Remove
`return on any directive' mode which was never used, and take
only one argument.
(consider_directive_while_skipping): New function, subroutine
of skip_if_group. Logic streamlined a bit.
(conditional_skip, do_elif, do_else): Call skip_if_group with
only one argument.
Mon Jan 4 15:27:30 1999 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (do_undef): EOF immediately after '#undef FOO' is not an
error.
Mon Jan 4 11:55:51 1999 Jason Merrill <jason@yorick.cygnus.com>
* extend.texi (Bound member functions): Document.
Mon Jan 4 11:01:48 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* mips-tdump.c (st_to_string, sc_to_string, glevel_to_string,
lang_to_string, type_to_string): Make return type const char*.
(print_symbol): Apply `const' keyword to a char*.
(print_file_desc): Cast structure member `crfd' to ulong when
comparing against one.
* mips-tfile.c (pfatal_with_name): Apply `const' keyword to char*.
(fatal, error): Add ATTRIBUTE_PRINTF_1 to prototypes.
(progname, input_name): Apply `const' keyword to a char*.
Don't redundantly include sys/stat.h.
(alloc_info): Apply `const' keyword to a char*.
(st_to_string, sc_to_string): Likewise.
(hash_string): Cast variable `hash_string' to a symint_t when
comparing against one.
(add_string): Cast PAGE_USIZE to Ptrdiff_t when comparing against one.
Likewise cast it to long when comparing against one.
(add_local_symbol): Apply `const' keyword to a char*.
(add_ext_symbol): Likewise.
(add_unknown_tag): Likewise.
(add_procedure): Cast a printf-style field width to an int.
(add_file): Cast PAGE_USIZE to long when comparing against one.
(parse_begin): Cast a printf-style field width to an int.
(parse_bend): Likewise.
(parse_def): Likewise.
(parse_end): Likewise.
(mark_stabs): Mark parameter `start' with ATTRIBUTE_UNUSED.
(parse_stabs_common): Fix format specifier.
(parse_input): Change type of variable `i' to Size_t.
(write_object): Fix arguments to match format specifiers.
Cast variable `num_write' to long when comparing against one.
(read_seek): Cast variable `sys_read' to symint_t when comparing
against one. Fix arguments to match format specifiers. Cast
variable `size' to long when comparing against one.
(copy_object): Cast result of `sizeof' to int when comparing
against one. Fix arguments to match format specifiers. Cast
variable `ifd' to long when comparing against a signed value.
Likewise, likewise.
Mon Jan 4 10:30:33 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-common.c (decl_attributes): Allow applying attribute `unused'
on a LABEL_DECL.
* c-parse.in (label): Parse attributes after a label, and call
`decl_attributes' to handle them.
* gansidecl.h (ATTRIBUTE_UNUSED_LABEL): Define.
* genrecog.c (OUTPUT_LABEL, write_tree_1, write_tree): When
generating labels, mark them with ATTRIBUTE_UNUSED_LABEL.
* invoke.texi: Note that labels can be marked `unused'.
Sun Jan 3 23:32:18 1999 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Jan 3 23:00:42 1999 Jeffrey A Law (law@cygnus.com)
* optabs.c (emit_cmp_and_jump_insns): Use CONSTANT_P canonicalizing
RTL for a compare/jump sequence.
Sun Jan 3 22:58:15 1999 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* optabs.c (emit_cmp_insn): Abort if asked to emit non-canonical RTL
for a target with HAVE_cc0 defined.
(emit_cmp_and_jump_insns): New function.
* expr.h (emit_cmp_and_jump_insns): Prototype it.
* loop.c (check_dbra_loop): Use it to replace calls
to emit_cmp_insn and emit_jump_insn and to canonicalize
the comparison if necessary.
* unroll.c (unroll_loop): Likewise.
Sun Jan 3 21:01:04 1999 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* fixincludes (sys/utsname.h): Provide forward declaration of
struct utsname on Ultrix V4.[35].
* mips.md (div_trap): Use local labels instead of dot-relative
branches.
Sun Jan 3 20:40:34 1999 Jeffrey A Law (law@cygnus.com)
* pa.md (branch, negated branch): Handle (const_int 0) as first
source operand.
* pa.c (output_cbranch): Likewise.
Sun Jan 3 03:20:38 1999 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (rs6000_stack_info): Undo spurious part of last
change.
1999-01-01 Manfred Hollstein <manfred@s-direktnet.de>
* extend.texi (__builtin_constant_p): Add missing @smallexample.
Fri Jan 1 11:48:20 1999 Jeffrey A Law (law@cygnus.com)
* i386.md (doubleword shifts): Fix dumb mistakes in previous change.
Wed Dec 30 23:38:55 1998 Jeffrey A Law (law@cygnus.com)
* m68k.md (adddi_dilshr32): Allow all operands to be registers too.
(adddi_dishl32): Similarly.
* cse.c (invalidate_skipped_block): Call invalidate_from_clobbers
for each insn in the skipped block.
* reload1.c (reload_as_needed): Verify that the insn satisfies its
constraints after replacing a register address with an autoincrement
address for reload inheritance purposes.
* i386.md (doubleword shifts): Avoid namespace pollution.
Wed Dec 30 23:00:28 1998 David O'Brien <obrien@NUXI.com>
* configure.in (FreeBSD ELF): Needs special crt files.
Wed Dec 30 22:50:13 1998 Geoffrey Noer <noer@cygnus.com>
* i386/xm-cygwin.h: Change DIR_SEPARATOR to forward slash.
1998-12-30 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* loop.c (check_dbra_loop): While reversing the loop, if the
comparison value has a VOID mode use the mode of the other operand
to compute the mask.
Wed Dec 30 22:24:00 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.md ({save,restore}_stack_function): Take 2 operands to
avoid warnings in compiling explow.c.
(patch from Ken Raeburn, raeburn@cygnus.com)
* rs6000.c (rs6000_stack_info): Force 8-byte alignment of
fpmem_offset. Compute total size after that, and then
rs6000_fpmem_offset using both values.
Mon Dec 28 19:26:32 1998 Gerald Pfeifer <pfeifer@dbai.tuwien.ac.at>
* gcc.texi (Non-bugs): ``Empty'' loops will be optimized away in
the future; indeed that already happens in some cases.
Tue Dec 29 11:58:53 1998 Richard Henderson <rth@cygnus.com>
* sparc.c (input_operand): Recognize (const (constant_p_rtx)).
(arith_operand): Remove constant_p_rtx handling.
(const64_operand, const64_high_operand): Likewise.
(arith11_operand, arith10_operand, arith_double_operand): Likewise.
(arith11_double_operand, arith10_double_operand, small_int): Likewise.
(small_int_or_double, uns_small_int, zero_operand): Likewise.
* sparc.h (PREDICATE_CODES): Likewise.
* rtl.h (CONSTANT_P): Remove CONSTANT_P_RTX.
Tue Dec 29 11:32:54 1998 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
* rtl.def (CONSTANT_P_RTX): Clarify commentary.
* expr.c (expand_builtin, case BUILT_IN_CONSTANT_P): Rework to
consider constant CONSTRUCTOR constant and to defer some cases
to cse.
* cse.c (fold_rtx, case CONST): Add handling for CONSTANT_P_RTX.
* regclass.c (reg_scan_mark_refs, case CONST): Likewise.
Tue Dec 29 11:30:10 1998 Richard Henderson <rth@cygnus.com>
* expr.c (init_expr_once): Kill can_handle_constant_p recognition.
* cse.c (fold_rtx, case 'x'): Remove standalone CONSTANT_P_RTX code.
* alpha.c (reg_or_6bit_operand): Remove CONSTANT_P_RTX handling.
(reg_or_8bit_operand, cint8_operand, add_operand): Likewise.
(sext_add_operand, and_operand, or_operand): Likewise.
(reg_or_cint_operand, some_operand, input_operand): Likewise.
* alpha.h (PREDICATE_CODES): Likewise.
Sat Dec 26 23:26:26 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Dec 26 09:17:04 1998 Jeffrey A Law (law@cygnus.com)
* gengenrtl.c (gencode): Always use bzero to clear memory instead
of dangerous casts and stores.
* Makefile.in (compare, gnucompare): Add missing else true clauses.
Fri Dec 25 23:00:56 1998 Jeffrey A Law (law@cygnus.com)
* alpha.md (builtin_longjmp): Add missing "DONE".
Thu Dec 24 10:39:57 1998 Stan Cox <scox@cygnus.com>
* gcc.c (execute): Enable -pipe with win32.
Wed Dec 23 10:27:44 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/t-arm-elf: Add multiplib option for leading
underscores.
* config/arm/thumb.h (ASM_OUTPUT_LABELREF): Use variable
'user_label_prefix' rather than macro USER_LABEL_PREFIX.
(thumb_shiftable_const): Use macro 'BASE_REG_CLASS' rather
than variable 'reload_address_base_reg_class'. [Note this
change is unrelated to the others in this patch].
* config/arm/unknown-elf.h (USER_LABEL_PREFIX): Default to no
leading underscore.
Wed Dec 23 09:51:32 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* alias.c (record_alias_subset): Remove ignored `&'.
(init_alias_once): Likewise.
* c-lex.c (UNGETC): Cast first argument of comma expression to void.
* config/mips/mips.c (mips_asm_file_end): Cast the result of
fwrite to `int' when comparing against one.
* config/mips/mips.h (CAN_ELIMINATE): Add parens around && within ||.
(INITIAL_ELIMINATION_OFFSET): Add braces to avoid ambiguous `else'.
* cse.c (rehash_using_reg): Change type of variable `i' to
unsigned int.
* dwarf2out.c (initial_return_save): Cast -1 to unsigned before
assigning it to one.
* except.c (duplicate_eh_handlers): Remove unused variable `tmp'.
* final.c (final_scan_insn): Likewise for variable `i'.
(output_asm_insn): Cast a char to unsigned char when used as an
array index.
* gcse.c (compute_pre_ppinout): Cast -1 to SBITMAP_ELT_TYPE when
assigning it to one.
* loop.c (strength_reduce): Remove unused variables `count' and `temp'.
* recog.c (preprocess_constraints): Cast a char to unsigned char
when used as an array index.
* regmove.c (find_matches): Likewise.
* reload1.c (calculate_needs): Add default case in switch.
(eliminate_regs_in_insn): Initialize variable `offset'.
(set_offsets_for_label): Change type of variable `i' to unsigned.
(reload_as_needed): Wrap variable `i' in macro check on
AUTO_INC_DEC || INSN_CLOBBERS_REGNO_P.
* scan-decls.c (scan_decls): Mark parameters `argc' and `argv'
with ATTRIBUTE_UNUSED. Cast variable `start_written' to size_t
when comparing against one.
* stor-layout.c (layout_decl): Cast maximum_field_alignment to
unsigned when comparing against one. Likewise for
GET_MODE_ALIGNMENT().
(layout_record): Cast record_align to int when comparing against a
signed value.
(layout_type): Cast TYPE_ALIGN() to int when comparing against a
signed value.
* tree.c (get_identifier): Cast variable `len' to unsigned when
comparing against one.
(maybe_get_identifier): Likewise
Wed Dec 23 00:10:01 1998 Jeffrey A Law (law@cygnus.com)
* toplev.c (rest_of_compilation): Do not set reload_completed.
* reload1.c (reload): Set reload_completed before calling
cleanup_subreg_operands.
Tue Dec 22 23:58:31 1998 Richard Henderson <rth@cygnus.com>
* reload1.c (emit_reload_insns): Check `set' not null before use.
Tue Dec 22 15:15:45 1998 Nick Clifton <nickc@cygnus.com>
* rtlanal.c (multiple_sets): Change type of 'found' from 'rtx' to
'int'.
Tue Dec 22 13:55:44 1998 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* halfpic.c (half_pic_encode): Delete redundant code.
Tue Dec 22 13:02:22 1998 Michael Meissner <meissner@cygnus.com>
* toplev.c (main): Delete handling of -dM as a preprocessor
option.
Mon Dec 21 17:39:38 1998 Michael Meissner <meissner@cygnus.com>
* toplev.c (main): Don't emit any warnings when using -dD, -dM, or
-dI, which are handled by the preprocessor.
Sun Dec 20 16:13:44 1998 John F. Carr <jfc@mit.edu>
* configure.in: Handle Digital UNIX 5.x the same as 4.x.
* i386/sol2.h: Define LOCAL_LABEL_PREFIX as ".".
Sun Dec 20 07:39:52 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Dec 19 22:24:22 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Dec 19 21:41:32 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Dec 19 09:52:27 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* genattr.c (fatal): Qualify a char* with the `const' keyword.
* genattrtab.c (fatal, attr_printf, attr_string, write_attr_set,
write_unit_name, write_eligible_delay, expand_units,
make_length_attrs, write_attr_case, find_attr,
make_internal_attr): Likewise.
* gencheck.c (tree_codes): Likewise.
* gencodes.c (fatal): Likewise.
* genconfig.c (fatal): Likewise.
* genemit.c (fatal): Likewise.
* genextract.c (fatal, walk_rtx, copystr): Likewise.
* genflags.c (fatal): Likewise.
* genopinit.c (fatal, optabs, gen_insn): Likewise.
* genoutput.c (fatal, error, predicates): Likewise.
* genpeep.c (fatal): Likewise.
* genrecog.c (fatal, decision, pred_table, add_to_sequence,
write_tree_1, write_tree, change_state, copystr, indents): Likewise.
Thu Dec 17 18:21:49 1998 Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
* configure.in (with-fast-fixincludes): Fix whitespace.
* configure: Rebuilt.
* fixincludes (c_asm.h): Wrap Digital UNIX V4.0B DEC C specific
asm() etc. function declarations in __DECC.
Thu Dec 17 13:57:23 1998 Nick Clifton <nickc@cygnus.com>
* expr.c (emit_move_insn_1): Only emit a clobber if the target
is a pseudo register.
Thu Dec 17 13:50:29 1998 Nick Clifton <nickc@cygnus.com>
* gcse.c: Include expr.h in order to get the prototype for
get_condition() which is used in delete_null_pointer_checks().
Thu Dec 17 15:58:26 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* hwint.h: New file to consolidate HOST_WIDE_INT (etc) macros.
Thu Dec 17 12:31:12 1998 Jim Wilson <wilson@cygnus.com>
* Makefile.in (INTERNAL_CFLAGS): Add SCHED_CFLAGS.
(ALL_CFLAGS): Delete SCHED_CFLAGS.
1998-12-17 Vladimir N. Makarov <vmakarov@cygnus.com>
* config/i60/i960.md (extendqihi2): Fix typo (usage ',' instead of
';').
1998-12-17 Michael Tiemann <tiemann@axon.cygnus.com>
* i960.md (extend*, zero_extend*): Don't generate rtl that looks
like (subreg:SI (reg:SI N) 0), because it's wrong, and it hides
optimizations from the combiner.
Thu Dec 17 08:27:03 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (combine_givs_used_by_other): Don't depend on n_times_set.
Wed Dec 16 17:30:35 1998 Nick Clifton <nickc@cygnus.com>
* toplev.c (main): Disable optimize_size if a specific
optimization level is requested. Always set optimization
level to 2 if -Os is specified.
Wed Dec 16 16:33:04 1998 Dave Brolley <brolley@cygnus.com>
* objc/lang-specs.h: Pass -MD, -MMD and -MG to cc1obj if configured with
cpplib.
* cpplib.c (cpp_start_read): If in_fname is not initialized, try to
initialize it using fname.
1998-12-16 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (do_include): Treat #include_next in the
primary source file as #include plus warning. Treat
#include_next in a file included by absolute path as an
error. fp == CPP_NULL_BUFFER is a fatal inconsistency.
Wed Dec 16 12:28:54 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c: Don't define MIN/MAX anymore.
* cpplib.c: Likewise.
* machmode.h: Likewise.
* system.h: Provide definitions for MIN/MAX.
Tue Dec 15 23:47:42 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* fix-header.c: Don't define xstrdup here.
Wed Dec 16 05:11:04 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (consec_sets_giv): New argument last_consec_insn.
(strength_reduce): Provide / use it.
Wed Dec 16 17:24:07 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.h (loop_info): New field 'vtop'.
* loop.c (check_dbra_loop): Use loop_info->vtop rather than
scanning loop for vtop.
* unroll.c (subtract_reg_term, find_common_reg_term): New functions.
(loop_iterations): Use them to determine if loop has a constant
number of iterations. Set loop_info->vtop. Don't subtract
common reg term from initial_value and final_value if have a
do-while loop.
Tue Dec 15 13:49:55 1998 Jeffrey A Law (law@cygnus.com)
* mn10200.md (addsi3 expander): Use "nonmemory_operand" for operand 2.
* mn10300.md (bset, bclr): Operand 0 is a read/write operand.
* mn10200.md (abssf2, negsf2): New expanders.
* mn10300.md (absdf2, abssf2, negdf2, negsf2): New expanders.
Tue Dec 15 11:55:30 1998 Nick Clifton <nickc@cygnus.com>
* integrate.c (copy_rtx_and_substitute): If a SUBREG is
replaced by a CONCAT whose components do not have the same
mode as the original SUBREG, use a new SUBREG to restore the
mode.
* emit-rtl.c (subreg_realpart_p): Cope with subregs containing
multiword complex values.
1998-12-15 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cppalloc.c: Add xstrdup here.
* cpplib.h: Remove savestring prototype.
* cpplib.c: Remove savestring function. s/savestring/xstrdup/
throughout.
* cppfiles.c: s/savestring/xstrdup/ throughout.
1998-12-15 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c: Make all directive handlers read their own
arguments.
(struct directive): Remove last two arguments from FUNC
member prototype. Remove `command_reads_line' member
entirely.
(directive_table): Remove initializations of
command_reads_line flag. Pretty-print.
(eval_if_expression, do_define, do_line, do_include,
do_undef, do_error, do_pragma, do_ident, do_if, do_xifdef,
do_else, do_elif, do_sccs, do_assert, do_unassert,
do_warning): Take only two args.
(cpp_define): Call do_define with two args and the text to
define stuffed into a buffer.
(make_assertion): Call do_assert with two args.
(handle_directive): Call do_line with two args. Call
kt->func with two args. Remove command_reads_line
processing.
(do_define, do_undef, do_error, do_warning, do_pragma,
do_sccs): Read the rest of the line here.
(do_ident): Gobble rest of line, as cccp does.
(cpp_undef): New function.
(cpp_start_read): Call cpp_undef instead of do_undef.
1998-12-15 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpphash.h (union hash_value): Remove `keydef' member, add a
`struct hashnode *aschain' member for #assert.
* cpplib.c (struct tokenlist_list, struct
assertion_hashnode): Delete structure definitions.
(assertion_install, assertion_lookup, delete_assertion,
check_assertion, compare_token_lists, reverse_token_list,
read_token_list, free_token_list): Delete functions.
(parse_assertion): New function.
(cpp_cleanup): Don't destroy the assertion_hashtable.
(do_assert): Gut and rewrite. #assert foo (bar) places
entries for `#foo' and `#foo(bar)' in the macro hash table,
type T_ASSERT. The value union's `aschain' member is used
to chain all answers for a given predicate together.
(do_unassert): Also rewritten. Take an un-asserted
answer off the chain from its predicate and call
delete_macro on the hashnode, or walk a predicate chain
calling delete_macro on all the entries.
(cpp_read_check_assertion): Simply call parse_assertion to
get the canonical assertion name, and look that up in the
hash table.
* cpplib.h (ASSERTION_HASHNODE,ASSERTION_HASHSIZE,assertion_hashtab):
Removed.
* cpphash.c (install): Use bcopy instead of an explicit loop
to copy the macro name.
* cppexp.c (cpp_lex): Convert the result of
cpp_read_check_assertion to a `struct operation' directly;
don't go through parse_number.
Tue Dec 15 18:27:39 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.h (struct induction): Delete times_used member.
* loop.c (n_times_set): Rename to set_in_loop. Changed all users.
(n_times_used): Rename to n_times_set. Changed all users.
(scan_loop): Free reg_single_usage before strength reduction.
(record_giv, combine_givs): Remove handling of times_used member.
(combine_givs_used_once): Rename to:
(combine_givs_used_by_other) . Changed all callers.
Tue Dec 15 01:45:26 1998 Jason Merrill <jason@yorick.cygnus.com>
* dwarf2out.c (gen_struct_or_union_type_die): Check AGGREGATE_TYPE_P
instead of TREE_CODE_CLASS == 't'.
(gen_type_die): Likewise.
(scope_die_for): Ignore FUNCTION_TYPE "scopes".
Mon Dec 14 16:23:27 1998 Jim Wilson <wilson@cygnus.com>
* real.c (endian): Disable last change unless
HOST_BITS_PER_WIDE_INT is greater than 32.
Mon Dec 14 17:13:36 1998 Andrew MacLeod <amacleod@cygnus.com>
* output.h (force_data_section): New prototype.
* varasm.c (force_data_section): New function to force the
data section, regardless of what in_section thinks.
* dwarf2out.c (output_call_frame_info): Call force_data_section
since varasm may not realize we've changes sections.
Mon Dec 14 14:09:34 1998 Nick Clifton <nickc@cygnus.com>
* reload1.c (reload): Delete REG_RETVAL and REG_LIBCALL notes
after completing reload.
* rtl.texi: Document that REG_RETVAL and REG_LIBCALL are
deleted after reload.
Mon Dec 14 01:39:28 1998 Jeffrey A Law (law@cygnus.com)
* rtl.h (multiple_sets): Fix prototype.
* rtlanal.c (multiple_sets): Fix return type.
Sun Dec 13 12:43:58 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Dec 13 01:05:22 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
1998-12-13 Manfred Hollstein <manfred@s-direktnet.de>
* protoize.c (fputs): Wrap extern declaration in #ifndef fputs.
Sun Dec 13 00:24:14 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* rtl.h (recompute_reg_usage): Add second argument.
* flow.c (recompute_reg_usage): Likewise.
* toplev.c (rest_of_compilation): Supply second argument to
recompute_reg_usage.
* reload1.c (compute_use_by_pseudos): Allow reg_renumber[regno] < 0
after reload.
Sat Dec 12 23:39:10 1998 Jeffrey A Law (law@cygnus.com)
* m68k/t-m68kelf (MULTILIB_OPTIONS): Add mcpu32.
(MULTILIB_MATCHES): -m68332 now uses mcpu32 libraries, not m68000.
(MULTILIB_EXCEPTIONS): Don't build 68881 libraries for m68000,
mcpu32 or m5200.
* i386/next.h (ASM_OUTPUT_ALIGN): Use 0x90 for fill character.
* rtlanal.c (multiple_sets): New function.
* rtl.h (multiple_sets): Declare it.
* local-alloc.c (wipe_dead_reg): Use it.
* global.c (global_conflicts): Likewise.
Sat Dec 12 22:13:02 1998 Mark Mitchell <mark@markmitchell.com>
* global.c (record_conflicts): Don't use an array of shorts to
store an array of ints.
(global_conflicts): Likewise.
Sat Dec 12 16:49:24 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_expand_block_move): mode_for_size expects
bits, not bytes. Infer extra alignment from addressof.
1998-12-11 Michael Meissner <meissner@cygnus.com>
* rs6000/sysv4.h (ASM_OUTPUT_ALIGNED_LOCAL): Put small data in the
.sbss section, not .sdata.
1998-12-11 Manfred Hollstein <manfred@s-direktnet.de>
* cccp.c: Do not #include <sys/stat.h> here; this is already done
by "system.h".
* collect2.c: Likewise.
* cpplib.h: Likewise.
* gcc.c: Likewise.
* gcov.c: Likewise.
* getpwd.c: Likewise.
* protoize.c: Likewise.
* toplev.c: Likewise.
* cpplib.h (HOST_WIDE_INT): Get definition from "machmode.h"
and don't try to define it here.
* Makefile.in (cppmain.o): Depend on machmode.h.
(cpplib.o): Likewise.
(cpperror.o): Likewise.
(cppexp.o): Likewise.
(cppfiles.o): Likewise.
(cpphash.o): Likewise.
(cppalloc.o): Likewise.
(fix-header.o): Likewise.
(scan-decls.o): Likewise.
Fri Dec 11 11:02:49 1998 Stan Cox <scox@cygnus.com>
* sh.c (print_operand): Lookup interrupt_handler attribute instead
of relying on static variable.
* (calc_live_regs): Likewise.
* (sh_pragma_insert_attributes): Create interrupt_handler
attribute if a pragma was specified.
* (sh_valid_machine_decl_attribute): Don't set static flag.
* sh.h (PRAGMA_INSERT_ATTRIBUTES): New.
Fri Dec 11 12:56:07 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_combine): Use BASIC_BLOCK_LIVE_AT_START
to determine if a register is live at a jump destination.
Everything is dead at a BARRIER.
Thu Dec 10 16:02:06 1998 Jim Wilson <wilson@cygnus.com>
* cse.c (simplify_unary_operation): Sign-extend constants when
they have the most significant bit set for the target.
* real.c (endian): Sign-extend 32 bit output values on a 64 bit
host.
* m32r/m32r.c (m32r_expand_prologue): Store pretend_size in
HOST_WIDE_INT temporary before negating it.
* m32r/m32r.md (movsi_insn+1): Use ~0xffff instead of 0xffff0000.
Thu Dec 10 15:05:59 1998 Dave Brolley <brolley@cygnus.com>
* objc/objc-act.c (lang_init_options): Enclose cpplib related code in
#if USE_CPPLIB.
Thu Dec 10 13:39:46 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* collect2.h: New header file for prototypes.
* Makefile.in (collect2.o, tlink.o): Depend on collect2.h.
* collect2.c: Include collect2.h.
* tlink.c: Likewise.
Wed Dec 9 23:55:11 1998 Jeffrey A Law (law@cygnus.com)
* flow.c: Update some comments.
Wed Dec 9 15:29:26 1998 Dave Brolley <brolley@cygnus.com>
* objc/objc-act.c (cpp_initialized): Removed.
(lang_init_options): Initialize cpplib.
(lang_decode_option): Move initialization of cpplib to
lang_init_options.
* c-lang.c (parse_options,parse_in): Added.
(lang_init_options): Initialized cpplib here.
* c-decl.c (parse_options,cpp_initialized): Removed.
(c_decode_option): Move initialization of cpplib to
lang_init_options.
Wed Dec 9 19:36:57 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_combine, reload_combine_note_store):
Make STORE_RUID always valid.
(reload_combine): Check if BASE is clobbered too early.
Wed Dec 9 09:53:58 1998 Nick Clifton <nickc@cygnus.com>
* reload.c (find_reloads): Display the insn that cannot be
reloaded.
Wed Dec 9 12:15:26 1998 Dave Brolley <brolley@cygnus.com>
* cccp.c (create_definition): Fix end of buffer logic.
Wed Dec 9 10:15:45 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* except.c (duplicate_eh_handlers, rethrow_symbol_map): Function
pointer parameters changed to use the PARAMS() macro.
Wed Dec 9 09:12:40 1998 Andrew MacLeod <amacleod@cygnus.com>
* except.h (struct handler_info): Add handler_number field.
* except.c (gen_exception_label): EH labels no longer need to be
on the permanent obstack.
(get_new_handler): Set the label number field.
(output_exception_table_entry): Regenerate handler label reference
from the label number field.
(init_eh): Remove a blank line.
* integrate.c (get_label_from_map): Labels no longer need to be
on the permanent obstack.
Tue Dec 8 22:04:33 1998 Jim Wilson <wilson@cygnus.com>
* i960/i960.h (CONST_COSTS, case CONST_INT): Accept power2_operand
only when OUTER_CODE is SET.
Tue Dec 8 22:47:15 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (strength_reduce): If scan_start points to the loop exit
test, be wary of subversive use of gotos inside expression statements.
Don't set maybe_multiple for a backward jump that does not
include the label under consideration into its range.
* unroll.c (biv_total_increment): Make use of maybe_multiple field.
Tue Dec 8 22:33:18 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* explow.c (plus_constant_wide): Don't immediately return with
result of recursive call.
Tue Dec 8 15:32:56 1998 Andrew MacLeod <amacleod@cygnus.com>
* eh-common.h (struct eh_context): Add table_index for rethrows.
* rtl.h (enum reg_note): Add REG_EH_REGION and REG_EH_RETHROW reg notes.
(SYMBOL_REF_NEED_ADJUST): New flag indicating symbol needs to be
processed when inlined or unrolled (ie duplicated in some way).
* rtl.c (reg_note_name): Add strings for new reg_note enums.
* expr.h (rethrow_libfunc): New library decl.
* optabs.c (rethrow_libfunc): Initialize.
* except.h (struct eh_entry): Add new field 'rethrow_label'.
(new_eh_region_entry): No longer exported from except.c.
(duplicate_handlers): Renamed to duplicate_eh_handlers and
different prototype.
(rethrow_symbol_map, rethrow_used): New exported functions.
(eh_region_from_symbol): New exported function.
* except.c (create_rethrow_ref): New function to create a single
SYMBOL_REF for a rethrow region.
(push_eh_entry): Initialize a rethrow ref.
(func_eh_entry): Add a rethrow_label field.
(new_eh_region_entry): Make static, and initialize the rethrow entry.
(duplicate_eh_handlers): Create a new region, and remap labels/symbols.
(eh_region_from_symbol): Find an EH region based on its rethrow symbol.
(rethrow_symbol_map): Given a label map, maps a rethrow symbol for
a region into an appropriate new symbol.
(rethrow_used): Indicate whether a rethrow symbol has been referenced.
(expand_eh_region_end): Don't issue jump around code for new-exceptions.
(end_catch_handler): Emit a barrier for new-exceptions since
control can never drop through the end of a catch block.
(expand_end_all_catch): new-exceptions never fall through a catch
block.
(expand_rethrow): Use __rethrow routine for new exceptions.
(output_exception_table_entry): Generate rethrow labels, if needed.
(output_exception_table): Generate start and end rethrow labels.
(init_eh): Create rethrow symbols for beginning and end of table.
(scan_region): Don't eliminate EH regions which are the targets of
rethrows.
* flow.c (make_edges): Add different edges for rethrow calls,
identified by having the REG_EH_RETHROW reg label.
(delete_unreachable_blocks): Don't delete regions markers which are
the target of a rethrow.
* integrate.c (save_for_inline_eh_labelmap): New callback routine to
allow save_for_inline_copying to call duplicate_eh_handlers.
(save_for_inline_copying): Call duplicate_eh_handlers instead of
exposing internal details of exception regions.
(copy_for_inline): Check if SYMBOL_REFs need adjustment.
(expand_inline_function_eh_labelmap): New callback routine to
allow expand_inline_function to call duplicate_eh_handlers.
(expand_inline_function): Call duplicate_eh_handlers instead of
exposing internal details of exception regions.
(copy_rtx_and_substitute): Adjust SYMBOL_REFS if SYMBOL_REF_NEED_ADJUST
flag is set.
* libgcc2.c (find_exception_handler): Generalize to enable it to
pick up processing where it left off last time for a rethrow.
(__unwinding_cleanup): New function. debug hook which is called before
unwinding when __throw finds there is nothing but cleanups left.
(throw_helper): Common parts of __throw extracted out for reuse.
(__throw): Common parts moved to throw_helper.
(__rethrow): New function for performing rethrows.
Tue Dec 8 13:11:04 1998 Jeffrey A Law (law@cygnus.com)
* reload1.c (current_function_decl): Tweak declaration.
Tue Dec 8 10:23:52 1998 Richard Henderson <rth@cygnus.com>
* c-decl.c (flag_isoc9x): Default off.
(c_decode_option): Kill -std=gnu, add -std=gnu89 and -std=gnu9x.
* cccp.c (print_help, main): Likewise.
* gcc.c (default_compilers): Update for -std=gnu*.
Tue Dec 8 01:14:46 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (DEMANGLE_H): Change location to shared demangle.h.
* demangle.h: Deleted.
* reload1.c (current_function_decl): Declare.
Tue Dec 8 11:58:51 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cpplib.c (convert_string): Use `0x00ff', not `0x00ffU'.
Tue Dec 8 09:28:36 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* dbxout.c: If USG is defined use gstab.h, even if HAVE_STAB_H is set.
1998-12-08 Ulrich Drepper <drepper@cygnus.com>
* configure.in: Test for availability of putc_unlocked, fputc_unlocked,
and fputs_unlocked.
* configure: Rebuilt.
* system.h: If the *_unlocked functions are available use them
instead of the locked counterparts by defining macros.
* config.in: Regenerated.
Tue Dec 8 00:34:05 1998 Mike Stump <mrs@wrs.com>
* i386/bsd.h (ASM_FILE_START): Don't use dump_base_name, it is
wrong and should only be used for dump related things, not
debugging information, instead main_input_filename should be used.
Also, reuse output_file_directive if possible.
* i386/aix386ng.h (ASM_FILE_START): Likewise.
* i386/isc.h (ASM_FILE_START): Likewise.
* i386/win-nt.h (ASM_FILE_START): Likewise.
* i386/sun386.h (ASM_FILE_START): Likewise.
Mon Dec 7 23:56:28 1998 Robert Lipe <robertl@dgii.com>
* configure.in (mips*-*-linux*): Handle big and little endian
systems.
* configure: Rebuilt.
Mon Dec 7 23:14:51 1998 Mike Stump <mrs@wrs.com>
* emit-rtl.c: Fix typo.
Mon Dec 7 23:07:38 1998 Nathan Sidwell <nathan@acm.org>
* reload1.c (eliminate_regs): Don't do anything, if we're not
generating code.
Mon Dec 7 15:27:09 1998 DJ Delorie <dj@cygnus.com>
* mips/mips.h (ENCODE_SECTION_INFO): Handle TARGET_EMBEDDED_DATA.
Add comment.
* mips/mips.c (mips_select_section): Add comment.
Mon Dec 7 17:55:06 1998 Mike Stump <mrs@wrs.com>
* cccp.c (ignore_escape_flag): Add support for \ as `natural'
characters in file names in #line to be consistent with #include
handling. We support escape processing in the # 1 "..." version of
the command. See also support in cp/lex.c.
(handle_directive): Likewise.
(do_line): Likewise.
1998-12-07 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (initialize_char_syntax): Use ISALPHA and ISALNUM
so it'll work on non-ASCII platforms. Always consider $ an
identifier character. Take no arguments.
(cpp_reader_init): Call initialize_char_syntax with no
arguments.
(cpp_start_read): Don't call initialize_char_syntax again.
Clear is_idchar['$'] and is_idstart['$'] if not
opts->dollars_in_ident.
* cpplib.h (struct cpp_reader): Replace void *data element by
cpp_options *opts. Rearrange elements to make gdb printout
less annoying (put buffer stack at end).
(CPP_OPTIONS): Get rid of now-unnecessary cast.
* cppmain.c: s/data/opts/ when initializing cpp_reader
structure.
* c-decl.c: Likewise.
* objc/objc-act.c: Likewise.
* fix-header.c: Likewise.
1998-12-07 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.h (struct cpp_buffer): Replace dir and dlen members
with a struct file_name_list pointer.
(struct cpp_reader): Add pointer to chain of `actual
directory' include searchpath entries.
(struct file_name_list): Add *alloc pointer for the sake of
the actual-directory chain.
Move definition of HOST_WIDE_INT here.
(cpp_parse_escape): Change prototype to match changes in
cppexp.c.
* cppfiles.c (actual_directory): New function.
(finclude): Use it to initialize the buffer's actual_dir
entry.
(find_include_file): We don't need to fix up max_include_len
here.
* cpplib.c (do_include): Don't allocate a file_name_list on
the fly for current directory "" includes, use the one that's
been preallocated in pfile->buffer->actual_dir. Hoist out
duplicate code from the search_start selection logic.
(cpp_reader_init): Initialize pfile->actual_dirs.
Remove definition of HOST_WIDE_INT. Change calls
to cpp_parse_escape to match changes in cppexp.c (note
hardcoded MASK, which is safe since this is the source
character set).
* cppexp.c: Bring over changes to cpp_parse_escape from cccp.c
to handle wide character constants in #if directives. The
function now returns a HOST_WIDE_INT, and takes a third
argument which is a binary mask for all legal values (0x00ff
for 8-bit `char', 0xffff for 16-bit `wchar_t', etc.) Define
MAX_CHAR_TYPE_MASK and MAX_WCHAR_TYPE_MASK. Change callers of
cpp_parse_escape to match. [Fixes c-torture/execute/widechar-1.c]
Mon Dec 7 15:38:25 1998 Dave Brolley <brolley@cygnus.com>
* gcc.c (default_compilers): Fix typo in USE_CPPLIB spec for cc1.
Mon Dec 7 15:38:25 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-aux-info.c (concat): Wrap function definition in !USE_CPPLIB.
* cppalloc.c: Move function `xcalloc' from cpplib.c to here.
* cpplib.c: Move function `xcalloc' from here to cppalloc.c.
Mon Dec 7 11:30:49 1998 Nick Clifton <nickc@cygnus.com>
* final.c (output_asm_name): Use tabs to separate comments from
assembly text.
Include instruction lengths (if defined) in output.
Mon Dec 7 10:53:38 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.c (check_dbra_loop): Fix initial_value and initial_equiv_value
in the loop_info structure.
Mon Dec 7 11:04:40 1998 Catherine Moore <clm@cygnus.com>
* configure.in (arm*-*-ecos-elf): New target.
* configure: Regenerated.
* config/arm/elf.h (ASM_WEAKEN_LABEL): Define.
* config/arm/ecos-elf.h: New file.
* config/arm/unknown-elf.h (TARGET_VERSION): Check
for redefinition.
Mon Dec 7 16:15:51 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.c (output_far_jump): Emit braf only for TARGET_SH2.
Sun Dec 6 04:19:45 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Dec 6 05:16:16 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.c (check_dbra_loop): New argument loop_info. Update fields
as needed.
Sun Dec 6 03:40:13 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Dec 6 07:49:29 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* gcc.texi (Bug Reporting): 40Kb is a soft limit, larger
compressed reports are ok and preferred over URLs.
Sun Dec 6 07:45:33 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* invoke.texi (Warning Options): Soften the tone of -pedantic.
Sun Dec 6 00:20:44 1998 H.J. Lu (hjl@gnu.org)
* print-rtl.c (print_rtx): Add prototype.
* unroll.c (iteration_info): Make it static.
Sun Dec 6 01:19:46 1998 Richard Henderson <rth@cygnus.com>
* alias.c (memrefs_conflict_p): A second ANDed address
disables the aligned address optimization.
Sat Dec 5 18:48:25 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_emit_set_const_1): Fix parenthesis error
in -c << n case.
Sat Dec 5 15:14:52 1998 Jason Merrill <jason@yorick.cygnus.com>
* i960.h (BOOL_TYPE_SIZE): Define.
Sun Dec 6 00:28:16 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (valid_parallel_load_store): Flog functionality
from old valid_parallel_operands_4.
(valid_parallel_operands_4): Check that operands for 4 operand
parallel insns are valid, excluding load/store insns.
* config/c4x/c4x.h (valid_parallel_load_store): Add prototype.
* config/c4x/c4x.md (*movqf_parallel, *movqi_parallel): Use
valid_parallel_load_store instead of valid_parallel_operands_4.
(*absqf2_movqf_clobber, *floatqiqf2_movqf_clobber,
*negqf2_movqf_clobber, *absqi2_movqi_clobber,
*fixqfqi2_movqi_clobber, *negqi2_movqi_clobber,
*notqi_movqi_clobber): Use valid_parallel_operands_4.
(*subqf3_movqf_clobber, *ashlqi3_movqi_clobber,
*ashrqi3_movqi_clobber, *lshrqi3_movqi_clobber,
*subqi3_movqi_clobber): Use valid_parallel_operands_5.
Sat Dec 5 23:52:01 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (iteration_info): Delete extern.
Fri Dec 4 20:15:57 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* tm.texi (SMALL_REGISTER_CLASSES): Make description match reality.
* final.c (cleanup_subreg_operands): Delete some unused code.
* recog.h (MAX_RECOG_ALTERNATIVES): New macro.
(struct insn_alternative): New structure definition.
(recog_op_alt): Declare variable.
(preprocess_constraints): Declare function.
* recog.c (recog_op_alt): New variable.
(extract_insn): Verify number of alternatives is in range.
(preprocess_constraints): New function.
* reg-stack.c: Include recog.h.
(constrain_asm_operands): Delete.
(get_asm_operand_lengths): Delete.
(get_asm_operand_n_inputs): New function.
(record_asm_reg_life): Delete OPERANDS, CONSTRAINTS, N_INPUTS and
N_OUTPUTS args. All callers changed.
Compute number of inputs and outputs here by calling
get_asm_operand_n_inputs.
Instead of constrain_asm_operands, call extract_insn,
constrain_operands and preprocess_constraints. Use information
computed by these functions throughout.
(record_reg_life): Delete code that is unused due to changes in
record_asm_reg_life.
(subst_asm_stack_regs): Delete OPERANDS, OPERAND_LOC, CONSTRAINTS,
N_INPUTS and N_OUTPUTS args. All callers changed.
Similar changes as in record_asm_reg_life.
(subst_stack_regs): Move n_operands declaration into the if statement
where it's used.
Delete code that is unused due to changes in subst_asm_stack_regs.
* stmt.c (expand_asm_operands): Verify number of alternatives is in
range.
* Makefile.in (reg-stack.o): Depend on recog.h.
Fri Dec 4 02:23:24 1998 Jeffrey A Law (law@cygnus.com)
* except.c (set_exception_version_code): Argument is an "int".
Fri Dec 4 01:29:28 1998 Jeffrey A Law (law@cygnus.com)
* configure.in (hppa2*-*-*): Handle like hppa1.1-*-* for now.
* configure: Rebuilt.
Fri Dec 4 01:29:28 1998 Robert Lipe <robertl@dgii.com>
* configure.in (mipsel-*-linux*): New target.
* mips/linux.h: New file, based on other Linux targets.
Thu Dec 3 11:19:50 1998 Mike Stump <mrs@wrs.com>
* gthr-vxworks.h (__ehdtor): Fix memory leak. The delete hook
runs in the context of the deleter, not the deletee, so we must
use taskVarGet to find the correct memory to free.
(__gthread_key_create): Initialize the task
variable subsystem so that the task variable is still active when
the delete hook is run.
1998-12-03 Joseph S. Myers <jsm28@cam.ac.uk>
* pdp11.h: Use optimize_size for space optimizations.
* pdp11.c: Likewise.
* pdp11.md: Likewise.
* pdp11.h (TARGET_40_PLUS): Fix typo.
Thu Dec 3 11:48:32 1998 Jeffrey A Law (law@cygnus.com)
* local-alloc.c (block_alloc): Slightly retune heuristic to widen
qty lifetimes.
Thu Dec 3 22:30:18 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* alias.c (addr_side_effect_eval): New function.
(memrefs_conflict_p): Use it.
* rtl.h (addr_side_effect_eval): Prototype it.
1998-12-02 Joseph S. Myers <jsm28@cam.ac.uk>
* pdp11.md (extendsfdf2): Fix mode mismatch in SET.
Wed Dec 2 11:23:07 1998 Jim Wilson <wilson@cygnus.com>
* reload.c (find_reloads): When force const to memory, put result
in substed_operand not *recog_operand_loc.
1998-12-02 Ulrich Drepper <drepper@cygnus.com>
* c-lex.c: Fix indentation from last patch.
Remove trailing whitespace.
* real.c: Likewise.
Wed Dec 2 10:11:12 1998 Jeffrey A Law (law@cygnus.com)
* flow.c (delete_block): Call set_last_insn after we have reset
NEXT_INSN (kept_tail).
Wed Dec 2 00:47:31 1998 Jeffrey A Law (law@cygnus.com)
* mips.md (trap_if): Use "$0" for the value zero.
Tue Dec 1 20:49:49 1998 Ulrich Drepper <drepper@cygnus.com>
Stephen L Moshier <moshier@world.std.com>
Richard Henderson <rth@cygnus.com>
* c-common.c (declare_function_name): Declare predefined variable
`__func__'.
* c-decl.c (flag_isoc9x): Set to 1 by default.
(c_decode_option): Handle -std= option. Remove -flang-isoc9x.
(grokdeclarator): Always emit warning about implicit int for ISO C 9x.
* c-parse.in: Allow constructors in ISO C 9x.
Rewrite designator list handling.
Allow [*] parameters.
Don't warn about comma at end of enum definition for ISO C 9x.
* cccp.c (c9x): New variable.
(rest_extension): New variable.
(print_help): Document new -std= option.
(main): Recognize -std= option. Set c9x appropriately.
(create_definition): Recognize ISO C 9x vararg macros.
* gcc.c (default_compilers): Adjust specs for -std options.
(option_map): Add --std.
(display_help): Document -std.
* toplev.c (documented_lang_options): Add -std and remove
-flang-isoc9x.
* c-lex.c (yylex): Recognize hex FP constants and call REAL_VALUE_ATOF
or REAL_VALUE_HTOF based on base of the constants.
* fold-const.c (real_hex_to_f): New function. Replacement function
for hex FP conversion if REAL_ARITHMETIC is not defined.
* real.c (asctoeg): Add handling of hex FP constants.
* real.h: Define REAL_VALUE_HTOF if necessary using ereal_atof or
real_hex_to_f.
Tue Dec 1 16:45:49 1998 Stan Cox <scox@cygnus.com>
* mips.md (divmodsi4*, divmoddi4*, udivmodsi4*, udivmoddi4): Add
-mcheck-range-division/-mcheck-zero-division checking. Avoid as macro
expansion. Use hi/lo as destination register.
(div_trap): New.
(divsi3*, divdi3*, modsi3*, moddi3*, udivsi3*, udivdi3*, umodsi3*,
umoddi3*): Add -mcheck-range-division/-mcheck-zero-division checking.
Avoid as macro expansion. Use hi/lo as destination register.
* mips.h (MASK_CHECK_RANGE_DIV): New.
(MASK_NO_CHECK_ZERO_DIV): New.
(ELIMINABLE_REGS): Added GP_REG_FIRST + 31.
(CAN_ELIMINATE, INITIAL_ELIMINATION_OFFSET): Allow for getting
return address for leaf functions out of r31 to support
builtin_return_address.
Tue Dec 1 15:03:30 1998 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* jump.c (jump_optimize): Call regs_set_between_p with PREV_INSN(x),
NEXT_INSN(x) to check insn x.
Tue Dec 1 15:20:44 1998 Jeffrey A Law (law@cygnus.com)
* flow.c (delete_block): Call set_last_insn if we end up deleting the
last insn in the rtl chain.
* reload1.c (reload): Do not set reload_completed or split insns
here. Instead...
* toplev.c (rest_of_compilation): Set reload_completed after
reload returns. Split insns after reload_cse has run.
Tue Dec 1 11:55:04 1998 Richard Henderson <rth@cygnus.com>
* final.c (final_scan_insn): Abort if block_depth falls below 0.
Tue Dec 1 10:23:16 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/t-arm-elf (LIBGCC2_CFLAGS): Define inhibit_libc.
Tue Dec 1 10:22:18 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/unknown-elf.h (ASM_OUTPUT_DWARF2_ADDR_CONST): Remove
use of user-label_prefix.
Tue Dec 1 17:58:26 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (emit_reload_insns): Clear spill_reg_store
when doing a new non-inherited reload from the same pseudo.
* local-alloc.c (function_invariant_p): New function.
(update_equiv_regs): Use function_invariant_p instead of CONSTANT_P
to decide if an equivalence should be recorded.
* reload1.c (num_eliminable_invariants): New static variable.
(reload): Set it. Use function_invariant_p instead of CONSTANT_P
to decide if an equivalence should be recorded.
Unshare PLUS.
(calculate_needs_all_insns): Skip insns that only set an equivalence.
Take num_eliminable_invariants into account when deciding
if register elimination should be done.
(reload_as_needed): Take num_eliminable_invariants into account
when deciding if register elimination should be done.
(eliminate_regs): Handle non-constant reg_equiv_constant.
* rtl.h (function_invariant_p): Declare.
Mon Nov 30 02:00:08 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Nov 30 00:42:59 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Nov 29 22:59:40 1998 Jason Merrill <jason@yorick.cygnus.com>
* except.c (add_new_handler): Complain about additional handlers
after one that catches everything.
Sat Nov 28 10:56:32 1998 Jeffrey A Law (law@cygnus.com)
* configure.in (alpha*-*-netbsd): Fix typo.
* configure: Rebuilt.
Fri Nov 27 12:28:56 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* system.h: Include libiberty.h.
* c-aux-info.c: Remove prototypes for concat/concat3. Change
function `concat' from fixed parameters to variable parameters,
as is done in libiberty. All callers of concat/concat3
changed to use the new `concat' with variable args.
* cccp.c: Remove things made redundant by libiberty.h and/or
conform to libiberty standards.
* cexp.y: Likewise.
* collect2.c: Likewise.
* config/1750a/1750a.h: Likewise.
* cppalloc.c: Likewise.
* cppexp.c: Likewise.
* cppfiles.c: Likewise.
* cpphash.c: Likewise.
* cpplib.c: Likewise.
* dyn-string.c: Likewise.
* fix-header.c: Likewise.
* gcc.c: Likewise.
* gcov.c: Likewise.
* genattr.c: Likewise.
* genattrtab.c: Likewise.
* gencheck.c: Likewise.
* gencodes.c: Likewise.
* genconfig.c: Likewise.
* genemit.c: Likewise.
* genextract.c: Likewise.
* genflags.c: Likewise.
* gengenrtl.c: Likewise.
* genopinit.c: Likewise.
* genoutput.c: Likewise.
* genpeep.c: Likewise.
* genrecog.c: Likewise.
* getpwd.c: Likewise.
* halfpic.c: Likewise.
* hash.c: Likewise.
* mips-tdump.c: Likewise. Wrap malloc/realloc/calloc prototypes
in NEED_DECLARATION_* macros.
* mips-tfile.c: Remove things made redundant by libiberty.h and/or
conform to libiberty standards.
(fatal): Fix const-ification of variable `format' in
!ANSI_PROTOTYPES case.
* prefix.c: Remove things made redundant by libiberty.h and/or
conform to libiberty standards.
* print-rtl.c: Rename variable `spaces' to `xspaces' to avoid
conflicting with function `spaces' from libiberty.
* profile.c: Remove things made redundant by libiberty.h and/or
conform to libiberty standards.
* protoize.c: Likewise.
* rtl.h: Likewise.
* scan.h: Likewise.
* tlink.c: Likewise.
* toplev.c: Likewise.
* toplev.h: Likewise.
* tree.h: Likewise.
Thu Nov 26 08:38:06 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cppfiles.c (simplify_pathname): Un-ANSI-fy function definition.
Thu Nov 26 23:45:37 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* README.C4X: Updated URLs.
* config/c4x/c4x.c (c4x_address_conflict): Fix typo.
(valid_parallel_operands_5): Remove unused variable.
Thu Nov 26 23:40:03 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (TARGET_DEFAULT): Fix typo.
1998-11-26 Manfred Hollstein <manfred@s-direktnet.de>
* Makefile.in (CONFIG_LANGUAGES): New macro taking all languages
which can be configured.
(LANGUAGES): Use $(CONFIG_LANGUAGES) instead of @all_languages@
(Makefile): Pass actual LANGUAGES through the environment when
re-configuring.
(cstamp-h): Likewise.
(config.status): Likewise.
* configure.in (enable_languages): Add new configuration parameter
"--enable-languages=lang1,lang2,...".
(${srcdir}/*/config-lang.in): Change handling to configure only
those directories, that the user might have enabled; default to
"all" existing languages.
* configure: Regenerate.
Thu Nov 26 00:19:19 1998 Richard Henderson <rth@cygnus.com>
* rtlanal.c (regs_set_between_p): New function.
* rtl.h (regs_set_between_p): Prototype it.
* jump.c (jump_optimize): Use it instead of modified_between_p
in the Sep 2 change.
Wed Nov 25 23:32:02 1998 Ian Dall <Ian.Dall@dsto.defence.gov.au>
Matthias Pfaller <leo@dachau.marco.de>
* invoke.texi (Option Summary, NS32K Options): Add description
of NS32K specific options.
* ns32k.md (tstdf, cmpdf, movdf, truncdfsf2, fixdfqi2, fixdfhi2,
fixdfsi2, fixunsdfqi2, fixunsdfhi2, fixunsdfsi2, fix_truncdfqi2,
fix_truncdfhi2, fix_truncdfsi2, adddf3, subdf3, muldf3, divdf3,
negdf2, absdf2): Use l instead of f since the double class and
float class are no longer the same.
(cmpsi, truncsiqi2, truncsihi2, addsi3, subsi3, mulsi3, umulsidi3,
divsi3, modsi3, andsi3, iorsi3, xorsi3, negsi2, one_cmplsi2,
ashlsi3, ashlhi3, ashlqi3, rotlsi3, rotlhi3, rotlqi3, abssi2,...):
Use "g" instead of "rmn" since LEGITIMATE_PIC_OPERAND has been
fixed.
(cmpsi, cmphi, cmpqi): Use general_operand instead of
non_immediate_operand. Removes erroneous assumption that can't
compare constants.
(movsf, movsi, movhi, movqi,...): New register numbering scheme.
(movsi, addsi3): Use NS32K_DISPLACEMENT_P instead of hard coded
constants.
(movstrsi, movstrsi1, movstrsi2): Completely new block move
scheme.
(...): Patterns to exploit multiply-add instructions.
(udivmodsi4, udivmodsi_internal4, udivmodhi4,
udivmoddihi4_internal, udivmodqi4, udivmoddiqi4_internal): New
patterns to exploit extended divide insns.
(udivsi3, udivhi3, udivqi3): Remove since superseded by udivmodsi
etc patterns.
* ns32k.h (FUNCTION_VALUE, LIBCALL_VALUE): Use f0 for complex
float return values as well as simple scalar floats.
(TARGET_32381, TARGET_MULT_ADD, TARGET_SWITCHES):
Support new flag to denote 32381 fpu.
(OVERRIDE_OPTIONS): 32381 is a strict superset of 32081.
(CONDITIONAL_REGISTER_USAGE): Disable extra 32381 registers if not
compiling for 32381.
(FIRST_PSEUDO_REGISTER, FIXED_REGISTERS, CALL_USED_REGISTERS,
REGISTER_NAMES, ADDITIONAL_REGISTER_NAMES, OUTPUT_REGISTER_NAMES,
REG_ALLOC_ORDER, DBX_REGISTER_NUMBER, R0_REGNUM, F0_REGNUM,
L1_REGNUM, STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM,
LONG_FP_REGS_P, ARG_POINTER_REGNUM, reg_class, REG_CLASS_NAMES,
REG_CLASS_CONTENTS, SUBSET_P,REGNO_REG_CLASS,
REG_CLASS_FROM_LETTER, FUNCTION_PROLOGUE, FUNCTION_EPILOGUE,
REGNO_OK_FOR_INDEX_P, FP_REG_P, REG_OK_FOR_INDEX_P,
REG_OK_FOR_BASE_P, MEM_REG): New register scheme to include 32381
fpu registers and special register classes for new 32381
instructions dotf and polyf.
(MODES_TIEABLE_P): Allow all integer modes, notably DI and SI, to
be tieable.
(INCOMING_RETURN_ADDR_RTX, RETURN_ADDR_RTX,
INCOMING_FRAME_SP_OFFSET): New macros in case DWARF support is
required.
(SMALL_REGISTER_CLASSES): Make dependent on -mmult-add option.
(MOVE_RATIO): Set to zero because of smart movstrsi implementation.
(REGISTER_MOVE_COST): Move code to register_move_cost function for
ease of coding and debugging.
(CLASS_LIKELY_SPILLED_P): Under new register scheme class
LONG_FLOAT_REGO is likely spilled but not caught by default
definition.
(CONSTANT_ADDRESS_P, CONSTANT_ADDRESS_NO_LABEL_P): Use macro
instead of hard coded numbers in range check.
(ASM_OUTPUT_LABELREF_AS_INT): Delete since unused.
(...): Add prototypes for functions in ns32k.c but disable because
of problems when ns32k.h is included in machine independent files.
* ns32k.c: Include "system.h", "tree.h", "expr.h", "flags.h".
(ns32k_reg_class_contents, regcass_map, ns32k_out_reg_names,
hard_regno_mode_ok, secondary_reload_class,
print_operand, print_operand_address): New register scheme to
include 32381 fpu registers and special register classes for new
32381 instructions dotf and polyf.
(gen_indexed_expr): Make static to keep namespace clean.
(check_reg): Remove since never called.
(move_tail, expand_block_move): Helper functions for "movstrsi"
block move insn.
(register_move_cost): Helper function for REGISTER_MOVE_COST macro.
Increase cost of moves which go via memory.
* netbsd.h (TARGET_DEFAULT): Set (new) 32381 fpu flag.
(CPP_PREDEFINES): No longer predefine "unix".
* ns32k.md (movsi, movsi, adddi3, subdi3, subsi3, subhi3, subqi3,...):
Remove erroneous %$. print_operand() can work out from the rtx is
an immediate prefix is required.
* ns32k.h (RETURN_POPS_ARGS, VALID_MACHINE_DECL_ATTRIBUTE,
VALID_MACHINE_TYPE_ATTRIBUTE, COMP_TYPE_ATTRIBUTES,
SET_DEFAULT_TYPE_ATTRIBUTES): Support for -mrtd calling
convention.
(LEGITIMATE_PIC_OPERAND_P, SYMBOLIC_CONST): Correct handling of
pic operands.
* ns32k.c (symbolic_reference_mentioned_p, print_operand):
Correct handling of pic operands.
(ns32k_valid_decl_attribute_p, ns32k_valid_type_attribute_p,
ns32k_comp_type_attributes, ns32k_return_pops_args): Support for
-mrtd calling convention.
Wed Nov 25 23:42:20 1998 Tom Tromey <tromey@cygnus.com>
* gcc.c (option_map): Recognize --output-class-directory.
Thu Nov 26 18:26:21 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.h (precondition_loop_p): Added new mode argument.
* unroll.c (precondition_loop_p): Likewise.
(approx_final_value): Function deleted and subsumed
into loop_iterations.
(loop_find_equiv_value): New function.
(loop_iterations): Use loop_find_equiv_value to find increments
too large to be immediate constants. Also use it to find terms
common to initial and final iteration values that can be removed.
Thu Nov 26 18:05:04 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.h (struct loop_info): Define new structure.
(precondition_loop_p): Added prototype.
(unroll_loop): Added new argument loop_info to prototype.
(final_biv_value, final_giv_value): Added new argument n_iterations
to prototype.
* loop.c (strength_reduce): Declare new structure loop_iteration_info
and new pointer loop_info.
(loop_n_iterations): Replace global variable by element in
loop_info structure.
(check_final_value): New argument n_iterations.
(insert_bct): New argument loop_info.
(loop_unroll_factor): Replace global array by element in
loop_info structure.
(loop_optimize): Remove code to allocate and initialize
loop_unroll_factor_array.
* unroll.c (precondition_loop_p): No longer static since
used by branch on count optimization.
(precondition_loop_p, unroll_loop): New argument loop_info.
(final_biv_value, final_giv_value, find_splittable_regs): New
argument n_iterations.
(loop_iteration_var, loop_initial_value, loop_increment,
loop_final_value, loop_comparison_code, loop_unroll_factor):
Replaced global variables by loop_info structure.
(loop_unroll_factor): Replace global array by element in
loop_info structure.
Thu Nov 26 17:49:29 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* loop.c (check_dbra_loop): Update JUMP_LABEL field of jump insn
when loop reversed.
* unroll.c (precondition_loop_p): Return loop_initial_value
for initial_value instead of loop_iteration_var.
Thu Nov 26 17:15:38 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md: Fix minor formatting problems. Update docs.
(*b, *b_rev, *b_noov, *b_noov_rev, *db,
decrement_and_branch_until_zero, rptb_end): Use c4x_output_cbranch
to output the instruction sequences.
(rpts): Delete.
(rptb_top): Provide alternatives to use any register or memory
for loop counter.
(rptb_end): Emit use of operands rather than assigning them
explicitly to the RS and RE registers.
Thu Nov 26 16:37:59 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_modified_between_p, c4x_mem_set_p,
c4x_mem_set_p, c4x_mem_modified_between_p, c4x_insn_moveable_p,
c4x_parallel_pack, c4x_parallel_find, c4x_update_info_reg,
c4x_update_info_regs, c4x_copy_insn_after, c4x_copy_insns_after,
c4x_merge_notes, c4x_parallel_process,
c4x_combine_parallel_independent, c4x_combine_parallel_dependent,
c4x_combine_parallel): Delete.
Thu Nov 26 15:16:05 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_override_options): For compatibility
with old target options clear flag_branch_on_count_reg if
-mno-rptb specified and set flag_argument_alias is -mno-aliases
specified.
(c4x_output_cbranch): Handle a sequence of insns rather than a
single insn.
(c4x_rptb_insert): Do not emit a RPTB insn if the RC register
has not been allocated as the loop counter.
(c4x_address_conflict): Do not allow two volatile memory references.
(valid_parallel_operands_4, valid_parallel_operands_5,
valid_parallel_operands_6): Reject pattern if the register destination
of the first set is used as part of an address in the second set.
Thu Nov 26 14:56:32 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.h (TARGET_DEFAULT): Add PARALEL_MPY_FLAG.
(TARGET_SMALL_REG_CLASS): Set to 0 so that SMALL_REGISTER_CLASSES
is no longer enabled if PARALLEL_MPY_FLAG set.
(HARD_REGNO_CALL_CLOBBERED): Add parentheses to remove ambiguity.
(REG_CLASS_CONTENTS): Add braces around initializers.
(HAVE_MULTIPLE_PACK): Define.
(ASM_OUTPUT_BYTE_FLOAT): Use %lf format specifier with
REAL_VALUE_TO_DECIMAL.
(ASM_OUTPUT_SHORT_FLOAT): Use %lf format specifier with
REAL_VALUE_TO_DECIMAL.
(ar0_reg_operand): Add prototype.
(ar0_mem_operand): Likewise.
(ar1_reg_operand): Likewise.
(ar1_mem_operand): Likewise.
(ar2_reg_operand): Likewise.
(ar2_mem_operand): Likewise.
(ar3_reg_operand): Likewise.
(ar3_mem_operand): Likewise.
(ar4_reg_operand): Likewise.
(ar4_mem_operand): Likewise.
(ar5_reg_operand): Likewise.
(ar5_mem_operand): Likewise.
(ar6_reg_operand): Likewise.
(ar6_mem_operand): Likewise.
(ar7_reg_operand): Likewise.
(ar7_mem_operand): Likewise.
(ir0_reg_operand): Likewise.
(ir0_mem_operand): Likewise.
(ir1_reg_operand): Likewise.
(ir1_mem_operand): Likewise.
(group1_reg_operand): Likewise.
(group1_mem_operand): Likewise.
(ir1_reg_operand): Likewise.
(arx_reg_operand): Likewise.
(not_rc_reg): Likewise.
(not_modify_reg): Likewise.
(c4x_group1_reg_operand): Remove prototype.
(c4x_group1_mem_operand): Likewise.
(c4x_arx_reg_operand): Likewise.
Wed Nov 25 19:02:55 1998 (Stephen L Moshier) <moshier@world.std.com>
* emit-rtl.c (gen_lowpart_common): Remove earlier change.
* real.c (make_nan): Make SIGN arg actually specify the sign bit.
Thu Nov 26 14:12:05 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (addqi3): Emit addqi3_noclobber pattern
during reload.
Wed Nov 25 22:05:28 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* config/sh/lib1funcs.asm (___udivsi3_i4): Don't switch to sz == 1
unless FMOVD_WORKS is defined.
Wed Nov 25 20:11:04 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* regclass.c (init_reg_sets): Move code that calculates tables
dependent on reg_class_contents from here...
(init_reg_sets_1): To here.
Wed Nov 25 14:54:46 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.h: Delete struct import_file. Add ihash element to
struct cpp_buffer. Delete dont_repeat_files and
import_hash_table elements from cpp_reader; change
all_include_files to a hash table. Delete all foobar_include
/ last_foobar_include elements from struct cpp_options; put
back four such: quote_include, bracket_include,
system_include, after_include. Redo struct file_name_list
completely. Add new structure type include_hash. Add
prototypes for merge_include_chains and include_hash. Change
prototypes for finclude, find_include_file, and
append_include_chain to match changes below.
* cppfiles.c (simplify_pathname, include_hash,
remap_filename, merge_include_chains): New functions.
(add_import, lookup_import, open_include_file): Removed.
(INO_T_EQ): Define this (copied from cccp.c).
(hack_vms_include_specification): Remove all calls and #if 0
out the definition. It was being called incorrectly and at
the wrong times. Until a VMSie can look at this, it's better
to not pretend to support it.
(append_include_chain): Change calling convention; now takes
only one directory at a time, and sets up the data structure
itself.
(redundant_include_p): Rewritten - this is now used for all
include redundancy, whether by #ifndef, #import, or #pragma
once. Looks up things in the include hash table.
(file_cleanup): Decrement pfile->system_include_depth here if
it's >0.
(find_include_file): Calling convention changed; now passes
around a struct include_hash instead of 3 separate parameters.
Guts ripped out and replaced with new include_hash mechanism.
(finclude): Calling convention changed as for
find_include_file. Error exits pulled out-of-line. Reformat.
(safe_read): Return a long, not an int.
(deps_output): Don't recurse.
* cpplib.c (is_system_include): Deleted.
(path_include): Fix up call to append_include_chain.
(do_include): Fix up calls to find_include_file and finclude.
Clean up dependency output a bit. Shorten obnoxiously lengthy
#import warning message. Don't decrement
pfile->system_include_depth here.
(do_pragma): Understand the include_hash structure. Reformat.
(do_endif): Correct handling of control macros. Understand
the include_hash.
(cpp_start_read): Fix up calls to finclude. Call
merge_include_chains.
(cpp_handle_option): Fix up calls to append_include_chain.
Understand the four partial include chains.
(cpp_finish): Add debugging code (#if 0-ed out) for the
include_hash.
(cpp_cleanup): Free the include_hash, not the import hash and
the all_include and dont_repeat lists which no longer exist.
Wed Nov 25 11:26:19 1998 Jeffrey A Law (law@cygnus.com)
* toplev.c (no_new_pseudos): Define.
(rest_of_compilation): Set no_new_pseudos as needed.
* emit-rtl.c (gen_reg_rtx): Abort if we try to create a new pseudo
if no_new_pseudos is set.
* rtl.h (no_new_pseudos): Declare it.
* reload1.c (reload): Update comments.
* md.texi: Corresponding changes.
Wed Nov 25 11:26:17 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload1.c (reg_used_in_insn): Renamed from reg_used_by_pseudo.
(choose_reload_regs): Rename it here as well. When computing it,
also merge in used hardregs.
1998-11-25 Zack Weinberg <zack@rabi.phys.columbia.edu>
* gcc.c: Split out Objective-C specs to...
* objc/lang-specs.h: here. (New file.) Make the specs cpplib
aware.
* c-lex.c (init_parse): Always initialize the filename global.
* objc/objc-act.c (lang_init): Always call check_newline at
beginning of file.
Wed Nov 25 00:48:29 1998 Graham <grahams@rcp.co.uk>
* reload1.c (reload): Remove unused variable.
(reload_reg_free_for_value_p): Add missing parameter definition.
* jump.c (jump_optimize): Remove unused variable.
Wed Nov 25 00:07:11 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (graph.o): Depend on $(RTL_H), not rtl.h.
* cse.c (fold_rtx): Make autoincrement addressing mode tests be
runtime selectable.
* expr.c (move_by_pieces): Similarly.
(move_by_pieces_1, clear_by_pieces, clear_by_pieces_1): Similarly.
* flow.c (find_auto_inc): Similarly.
(try_pre_increment): Similarly.
* loop.c (strength_reduce): Similarly.
* regclass.c (auto_inc_dec_reg_p): Similarly.
* regmove.c (try_auto_increment): Similarly.
(fixup_match_1): Similarly.
* rtl.h (HAVE_PRE_INCREMENT): Define if not already defined.
(HAVE_PRE_DECREMENT): Similarly.
(HAVE_POST_INCREMENT, HAVE_POST_DECREMENT): Similarly.
* Corresponding changes to all target header files.
* tm.texi: Update docs for autoinc addressing modes.
Tue Nov 24 20:24:59 1998 Jim Wilson <wilson@cygnus.com>
* configure.in (m68020-*-elf*, m68k-*-elf*): New targets.
* configure: Rebuild.
* config/elfos.h: New file.
* config/m68k/m68020-elf.h, config/m68k/m68kelf.h,
config/m68k/t-m68kelf: New file.
Tue Nov 24 13:40:06 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (HOST_AR): Define.
(HOST_AR_FLAGS, HOST_RANLIB, HOST_RANLIB_TEST): Similarly.
(libcpp.a): Use the host tools explicitly.
(STAGESTUFF): Add libcpp.a.
Tue Nov 24 09:33:49 1998 Nick Clifton <nickc@cygnus.com>
* config/m32r/m32r.md (movstrsi_internal): Describe changes made
to source and destination registers.
Mon Nov 23 20:28:02 1998 Mike Stump <mrs@wrs.com>
* libgcc2.c (top_elt): Remove top_elt, it isn't thread safe.
The strategy we now use is to pre allocate the top_elt along
with the EH context so that each thread has its own top_elt.
This is necessary as the dynamic cleanup chain is used on the
top element of the stack and each thread MUST have its own.
(eh_context_static): Likewise.
(new_eh_context): Likewise.
(__sjthrow): Likewise.
Mon Nov 23 20:25:03 1998 Jason Merrill <jason@yorick.cygnus.com>
* i386/linux.h (ASM_OUTPUT_MAX_SKIP_ALIGN): Wrap in do...while.
* i386.md (prologue_get_pc): Remove unused variable.
Mon Nov 23 17:05:40 1998 Geoffrey Noer <noer@cygnus.com>
* i386/xm-cygwin.h: Rename cygwin_ path funcs back to cygwin32_.
Mon Nov 23 16:40:00 1998 Ulrich Drepper <drepper@cygnus.com>
* Makefile.in (OBJS): Add graph.o.
(graph.o): New dependency list.
* flags.h: Declare dump_for_graph and define graph_dump_types type.
* print-rtl.c (dump_for_graph): Define new variable.
(print_rtx): Rewrite to allow use in graph dumping functions.
* toplev.c: Declare print_rtl_graph_with_bb, clean_graph_dump_file,
finish_graph_dump_file.
Define graph_dump_format.
(compile_file): If graph dumping is enabled also clear these files.
Finish graph dump files.
(rest_of_compilation): Also dump graph information if enabled.
(main): Recognize -dv to enabled VCG based graph dumping.
* graph.c: New file. Graph dumping functions.
Mon Nov 23 16:39:04 1998 Richard Henderson <rth@cygnus.com>
* configure.in: Look for <sys/stat.h>.
* system.h: Include it before substitute S_ISREG definitions.
Mon Nov 23 17:40:37 1998 Gavin Romig-Koch <gavin@cygnus.com>
* config/mips/abi.h: Use ABI_O64, duplicating ABI_32 usage.
* config/mips/iris6.h: Same.
* config/mips/mips.md: Same.
* config/mips/mips.c: Same; also add "-mabi=o64" option.
* config/mips/mips.h: Same; also define ABI_O64.
Mon Nov 23 17:02:27 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* configure.in: Use AC_PREREQ(2.12.1).
Mon Nov 23 10:16:38 1998 Melissa O'Neill <oneill@cs.sfu.ca>
* cccp.c (S_ISREG, S_ISDIR): Delete defines.
* cpplib.c, gcc.c: Likewise.
* system.h (S_ISREG, S_ISDIR): Define if not already defined.
Mon Nov 23 09:53:44 1998 Richard Henderson <rth@cygnus.com>
* local-alloc.c (local_alloc): Use malloc not alloca for
reg_qty, reg_offset, ref_next_in_qty.
Mon Nov 23 16:46:46 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* caller-save.c (insert_one_insn): Initialize the live_before and
live_after register sets.
Add SH4 support:
* config/sh/lib1funcs.asm (___movstr_i4_even, ___movstr_i4_odd): Define.
(___movstrSI12_i4, ___sdivsi3_i4, ___udivsi3_i4): Define.
* sh.c (reg_class_from_letter, regno_reg_class): Add DF_REGS.
(fp_reg_names, assembler_dialect): New variables.
(print_operand_address): Handle SUBREGs.
(print_operand): Added 'o' case.
Don't use adj_offsettable_operand on PRE_DEC / POST_INC.
Name of FP registers depends on mode.
(expand_block_move): Emit different code for SH4 hardware.
(prepare_scc_operands): Use emit_sf_insn / emit_df_insn as appropriate.
(from_compare): Likewise.
(add_constant): New argument last_value. Changed all callers.
(find_barrier): Don't try HImode load for FPUL_REG.
(machine_dependent_reorg): Likewise.
(sfunc_uses_reg): A CLOBBER cannot be the address register use.
(gen_far_branch): Emit a barrier after the new jump.
(barrier_align): Don't trust instruction lengths before
fixing up pcloads.
(machine_dependent_reorg): Add support for FIRST_XD_REG .. LAST_XD_REG.
Use auto-inc addressing for fp registers if doubles need to
be loaded in two steps.
Set sh_flag_remove_dead_before_cse.
(push): Support for TARGET_FMOVD. Use gen_push_fpul for fpul.
(pop): Support for TARGET_FMOVD. Use gen_pop_fpul for fpul.
(calc_live_regs): Support for TARGET_FMOVD. Don't save FPSCR.
Support for FIRST_XD_REG .. LAST_XD_REG.
(sh_expand_prologue): Support for FIRST_XD_REG .. LAST_XD_REG.
(sh_expand_epilogue): Likewise.
(sh_builtin_saveregs): Use DFmode moves for fp regs on SH4.
(initial_elimination_offset): Take TARGET_ALIGN_DOUBLE into account.
(arith_reg_operand): FPUL_REG is OK for SH4.
(fp_arith_reg_operand, fp_extended_operand): New functions.
(tertiary_reload_operand, fpscr_operand): Likewise.
(commutative_float_operator, noncommutative_float_operator): Likewise.
(binary_float_operator, get_fpscr_rtx, emit_sf_insn): Likewise.
(emit_df_insn, expand_sf_unop, expand_sf_binop): Likewise.
(expand_df_unop, expand_df_binop, expand_fp_branch): Likewise.
(emit_fpscr_use, mark_use, remove_dead_before_cse): Likewise.
* sh.h (CPP_SPEC): Add support for -m4, m4-single, m4-single-only.
(CONDITIONAL_REGISTER_USAGE): Likewise.
(HARD_SH4_BIT, FPU_SINGLE_BIT, SH4_BIT, FMOVD_BIT): Define.
(TARGET_CACHE32, TARGET_SUPERSCALAR, TARGET_HARWARD): Define.
(TARGET_HARD_SH4, TARGET_FPU_SINGLE, TARGET_SH4, TARGET_FMOVD): Define.
(target_flag): Add -m4, m4-single, m4-single-only, -mfmovd.
(OPTIMIZATION_OPTIONS): If optimizing, set flag_omit_frame_pointer
to -1 and sh_flag_remove_dead_before_cse to 1.
(ASSEMBLER_DIALECT): Define to assembler_dialect.
(assembler_dialect, fp_reg_names): Declare.
(OVERRIDE_OPTIONS): Add code for TARGET_SH4.
Hide names of registers that are not accessible.
(CACHE_LOG): Take TARGET_CACHE32 into account.
(LOOP_ALIGN): Take TARGET_HARWARD into account.
(FIRST_XD_REG, LAST_XD_REG, FPSCR_REG): Define.
(FIRST_PSEUDO_REGISTER: Now 49.
(FIXED_REGISTERS, CALL_USED_REGISTERS): Include values for registers.
(HARD_REGNO_NREGS): Special treatment of FIRST_XD_REG .. LAST_XD_REG.
(HARD_REGNO_MODE_OK): Update.
(enum reg_class): Add DF_REGS and FPSCR_REGS.
(REG_CLASS_NAMES, REG_CLASS_CONTENTS, REG_ALLOC_ORDER): Likewise.
(SECONDARY_OUTPUT_RELOAD_CLASS, SECONDARY_INPUT_RELOAD_CLASS): Update.
(CLASS_CANNOT_CHANGE_SIZE, DEBUG_REGISTER_NAMES): Define.
(NPARM_REGS): Eight floating point parameter registers on SH4.
(BASE_RETURN_VALUE_REG): SH4 also passes double values
in floating point registers.
(GET_SH_ARG_CLASS): Likewise.
Complex float types are also returned in float registers.
(BASE_ARG_REG): Complex float types are also passes in float registers.
(FUNCTION_VALUE): Change mode like PROMOTE_MODE does.
(LIBCALL_VALUE): Remove trailing semicolon.
(ROUND_REG): Round when double precision value is passed in floating
point register(s).
(FUNCTION_ARG_ADVANCE): No change wanted for SH4 when things are
passed on the stack.
(FUNCTION_ARG): Little endian adjustment for SH4 SFmode.
(FUNCTION_ARG_PARTIAL_NREGS): Zero for SH4.
(TRAMPOLINE_ALIGNMENT): Take TARGET_HARWARD into account.
(INITIALIZE_TRAMPOLINE): Emit ic_invalidate_line for TARGET_HARWARD.
(MODE_DISP_OK_8): Not for SH4 DFmode.
(GO_IF_LEGITIMATE_ADDRESS): No base reg + index reg for SH4 DFmode.
Allow indexed addressing for PSImode after reload.
(LEGITIMIZE_ADDRESS): Not for SH4 DFmode.
(LEGITIMIZE_RELOAD_ADDRESS): Handle SH3E SFmode.
Don't change SH4 DFmode nor PSImode RELOAD_FOR_INPUT_ADDRESS.
(DOUBLE_TYPE_SIZE): 64 for SH4.
(RTX_COSTS): Add PLUS case.
Increase cost of ASHIFT, ASHIFTRT, LSHIFTRT case.
(REGISTER_MOVE_COST): Add handling of R0_REGS, FPUL_REGS, T_REGS,
MAC_REGS, PR_REGS, DF_REGS.
(REGISTER_NAMES): Use fp_reg_names.
(enum processor_type): Add PROCESSOR_SH4.
(sh_flag_remove_dead_before_cse): Declare.
(rtx_equal_function_value_matters, fpscr_rtx, get_fpscr_rtx): Declare.
(PREDICATE_CODES): Add binary_float_operator,
commutative_float_operator, fp_arith_reg_operand, fp_extended_operand,
fpscr_operand, noncommutative_float_operator.
(ADJUST_COST): Use different scale for TARGET_SUPERSCALAR.
(SH_DYNAMIC_SHIFT_COST): Cheaper for SH4.
* sh.md (attribute cpu): Add value sh4.
(attrbutes fmovd, issues): Define.
(attribute type): Add values dfp_arith, dfp_cmp, dfp_conv, dfdiv.
(function units memory, int, mpy, fp): Make dependent on issue rate.
(function units issue, single_issue, load_si, load): Define.
(function units load_store, fdiv, gp_fpul): Define.
(attribute hit_stack): Provide proper default.
(use_sfunc_addr+1, udivsi3): Predicated on ! TARGET_SH4.
(udivsi3_i4, udivsi3_i4_single, divsi3_i4, divsi3_i4_single): New insns.
(udivsi3, divsi3): Emit special patterns for SH4 hardware,
(mulsi3_call): Now uses match_operand for function address.
(mulsi3): Also emit code for SH1 case. Wrap result in REG_LIBCALL /
REG_RETVAL notes.
(push, pop, push_e, pop_e): Now define_expands.
(push_fpul, push_4, pop_fpul, pop_4, ic_invalidate_line): New expanders.
(movsi_ie): Added y/i alternative.
(ic_invalidate_line_i, movdf_i4): New insns.
(movdf_i4+[123], reload_outdf+[12345], movsi_y+[12]): New splitters.
(reload_indf, reload_outdf, reload_outsf, reload_insi): New expanders.
(movdf): Add special code for SH4.
(movsf_ie, movsf_ie+1, reload_insf, calli): Make use of fpscr visible.
(call_valuei, calli, call_value): Likewise.
(movsf): Emit no-op move.
(mov_nop, movsi_y): New insns.
(blt, sge): Generalize to handle DFmode.
(return predicate): Call emit_fpscr_use and remove_dead_before_cse.
(block_move_real, block_lump_real): Predicate on ! TARGET_HARD_SH4.
(block_move_real_i4, block_lump_real_i4, fpu_switch): New insns.
(fpu_switch0, fpu_switch1, movpsi): New expanders.
(fpu_switch+[12], fix_truncsfsi2_i4_2+1): New splitters.
(toggle_sz): New insn.
(addsf3, subsf3, mulsf3, divsf3): Now define_expands.
(addsf3_i, subsf3_i, mulsf3_i4, mulsf3_ie, divsf3_i): New insns.
(macsf3): Make use of fpscr visible. Disable for SH4.
(floatsisf2): Make use of fpscr visible.
(floatsisf2_i4): New insn.
(floatsisf2_ie, fixsfsi, cmpgtsf_t, cmpeqsf_t): Disable for SH4.
(ieee_ccmpeqsf_t): Likewise.
(fix_truncsfsi2): Emit different code for SH4.
(fix_truncsfsi2_i4, fix_truncsfsi2_i4_2, cmpgtsf_t_i4): New insns.
(cmpeqsf_t_i4, ieee_ccmpeqsf_t_4): New insns.
(negsf2, sqrtsf2, abssf2): Now expanders.
(adddf3, subdf3i, muldf2, divdf3, floatsidf2): New expanders.
(negsf2_i, sqrtsf2_i, abssf2_i, adddf3_i, subdf3_i): New insns.
(muldf3_i, divdf3_i, floatsidf2_i, fix_truncdfsi2_i): New insns.
(fix_truncdfsi2, cmpdf, negdf2, sqrtdf2, absdf2): New expanders.
(fix_truncdfsi2_i4, cmpgtdf_t, cmpeqdf_t, ieee_ccmpeqdf_t): New insns.
(fix_truncdfsi2_i4_2+1): New splitters.
(negdf2_i, sqrtdf2_i, absdf2_i, extendsfdf2_i4): New insns.
(extendsfdf2, truncdfsf2): New expanders.
(truncdfsf2_i4): New insn.
* t-sh (LIB1ASMFUNCS): Add _movstr_i4, _sdivsi3_i4, _udivsi3_i4.
(MULTILIB_OPTIONS): Add m4-single-only/m4-single/m4.
* float-sh.h: When testing for __SH3E__, also test for
__SH4_SINGLE_ONLY__ .
* va-sh.h (__va_freg): Define to float.
(__va_greg, __fa_freg, __gnuc_va_list, va_start):
Define for __SH4_SINGLE_ONLY__ like for __SH3E__ .
(__PASS_AS_FLOAT, __TARGET_SH4_P): Likewise.
(__PASS_AS_FLOAT): Use different definition for __SH4__ and
__SH4_SINGLE__.
(TARGET_SH4_P): Define.
(va_arg): Use it.
* sh.md (movdf_k, movsf_i): Tweak the condition so that
init_expr_once is satisfied about the existence of load / store insns.
* sh.md (movsi_i, movsi_ie, movsi_i_lowpart, movsf_i, movsf_ie):
Change m constraint in source operand to mr / mf.
* va-sh.h (__va_arg_sh1): Use __asm instead of asm.
* (__VA_REEF): Define.
(__va_arg_sh1): Use it.
* va-sh.h (va_start, va_arg, va_copy): Add parentheses.
Sun Nov 22 21:34:02 1998 Jeffrey A Law (law@cygnus.com)
* i386/dgux.c (struct option): Add new "description field".
* m88k/m88k.c (struct option): Likewise.
Sun Nov 22 16:07:57 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Nov 22 13:40:02 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* regmove.c (regmove_profitable_p): Use return value of find_matches
properly.
Sun Nov 22 02:47:37 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Nov 21 22:12:09 1998 Jeffrey A Law (law@cygnus.com)
* reload1.c (eliminate_regs): Do not lose if eliminate_regs is called
without reload having been called earlier.
* v850.c (ep_memory_operand): Offsets < 0 are not valid for EP
addressing modes.
(v850_reorg): Similarly.
* loop.c (check_dbra_loop): Avoid using gen_add2_insn.
Sat Nov 21 02:18:38 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (move_movables): Start of libcall might be new loop start.
Fri Nov 20 12:14:16 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* hash.c (hash_table_init_n): Wrap prototype arguments in PARAMS().
Fri Nov 20 08:34:00 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* function.c (nonlocal_goto_handler_slots): Renamed from
nonlocal_goto_handler_slot; now an EXPR_LIST chain.
(push_function_context_to): Adjust for this change.
(pop_function_context_from): Likewise.
(init_function_start): Likewise.
(expand_function_end): Likewise.
* function.h (struct function): Likewise.
* calls.c (expand_call): Likewise.
* explow.c (allocate_dynamic_stack_space): Likewise.
* expr.h (nonlocal_goto_handler_slots): Rename its declaration.
* stmt.c (declare_nonlocal_label): Make a new handler slot for each
label.
(expand_goto): When doing a nonlocal goto, find corresponding handler
slot for it. Don't put the label address in the static chain register.
(expand_end_bindings): Break out nonlocal goto handling code into
three new functions.
(expand_nl_handler_label, expand_nl_goto_receiver,
expand_nl_goto_receivers): New static functions, broken out of
expand_end_bindings and adapted to create one handler per nonlocal
label.
* function.c (delete_handlers): Delete insn if it references any of
the nonlocal goto handler slots.
* i960.md (nonlocal_goto): Comment out code that modifies
static_chain_rtx.
* sparc.md (nonlocal_goto): Likewise.
(goto_handler_and_restore_v9): Comment out.
(goto_handler_and_restore_v9_sp64): Comment out.
Thu Nov 19 23:44:38 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* expr.c (STACK_BYTES): Delete unused macro.
* calls.c: Provide default for PREFERRED_STACK_BOUNDARY.
(STACK_BYTES): Use PREFERRED_STACK_BOUNDARY, not STACK_BOUNDARY.
(expand_call): Likewise.
(emit_library_call): Likewise.
(emit_library_call_value): Likewise.
* function.c: Provide default for PREFERRED_STACK_BOUNDARY.
(STACK_BYTES): Use PREFERRED_STACK_BOUNDARY, not STACK_BOUNDARY.
* explow.c: Provide default for PREFERRED_STACK_BOUNDARY.
(round_push): Use PREFERRED_STACK_BOUNDARY, not STACK_BOUNDARY.
(allocate_dynamic_stack_space): Likewise.
* tm.texi (PREFERRED_STACK_BOUNDARY): Document new macro.
(STACK_BOUNDARY): Update description to reflect the new situation.
Thu Nov 19 22:20:51 1998 Jeffrey A Law (law@cygnus.com)
* reorg.c (relax_delay_slots): When optimizing for code size, if a
return with a filled delay slot is followed by a return with an
unfilled delay slot, delete the first return and reemit the insn
that was previously in its delay slot.
* i860.c (single_insn_src_p): Add missing parens.
* ginclude/math-3300.h: Likewise.
Thu Nov 19 20:55:59 1998 H.J. Lu (hjl@gnu.org)
* regclass.c (init_reg_sets_1): Add prototype.
(init_reg_modes): Likewise.
1998-11-19 Zack Weinberg <zack@rabi.phys.columbia.edu>
* c-common.c: Change warning messages to say `comparison is
always true' or `comparison is always false' instead of the
confusing `is always 0', `is always 1'.
Thu Nov 19 19:05:49 1998 Per Bothner <bothner@cygnus.com>
* print-tree.c (print_node): After printing BLOCK or BIND_EXPR,
break instead of return (which loses closing '>').
Thu Nov 19 19:34:13 1998 Jeffrey A Law (law@cygnus.com)
* i386.h (LEGITIMATE_CONSTANT_P): Reject CONST_DOUBLEs that are not
standard 387 constants.
* i386.md (jump): Explicitly set "memory" attribute.
(indirect_jump, prologue_set_stack_ptr): Likewise.
(prologue_get_pc_and_set_got, pop): Likewise.
(allocate_stack_worder, blockage, return_internal): Likewise.
(return_pop_internal, nop): Likewise.
(epilogue_set_stack_ptr, leave): Likewise.
Thu Nov 19 15:42:54 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/coff.h: Set USER_LABEL_PREFIX to "_".
Thu Nov 19 23:20:59 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_reg_free_for_value_p):
Early auto_inc reloads don't conflict with outputs.
Thu Nov 19 12:58:55 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* configure.in: Don't do AC_CHECK_HEADERS(wait.h sys/wait.h).
Instead call AC_HEADER_SYS_WAIT.
* collect2.c: Don't provide defaults for sys/wait.h macros.
* gcc.c: Likewise.
* protoize.c: Likewise. Also, don't include sys/wait.h.
* system.h: Include sys/wait.h and provide macro defaults.
1998-11-19 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* Makefile.in (mandir): Set to @mandir@.
(man1dir): New variable to hold the former value of $(mandir).
Replace all uses of $(mandir) by $(man1dir).
Wed Nov 18 16:31:28 1998 Jim Wilson <wilson@cygnus.com>
* reload.c (find_reloads_address_part): If have a CONST_INT, create
a new one before passing it to force_const_mem.
* reload.c (find_reloads_toplev): Pass &x instead of NULL_PTR in
find_reloads_address call.
Wed Nov 18 22:13:00 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* expr.c (store_expr): Don't generate load-store pair
if TEMP is identical (according to ==) with TARGET.
Tue Nov 17 22:25:16 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_reg_free_for_value_p): When considered reload
has an output, matching inputs are not sufficient to avoid conflict.
Tue Nov 17 11:51:16 1998 Mark Mitchell <mark@markmitchell.com>
* hash.h (hash_table_key): New type.
(hash_entry): Change `string' field to generic `key'.
(hash_table): Add `comp' and `hash' functions.
(hash_table_init): Take them as input.
(hash_table_init_n): Likewise.
(hash_lookup): Modify for generic keys.
(hash_newfunc): Likewise.
(hash_traverse): Likewise.
(string_hash): New function.
(string_compare): Likewise.
(string_copy): Likewise.
* hash.c (hash_table_init_n): Modify for generic keys.
(hash_table_init): Likewise.
(hash_lookup): Likewise.
(hash_newfunc): Likewise.
(hash_traverse): Likewise.
(string_hash): Split out from hash_lookup.
(string_compare): New function.
(string_copy): Split out from hash_lookup.
* tlink.c (symbol_hash_newfunc): Modify for new interfaces to hash
tables.
(symbol_hash_lookup): Likewise.
(file_hash_newfunc): Likewise.
(file_hash_lookup): Likewise.
(demangled_hash_newfunc): Likewise.
(demangled_hash_lookup): Likewise.
(tlink_int): Likewise.
(read_repo_file): Likewise.
(recompile_files): Likewise.
(demangle_new_symbols): Likewise.
(scan_linker_output): Likewise.
Tue Nov 17 17:13:53 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* flow.c (insn_dead_p): New argument NOTES. Changed all callers.
Mon Nov 16 17:56:07 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (output_mi_thunk): Improve test for local branch.
Mon Nov 16 17:56:07 1998 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* rs6000.c (output_mi_thunk): Correct test for aggregate values.
Mon Nov 16 21:02:52 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_reg_free_before_p): Delete.
Changed all callers to use reload_reg_free_for_value_p instead.
(reload_reg_free_for_value_p): Handle more reload types.
A RELOAD_FOR_INPUT doesn't conflict with its
RELOAD_FOR_INPUT_ADDRESS / RELOAD_FOR_INPADDR_ADDRESS.
Add special case for OUT == const0_rtx.
Added ignore_address_reloads argument. Changed all callers.
Mon Nov 16 02:22:29 1998 Jason Merrill <jason@yorick.cygnus.com>
* toplev.c (compile_file): Don't pedwarn about undefined static
functions just because we passed -Wunused.
Mon Nov 16 04:41:41 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* function.c (purge_addressof_1): Unshare rtl created by
store_bit_field.
Mon Nov 16 04:23:06 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* regmove.c (regmove_optimize): Don't do anything but
optimize_reg_copy[123] when flag_regmove is not set.
Sat Nov 14 15:05:07 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (addsi3, subsi3): Revise 5 Nov change to store DImode
value in paradoxical SImode result, rather than truncating midpoint.
Fri Nov 13 22:19:23 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (reg_not_elim_operand): New.
* alpha.h (PREDICATE_CODES): Add it.
* alpha.md (s48addq, s48subq patterns): Use it as the predicate
for the multiplicand.
Fri Nov 13 22:50:37 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (movsf): Remove explicit secondary-reload-like
functionality. Only truncate SFmode store if in FPR.
(movsf splitters): Combine const_double splitters.
(movsf_hardfloat): Add GPR support.
Fri Nov 13 11:02:11 1998 Stan Cox <scox@cygnus.com>
* splet.h (SUBTARGET_OVERRIDE_OPTIONS): New to
deprecate -mlive-g0 and -mbroken-saverestore.
* t-splet (MULTILIB_OPTIONS): Likewise.
* sparc.c (sparc_flat_compute_frame_size): Correctly calc args_size
in a leaf function. Clarify total_size/extra_size relationship.
Thu Nov 12 19:20:57 1998 Geoffrey Noer <noer@cygnus.com>
* i386/cygwin32.asm: Delete.
* i386/cygwin.asm: New file, renamed from cygwin32.asm.
* i386/cygwin32.h: Delete.
* i386/cygwin.h: New file, renamed from cygwin32.h.
* i386/t-cygwin32: Delete.
* i386/t-cygwin: New file, renamed from t-cygwin32. Include
cygwin.asm instead of cygwin32.asm. Remove "32" from comment.
* i386/x-cygwin32: Delete.
* i386/x-cygwin: New file, renamed from x-cygwin32.
* i386/xm-cygwin32: Delete.
* i386/xm-cygwin: New file, renamed from xm-cygwin32. Use newly
renamed cygwin_ funcs for path translations.
* i386/win32.h: Define __CYGWIN__ when -mcygwin given.
* i386/winnt.c: Remove "32" from comment about cygwin.
* i386/mingw32.h: Fix references to cygwin32.h in light of above.
* rs6000/cygwin32.h: Delete.
* rs6000/cygwin.h: New file, renamed from cygwin32.h. Add
-D__CYGWIN__ to CPP_PREDEFINES.
* rs6000/x-cygwin32: Delete.
* rs6000/x-cygwin: New file, renamed from x-cygwin32.
* rs6000/xm-cygwin32: Delete.
* rs6000/xm-cygwin: New file, renamed from xm-cygwin32.
* configure.in: Check for cygwin* instead of cygwin32. Account
for the rename of cygwin-related config files to lose the "32"s.
* configure: Regenerate.
* cccp.c, collect2.c, gcc.c, getpwd.c, libgcc2.c, protoize.c,
toplev.c: Change all refs to __CYGWIN32__ to __CYGWIN__.
Wed Nov 11 12:25:19 1998 Tom Tromey <tromey@cygnus.com>
* Makefile.in (JAVAGC): New macro.
* configure: Rebuilt.
* configure.in: Recognize --enable-java-gc argument. Subst
`JAVAGC' variable.
Thu Nov 12 03:32:16 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
Handle equivalences that have been obscured by gcse:
* reload1.c (reload): Handle equivalences set up in multiple places.
* local-alloc.c (reg_equiv_init_insns): New variable.
(no_equiv): New function.
(update_equiv_regs): Handle equivalences set up in multiple places.
Don't ignore an insn just because its destination is likely to be
spilled.
Wed Nov 11 13:46:13 1998 Jim Wilson <wilson@cygnus.com>
* except.c (expand_eh_return): Readd force_operand call lost in
Sept 15 change.
Tue Nov 10 17:04:11 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (LEGITIMIZE_ADDRESS): Add missing goto on last case.
1998-11-09 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* dbxout.c: Check HAVE_STAB_H instead of HAVE_STABS_H.
Mon Nov 9 20:15:19 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* regmove.c (regmove_optimize): Fix error in last change.
Mon Nov 9 16:37:52 1998 Andrew Cagney <cagney@b1.cygnus.com>
* mips.c (function_prologue): When TARGET_MIPS16, adjust the register
offset in the .mask pseudo to compensate for frame pointer adjustments.
(mips16_fp_args, build_mips16_call_stub): For little endian, do not
word swap arguments moved to/from FP registers.
* mips16.S (DFREVCMP): Reverse arguments to OPCODE.
Mon Nov 9 09:47:06 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Nov 9 02:14:14 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Nov 9 03:06:24 1998 Jeffrey A Law (law@cygnus.com)
* reload1.c (delete_output_reload_insn): If a pseudo is set multiple
times, then it can not be completely replaced.
Mon Nov 9 00:39:02 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (call, call_value) [OSF]: Correct alt 3 insn length.
Sun Nov 8 17:50:30 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gansidecl.h: Prepend a "G" to the macro wrapping this file
(to distinguish it from the macro wrapping ansidecl.h.)
Include libiberty's ansidecl.h. Remove all redundant definitions.
Define the PROTO() style macros in terms of the PARAMS() ones.
* calls.c (emit_library_call): Switch on ANSI_PROTOTYPES, not
__STDC__, when deciding whether to use ANSI variable args.
(emit_library_call_value): Likewise.
* cccp.c (error): Likewise.
(warning): Likewise.
(error_with_line): Likewise.
(warning_with_line): Likewise.
(pedwarn): Likewise.
(pedwarn_with_line): Likewise.
(pedwarn_with_file_and_line): Likewise.
(fatal): Likewise.
* cexp.y (error): Likewise.
(pedwarn): Likewise.
(warning): Likewise.
* collect2.c (fatal_perror): Likewise.
(fatal): Likewise.
(error): Likewise.
* combine.c (gen_rtx_combine): Likewise.
* cpperror.c (cpp_message): Likewise.
(cpp_fatal): Likewise.
* cpplib.c (cpp_error): Likewise.
(cpp_warning): Likewise.
(cpp_pedwarn): Likewise.
(cpp_error_with_line): Likewise.
(cpp_warning_with_line): Likewise.
(cpp_pedwarn_with_line): Likewise.
(cpp_pedwarn_with_file_and_line): Likewise.
* cpplib.h: Don't define PARAMS() macro.
* demangle.h: Likewise.
* doprint.c (checkit): Switch on ANSI_PROTOTYPES, not __STDC__,
when deciding whether to use ANSI variable args.
* emit-rtl.c (gen_rtx): Likewise.
(gen_rtvec): Likewise.
* final.c (asm_fprintf): Likewise.
* fix-header.c (cpp_message): Likewise.
(fatal): Likewise.
(cpp_fatal): Likewise.
* gcc.c (concat): Likewise.
(fatal): Likewise.
(error): Likewise.
* genattr.c (fatal): Likewise.
* genattrtab.c (attr_rtx): Likewise.
(attr_printf): Likewise.
(fatal): Likewise.
* gencodes.c (fatal): Likewise.
* genconfig.c (fatal): Likewise.
* genemit.c (fatal): Likewise.
* genextract.c (fatal): Likewise.
* genflags.c (fatal): Likewise.
* genopinit.c (fatal): Likewise.
* genoutput.c (fatal): Likewise.
(error): Likewise.
* genpeep.c (fatal): Likewise.
* genrecog.c (fatal): Likewise.
* halfpic.h: Switch on ANSI_PROTOTYPES, not __STDC__, when
deciding whether to declare `tree_node' and `rtx_def'.
* hash.h: Don't define stuff we get from gansidecl.h.
* mips-tfile.c: Likewise. Define __proto() in terms of PARAMS().
(fatal): Switch on ANSI_PROTOTYPES, not __STDC__, when deciding
whether to use ANSI variable args.
(error): Likewise.
* prefix.c (concat): Likewise.
* scan.h: Likewise.
* system.h: Likewise.
* toplev.c (error_with_file_and_line): Likewise.
(error_with_decl): Likewise.
(error_for_asm): Likewise.
(error): Likewise.
(fatal): Likewise.
(warning_with_file_and_line): Likewise.
(warning_with_decl): Likewise.
(warning_for_asm): Likewise.
(warning): Likewise.
(pedwarn): Likewise.
(pedwarn_with_decl): Likewise.
(pedwarn_with_file_and_line): Likewise.
(sorry): Likewise.
(really_sorry): Likewise.
* toplev.h: Switch on ANSI_PROTOTYPES, not __STDC__, when deciding
whether to declare `tree_node' and `rtx_def'.
* tree.c (build): Switch on ANSI_PROTOTYPES, not __STDC__, when
deciding whether to use ANSI variable args.
(build_nt): Likewise.
(build_parse_node): Likewise.
Sun Nov 8 13:10:55 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sat Nov 7 23:34:01 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (libcpp.a): Check RANLIB_TEST before runing RANLIB.
Sat Nov 7 22:26:19 1998 David Edelsohn <edelsohn@gnu.org>
* collect2.c (main, case 'b'): Use else if.
Sat Nov 7 15:35:25 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* configure.in (host_xm_file, build_xm_file, xm_file, tm_file):
Arrange to include gansidecl.h in {ht}config.h & tm.h just
before the config/ directory headers.
(tm_file_list, host_xm_file_list, build_xm_file_list): Handle
gansidecl.h in the list of dependencies.
* Makefile.in (RTL_BASE_H): Don't depend on gansidecl.h.
(TREE_H, DEMANGLE_H, RECOG_H, REGS_H, libgcc2.a, stmp-multilib,
mbchar.o, collect2.o, pexecute.o, vfprintf.o, splay-tree.o, gcc.o,
gencheck.o, choose-temp.o, mkstemp.o, mkstemp.o, prefix.o,
dyn-string.o, cexp.o, cccp.o, cppmain.o, cpplib.o, cpperror.o,
cppexp.o, cppfiles.o, cpphash.o, cppalloc.o, scan-decls.o):
Likewise.
* cccp.c: Don't include gansidecl.h.
* cexp.y: Likewise.
* collect2.c: Likewise.
* config/c4x/c4x.c: Likewise.
* config/v850/v850.h: Likewise.
* cppalloc.c: Likewise.
* cpperror.c: Likewise.
* cppexp.c: Likewise.
* cppfiles.c: Likewise.
* cpphash.c: Likewise.
* cpplib.c: Likewise.
* cppmain.c: Likewise.
* cppulp.c: Likewise.
* demangle.h: Likewise.
* doprint.c: Likewise.
* dyn-string.c: Likewise.
* eh-common.h: Likewise.
* fix-header.c: Likewise.
* frame.c: Likewise.
* gcc.c: Likewise.
* gcov.c: Likewise.
* gen-protos.c: Likewise.
* gencheck.c: Likewise.
* halfpic.h: Likewise.
* hash.c: Likewise.
* machmode.h: Likewise.
* mbchar.c: Likewise.
* prefix.c: Likewise.
* protoize.c: Likewise.
* recog.h: Likewise.
* rtl.h: Likewise.
* scan-decls.c: Likewise.
* tree.h: Likewise.
* varray.h: Likewise.
Sat Nov 7 11:37:53 1998 Richard Henderson <rth@cygnus.com>
* i386.md (call_value_pop): If we're not popping anything,
defer to call_value.
(call_pop): Likewise defer to call.
Sat Nov 7 02:49:56 1998 Richard Henderson <rth@cygnus.com>
* function.c (purge_addressof): Clear purge_addressof_replacements
only after processing the whole function.
Sat Nov 7 00:54:55 1998 Jeffrey A Law (law@cygnus.com)
* reload1.c (reload): If we can not perform a particular elimination
when we thought we could earlier, then we must always iterate through
the loop at least one more time.
Fri Nov 6 19:37:33 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (add_operand): Simplify the CONST_INT match.
(sext_add_operand): Correct typo in comparison by using
CONST_OK_FOR_LETTER_P.
* alpha.md (s?addq): Use sext_add_operand to allow the negative
constant alternatives to be generated.
(mulsi3, muldi3, umuldi3_highpart): Loosen constraints to allow
small constants, since the hw instructions do.
Fri Nov 6 20:15:19 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload1.c (emit_reload_insns): When rewriting the SET_DEST of a
previous insn to store directly into our reload register, make sure
that if the source of the previous insn is a reload register, its
spill_reg_store and spill_reg_stored_to values are cleared.
Fri Nov 6 16:35:10 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (floatunssidf2_internal splitter): Use base register
operand, not hard-coded SP.
Fri Nov 6 04:07:53 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* jump.c (calculate_can_reach_end): Fix thinko.
Fri Nov 6 00:16:04 1998 Jeffrey A Law (law@cygnus.com)
* reorg.c (fill_simple_delay_slots): Fix typo.
* romp.h (LEGITIMIZE_ADDRESS): Fix typo.
Fri Nov 6 00:10:00 1998 Jan Hubicka (hubicka@freesoft.cz)
* i386.md (extendsidi2): Use # in the output template.
(extendsidi splitters): New splitters.
Thu Nov 5 11:13:27 1998 Nick Clifton <nickc@cygnus.com>
* configure.in: Use unknown-elf.h as tm_file for arm-elf
configurations.
* configure: Regenerate.
Thu Nov 5 07:59:05 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* jump.c (init_label_info, delete_barrier_successors,
mark_all_labels, delete_unreferenced_labels,
delete_noop_moves, calculate_can_reach_end): New functions broken
out of jump_optimize.
(jump_optimize): Use them.
Thu Nov 5 07:57:45 1998 Andrew MacLeod <amacleod@cygnus.com>
* except.c (expand_fixup_region_end): Make sure outer context labels
are not issued in an inner context during cleanups.
Thu Nov 5 04:03:06 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (addsi3, subsi3): No new temporaries once cse is
no longer expected.
Thu Nov 5 03:29:19 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (addsi3, subsi3): Expand to a DImode temporary so as
to expose this midpoint to CSE.
Thu Nov 5 03:42:54 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (movdf_const_intreg_sp64): Enable again.
Thu Nov 5 10:53:01 1998 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* configure.in: Bring over gcc2 change of Nov 19 1997.
Wed Nov 4 23:43:08 1998 Graham <grahams@rcp.co.uk>
* toplev.c (output_lang_identify): Make definition dependent on
ASM_IDENTIFY_LANGUAGE.
* print-rtl.c (spaces): Make static.
Wed Nov 4 22:16:36 1998 Hans-Peter Nilsson <hp@axis.se>
* extend.texi: Clarify proper uses for register clobbers in asms.
Wed Nov 4 22:16:36 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* recog.h (enum op_type): Define.
(constrain_operands): Adjust prototype.
(recog_op_type): Declare new variable.
* recog.c (recog_op_type): New variable.
(insn_invalid_p): Allow modifying an asm statement after reload.
(extract_insn): Set up recog_op_type.
(constrain_operands): Lose INSN_CODE_NUM arg. All callers changed.
Don't compute operand types, use recog_op_type.
Use the information computed by extract_insn instead of the previous
method of finding it by insn code number.
* caller-save.c (init_caller_save): Use extract_insn, not insn_extract.
* reorg.c (fill_slots_from_thread): Likewise.
* reload1.c (reload_as_needed): Likewise.
(gen_reload): Likewise.
(inc_for_reload): Likewise.
(reload_cse_simplify_operands): Likewise.
Use the information computed by extract_insn instead of the previous
method of finding it by insn code number.
* genattrtab.c (write_attr_case): Generate call to extract_insn, not
insn_extract.
* final.c (final_scan_insn): Use extract_insn, not insn_extract.
(cleanup_operand_subregs): Use extract_insn, not insn_extract.
Use the information computed by extract_insn instead of the previous
method of finding it by insn code number.
* regmove.c (find_matches): Likewise. Change meaning of the return
value to be nonzero if the optimization can be performed, zero if
not. All callers changed.
Shorten some variable names to fix formatting problems.
(regmove_optimize): Shorten some variable names to fix formatting
problems.
Use the information computed by extract_insn instead of the previous
method of finding it by insn code number.
* regclass.c (scan_one_insn): Likewise.
(record_reg_classes): Don't compute operand types, use recog_op_type.
* reload.c (find_reloads): Lose CONSTRAINTS1 variable; use
recog_constraints instead.
Wed Nov 4 21:37:46 1998 Jeffrey A Law (law@cygnus.com)
* rtl.h (flow2_completed): Declare.
* flow.c (flow2_completed): Definition.
* toplev.c (rest_of_compilation): Set and clear flow2_completed
as necessary.
Wed Nov 4 19:15:37 1998 Melissa O'Neill <oneill@cs.sfu.ca>
* Makefile.in (libcpp.a): Ranlib libcpp.a.
* cppulp.c (user_label_prefix): Initialize.
Wed Nov 4 19:07:08 1998 John Wehle (john@feith.com)
* flow.c (mark_regs_live_at_end): Mark the stack pointer as live
at a RETURN if current_function_sp_is_unchanging is set.
Wed Nov 4 18:16:29 1998 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* emit-rtl.c (try_split): Fixed error in Oct 10 patch.
Wed Nov 4 15:11:15 1998 Geoffrey Noer <noer@cygnus.com>
* i386/cygwin32.h (MASK_WIN32, MASK_CYGWIN, MASK_WINDOWS, MASK_DLL,
TARGET_WIN32, TARGET_CYGWIN, TARGET_WINDOWS, TARGET_DLL): New.
(SUBTARGET_SWITCHES): Add -mno-cygwin, -mcygwin, and -mdll options.
(CPP_PREDEFINES): Don't define __CYGWIN32__ here.
(STARTFILE_SPEC): Handle -mdll, -mno-cygwin options.
(CPP_SPEC): Handle -mno-cygwin option. Define __CYWIN__ in addition
to __CYGWIN32__.
(LIB_SPEC): Handle -mno-cygwin option.
(LINK_SPEC): Handle -mdll.
Wed Nov 4 22:56:14 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload.c (find_reloads): Fix test for usage by other reload
to handle secondary reloads properly.
Wed Nov 4 17:25:10 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* reload1.c (ELIMINABLE_REGS, NUM_ELIMINABLE_REGS): Introduce an
intermediate structure which has exactly the members provided by
ELIMINABLE_REGS. Define NUM_ELIMINABLE_REGS in terms of the
static intermediate structure.
(init_elim_table): Xmalloc() `reg_eliminate', and initialize it
from the intermediate structure. Do the same analogous fix in
the case where ELIMINABLE_REGS is not defined.
Tue Nov 3 20:50:03 1998 Jeffrey A Law (law@cygnus.com)
* pa.h (SELECT_SECTION): Fix thinko.
Tue Nov 3 17:51:36 1998 Jim Wilson <wilson@cygnus.com>
* dwarf2out.c (output_call_frame_info): Comments on last change.
Tue Nov 3 07:51:43 1998 Richard Earnshaw (rearnsha@arm.com)
* arm.c (add_constant): When taking the address of an item in the
pool, get the mode of the item addressed.
* arm.c (final_prescan_insn case INSN): If an insn doesn't
contain a SET or a PARALLEL, don't consider it for conditional
execution.
Restore ABI compatibility for NetBSD.
* arm/netbsd.h (DEFAULT_PCC_STRUCT_RETURN): Override setting in
arm.h.
(RETURN_IN_MEMORY): Likewise.
Mon Nov 2 11:46:17 1998 Doug Evans <devans@canuck.cygnus.com>
* m32r/m32r.c (m32r_expand_block_move): Fix byte count computations.
(m32r_output_block_move): Rewrite bytes < 4 handling.
Mon Nov 2 10:10:35 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* configure.in: Call AC_FUNC_VFORK.
* collect2.c: Define VFORK_STRING as a printable string for
error messages (either "vfork" or "fork".) If HAVE_VFORK_H is
defined, include vfork.h. If VMS is defined, define vfork()
appropriately. Remove vfork check on USG, we're using autoconf.
(collect_execute): Pass VFORK_STRING to fatal_perror instead of
checking locally what string to pass.
(scan_prog_file): Likewise.
(scan_libraries): Likewise.
* gcc.c: Remove vfork check on USG, we're using autoconf.
Besides, no calls to vfork/fork occur in this file.
* protoize.c: Likewise.
Mon Nov 2 07:52:28 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* configure.in (DEFAULT_LINKER): Renamed from LD.
(DEFAULT_ASSEMBLER): Renamed from AS; reverted Schwab's patch.
(gcc_cv_as): Try $DEFAULT_ASSEMBLER before $AS.
* configure: Rebuilt.
Mon Nov 2 01:48:10 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* BUGS: Fix the regexp for `more' to find the appropriate node.
Reported by Joerg Pietschmann <joerg_pietschmann@zkb.ch>
* BUGS: Added link to the WWW FAQ.
Sun Nov 1 18:27:15 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Nov 1 11:04:32 1998 Jeffrey A Law (law@cygnus.com)
* From Christian Gafton:
* i386/linux.h (CPP_PREDEFINES): Add -D__i386__.
* sparc/linux.h (CPP_PREDEFINES): Add -D__sparc__.
* sparc/linux64.h (CPP_PREDEFINES): Add -D__sparc__.
Sat Oct 31 21:42:39 1998 Mark Mitchell <mark@markmitchell.com>
* c-common.c (c_get_alias_set): Allow all type-punning through
unions. Don't get confused about the type of a bit-field, despite
the antics of build_modify_expr.
Sat Oct 31 22:35:29 1998 Jean-Pierre Radley <jpr@jpr.com>
* fixinc.sco: Parameterize #include_next values.
* fixinc/fixinc.sco: Likewise.
Sat Oct 31 20:39:35 1998 Jeffrey A Law (law@cygnus.com)
* toplev.c (rest_of_compilation): No longer set reload_completed.
* reload1.c (reload): Set it here. Perform instruction splitting
after reload has completed if we will be running the scheduler
again.
Sat Oct 31 12:30:02 1998 Jeffrey A Law (law@cygnus.com)
* jump.c (jump_optimize): Initialize mappings from INSN_UID to
EH region if exceptions are enabled and we're performing cross
jump optimizations.
(find_cross_jump): Exit loop if the insns are in different EH regions.
Sat Oct 31 10:02:48 1998 Mark Mitchell <mark@markmitchell.com>
* dwarf2out.c (output_call_frame_info): Use
ASM_OUTPUT_DWARF_DELTA4 for the CIE offset to match frame.c.
Sat Oct 31 10:23:14 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
Reinstall Apr 24th fix, lost during May 6th gcc2 merge:
* c-common.c (check_format_info): Don't check for the 'x'
format character twice, instead check for 'x' and 'X'
Fri Oct 30 14:50:25 1998 Jeffrey A Law (law@cygnus.com)
* configure.in (assembler features): Also make gas is configured if
we find it in the source tree.
Fri Oct 30 13:23:20 1998 Richard Henderson <rth@cygnus.com>
* i386.c (i386_comp_type_attributes): Compare whether the
attributes are defined, not their tree nodes.
Fri Oct 30 11:39:47 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* configure.in (gxx_include_dir): Bitten by autoconf quoting
characters. :-(
* configure: Rebuilt.
Fri Oct 30 10:43:29 1998 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* configure.in: Ignore non-absolute value in $AS.
Fri Oct 30 00:54:25 1998 Peter Jakubek <pjak@snafu.de>
* m68k.h (INDIRECTABLE_1_ADDRESS_P): Fix thinko.
Fri Oct 30 00:42:34 1998 Mark Elbrecht <snowball3@usa.net>
* configure.in (msdosdjgpp): Set exeext and target_alias.
Thu Oct 29 23:55:43 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* flow.c (XNMALLOC): New macro.
(flow_int_list_blocks, basic_block_succ, basic_block_pred): New
static variables.
(add_edge, add_edge_to_label): New static functions.
(free_bb_memory): New function.
(flow_delete_insn): Delete function.
(basic_block_drops_in): Delete variable.
(find_basic_blocks): Allocate and initialize basic_block_head,
basic_block_succ. Don't allocate basic_block_drops_in.
Call free_bb_memory at the beginning.
(find_basic_blocks_1): Don't do multiple passes.
Delete code to compute basic_block_drops_in.
After calling make_edges, mark blocks reached by current block live.
Update test for unreachable live blocks.
(mark_label_ref): Delete args X, CHECKDUP. Add PRED arg. All callers
changed.
Simplify to call add_edge_to_label when a LABEL_REF is found.
(make_edges): Simplify to call add_edge_to_label instead of
mark_label_ref most of the time.
Compute here whether control drops into the next block.
(delete_unreachable_blocks): Return void. All callers changed.
Delete unreachable blocks in reverse order.
After deleting all unreachable blocks, renumber the remaining ones
and update n_basic_blocks.
(delete_block): Speed up deletion a bit.
Don't set basic_block_drops_in for deleted blocks.
(free_basic_block_vars): Don't free basic_block_drops_in.
(life_analysis_1): Update to use new edge representation.
(dump_flow_info): Delete code to print basic block info; call
dump_bb_data instead.
(compute_preds_succs): Delete code to recompute basic_block_drops_in
and uid_block_number.
Simply copy the previously computed cfg.
(dump_bb_data): New arg LIVE_INFO. All callers changed.
Print register lifetime information if LIVE_INFO is nonzero.
* basic-block.h (dump_bb_data): Adjust prototype.
* gcse.c (gcse_main): Update call to dump_bb_data.
* rtl.h (free_bb_memory): Declare.
* toplev.c (rest_of_compilation): Call free_bb_memory.
* reload1.c (struct elim_table): Delete MAX_OFFSET member.
(update_eliminable_offsets): Don't compute it.
(set_initial_elim_offsets): Don't initialize it.
Break out some code into set_initial_label_offsets so the rest of
this function can be called from reload_as_needed.
Assume that INITIAL_FRAME_POINTER_OFFSET is defined when
ELIMINABLE_REGS isn't.
(set_initial_label_offsets): New function, broken out of
set_initial_elim_offsets.
(set_offsets_for_label): New function, broken out of set_label_offsets
and reload_as_needed.
(reload): Call the two new functions.
(reload_as_needed): Call set_initial_elim_offsets instead of
duplicating the code. Likewise for set_offsets_for_label.
* reload1.c (choose_reload_regs): Fix typo in Oct 17 change.
(emit_reload_insns): Ensure that when we set reg_reloaded_valid for
any hard reg, reg_reloaded_dead contains valid data.
Thu Oct 29 22:30:54 1998 Marcus Meissner <Marcus.Meissner@informatik.uni-erlangen.de>
* i386.c (i386_comp_type_attributes): Return nonzero for mismatched
"stdcall" and "cdecl" attributes.
Thu Oct 29 19:05:17 1998 Jim Wilson <wilson@cygnus.com>
* sched.c (update_flow_info): Add code to ! found_orig_dest case to
handle deleted no-op moves of hard registers.
* haifa-sched.c (update_flow_info): Likewise.
Thu Oct 29 18:07:47 1998 Jeffrey A Law (law@cygnus.com)
* mips.md (reload_{in,out}{si,di}): Emit a USE of HILO at the end
of the sequences to reload the HILO register which do not actually
reference HILO.
Thu Oct 29 12:39:35 1998 Jim Wilson <wilson@cygnus.com>
* c-common.c (c_get_alias_set): Handle ARRAY_REF of union field.
Thu Oct 29 14:10:22 1998 Andrew MacLeod <amacleod@cygnus.com>
* except.c (emit_eh_context): Make the EH context register stay alive
at -O0 so stupid.c doesn't get confused.
1998-10-29 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* emit-rtl.c (try_split): Do not try to split a BARRIER.
Thu Oct 29 01:33:54 1998 Jan Hubicka <hubicka@freesoft.cz>
Jeffrey A Law (law@cygnus.com)
* i386.md: Change ix86_cpu == PROCESSOR_PENTIUM to TARGET_PENTIUM.
(zero_extendsidi2): Use # in output template and handle completely by
splits.
(zero_extend splitters): New define_splits.
(ashiftrt_32): New pattern.
Wed Oct 28 22:58:35 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (append_random_chars): New fn.
(get_file_function_name_long): Use it.
Wed Oct 28 22:27:05 1998 Richard Henderson <rth@cygnus.com>
* Makefile.in (cc1): Put C_OBJS, and thence @extra_c_objs@ last.
(LIBCPP_OBJS): New. Add cppulp.o.
(cppmain, fix-header): Depend on and use libcpp.a.
* configure.in (extra_c_objs, extra_cxx_objs): Use libcpp.a instead
of the individual object files.
* objc/Make-lang.in (cc1obj): Put OBJC_OBJS, and thence @extra_c_objs@,
last.
* cccp.c (user_label_prefix): New.
(main): Set it off -f*leading-underscore.
(special_symbol): Use it.
* cpplib.c (special_symbol): Likewise.
(cpp_handle_option): Handle -f*leading-underscore.
* cppulp.c: New file.
* output.h (user_label_prefix): Declare it.
* dwarf2out.c (ASM_NAME_TO_STRING): Prepend user_label_prefix.
* toplev.c (f_options, main): Handle -f*leading-underscore.
* defaults.h (ASM_OUTPUT_LABELREF): Use asm_fprintf instead of
referencing USER_LABEL_PREFIX directly.
* config/nextstep.h (ASM_OUTPUT_LABELREF): Likewise.
* m32r/m32r.h (ASM_OUTPUT_LABELREF): Likewise.
* final.c (asm_fprintf): Use user_label_prefix instead.
* arm/thumb.c (thumb_print_operand): Likewise.
* gcc.c (default_compilers): Pass -f*leading-underscore on to
cpp wherever appropriate.
Wed Oct 28 23:09:25 1998 Robert Lipe <robertl@dgii.com>
* sco5.h (SUBTARGET_SWITCHES): Add documentation for OpenServer-
specific compiler switches.
Wed Oct 28 21:05:53 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (c-common.o): Depend on c-pragma.h. Use $(RTL_H) instead
of rtl.h.
Wed Oct 28 20:52:47 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gcc.c (EXTRA_SPECS, extra_specs): Introduce an intermediate
structure which has exactly the members provided by EXTRA_SPECS.
Xmalloc() the real `extra_specs', and initialize it from this
intermediate structure.
* alpha.h (EXTRA_SPECS): Revert change for missing initializers.
* mips.h (EXTRA_SPECS): Likewise.
* sparc.h (EXTRA_SPECS): Likewise.
Wed Oct 28 16:46:07 1998 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* function.c (purge_addressof_1): Instead of aborting when a
bitfield insertion as a replacement for (MEM (ADDRESSOF)) does not
work just put the ADDRESSOF on stack. Otherwise remember all such
successful replacements, so that exactly the same replacements
can be made on the REG_NOTEs. Remove the special case for CALL
insns again.
(purge_addressof_replacements): New variable.
(purge_addressof): Clear it at end.
1998-10-28 Zack Weinberg <zack@rabi.phys.columbia.edu>
* c-lang.c: Declare extern char *yy_cur if USE_CPPLIB.
(lang_init): Call check_newline always.
* c-lex.c (init_parse) [USE_CPPLIB=1]: After calling
cpp_start_read, set yy_cur and yy_lim to read from
parse_in.token_buffer, so that we'll see the first #line
directive.
* cpplib.c (cpp_start_read): finclude the main input file
before processing -include/-imacros. Process -imacros and
-include separately, and handle -include by stacking a
buffer for the file in question as if it'd been #included.
* toplev.c (documented_lang_options): Recognize -H when
USE_CPPLIB is on.
1998-10-28 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c: Merge do_once into do_pragma. Break file handling
code out of do_include.
Move append_include_chain, deps_output,
file_cleanup, redundant_include_p, import_hash,
lookup_import, add_import, read_filename_string, read_name_map,
open_include_file, finclude, safe_read to cppfiles.c.
Move prototypes for deps_output, append_include_chain,
finclude to cpplib.h. Move definition of struct
file_name_list there also.
* cppfiles.c: New file. Contains all the above functions
broken out of cpplib.c; also hack_vms_include_specification
from cccp.c and find_include_file, a new function broken out of
do_include.
* Makefile.in (cppmain): Depend on cppfiles.o.
(fix-header): Likewise.
(cppfiles.o): New target.
* configure.in (--enable-c-cpplib): Add cppfiles.o to
extra_c_objs. Add ../cppfiles.o to extra_cxx_objs.
Wed Oct 28 14:06:49 1998 Jim Wilson <wilson@cygnus.com>
* dwarfout.c (dwarfout_file_scope_decl): If DECL_CONTEXT, don't abort
if pending_types is non-zero.
(dwarfout_finish): Verify pending_types is zero before finishing.
Wed Oct 28 10:29:09 1998 Nick Clifton <nickc@cygnus.com>
* expr.c (convert_move): Use shifts to perform the move if a
suitable extend pattern cannot be found. Code written by
Richard Henderson <rth@cygnus.com>.
Wed Oct 28 03:59:29 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* regclass.c (renumber, regno_allocated): New static variables, moved
out of allocate_reg_info.
(allocate_reg_info): Move these two variables outside the function.
Move code to free memory into new function free_reg_info.
(free_reg_info): New function, broken out of allocate_reg_info.
* toplev.c (compile_file): Call free_reg_info, not allocate_reg_info.
* rtl.h (allocate_reg_info): Don't declare.
(free_reg_info): Declare.
* final.c (cleanup_subreg_operands): ASM_INPUTs need no treatment.
Wed Oct 28 02:38:12 1998 Jason Merrill <jason@yorick.cygnus.com>
* toplev.c (compile_file): Temporarily revert last change.
Wed Oct 28 00:00:35 1998 Jason Merrill <jason@yorick.cygnus.com>
* c-typeck.c (convert_for_assignment): Parenthesize.
1998-10-28 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* reload1.c (delete_output_reload): Avoid ambigous else.
Wed Oct 28 00:10:35 1998 Jeffrey A Law (law@cygnus.com)
* toplev.c (compile_file): Call allocate_reg_info to free register
table memory.
* rtl.h (allocate_reg_info): Declare.
* PROJECTS: Remove entry for local spilling.
* final.c (cleanup_subreg_operands): New function.
(final_scan_insn): Use it.
(alter_subreg): Clear the "used" field when we turn a SUBREG into
a REG.
* reload1.c (reload): Delete CLOBBER insns and also cleanup SUBREG
operands when reload has finished.
* reload.h (cleanup_subreg_operands): Declare..
* flow.c (life_analysis_1): No longer delete CLOBBER insns after
reload. Handled in reload itself.
Tue Oct 27 23:32:34 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload1.c (verify_initial_offsets): New function.
(reload): Call it after reload_as_needed. Also verify that the frame
size stays constant during reload_as_needed.
* i386.h (CONST_DOUBLE_OK_FOR_LETTER_P): Undo Jul 26 change.
* reload.h (struct insn_chain): Add need_operand_change element.
* reload1.c (new_insn_chain): Clear it.
(calculate_needs_all_insns): Set it; don't overload need_reload.
(reload_as_needed): Use it.
* reload.c (find_reloads_address): Use BASE_REG_CLASS instead of
reload_address_base_reg_class throughout. Similar for INDEX_REG_CLASS
and reload_address_index_reg_class.
(find_reloads_address_1): Likewise.
* reload.h (reload_address_base_reg_class,
reload_address_index_reg_class): Don't declare.
* reload1.c (reg_old_renumber, pseudo_previous_regs,
pseudo_forbidden_regs, bad_spill_regs_global): New static variables.
(used_spill_regs): Now static.
(reload_address_base_reg_class, reload_address_index_reg_class,
regs_explicitly_used, counted_for_groups, counted_for_nongroups,
basic_block_needs, max_needs, group_size, group_mode, max_groups,
max_nongroups, max_needs_insn, max_groups_insn, max_nongroups_insn,
forbidden_regs):
Deleted variables.
(init_reload): Delete code to compute base/index reg classes.
(reload): Delete variable J.
Delete code to manage basic_block_needs.
Don't compute regs_explicitly_used.
Allocate, initialize and free reg_old_renumber, pseudo_forbidden_regs,
pseudo_previous_regs.
Initialize bad_spill_regs_global.
Don't call order_regs_for_reload here.
Don't initialize spill_reg_order and n_spills.
Don't forbid explicitly used regs to be used for spill regs.
Change main loop to infinite loop, with explicit break statements.
Make SOMETHING_CHANGED variable local to that loop.
Don't initialize max_needs, max_groups, max_nongroups, max_needs_insn,
max_groups_insn, max_nongroups_insn, group_size, group_mode.
Make sure spilled_pseudos is cleared before calling spill_hard_reg or
new_spill_reg.
Don't call dump_needs.
Delete code to reset potential_reload_regs.
Delete code to terminate loop conditional on the global needs variables
showing no further needs.
(calculate_needs_all_insns): Return void. All callers changed.
Initialize something_needs_elimination here, not in reload.
Delete avoid_return_reg kludge.
(calculate_needs): Lose AVOID_RETURN_REG and GLOBAL args, return void.
All callers changed.
Initialize the group_mode and group_size elements of the arg CHAIN.
Delete code to manage basic_block_needs.
Operate on elements of CHAIN instead of global variables.
Delete avoid_return_reg kludge.
(find_tworeg_group): Lose GLOBAL arg, take CHAIN arg, return void.
All callers changed.
Operate on elements of CHAIN instead of global variables.
Delete special SMALL_REGISTER_CLASSES code.
Delete spill_failure code; now in new_spill_reg.
(find_group): Lose GLOBAL arg, take CHAIN arg, return void.
All callers changed.
Operate on elements of CHAIN instead of global variables.
(maybe_mark_pseudo_spilled): New static function.
(find_reload_regs): Lose GLOBAL arg, take CHAIN arg, return void.
All callers changed.
Operate on elements of CHAIN instead of global variables.
Call order_regs_for_reload here, not in reload.
Initialize spill_reg_order and n_spills.
Simplify test whether an asm insn is involved.
Delete spill_failure code; now in new_spill_reg.
Call maybe_mark_pseudo_spilled for everything marked as live in
CHAIN. Merge CHAIN's used_spill_regs into the global variable
used_spill_regs.
(dump_needs): Take CHAIN arg. No longer static, to prevent the
compiler from optimizing this function (now unused) away.
Operate on elements of CHAIN instead of global variables.
(possible_group_p): Lose MAX_GROUPS arg, take CHAIN arg. All callers
changed.
Operate on elements of CHAIN instead of global variables.
(count_possible_groups): Lose GROUP_SIZE, GROUP_MODE, MAX_GROUPS args,
take CHAIN arg. All callers changed.
Operate on elements of CHAIN instead of global variables.
(new_spill_reg): Lose MAX_NEEDS, MAX_NONGROUPS, GLOBAL args, take
CHAIN, NONGROUP args. Return void. All callers changed.
Verify caller isn't trying to spill a pseudo.
Simplify test for illegal reg, just use bad_spill_regs.
Generate better error messages.
Operate on elements of CHAIN instead of global variables.
Mark spilled register in CHAIN's used_spill_regs element.
Don't call spill_hard_reg.
(spill_hard_reg): Lose GLOBAL arg, return void. All callers changed.
Mark spilled hard regs in bad_spill_regs_global.
Mark affected pseudos in spilled_pseudos, but don't spill them.
(ior_hard_reg_set): New static function.
(finish_spills): Return int. All callers changed.
Compute spill_reg_order, n_spills and spill_regs here. Also update
regs_ever_live for regs used as spills.
For every pseudo in spilled_pseudos, spill it and mark the previous
hard reg it had in pseudo_previous_regs. Compute which hard regs
are used as spills in insns during which it is live, and retry global
register allocation. Update all life information in the
reload_insn_chain not to include pseudos without hard regs.
Call alter_reg for all affected speudos.
(scan_paradoxical_subregs): Disable SMALL_REGISTER_CLASSES special
case, it's not clear what it's supposed to do.
(hard_reg_use_compare): Take bad_spill_regs into account.
(pseudos_counted): New static variable.
(count_pseudo): New static function.
(order_regs_for_reload): Take CHAIN arg. All callers changed.
Initialize bad_spill_regs from bad_spill_regs_global, then merge any
hard registers explicitly used across the current insn into the set.
Compute hard_reg_n_uses taking only pseudos live across this insn
into account.
Tweak sorting of potential_reload_regs.
(compare_spill_regs): Delete function.
(reload_as_needed): Don't sort the spill_regs array, it's computed
in proper order in finish_spills.
Delete avoid_return_reg kludge.
Delete code to manage basic_block_needs.
(allocate_reload_reg): Minor speed/readability tweaks.
Operate on elements of CHAIN instead of global variables.
(choose_reload_regs): Lose AVOID_RETURN_REG arg. All callers changed.
Delete avoid_return_reg kludge.
Initialize reload_reg_used from CHAIN's used_spill_regs element.
Delete unused label FAIL.
(reload_combine): Replace reload_address_index_reg_class with
INDEX_REGS.
Don't use used_spill_regs to determine information about lifetime of
hard regs.
Tue Oct 27 13:15:02 1998 Nick Clifton <nickc@cygnus.com>
* toplev.c (display_help): Ignore empty target specific
options, and if -W is also specified on the command line then
display undocumented options.
* config/arm/arm.c: Updated with changes in devo sources.
* config/arm/arm.h: Updated with changes in devo sources.
* config/arm/lib1funcs.asm: Updated with changes in devo sources.
* config/arm/lib1thumb.asm: Add ELF support.
Tue Oct 27 16:11:43 1998 David Edelsohn <edelsohn@gnu.org>
* collect2.c (aix64_flag): New variable.
(main, case 'b'): Parse it.
(GCC_CHECK_HDR): Object magic number must match mode.
(scan_prog_file): Only check for shared object if valid header.
Print debugging if header/mode mismatch.
Tue Oct 27 10:15:02 1998 Nick Clifton <nickc@cygnus.com>
Added support for arm-elf-linux configuration, submitted by Philip
Blundell <pb@nexus.co.uk>, and integrated this with the arm-elf
code developed by Catherine Moore <clm@cygnus.com>. The following
files are affected:
* configure.in: Add arm-*-linux-gnu, armv2-*-linux and arm-*-elf
targets.
* configure: Regenerated.
* config/arm/aout.h: Add default definitions of REGISTER_PREFIX,
USER_LABEL_PREFIX and LOCAL_LABEL_PREFIX. Make other macro
definitions conditional on their not having been already defined.
* config/arm/lin1funcs.asm: Add ELF only macros to generate .size
and .type directives, and add "(PLT)" qualification to function
calls.
* config/arm/linux.h: Deleted. This file is now superseded by
either linux-elf.h or linux-aout.h.
* config/arm/linux-gas.h: Define `inhibit_libc' if cross-compiling.
(CLEAR_INSN_CACHE): New macro, currently disabled (awaiting kernel
support).
Move definitions from old linux.h file here.
* config/arm/elf.h: New file. Generic ARM/ELF support.
* config/arm/linux-aout.h: New file. Support for Linux with a.out.
* config/arm/linux-elf.h: New file. Support for Linux with ELF.
* config/arm/linux-elf26.h: New file. Support for Linux with ELF
using the 26bit APCS.
* config/arm/unknown-elf.h: New file. Support for OS'es other
than Linux with ELF.
* config/arm/t-arm-elf: New file. makefile fragment for arm-elf
builds.
* config/arm/coff.h: Include aout.h for basic assembler macros.
Add support for -mstructure_size_boundary=<n> command line option.
* config/arm/arm.h: Add support for -mstructure_size_boundary=<n>
command line option. Make macro definitions conditional on their
not having been already defined.
* config/arm/arm.c: Add support for -mstructure_size_boundary=<n>
command line option.
Tue Oct 27 08:56:46 1998 Andrew MacLeod <amacleod@cygnus.com>
* dwarfout.c (ASM_OUTPUT_DWARF_STRING_NEWLINE): ASM_OUTPUT_DWARF_STRING
has been changed to not include a newline. Use this macro instead.
(output_enumeral_list, const_value_attribute, name_attribute,
comp_dir_attribute, prototyped_attribute, producer_attribute,
inline_attribute, pure_or_virtual_attribute, output_inheritance_die,
dwarfout_file_scope_decl, generate_new_sfname_entry,
generate_macinfo_entry, dwarfout_init, dwarfout_finish): Use
ASM_OUTPUT_DWARF_STRING_NEWLINE macro.
Mon Oct 26 13:35:02 1998 Richard Henderson <rth@cygnus.com>
* combine.c (subst): Process the inputs to a parallel asm_operands
only once.
Mon Oct 26 13:32:31 1998 Richard Henderson <rth@cygnus.com>
* stmt.c (expand_asm_operands): Accept `=' or `+' at any position.
Mon Oct 26 12:53:14 1998 Jeffrey A Law (law@cygnus.com)
* tm.texi (ASM_OUTPUT_MAX_SKIP_ALIGN): Document.
Mon Oct 26 00:36:58 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Oct 25 23:36:52 1998 Jason Merrill <jason@yorick.cygnus.com>
* stmt.c (expand_fixup): Set fixup->before_jump to a
NOTE_INSN_DELETED instead of a NOTE_INSN_BLOCK_BEG.
Sun Oct 25 15:49:57 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (recog.o): Depend on toplev.h.
(insn-emit.o): Depend on recog.h.
(insn-peep.o): Depend on recog.h and insn-config.h.
* combine.c (simplify_set): Remove unused variable `scratches'.
* final.c (final_scan_insn): Wrap declaration of variables `vlen'
and `idx' in macro conditional controlling their use.
* genemit.c (main): Make the generated output file include
recog.h. Don't have it declare `insn_operand_constraint', since
we get it from recog.h.
* genpeep.c (main): Make the generated output file include
insn-config.h and recog.h.
* recog.c: Include toplev.h.
(extract_insn): Remove unused variable `p'.
* regclass.c (fix_register): Add missing braces around initializer
for `what_option'.
(allocate_reg_info): Move variable `i' into the scope where it is
used. Change its type to `size_t'.
Sun Oct 25 13:10:15 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload.c (push_reload): When merging reloads, make sure
that reload_in_reg and reload_in are from the same reload in
all cases.
Sun Oct 25 12:07:00 1998 Mumit Khan <khan@xraylith.wisc.edu>
* i386/crtdll.h (CPP_PREDEFINES): Fix typo.
* i386/mingw32.h (CPP_PREDEFINES): Likewise.
Fri Oct 23 23:42:03 1998 David Edelsohn <edelsohn@gnu.org>
* loop.c (loop_has_tablejump): New variable.
(prescan_loop): Scan for it.
(insert_bct): Replace explicit scan with use of it.
* regclass.c (regclass): Restore loop variable j.
(record_reg_classes): Deterine op_types modifiers and initialize
classes[i] before matching constraints. Handle matching
constraints 5-9.
Fri Oct 23 13:55:48 1998 Jim Wilson <wilson@cygnus.com>
* m32r/m32r.c (gen_split_move_double): Call alter_subreg. Delete
subreg support.
Fri Oct 23 16:19:24 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* mips.h (EXTRA_SPECS): Add missing initializers.
Fri Oct 23 16:08:39 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* sparc.h (EXTRA_SPECS): Add missing initializers.
(sparc_defer_case_vector): Provide a prototype.
* svr4.h (ASM_OUTPUT_ASCII): Cast STRING_LIMIT to (long) when
comparing it to the result of a pointer subtraction.
Fri Oct 23 15:34:14 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* alpha.c (override_options): Use ISDIGIT(), not isdigit(). Cast
the argument to (unsigned char).
* alpha.h (EXTRA_SPECS): Add missing initializers.
(ASM_GENERATE_INTERNAL_LABEL): Ensure the argument matches the
format specifier.
Fri Oct 23 13:12:35 1998 Jeffrey A Law (law@cygnus.com)
* flow.c (life_analysis_1): Enable "rescan" code after reload.
(propagate_block): Delete dead code after reload.
* sched.c (update_flow_info): Revert Oct 19, 1998 change. Brings
back Oct 15, 1998 change.
* haifa-sched.c (update_flow_info): Likewise.
* flow.c (life_analysis_1): Delete CLOBBER insns after reload.
* mn10200.md (truncated shift): Accept constant inputs too.
Fri Oct 23 04:06:57 1998 Richard Earnshaw (rearnsha@arm.com)
* machmode.h (mode_mask_array): No longer const.
* rtl.c (init_rtl): Fully initialize it if EXTRA_CC_MODES defined.
Fri Oct 23 11:19:06 1998 Martin v. Löwis <loewis@informatik.hu-berlin.de>
* frame.c: Somewhat explain `FDE'.
Suggested by Brendan Kehoe
Fri Oct 23 00:56:11 1998 Jason Merrill <jason@yorick.cygnus.com>
* expr.c (pending_chain): Move up.
(save_expr_status): Do save pending_chain.
(restore_expr_status): And restore it.
* function.h (struct function): Add pending_chain.
1998-10-23 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* reorg.c (relax_delay_slots): Fixed test for mostly_true_jump. The
did not match the code.
Fri Oct 23 00:07:01 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* regclass.c (regclass): Break out some code into new function
scan_one_insn, and into regclass_init.
(init_cost): New static variable, moved out of regclass.
(regclass_init): Initialize it here, not in .
(scan_one_insn): New static function, broken out of regclass.
* recog.c (apply_change_group): Break out some code into new
function insn_invalid_p.
(insn_invalid_p): New static fn, broken out of apply_change_group.
Thu Oct 22 22:34:42 1998 Jim Wilson <wilson@cygnus.com>
* reload1.c (reload_as_needed): When rewrite POST_INC, verify
reg_reloaded_contents matches incremented pseudo.
* v850/v850.c (v850_reorg): Call alter_subreg. Delete subreg support.
Fri Oct 23 11:11:56 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* rtl.def (POST_MODIFY, PRE_MODIFY): New generalized operators for
addressing modes with side effects. These are currently
placeholders for the C4x target.
Thu Oct 22 16:46:35 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* loop.c (express_from): Make sure that when generating a PLUS of
a PLUS, any constant expression appears on the outermost PLUS.
Thu Oct 22 15:46:23 1998 Per Bothner (bothner@cygnus.com)
* Makefile.in (distdir-cvs, distdir-start): Clean up so it
works if "$(srcdir)" != ".".
Wed Oct 21 19:23:59 1998 Jim Wilson <wilson@cygnus.com>
* expmed.c (store_bit_field): If need to add a SUBREG, then remove
existing SUBREG if we can, otherwise abort.
Wed Oct 21 09:58:51 1998 Mark Mitchell <mark@markmitchell.com>
* c-common.c (c_apply_type_quals_to_decl): Don't crash when
`restrict' is applied to a non-pointer variable.
Wed Oct 21 09:18:58 1998 Mark Mitchell <mark@markmitchell.com>
* invoke.texi: Document -flang-isoc9x.
* Makefile.in (OBJS): Add splay-tree.o.
(c-common.o): Depend on rtl.h.
(splay-tree.o): List dependencies and provide build rule.
* rtl.h (record_alias_subset): New function.
* alias.c: Include splay-tree.h.
(alias_set_entry): New type.
(CHECK_ALIAS_SETS_FOR_CONSISTENCY): Remove.
(DIFFERENT_ALIAS_SETS_P): Use mem_in_disjoint_alias_sets_p.
(mems_in_disjoin_alias_sets_p): New function.
(alias_set_compare): Likewise.
(insert_subset_children): Likewise.
(get_alias_set_entry): Likewise.
* tree.h (TYPE_RESTRICT): New macro.
(TYPE_UNQUALIFIED): New manifest constant.
(TYPE_QUAL_CONST): Likewise.
(TYPE_QUAL_VOLATILE): Likewise.
(TYPE_QUAL_RESTRICT): Likewise.
(tree_type): Add restrict_flag. Reduce count of free bits.
(DECL_POINTER_ALIAS_SET): New macro.
(DECL_POINTER_ALIAS_SET_KNOWN_P): Likewise.
(tree_decl): Add pointer_alias_set.
(build_qualified_type): New function.
(build_type_variant): Define in terms of build_qualified_type.
* tree.c (set_type_quals): New function.
(make_node): Initialize DECL_POINTER_ALIAS_SET.
(build_type_attribute_variant): Use build_qualified_type and
set_type_quals.
(build_type_variant): Rename, and modify, to become...
(build_qualified_type): New function.
(build_complex_type): Use set_type_quals.
* c-tree.h (C_TYPE_OBJECT_P): New macro.
(C_TYPE_FUNCTION_P): Likewise.
(C_TYPE_INCOMPLETE_P): Likewise.
(C_TYPE_OBJECT_OR_INCOMPLETE_P): Likewise.
(c_apply_type_quals_to_decl): New function.
(c_build_qualified_type): New function.
(c_build_type_variant): Define in terms of c_build_qualified_type.
(flag_isoc9x): Declare.
* c-typeck.c (qualify_type): Use c_build_qualified_type.
(common_type): Change to use TYPE_QUALS.
(comptypes): Likewise.
(convert_for_assignment): Likewise.
* c-aux-info.c (gen_type): Likewise. Deal with `restrict'.
* c-decl.c (flag_isoc9x): Define.
(c_decode_option): Handle -flang-isoc9x.
(grokdeclarator): Update to handle restrict. Use TYPE_QUALS,
c_build_qualified_type, etc. Use c_apply_type_quals_to_decl.
* c-lex.c (init_lex): Deal with restrict.
(init_lex): Don't treat restrict as a reserved word in
-traditional mode, or without -flang-isoc9x.
* c-lex.h (rid): Add RID_RESTRICT.
* c-parse.gperf (restrict, __restrict, __restrict__): Make
equivalent to RID_RESTRICT.
* c-parse.in (TYPE_QUAL): Update comment.
* c-common.c: Include rtl.h.
(c_find_base_decl): New function.
(c_build_type_variant): Rename, and modify, to become ...
(c_build_qualified_type): New function.
(c_apply_type_quals_to_decl): Likewise.
(c_get_alias_set): For INDIRECT_REFs, check to see if we can find
a particular alias set for the reference.
* toplev.c (documented_lang_options): Add -flang-isoc9x.
Wed Oct 21 09:15:06 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.h (TARGET_SWITCHES): Document arm specific
command line switches.
Tue Oct 20 10:04:51 1998 Graham <grahams@rcp.co.uk>
* reload.c (loc_mentioned_in_p): Add missing braces to bind
else to correct if.
Mon Oct 19 16:34:05 1998 Tom Tromey <tromey@cygnus.com>
* gcc.c (option_map): Added --classpath and --CLASSPATH.
Tue Oct 20 10:59:02 1998 Gavin Romig-Koch <gavin@cygnus.com>
* regclass.c (fix_register): Add error message.
* invoke.texi (-fcall-used-REG,-fcall-saved-REG): Note the
new error message.
Tue Oct 20 10:12:17 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* c-decl.c (warn_missing_noreturn): New global variable.
(c_decode_option): Check for new flags -W{no-}missing-noreturn.
(finish_function): Implement missing noreturn warning.
* c-tree.h (warn_missing_noreturn): Declare extern.
* invoke.texi: Document new flags.
* toplev.c (documented_lang_options): Add description.
Tue Oct 20 22:16:11 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c (c4x_parallel_process): Disable until BCT
loop optimization stable for the C4x.
(c4x_rptb_info_t, c4x_dump, c4x_rptb_in_range, c4x_rptb_unjumped_loop,
c4x_rptb_find_comp_and_jump, c4x_rptb_loop_info_get,
c4x_rptb_emit_init, c4x_rptb_process): Deleted (superseded by BCT
loop optimization).
(c4x_address_conflict): Be more paranoid when packing a volatile
memref in a parallel load/store.
Tue Oct 20 21:56:05 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.md (repeat_block_top, repeat_block_end,
repeat_block_filler): Deleted.
(*ashlqi3_set, *ashrqi3_const_set, *ashrqi3_nonconst_clobber):
Condition code not set if destination register from 'c' class.
(*subbqi3_carry_clobber): Fix typo.
1998-10-18 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* reorg.c (steal_delay_list_from_target): Check for insns that
modify the condition codes and effect the direction of the jump
in the sequence.
Sat Oct 17 13:09:09 1998 Graham <grahams@rcp.co.uk>
* function.c (purge_addressof_1): Replace call to
emit_insns_before() with emit_insn_before().
Mon Oct 19 19:34:03 1998 Mike Stump <mrs@wrs.com>
* libgcc2.c (__pure_virtual): Call __terminate instead of _exit.
Mon Oct 19 13:26:24 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* jump.c (sets_cc0_p): Compile only if HAVE_cc0.
Mon Oct 19 11:40:56 1998 Jeffrey A Law (law@cygnus.com)
* gcse.c (compute_hash_table): Correctly identify hard regs which are
clobbered across calls.
* loop.c (scan_loop): Be more selective about what invariants are
moved out of a loop.
Mon Oct 19 10:46:58 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Oct 19 11:40:56 1998 Jeffrey A Law (law@cygnus.com)
* libgcc2.c (eh_context_static): Do not call malloc to allocate the
static eh_context structure.
Mon Oct 19 10:45:40 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* combine.c (recog_for_combine): Lose PADDED_SCRATCHES arg. All
callers changed.
(try_combine): Don't update max_scratch.
* flow.c (max_scratch, num_scratch): Delete variables.
(life_analysis_1): Don't initialize max_scratch.
(propagate_block): Don't update max_scratch.
(mark_set_1): Don't increment num_scratch.
* regs.h (max_scratch): Delete declaration.
Mon Oct 19 10:28:15 1998 Jeffrey A Law (law@cygnus.com)
* reload1.c (reload_reg_free_before_p): Hack. Return 0 if EQUIV
is nonzero. This is temporary!
* sched.c (update_flow_info): Handle death notes made invalid by
instruction splitting. Partially reverts Oct 15, 1998 patch.
* haifa-sched.c (update_flow_info): Likewise.
Sun Oct 18 17:31:26 1998 Jeffrey A Law (law@cygnus.com)
* function.c (uninitialized_vars_warning): Do not warn for a VAR_DECL
if it has a nonzero DECL_INITIAL.
Sat Oct 17 23:18:08 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (flow.o): Depend on recog.h.
* cpplib.h (directive_table): Add missing initializiers.
(finclude): Change type of variable `bsize' to size_t.
* cse.c (rtx_cost): Mark parameter `outer_code' with ATTRIBUTE_UNUSED.
* dwarfout.h (dwarfout_label): Wrap prototype in macro RTX_CODE.
* fix-header.c (lookup_std_proto): Cast the result of `strlen' to
`int' when comparing against one.
(cpp_file_line_for_message): Mark parameter `pfile' with
ATTRIBUTE_UNUSED.
(cpp_fatal): Mark parameter `pfile' with ATTRIBUTE_UNUSED.
* flow.c: Include recog.h.
(sbitmap_copy): Cast arguments 1 & 2 of `bcopy' to (PTR).
* function.c (thread_prologue_and_epilogue_insns): Mark parameter
`f' with ATTRIBUTE_UNUSED.
(reposition_prologue_and_epilogue_notes): Likewise.
* genopinit.c (gen_insn): Cast argument of ctype functions to
`unsigned char'.
* haifa-sched.c: Include recog.h.
(blockage_range): Cast result of UNIT_BLOCKED macro to (int) when
comparing against one.
* libgcc2.a (__throw): Revert ATTRIBUTE_UNUSED change for now.
* mips-tfile.c (parse_end): Cast the argument of ctype function to
`unsigned char'.
(parse_ent): Likewise.
(parse_input): Likewise.
* optabs.c (init_libfuncs): Likewise.
* protoize.c (find_rightmost_formals_list): Likewise.
* recog.h (const_double_operand): Fix typo in prototype.
* tlink.c (scan_linker_output): Cast the argument of ctype
function to `unsigned char'.
* toplev.c (check_lang_option): Cast the result of `strlen' to
`int' when comparing against one.
Sat Oct 17 13:09:09 1998 Graham <grahams@rcp.co.uk>
* gcse.c (dump_cuid_table): Correct typo.
Sat Oct 17 11:02:47 1998 Nick Clifton <nickc@cygnus.com>
* toplev.c (display_help): Prepend '-m' to target specific
options.
(check_lang_option): Ignore text after end of first word of a
language specific option.
Sat Oct 17 02:26:03 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload1.c (reg_used_by_pseudo): New static variable.
(choose_reload_regs): Initialize it.
Use it instead of testing spill_reg_order to determine whether a
pseudo is live in a hard register across the current insn.
Fix a typo in a reference to reload_reg_rtx.
* flow.c (propagate_block): Replace code that computes and uses
regs_sometimes_live with simpler code that just walks the set of
currently live registers.
* Makefile.in (insn-extract.o): Fix dependencies.
* genextract.c (main): Generate includes for insn-config.h and
recog.h.
Delete generation of declarations which are now in recog.h.
* genrecog.c (main): Delete generation of definitions which are
now in recog.c.
* local-alloc.c (block_alloc): Use extract_insn and the variables
it sets up instead of looking up values by insn_code.
* recog.c (recog_operand, recog_operand_loc, recog_dup_loc,
recog_dup_num): Define here instead of generating the definition in
genrecog.c.
(recog_n_operands, recog_n_dups, recog_n_alternatives,
recog_operand_mode, recog_constraints, recog_operand_address_p):
New variables.
(extract_insn): New function.
* recog.h (extract_insn): Declare function.
(which_alternative, recog_n_operands, recog_n_dups,
recog_n_alternatives, recog_operand_mode, recog_constraints,
recog_operand_address_p): Declare variables.
* regclass.c (n_occurrences): New static function.
* reload.c (n_occurrences): Delete function.
(find_reloads): Use extract_insn.
* reload.h (n_occurrences): Delete declaration.
Sat Oct 17 01:17:51 1998 Jeffrey A Law (law@cygnus.com)
* reload1.c (reload_as_needed): Fix test for when to call
update_eliminable_offsets.
Fri Oct 16 20:40:50 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
Fix consistency problems with reg_equiv_{mem,address};
Improve reload inheritance;
* reload.c (reload_out_reg): New variable.
(loc_mentioned_in_p, remove_address_replacements): New functions.
(remove_replacements): Deleted.
(push_reload): Set reload_out_reg[i].
When merging, also set reload_{in,out}_reg[i], and remove
duplicate address reloads.
(combine_reloads): Copy reload_out_reg[i].
(find_reloads): Do make_memloc substitution also when
reg_equiv_memory_loc[regno] and num_not_at_initial_offset
are both nonzero.
Include *recog_operand_loc in commutativity operand changes.
Generate optional output reloads.
Delete reference to n_memlocs. Don't set *recog_operand_loc before
processing operands. Call make_memloc in reg_equiv_address code.
Set *recog_operand_loc only after processing operands, and only
if replace is true. Return a value.
When changing address reload types for operands that didn't get
reloaded, use RELOAD_FOR_OPADDR_ADDRESS for
RELOAD_FOR_INPADDR_ADDRESS / RELOAD_FOR_OUTADDR_ADDRESS reloads.
Don't emit USEs for pseudo SUBREGs when not replacing.
(find_reloads_address): Do make_memloc substitution also when
reg_equiv_memory_loc[regno] and num_not_at_initial_offset
are both nonzero.
(find_reloads_toplev): Likewise.
Call make_memloc in reg_equiv_address code.
(debug_reload_to_stream): Add code to output reload_out_reg.
(make_memloc): Delete local variable i, ifdefed out code, and
references to memlocs and n_memlocs.
(memlocs, n_memlocs): Delete.
(push_secondary_reload): Clear reload_out_reg.
(find_reloads_address_1): Provide memrefloc argument to all calls
to find_reloads_address.
In AUTO_INC code, handle non-directly addressable equivalences properly.
* reload.h (reload_out_reg, num_not_at_initial_offset): Declare.
(find_reloads): Add return type.
(remove_address_replacements, deallocate_reload_reg): Declare.
* reload1.c (num_not_at_initial_offset): No longer static.
(delete_address_reloads, delete_address_reloads_1): Likewise.
(deallocate_reload_reg): New function.
(spill_reg_stored_to): New array.
(eliminate_regs): Don't substitute from reg_equiv_memory_loc.
(eliminate_regs_in_insn): Move assignments of previous_offset and
max_offset fields, and recalculation of num_not_at_initial_offset
into new static function:
(update_eliminable_offsets) .
(reload_as_needed): Call update_eliminable_offsets after calling
find_reloads.
Call forget_old_reloads_1 with contents of reloaded auto_inc
expressions if the actual addressing can't be changed to match the
auto_inc.
(choose_reload_regs): For inheritance, replace
reload_reg_free_before_p test with reload_reg_used_at_all test, and
remove stand-alone reload_reg_used_at_all test.
Use reload_out_reg to determine which reload regs have output reloads.
Treat reload_override_in more similar to inherited reloads.
Handle (subreg (reg... for inheritance.
For flag_expensive_optimizations, add an extra pass to remove
unnecessary reloads from known working inheritance.
Delete obsolete code for pseudos replaced with MEMs.
Handle inheritance from auto_inc expressions.
(emit_reload_insns): If reload_in is a MEM, set OLD to
reload_in_reg[j].
Don't reload directly from oldequiv; if it's a pseudo with a
stack slot, use reload_in[j].
Check that reload_in_reg[j] is a MEM before replacing reload_in
from reg_reloaded_contents.
Include non-spill registers in reload inheritance processing.
Also try to use reload_out_reg to set spill_reg_store /
reg_last_reload_reg.
In code to set new_spill_reg_store, use single_set to find out if
there is a single set.
Add code that allows to delete optional output reloads.
Add code to allow deletion of output reloads that use no spill reg.
At the end, set reload_override_in to oldequiv.
Also call delete_output_reload if reload_out_reg is equal to old
in oldequiv code.
Add code to call delete_output_reload for stores with no matching load.
Set / use spill_reg_stored_to.
Handle case where secondary output reload uses a temporary, but
actual store isn't found.
When looking for a store of a value not loaded in order to call
delete_output_reload, count_occurrences should return 0 for no
loads; but discount inherited input reloadill_reg_stored_to.
Do checks for extra uses of REG. Changed all
callers.
Use delete_address_reloads.
(reload): Take return value of find_reloads into account.
If a no-op set needs more than one reload, delete it.
(reload_reg_free_before_p): RELOAD_FOR_INPUT
can ignore RELOAD_FOR_INPUT_ADDRESS / RELOAD_FOR_INPADDR_ADDRESS
for the same operand.
(clear_reload_reg_in_use): Check for other reloads that keep a
register in use.
(reload_reg_free_for_value_p): Handle RELOAD_FOR_OPERAND_ADDRESS /
RELOAD_FOR_OPADDR_ADDR.
Take into account when an address address reload is only needed
for the address reload we are considering.
(count_occurrences): Use rtx_equal_p for MEMs.
(inc_for_reload): Return instruction that stores into RELOADREG.
New argument two, IN, and rtx. Changed all callers.
(calculate_needs_all_insns, reload_as_needed):
Don't clear after_call for a CLOBBER.
Keep track of how many hard registers need to be copied from
after_call, and don't clear after_call before we have seen
that much copies, or we see a different instruction.
Fri Oct 16 10:58:23 1998 Jeffrey A Law (law@cygnus.com)
* flow.c (find_basic_blocks_1): Do not delete unreachable blocks
after reload has completed.
Fri Oct 16 17:26:10 1998 Dave Brolley <brolley@cygnus.com>
* cpplib.c (cpp_get_token): Replace whitespace that occurs between
a macro name and the next token with a single blank if that whitespace
is in a macro buffer and the next token is not '('.
Fri Oct 16 15:44:02 1998 Dave Brolley <brolley@cygnus.com>
* cccp.c (rescan): Handle multibyte characters ending in backslash.
(rescan): Likewise.
(skip_if_group): Likewise.
(skip_to_end_of_comment): Likewise.
(macarg1): Likewise.
(discard_comments): Likewise.
(change_newlines): Likewise.
Fri Oct 16 15:26:24 1998 Dave Brolley <brolley@cygnus.com>
* c-lex.c (yylex): Fix unaligned access of wchar_t.
Fri Oct 16 10:47:53 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.h (TARGET_SWITCHES): Add --help documentation.
(TARGET_OPTIONS): Add --help documentation.
Fri Oct 16 11:49:01 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* rtl.h (sets_cc0_p): Revert Oct 14 ATTRIBUTE_NORETURN change.
Fri Oct 16 07:08:46 1998 Bruce Korb <korb@datadesign.com>
* fixinc/* Moved in from ../contrib directory in preparation
for integrating it into the normal build process. In particular,
fixinc/Makefile.in must be config-ed into the build directory
as fixinc/Makefile. Proposed patches to ./Makefile.in and
./configure.in will be "in the mail" momentarily.
Fri Oct 16 08:13:46 1998 David S. Miller <davem@pierdol.cobaltnet.com>
* cse.c (cse_basic_block): Fixup hash flushing loop so we do not
accidently walk into the free list. Comment how that can happen.
(invalidate): Fix indentation.
Thu Oct 15 23:53:29 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
Jeffrey A Law (law@cygnus.com)
* flow.c (life_analysis_1): Do not clobber regs_ever_live after
reload. Never perform rescans of the insn chain after reload.
(propagate_block): Do not delete insn or create new autoinc addressing
modes after reload.
* jump.c (jump_optimize): Unconditionally use the code that was
previously conditional on PRESERVE_DEATH_INFO_REGNO_P.
* reload1.c (reload): When reloading is finished, delete all
REG_DEAD and REG_UNUSED notes.
(emit_reload_insns): Delete all code that was conditional on
PRESERVE_DEATH_INFO_REGNO_P.
(no_longer_dead_regs): Delete variable.
(reload_cse_delete_death_notes): Delete function.
(reload_cse_no_longer_dead): Delete function.
(reload_cse_regs_1): Delete all code to handle deletion of death
notes.
(reload_cse_noop_set_p): Likewise.
(reload_cse_simplify_set): Likewise.
(reload_cse_simplify_operands): Likewise.
(reload_cse_move2add): Likewise.
* reorg.c (used_spill_regs): Delete declaration.
(max_label_num_after_reload): Delete declaration.
(find_dead_or_set_registers): Don't assume that spill regs are
dead at a CODE_LABEL.
* rtlanal.c (dead_or_set_regno_p): Death notes are always accurate,
even after reload.
* sched.c (sched_analyze_insn): Likewise.
(update_flow_info): Likewise.
* haifa-sched.c (sched_analyze_insn): Likewise.
(update_flow_info): Likewise.
* tm.texi (PRESERVE_DEATH_INFO_REGNO_P): Delete documentation.
* toplev.c (max_label_num_after_reload): Delete variable.
(rest_of_compilation): Don't set max_label_num_after_reload.
Call life_analysis after reload_cse_regs if optimizing.
* config/gmicro/gmicro.h: Delete comment referring to
PRESERVE_DEATH_INFO_REGNO_P.
* config/i386/i386.h: Likewise.
* config/m88k/m88k.h: Likewise.
* config/m32r/m32r.h (PRESERVE_DEATH_INFO_REGNO_P): Delete definition.
* config/sh/sh.h: Likewise.
Thu Oct 15 19:48:41 1998 David Edelsohn <edelsohn@gnu.org>
* loop.c (strength_reduce): Restore marking bct_p as
ATTRIBUTE_UNUSED.
* rs6000.c (optimization_options): Change #ifdef HAIFA to
HAVE_decrement_and_branch_on_count.
(small_data_operand): Remove TARGET_ELF condition for marking
parameters ATTRIBUTE_UNUSED.
Thu Oct 15 11:45:51 1998 Robert Lipe <robertl@dgii.com>
* config/i386/sco5.h (MAX_OFILE_ALIGNMENT): Define.
(SELECT_SECTION): Resync with svr4.h.
Thu Oct 15 12:42:13 1998 David Edelsohn <edelsohn@gnu.org>
* loop.c (strength_reduce): Undo Oct 14 change marking bct_p
ATTRIBUTE_UNUSED.
Thu Oct 15 00:57:55 1998 Robert Lipe <robertl@dgii.com>
* c-pragma.c (handle_pragma_token): Test for null tree before
dereferencing TREE_CODE.
Thu Oct 15 17:36:48 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c: Convert to use GEN_INT.
(c4x_parallel_process): Rework to handle new repeat loop structure.
* config/c4x/c4x.md: Convert to use GEN_INT.
(rptb_end): Convert to use GE test. Replace uses with clobbers.
(decrement_and_branch_on_count): Likewise.
* config/c4x/c4x.h (REPEAT_BLOCK_PROCESS): Deleted hook now that
loop.c has the desired functionality.
(rc_reg_operand): New prototype.
* config/c4x/t-c4x: Can now build all front ends.
Wed Oct 14 23:27:08 1998 Didier FORT (didier.fort@fedex.com)
* fixincludes: Fix up rpc/{clnt,svr,xdr}.h for SunOS.
Wed Oct 14 22:13:28 1998 Joel Sherrill (joel@OARcorp.com)
* Makefile.in (stmp-fixinc): Do not install assert.h if not desired.
* config/t-rtems: Do not install assert.h -- use newlib's.
Wed Oct 14 21:57:08 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* combine.c (combine_instructions): When finished, call init_recog.
* regmove.c (optimize_reg_copy_3): Reject volatile MEMs.
Wed Oct 14 16:10:22 1998 Per Bothner <bothner@cygnus.com>
* toplev.c: If flag_syntax_only, don't open or write assembler file.
Wed Oct 14 13:26:05 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cppalloc.c (memory_full): Mark function prototype with
ATTRIBUTE_NORETURN.
* demangle.h (collect_exit): Likewise.
* fix-header.c (v_fatal, fatal): Likewise.
* gcc.c (pfatal_with_name, pfatal_pexecute, fatal, fancy_abort):
Likewise.
* gcov.c (print_usage): Likewise.
* genattr.c (fatal, fancy_abort): Likewise.
* genattrtab.c (fatal, fancy_abort): Likewise.
* gencodes.c (fatal, fancy_abort): Likewise.
* genconfig.c (fatal, fancy_abort): Likewise.
* genemit.c (fatal, fancy_abort): Likewise.
* genextract.c (fatal, fancy_abort): Likewise.
* genflags.c (fatal, fancy_abort): Likewise.
* genopinit.c (fatal, fancy_abort): Likewise.
* genoutput.c (fatal, fancy_abort): Likewise.
* genpeep.c (fatal, fancy_abort): Likewise.
* genrecog.c (fatal, fancy_abort): Likewise.
* libgcc2.c (__eprintf, __default_terminate, __sjthrow,
__sjpopnthrow, __throw): Likewise.
* objc/objc-act.c (objc_fatal): Likewise.
* protoize.c (usage, aux_info_corrupted,
declare_source_confusing): Likewise.
* rtl.c (dump_and_abort): Likewise.
* rtl.h (sets_cc0_p): Likewise.
* toplev.c (float_signal, pipe_closed): Likewise.
1998-10-14 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* dwarf2out.c (expand_builtin_dwarf_reg_size): Look at all ranges
when generating the decision tree for the general case.
* config/m68k/m68k.h (HARD_REGNO_MODE_OK): Don't accept modes
wider that 12 bytes in fpu regs or wider than 8 byte in fpa regs.
Wed Oct 14 11:14:02 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (sched.o): Depend on recog.h.
* alias.c (REG_BASE_VALUE): Cast the result of REGNO() macro to
(unsigned) when comparing against one.
(find_base_value): Likewise.
(record_base_value): Cast variable `regno' to (unsigned) when
comparing against one. Cast the result of REGNO() macro to
(unsigned) when comparing against one.
(memrefs_conflict_p): Change type of variables `r_x' and `r_y' to
unsigned.
(init_alias_analysis): Add unsigned variable `ui'. Use it as loop
variable where an unsigned index is needed.
* caller-save.c (init_caller_save): Cast `-1' to (enum insn_code)
before comparing against one.
* collect2.c: Add prototypes for functions `error', `fatal' and
`fatal_perror'. Make these functions take variable arguments
instead of faking it with a fixed number of args.
(write_c_file_stat): Cast the argument of ctype macro to (unsigned
char).
* combine.c (can_combine_p): Mark parameter `pred' with
ATTRIBUTE_UNUSED.
(find_split_point): Cast variable `src' to (unsigned
HOST_WIDE_INT) when comparing against one.
(simplify_rtx): Cast 1 to (unsigned HOST_WIDE_INT) in shift.
(simplify_logical): Likewise.
(force_to_mode): Cast result of INTVAL() macro to (unsigned
HOST_WIDE_INT) when comparing against one. Cast 1 to (unsigned
HOST_WIDE_INT) in shift.
(simplify_and_const_int): Cast result of INTVAL() macro to
`unsigned HOST_WIDE_INT' when comparing against one.
(merge_outer_ops): Cast variable const0 to `unsigned
HOST_WIDE_INT' when comparing against the result of
GET_MODE_MASK() macro.
(simplify_comparison): Likewise for variable `c0'. Cast variable
`const_op' to `unsigned HOST_WIDE_INT' when comparing against
one. Cast `1' to `unsigned HOST_WIDE_INT' in shift. Cast the
result of `GET_MODE_MASK()/2' to `HOST_WIDE_INT' when comparing
against one. Cast `1' to `unsigned HOST_WIDE_INT' in shift. Cast
result of INTVAL() macro to `unsigned HOST_WIDE_INT' when
comparing against one.
(distribute_notes): Wrap variable `cc0_setter' in macro `HAVE_cc0'.
config/mips/mips.c (gen_int_relational): Cast result of INTVAL()
macro to `unsigned HOST_WIDE_INT' when comparing against one.
(output_block_move): Cast `sizeof' expression to (int) when
comparing against one.
(function_arg): Cast BITS_PER_WORD to `unsigned' when comparing
against one.
(save_restore_insns): Cast `base_offset' to `long' to match format
specifier in fprintf.
* config/mips/mips.h (Pmode): Cast the result of `Pmode' macro
to `enum machine_mode'.
* flow.c (life_analysis_1): Remove unused variable `insn'.
* gcc.c (translate_options): Move variables `j' and `k' into the
scope in which they are used. Change their types to `size_t'.
(set_spec): Cast the argument of ctype macro to `unsigned char'.
(read_specs): Likewise.
(process_command): Cast `sizeof' to (int) when comparing against one.
(do_spec_1): Cast the argument of ctype macro to `unsigned char'.
(handle_braces): Cast both sides of `==' expression to `long' to
ensure sign matching.
(main): Cast variable `i' to `int' when comparing against one.
* gcov-io.h (__fetch_long): Change type of parameter `bytes' from
int to size_t. Cast variable `i' to size_t when comparing against
one.
* genattrtab.c (convert_set_attr_alternative): Remove unused
parameter `insn_code'. All callers changed.
(convert_set_attr): Likewise.
* genrecog.c (add_to_sequence): Cast result of XVECLEN() macro to
size_t when comparing against one. Likewise for variable `len'.
* global.c (global_alloc): Cast variable `max_regno' to size_t
when comparing against one. Likewise for variable `max_allocno'.
* jump.c (sets_cc0_p): Mark parameter `x' with ATTRIBUTE_UNUSED.
* local-alloc.c (validate_equiv_mem_from_store): Mark parameter
`set' with ATTRIBUTE_UNUSED.
(find_free_reg): Cast `sizeof' expression to (int) when comparing
against one.
* loop.c (count_loop_regs_set): Remove unused variable `dest'.
(strength_reduce): Mark parameter `bct_p' with ATTRIBUTE_UNUSED.
(get_condition): Cast variable `const_val' to `unsigned
HOST_WIDE_INT' when comparing against one. Cast unsigned
expression to HOST_WIDE_INT when comparing against one.
(insert_loop_mem): Mark parameter `data' with ATTRIBUTE_UNUSED.
(load_mems_and_recount_loop_regs_set): Cast variable `nregs' to
`unsigned' when comparing against one.
* protoize.c (is_id_char): Change type of parameter `ch' to
unsigned char.
(munge_compile_params): Cast argument of ctype macro to (const
unsigned char).
(process_aux_info_file): Cast variable `aux_info_size' to int when
comparing against one.
(forward_to_next_token_char): Cast argument of ctype macro to
`const unsigned char'.
(edit_formals_lists): Likewise.
(find_rightmost_formals_list): Likewise.
(add_local_decl): Likewise.
(add_global_decls): Likewise.
(edit_fn_definition): Likewise.
(do_cleaning): Likewise.
(scan_for_missed_items): Likewise.
(edit_file): Cast variable `orig_size' to (int) when comparing
against one.
(main): Cast argument of ctype macro to `const unsigned char'.
* recog.c (const_int_operand): Mark parameter `mode' with
ATTRIBUTE_UNUSED.
* regclass.c (record_reg_classes): Change type of variable `c' to
`unsigned char'. Cast `char' array index to `unsigned char'.
* reload.c (push_secondary_reload): Cast argument to
REG_CLASS_FROM_LETTER() macro to `unsigned char'.
* reload1.c (calculate_needs): Cast `char' array index to
`unsigned char'.
(set_label_offsets): Change type of variable `i' to unsigned int.
Cast result of XVECLEN() macro to unsigned when comparing against
one.
(mark_not_eliminable): Change type of variable `i' to unsigned.
(order_regs_for_reload): Likewise. Cast `max_regno' to unsigned
when comparing against one.
(reload_as_needed): Cast macro NUM_ELIMINABLE_REGS to (int) when
comparing against one.
(choose_reload_regs): Hide unused label `fail'.
(reload_cse_simplify_operands): Cast `char' array index to
`unsigned char'.
(reload_combine_note_store): Mark parameter `set' with
ATTRIBUTE_UNUSED. Cast UNITS_PER_WORD to unsigned when comparing
against one.
(reload_cse_move2add): Remove unused variable `src2'.
* sched.c: Include recog.h.
(sched_note_set): Remove unused parameter `b'. All callers
changed.
(split_hard_reg_notes): Likewise for parameter `orig_insn'.
(blockage_range): Cast result of UNIT_BLOCKED() macro to (int)
when comparing against one.
* stupid.c (stupid_find_reg): Mark parameter `changes_size' with
ATTRIBUTE_UNUSED. Cast `sizeof' expression to (int) when
comparing against one.
* unroll.c (precondition_loop_p): Remove unused parameter
`loop_end'. All callers changed.
Tue Oct 13 22:12:11 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* reload1.c (maybe_fix_stack_asms): New static function.
(reload): Call it.
* reload.h (compute_use_by_pseudos): Declare.
* reload1.c (spilled_pseudos, insns_need_reload): New variables.
(something_needs_reloads): Delete variable.
(finish_spills): New function.
(compute_use_by_pseudos): New function.
(delete_caller_save_insns): Lose argument FIRST. All callers changed.
Use the reload_insn_chain instead of walking the rtl directly.
(reload): Allocate and free spilled_pseudos.
Ensure that all calls of spill_hard_reg are followed by a call to
finish_spills.
Use the insns_need_reload list instead of something_needs_reloads
to find out if reload_as_needed must be called.
Clear unused_insn_chains at the end.
(calculate_needs_all_insns): Lose FIRST parameter. All callers
changed.
Delete code to keep track of current basic block.
Walk reload_insn_chain instead of the rtl structure. Build the
insns_need_reload chain.
Remember which insns need reloading/elimination by setting the
appropriate fields in struct insn_chain, not by putting modes on the
insn.
(calculate_needs): Lose THIS_BLOCK arg. Accept arg CHAIN instead of
arg INSN. All callers changed.
Delete declaration of struct needs.
Don't set something_needs_reloads.
Record insn needs in the CHAIN argument.
(spill_hard_reg): Record the affected pseudos in spilled_pseudos.
(reload_as_needed): Lose FIRST arg. All callers changed.
Walk the reload_insn_chain instead of the rtx structure.
Delete code to keep track of current basic block.
Rename one of the NEXT variables to OLD_NEXT.
(allocate_reload_reg): Accept arg CHAIN instead of arg INSN. All
callers changed.
(choose_reload_regs): Likewise.
(emit_reload_insns): Replace INSN and BB args with arg CHAIN. All
callers changed.
* caller-save.c (MOVE_MAX_WORDS): New macro. Use it throughout
instead of (MOVE_MAX / UNITS_PER_WORD) computation.
(hard_regs_live, hard_regs_need_restore): Delete variables.
(n_regs_saved): Now static.
(referenced_regs, this_insn_sets): New variables.
(setup_save_areas): Restructure the code a bit.
(restore_referenced_regs): Delete function.
(mark_referenced_regs): New function, similar to the old
restore_referenced_regs, but mark registers in referenced_regs.
(clear_reg_live): Delete function.
(mark_set_regs): Renamed from set_reg_live. All callers changed.
Only mark registers in this_insn_sets.
(save_call_clobbered_regs): Rework this function to walk the
reload_insn_chain instead of using the list of instructions directly.
Delete code to keep track of register lives, compute live regs on the
fly from information in the chain.
Instead of calling restore_referenced_regs, use mark_referenced_regs,
then walk the set it computes and call insert_restore as appropriate.
(insert_restore): Lose INSN and BLOCK args. Add CHAIN arg. All
callers changed.
Restructure the code a bit. Test hard_regs_saved instead of
hard_regs_need_restore.
(insert_save): Lose INSN and BLOCK args. Add CHAIN and TO_SAVE
args. All callers changed.
Restructure the code a bit. Use TO_SAVE to determine which regs to
save instead of more complicated test.
(insert_one_arg): Lose INSN and BLOCK args. Add CHAIN arg. All
callers changed.
Create a new insn_chain structure for the new insn and place it
into the chain.
* rtl.texi: Update documentation to reflect that reload no longer
puts modes on the insns.
1998-10-14 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* function.c (purge_addressof_1): Force the first argument of a
CALL insn to memory.
Wed Oct 14 00:38:40 1998 Jeffrey A Law (law@cygnus.com)
* rtl.h: Delete duplicate prototypes. Add some missing
prototypes.
* rtlanal.c (for_each_rtx): Formatting tweak.
1998-10-13 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* real.c (emdnorm and etoasc): Disable round to even for c4x target
to be compatible with TI compiler.
* Makefile.in (USER_H): Add va-c4x.h to definition.
Tue Oct 13 23:03:37 1998 Richard Henderson <rth@cygnus.com>
* function.c (purge_addressof_1): Fix typo in inequality: do
bitfield optimization for equal mode sizes.
* expmed.c (store_bit_field): Don't take subregs of subregs in
the movstrict case. Tidy a potential problem in the multi-word case.
(extract_bit_field): Likewise.
Tue Oct 13 22:12:11 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* flow.c (find_basic_blocks): Emit NOPs after normal calls in this
function.
Compute max_uid_for_flow by calling get_max_uid after the scan.
(find_basic_blocks_1): Don't emit NOPs here.
Tue Oct 13 22:05:49 1998 Richard Henderson <rth@cygnus.com>
* alias.c (base_alias_check): Accept new args for the modes of the
two references. Use them to determine if an AND can overlap. Update
all callers.
(memrefs_conflict_p): Assume sizes are aligned, and uses them
to determine if an AND can overlap.
Tue Oct 13 17:51:04 1998 Jim Wilson <wilson@cygnus.com>
* config/m68k/m68k.h (HARD_REGNO_MODE_OK): For FP regs, add REGNO >= 16
check. Add comment to document problems with TARGET_SUN_FPA version
of this macro.
* config/m68k/m68k.md (movxf+1): Support 'r'/'r' moves.
Tue Oct 13 17:46:18 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (gencheck.o): Depend on gansidecl.h.
* c-common.c (print_char_table): Add missing initializers.
(scan_char_table): Likewise.
(time_char_table): Likewise.
* c-decl.c (c_decode_option): Mark parameter `argc' with
ATTRIBUTE_UNUSED.
(declare_parm_level): Mark parameter `definition_flag' with
ATTRIBUTE_UNUSED.
* c-lex.c (readescape): Use `(unsigned)1' in shift.
(yylex): Likewise. Cast `sizeof' to an (int) when comparing
against one.
* calls.c (store_one_arg): Remove unused parameter `fndecl'. All
callers changed.
(emit_call_1): Mark parameters `fndecl' and `funtype' with
ATTRIBUTE_UNUSED.
(expand_call): Cast result of MIN() to (unsigned int) when
comparing against an unsigned value.
* cccp.c (pcfinclude): Remove unused parameter `limit'. All
callers changed.
(make_definition): Remove unused parameter `op'. All callers
changed.
(create_definition): Cast REST_EXTENSION_LENGTH to (long) when
comparing against the result of pointer arithmetic.
* config/mips/mips.h (FUNCTION_ARG_BOUNDARY): Cast to (unsigned)
when comparing against one.
* dwarf2out.c (dwarf2out_frame_debug): Cast REGNO() and
HARD_FRAME_POINTER_REGNUM to (unsigned) when comparing against
one.
(output_die): Move variable `i' into the scope in which it is
used. Change its type to `unsigned'.
(output_die): Cast the result of `strlen' to (int) when passing it
to ASM_OUTPUT_ASCII().
(output_pubnames): Likewise.
(output_line_info): Likewise.
* emit-rtl.c (global_rtl): Add missing initializers.
* explow.c (promote_mode): Mark parameter `for_call' with
ATTRIBUTE_UNUSED.
* expmed.c (expand_shift): Cast the result of GET_MODE_BITSIZE to
`unsigned HOST_WIDE_INT' when comparing against one.
(synth_mult): Change type of variable `cost' to int.
(emit_store_flag): Use `(unsigned HOST_WIDE_INT) 1' in shift.
* expr.c (copy_blkmode_from_reg): Cast BITS_PER_WORD to (unsigned)
when comparing against one.
(get_inner_reference): Change variable `alignment' to unsigned.
(expand_expr): Cast the result of GET_MODE_ALIGNMENT to (unsigned
int) when comparing against one.
(expand_builtin_setjmp): Change type of variable `i' to size_t.
* fold-const.c (div_and_round_double): Cast BASE to
(HOST_WIDE_INT) when comparing against one.
* gencheck.c: Include gansidecl.h.
(main): Mark parameter `argv' with ATTRIBUTE_UNUSED.
* optabs.c (gen_cond_trap): Mark parameters `code', `op2' and
`tcode' with ATTRIBUTE_UNUSED.
* real.c (edivm): Cast constant value to (unsigned long) in
expression compared against an unsigned value.
* stmt.c (expand_return): Cast BITS_PER_WORD to (unsigned) when
comparing against one.
(expand_end_case): Cast CASE_VALUES_THRESHOLD to (unsigned int)
when comparing against one.
* stor-layout.c (mode_for_size): Cast MAX_FIXED_MODE_SIZE to
(unsigned int) when comparing against one. Likewise for
GET_MODE_BITSIZE.
(smallest_mode_for_size): Likewise.
(save_storage_status): Mark parameter `p' with ATTRIBUTE_UNUSED.
(restore_storage_status): Likewise.
* toplev.c (debug_args): Add missing initializer.
(f_options): Spelling correction. Add missing initializers.
(documented_lang_options): Likewise.
(debug_end_source_file): Mark parameter `lineno' with
ATTRIBUTE_UNUSED.
* tree.c (valid_machine_attribute): Mark parameters `attr_args',
`decl' and `type' with ATTRIBUTE_UNUSED.
* varasm.c (decode_reg_name): Cast `sizeof' expression to (int)
when comparing against one.
(assemble_variable): Mark parameter `top_level' with
ATTRIBUTE_UNUSED.
(assemble_external_libcall): Mark parameter `fun' with
ATTRIBUTE_UNUSED.
(output_constant_pool): Mark parameters `fnname' and `fndecl' with
ATTRIBUTE_UNUSED.
Tue Oct 13 12:51:04 1998 Nick Clifton <nickc@cygnus.com>
* config/v850/lib1funcs.asm (_udivsi3): Add .type declaration.
Replace use of r5 with use of r19.
* config/v850/v850.h (LINK_POINTER_REGNUM): Define.
* config/v850/v850.c (compute_register_save_size): Allow for the
fact that helper functions save all registers, not just those used
by the function.
Replace constant 31 with macro LINK_POINTER_REGNUM.
* config/v850/v850.md: Use 'indirect_operand' rather than
'memory_operand' for bit test/set/clear patterns.
Tue Oct 13 11:49:14 1998 Jason Merrill <jason@yorick.cygnus.com>
* mips/iris6.h (ASM_OUTPUT_WEAK_ALIAS): Call ASM_GLOBALIZE_LABEL.
* varasm.c (assemble_start_function et al): Don't call
ASM_GLOBALIZE_LABEL for weak symbols.
Tue Oct 13 11:37:45 1998 Nick Clifton <nickc@cygnus.com>
* cse.c (equiv_constant): Check for NULL return from
gen_lowpart_if_possible().
Tue Oct 13 11:24:51 1998 Jeffrey A Law (law@cygnus.com)
* mn10200.md (addsi3, subsi3, negsi2): Only allow register operands.
* collect2.c (main): Pass -EL/-EB through to the compiler.
1998-10-12 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* expr.c (push_block): Handle targets where the stack grows
to higher addresses, but args grow to lower addresses and
ACCUMULATE_OUTGOING_ARGS is not defined.
Tue Oct 13 08:00:52 1998 Catherine Moore <clm@cygnus.com>
* config/v850/v850.c (print_operand): Extend meaning
of 'c' operands to support .vtinherit.
Tue Oct 13 21:38:35 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* config/c4x/c4x.c: Convert to gen_rtx_FOO.
Added ATTRIBUTE_UNUSED to unused function arguments.
(rc_reg_operand): New predicate.
(c4x_rptb_insert): New function.
(c4x_rptb_nop_p): Recognize modified rptb_top pattern.
(c4x_optimization_options): New function.
* config/c4x/c4x.md: Convert to gen_rtx_FOO.
(decrement_and_branch_on_count): New pattern.
(rptb_top): Modified pattern to work with BCT optimization.
* config/c4x/c4x.h (RC_REG): New register class.
(rc_reg_operand): Define prototype.
(IS_RC_REG): New macro.
(IS_RC_OR_PSEUDO_REG): New macro.
(IS_RC_OR_PSEUDO_REGNO): New macro.
(OPTIMIZATION_OPTIONS): Define.
Mon Oct 12 19:57:34 1998 Jason Merrill <jason@yorick.cygnus.com>
* collect2.c (extract_init_priority): No priority is 65535.
Mon Oct 12 12:10:37 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* Makefile.in (build_tooldir): New variable, same as old
$(tooldir), but without depending on $(libdir)/$(unlibsubdir).
(GCC_FOR_TARGET): Add -B$(build_tooldir)/bin/.
(bootstrap, bootstrap2, bootstrap3, bootstrap4): Likewise.
* configure.in (gxx_include_dir): Set default based on unlibsubdir.
* Makefile.in (tooldir): Likewise.
(cccp.o, cpplib.o): Use unlibsubdir implicitly through
gxx_include_dir, includedir and tooldir.
(protoize.o, unprotoize.o): Likewise.
Mon Oct 12 10:50:44 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.md: Replace (reg 24) with (reg:CC 24).
* config/arm/thumb.c (thumb_override_options): Add warning about
PIC code not being supported just yet.
Sun Oct 11 16:49:15 1998 John Wehle (john@feith.com)
* flow.c: Update comment.
(notice_stack_pointer_modification): New static function.
(record_volatile_insns): Use it.
(mark_regs_live_at_end): Mark the stack pointer as alive
at the end of the function if current_function_sp_is_unchanging
is set.
(life_analysis_1): Set current_function_sp_is_unchanging.
* function.c: Define it.
(init_function_start): Initialize it.
* output.h: Declare it.
* reorg.c (fill_simple_delay_slots, dbr_schedule): Mark
the stack pointer as alive at the end of the function if
current_function_sp_is_unchanging is set.
* i386.c (ix86_epilogue): Optimize the restoring
of the stack pointer.
Mon Oct 12 01:22:53 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Oct 11 23:04:30 1998 Robert Lipe <robertl@dgii.com>
* c-pragma.c (handle_pragma_token): If passed a token instead
of a tree, use that as the pack value.
Sun Oct 11 14:21:14 1998 Mark Mitchell <mark@markmitchell.com>
* flow.c (find_basic_blocks_1): Fix prototype.
Sun Oct 11 05:03:41 1998 Ken Raeburn <raeburn@cygnus.com>
* tree.h (DECL_NO_CHECK_MEMORY_USAGE): New macros.
(struct tree_decl): New fields no_check_memory_usage.
* c-common.c (enum attrs): Add A_NO_CHECK_MEMORY_USAGE.
(init_attributes): Register it as a new attribute.
(decl_attributes): Set flags on functions given that attribute.
* c-decl.c (duplicate_decls): Merge new attribute.
* expr.h (current_function_check_memory_usage): Declare new var.
* calls.c, expr.c, function.c, stmt.c, alpha.c, clipper.c, m88k.c,
pa.c, sparc.c: Replace uses of flag_check_memory_usage with
current_function_check_memory_usage.
* function.h: Add field to struct function.
* function.c (current_function_check_memory_usage): Define it.
(push_function_context_to, pop_function_context_from): Save and
restore it.
(expand_function_start): Set it, based on global flag and function
attribute.
* expr.c (expand_expr, case VAR_DECL): In memory-checking code, do
check non-automatic variables, to permit detection of writes to
read-only locations in embedded systems without memory management.
* calls.c (store_one_arg): Use ARGS_SIZE_RTX to get size of argument
when emitting chkr_set_right_libfunc call, even if the argument is
BLKmode or variable-sized; don't abort.
* optabs.c (init_optabs): Create Checker and __cyg_profile_*
symbols in Pmode, not VOIDmode.
Sun Oct 11 01:03:05 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cppexp.c: When forcing unsigned comparisons, cast both sides
of the operation.
* cpphash.h: Move static declaration of hashtab[]...
* cpphash.c: ...here.
* cpplib.c: Cast difference of two pointers to size_t before
comparing it to size_t. Cast signed to unsigned
before comparing to size_t. (FIXME: struct argdata should use
unsigned buffer sizes.)
* cpplib.h (struct cpp_reader): Declare token_buffer_size as
unsigned int. (CPP_WRITTEN): Cast return value to size_t.
(CPP_RESERVE): Parenthesize N for evaluation order, cast to
size_t before comparison.
Sun Oct 11 00:15:29 1998 Jeffrey A Law (law@cygnus.com)
* flow.c (find_basic_blocks): Delete "live_reachable_p" argument.
(find_basic_blocks_1): Similarly.
* output.h (find_basic_blocks): Fix prototype.
* gcse.c, toplev.c: Don't pass "live_reachable_p" argument to
find_basic_blocks anymore.
Sat Oct 10 22:00:34 1998 Richard Henderson <rth@cygnus.com>
* basic-block.h (EXECUTE_IF_SET_IN_SBITMAP): New macro.
(sbitmap_free, sbitmap_vector_free): New macros.
* output.h (rtl_dump_file): Declare.
Sat Oct 10 17:01:42 1998 Jeffrey A Law (law@cygnus.com)
* regmove.c (optimize_reg_copy_3): Honor TRULY_NOOP_TRUNCATION.
Fri Oct 9 22:08:05 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fp-bit.c (SFtype): Don't implicitly use int in declaration.
(DFtype): Likewise.
(_fpdiv_parts): Remove unused parameter `tmp', all callers changed.
(divide): Remove unused variable `tmp'.
(si_to_float): Cast numeric constant to (SItype) before comparing
it against one.
Fri Oct 9 16:03:19 1998 Graham <grahams@rcp.co.uk>
* flow.c (print_rtl_with_bb): Changed type of in_bb_p to match use.
* gcc.c (add_preprocessor_option): Correct typo when allocating
memory, sizeof() argument had one too many `*'.
(add_assembler_option): Likewise.
(add_linker_option): Likewise.
* gcov.c (output_data): Likewise.
* local-alloc.c (memref_used_between_p): Likewise.
(update_equiv_regs): Likewise.
* loop.c (strength_reduce): Likewise.
* reg-stack.c (record_asm_reg_life): Likewise.
(subst_asm_stack_reg): Likewise.
* reorg.c (dbr_schedule): Likewise.
Fri Oct 9 15:57:51 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* flow.c (life_analysis_1): Break out some functions.
(find_basic_blocks_1): Likewise. Also move some variables out and
make them static.
Rename NONLOCAL_LABEL_LIST arg to NONLOCAL_LABELS and initialize
new static var nonlocal_label_list with it.
(active_eh_region, nested_eh_region, label_value_list,
nonlocal_label_list): New static variables.
(make_edges, delete_unreachable_blocks, delete_block): New static
functions, broken out of find_basic_blocks_1.
(record_volatile_insns, mark_regs_live_at_end, set_noop_p,
noop_move_p): New static functions, broken out of life_analysis_1.
Fri Oct 9 15:49:29 1998 Richard Henderson <rth@cygnus.com>
* expmed.c (store_bit_field): Pun non-integral str_rtx modes.
Take extra care for op0 now possibly being a subreg.
(extract_bit_field): Likewise.
* function.c (purge_addressof_1): Revert Oct 4 change. Drop
the reg to memory if there is no equal sized integral mode.
* stor-layout.c (int_mode_for_mode): New function.
* machmode.h: Prototype it.
Fri Oct 9 14:26:44 1998 Jeffrey A Law (law@cygnus.com)
* global.c (build_insn_chain): Verify no real insns exist past the
end of the last basic block, then exit the loop.
Fri Oct 9 11:44:47 1998 David Edelsohn <edelsohn@gnu.org>
* loop.c (insert_bct): Ensure loop_iteration_var non-zero before use.
Thu Oct 8 21:59:47 1998 Dave Brolley <brolley@cygnus.com>
* emit-rtl.c (init_emit_once): Call INIT_EXPANDERS.
Thu Oct 8 22:03:45 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (RTX_COSTS): Add PROCESSOR_PPC604e cases.
Thu Oct 8 17:00:18 1998 Richard Henderson <rth@cygnus.com>
* flow.c (find_basic_blocks): Correctly determine when a call
is within an exception region.
Thu Oct 8 17:15:04 1998 Jeffrey A Law (law@cygnus.com)
* toplev.c (output_file_directive): Use DIR_SEPARATOR, not '/'.
* cpplib.h: Protect from multiple inclusions.
* cpplib.c: Fix minor formatting problems.
* i386/xm-cygwin32.h: Only define POSIX if it is not already defined.
* jump.c (jump_optimize): Revert accidental patch.
* Makefile.in (cpplib.o): Use unlibsubdir.
Thu Oct 8 12:50:47 1998 Jim Wilson <wilson@cygnus.com>
* loop.c (get_condition): Allow combine when either compare is
VOIDmode.
Thu Oct 8 11:31:01 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Thu Oct 8 12:21:14 1998 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* c-lex.c (remember_protocol_qualifiers): Handle RID_BYREF.
(init_lex): Initialize ridpointers[RID_BYREF].
* c-lex.h (enum rid): Add RID_BYREF.
* c-parse.gperf: Add RID_BYREF as a type qualifier.
* objc/objc-act.c (is_objc_type_qualifiers): Handle RID_BYREF.
(encode_type_qualifiers): Similarly.
* c-gperf.h: Rebuilt.
Thu Oct 8 05:56:00 1998 Jeffrey A Law (law@cygnus.com)
* c-common.c (type_for_mode): Only return TItype nodes when
HOST_BITS_PER_WIDE_INT is >= 64 bits.
* c-decl.c (intTI_type_node, unsigned_intTI_type_node): Only declare
when HOST_BITS_PER_WIDE_INT is >= 64 bits.
(init_decl_processing): Only create TItype nodes when
HOST_BITS_PER_WIDE_INT is >= 64 bits.
* c-tree.h (intTI_type_node, unsigned_intTI_type_node): Only declare
when HOST_BITS_PER_WIDE_INT is >= 64 bits.
Thu Oct 8 05:05:34 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* stmt.c (n_occurrences): New static function.
(expand_asm_operands): Verify that all constrains match in the
number of alternatives.
Verify that '+' or '=' are at the beginning of an output constraint.
Don't allow '&' for input operands.
Verify that '%' isn't written for the last operand.
* reload.c (find_reloads): Abort if an asm is found with invalid
constraints; all possible problems ought to be checked for earlier.
Thu Oct 8 04:26:20 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* flags.h (flag_branch_on_count_reg): Always declare.
* toplev.c (flag_branch_on_count_reg): Likewise.
* toplev.c: Fix typos.
* real.c (c4xtoe): Remove unused variables. Add some missing parens.
(toc4x): Similarly.
Thu Oct 8 01:25:22 1998 Richard Henderson <rth@cygnus.com>
* flow.c (find_basic_blocks): Calc upper bound for extra nops in
max_uids_for_flow.
(find_basic_blocks_1): Add a nop to the end of a basic block when
a trailing call insn does not have abnormal control flow.
* gcse.c (pre_transpout): New variable.
(alloc_pre_mem, free_pre_mem, dump_pre_data): Bookkeeping for it.
(compute_pre_transpout): Calculate it.
(compute_pre_ppinout): Use it to eliminate impossible placements
due to abnormal control flow through calls.
(compute_pre_data): Call compute_pre_transpout.
Wed Oct 7 21:40:24 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sol2-sld-64.h (ASM_CPU_SPEC): Fix typo.
Wed Oct 7 21:19:46 1998 Ken Raeburn <raeburn@cygnus.com>
* config/mips/mips.md (tablejump_internal3, tablejump_internal4
and matching define_insns): Tack on a `use' of the table label, so
flow analysis will recognize a tablejump.
Wed Oct 7 17:33:39 1998 Richard Henderson <rth@cygnus.com>
* gcse.c (pre_insert_insn): Tweek to notice that calls do not
always end basic blocks for abnormal edge reasons.
Wed Oct 7 14:40:43 1998 Nick Clifton <nickc@cygnus.com>
* config/i386/i386.h: Remove definition of
HANDLE_PRAGMA_PACK_PUSH_POP.
* config/i386/go32.h: Add definition of
HANDLE_PRAGMA_PACK_PUSH_POP.
* config/i386/win32.h: Add definition of
HANDLE_PRAGMA_PACK_PUSH_POP.
* config/i386/cygwin32.h: Add definition of
HANDLE_PRAGMA_PACK_PUSH_POP.
* c-pragma.c (insert_pack_attributes): Do not insert
attributes unless #pragma pack(push,<n>) is in effect.
Wed Oct 7 12:10:46 1998 Jim Wilson <wilson@cygnus.com>
* expr.c (emit_group_store): Handle a PARALLEL destination.
Wed Oct 7 10:07:29 1998 Richard Henderson <rth@cygnus.com>
* gcse.c (pre_insert_insn): When a call ends a bb, insert
the new insns before the argument regs are loaded.
Wed Oct 7 12:55:26 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (c-gperf.h): Add -L KR-C -F ', 0, 0' flags to gperf.
(c-parse.gperf): Update comments describing invocation flags.
(c-gperf.h): Regenerate using gperf 2.7.1 (19981006 egcs).
1998-10-07 Manfred Hollstein <manfred@s-direktnet.de>
* reload1.c (reload): Call free before clobbering the memory
locations or constants pointers.
Wed Oct 7 02:05:20 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sol2-sld-64.h (TRANSFER_FROM_TRAMPOLINE): Rework
for efficiency by checking whether we need to modify the current
stack permission at all.
(ASM_OUTPUT_CONSTRUCTOR, ASM_OUTPUT_DESTRUCTOR): Define.
* config/sparc/sparc.c (sparc_initialize_trampoline): Emit
__enable_execute_stack libcall here too if
TRANSFER_FROM_TRAMPOLINE is defined.
* config/sparc/sparc.h: Set TARGET_ARCH32 to a constant if
IN_LIBGCC2.
Wed Oct 7 02:27:52 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (DRIVER_DEFINES): Remove last change.
Wed Oct 7 01:08:43 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* jump.c (duplicate_loop_exit_test): Strip REG_WAS_0 notes off all
insns we're going to copy.
* regclass.c (reg_scan_mark_refs): Don't test X for NULL_RTX.
* loop.c (count_one_set): Add prototype.
* caller-save.c (restore_referenced_regs): Lose mode argument.
(insert_save): Lose mode argument.
(insert_restore): Lose mode argument.
(insert_one_insn): Lose mode argument.
(save_call_clobbered_regs): Lose mode argument.
(setup_save_areas): Take no argument and return void. All callers
changed.
Don't verify validity of memory addresses.
* reload.h (setup_save_ares): Adjust prototype.
(save_call_clobbered_regs): Likewise.
* reload1.c (delete_caller_save_insns): New function.
(caller_save_spill_class): Delete variable.
(caller_save_group_size): Delete variable.
(reload): Call setup_save_areas and save_call_clobbered_regs
in the main loop, before calling calculate_needs_all_insns.
Don't call save_call_clobbered_regs after the loop.
Call delete_caller_save_insns at the end of an iteration if
something changed.
Delete code to manage caller_save_spill_class.
Emit the final note before setting reload_first_uid.
Simplify test that determines whether reload_as_needed gets run.
(calculate_needs): Delete code to manage caller_save_spill_class.
Tue Oct 6 15:42:27 1998 Richard Henderson <rth@cygnus.com>
* collect2.c (main): Initialize ld_file_name.
Tue Oct 6 15:45:15 1998 Catherine Moore <clm@cygnus.com>
* config/sparc/sysv4.h (ASM_OUTPUT_SECTION_NAME): Don't
check for flag_function_sections.
Tue Oct 6 20:02:31 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* cse.c (insert_regs): Fix bug in Sep 24 change.
Tue Oct 6 17:00:42 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* flags.h (flag_dump_unnumbered): Declare.
* toplev.c (flag_dump_unnumbered): Don't declare.
* print-rtl.c (flags.h): Include.
(print_rtl_single): Add return value.
* rtl.h (print_rtl_single): Update declaration.
* flow.c (flag_dump_unnumbered): Don't declare.
(print_rtl_with_bb): Use return value of print_rtl_single.
Tue Oct 6 01:36:00 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* loop.c (count_one_set): New static function, broken out of
count_loop_regs_set.
(count_loop_regs_set): Call it.
* global.c (mark_reg_store): Handle clobbers here by not calling
set_preference.
(mark_reg_clobber): Just call mark_reg_store after ensuring SETTER
is in fact a clobber.
* integrate.c (process_reg_param): New function, broken out of
expand_inline_function.
(expand_inline_function): Call it.
* i386.md (addsidi3_1): Delete unused variable temp.
(addsidi3_2): Likewise.
(clstrstrsi): Delete unused variable addr1.
* rtl.h: Don't declare any functions also declared in recog.h.
* Makefile.in (stupid.o): Update dependencies.
(global.o): Likewise.
* global.c: Include reload.h.
(reg_becomes_live): New function.
(reg_dies): New function.
(build_insn_chain): New function.
(global_alloc): Call build_insn_chain before calling reload.
* reload.h (struct needs): New structure definition.
(struct insn_chain): Likewise.
(reload_insn_chain): Declare variable.
(new_insn_chain): Declare function.
* reload1.c (reload_startobj): New variable.
(reload_insn_chain): New variable.
(unused_insn_chains): New variable.
(new_insn_chain): New function.
(init_reload): Initialize reload_startobj, not reload_firstobj.
(reload): Initialize reload_firstobj.
Before returning, free everything on the reload_obstack.
* stupid.c: Include insn-config.h, reload.h and basic-block.h.
(reg_where_dead_chain, reg_where_born_exact, reg_where_born_clobber,
current_chain): New variables.
(reg_where_born): Delete variable.
(REG_WHERE_BORN): New macro.
(find_clobbered_regs): New function.
(stupid_life_analysis): Don't allocate/free reg_where_born.
Allocate and free reg_where_born_exact, reg_where_born_clobber,
reg_where_dead_chain.
Use REG_WHERE_BORN instead of reg_where_born.
While processing the insns, build the reload_insn_chain with
information about register lifetimes.
(stupid_reg_compare): Use REG_WHERE_BORN instead of reg_where_born.
(stupid_mark_refs): Replace arg INSN with arg CHAIN. All callers
changed.
Compute and information about birth and death of pseudo registers in
reg_where_dead_chain, reg_where_born_exact and reg_where_born_clobber.
Delete code to set elements of reg_where_born.
Mon Oct 5 22:34:30 1998 Alexandre Petit-Bianco <apbianco@cygnus.com>
* tree.def (GOTO_EXPR): Modified documentation.
* expr.c (expand_expr): Expand GOTO_EXPR into a goto or a computed
goto.
Mon Oct 5 22:43:36 1998 David Edelsohn <edelsohn@gnu.org>
* unroll.c (loop_iteration_var, loop_initial_value, loop_increment
loop_final_value, loop_comparison_code): No longer static.
(unroll_loop): Delete loop_start_value update.
* loop.h (loop_iteration_var, loop_initial_value, loop_increment,
loop_final_value, loop_comparison_code): Extern.
(loop_start_value): Delete extern.
* loop.c (loop_can_insert_bct, loop_increment, loop_start_value,
loop_comparison_value, loop_comparison_code): Delete.
(loop_optimize): Remove initialization for deleted variables.
(strength_reduce): Delete analyze_loop_iterations call. Only call
insert_bct if flag_branch_count_on_reg set.
(analyze_loop_iterations): Delete.
(insert_bct): Remove iteration count calculation. Move checks for
viable BCT optimization to here. Obtain iteration count from
loop_iterations and correct for unrolling. Check for enough
iteration to be beneficial. Comment out runtime iteration count
case.
(insert_bct): Print iteration count in dump file. Remove
loop_var_mode and use word_mode directly.
* rs6000.h (processor_type): Add PROCESSOR_PPC604e.
* rs6000.c (rs6000_override_options): Use it.
(optimization_options): Enable use of flag_branch_on_count_reg.
* rs6000.md (define_function_unit): Describe 604e.
1998-10-05 Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* loop.c (move_movables): Corrected threshold calculation for
moved_once registers.
Mon Oct 5 21:18:45 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* loop.c (combine_givs_p): Fix test for identical givs.
Mon Oct 5 10:11:28 1998 Nick Clifton <nickc@cygnus.com>
* dwarf2out.c (gen_subprogram_die): If errorcount nonzero, don't
call abort if the function is already defined.
Mon Oct 5 10:02:36 1998 Jeffrey A Law (law@cygnus.com)
* combine.c (simplify_rtx): Do not replace TRUNCATE with a SUBREG if
truncation is not a no-op.
Mon Oct 5 09:02:04 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Oct 5 08:19:55 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Oct 5 01:07:23 1998 Torbjorn Granlund <tege@matematik.su.se>
* expmed.c (expand_divmod): Don't widen for computing remainder
if we seem to have a divmod pattern for needed mode.
Mon Oct 5 01:01:42 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (macroexpand): Correct off-by-one error in handling
of escapes.
Sun Oct 4 23:58:30 1998 Richard Henderson <rth@cygnus.com>
* combine.c (expand_field_assignment): Don't do bitwise operations
on MODE_FLOAT; pun to MODE_INT if possible.
Sun Oct 4 18:33:24 1998 Jason Merrill <jason@yorick.cygnus.com>
scott snyder <snyder@d0sgif.fnal.gov>
* tlink.c (scan_linker_output): Recognize errors from irix 6.2
linker. Recognize mangled names in quotes.
Sun Oct 4 02:58:20 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* config/sparc/sparc.md (ashldi3+1): Name it ashldi3_sp64.
(ashlsi3_const1, ashldi3_const1): New combiner patterns.
(ashrsi3_extend, ashrsi3_extend2): New combiner patterns.
(lshrsi3_extend, lshrsi3_extend2): Likewise.
Sun Oct 4 00:23:00 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* function.c (purge_addressof_1): If trying to take a sub-word
integral piece of a floating point mode, put it on the stack.
Sat Oct 3 19:01:03 1998 Richard Henderson <rth@cygnus.com>
* alpha/linux.h (CPP_PREDEFINES): Define __alpha__ for imake.
Sat Oct 3 14:42:19 1998 Jason Merrill <jason@yorick.cygnus.com>
* PROJECTS: Remove template friends.
* collect2.c (sort_ids): Remove unused variable.
* tm.texi (MATH_LIBRARY): Document.
(NEED_MATH_LIBRARY): Remove.
* varasm.c (assemble_start_function, assemble_variable, weak_finish,
assemble_alias): Do ASM_GLOBALIZE_LABEL for weak symbols, too.
Sat Oct 3 16:14:44 1998 John Carr <jfc@mit.edu>
* dwarf2out.c (expand_builtin_dwarf_reg_size): Initialize
last_end to 0x7fffffff.
Fri Oct 2 19:14:20 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* function.c (purge_addressof_1): Do not perform endianness
corrections on bitpos, who we call will do it for us.
Fri Oct 2 11:52:35 1998 Jeffrey A Law (law@cygnus.com)
* h8300.c (WORD_REG_USED): Fix typo.
(initial_offset): Use WORD_REG_USED.
* h8300.c (handle_pragma): Fix typo.
Fri Oct 2 10:51:35 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* caller-save.c (insert_save_restore): Break this function up
into new functions insert_restore, insert_save and insert_one_insn.
All callers changed.
(insert_restore): New function, mostly broken out of
insert_save_restore.
(insert_save): Likewise.
(insert_one_insn): Likewise.
(restore_referenced_regs): New argument BLOCK. All callers changed.
(save_call_clobbered_regs): Don't keep track of basic block boundaries
in this function, do it in insert_one_insn instead.
* reload1.c (reload): Break out some more pieces into separate
functions.
(dump_needs): New function, broken out of reload.
(set_initial_elim_offsets): Likewise.
(init_elim_table): Likewise.
(update_eliminables): Likewise.
* global.c (global_alloc): Delete code to manage the scratch_list.
* local-alloc.c (qty_scratch_rtx): Delete.
(scratch_block): Delete.
(scratch_list): Delete.
(scratch_list_length): Delete.
(scratch_index): Delete.
(alloc_qty_for_scratch): Delete.
(local-alloc): Update initialization of max_qty.
Delete code to manage the scratch list.
Delete code to allocate/initialize qty_scratch_rtx.
(block_alloc): Don't allocate quantities for scratches.
Delete code to manage the scratch list.
* regs.h (scratch_list): Delete declaration.
(scratch_block): Delete declaration.
(scratch_list_length): Delete declaration.
* reload1.c (reload): Delete code to manage the scratch list.
(spill_hard_reg): Likewise.
(mark_scratch_live): Delete.
* recog.c (alter_subreg): Delete declaration.
1998-10-02 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* Makefile.in (cccp.o): Fix typo in last patch.
Fri Oct 2 16:13:12 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* t-sh (LIB1ASMFUNCS): Add _set_fpscr .
* config/sh/lib1funcs.asm (___set_fpscr): Add.
Fri Oct 2 02:01:59 1998 Jeffrey A Law (law@cygnus.com)
* regclass.c (reg_scan_mark_refs): Return immediately if passed a
NULL_RTX as an argument.
* Makefile.in (unlibsubdir): Define.
(DRIVER_DEFINES): Use unlibsubdir.
(cccp.o, cpplib.o, protoize.o, unprotoize.o): Similarly.
(stmp-fixinc): Similarly.
Thu Oct 1 19:58:30 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* regmove.c (regmove_optimize): Add variable old_max_uid.
At the end of the function, update basic_block_end.
Thu Oct 1 17:58:25 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* dwarf2out.c (expand_builtin_dwarf_reg_size): Use
FIRST_PSEUDO_REGISTER as upper bound for last_end, not an
arbitrary constant.
Thu Oct 1 17:57:14 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.c: Improve interworking support.
Thu Oct 1 18:43:35 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (choose_reload_regs): Fix test if reload_reg_rtx[r] was
copied from reload_out[r] .
Thu Oct 1 19:20:09 1998 John Carr <jfc@mit.edu>
* dwarf2out.c (expand_builtin_dwarf_reg_size): Fix to work
with more than three size ranges.
* flow.c (sbitmap_copy): Use bcopy to copy bitmap.
* rtl.c (mode_name): Add a null string at the end of the array.
(mode_wider_mode): Change type to unsigned char.
(mode_mask_array): New variable.
(init_rtl): Update for mode_wider_mode type change.
* rtl.h (mode_wider_mode): Change type to unsigned char.
(mode_mask_array): Declare.
(GET_MODE_MASK): Use mode_mask_array.
Thu Oct 1 15:56:01 1998 Gavin Romig-Koch <gavin@cygnus.com>
* calls.c (expand_call) : Encapsulate code into
copy_blkmode_from_reg.
* expr.c (copy_blkmode_from_reg): New function.
* expr.h (copy_blkmode_from_reg): New function.
* integrate.c (function_cannot_inline_p): We can inline
these now.
(expand_inline_function): Use copy_blkmode_from_reg
if needed. Avoid creating BLKmode REGs.
(copy_rtx_and_substitute): Don't try to SUBREG a BLKmode
object.
Thu Oct 1 10:42:27 1998 Nick Clifton <nickc@cygnus.com>
* config/v850/v850.c: Add function prototypes.
Add support for v850 special data areas.
* config/v850/v850.h: Add support for v850 special data areas.
* c-pragma.c: Add support for HANDLE_PRAGMA_PACK and
HANDLE_PRAGMA_PACK_PUSH_POP.
(push_alignment): New function: Cache an alignment requested
by a #pragma pack(push,<n>).
(pop_alignment): New function: Pop an alignment from the
alignment stack.
(insert_pack_attributes): New function: Generate __packed__
and __aligned__ attributes for new decls whilst a #pragma pack
is in effect.
(add_weak): New function: Cache a #pragma weak directive.
(handle_pragma_token): Document calling conventions. Add
support for #pragma pack(push,<n>) and #pragma pack (pop).
* c-pragma.h: If HANDLE_SYSV_PRAGMA or HANDLE_PRAGMA_PACK_PUSH_POP
are defined enable HANDLE_PRAGMA_PACK.
Move 'struct weak_syms' here (from varasm.c).
Add pragma states for push and pop pragmas.
* c-common.c (decl_attributes): Call PRAGMA_INSERT_ATTRIBUTES
if it is defined.
* c-lex.c: Replace occurances of HANDLE_SYSV_PRAGMA with
HANDLE_GENERIC_PRAGMAS.
* varasm.c: Move definition of 'struct weak_syms' into
c-pragma.h.
(handle_pragma_weak): Deleted.
* config/i386/i386.h: Define HANDLE_PRAGMA_PACK_PUSH_POP.
* config/winnt/win-nt.h: Define HANDLE_PRAGMA_PACK_PUSH_POP.
* c-decl.c (start_function): Add invocation of
SET_DEFAULT_DECL_ATTRIBUTES, if defined.
* tm.texi: Remove description of non-existent macro
SET_DEFAULT_SECTION_NAME.
(HANDLE_SYSV_PRAGMA): Document.
(HANDLE_PRAGMA_PACK_PUSH_POP): Document.
Wed Sep 30 22:27:53 1998 Robert Lipe <robertl@dgii.com>
* config.sub: Recognize i[34567]86-pc-udk as new target.
* configure.in: Likewise.
* config/i386/t-udk: New file.
* config/i386/udk.h: New file.
Wed Sep 30 19:33:07 1998 Jeffrey A Law (law@cygnus.com)
* reorg.c (check_annul_list_true_false): Remove unused variables.
(steal_delay_list_from_target): Add missing "used_annul" variable.
(try_merge_delay_insns): Close out half formed comment.
Wed Sep 30 19:13:20 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cpplib.c (macroexpand): If arg->raw_before or
arg->raw_after, remove any no-reexpansion escape at the
beginning of the pasted token. Correct handling of whitespace
markers and no-reexpand markers at the end if arg->raw_after.
* toplev.c (documented_lang_options): Recognize -include,
-imacros, -iwithprefix, -iwithprefixbefore.
* cpplib.c (cpp_start_read): Process -imacros and -include
switches at the same time and in command-line order, after
initializing the dependency-output code. Emit properly nested
#line directives for them. Emit a #line for the main file
before processing these switches, and don't do it again
afterward.
Wed Sep 30 18:03:22 1998 Richard Henderson <rth@cygnus.com>
* function.c (purge_addressof_1): Use bitfield manipulation
routines to handle mem mode < reg mode.
Wed Sep 30 18:43:32 1998 Herman ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* reorg.c (try_merge_delay_insns): Account for resources referenced
in each instruction in INSN's delay list before trying to eliminate
useless instructions. Similarly when looking at a trial insn's delay
slots.
* reorg.c (check_annul_list_true_false): New function.
(steal_delay_list_from_{target,fallthrough}): Call it and also
refine tests for when we may annul if already filled a slot.
(fill_slots_from_thread): Likewise.
(delete_from_delay_slot): Return newly-created thread.
(try_merge_delay_isns): Use its new return value.
Wed Sep 30 18:29:26 1998 Jeffrey A Law (law@cygnus.com)
* loop.c (check_dbra_loop): Use a vanilla loop reversal if the biv is
used to compute a giv or as some other non-counting use.
Wed Sep 30 18:19:27 1998 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* regs.h (HARD_REGNO_CALL_PART_CLOBBERED): New macro.
* local-alloc.c (find_free_reg): Use it.
* global.c (find_reg): Likewise.
* tm.texi: Document HARD_REGNO_CALL_PART_CLOBBERED.
* regs.h (HARD_REGNO_CALLER_SAVE_MODE): New macro.
* caller-save.c (init_caller_save): Use it.
* tm.texi: Document HARD_REGNO_CALLER_SAVE_MODE.
Wed Sep 30 12:57:30 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* configure.in: Add --enable-cpplib option which uses cpplib
for cpp, but doesn't link cpplib into cc1. Make help text
capitalization consistent.
* configure: Rebuilt.
Wed Sep 30 10:09:39 1998 Mark Mitchell <mark@markmitchell.com>
* function.c (gen_mem_addressof): If the address REG is
REG_USERVAR_P make the new REG be so also.
* loop.c (scan_loop): Apply DeMorgan's laws and add documentation
in an attempt to clarify slightly.
Wed Sep 30 09:57:40 1998 Jeffrey A Law (law@cygnus.com)
* expr.c (expand_expr): Handle COMPONENT_REF, BIT_FIELD_REF ARRAY_REF
and INDIRECT_REF in code to check MAX_INTEGER_COMPUTATION_MODE.
Wed Sep 30 10:13:39 1998 Catherine Moore <clm@cygnus.com>
* toplev.c: Fix last patch.
Tue Sep 29 20:03:18 1998 Jim Wilson <wilson@cygnus.com>
* loop.c (get_condition): Fix typo in May 9 change.
Tue Sep 29 11:11:38 1998 Andrew MacLeod <amacleod@cygnus.com>
* invoke.texi (-fexceptions): Merge 2 different descriptions.
Mon Sep 28 22:08:52 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* toplev.c (documented_lang_options): Spelling corrections.
Mon Sep 28 19:41:24 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* configure.in: New flags --with-ld and --with-as, equivalent
to setting LD and AS environment variables. Test whether
specified arguments are GNU commands, and report them with
checking messages. Use the specified AS for configure
tests too.
* configure: Likewise.
* acconfig.h: Add DEFAULT_ASSEMBLER and DEFAULT_LINKER.
* config.in: Likewise.
* gcc.c (find_a_file): When looking for `as' and `ld', return
the DEFAULT program if it exists.
* collect2.c (main): Use DEFAULT_LINKER if it exists.
* gcc.c (find_a_file): The test for existence of a full
pathname was reversed.
Mon Sep 28 17:34:35 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.h (ASM_OUTPUT_MI_THUNK): Only define on ELF systems.
* rs6000.c (output_mi_thunk): Always use a raw jump for now.
Mon Sep 28 14:24:03 1998 Mark Mitchell <mark@markmitchell.com>
* tree.h (TYPE_BINFO): Document.
Mon Sep 28 12:55:49 1998 Stan Cox <scox@cygnus.com>
* i386-coff.h (dbxcoff.h): Added.
Mon Sep 28 12:51:00 1998 Catherine Moore <clm@cygnus.com>
* toplev.c: Fix bad patch around flag_data_sections.
Mon Sep 28 10:32:28 1998 Nick Clifton <nickc@cygnus.com>
* reload1.c (reload): Use reload_address_index_reg_class and
reload_address_base_reg_class when setting
caller_save_spill_class. (Patch generated by Jim Wilson:
wilson@cygnus.com).
Mon Sep 28 07:43:34 1998 Mark Mitchell <mark@markmitchell.com>
* c-common.c (c_get_alias_set): Tighten slightly for FUNCTION_TYPEs
and ARRAY_TYPEs. Tidy up. Improve support for type-punning.
* expr.c (store_field): Add alias_set parameter. Set the
MEM_ALIAS_SET accordingly, if the target is a MEM.
(expand_assignment): Use it.
(store_constructor_field): Pass 0.
(expand_expr): Likewise.
Mon Sep 28 07:54:03 1998 Catherine Moore <clm@cygnus.com>
* flags.h: Add flag_data_sections.
* toplev.c: Add option -fdata-sections. Add flag_data_sections.
(compile_file): Error if flag_data_sections not supported.
* varasm.c (assemble_variable): Handle flag_data_sections.
* config/svr4.h: Modify prefixes for UNIQUE_SECTION_NAME.
* config/mips/elf.h: Likewise.
* config/mips/elf64.h: Likewise.
* invoke.texi: Describe -fdata-sections.
Mon Sep 28 04:15:44 1998 Craig Burley <burley@melange.gnu.org>
* invoke.texi (-ffloat-store): Clarify that this option
does not affect intermediate results -- only variables.
Mon Sep 28 04:11:35 1998 Jeffrey A Law (law@cygnus.com)
* cpp.texi: Update for Fortran usage from Craig.
Fri Sep 25 22:09:47 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (function_arg_boundary): Revert accidental change on
September 18.
Fri Sep 25 20:30:00 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.h (ASM_OUTPUT_MI_THUNK): Declare, call output_mi_thunk.
(output_mi_thunk): Declare.
* rs6000.c (output_mi_thunk): Function to create thunks for MI.
(output_function_profiler): Use r12 for temp, instead of r11 so
that we preserve the static chain register.
Fri Sep 25 14:18:33 1998 Jim Wilson <wilson@cygnus.com>
* sdbout.c (sdbout_one_type): Don't look at TYPE_BINFO field of enums.
Fri Sep 25 19:30:19 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.c (gen_shl_sext): Fix case 5.
Fri Sep 25 17:35:23 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_combine): Re-add line that got accidentally lost.
Fri Sep 25 10:43:47 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cccp.c (pedwarn_with_file_and_line): For !__STDC__ case, avoid
accessing variables until they are initialized via va_arg().
Thu Sep 24 22:12:16 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* reload1.c (reload_combine): Initialize set before using.
Thu Sep 24 18:53:20 1998 Jason Merrill <jason@yorick.cygnus.com>
* sdbout.c (sdbout_field_types): Don't emit the types of fields we
won't be emitting.
Thu Sep 24 17:05:30 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/arm.md (insv): Add comment. In CONST_INT case, and
operand3 with mask before using it. Patch provided by Jim Wilson.
Thu Sep 24 15:08:08 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* config/sparc/sparc.c (function_value): Perform the equivalent of
PROMOTE_MODE for ARCH64.
(eligible_for_epilogue_delay): Allow DImode operations in delay
slot of a return for ARCH64.
Thu Sep 24 22:17:54 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.md (sqrtsf2): Fix mode of sqrt.
Thu Sep 24 21:48:51 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (choose_reload_regs): Also try inheritance when
reload_in is a stack slot of a pseudo, even if we already got a
reload reg.
Thu Sep 24 21:22:39 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_cse_regs_1): Renamed from reload_cse_regs.
(reload_cse_regs): New function body: call reload_cse_regs_1,
reload_combine, reload_cse_move2add.
When doing expensive_optimizations, call reload_cse_regs_1 a
second time after reload_cse_move2add.
(reload_combine, reload_combine_note_store): New functions.
(reload_combine_note_use): New function.
(reload_cse_move2add, move2add_note_store): New functions.
Thu Sep 24 18:48:43 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload.c (find_reloads): In code to promote RELOAD_FOR_X_ADDR_ADDR
reloads to RELOAD_FOR_X_ADDRESS reloads, test for reload sharing.
Properly keep track of first RELOAD_FOR_X_ADDRESS also for
more than 3 such reloads.
If there is not more than one RELOAD_FOR_X_ADDRESS, don't change
RELOAD_FOR_X_ADDR_ADDR reload.
Thu Sep 24 17:45:55 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* expr.c (store_constructor): When initializing a field that is smaller
than a word, at the start of a word, try to widen it to a full word.
* cse.c (cse_insn): When we are about to change a register,
remove any invalid references to it.
(remove_invalid_subreg_refs): New function.
(mention_regs): Special treatment for SUBREGs.
(insert_regs): Don't strip SUBREG for call to mention_regs.
Check if reg_tick needs to be bumped up before that call.
(lookup_as_function): Try to match known word_mode constants when
looking for a norrower constant.
(canon_hash): Special treatment for SUBREGs.
Thu Sep 24 01:35:34 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sol2-sld-64.h (TRANSFER_FROM_TRAMPOLINE): Define.
* config/sparc/sparc.c (sparc64_initialize_trampoline): If that is
defined, emit libcall to __enable_execute_stack. Also fix opcodes
and offsets in actual stack trampoline code so they match the
commentary and actually work.
Thu Sep 24 01:19:02 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* configure.in (sparcv9-*-solaris): Use t-sol2 and t-sol2-64 for
tmake_file.
(sparc64-*-linux): Use t-linux and sparc/t-linux64 for
tmake_file. Set extra_parts to needed crt objects.
* configure: Rebuilt.
* config/sparc/linux64.h (SPARC_BI_ARCH): Define.
(TARGET_DEFAULT): Set if default is v9 or ultra.
(STARTFILE_SPEC32, STARTFILE_SPEC64): New macros.
(STARTFILE_SPEC): Set to those upon SPARC_BI_ARCH.
(ENDFILE_SPEC32, ENDFILE_SPEC64, ENDFILE_SPEC): Likewise.
(SUBTARGET_EXTRA_SPECS, LINK_ARCH32_SPEC, LINK_ARCH64_SPEC,
LINK_SPEC, LINK_ARCH_SPEC): Likewise.
(TARGET_VERSION): Define.
(MULTILIB_DEFAULT): Define.
* config/sparc/sparc.h (CPP_CPU_DEFAULT_SPEC): Rearrange so that
mixed 32/64 bit compilers based upon SPARC_BI_ARCH work.
(CPP_CPU64_DEFAULT_SPEC, CPP_CPU32_DEFAULT_SEC): Define
appropriately.
(TARGET_SWITCHES): Allow ptr32/ptr64 options once more.
* config/sparc/sparc.c (sparc_override_options): If arch and
pointer size disagree, emit diagnostic and fix it up. If
SPARC_BI_ARCH and TARGET_ARCH32, set cmodel to CM_32. Turn off
V8PLUS in 64-bit mode.
* config/sparc/t-linux64: New file.
* config/sparc/t-sol2-64: New file.
* config/sparc/t-sol2: Adjust build rules to use MULTILIB_CFLAGS.
* config/sparc/sol2-sld-64.h (SPARC_BI_ARCH): Define.
(ASM_CPU32_DEFAULT_SPEC, ASM_CPU64_DEFAULT_SPEC,
CPP_CPU32_DEFAULT_SPEC, CPP_CPU64_DEFAULT_SPEC): Define.
(ASM_SPEC, CPP_CPU_SPEC): Set appropriately based upon those.
(STARTFILE_SPEC32, STARTFILE_SPEC32, STARTFILE_ARCH_SPEC):
Define.
(STARTFILE_SPEC): Set appropriately based upon those.
(CPP_CPU_DEFAULT_SPEC, ASM_CPU_DEFAULT_SPEC): Set based upon
disposition of DEFAULT_ARCH32_P.
(LINK_ARCH32_SPEC, LINK_ARCH64_SPEC): Define.
(LINK_ARCH_SPEC, LINK_ARCH_DEFAULT_SPEC): Set based upon those.
(CC1_SPEC, MULTILIB_DEFAULTS): Set based upon DEFAULT_ARCH32_P.
(MD_STARTFILE_PREFIX): Set correctly based upon SPARC_BI_ARCH.
* config/sparc/xm-sysv4-64.h (HOST_BITS_PER_LONG): Only set on
arch64/v9.
* config/sparc/xm-sp64.h (HOST_BITS_PER_LONG): Likewise.
Wed Sep 23 22:32:31 1998 Mark Mitchell <mark@markmitchell.com>
* rtl.h (init_virtual_regs): New function.
* emit-rtl.c (init_virtual_regs): Define.
(insn_emit): Use it.
* integrate.c (save_for_inline_copying): Likewise.
Wed Sep 23 16:22:01 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/thumb.h: The following patches were made by Jim Wilson:
(enum reg_class): Add NONARG_LO_REGS support.
(REG_CLASS_NAMES, REG_CLASS_CONTENTS, REGNO_REG_CLASS,
PREFERRED_RELOAD_CLASS, SECONDARY_RELOAD_CLASS): Likewise.
(GO_IF_LEGITIMATE_ADDRESS): Disable REG+REG addresses before reload
completes. Re-enable HImode REG+OFFSET addresses.
(LEGITIMIZE_RELOAD_ADDRESS): Define.
* expmed.c (extract_bit_field): Add comment from Jim Wilson.
Wed Sep 23 13:26:02 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (get_aligned_mem): Revert Sep 20 change.
(alpha_set_memflags, alpha_set_memflags_1): Likewise.
(alpha_align_insns): Properly calculate initial offset wrt max_align.
Wed Sep 23 10:45:44 1998 Richard Earnshaw (rearnsha@arm.com)
* arm.c (find_barrier): Revert change of Apr 23. Handle table
jumps as a single entity, taking into account the size of the
table.
Tue Sep 22 15:13:34 1998 Alexandre Petit-Bianco <apbianco@cygnus.com>
* tree.def (SWITCH_EXPR): New tree node definition.
Mon Sep 21 23:40:38 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Sep 21 22:31:14 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Sep 21 22:48:09 1998 Jeffrey A Law (law@cygnus.com)
* configure.in: Recognize i[34567]86-*-openbsd* and handle it like
NetBSD.
Mon Sep 21 22:05:28 1998 Jeffrey A Law (law@cygnus.com)
* Revert this patch.
* reload.c (find_reloads): Do not replace a pseudo with
(MEM (reg_equiv_addr)) in the initializing insn for the
pseudo.
Mon Sep 21 20:19:41 1998 John Carr <jfc@mit.edu>
* final.c (final_scan_insn): Disable tracking CC across branches.
Mon Sep 21 17:15:26 1998 Andrew MacLeod <amacleod@cygnus.com>
* expr.h (eh_rtime_match_libfunc): New extern declaration.
* optabs.c (init_optabs): Set eh_rtime_match_libfunc.
* except.c (start_catch_handler): Use eh_rtime_match_libfunc.
* libgcc2.c (__eh_rtime_match): Always return 0 if the matcher is
NULL. Only include <stdio.h> if inhibit_libc is not defined.
Mon Sep 21 14:10:51 1998 Jason Merrill <jason@yorick.cygnus.com>
* toplev.c (rest_of_compilation): Skip compiling anything with
DECL_EXTERNAL set, not just if it has DECL_INLINE as well.
Mon Sep 21 13:51:05 1998 Jim Wilson <wilson@cygnus.com>
* flow.c (find_basic_blocks): Delete check for in_libcall_block when
prev_code is a CALL_INSN. Change check for REG_RETVAL note to
use in_libcall_block.
(find_basic_blocks_1): Delete check for in_libcall_block when prev_code
is a CALL_INSN. If CALL_INSN and in_libcall_block, then change code
to INSN.
Mon Sep 21 14:02:23 1998 Robert Lipe <robertl@dgii.com>
* i386.h (TARGET_SWITCHES): Improve doc for align-double. Fix
typo in no-fancy-math-387 description.
Mon Sep 21 09:27:18 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Sep 21 09:24:49 1998 Stan Cox <scox@cygnus.com>
* i386-coff.h (DBX_DEBUGGING_INFO): Added.
Mon Sep 21 09:14:49 1998 Robert Lipe <robertl@dgii.com>
* i386.h (TARGET_SWITCHES): Add description fields for flags
documented in install.texi.
(TARGET_OPTIONS): Likewise.
Mon Sep 21 01:39:03 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Sep 21 01:53:05 1998 Felix Lee <flee@cygnus.com>
* c-lex.c (init_lex): Use getenv ("LANG"), not GET_ENVIRONMENT ().
* cccp.c (main): Likewise.
* cccp.c, collect2.c, cpplib.c, gcc.c, config/i386/xm-cygwin32.h:
Rename GET_ENVIRONMENT to GET_ENV_PATH_LIST, and fix some
macro-use bugs.
Mon Sep 21 00:52:12 1998 Per Bothner <bothner@cygnus.com>
* Makefile.in (LIBS): Link in libiberty.a.
* c-common.c, gcc.c, toplev.c: Replace (some) bcopy calls by memcpy.
Sun Sep 20 23:28:11 1998 Richard Henderson <rth@cygnus.com>
* reload1.c (emit_reload_insns): Accept a new arg for the bb. Use
it to update bb boundaries. Update caller.
* function.c (reposition_prologue_and_epilogue_notes): Update
bb boundaries wrt the moved note.
Sun Sep 20 20:57:02 1998 Robert Lipe <robertl@dgii.com>
* configure.in (i*86-*-sysv5*): Use fixinc.svr4 to patch byteorder
problems.
* configure: Regenerate.
Sun Sep 20 19:01:51 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_sr_alias_set): New variable.
(override_options): Set it.
(alpha_expand_prologue, alpha_expand_epilogue): Use it.
(mode_mask_operand): Fix signed-unsigned comparison warning.
(alpha_expand_block_move): Likewise.
(print_operand): Likewise.
(get_aligned_mem): Use change_address.
(alpha_set_memflags, alpha_set_memflags_1): Set the alias set.
(alphaev4_insn_pipe, alphaev4_next_group): New functions.
(alphaev4_next_nop, alphaev5_next_nop): New functions.
(alpha_align_insns): Remade from old alphaev5_align_insns
to handle multiple processors.
(alpha_reorg): Call alpha_align_insns for both ev4 and ev5.
* output.h (label_to_alignment): Prototype.
* tree.c (new_alias_set): New function.
* tree.h (new_alias_set): Declare it.
* c-common.c (c_get_alias_set): Use it.
Sun Sep 20 12:35:55 1998 Richard Henderson <rth@cygnus.com>
* fold-const.c (fold): Yet another COND_EXPR bug: when folding
to an ABS expr, convert an unsigned input to signed.
Sun Sep 20 12:14:45 1998 Jeffrey A Law (law@cygnus.com)
* fold-const.c (fold): Fix another type in COND_EXPR handling code.
1998-09-20 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* configure.in: Add support for c4x targets.
* configure: Rebuilt.
Sun Sep 20 00:00:51 1998 Richard Henderson <rth@cygnus.com>
* combine.c (distribute_notes): If an insn is a cc0 user, only
delete it if we can also delete the cc0 setter.
Sun Sep 20 00:22:23 1998 Michael Tiemann <michael@impact.tiemann.org>
* fold-const.c (fold): Fix typo in COND_EXPR handling code.
(invert_truthvalue): Enable truthvalue inversion for
floating-point operands if -ffast-math.
Sat Sep 19 23:58:07 1998 Melissa O'Neill <oneill@cs.sfu.ca>
* configure.in: Disable collect2 for nextstep. Instead use
crtbegin/crtend.
* configure: Rebuilt.
* config/nextstep.h (STARTFILE_SPEC): Add crtbegin.
(ENDFILE_SPEC): Define.
(OBJECT_FORMAT_MACHO): Define.
(EH_FRAME_SECTION_ASM_OP): Define.
* crtstuff.c: Handle MACHO.
Sun Sep 20 00:24:24 1998 Robert Lipe <robertl@dgii.com>
* config/i386/sco5.h (TARGET_MEM_FUNCTIONS): Define.
1998-09-19 Torbjorn Granlund <tege@matematik.su.se>
* fp-bit.c (pack_d): Do not clear SIGN when fraction is 0.
(_fpadd_parts): Get sign right for 0.
1998-09-19 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
* ginclude/varargs.h: Add support for C4x target.
* ginclude/stdargs.h: Likewise.
Sat Sep 19 12:05:09 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_return_addr): SET should be VOIDmode.
(alpha_emit_set_long_const): Rewrite to be callable from reload
and 32-bit hosts.
(alpha_expand_epilogue): Update for alpha_emit_set_long_const.
* alpha.md (movdi): Likewise.
Sat Sep 19 07:33:36 1998 Richard Earnshaw (rearnsha@arm.com)
* arm.c (add_constant): New parameter address_only, change caller.
Set it non-zero if taking the address of an item in the pool.
(arm_reorg): Handle cases where we need the address of an item in
the pool.
* arm.c (bad_signed_byte_operand): Check both arms of a sum in
a memory address.
* arm.md (splits for *extendqihi_insn and *extendqisi_insn): Handle
memory addresses that are not in standard canonical form.
Sat Sep 19 01:00:32 1998 Michael Hayes (mph@elec.canterbury.ac.nz)
* README.C4X: New file with information about the c4x ports.
* ginclude/va-c4x.h: New file for c4x varargs support.
* config/c4x: New directory with c4x port files.
Fri Sep 18 22:52:05 1998 Jeffrey A Law (law@cygnus.com)
* reload.c (find_reloads): Do not replace a pseudo with
(MEM (reg_equiv_addr)) in the initializing insn for the
pseudo.
Fri Sep 18 23:50:56 1998 David Edelsohn <edelsohn@gnu.org>
* toplev.c (rest_of_compilation): Set bct_p on second call to
loop_optimize.
* loop.c (loop_optimize, scan_loop, strength_reduce): New argument
bct_p.
(strength_reduce): Only call analyze_loop_iterations and
insert_bct if bct_p set.
(check_dbra_loop): Fix typo.
(insert_bct): Use word_mode instead of SImode.
(instrument_loop_bct): Likewise. Do not delete iteration count
condition code generation insn. Initialize iteration count before
loop start.
* rtl.h (loop_optimize): Update prototype.
* ginclude/va-ppc.h (va_arg): longlong types in overflow area are
not doubleword aligned.
* rs6000.c (optimization_options): New function.
(secondary_reload_class): Only call true_regnum for PSEUDO_REGs.
* rs6000.h (OPTIMIZATION_OPTIONS): Define.
(REG_ALLOC_ORDER): Allocate highest numbered condition regsiters
first; cr1 can be used for FP record condition insns.
Fri Sep 18 09:44:55 1998 Nick Clifton <nickc@cygnus.com>
* config/m32r/m32r.h (m32r_block_immediate_operand): Add to
PREDICATE_CODES.
* config/m32r/m32r.md: Add "movstrsi" and "movstrsi_internal"
patterns.
* config/m32r/m32r.c (m32r_print_operand): Add 's' and 'p'
operators.
(block_move_call): New function: Call a library routine to copy a
block of memory.
(m32r_expand_block_move): New function: Expand a "movstrsi"
pattern into a sequence of insns.
(m32r_output_block_move): New function: Expand a
"movstrsi_internal" pattern into a sequence of assembler opcodes.
(m32r_block_immediate_operand): New function: Return true if the
RTL is an integer constant, less than or equal to MAX_MOVE_BYTES.
Thu Sep 17 16:42:16 1998 Andrew MacLeod <amacleod@cygnus.com>
* except.c (start_catch_handler): Issue 'fatal' instead of 'error' and
re-align some code.
* libgcc2.c (__eh_rtime_match): fprintf a runtime error. Use <stdio.h>.
Thu Sep 17 12:24:33 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* regmove.c (copy_src_to_dest): Check that modes match.
Wed Sep 16 22:10:42 1998 Robert Lipe <robertl@dgii.com>
* config/i386/sco5.h (SUPPORTS_WEAK): True only if targeting ELF.
Wed Sep 16 15:24:54 1998 Richard Henderson <rth@cygnus.com>
* i386.h (PREFERRED_RELOAD_CLASS): Respect an existing class
narrower than FLOAT_REGS.
Wed Sep 16 17:51:00 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* cpplib.c: Removed OLD_GPLUSPLUS_INCLUDE_DIR.
* cccp.c: Likewise.
* Makefile.in (old_gxx_include_dir): Removed.
Wed Sep 16 12:29:22 1998 Nick Clifton <nickc@cygnus.com>
* config/sh/sh.h: Update definition of HANDLE_PRAGMA to match
new specification.
* config/sh/sh.c (handle_pragma): Rename to sh_handle_pragma().
(sh_handle_pragma): Change function arguments to match new
specification for HANDLE_PRAGMA.
Wed Sep 16 12:43:19 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* gen-protos.c (parse_fn_proto): Cast argument of ISALNUM to
`unsigned char'.
(main): Mark parameter `argc' with ATTRIBUTE_UNUSED.
When generating output, initialize missing struct member to zero.
Wed Sep 16 14:47:43 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* regmove.c (copy_src_to_dest): Don't copy if that requires
(a) new register(s).
Wed Sep 16 01:29:12 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* global.c (reg_allocno): Now static.
* reload1.c (reg_allocno): Delete declaration.
(order_regs_for_reload): Take no arguments. Don't treat regs
allocated by global differently than those allocated by local-alloc.
Wed Sep 16 01:09:01 1998 Kamil Iskra <iskra@student.uci.agh.edu.pl>
* m68k/m68k.c (output_function_prologue): Reverse NO_ADDSUB_Q
condition, fix format strings.
(output_function_epilogue): Likewise.
* m68k/m68k.c: Don't include <stdlib.h> directly.
Wed Sep 16 00:30:56 1998 Geoff Keating <geoffk@ozemail.com.au>
* gcse.c: New definition NEVER_SET for reg_first_set, reg_last_set,
mem_first_set, mem_last_set; because 0 can be a CUID.
(oprs_unchanged_p): Use new definition.
(record_last_reg_set_info): Likewise.
(record_last_mem_set_info): Likewise.
(compute_hash_table): Likewise.
Tue Sep 15 22:59:52 1998 Jeffrey A Law (law@cygnus.com)
* rs6000.c (output_epilogue): Handle Chill.
* mn10200.h (ASM_OUTPUT_DWARF2_ADDR_CONST): Define.
* mn10300.h (ASM_OUTPUT_DWARF2_ADDR_CONST): Define.
* combine.c (make_extraction): If no mode is specified for
an operand of insv, extv, or extzv, default it to word_mode.
(simplify_comparison): Similarly.
* expmed.c (store_bit_field): Similarly.
(extract_bit_field): Similarly.
* function.c (fixup_var_regs_1): Similarly.
* recog.c (validate_replace_rtx_1): Similarly.
* mips.md (extv, extzv, insv expanders): Default modes for most
operands. Handle TARGET_64BIT.
(movdi_uld, movdi_usd): New patterns.
* pa.c (emit_move_sequence): Do not replace a pseudo with its
equivalent memory location unless we have been provided a scratch
register. Similarly do not call find_replacement unless a
scratch register has been provided.
Tue Sep 15 19:23:01 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* i386.h (PREFERRED_RELOAD_CLASS): For standard 387 constants,
return FLOAT_REGS.
Tue Sep 15 19:09:06 1998 Richard Henderson <rth@cygnus.com>
* tree.h (BUILT_IN_CALLER_RETURN_ADDRESS): Unused. Kill.
(BUILT_IN_FP, BUILT_IN_SP, BUILT_IN_SET_RETURN_ADDR_REG): Kill.
(BUILT_IN_EH_STUB_OLD, BUILT_IN_EH_STUB, BUILT_IN_SET_EH_REGS): Kill.
(BUILT_IN_EH_RETURN, BUILT_IN_DWARF_CFA): New.
* c-decl.c (init_decl_processing): Update accordingly.
* expr.c (expand_builtin): Likewise.
* rtl.h (global_rtl): Add cfa entry.
(virtual_cfa_rtx, VIRTUAL_CFA_REGNUM): New.
(LAST_VIRTUAL_REGISTER): Update.
* emit-rtl.c (global_rtl): Add cfa entry.
(init_emit): Initialize it.
* function.c (cfa_offset): New.
(instantiate_virtual_regs): Initialize it.
(instantiate_virtual_regs_1): Instantiate virtual_cfa_rtx.
(expand_function_end): Call expand_eh_return.
* tm.texi (ARG_POINTER_CFA_OFFSET): New.
* except.c (current_function_eh_stub_label): Kill.
(current_function_eh_old_stub_label): Likwise; update all references.
(expand_builtin_set_return_addr_reg): Kill.
(expand_builtin_eh_stub_old, expand_builtin_eh_stub): Kill.
(expand_builtin_set_eh_regs): Kill.
(eh_regs): Produce a third reg for the actual handler address.
(eh_return_context, eh_return_stack_adjust): New.
(eh_return_handler, eh_return_stub_label): New.
(init_eh_for_function): Initialize them.
(expand_builtin_eh_return, expand_eh_return): New.
* except.h: Update prototypes.
* flow.c (find_basic_blocks_1): Update references to the stub label.
* function.h (struct function): Kill stub label elements.
* libgcc2.c (in_reg_window): For REG_SAVED_REG, check that the
register number is one that would be in the previous window.
Provide a dummy definition for non-windowed targets.
(get_reg_addr): New function.
(get_reg, put_reg, copy_reg): Use it.
(__throw): Rely on in_reg_window, not INCOMING_REGNO. Kill stub
generating code and use __builtin_eh_return. Use __builtin_dwarf_cfa.
* alpha.c (alpha_eh_epilogue_sp_ofs): New.
(alpha_init_expanders): Initialize it.
(alpha_expand_epilogue): Use it.
* alpha.h: Declare it.
* alpha.md (eh_epilogue): New.
* m68h.h (ARG_POINTER_CFA_OFFSET): New.
* sparc.h (ARG_POINTER_CFA_OFFSET): New.
Tue Sep 15 19:31:58 1998 Michael Meissner <meissner@cygnus.com>
* i960.h (CONST_COSTS): Fix thinko. Test flag, not the constant
flag bit mask.
Tue Sep 15 14:10:54 1998 Andrew MacLeod <amacleod@cygnus.com>
* except.h (struct eh_entry): Add false_label field.
(end_catch_handler): Add prototype.
* except.c (push_eh_entry): Set false_label field to NULL_RTX.
(start_catch_handler): When using old style exceptions, issue
runtime typematch code before continuing with the handler.
(end_catch_handler): New function, generates label after handler
if needed by older style exceptions.
(expand_start_all_catch): No need to check for new style exceptions.
(output_exception_table_entry): Only output the first handler label
for old style exceptions.
* libgcc2.c (__eh_rtime_match): New routine to lump runtime matching
mechanism into one function, if a runtime matcher is provided.
Tue Sep 15 13:53:59 1998 Andrew MacLeod <amacleod@cygnus.com>
* config/i960/i960.h (SLOW_BYTE_ACCESS): Change definition to 1.
Tue Sep 15 09:59:01 1998 Mark Mitchell <mark@markmitchell.com>
* integrate.c (copy_decl_list): Fix typo.
Tue Sep 15 04:18:52 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (movdf_const_intreg_sp32): Fix length
attribute.
Mon Sep 14 14:02:53 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Sep 14 10:33:56 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Mon Sep 14 09:51:05 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Sep 13 22:10:18 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* invoke.texi (C Dialect Options): Put back missing @end itemize.
Mon Sep 14 02:33:46 1998 Alexandre Oliva <oliva@dcc.unicamp.br>
* configure.in: Remove usage of `!' to negate the result of a
command; some common shells do not support it.
Sun Sep 13 19:17:35 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* configure.in: In sparc9-sol2 config, use 'if test' not
brackets.
* configure: Rebuilt.
* config/sparc/sol2-sld-64.h (SPARC_DEFAULT_CMODEL): Change to
CM_MEDANY.
(CPP_CPU_SPEC): Do not define _LP64, header files do this.
(CPP_CPU_DEFAULT_SPEC): Likewise.
* config/sparc/sol2.h (INIT_SUBTARGET_OPTABS): Get the names right
for arch64 libfuncs.
* config/sparc/sparc.md (goto_handler_and_restore): Allow any mode
for operand zero.
Sun Sep 13 09:11:59 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* acconfig.h (NEED_DECLARATION_STRSIGNAL): Provide a stub.
* collect2.c: Don't declare `sys_siglist' here.
(my_strsignal): Prototype and define new function. Use it in
place of `sys_siglist' hacks.
* mips_tfile.c: Likewise.
* configure.in (AC_CHECK_FUNCS): Check for strsignal.
(GCC_NEED_DECLARATIONS): Likewise.
* system.h (strsignal): Prototype it, if necessary.
(sys_siglist): Declare it, if necessary.
Sun Sep 13 04:37:28 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* loop.c (move_movables): While removing insn sequences, preserve
the next pointer of the most recently deleted insn when we skip
over a NOTE.
Sun Sep 13 08:13:39 1998 Ben Elliston <bje@cygnus.com>
* objc/config-lang.in: Do not output the name of the selected
thread file when building the front-end. The Makefile for the
runtime library will do this.
* objc/Make-lang.in: Do not build the runtime library or install
the Objective C header files. The Makefile for the runtime
library will do this.
* objc/Makefile.in (all.indirect): Only build the front-end.
(compiler): Rename to `frontend'.
(obj-runtime): Remove target.
(copy-headers): Likewise.
(clean): No need to remove `libobjc.a' any longer.
Sat Sep 12 11:37:19 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.h ({ASM,CPP}_CPU_SPEC): Add support for all machines
supported with -mcpu=xxx.
Fri Sep 11 23:55:54 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* flow.c (mark_set_1): Recognize multi-register structure return
values in CALL insns.
(mark_used_regs): Likewise.
(count_reg_sets_1): Likewise.
(count_reg_references): Likewise.
* rtlanal.c (note_stores): Likewise.
(reg_overlap_mentioned_p): Likewise.
* haifa-sched.c (check_live_1): Likewise.
(update_live_1): Likewise.
(sched_analyze_1): Likewise.
(sched_note_set): Likewise.
(birthing_insn_p): Likewise.
(attach_deaths): Likewise.
* config/sparc/sparc.md (movdf_const_intreg_sp64): Disable.
Fri Sep 11 22:57:55 1998 Eric Dumazet <dumazet@cosmosbay.com>
* config/i386/sco5.h (ASM_WEAKEN_LABEL): Defined as in svr4.h.
Thu Sep 10 22:02:04 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* glimits.h (__LONG_MAX__): Recognize __sparcv9 too.
Thu Sep 10 21:19:10 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* configure.in: Add check for GAS subsection -1 support.
* acconfig.h (HAVE_GAS_SUBSECTION_ORDERING): Add.
* configure config.in: Rebuilt.
* config/sparc/sparc.h (CASE_VECTOR_MODE): For V9 flag_pic, use
SImode is subsection -1 works, else use DImode.
(ASM_OUTPUT_ADDR_VEC_START, ASM_OUTPUT_ADDR_VEC_END): Define if
subsection -1 works.
* config/sparc/sparc.c (sparc_output_addr_vec,
sparc_output_addr_diff_vec): Use them if defined.
Thu Sep 10 10:46:01 1998 Mark Mitchell <mark@markmitchell.com>
* tree.h (DECL_ORIGIN): New macro.
* integrate.c (copy_and_set_decl_abstract_origin): New function.
(copy_decl_list): Use it.
(integrate_parm_decls): Likewise.
(integrate_decl_tree): Likewise.
* dwarf2out.c (decl_ultimate_origin): Simplify.
* dwarfout.c (decl_ultimate_origin): Likewise.
* c-decl.c (duplicate_decls): Use DECL_ORIGIN.
(pushdecl): Likewise.
Thu Sep 10 08:01:31 1998 Anthony Green <green@cygnus.com>
* config/rs6000/rs6000.c (output_epilog): Add Java support.
Thu Sep 10 14:48:59 1998 Martin von Löwis <loewis@informatik.hu-berlin.de>
* invoke.texi (C++ Dialect Options): Document -fhonor-std.
Thu Sep 10 01:38:05 1998 Jeffrey A Law (law@cygnus.com)
* reg-stack.c (straighten_stack): Do nothing if the virtual stack is
empty or has a single entry.
* toplev.c (rest_of_compilation): Open up the dump file for reg-stack
before calling reg_to_stack.
Thu Sep 10 00:03:34 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (alphaev5_insn_pipe): Abort on default case.
(alphaev5_next_group): Swallow CLOBBERs and USEs.
* c-tree.h (warn_long_long): Declare it.
Wed Sep 9 23:31:36 1998 (Stephen L Moshier) <moshier@world.std.com>
* emit-rtl.c (gen_lowpart_common): Disable optimization of
initialized float-int union if the value is a NaN.
Wed Sep 9 23:00:48 1998 Nathan Sidwell <nathan@acm.org>
* c-lex.c (real_yylex): Don't warn about long long constants if
we're allowing long long
Wed Sep 9 21:58:41 1998 Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>
* except.h (current_function_eh_stub_label): Declare.
(current_function_eh_old_stub_label): Declare.
* function.h (struct function): New members eh_stub_label and
eh_old_stub_label.
* except.c (current_function_eh_stub_label): New variable.
(current_function_eh_old_stub_label): New variable.
(init_eh_for_function): Clear them.
(save_eh_status): Save them.
(restore_eh_status): Restore them.
(expand_builtin_eh_stub): Set current_function_eh_stub_label.
(expand_builtin_eh_stub_old): Set current_function_eh_old_stub_label.
* flow.c (find_basic_blocks_1): When handling a REG_LABEL note, don't
make an edge from the block that contains it to the block starting
with the label if this label is one of the eh stub labels.
If eh stub labels exist, show they are reachable from the last block
in the function.
* reload1.c (reload): Break out several subroutines and make some
variables global.
(calculate_needs_all_insns): New function, broken out of reload.
(calculate_needs): Likewise.
(find_reload_regs): Likewise.
(find_group): Likewise.
(find_tworeg_group): Likewise.
(something_needs_reloads): New global variable, formerly in reload.
(something_needs_elimination): Likewise.
(caller_save_spill_class): Likewise.
(caller_save_group_size): Likewise.
(max_needs): Likewise.
(group_size): Likewise.
(max_groups): Likewise.
(max_nongroups): Likewise.
(group_mode): Likewise.
(max_needs_insn): Likewise.
(max_groups_insn): Likewise.
(max_nongroups_insn): Likewise.
(failure): Likewise.
* print-rtl.c (print_rtx): For MEMs, print MEM_ALIAS_SET.
Wed Sep 9 13:14:41 1998 Richard Henderson <rth@cygnus.com>
* loop.c (load_mems): Copy rtx for output mem.
Wed Sep 9 15:16:58 1998 Gavin Romig-Koch <gavin@cygnus.com>
* mips/abi64.h (LONG_MAX_SPEC): Don't set LONG_MAX for
mips1 or mips2 either.
Wed Sep 9 12:31:35 1998 Jeffrey A Law (law@cygnus.com)
* pa.c (pa_reorg): New marking scheme for jumps inside switch
tables.
(pa_adjust_insn_length): Update to work with new marking scheme
for jumps inside switch tables.
* pa.md (switch_jump): Remove pattern.
(jump): Handle jumps inside jump tables.
* Makefile.in (profile.o): Depend on insn-config.h
Wed Sep 9 09:36:51 1998 Jim Wilson <wilson@cygnus.com>
* iris6.h (DWARF2_UNWIND_INFO): Undef.
Wed Sep 9 01:32:01 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
Add preliminary native sparcv9 Solaris support.
* configure.in: Recognize sparv9-*-solaris2*
* configure: Rebuilt.
* config.sub: Recognize sparcv9 just like sparc64.
* config/sparc/sol2-c1.asm config/sparc/sol2-ci.asm
config/sparc/sol2-cn.asm: Macroize so it can be shared between
32-bit and 64-bit Solaris systems.
* config/sparc/t-sol2: Assemble those with cpp.
* config/sparc/sparc.h (TARGET_CPU_sparcv9): New alias for v9.
(*TF*_LIBCALL): If ARCH64 use V9 names.
* config/sparc/{xm-sysv4-64,sol2-sld-64}.h: New files.
Wed Sep 9 01:07:30 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* config/sparc/sparc.h (TARGET_CM_MEDMID): Fix documentation.
(CASE_VECTOR_MODE): Set to SImode even if PTR64, when MEDLOW and
not doing pic.
(ASM_OUTPUT_ADDR_{VEC,DIFF}_ELT): Check CASE_VECTOR_MODE not
Pmode.
* config/sparc/sparc.md (tablejump): Likewise, and sign extend op0
to Pmode if CASE_VECTOR_MODE is something else.
Wed Sep 9 00:10:31 1998 Jeffrey A Law (law@cygnus.com)
* prefix.c (update_path): Correctly handle cases where PATH is
a substring of the builtin prefix, but specifies a different
directory location.
Tue Sep 8 23:46:04 1998 Hans-Peter Nilsson <hp@axis.se>
* expr.c: Corrected comment about what MOVE_RATIO does.
* config/alpha/alpha.h: Likewise.
* config/1750a/1750a.h: Likewise.
* config/clipper/clipper.h: Likewise.
* config/i386/i386.h: Likewise.
Tue Sep 8 22:56:12 1998 Jeffrey A Law (law@cygnus.com)
* configure.in (m68k-next-nextstep3*): Use collect2.
Similarly for x86 NeXT configurations.
* configure: Rebuilt.
Tue Sep 8 01:38:57 1998 Nathan Sidwell <nathan@acm.org>
* configure.in: Don't assume srcdir is .../gcc.
* configure: Rebuilt.
Sat Sep 5 16:34:34 1998 John Wehle (john@feith.com)
* global.c: Update comments.
(global_alloc): Assign allocation-numbers
even for registers allocated by local_alloc in case
they are later spilled and retry_global_alloc is called.
(mark_reg_store, mark_reg_clobber,
mark_reg_conflicts, mark_reg_death): Always record a
conflict with a pseudo register even if it has been
assigned to a hard register.
(dump_conflicts): Don't list pseudo registers already assigned to
a hard register as needing to be allocated, but do list their
conflicts.
* local-alloc.c: Update comment.
Mon Sep 7 23:38:01 1998 Jeffrey A Law (law@cygnus.com)
* configure.in: Check for bogus GCC_EXEC_PREFIX and LIBRARY_PATH.
* configure: Rebuilt.
Mon Sep 7 22:41:46 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.c (rs6000_override_options): Fix name for ec603e, to add
missing 'c'.
* t-ppccomm (MULTILIB_MATCHES_FLOAT): Add support for -mcpu=xxx
for all targets that set -msoft-float.
Mon Sep 7 23:30:07 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* toplev.c (print_switch_values): Make static to match prototype.
Mon Sep 7 19:13:59 1998 Jeffrey A Law (law@cygnus.com)
* configure.in: If we are unable to find the "gnatbind" program,
then do not configure the ada subdir.
* configure: Rebuilt.
Sun Sep 6 14:03:58 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Sep 6 13:28:07 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Sun Sep 6 08:54:14 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (toplev.o): Depend on $(EXPR_H).
(insn-extract.o, insn-attrtab.o): Depend on toplev.h.
* gansidecl.h: Define ATTRIBUTE_NORETURN.
* genattrtab.c: Have insn-attrtab.c include toplev.h.
* genextract.c: Have insn-extract.c include toplev.h.
* rtl.h: Don't prototype `fatal_insn_not_found' and `fatal_insn'.
* toplev.c: Include expr.h.
(really_sorry, fancy_abort): Remove prototypes.
(set_target_switch): Add argument in prototype.
(vfatal): Mark prototype with ATTRIBUTE_NORETURN.
(v_really_sorry): Likewise.
(print_version, print_single_switch, print_switch_values): Make
static and add prototype arguments.
(decl_printable_name): Add prototype arguments.
(lang_expand_expr_t): New typedef.
(lang_expand_expr): Declare as a lang_expand_expr_t.
(incomplete_decl_finalize_hook): Add prototype argument.
(decl_name): Mark variable `verbosity' with ATTRIBUTE_UNUSED.
(botch): Likewise for variable `s'.
(rest_of_type_compilation): Mark variables `type' and `toplev'
with ATTRIBUTE_UNUSED if none of DBX_DEBUGGING_INFO,
XCOFF_DEBUGGING_INFO or SDB_DEBUGGING_INFO are defined.
(display_help): Make variable `i' an `unsigned long'.
(main): Remove unused parameter `envp'.
Cast assignment to `lang_expand_expr' to a `lang_expand_expr_t'.
Cast -1 when comparing it with a `size_t'.
* toplev.h (fatal, fatal_io_error, pfatal_with_name): Mark
prototype with ATTRIBUTE_NORETURN.
(fatal_insn_not_found, fatal_insn, really_sorry,
push_float_handler, pop_float_handler): Add prototypes.
(fancy_abort): Mark prototype with ATTRIBUTE_NORETURN.
(do_abort, botch): Add prototypes.
Sat Sep 6 12:05:18 1998 John Carr <jfc@mit.edu>
* final.c (final): If a label is reached only from a single jump,
call NOTICE_UPDATE_CC on the jump and its predecessor before
emitting the insn after the label.
* i386.h: Add AMD K6 support.
Change TARGET_* macros to use table lookup.
(INITIALIZE_TRAMPOLINE): Improve trampoline code.
(ADJUST_COST): Change definition to call function in i386.c.
(ISSUE_RATE): Define as 2 for anything newer than an 80486.
* i386.c: Add AMD K6 support.
Add constants for feature tests used by TARGET_* macros.
(split_di): If before reload, call gen_lowpart and gen_highpart.
(x86_adjust_cost): New function.
(put_jump_code): New function.
(print_operand): New codes 'D' and 'd'.
* i386.md: New insn types. New insn attribute "memory".
Redefine scheduling parameters to use new types and add AMD K6
support. Explicitly set type of most insns.
(move insns): K6 prefers movl $0,reg to xorl reg,reg. Pentium
Pro and K6 prefer movl $1,reg to incl reg.
(adddi3, subdi3): Set cc_status.
(DImode shift patterns): Change label counters from HOST_WIDE_INT
to int; x86 can't have more than 2^31 DImode shifts per file.
(setcc): Combine all setcc patterns. Allow writing memory.
Combine all jump patterns using match_operator.
(*bzero): Name pattern. Emit multiple stos instructions when that
is faster than rep stos.
(xordi3, anddi3, iordi3): Simplify DImode logical patterns and
add define_split.
Sun Sep 6 11:17:20 1998 Dave Love <d.love@dl.ac.uk>
* config/m68k/x-next (BOOT_LDFLAGS): Define suitably for f771
linking.
Sat Sep 5 22:05:25 1998 Richard Henderson <rth@cygnus.com>
* alpha.c (alpha_ra_ever_killed): Inspect the topmost sequence,
not whatever we're generating now.
* alpha.c (set_frame_related_p, FRP): New.
(alpha_expand_prologue): Mark frame related insns.
(alpha_expand_epilogue): Likewise, but with a null FRP.
* alpha.h (INCOMING_RETURN_ADDR_RTX): New.
* alpha.md (exception_receiver): New.
* alpha/crtbegin.asm (.eh_frame): New beginning.
(__do_frame_setup, __do_frame_takedown): New.
* alpha/crtend.asm (.eh_frame): New ending.
* alpha/elf.h (DWARF2_DEBUGGING_INFO): Define.
(ASM_SPEC): Don't emit both dwarf2 and mdebug.
(ASM_FILE_START): Don't emit .file for dwarf2.
* rtl.h (enum reg_note): Add REG_FRAME_RELATED_EXPR.
* rtl.c (reg_note_name): Likewise.
* rtl.texi (REG_NOTES): Likewise.
* dwarf2out.c (dwarf2out_frame_debug): Use it. Recognize a store
without an offset.
Sat Sep 5 14:47:17 1998 Richard Henderson <rth@cygnus.com>
* i386.h (PREFERRED_RELOAD_CLASS): Standard fp constants load to TOS.
* i386.md (movsf, movdf, movxf): Validate memory address returned
from force_const_mem. Kill useless REG_EQUAL setting code.
Sat Sep 5 14:23:31 1998 Torbjorn Granlund <tege@matematik.su.se>
* m68k.md (zero_extendsidi2): Fix typo.
Sat Sep 5 13:40:24 1998 Krister Walfridsson <cato@df.lth.se>
* configure.in: Removed references to the removed file.
* config/xm-netbsd.h: Use ${cpu_type}/xm-netbsd.h for
arm*-*-netbsd* and ns32k-*-netbsd*.
* config/i386/xm-netbsd.h: Removed unnecessary file.
* config/m68k/xm-netbsd.h: Likewise.
* config/sparc/xm-netbsd.h: Likewise.
* config/mips/xm-netbsd.h: Likewise.
Sat Aug 29 13:32:58 1998 Mumit Khan <khan@xraylith.wisc.edu>
* i386/cygwin32.h (BIGGEST_ALIGNMENT): Define.
(PCC_BITFIELD_TYPE_MATTERS): Define to be 0.
* i386/cygwin32.h (ASM_OUTPUT_SECTION_NAME): Don't check for
for exact section attributions.
* i386/mingw32.h (CPP_PREDEFINES): Add __MSVCRT__ for msvc
runtime.
* i386/crtdll.h (CPP_PREDEFINES): Define.
Sat Sep 5 03:23:05 1998 Jeffrey A Law (law@cygnus.com)
* m68k.md (5200 movqi): Do not allow byte sized memory references
using address regs.
* m68k.c (output_move_qimode): Do not use byte sized operations on
address registers.
* Makefile.in (pexecute.o): Use pexecute.c from libiberty. Provide
explicit rules for building. Similarly for alloca, vfprintf,
choose-temp and mkstemp, getopt, getopt1, and obstack.
(INCLUDES): Add $(srcdir)/../include.
* pexecute.c, alloca.c, vfprintf.c, choose-temp.c, mkstemp.c: Delete.
* getopt.h, getopt.c getopt1.c, obstack.c, obstack.h: Likewise.
Fri Sep 4 11:57:50 1998 Tom Tromey <tromey@cygnus.com>
* gcc.c (do_spec_1): [case 'o'] Account for
lang_specific_extra_outfiles.
(main): Correctly clear all slots in outfiles for
lang_specific_extra_outfiles. Set input_file_number before
calling lang_specific_pre_link.
Fri Sep 4 10:37:07 1998 Jim Wilson <wilson@cygnus.com>
* loop.c (load_mems): Fix JUMP_LABEL field after for_each_rtx call.
Fri Sep 4 02:01:05 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (output_double_int): In all V9 symbolic
cases, use xword.
(sparc_output_deferred_case_vectors): If no work to do, return.
Fix thinko in Sept 1 change.
1998-09-03 SL Baur <steve@altair.xemacs.org>
* Makefile.in: Add semicolon in BISON definition for portability.
Thu Sep 3 13:34:41 1998 Toon Moene <toon@moene.indiv.nluug.nl>
* config/nextstep.c (handle_pragma): Correct name of third
argument.
Tue Sep 1 11:30:33 1998 Nick Clifton <nickc@cygnus.com>
* config/m32r/m32r.md: Change (reg:CC 17) to (reg:SI 17).
* config/m32r/m32r.h: Make register 17 be fixed.
* config/m32r/m32r.c: Use SImode for cc operations.
Thu Sep 3 18:17:34 1998 Benjamin Kosnik <bkoz@cygnus.com>
* invoke.texi (Warning Options): Add -Wnon-template-friend
documentation.
Thu Sep 3 18:16:16 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.c (rs6000_override_options): Add -mcpu={401,e603e}.
Thu Sep 3 18:05:16 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (movsf): Disable explicit secondary-reload-like
functionality if TARGET_POWERPC64.
(movdf): Remove TARGET_POWERPC64 explicit secondary-reload-like
functionality.
Thu Sep 3 11:41:40 1998 Robert Lipe <robertl@dgii.com>
* fixinc.sco: Borrow code to wrap 'bool' typedefs from tinfo.h
and term.h from fixinc.wrap.
Thu Sep 3 09:47:31 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* aclocal.m4 (GCC_HEADER_STRING): New macro to detect if it is
safe to include both string.h and strings.h together.
(GCC_NEED_DECLARATION): Test STRING_WITH_STRINGS when deciding
which headers to search for function declarations. Continue to
prefer string.h over strings.h when both are not acceptable.
* acconfig.h (STRING_WITH_STRINGS): Add stub.
* configure.in: Call GCC_HEADER_STRING.
* system.h: Test STRING_WITH_STRINGS when deciding which headers
to include. Continue to prefer string.h over strings.h when both
are not acceptable.
Wed Sep 2 23:56:29 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (output_double_int): If V9 and MEDLOW, do
not assume top 32-bits of symbolic addresses are zero if
flag_pic.
Thu Sep 3 00:23:21 1998 Richard Henderson <rth@cygnus.com>
* ginclude/va-alpha.h: Protect entire second portion of the
file against double inclusion.
Thu Sep 3 00:37:55 1998 Ovidiu Predescu <ovidiu@aracnet.com>
Added support for the Boehm's garbage collector.
* configure.in: Handle --enable-objc-gc.
* configure: Rebuilt.
* Makefile.in (CHECK_TARGETS): Add check-objc.
(check-objc): New rule.
* objc/Make-lang.in: Build a different Objective-C library that
runs with the Boehm's collector.
* objc/encoding.c (objc_round_acc_size_for_types): New function.
* objc/encoding.c: Correctly compute the size of compound types in
the presence of bitfields. Skip the variable name of the type if
any. Added support for long long.
* objc/encoding.h (_C_GCINVISIBLE): New specifier.
(_F_GCINVISIBLE): New mask.
* objc/gc.c: New file. Compute the type memory mask associated with
a class based on the runtime information.
* objc/misc.c: Added the hooks that use the Boehm's collector
allocation functions.
* objc/objc-act.c (build_class_template): Generate a new class
member (gc_object_type) to hold the class' type memory mask.
(build_shared_structure_initializer): Initialize the new member to
NULL.
(encode_complete_bitfield): New function. Generate the new
encoding.
(encode_field_decl): Generate the new encoding only for the GNU
runtime.
* objc/objc-api.h (_C_LNG_LNG, _C_ULNG_LNG): New specifiers for the
long long types.
(class_get_gc_object_type): New function to mark a pointer instance
variable as a weak pointer.
* objc/objc-features.texi: New file.
* objc/objc.h (gc_object_type): New class member.
* objc/objects.c (class_create_instance): Create a typed memory
object when compiled with Boehm's collector support.
* objc/sendmsg.c (__objc_init_install_dtable): Call
__objc_send_initialize instead of setting the initialize flag.
(__objc_send_initialize): Call __objc_generate_gc_type_description
to generate the class type memory mask. Rewrite the code that
sends the +initialize so that it is called only once (bug report
and fix from Ronald Pijnacker <Ronald.Pijnacker@best.ms.philips.com>).
* testsuite/objc: New testsuite for Objective-C type encoding.
* testsuite/lib/objc-torture.exp: New file.
* testsuite/lib/objc.exp: New file.
Wed Sep 2 14:47:36 1998 Jim Wilson <wilson@cygnus.com>
* jump.c (jump_optimize): In if/then/else transformations, add
another call to modified_between_p for the jump insn.
Wed Sep 2 14:16:49 1998 Jeffrey A Law (law@cygnus.com)
* fix-header.c (symlink): Treat like readlink.
Wed Sep 2 19:30:06 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* dwarfout.c (fundamental_type_code): Encode 32 bit floats/doubles
as FT_float.
Wed Sep 2 10:06:07 1998 Nick Clifton <nickc@cygnus.com>
* config/nextstep.h: Update HANDLE_PRAGMA macro.
* config/h8300/h8300.h: Update HANDLE_PRAGMA macro.
* config/i960/i960.h: Update HANDLE_PRAGMA macro.
* config/nextstep.c (handle_pragma): Take three arguments, as per
the new HANDLE_PRAGMA macro specification.
* config/h8300/h8300.c (handle_pragma): Take three arguments, as
per the new HANDLE_PRAGMA macro specification.
* config/i960/i960.c (process_pragma): Take three arguments, as
per the new HANDLE_PRAGMA macro specification.
Wed Sep 2 09:25:29 1998 Nick Clifton <nickc@cygnus.com>
* c-lex.c (check_newline): Call HANDLE_PRAGMA before
HANDLE_SYSV_PRAGMA if both are defined. Generate warning messages
if unknown pragmas are encountered.
(handle_sysv_pragma): Interpret return code from
handle_pragma_token (). Return success/failure indication rather
than next unprocessed character.
(pragma_getc): New function: retrieves characters from the
input stream. Defined when HANDLE_PRAGMA is enabled.
(pragma_ungetc): New function: replaces characters back into the
input stream. Defined when HANDLE_PRAGMA is enabled.
* c-pragma.c (handle_pragma_token): Return success/failure status
of the parse.
* c-pragma.h: Change prototype of handle_pragma_token().
* varasm.c (handle_pragma_weak): Only create this function if
HANDLE_PRAGMA_WEAK is defined.
* c-common,c (decl_attributes): If defined call the expression
contained within the INSERT_ATTRIBUTES macro before adding
attributes to a decl.
* tm.texi (HANDLE_PRAGMA): Document the new version of
HANDLE_PRAGMA, which takes three arguments.
(INSERT_ATTRIBUTES): Document this new macro.
* LANGUAGES: Document the new version of HANDLE_PRAGMA and the
new INSERT_ATTRIBUTES macro.
Wed Sep 2 02:03:23 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (movdf): Only generate special RTL for
LABEL_REFs when PIC.
(move_label_di): Remove.
(movdi_pic_label_ref, movdi_high_pic_label_ref,
movdi_lo_sum_pic_label_ref): New patterns for 64-bit label
references when PIC.
* config/sparc/sparc.h (ASM_OUTPUT_ADDR_VEC_ELT,
ASM_OUTPUT_ADDR_DIFF_ELT): Don't do anything special for MEDLOW,
output an .xword for all 64-bit cases.
Tue Sep 1 15:55:17 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (finalize_pic): Don't output arbitrary
alignment, use FUNCTION_BOUNDARY instead.
(sparc_output_deferred_case_vectors): Likewise.
Mon Aug 31 17:25:41 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (movsf_const_intreg): Kill warning.
(movtf_insn_sp64, movtf_no_e_insn_sp64): Reorder alternatives.
Mon Aug 31 13:57:55 1998 Richard Henderson <rth@cygnus.com>
* alpha/va_list.h: New file.
* alpha/x-alpha (EXTRA_HEADERS): New. Add va_list.h.
Mon Aug 31 14:55:02 1998 Jeffrey A Law (law@cygnus.com)
* NEWS: Add SCO Openserver and Unixware 7 notes.
* NEWS: Fix typos.
Mon Aug 31 15:42:18 1998 Dave Brolley <brolley@cygnus.com>
* varasm.c (compare_constant_1): Handle RANGE_EXPR.
(record_constant_1): Handle RANGE_EXPR.
Mon Aug 31 10:54:03 1998 Richard Henderson <rth@cygnus.com>
* print-rtl.c (print_rtx): NOTE_INSN_LIVE has an rtx not a bitmap.
* haifa-sched.c (sched_analyze): Handle NOTE_INSN_RANGE_START
and NOTE_INSN_RANGE_END specially.
(reemit_notes): Likewise.
Mon Aug 31 10:18:52 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* sparc.c (TMASK, UMASK): Use `(unsigned)1' not `1U'.
(ultrasparc_sched_init): Remove unneeded &.
Mon Aug 31 10:47:16 1998 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* config/m68k/m68k.h (TARGET_SWITCHES): Don't remove MASK_68040
for m68020-60, to prevent the use of fintrz.
Sun Aug 30 22:17:20 1998 Mark Mitchell <mark@markmitchell.com>
* configure.in: If the native compiler is GCC use $(WARN_CFLAGS)
even in stage1.
* Makefile.in: Likewise.
* configure: Regenerated.
Sun Aug 30 22:15:41 1998 H.J. Lu (hjl@gnu.org)
* configure.in (gxx_include_dir): Changed to
'${prefix}/include/g++'-${libstdcxx_interface}.
* configure: Rebuilt.
Sun Aug 30 20:19:43 1998 Hans-Peter Nilsson <hp@axis.se>
* expr.c (expand_expr): Change ">" to ">=" making MOVE_RATIO use
consistent.
* tm.texi (Costs): Say MOVE_RATIO is number of mem-mem move
*sequences* *below* which scalar moves will be used.
Sun Aug 30 17:18:43 1998 Jeffrey A Law (law@cygnus.com)
* collect2.c (mktemp): Delete unused declaration.
* config/xm-netbsd.h: Remove unnecessary file.
* config/*/xm-netbsd.h: Do not include the generic xm-netbsd.h
file anymore, it is not needed.
Sun Aug 30 16:05:45 1998 Mark Mitchell <mark@markmitchell.com>
* convert.c (convert_to_integer): Issue an error on conversions to
incomplete types.
Sun Aug 30 16:47:20 1998 Martin von Lvwis <loewis@informatik.hu-berlin.de>
* Makefile.in: Add lang_tree_files and gencheck.h.
* configure.in: Generate them.
* gencheck.c: Include gencheck.h.
Sat Aug 29 21:38:24 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (pic_lo_sum_di, pic_sethi_di): Rename to
movdi_lo_sum_pic and movdi_high_pic and make visible.
* config/sparc/sparc.c (legitimize_pic_address): For -fPIC,
emit these when Pmode is not SImode.
* config/sparc/linux64.h (SPARC_DEFAULT_CMODEL): Make CM_MEDLOW.
Sat Aug 29 14:59:32 1998 Mumit Khan <khan@xraylith.wisc.edu>
* i386/cygwin32.h (ASM_OUTPUT_SECTION_NAME): Don't emit
.linkonce directive after the first time.
Sat Aug 29 12:39:56 1998 Jeffrey A Law (law@cygnus.com)
* m68k.md (beq0_di): Generate correct (and more efficient) code when
the clobbered operand overlaps with an input.
(bne0_di): Similarly.
* Makefile.in (INSTALL): Remove "--no-header" argument.
* NEWS: Various updates.
Fri Aug 28 19:00:44 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (arith_operand, const64_operand,
const64_high_operand, arith_double_4096_operand): Mark mode as
unused.
(create_simple_focus_bits): Remove unused arg highest_bit_set, all
callers changed.
(sparc_emit_set_const64): Remove unused variable i.
(sparc_splitdi_legitimate): Likewise for addr_part.
(ultra_code_from_mask): Likewise for mask.
(ultra_cmove_results_ready_p): Fixup entry modulo calc. and
reverse return values so it matches usage and comments.
(ultra_flush_pipeline): Likewise.
(ultra_fpmode_conflict_exists): Likewise, remove unused variable
this_type, and allow loads and stores of differing FP modes as
they do not create a conflict.
(ultra_find_type): Initialize fpmode to SFmode, fix
parenthesization thinkos in large conditional.
(ultrasparc_sched_init): Mark dump and sched_verbose as unused.
Init free_slot_mask after ultra_cur_hist is reset, not before.
(ultrasparc_rescan_pipeline_state): Remove unused variable ucode.
(ultrasparc_sched_reorder): Don't bzero current pipeline state,
use ultra_flush_pipeline instead, then re-init group pointer.
Fix statement with no effect. If no progress made in, and no
instructions scheduled at all, advance to new pipeline cycle else
we get into an endless loop.
(ultrasparc_adjust_cost): Remove previous arg.
* config/sparc/sparc.h (ADJUST_COST): Update to reflect that.
Fri Aug 28 13:52:35 1998 Jim Wilson <wilson@cygnus.com>
* sparc.md (DImode, DFmode, TFmode splits): Delete self_reference
code. Use reg_overlap_mentioned_p to detect when source and
destination overlap.
(negtf2_notv9+1): Use DFmode instead of SFmode in last two operands.
1998-08-28 Brendan Kehoe <brendan@cygnus.com>
* loop.c (check_dbra_loop): Pass COMPARISON_VALUE, not
COMPARISON_VAL, into invariant_p.
Fri Aug 28 15:13:25 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* regmove.c (regclass_compatible_p): New function.
(regmove_optimize): Use it.
Use NREGS parameter instead of calling max_reg_num.
(fixup_match_1): Don't use code = MINUS when later tieing with
a hard register is likely.
Fri Aug 28 14:54:07 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (check_dbra_loop): Fix calculation of FINAL_VALUE when
COMPARISON_VAL was normalized.
Thu Aug 27 20:10:46 1998 Jeffrey A Law (law@cygnus.com)
* loop.c (check_dbra_loop): The loop ending comparison value
must be an invariant or we can not reverse the loop.
* loop.c (scan_loop): Count down from max_reg_num - 1 to
FIRST_PSEUDO_REGISTER to avoid calling max_reg_num each iteration
of the loop.
(load_mems_and_recount_loop_regs_set): Likewise.
* i386.c (print_operand): Remove obsolete 'c' docs.
Wed Aug 26 17:13:37 1998 Tom Tromey <tromey@cygnus.com>
* gthr.h: Document __GTHREAD_MUTEX_INIT_FUNCTION.
* frame.c (init_object_mutex): New function.
(init_object_mutex_once): Likewise.
(find_fde): Call it.
(__register_frame_info): Likewise.
(__register_frame_info_table): Likewise.
(__deregister_frame_info): Likewise.
Thu Aug 27 15:14:18 1998 Jeffrey A Law (law@cygnus.com)
* haifa-sched.c (sched_analyze_insn): Fix thinko in last change.
Thu Aug 27 16:34:51 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (check_dbra_loop): Enable code for reversal
of some loops without a known constant loop end.
Wed Aug 26 18:38:15 1998 Richard Henderson <rth@cygnus.com>
* haifa-sched.c (last_clock_var): New.
(schedule_block): Initialize it.
(schedule_insn): Use it to fill insn modes with issue information.
* alpha.c (alpha_handle_trap_shadows): Remove do-nothing exit.
Tag trapb and next insn with TImode.
(alphaev5_insn_pipe, alphaev5_next_group, alphaev5_align_insns): New.
(alpha_reorg): Add conditional for alpha_handle_trap_shadows.
Invoke alphaev5_align_insns as appropriate.
* alpha.h (LABEL_ALIGN_AFTER_BARRIER): Was ALIGN_LABEL_AFTER_BARRIER.
(MD_SCHED_VARIABLE_ISSUE): New.
* alpha.md (attr type): Add multi.
(define_asm_attributes): New.
(prologue_stack_probe_loop, builtin_setjmp_receiver): Set type multi.
(arg_home): Likewise.
(fnop, unop, realign): New.
Wed Aug 26 15:55:41 1998 Jim Wilson <wilson@cygnus.com>
* iris5.h (PREFERRED_DEBUGGING_TYPE): Undef.
* iris5gas.h (PREFERRED_DEBUGGING_TYPE): Define.
* configure.in (powerpc-ibm-aix4.[12]*): Change from 4.[12].*.
(rs6000-ibm-aix4.[12]*): Likewise.
* configure: Regenerate.
Wed Aug 26 09:30:59 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/thumb.c (thumb_exit): Do not move a4 into lr if it
already contains the return address.
Wed Aug 26 12:57:09 1998 Jeffrey A Law (law@cygnus.com)
* calls.c (expand_call): Use bitfield instructions to extract/deposit
word sized hunks when loading unaligned args into registers.
* haifa-sched.c (sched_analyze_insn): Only create scheduling
barriers for LOOP, EH and SETJMP notes on the loop_notes list.
* mn10300.h (RTX_COSTS): Handle UDIV and UMOD too.
Wed Aug 26 16:35:37 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* loop.c (check_dbra_loop): Add some code that would allow reversal
of some loops without a known constant loop end if it were enabled.
Wed Aug 26 11:08:44 1998 Gavin Romig-Koch <gavin@cygnus.com>
* mips.md (lshrsi3_internal2+2): Fix type-o.
Wed Aug 26 10:53:03 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* system.h: Include stdarg.h/varargs.h, make sure they are ordered
correctly with regards to stdio.h.
* calls.c: Remove stdarg.h/varargs.h.
* cccp.c: Likewise.
* cexp.y: Likewise.
* combine.c: Likewise.
* cpperror.c: Likewise.
* cpplib.c: Likewise.
* cpplib.h: Likewise.
* doprint.c: Likewise.
* emit-rtl.c: Likewise.
* final.c: Likewise.
* fix-header.c: Likewise.
* gcc.c: Likewise.
* genattr.c: Likewise.
* genattrtab.c: Likewise.
* gencodes.c: Likewise.
* genconfig.c: Likewise.
* genemit.c: Likewise.
* genextract.c: Likewise.
* genflags.c: Likewise.
* genopinit.c: Likewise.
* genoutput.c: Likewise.
* genpeep.c: Likewise.
* genrecog.c: Likewise.
* mips-tfile.c: Likewise.
* prefix.c: Likewise.
* protoize.c: Likewise.
* regmove.c: Likewise.
* toplev.c: Likewise.
* tree.c: Likewise.
Wed Aug 26 05:09:27 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* config/sparc/sparc.c (sparc_override_options): If not
TARGET_FPU, turn off TARGET_VIS.
* config/sparc/sparc.h (TARGET_SWITCHES): Add no-vis.
(LEGITIMATE_CONSTANT_P): Allow SF/DF mode zero when TARGET_VIS.
* config/sparc/sparc.md (movsi_insn): Use fzeros not fzero.
(movdi_insn_sp64): Add VIS fzero alternative.
(clear_sf, clear_df): New VIS patterns.
(movsf, movdf expanders): Allow fp_zero_operand flat out when
TARGET_VIS.
(one_cmpldi2_sp64): Provide new fnot1 VIS alternative.
Tue Aug 25 10:57:41 1998 Mark Mitchell <mark@markmitchell.com>
* loop.c (n_times_set, n_times_used, may_not_optimize,
reg_single_usage): Convert to varrays. All uses changed.
(insert_loop_mem): Return a value.
(scan_loop): Tweak AVOID_CC_MODE_COPIES code.
(load_mems_and_recount_loop_regs_set): Likewise. Grow the arrays, if
necessary.
Tue Aug 25 23:57:12 1998 Jeffrey A Law (law@cygnus.com)
* From Alexandre:
* configure.in: Do not set thread_file to "irix" since no such
support exists yet.
* sparc.md (float abs/neg splits): Check reload_completed before
calling alter_subreg.
Tue Aug 25 19:17:59 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (sparc_absnegfloat_split_legitimate): New
function.
* config/sparc/sparc.h: Declare it.
* config/sparc/sparc.md (float abs/neg splits): Use it.
(all other splits): Handle SUBREGs properly where necessary.
(unnamed (1<<x)-1 V8PLUS pattern): Disable for now.
Tue Aug 25 19:48:46 1998 Jeffrey A Law (law@cygnus.com)
* reorg.c (fill_simple_delay_slots): Do not abort if we encounter
an insn on the unfilled_slots_list that has no delay slots.
(fill_eager_delay_slots): Similarly.
Tue Aug 25 13:35:20 1998 Nick Clifton <nickc@cygnus.com>
* config/v850/v850.c (movsi_source_operand): Treat CONSTANT_P_RTX
as an ordinary operand.
Tue Aug 25 12:54:57 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (valid_machine_attribute): Don't apply attributes to both
decl and type.
Tue Aug 25 12:23:20 1998 Richard Henderson <rth@cygnus.com>
* reload.c (operands_match_p): Handle rtvecs.
* i386.c (legitimate_pic_address_disp_p): New.
(legitimate_address_p): Use it.
(legitimize_pic_address): Use unspecs to represent @GOT and @GOTOFF.
Handle constant pool symbols just like statics.
(emit_pic_move): Use Pmode not SImode for clarity.
(output_pic_addr_const) [SYMBOL_REF]: Remove @GOT and @GOTOFF hacks.
[UNSPEC]: New, handling what we killed above.
[PLUS]: Detect and abort on invalid symbol arithmetic.
* i386.h (CONSTANT_ADDRESS_P): Remove HIGH.
Tue Aug 25 12:02:23 1998 Mark Mitchell <mark@markmitchell.com>
* alias.c: Include output.h.
(DIFFERENT_ALIAS_SETS_P): Don't treat alias sets as
different if we're in a varargs function.
* Makefile.in (alias.o): Depend on output.h
Tue Aug 25 19:20:12 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* sh.h (GIV_SORT_CRITERION): Delete.
Tue Aug 25 13:19:46 1998 Dave Brolley <brolley@cygnus.com>
* regclass.c (regclass): Use xmalloc/free instead of alloca.
* stupid.c (stupid_life_analysis): Likewise.
* reload1.c (reload): Likewise.
Tue Aug 25 05:48:18 1998 Jakub Jelinek <jj@sunsite.ms.mff.cuni.cz>
* config/sparc/sparc.c (arith_4096_operand, arith_add_operand,
arith_double_4096_operand, arith_double_add_operand): New
predicates.
* config/sparc/sparc.h (PREDICATE_CODES): Add them, declare them.
* config/sparc/sparc.md (adddi3, addsi3, subdi3, subsi3): Use
them to transform add/sub 4096 into add/sub -4096.
Mon Aug 24 23:31:03 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* loop.c (scan_loop): Allocate some slop to handle pseudos
generated by move_movables.
(load_mems_and_recount_loop_regs_set): Honor AVOID_CC_MODE_COPIES
here too.
Mon Aug 24 19:45:40 1998 Jim Wilson <wilson@cygnus.com>
* tree.def (DECL_RESULT): Correct documentation.
Tue Aug 25 01:15:27 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* reload1.c (reload_reg_free_before_p): New argument EQUIV; Changed
all callers. Abort for RELOAD_FOR_INSN. RELOAD_FOR_OUTADDR_ADDR:
conflicts will all RELOAD_FOR_OUTPUT reloads.
* reload1.c (reload_cse_regs_1): When deleting a no-op move that
loads the function result, substitute with a USE.
Mon Aug 24 15:20:19 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (GO_IF_LEGITIMATE_ADDRESS): Use TARGET_POWERPC64
when testing LEGITIMATE_INDEXED_ADDRESS_P DFmode and DImode.
(LEGITIMIZE_ADDRESS): Use TARGET_POWERPC64 for INDEXED fixup.
* rs6000.c (print_operand, case 'L'): Add UNITS_PER_WORD, not 4.
(print_operand, cases 'O' and 'T'): Fix typos in lossage strings.
* rs6000.md (fix_truncdfsi2_store): Remove %w from non-CONST_INT
operand.
(movdf_softfloat32, movdf_hardfloat64, movdf_softfloat64): Change
'o' to 'm' for GPR variant constraints.
Mon Aug 24 10:25:46 1998 Jeffrey A Law (law@cygnus.com)
* loop.c (scan_loop): Honor AVOID_CC_MODE_COPIES.
* h8300.h (STRIP_NAME_ENCODING): Fix typo.
* sparc.md (TFmode splits): Use reg_overlap_mentioned_p to detect
when the source and destination overlap.
* stmt.c (emit_case_nodes): Change rtx_function to rtx_fn to avoid
clash with global type.
Mon Aug 24 00:53:53 1998 Jason Merrill <jason@yorick.cygnus.com>
* fixinc.irix: Add curses.h handling from fixinc.wrap.
* c-common.c (combine_strings): Also set TREE_READONLY.
Change warn_write_strings to flag_const_strings.
* c-decl.c, c-tree.h: Likewise.
Sun Aug 23 18:39:11 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (sparc_emit_set_const32): If outputting a
CONST_INT, not a symbolic reference, don't use a HIGH/LO_SUM
sequence, use SET/IOR instead so CSE can see it.
* config/sparc/sparc.md (movhi_const64_special,
movsi_const64_special): New patterns necessitated by that change.
(movhi_high): Remove.
(movhi_lo_sum): Change to match an IOR.
(movdf_insn_sp32): Test TARGET_V9 not TARGET_ARCH64.
(movdf_insn_v9only): New pattern for when V9 but not ARCH64.
(movdf_insn_sp64): Test both TARGET_V9 and TARGET_ARCH64.
(movdf splits): Allow when not V9 or when not ARCH64 and integer
registers are involved.
(snesi_zero_extend split): Remove reload_completed test.
(unnamed plus and minus zero_extend sidi splits): Add it.
Sun Aug 23 11:56:08 1998 Mark Mitchell <mark@markmitchell.com>
* extend.texi: Remove description of extension to explicit
instantiation that is now endorsed by standard C++.
Sun Aug 23 09:39:09 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/arc/arc.c (arc_initialize_pic): Remove.
* config/arc/arc.h (INITIALIZE_PIC): Similarly, this routine does
nothing on any platform and is invoked by no-one, it does not even
appear in the documentation.
* config/sparc/sparc.h (INITIALIZE_PIC): Likewise.
* config/sparc/sparc.c (initialize_pic): Likewise.
(find_addr_reg): Remove this as well, no longer referenced after
my rewrite.
Sun Aug 23 00:17:14 1998 Jeffrey A Law (law@cygnus.com)
* recog.c (validate_replace_rtx_group): New function.
* recog.h (validate_replace_rtx_group): Declare it.
* regmove.c (optimize_reg_copy_3): If any substitution fails, then undo
the entire group of substitutions.
Sat Aug 22 23:31:00 1998 Klaus-Georg Adams (Klaus-Georg.Adams@chemie.uni-karlsruhe.de)
* loop.c (load_mems): Fix initializers.
Fri Aug 21 23:07:46 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (TFmode splits): Handle destination
registers being referenced in the address correctly.
* expmed.c (make_tree) [CONST_INT]: Sign extend even if
TREE_UNSIGNED, when bitsize of type's mode is larger than
HOST_BITS_PER_WIDE_INT.
Fri Aug 21 19:31:31 1998 Alexandre Petit-Bianco <apbianco@cygnus.com>
* tree.def (LABELED_BLOCK_EXPR, EXIT_BLOCK_EXPR): New tree nodes.
* tree.h (LABELED_BLOCK_LABEL, LABELED_BLOCK_BODY,
EXIT_BLOCK_LABELED_BLOCK, EXIT_BLOCK_RETURN, LOOP_EXPR_BODY): New
macros.
* expr.c (expand_expr): Handle LABELED_BLOCK_EXPR and
EXIT_BLOCK_EXPR.
Thu Aug 20 19:43:44 1998 Jeffrey A Law (law@cygnus.com)
* h8300.c (h8300_encode_label): Use '&' for tiny data items.
* h8300.h (TINY_DATA_NAME_P): Likewise.
(STRIP_NAME_ENCODING): Handle '&'.
* mn10200.h (REG_OK_FOR_INDEX_P): Do not check the mode of the
register (it could be accessed via an outer SUBREG).
(REG_OK_FOR_BASE_P): Likewise.
(GO_IF_LEGITIMATE_ADDRESS): Consistently use REGNO_OK_FOR_BASE_P.
* remove.c (optimize_reg_copy_3): Abort instead of silently generating
bogus rtl.
* jump.c (rtx_renumbered_equal_p): Do not consider PLUS commutative.
Thu Aug 20 17:35:20 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (movtf_insn_sp32): All memory operands
must be offsettable so the splits can be made.
Thu Aug 20 13:56:53 1998 Michael Meissner <meissner@cygnus.com>
* config/i386/winnt.c: Include system.h, not stdio.h to get
sys/param.h pulled in before rtl.h in case the system defines MIN
and MAX.
Thu Aug 20 13:44:20 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.md (movqi, movhi): Add CONSTANT_P_RTX.
Thu Aug 20 13:15:11 1998 Dave Brolley <brolley@cygnus.com>
* stor-layout.c (layout_type): Compute TYPE_SIZE_UNIT correctly for
arrays of bits.
* cpplib.c (cpp_define): Handle macros with parameters.
Wed Aug 19 21:33:19 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (rs6000_output_load_toc_table): Use ld for 64-bit.
(output_toc): Use single TOC slot or llong minimal-toc for DFmode
and DImode 64-bit. Use llong for minimal-toc SFmode and
SYMBOL_REF / LABEL_REF 64-bit.
(output_function_profiler): Use llong for profiler label and ld to
load 64-bit label address.
Wed Aug 19 17:52:27 1998 Nick Clifton (nickc@cygnus.com)
* config/arm/thumb.md (extendqisi2_insn): Cope with REG +
OFFSET addressing.
Wed Aug 19 14:13:31 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Wed Aug 19 13:10:30 1998 Jeff Law (law@cygnus.com)
* version.c: Bump for snapshot.
Wed Aug 19 13:06:47 1998 Jason Merrill <jason@yorick.cygnus.com>
* collect2.c (extract_init_priority): Use atoi instead of strtoul.
Wed Aug 19 13:51:35 1998 Hans-Peter Nilsson <hp@axis.se>
* tm.texi (Misc): Fix typo "teh".
* tm.texi (PIC): Fix typo "PPIC".
* tm.texi (Caller Saves): Say that DEFAULT_CALLER_SAVES has no
effect when -O2 and higher.
* invoke.texi (Optimize Options): Likewise for -fcaller-saves.
1998-08-19 Michael Hayes <michaelh@ongaonga.chch.cri.nz>
* regclass.c: Changed register set documentation to be consistent
with GCC behavior.
* final.c (final_start_function): Removed redundant test for
call_fixed_regs.
Wed Aug 19 13:28:41 1998 Mark Mitchell <mark@markmitchell.com>
* rtl.h (rtx_function): New type.
(for_each_rtx): New function.
* rtlanal.c (for_each_rtx): Define it.
* recog.c (change_t): New type.
(change_objects, change_old_codes, change_locs, change_olds):
Replace with ...
(changes): New variable.
(validate_change): Dynamically allocate room for more changes, if
necessary. Uses changes array instead of change_objects, etc.
(apply_change_group): Use changes array instead of
change_objects, etc.
* loop.c (loop_mem_info): New type.
(loop_mems): New variable.
(loop_mems_idx): Likewise.
(looop_mems_allocated): Likewise.
(scan_loop): Remove nregs parameter.
(next_insn_in_loop): New function.
(load_mems_and_recount_loop_regs_set): Likewise.
(load_mems): Likewise.
(insert_loop_mem): Likewise.
(replace_loop_mem): Likewise.
(replace_label): Likewise.
(INSN_IN_RANGE_P): New macro.
(loop_optimize): Don't pass max_reg_num() to scan_loop.
(scan_loop): Remove nregs parameter, compute it after any new
registers are created by load_mems. Use INSN_IN_RANGE_P and
next_insn_in_loop rather than expanding them inline. Call
load_mems to load memory into pseudos, if appropriate.
(prescan_loop): Figure out whether or not there are jumps from the
loop to targets other than the label immediately following the
loop. Call insert_loop_mem to notice all the MEMs used in the
loop, if it could be safe to pull MEMs into REGs for the duration
of the loop.
(strength_reduce): Use next_insn_in_loop. Tweak comments.
Wed Aug 19 08:29:44 1998 Richard Earnshaw (rearnsha@arm.com)
* arm.c (arm_override_options): Remove lie about ignoring PIC flag.
Wed Aug 19 07:08:15 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (finalize_pic): Check for the correct
nonlocal_goto_receiver UNSPEC number.
* config/sparc/sparc.md (nonlocal_goto_receiver): Add comment
making note of this dependency existing in sparc.c.
(negtf2_notv9 split): Give NEG SFmode.
(negsf2): Fix insn output string.
Tue Aug 18 12:40:27 1998 Richard Henderson <rth@cygnus.com>
* c-common.c (decl_attributes): Issue an error if the argument
to alias is not a string.
Tue Aug 18 10:33:30 1998 Jeffrey A Law (law@cygnus.com)
* haifa-sched.c (sched_analyze): Put all JUMP_INSNs on the last
pending memory flush list.
* combine.c (can_combine_p): Allow combining insns with REG_RETVAL
notes.
(try_combine): Allow combining insns with REG_LIBCALL notes.
* expr.c (emit_block_move): Do not call memcpy as a libcall
instead build up a CALL_EXPR and call it like any other
function.
(clear_storage): Similarly for memset.
* regmove.c (fixup_match_2): Do not call reg_overlap_mentioned_p
on notes.
* Makefile.in (cplus-dem.o): Provide explicit rules for building
cplus-dem.o.
* regmove.c (optimize_reg_copy_1): Update REG_N_CALLS_CROSSED
and REG_LIVE_LENGTH as successful substitutions are made.
Tue Aug 18 07:15:27 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* config/sparc/sparc.c (ultra_find_type): Add empty semicolon
statement after end of loop label.
Tue Aug 18 07:13:27 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (ultra_types_avail): New variable.
(ultra_build_types_avail): New function to record mask of insn
types in ready list at this cycle.
(ultrasparc_sched_reorder): Call it.
(ultra_find_type): Use it to quicken the search. Also simplif
dependency check, don't use rtx_equal_p because we know exactly
what we are looking for.
Tue Aug 18 03:20:53 1998 Richard Earnshaw (rearnsha@arm.com)
* arm.h (SECONDARY_INPUT_RELOAD_CLASS): Return NO_REGS if compiling
for architecture v4.
Mon Aug 17 21:26:38 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (sltu, sgeu): Don't FAIL, call
gen_compare_reg.
(movsf_const_intreg, movsf_const_high, movsf_const_lo,
movdf_const_intreg and helper splits): New patterns to move float
constants into integer registers.
(negtf2, negdf2, abstf2, absdf2): Rework using new patterns and
splits.
Mon Aug 17 11:46:19 1998 Jeffrey A Law (law@cygnus.com)
* From Graham
* tree.c (build_index_type): Copy TYPE_SIZE_UNIT from sizetype
to itype.
* c-decl.c (finish_enum): Copy TYPE_SIZ_UNIT from enumtype to tem.
* rs6000.c (secondary_reload_class): For TARGET_ELF, indicate that
a BASE_REGS register is needed as an intermediate when copying
a symbolic value into any register class other than BASE_REGS.
* expr.c (move_by_pieces): No longer static. Remove prototype.
* rtl.h (move_by_pieces): Add extern prototype.
* mips.c (expand_block_move): Handle aligned straight line copy by
calling move_by_pieces.
* expr.c (expand_expr): Allow assignments from TImode PARM_DECLs
and VAR_DECLs.
Mon Aug 17 10:28:52 1998 Mark Mitchell <mark@markmitchell.com>
* stmt.c (expand_end_loop): Tidy. Allow unconditional
jumps out of the loop to be treated as part of the exit test.
Mon Aug 17 10:06:11 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
Jeff Law <law@cygnus.com>
* Makefile.in (cplus-dep.o): Use cplus-dem.c from libiberty.
* cplus-dem.c: Delete.
* Makefile.in (fold-const.o): Depend on $(RTL_H).
* fold-const.c: Include rtl.h to get the prototype for
`set_identifier_local_value'.
* loop.c (express_from_1): Remove unused variable `tmp'.
(combine_givs): Cast the first argument of bzero to char *.
* toplev.c (display_help): Remove unused variable `looking_for_start'.
* c-decl.c (init_decl_processing): Remove unneeded &.
* alpha.h (alpha_initialize_trampoline): Provide prototype.
* except.c (set_exception_lang_code, set_exception_version_code):
Change parameter from `short' to `int' to avoid using a gcc
extension.
* except.h (set_exception_lang_code, set_exception_version_code):
Likewise for prototypes.
* flow.c (count_reg_references): Remove unused variables `regno'
and `i'.
* gcse.c (hash_scan_insn): Declare parameter `in_libcall_block'.
* prefix.c (translate_name): Cast the result of `alloca'.
* varray.h (VARRAY_FREE): Reimplement as a `do-while(0)' statement.
Mon Aug 17 09:23:42 1998 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* config/m68k/m68k.c: Include "system.h" instead of <stdio.h>.
Include "toplev.h".
(valid_dbcc_comparison_p): Mark mode argument as unused.
(symbolic_operand): Likewise.
(legitimize_pic_address): Likewise.
(const_uint32_operand): Likewise.
(const_sint32_operand): Likewise.
* sched.c [!INSN_SCHEDULING]: Define only dummy function
schedule_insns and comment out rest of file.
* m68k.c (output_move_simode_const): Use subl to move a zero into an
address register.
(output_move_[hq]imode): Likewise.
Mon Aug 17 09:15:47 1998 Jeffrey A Law (law@cygnus.com)
* toplev.c (main): Enable -fstrict-aliasing for -O2 and above.
* invoke.texi: Corresponding changes.
Mon Aug 17 02:03:55 1998 Richard Henderson <rth@cygnus.com>
* regclass.c (allocate_reg_info): Respect MIN when clearing data.
Sun Aug 16 17:37:06 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (ultra_code_from_mask,
ultra_cmove_results_ready_p, ultra_fpmode_conflict_exists,
ultra_find_type, ultra_schedule_insn, ultra_flush_pipeline,
ultrasparc_sched_init, ultrasparc_variable_issue,
ultra_rescan_pipeline_state, ultrasparc_sched_reorder): New
functions to describe UltraSPARC pipeline exactly to Haifa.
(ultrasparc_adjust_cost): Indicate IMUL type insns have zero cost,
as there is nothing the scheduler can do about it. Indicate that
REG_DEP_OUTPUT's collide. Fixup formatting.
* config/sparc/sparc.h (RTX_COSTS): Fixup integer multiply and
divide costs on Ultra for DImode.
(MD_SCHED_INIT, MD_SCHED_REORDER, MD_SCHED_VARIABLE_ISSUE):
Define.
* config/sparc/sparc.md (ieu_unnamed function unit): Rename to
ieuN and add call_no_delay_slot to type list.
(cti function unit): New unit for branches on UltraSPARC.
(subx/addx insns): Set type to misc.
(sidi zero/sign extension insns on arch64): Set type to shift.
(sign_extendhidi2_insn): Set type to sload.
Sun Aug 16 13:52:00 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.c (rs6000_stack_info): Use if == 0 for sizes.
(output_epilog): Use if != 0 for offset.
(rs6000_fatal_bad_address): Prepare for Intl.
* rs6000.h (rs6000_fatal_bad_address): Declare.
* rs6000.md (movsfcc, movdfcc): Use else if.
(elf_high): Use {liu|lis}.
(elf_low): Use {cal|la}. Remove %a template from old mnemonics.
(movsi): Use rs6000_fatal_bad_address.
Sun Aug 16 01:53:21 1998 Richard Henderson <rth@cygnus.com>
* reload.c (find_equiv_reg): Reject equivalences separated
by a volatile instruction.
Sun Aug 16 00:21:44 1998 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
* rs6000/linux.h (CPP_OS_DEFAULT_SPEC): Define.
Sat Aug 15 20:51:35 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (movsicc): Fix mode mismatch.
Sat Aug 15 20:22:33 1998 H.J. Lu (hjl@gnu.org)
* config/alpha/alpha.h (ASM_OUTPUT_MI_THUNK): Handle aggregated
return type.
* config/alpha/win-nt.h (ASM_OUTPUT_MI_THUNK): Likewise.
Sat Aug 15 08:39:49 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (movsi_lo_sum_pic_label_reg): Remove
write-only modifier from operand 1 constraint.
Sat Aug 15 06:28:19 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (sparc_emit_set_const64_quick1): If
emitting a XOR of -1 at the end, emit a NOT instead for combine's
sake.
(sparc_emit_set_const64): Likewise, also when computing trailing
bits do not negate low_bits and make fast_int an int.
Fri Aug 14 21:07:03 1998 Jeffrey A Law (law@cygnus.com)
* loop.c (add_label_notes): Do not ignore references to labels
before dispatch tables. Mirrors Apr 8 change to mark_jump_label.
* gcse.c (add_label_notes): Similarly.
* pa.h (ASM_OUTPUT_MI_THUNK): Strip name encoding.
* m68k.md (adddi_dilshr32): One of the operands must be a register.
(adddi_dishl32): Similarly.
Fri Aug 14 14:12:59 1998 Jason Merrill <jason@yorick.cygnus.com>
* i386.h (MODES_TIEABLE_P): Reorganize to shut up warnings.
* alias.c (memrefs_conflict_p): Add braces to shut up warnings.
* cse.c (cse_basic_block): Add parens to shut up warnings.
Fri Aug 14 12:58:21 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (sparc_emit_set_const64_quick2,
sparc_emit_set_const64_longway, const64_is_2insns,
create_simple_focus_bits, sparc_emit_set_const64): Fix more bugs
in 64-bit constant formation.
* config/sparc/sparc.md (snesi_zero_extend split): Generate
rtl for addx not subx.
(define_insn movdi_const64_special): Make available even when
HOST_BITS_PER_WIDE_INT is not 64.
(movdi_lo_sum_sp64_cint, movdi_high_sp64_cint): Remove.
(losum_di_medlow, sethm, setlo): Make op2 symbolic_operand.
(cmp_siqi_trunc_set, cmp_diqi_trunc_set): Encapsulate both
instances of operand 1 inside a QI subreg.
(xordi3_sp64_dbl): Remove '%' constraint for op1.
(one_cmpldi2_sp64): Fix output string.
(one_cmplsi2_not_liveg0): Rewrite to remove unneeded extra
alternative case.
(unnamed arch64 ashift DI): Truncate shift count if greater than
63, not 31.
Fri Aug 14 21:52:53 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* expr.c (store_expr): Don't optimize away load-store pair
when either source or destination have a side effect.
Fri Aug 14 16:50:10 1998 John Carr <jfc@mit.edu>
* genrecog.c (add_to_sequence): Fatal error if the modes of the
operands of SET are incompatible.
* alpha.md: Fix max and min patterns so modes of SET operands match.
Fri Aug 14 12:22:55 1998 Ian Lance Taylor <ian@cygnus.com>
* configure.in: Avoid [[ by using test and changequote.
* configure: Rebuild.
Fri Aug 14 01:22:31 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* rtl.def (CONSTANT_P_RTX): Fix typo in string name.
* config/sparc/sparc.md (seqdi_special_trunc, snedi_special_trunc,
seqsi_special_extend, snesi_special_extend, snesi_zero_extend and
split, snedi_zero_trunc and split, seqsi_zero_extend and split,
seqdi_zero_trunc and split, pic_lo_sum_di, pic_sethi_di,
movdi_cc_sp64_trunc, movdi_cc_reg_sp64_trunc, addx_extend_sp32 and
split, addx_extend_sp64, subx_extend_sp64, subx_extend and split):
Fix mismatching modes in SET operands.
(conditional move patterns): Fix formatting.
(unnamed subx arch64 pattern): Remove duplicate insn.
Fri Aug 14 00:34:34 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (const64_operand, const64_high_operand):
Get it right when HOST_BITS_PER_WIDE_INT is not 64.
(input_operand): Fixup test for what we accept for constant
integers.
(sparc_emit_set_const32, sparc_emit_set_symbolic_const64): Give
set VOIDmode.
(safe_constDI): Remove.
(sparc_emit_set_safe_HIGH64, gen_safe_SET64, gen_safe_OR64,
gen_safe_XOR64): New functions.
(sparc_emit_set_const64_quick1, sparc_emit_set_const64_quick2,
sparc_emit_set_const64_longway, sparc_emit_set_const64): Use
them.
* config/sparc/sparc.md (define_insn xordi3_sp64_dbl): Only make
available when HOST_BITS_PER_WIDE_INT is not 64.
(define_insn movdi_sp64_dbl, movdi_const64_special): Likewise and
move before movdi_insn_sp64 pattern.
(define_insn movdi_lo_sum_sp64_dbl, movdi_high_sp64_dbl): Remove.
(define_insn sethi_di_medlow, seth44, setm44, sethh): Use
symbolic_operand as predicate for second operand.
(DImode minus split on arch32, negsi2 expander, one_cmplsi2
expander): Give set VOIDmode.
Fri Aug 14 01:45:06 1998 Mumit Khan <khan@xraylith.wisc.edu>
* i386/cygwin32 (DEFAULT_PCC_STRUCT_RETURN): Define.
Fri Aug 14 01:40:21 1998 Geoffrey Keating <geoffk@ozemail.com.au>
* rs6000/linux.h (LINK_SPEC): Pass -G args to the linker.
Fri Aug 14 01:23:23 1998 Richard Earnshaw (rearnsha@arm.com)
* arm/netbsd.h (TARGET_DEFAULT): Default includes software floating
point.
(CPP_FLOAT_DEFAULT_SPEC): Re-define accordingly.
Fri Aug 14 01:19:08 1998 Robert Lipe <robertl@dgii.com>
* install.texi: Various SCO OpenServer tweaks.
Thu Aug 13 20:14:40 1998 Jim Wilson <wilson@cygnus.com>
* reload1.c (eliminate_regs_in_insn): Handle another case when
eliminating the frame pointer to the hard frame pointer. Add
missing ep->to_rtx check to one existing case.
* mips/mips.md (movhi_internal2+2): Fix typo mem:SI -> mem:HI.
Thu Aug 13 17:08:11 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.h: De-conditionalize init_priority code.
* mips.h (NM_FLAGS): Change from -Bp to -Bn.
* collect2.c (NM_FLAGS): Change from -p to -n.
* configure.in: Turn on collect2 for mipstx39-elf.
Handle use_collect2=no properly.
* c-common.c: De-conditionalize init_priority code.
* collect2.c (extract_init_priority, sort_ids): New fns.
(main): Call sort_ids.
Move sequence_number to file scope.
* configure.in: Handle --enable-init-priority.
* c-common.c (attrs): Add A_INIT_PRIORITY.
(init_attributes, decl_attributes): Likewise.
* tree.h (DEFAULT_INIT_PRIORITY, MAX_INIT_PRIORITY): New macros.
* tree.c (get_file_function_name_long): Split out...
(get_file_function_name): ...from here.
Thu Aug 13 16:09:53 1998 Martin von Loewis <loewis@informatik.hu-berlin.de>
* expr.c (safe_from_p): Change code to ERROR_MARK only when not
accessing nodes.
Thu Aug 13 15:24:48 1998 Jason Merrill <jason@yorick.cygnus.com>
* toplev.c (display_help): Add braces to shut up warnings.
* tree.c (simple_cst_equal): Likewise.
* fold-const.c (non_lvalue): Don't deal with null pointer
constants here.
(fold, case COMPOUND_EXPR): Wrap a constant 0 in a NOP_EXPR.
* c-typeck.c (initializer_constant_valid_p): Allow conversion of 0
of any size to a pointer.
Thu Aug 13 12:53:13 1998 Jim Wilson <wilson@cygnus.com>
* i386/winnt.c (i386_pe_asm_file_end): Check TREE_SYMBOL_REFERENCED.
Wed Aug 12 17:25:18 1998 Jeffrey A Law (law@cygnus.com)
* mn10300.c (REG_SAVE_BYTES): Only reserve space for registers
which will be saved.
* mn10300.md (prologue insn): Only save registers which need saving.
(epilogue insn): Similarly.
* mn10300.c, mn10300.h, mn10300.md: Remove "global zero register"
optimizations.
Wed Aug 12 12:39:16 1998 Gavin Romig-Koch <gavin@cygnus.com>
* mips/mips.h (ENCODE_SECTION_INFO): Set SYMBOL_REF_FLAG for
VAR_DECL's in gp addressable sections.
Tue Aug 11 23:02:31 1998 John Carr <jfc@mit.edu>
* sparc.c: Change return <exp> to <exp>; return; in functions
returning void.
* sparc.md: Add empty semicolon statement after final label in
move expanders.
Tue Aug 11 22:42:01 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.md (define_insn addx_extend): Rename to
addx_extend_sp64, only allow when TARGET_ARCH64.
(define_insn addx_extend_sp32 and split): Version that works when
not TARGET_ARCH64.
(define_insn subx_extend): Likewise.
(define_split adddi3 and subdi3 with zero extension): Fixup and
correct bugs when not TARGET_ARCH64.
Tue Aug 11 16:04:34 1998 John Carr <jfc@mit.edu>
* except.c (set_exception_lang_code, set_exception_version_code):
Use prototype-style definition if __STDC__, to match declaration
in except.h.
* genemit.c: Change FAIL and DONE macros not to use loops.
Tue Aug 11 12:27:03 1998 Jim Wilson <wilson@cygnus.com>
* dwarf2out.c (ASM_OUTPUT_DWARF_ADDR_CONST): Use
ASM_OUTPUT_DWARF2_ADDR_CONST if defined.
* mips/mips.md (reload_outsi): Use M16_REG_P when TARGET_MIPS16.
Tue Aug 11 18:12:53 1998 Dave Love <d.love@dl.ac.uk>
* README.g77: Update from Craig.
Tue Aug 11 04:46:01 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (sparc_emit_set_const32): INTVAL is of
type HOST_WIDE_INT.
(safe_constDI sparc_emit_set_const64_quick1,
sparc_emit_set_const64_quick2, sparc_emit_set_const64_longway,
analyze_64bit_constant, const64_is_2insns,
create_simple_focus_bits): Fix some bugs when compiled on real
64-bit hosts.
(function_arg_record_value_3, function_arg_record_value_2,
function_arg_record_value): Add fully prototyped forward decls.
* config/sparc/sparc.md (define_insn cmpsi_insn_sp32): Rename back
to cmpsi_insn and use on both 64 and 32 bit targets.
(define_insn cmpsi_insn_sp64): Remove.
(define_expand zero_extendsidi2): Allow for 32-bit target too.
(define_insn zero_extendsidi2_insn): Rename to
zero_extendsidi2_insn_sp64.
(define_insn zero_extendsidi2_insn_sp32): New pattern and
associated forced split for it.
* config/sparc/sparc.c (const64_operand, const64_high_operand):
New predicates.
* config/sparc/sparc.h: Declare them.
(PREDICATE_CODES): Add them.
* config/sparc/sparc.md (movdi_lo_sum_sp64_dbl,
movdi_high_sp64_dbl, xordi3_sp64_dbl): Use them.
Mon Aug 10 22:57:24 1998 John Carr <jfc@mit.edu>
* config/sparc/sparc.md (define_insn jump): Output ba,pt not b,pt
in v9 case as the latter makes the Solaris assembler crash.
Mon Aug 10 22:39:09 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* config/sparc/sparc.c (input_operand): Do not accept a LO_SUM MEM
for TFmode when !v9. We require offsettable memory addresses.
* config/sparc/sparc.h (ALTER_HARD_SUBREG): Handle TFmode to
DFmode register number conversions.
* config/sparc/sparc.md (define_split DFmode moves): If register
is a SUBREG do alter_subreg on it before using.
(define_expand movtf): Fixup comment about alignment on v9.
(define_split TFmode moves): Don't use gen_{high,low}part, create
explicit SUBREGs instead.
Mon Aug 10 19:02:55 1998 John Carr <jfc@mit.edu>
* Makefile.in (mbchar.o): Depend on mbchar.c.
Mon Aug 10 04:28:13 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
Richard Henderson <rth@cygnus.com>
Rewrite Sparc backend for better code generation and
improved sparc64 support.
* config/sparc/sp64-elf.h: Set JUMP_TABLES_IN_TEXT_SECTION to
zero.
* config/sparc/sysv4.h: Likewise.
* config/sparc/sparc.c (v8plus_regcmp_p, sparc_operand,
move_operand, v8plus_regcmp_op, emit_move_sequence,
singlemove_string, doublemove_string, mem_aligned_8,
output_move_double, output_move_quad, output_fp_move_double,
move_quad_direction, output_fp_move_quad, output_scc_insn):
Remove.
(small_int_or_double): New predicate.
(gen_compare_reg): Remove TARGET_V8PLUS cmpdi_v8plus emission.
(legitimize_pic_address): Emit movsi_{high,lo_sum}_pic instead of
old pic_{sethi,lo_sum}_si patterns.
(mem_min_alignment): New generic function to replace
mem_aligned_8, which uses REGNO_POINTER_ALIGN information when
available and can test for arbitrary alignments. All callers
changed.
(save_regs, restore_regs, build_big_number,
output_function_prologue, output_cbranch, output_return,
sparc_flat_save_restore, sparc_flat_output_function_prologue,
sparc_flat_output_function_epilogue): Prettify
insn output.
(output_function_epilogue): Likewise and add code to output
deferred case vectors.
(output_v9branch): Likewise, add new arg INSN and use it to tack
on branch prediction settings. All callers changed.
(print_operand): Likewise and output %l44 for LO_SUMs when
TARGET_CM_MEDMID.
(sparc_splitdi_legitimate): New function to make sure DImode
splits can be run properly when !arch64.
(sparc_initialize_trampoline, sparc64_initialize_trampoline):
Reformat example code in comments.
(set_extends): Remove UNSPEC/v8plus_clear_high case.
(sparc_addr_diff_list, sparc_addr_list): New statics to keep track
of deferred case vectors we need to output.
(sparc_defer_case_vector): Record a case vector.
(sparc_output_addr_vec, sparc_output_addr_diff_vec,
sparc_output_deferred_case_vectors): New functions to output them.
(sparc_emit_set_const32): New function to form 32-bit constants in
registers when that requires more than one instruction.
(safe_constDI, sparc_emit_set_const64_quick1,
sparc_emit_set_const64_quick2, sparc_emit_set_const64_longway,
analyze_64bit_constant, const64_is_2insns,
create_simple_focus_bits, sparc_emit_set_const64): New functions
which do the same for 64-bit constants when arch64.
(sparc_emit_set_symbolic_const64): New function to emit address
loading for all code models on v9.
* config/sparc/sparc.h (CONDITIONAL_REGISTER_USAGE): Do not make
%g1 fixed when arch64, unfix %g0 when TARGET_LIVE_G0.
(ALTER_HARD_SUBREG): Fix thinko, return REGNO + 1 not 1.
(SECONDARY_INPUT_RELOAD_CLASS, SECONDARY_OUTPUT_RELOAD_CLASS): Fix
inaccuracies in comments, add symbolic and text_segment operands
when TARGET_CM_MEDANY and TARGET_CM_EMBMEDANY respectively. Use
GENERAL_REGS in these cases as a temp REG is needed to load these
addresses into a register properly.
(EXTRA_CONSTRAINT): Document more accurately, remove Q case as it
is no longer used.
(GO_IF_LEGITIMATE_ADDRESS): Allow TFmode for LO_SUM on v9 since fp
quads are guaranteed to have 16-byte alignment.
(LEGITIMIZE_ADDRESS): For SYMBOL_REF, CONST, and LABEL_REF use
copy_to_suggested_reg instead of explicit LO_SUM and HIGH.
(ASM_OUTPUT_ADDR_VEC, ASM_OUTPUT_ADDR_DIFF_VEC): New macros for
deferred case vector implementation.
(ASM_OUTPUT_ADDR_VEC_ELT): Use fputc to output newline.
(ASM_OUTPUT_ADDR_DIFF_ELT): Parenthesize LABEL in macro calls.
Generate "internal label - label" instead of "label - 1b".
(PRINT_OPERAND_ADDRESS): For LO_SUM use %l44 on TARGET_CM_MEDMID.
(PREDICATE_CODES): Remove sparc_operand, move_operand,
v8plus_regcmp_op. Add small_int_or_double, input_operand, and
zero_operand.
(doublemove_string, output_block_move, output_fp_move_double,
output_fp_move_quad, output_move_double, output_move_quad,
output_scc_insn, singlemove_string, mem_aligned_8, move_operand,
sparc_operand, v8plus_regcmp_op, v8plus_regcmp_p): Remove externs.
(sparc_emit_set_const32, sparc_emit_set_const64,
sparc_emit_set_symbolic_const64, input_operand, zero_operand,
mem_min_alignment, small_int_or_double): Add externs.
* config/sparc/sparc.md: Document the many uses of UNSPEC and
UNSPEC_VOLATILE in this backend.
(define_function_unit ieu): Rename to ieu_unnamed. Add move and
unary to types which execute in it.
(define_function_unit ieu_shift): Rename to ieu0.
(define_function_unit ieu1): New, executes compare, call, and
uncond_branch type insns.
(define_function_units for type fdivs, fdivd, fsqrt): These
execute in the fpu multiply unit not the adder on UltraSparc.
(define_expand cmpdi): Disallow TARGET_V8PLUS.
(define_insn cmpsi_insn): Rename to cmpsi_insn_sp32.
(define_insn cmpsi_insn_sp64): New, same as sp32 variant except it
allows the arith_double_operand predicate and rHI constraint when
TARGET_ARCH64.
(define_insn cmpdi_sp64, cmpsf_fpe, cmpdf_fpe, cmptf_fpe,
cmpsf_fp, cmpdf_fp, cmptf_fp, sltu_insn, neg_sltu_insn,
neg_sltu_minux_x, neg_sltu_plus_x, sgeu_insn, neg_sgeu_insn,
sltu_plus_x, sltu_plus_x, sltu_plus_x_plus_y, x_minus_sltu,
sgeu_plus_x, x_minus_sgeu, movqi_cc_sp64, movhi_cc_sp64,
movsi_cc_sp64, movdi_cc_sp64, movsf_cc_sp64, movdf_cc_sp64,
movtf_cc_sp64, movqi_cc_reg_sp64, movhi_cc_reg_sp64,
movsi_cc_reg_sp64, movdi_cc_reg_sp64, movsf_cc_reg_sp64,
movdf_cc_reg_sp64, movtf_cc_reg_sp64, zero_extendhisi2_insn,
cmp_siqi_trunc, cmp_siqi_trunc_set, sign_extendhisi2_insn,
sign_extendqihi2_insn, sign_extendqisi2_insn,
sign_extendqidi2_insn, sign_extendhidi2_insn,
extendsfdf2, extendsftf2, extenddftf2, truncdfsf2, trunctfsf2,
trunctfdf2, floatsisf2, floatsidf2, floatsitf2, floatdisf2,
floatdidf2, floatditf2, fix_truncsfsi2, fix_truncdfsi2,
fix_trunctfsi2, fix_truncsfdi2, fix_truncdfdi2, fix_trunctfdi2,
adddi3_sp64, addsi3, cmp_ccx_plus, cmp_cc_plus_set, subdi_sp64,
subsi3, cmp_minus_ccx, cmp_minus_ccx_set, mulsi3, muldi3,
muldi3_v8plus, cmp_mul_set, mulsidi3, mulsidi3_v8plus,
const_mulsidi3_v8plus, mulsidi3_sp32, const_mulsidi3,
smulsi3_highpart_v8plus, unnamed subreg mult,
const_smulsi3_highpart_v8plus, smulsi3_highpart_sp32,
const_smulsi3_highpart, umulsidi3_v8plus, umulsidi3_sp32,
const_umulsidi3, const_umulsidi3_v8plus, umulsi3_highpart_v8plus,
const_umulsi3_highpart_v8plus, umulsi3_highpart_sp32,
const_umulsi3_highpart, divsi3, divdi3, cmp_sdiv_cc_set, udivsi3,
udivdi3, cmp_udiv_cc_set, smacsi, smacdi, umacdi, anddi3_sp64,
andsi3, and_not_di_sp64, and_not_si, iordi3_sp64, iorsi3,
or_not_di_sp64, or_not_si, xordi3_sp64, xorsi3, xor_not_di_sp64,
xor_not_si, cmp_cc_arith_op, cmp_ccx_arith_op,
cmp_cc_arith_op_set, cmp_ccx_arith_op_set, cmp_ccx_xor_not,
cmp_cc_xor_not_set, cmp_ccx_xor_not_set, cmp_cc_arith_op_not,
cmp_ccx_arith_op_not, cmp_cc_arith_op_not_set,
cmp_ccx_arith_op_not_set, negdi2_sp64, cmp_cc_neg, cmp_ccx_neg,
cmp_cc_set_neg, cmp_ccx_set_neg, one_cmpldi2_sp64, cmp_cc_not,
cmp_ccx_not, cmp_cc_set_not, cmp_ccx_set_not, addtf3, adddf3,
addsf3, subtf3, subdf3, subsf3, multf3, muldf3, mulsf3,
muldf3_extend, multf3_extend, divtf3, divdf3, divsf3, negtf2,
negdf2, negsf2, abstf2, absdf2, abssf2, sqrttf2, sqrtdf2, sqrtsf2,
ashlsi3, ashldi3, unnamed DI ashift, cmp_cc_ashift_1,
cmp_cc_set_ashift_1, ashrsi3, ashrdi3, unnamed DI ashiftrt,
ashrdi3_v8plus, lshrsi3, lshrdi3, unnamed DI lshiftrt,
lshrdi3_v8plus, tablejump_sp32, tablejump_sp64, call_address_sp32,
call_symbolic_sp32, call_address_sp64, call_symbolic_sp64,
call_address_struct_value_sp32, call_symbolic_struct_value_sp32,
call_address_untyped_struct_value_sp32,
call_symbolic_untyped_struct_value_sp32, call_value_address_sp32,
call_value_symbolic_sp32, call_value_address_sp64,
call_value_symbolic_sp64, branch_sp32, branch_sp64,
flush_register_windows, goto_handler_and_restore,
goto_handler_and_restore_v9, goto_handler_and_restore_v9_sp64,
flush, all ldd/std peepholes, return_qi, return_hi, return_si,
return_addsi, return_di, return_adddi, return_sf, all call+jump
peepholes, trap, unnamed trap insns): Prettify output strings.
(define_insn anddi3_sp32, and_not_di_sp32, iordi3_sp32,
or_not_di_sp32, xordi3_sp32, xor_not_di_sp32, one_cmpldi2):
Likewise and force + implement splits for integer cases.
(define_insn return_sf_no_fpu): Likewise and allow to match when
no-fpu because of our subreg SFmode splits.
(define_insn zero_extendqihi2, zero_extendqisi2_insn,
zero_extendqidi2_insn, zero_extendhidi2_insn,
zero_extendsidi2_insn, sign_extendsidi2_insn): Likewise and use
input_operand for second operand.
(cmp_minus_cc, cmp_minus_cc_set): Likewise and use
reg_or_0_operand for operand 2 so new splits can use it.
(cmp_zero_extendqisi2, cmp_zero_extendqisi2_set, cmp_cc_plus,
cmp_cc_xor_not): Likewise and don't forget to check TARGET_LIVE_G0
too.
(cmp_zero_extract, cmp_zero_extract_sp64): Likewise and allow
CONST_DOUBLEs for operand 2.
(define_insn move_label_di): Likewise and label distance
optimization because it no longer works with new deferred case
vector scheme. To be revisited.
(define_insn x_minus_y_minus_sltu, x_minus_sltu_plus_y): Likewise
and allow reg_or_0_operand and J constraint for second operand.
(define_insn jump): Set branch predict taken on V9.
(define_insn tablejump): Emit LABEL_REF + PLUS memory address for
new deferred case vector scheme.
(define_insn pic_tablejump_32, pic_tablejump_64): Remove.
(define_insn negdi2_sp32): Force + implement splits.
(define_insn negsi2, one_cmplsi2): Rename to negsi2_not_liveg0 and
one_cmplsi2_not_liveg0 respectively, and create expander of original
names which emit special rtl for TARGET_LIVE_G0.
(define_insn cmpdi_v8plus, scc_si, scc_di): Remove.
(define_insn seq, sne, slt, sge, sle, sltu, sgeu): Don't do
gen_compare_reg, FAIL instead.
(define_insn sgtu, sleu): Likewise and check gen_s*() return
values when trying to reverse condition codes, if they FAIL then
do likewise.
(define_insn snesi_zero, neg_snesi_zero, snesi_zero_extend,
snedi_zero, neg_snedi_zero, snedi_zero_trunc, seqsi_zero,
neg_seqsi_zero, seqsi_zero_extend, seqdi_zero, neg_seqdi_zero,
seqdi_zero_trunc, x_plus_i_ne_0, x_minus_i_ne_0, x_plus_i_eq_0,
x_minus_i_eq_0): Add new splits to perform these multi-insn cases,
set output string to # to indicate they are mandatory splits.
(define_insn pic_lo_sum_si, pic_sethi_si, pic_lo_sum_di,
pic_sethi_di, move_pic_label_si): Remove.
(define_insn movsi_low_sum, movsi_high, movsi_lo_sum_pic,
movsi_high_pic, movsi_pic_label_reg): New patterns to take their
place.
(define_expand movsi_pic_label_ref, define_insn
movsi_high_pic_label_ref, movsi_lo_sum_pic_label_ref): New
expander and insns to handle PIC label references and deferred
case vectors.
(define_insn get_pc_via_rdpc): Comment out as it is no longer
used.
(define_expand movqi, movhi, movsi, movdi, movsf, movdf, movtf):
Rewrite to not use emit_move_sequence, make use of new constant
formation code, and new splits for all multi-insn cases.
(define_insn movqi_insn): Remove sethi case, it can never happen.
Use reg_or_zero_operand instead of const0_rtx explicit test,
use input_operand instead of move_operand for source, and use
general_operand now for dest.
(define_insn movhi_insn): Similar but leave sethi case.
(define_insn lo_sum_qi, store_qi, store_hi): Remove.
(define_insn sethi_hi lo_sum_hi): Rename to movhi_high and
movhi_lo_sum respectively, prettify output string.
(define_insn movsi_zero_liveg0): New pattern to put zero into a
register when needed on TARGET_LIVE_G0.
(define_insn movsi_insn): Use general_operand and input_operand
for dest and src respectively. Simplify applicability test.
Prettify output strings, and add clr alternative for J
constraint.
(define_insn movdi_sp32_v9, movdi_sp32, define_splits for
deprecated std and reg-reg DI moves): Remove and...
(define_insn movdi_insn_sp32, movdi_insn_sp64): Replace with new
implementation which uses forced splits for all non-single insn
cases.
(define_split DI move cases on !arch64): New splits to handle all
situations of 64-bit double register DImode on 32bit, and
unaligned registers and memory addresses for all subtargets.
(define_insn movsf_const_insn, movdf_const_insn, store_sf):
Remove.
(define_insn movsf_insn, movsf_no_f_insn): Use general_operand and
input_operand for dest and src respectively, prettify output
strings.
(define_insn movdf_insn, movdf_no_e_insn, store_df,
movtf_const_insn, movtf_insn, movtf_no_e_insn, store_tf): Remove
and...
(define_insn movdf_insn_sp32, movdf_no_e_insn_sp32,
movdf_insn_sp64, movdf_no_e_insn_sp64, movtf_insn,
movtf_no_e_insn_sp32, movtf_insn_hq_sp64, movtf_insn_sp64,
movtf_no_e_insn_sp64): Replace with new
implementation which uses forced splits for all non-single insn
cases.
(define_split DF move cases): New splits in similar vein to DI
move counterparts.
(define_insn sethi_di_medlow, sethi_di_medium_pic,
sethi_di_embmedany_data, sethi_di_embmedany_text, sethi_di_sp64,
movdi_sp64_insn): Remove old v9 code model and constant loading
support insns and..
(define_insn pic_lo_sum_di, pic_sethi_di,
sethi_di_medlow_embmedany_pic, sethi_di_medlow, losum_di_medlow,
seth44, setm44, setl44, sethh, setlm, sethm, setlo,
embmedany_sethi, embmedany_losum, embmedany_brsum,
embmedany_textuhi, embmedany_texthi, embmedany_textulo,
embmedany_textlo, movdi_lo_sum_sp64_cint, movdi_lo_sum_sp64_dbl,
movdi_high_sp64_cint, movdi_high_sp64_dbl): Replace with new
scheme, using unspecs, secondary reloads, and one to one sparc
insn to rtl insn mapping for better scheduling and code gen.
(define_expand reload_indi, reload_outdi): Reload helpers for
MEDANY and EMBMEDANY symbol address loading cases which require a
temporary register.
(define_expand movsicc): Remove v8plus_regcmp cases.
(define_insn movdi_cc_sp64_trunc, movdi_cc_reg_sp64_trunc,
cmp_zero_extendqidi2, cmp_zero_extendqidi2_set, cmp_qidi_trunc,
cmp_diqi_trunc_set): New patterns used by some of the new scc
splits on arch64.
(define_insn xordi3_sp64_dbl): New pattern used for constant
formation when crossing from 32-bit targets.
(define_insn movsi_cc_reg_v8plus, v8plus_clear_high, and helper
split): Remove.
(define_insn addx, subx): Make visible and prettify.
(define_insn adddi3_insn_sp32): Likewise and force split.
(define_insn addx_extend, subx_extend, unnamed): New patterns for
64bit scc split usage.
(define_insn unnamed plusDI zero_extend, unnamed minusDI
zero_extend, subdi3): Force and implement splits.
* final.c (final_scan_insn): Don't output labels if target
specifies ASM_OUTPUT_ADDR_{DIFF}_VEC. Do these macro operations
instead.
* reorg.c (dbr_schedule): When taking on BR_PRED notes at the end,
don't forget to walk inside SEQUENCESs too as these are what the
delay slot scheduler will create.
Mon Aug 10 01:21:01 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (extxl+1,+2): New patterns to work around
combine lossage.
Sat Aug 8 19:20:22 1998 Gary Thomas (gdt@linuxppc.org)
* rs6000.c (rs6000_allocate_stack_space): Fix typo which
caused bad assembly code to be generated.
Sat Aug 8 18:53:28 1998 Jeffrey A Law (law@cygnus.com)
* netbsd.h: Fix typo.
Mon Aug 3 00:06:42 1998 Robert Lipe <robertl@dgii.com>
* config.sub: Fix typo.
Sun Aug 2 22:39:08 1998 Hans-Peter Nilsson <hp@axis.se>
* invoke.texi (Environment Variables): Typo: Change "ascpects"
into "aspects".
(Running Protoize): Typo: Change "ther" into "other".
Sun Aug 2 00:42:50 1998 Jeffrey A Law (law@cygnus.com)
* i386/netbsd.h: Undo previous change to DWARF2_UNWIND_INFO.
* m68k/netbsd.h: Likewise.
* ns32k/netbsd.h: Likewise.
* sparc/netbsd.h: Likewise.
Sat Aug 1 17:59:30 1998 Richard Henderson <rth@cygnus.com>
* ginclude/va-alpha.h (va_list): Use a typedef, not a define.
* ginclude/va-clipper.h (va_list): Likewise.
Fri Jul 31 20:22:02 1998 Michael Meissner <meissner@cygnus.com>
* rs6000.c (rs6000_override_options): If big endian and -Os, use
load/store multiple instructions unless user overrides.
Fri Jul 31 17:08:59 1998 Jeffrey A Law (law@cygnus.com)
* ns32k/netbsd.h: Fix typo.
Fri Jul 31 10:23:55 1998 Doug Evans <devans@canuck.cygnus.com>
* m32r/m32r.h (ASM_OUTPUT_SOURCE_LINE): Always output line number
labels with .debugsym if no parallel insns.
Thu Jul 30 19:15:53 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (fp cmp): Replicate patterns for ALPHA_TP_INSN.
(fcmov): Remove ALPHA_TP_INSN patterns -- fcmov doesn't trap.
Thu Jul 30 19:50:15 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000/x-aix43 (AR_FOR_TARGET_FLAGS): Delete.
(AR_FOR_TARGET): Define.
Thu Jul 30 12:29:12 1998 Mark Mitchell <mark@markmitchell.com>
* dyn-string.h: New file.
* dyn-string.c: Likewise.
* Makefile.in (OBJS): Add dyn-string.o.
(dwarf2out.o): Add dyn-string.h dependency.
(dyn-string.o): List dependencies.
* dwarf2out.c: Include dyn-string.h.
(ASM_NAME_TO_STRING): Use dyn_string_append, rather than strcpy.
(addr_const_to_string): Take a dyn_string_t, not a char* as a
prototype. Use dyn_string_append rather than strcat, throughout.
(addr_to_string): Use dyn_string_t.
Thu Jul 30 13:08:07 1998 Ken Raeburn <raeburn@cygnus.com>
Function entry/exit profiling instrumentation:
* expr.h (profile_function_entry_libfunc,
profile_function_exit_libfunc): Declare new variables.
* optabs.c: Define them here.
(init_optabs): Initialize them.
* tree.h (struct tree_decl): New flag
no_instrument_function_entry_exit.
(DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT): New accessor macro.
* c-decl.c (duplicate_decls): Merge it.
* c-common.c (enum attrs): New value A_NO_INSTRUMENT_FUNCTION.
(init_attributes): Use it for "no_instrument_function".
(decl_attributes): Handle it, for functions that have not yet been
compiled. Set decl flag.
* flags.h (flag_instrument_function_entry_exit): Declare new
variable.
* toplev.c (flag_instrument_function_entry_exit): Define it here.
(f_options): New option "instrument-functions".
* function.h (struct function): New field instrument_entry_exit.
* function.c (current_function_instrument_entry_exit): New
variable.
(push_function_context_to, pop_function_context_from): Save and
restore.
(expand_function_start): Set current_ variable, maybe emit return
label and entry profile call.
(expand_function_end): Maybe emit exit profile call.
Thu Jul 30 00:58:34 1998 Jeffrey A Law (law@cygnus.com)
* i386.md (movqi): When optimizing a load of (const_int 1) into a
NON_QI_REG_P, pretend the register is SImode.
Wed Jul 29 23:49:23 1998 Todd Vierling <tv@netbsd.org>
* configure.in: Use xm-netbsd.h as the NetBSD xm file (not xm-siglist).
Accept arm32 as arm, m68k4k as m68k, mipsle as mips-dec, and any
manufacturer id for ns32k.
* configure: Regenerated.
* config/netbsd.h: When using ASM_WEAKEN_LABEL, make it global too.
* config/t-netbsd: Don't compile libgcc1-test as the fns are in libc.
* config/i386/netbsd.h: Undefine DWARF2_UNWIND_INFO, not define as 0.
* config/m68k/netbsd.h: Same.
* config/ns32k/netbsd.h: Same.
* config/sparc/netbsd.h: Same.
Wed Jul 29 22:39:21 1998 Jeffrey A Law (law@cygnus.com)
* unroll.c (unroll_loop): Do not abort for an UNROLL_MODULO
or UNROLL_COMPLETELY loop that starts with a jump to its
exit code.
Wed Jul 29 22:18:14 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000/rs6000.md (absdi2 define_split): Swap operands of MINUS.
* rs6000/rs6000.c (mask64_operand): Use HOST_BITS_PER_WIDE_INT.
(print_operand, case 'B'): Don't fall through.
(print_operand, case 'S'): Correct mask begin/end computation.
Use HOST_BITS_PER_WIDE_INT.
* rs6000/rs6000.h (CPP_PREDEFINES): Define _LONG_LONG.
(CONDITIONAL_REGISTER_USAGE): GPR13 fixed if TARGET_64BIT.
* rs6000/aix41.h (CPP_PREDEFINES): Same.
* rs6000/aix43.h (CPP_PREDEFINES): Same.
Wed Jul 29 11:47:10 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/thumb.md (extendqisi2_insn): Remove earlyclobber
constraint from second alternative.
Tue Jul 28 23:29:04 1998 Jason Merrill <jason@yorick.cygnus.com>
* configure.in: Fix --without/--disable cases for local-prefix,
gxx-include-dir and checking.
Tue Jul 28 22:01:23 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* configure.in (enable_haifa): Set by default for sparc64 too.
configure: Rebuilt.
Tue Jul 28 23:29:04 1998 Jason Merrill <jason@yorick.cygnus.com>
* i386/cygwin32.h (VALID_MACHINE_TYPE_ATTRIBUTE): New macro.
* i386/winnt.c (associated_type): New fn.
(i386_pe_valid_type_attribute_p): New fn.
(i386_pe_check_vtable_importexport): Remove.
(i386_pe_dllexport_p): Use associated_type.
(i386_pe_dllimport_p): Likewise.
From Antonio M. O. Neto <anmendes@cruzeironet.com.br>:
* i386.c (i386_valid_type_attribute_p): Also accept
attributes for METHOD_TYPEs.
Tue Jul 28 23:17:39 1998 Peter Gerwinski <peter@gerwinski.de>
* tree.c (build_range_type): Copy TYPE_SIZE_UNIT.
Tue Jul 28 22:31:12 1998 Craig Burley <burley@gnu.org>
* gcc.c: Fix commentary describing %g, %u, %U, and %O.
* gcc.c (do_spec_1): Fix handling of %g%O and %U%O to prevent
them from generating a new base name for each occurrence of
a specific suffix.
1998-07-28 Vladimir N. Makarov <vmakarov@cygnus.com>
* cse.c (cse_insn): Enable substitution inside libcall only for REG,
SUBREG, MEM.
* rtlanal.c (replace_rtx): Prohibit replaces in CONST_DOUBLE.
* cplus-dem.c (type_kind_t): New type.
(demangle_template_value_parm): Add type_kind_t parameter. Rely
on this parameter, rather than demangling the type again.
(demangle_integral_value): Pass tk_integral.
(demangle_template_: Pass the value returned from do_type.
(do_type): Return a type_kind_t. Pass tk_integral to
demangle_template_value_parm for array bounds.
(demangle_fund_type): Likewise.
Mon Jul 27 00:54:41 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (simple_cst_equal, case CONSTRUCTOR): OK if the elts are
identical.
Mon Jul 27 22:18:36 1998 Jeffrey A Law (law@cygnus.com)
* pa.c (move_operand): Accept CONSTANT_P_RTX.
Mon Jul 27 17:18:52 1998 Dave Brolley <brolley@cygnus.com>
* stor-layout.c (layout_type): Handle arrays of bits, for Chill.
* expr.c (get_inner_reference): Handle zero-based, unsigned, array
index conversion.
Mon Jul 27 14:51:33 1998 Jeffrey A Law (law@cygnus.com)
* mn10300.h (DEBUGGER_AUTO_OFFSET): Define.
(DEBUGGER_ARG_OFFSET): Likewise.
* mn10300.md (movsf): Remove last change. Not needed.
Mon Jul 27 14:22:36 1998 Dave Brolley <brolley@cygnus.com>
* c-lex.c (yylex): Fix boundary conditions in character literal and
string literal loops.
Mon Jul 27 11:43:54 1998 Stan Cox <scox@cygnus.com>
* longlong.h (count_leading_zeros): Sparclite scan instruction was
being invoked incorrectly.
* i386.c (ix86_prologue): Added SUBTARGET_PROLOGUE invocation.
* i386/cygwin32.h (STARTFILE_SPEC, LIB_SPEC, SUBTARGET_PROLOGUE):
Add -pg support.
* i386/win32.h: New file. Hybrid mingw32.h/cygwin32.h configuration.
* configure.in: Added i[34567]86-*-win32.
* config.sub: Likewise.
* configure: Rebuilt.
Sun Jul 26 01:11:12 1998 H.J. Lu (hjl@gnu.org)
* i386.h (CONST_DOUBLE_OK_FOR_LETTER_P): Return 0 when eliminating
the frame pointer and compiling PIC code and reload has not completed.
* i386.c (output_to_reg): Add code to emulate non-popping DImode
case.
Sun Jul 26 01:01:32 1998 Jeffrey A Law (law@cygnus.com)
* regmove.c (regmove_optimize): Fix typo initializing regmove_bb_head.
Sat Jul 25 23:29:23 1998 Gerald Pfeifer <pfeifer@dbai.tuwien.ac.at>
* Makefile.in (install-info): Only try to update the info
directory file if it exists in the first place.
Fri Jul 24 18:58:37 1998 Klaus Espenlaub <kespenla@student.informatik.uni-ulm.de>
* rs6000.h (ASM_OUTPUT_CONSTRUCTOR, ASM_OUTPUT_DESTRUCTOR): Delete.
Fri Jul 24 14:20:26 1998 Jeffrey A Law (law@cygnus.com)
* mn10300.md (movqi, movhi, movsi, movsf): Correctly handle
CONST_DOUBLE source.
Fri Jul 24 11:17:04 1998 Nick Clifton <nickc@cygnus.com>
* config/arm/thumb.c (thumb_print_operand): Decode %_ in asm
strings as the insertion of USER_LABEL_PREFIX.
* config/arm/thumb.h (PRINT_OPERAND_PUNCT_VALID_P): Accept _ as a
valid code.
* config/arm/thumb.md: Use %_ as a prefix to gcc library function
calls.
Thu Jul 23 18:53:20 1998 Jim Wilson <wilson@cygnus.com>
* dbxout.c (dbxout_range_type): Only call dbxout_type_index for
already defined type.
Thu Jul 23 13:49:41 1998 Jeffrey A Law (law@cygnus.com)
* expr.c (check_max_integer_computation_mode): Allow conversions
of constant integers to MAX_INTEGER_COMPUTATION_MODE.
(expand_expr): Likewise.
Thu Jul 23 11:12:06 1998 Alexandre Petit-Bianco <apbianco@cygnus.com>
* expr.c (expand_expr): Expand RETURN_EXPR.
Thu Jul 23 11:00:29 1998 Jim Wilson <wilson@cygnus.com>
* dwarf2out.c (dwarf2out_finish): Call stripattributes on TEXT_SECTION.
Wed Jul 22 19:10:00 1998 Catherine Moore <clm@cygnus.com>
* dwarf2out.c (output_aranges): Call stripattributes
for TEXT_SECTION references.
(output_line_info): Likewise.
Wed Jul 22 14:08:54 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* profile.c (branch_prob): Call allocate_reg_info after outputting
profile rtl in instrument_arcs.
Wed Jul 22 12:47:49 1998 Jim Wilson <wilson@cygnus.com>
* fixinc.irix (math.h): Install wrapper instead of copying.
Wed Jul 22 12:37:14 1998 Alexandre Petit-Bianco <apbianco@cygnus.com>
* tree.def (EXPR_WITH_FILE_LOCATION): Defined as an 'e' expression
so WFL are expanded correctly when contained in a COMPOUND_EXPR.
* tree.h (EXPR_WFL_EMIT_LINE_NOTE): Change macro not to use
lang_flag_0. Added documentation in the flag table.
Tue Jul 21 23:28:35 1998 Klaus Kaempf <kkaempf@rmi.de>
* cccp.c (do_include): Fix vax c style include handling.
Tue Jul 21 13:28:19 1998 Jason Merrill <jason@yorick.cygnus.com>
* cplus-dem.c (do_type): Use demangle_template_value_parm for arrays.
Sun Jul 12 01:27:05 1998 Jason Merrill <jason@yorick.cygnus.com>
* fold-const.c (non_lvalue): Don't deal with null pointer
constants here.
(fold, case COMPOUND_EXPR): Wrap a constant 0 in a NOP_EXPR.
Tue Jul 21 15:49:31 1998 David Edelsohn <edelsohn@gnu.org>
* rs6000.h (PREDICATE_CODES): Add CONSTANT_P_RTX.
* rs6000.md (movsi, movdi): Add CONSTANT_P_RTX.
* rs6000.c (short_cint_operand): Add CONSTANT_P_RTX.
(u_short_cint_operand): Same.
(reg_or_cint_operand): Same.
(logical_operand): Same.
(input_operand): Same.
(reg_or_short_operand): Use u_short_cint_operand.
Tue Jul 21 08:56:42 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (fix_truncdfsi2, fix_truncsfsi2): Remove the define_expands,
but keep the insns and splits. Adjust so when the ultimate destination
is memory, use cvtql.
Tue Jul 21 08:55:09 1998 Richard Henderson <rth@cygnus.com>
* flow.c (regno_uninitialized): Fixed regs are never uninitialized.
Tue Jul 21 00:31:01 1998 Jeffrey A Law (law@cygnus.com)
* gcc.c (do_spec): Call "error" not "warning".
* configure.in: Fix minor problems with gas feature detection code.
* configure: Rebuilt.
* gcc.c (do_spec): Issue a warning for '%[]' usage.
* Undo this change.
* gcc.c: Delete %[spec] support.
(do_spec_1, case '('): Likewise.
(do_spec_1, case '['): Call error.
Mon Jul 20 22:34:17 1998 Richard Henderson <rth@cygnus.com>
* alpha.h (CPP_SPEC): Tidy. Hook to cpp_cpu and cpp_subtarget.
(CPP_SUBTARGET_SPEC): Default to empty string.
(CPP_AM_*, CPP_IM_*, CPP_CPU_*, CPP_CPU_SPEC): New.
(EXTRA_SPECS, SUBTARGET_EXTRA_SPECS): New.
* alpha/elf.h (LD_SPEC): Use %(elf_dynamic_linker).
* alpha/linux-elf.h (SUBTARGET_EXTRA_SPECS): New.
(LIB_SPEC): Tidy.
* alpha/linux.h (CPP_PREDEFINES): Tidy.
* alpha/netbsd-elf.h (SUBTARGET_EXTRA_SPECS): New.
* alpha/netbsd.h (CPP_PREDEFINES): Tidy.
* alpha/osf.h (CPP_PREDEFINES): Remove bits subsumed by CPP_CPU_SPEC.
* alpha/win-nt.h (CPP_PREDEFINES): Likewise.
* alpha/vsf.h (CPP_PREDEFINES): Likewise.
(CPP_SUBTARGET_SPEC): New. Do this instead of overriding CPP_SPEC.
* alpha/vxworks.h: Likewise.
Mon Jul 20 22:51:57 1998 Ken Raeburn <raeburn@cygnus.com>
* mips.md (reload_outsi): Added missing REGNO call.
(smulsi3_highpart, umulsi3_highpart): Provide prototype for
function pointer.
(mul_acc_di, mul_acc_64bit_di): Don't use match_op_dup, use
another match_operator and compare the codes.
* mips.h (MASK_DEBUG_E, MASK_DEBUG_I): Set to zero.
* MIPS multiply pattern fixes:
* mips.h (enum reg_class, REG_CLASS_NAMES, REG_CLASS_CONTENTS):
Add union classes for HI, LO, or HILO plus general registers.
(GENERATE_MADD): Deleted.
* mips.md (mulsi3_mult3): Don't disparage output-LO alternative.
Add TARGET_MAD to condition.
(mulsi3): Test HAVE_mulsi3_mult3, not specific flags.
(mul_acc_si): Expand GENERATE_MADD here; it's the only use. Use
"*d" for accumulator, to give preference to LO initially but not
during reload.
Mon Jul 20 16:16:38 1998 Dave Brolley <brolley@cygnus.com>
* configure.in (enable_c_mbchar): New configure option.
(extra_cpp_objs): Always available now.
* cexp.y (mbchar.h): #include it.
(yylex): Handle Multibyte characters in character literals.
* cccp.c (mbchar.h): #include it.
(main): Set character set based on LANG environment variable.
(rescan): Handle multibyte characters in comments.
(skip_if_group): See above.
(validate_else): See above.
(skip_to_end_of_comment): See above.
(macarg1): See above.
(discard_comments): See above.
(rescan): Handle multibyte characters in string and character literals.
(collect_expansion): See above.
(skip_quoted_string): See above.
(macroexpand): See above.
(macarg1): See above.
(discard_comments): See above.
(change_newlines): See above.
* c-lex.c (mbchar.h): #include it.
(GET_ENVIRONMENT): New macro.
(init_lex): Set character set based on LANG environment variable.
(yylex): Handle multibyte characters in character literals.
(yylex): Handle multibyte characters in string literals.
* Makefile.in (mbchar.o): New target.
(cccp$(exeext)): @extra_cpp_objs@ is always available.
(cppmain$(exeext)): @extra_cpp_objs@ is always available.
* mbchar.[ch]: New files for multibyte character handling.
Mon Jul 20 01:11:11 1998 David S. Miller <davem@pierdol.cobaltmicro.com>
* jump.c (jump_optimize): When simplifying noop moves and
PUSH_ROUNDING, fix thinko so we use same criterion for identifying
the PUSHes to rewrite in second loop as we did in the first.
Sun Jul 19 08:23:53 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cplus-dem.c (demangle_nested_args): Make function definition
static to match the prototype.
Fri Jul 17 14:58:44 1998 Richard Henderson <rth@cygnus.com>
* alloca.c: Respect USE_C_ALLOCA.
* gencheck.c (xmalloc): Ignore __GNUC__ for definition.
* gengenrtl.c (xmalloc): Likewise.
Fri Jul 17 14:18:14 1998 Richard Henderson <rth@cygnus.com>
* loop.h (struct induction): Add no_const_addval.
* loop.c (the_movables, reg_address_cost): New variables.
(init_loop): Init reg_address_cost.
(loop_optimize): Call end_alias_analysis.
(scan_loop): Init the_movables.
(record_giv): Init induction->no_const_addval.
(basic_induction_var) [PLUS]: Use rtx_equal_p instead of ==.
[REG]: Rearrange loop search test to catch more cases.
(general_induction_var): Return success not benefit; take an extra
argument for that. Change all callers.
(simplify_giv_expr) [PLUS]: Always combine invariants. Use sge_plus.
[MULT]: Use rtx_equal_p instead of ==. Combine simple invariants.
[default]: Search the_movables for additional combinations.
(sge_plus_constant, sge_plus): New functions.
(express_from_1): New function.
(express_from): Always define. Rewrite using express_from_1.
(combine_givs_p): Handle more cases. Ignore address cost.
(cmp_combine_givs_stats): New function.
(combine_givs_used_once, combine_givs_benefit_from): New functions.
(combine_givs): Rewrite to do best-fit combination.
* fold-const.c (operand_equal_p): Handle RTL_EXPR.
(fold): Do a complete (A*C)+(B*C) association check.
Fri Jul 17 11:21:55 1998 Jim Wilson <wilson@cygnus.com>
* function.c (fixup_var_refs_insns): Handle CLOBBER of a CONCAT.
Fri Jul 17 11:48:55 1998 Jeffrey A Law (law@cygnus.com)
* mn10300.c (MODES_TIEABLE_P): Fix typo.
Fri Jul 17 03:26:12 1998 Rihcard Earnshaw (rearnsha@arm.com)
* tree.c (valid_machine_attribute): Only create a new type variant if
there is a decl to use it.
Thu Jul 16 14:48:04 1998 Nick Clifton <nickc@cygnus.com>
* gcc.c (do_spec_1): Cope with %g/%u/%U options which do not have
a suffix.
Fri Jul 17 03:24:40 1998 Hans-Peter Nilsson <hp@axis.se>
* extend.texi (Explicit Reg Vars): Typo: change "may deleted" into "may
be deleted"
Thu Jul 16 14:48:47 1998 Jeffrey A Law (law@cygnus.com)
* mn10300.c (count_tst_insns): New arg oreg_countp. Callers changed.
Simplify tests for clearing an address register.
(expand_prologue): Corresponding changes.
* mn10300.md (movXX patterns): Make sure the destination is an
ADDRESS_REG when substituting "zero_areg" for (const_int 0).
(logical patterns): Split into expanders + patterns.
(zero and sign extension patterns): Similarly.
(shift patterns): Similarly.
Thu Jul 16 01:17:44 1998 Richard Henderson <rth@cygnus.com>
* loop.c (emit_iv_add_mult): Scan the entire insn list generated
for the sequence, recording base values.
Wed Jul 15 10:49:55 1998 Richard Henderson <rth@cygnus.com>
* i386.h (CPP_CPU_SPEC): Remove -Asystem(unix).
Tue Jul 14 14:15:30 1998 Nick Clifton <nickc@cygnus.com>
* gcc.c: Remove ANSI-C ism from --help code.
* toplev.c: Support --help with USE_CPPLIB.
Tue Jul 14 14:46:08 1998 Jeffrey A Law (law@cygnus.com)
* configure.in: Rework gas feature code to work with symlink based
source trees.
* extend.texi: Clarify some issues related to local variables
assigned to explicit registers.
* mn10300.md (mulsi): Turn into expander + pattern.
* mn10300.md (movsi, movsf, movdi, movdf): Remove "x" from I -> a
alternative.
Tue Jul 14 07:41:59 1998 Richard Earnshaw (rearnsha@arm.com)
* arm/tcoff.h (USER_LABEL_PREFIX): Make it empty to match coff.h.
Tue Jul 14 03:02:44 1998 Jeffrey A Law (law@cygnus.com)
* version.c: Bump again to distinguish mainline tree from the
egcs-1.1 branch.
See ChangeLog.0 for earlier changes.
Local Variables:
add-log-time-format: current-time-string
End:
Index: head/contrib/gcc/c-lang.c
===================================================================
--- head/contrib/gcc/c-lang.c (revision 52750)
+++ head/contrib/gcc/c-lang.c (revision 52751)
@@ -1,213 +1,217 @@
/* Language-specific hook definitions for C front end.
Copyright (C) 1991, 1995, 1997, 1998 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. */
#include "config.h"
#include "system.h"
#include "tree.h"
#include "input.h"
#include "c-tree.h"
#include "c-lex.h"
#include "toplev.h"
#include "output.h"
#if USE_CPPLIB
#include "cpplib.h"
extern char *yy_cur;
extern cpp_reader parse_in;
extern cpp_options parse_options;
#endif
/* Each of the functions defined here
is an alternative to a function in objc-actions.c. */
int
lang_decode_option (argc, argv)
int argc;
char **argv;
{
return c_decode_option (argc, argv);
}
void
lang_init_options ()
{
#if USE_CPPLIB
cpp_reader_init (&parse_in);
parse_in.opts = &parse_options;
cpp_options_init (&parse_options);
#endif
}
void
lang_init ()
{
/* the beginning of the file is a new line; check for # */
/* With luck, we discover the real source file's name from that
and put it in input_filename. */
#if !USE_CPPLIB
ungetc (check_newline (), finput);
#else
check_newline ();
yy_cur--;
#endif
}
void
lang_finish ()
{
}
char *
lang_identify ()
{
return "c";
}
void
print_lang_statistics ()
{
}
/* used by print-tree.c */
void
lang_print_xnode (file, node, indent)
FILE *file ATTRIBUTE_UNUSED;
tree node ATTRIBUTE_UNUSED;
int indent ATTRIBUTE_UNUSED;
{
}
/* Used by c-lex.c, but only for objc. */
tree
lookup_interface (arg)
tree arg ATTRIBUTE_UNUSED;
{
return 0;
}
tree
is_class_name (arg)
tree arg ATTRIBUTE_UNUSED;
{
return 0;
}
void
maybe_objc_check_decl (decl)
tree decl ATTRIBUTE_UNUSED;
{
}
int
maybe_objc_comptypes (lhs, rhs, reflexive)
tree lhs ATTRIBUTE_UNUSED;
tree rhs ATTRIBUTE_UNUSED;
int reflexive ATTRIBUTE_UNUSED;
{
return -1;
}
tree
maybe_objc_method_name (decl)
tree decl ATTRIBUTE_UNUSED;
{
return 0;
}
tree
maybe_building_objc_message_expr ()
{
return 0;
}
int
recognize_objc_keyword ()
{
return 0;
}
tree
build_objc_string (len, str)
int len ATTRIBUTE_UNUSED;
const char *str ATTRIBUTE_UNUSED;
{
abort ();
return NULL_TREE;
}
/* Called at end of parsing, but before end-of-file processing. */
void
finish_file ()
{
#ifndef ASM_OUTPUT_CONSTRUCTOR
extern tree static_ctors;
#endif
#ifndef ASM_OUTPUT_DESTRUCTOR
extern tree static_dtors;
#endif
extern tree build_function_call PROTO((tree, tree));
#if !defined(ASM_OUTPUT_CONSTRUCTOR) || !defined(ASM_OUTPUT_DESTRUCTOR)
tree void_list_node = build_tree_list (NULL_TREE, void_type_node);
#endif
#ifndef ASM_OUTPUT_CONSTRUCTOR
if (static_ctors)
{
tree fnname = get_file_function_name ('I');
start_function (void_list_node,
- build_parse_node (CALL_EXPR, fnname, void_list_node,
+ build_parse_node (CALL_EXPR, fnname,
+ tree_cons (NULL_TREE, NULL_TREE,
+ void_list_node),
NULL_TREE),
NULL_TREE, NULL_TREE, 0);
fnname = DECL_ASSEMBLER_NAME (current_function_decl);
store_parm_decls ();
for (; static_ctors; static_ctors = TREE_CHAIN (static_ctors))
expand_expr_stmt (build_function_call (TREE_VALUE (static_ctors),
NULL_TREE));
finish_function (0);
assemble_constructor (IDENTIFIER_POINTER (fnname));
}
#endif
#ifndef ASM_OUTPUT_DESTRUCTOR
if (static_dtors)
{
tree fnname = get_file_function_name ('D');
start_function (void_list_node,
- build_parse_node (CALL_EXPR, fnname, void_list_node,
+ build_parse_node (CALL_EXPR, fnname,
+ tree_cons (NULL_TREE, NULL_TREE,
+ void_list_node),
NULL_TREE),
NULL_TREE, NULL_TREE, 0);
fnname = DECL_ASSEMBLER_NAME (current_function_decl);
store_parm_decls ();
for (; static_dtors; static_dtors = TREE_CHAIN (static_dtors))
expand_expr_stmt (build_function_call (TREE_VALUE (static_dtors),
NULL_TREE));
finish_function (0);
assemble_destructor (IDENTIFIER_POINTER (fnname));
}
#endif
}
Index: head/contrib/gcc/c-typeck.c
===================================================================
--- head/contrib/gcc/c-typeck.c (revision 52750)
+++ head/contrib/gcc/c-typeck.c (revision 52751)
@@ -1,6947 +1,6948 @@
/* Build expressions with type checking for C compiler.
Copyright (C) 1987, 88, 91-97, 1998 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 is part of the C front end.
It contains routines to build C expressions given their operands,
including computing the types of the result, C-specific error checks,
and some optimization.
There are also routines to build RETURN_STMT nodes and CASE_STMT nodes,
and to process initializations in declarations (since they work
like a strange sort of assignment). */
#include "config.h"
#include "system.h"
#include "tree.h"
#include "c-tree.h"
#include "flags.h"
#include "output.h"
#include "rtl.h"
#include "expr.h"
#include "toplev.h"
#include "intl.h"
/* Nonzero if we've already printed a "missing braces around initializer"
message within this initializer. */
static int missing_braces_mentioned;
static tree qualify_type PROTO((tree, tree));
static int comp_target_types PROTO((tree, tree));
static int function_types_compatible_p PROTO((tree, tree));
static int type_lists_compatible_p PROTO((tree, tree));
static int self_promoting_type_p PROTO((tree));
static tree decl_constant_value PROTO((tree));
static tree lookup_field PROTO((tree, tree, tree *));
static tree convert_arguments PROTO((tree, tree, tree, tree));
static tree pointer_int_sum PROTO((enum tree_code, tree, tree));
static tree pointer_diff PROTO((tree, tree));
static tree unary_complex_lvalue PROTO((enum tree_code, tree));
static void pedantic_lvalue_warning PROTO((enum tree_code));
static tree internal_build_compound_expr PROTO((tree, int));
static tree convert_for_assignment PROTO((tree, tree, const char *, tree,
tree, int));
static void warn_for_assignment PROTO((const char *, const char *,
tree, int));
static tree valid_compound_expr_initializer PROTO((tree, tree));
static void push_string PROTO((const char *));
static void push_member_name PROTO((tree));
static void push_array_bounds PROTO((int));
static int spelling_length PROTO((void));
static char *print_spelling PROTO((char *));
static void warning_init PROTO((const char *));
static tree digest_init PROTO((tree, tree, int, int));
static void check_init_type_bitfields PROTO((tree));
static void output_init_element PROTO((tree, tree, tree, int));
static void output_pending_init_elements PROTO((int));
static void add_pending_init PROTO((tree, tree));
static int pending_init_member PROTO((tree));
/* Do `exp = require_complete_type (exp);' to make sure exp
does not have an incomplete type. (That includes void types.) */
tree
require_complete_type (value)
tree value;
{
tree type = TREE_TYPE (value);
if (TREE_CODE (value) == ERROR_MARK)
return error_mark_node;
/* First, detect a valid value with a complete type. */
if (TYPE_SIZE (type) != 0
&& type != void_type_node)
return value;
incomplete_type_error (value, type);
return error_mark_node;
}
/* Print an error message for invalid use of an incomplete type.
VALUE is the expression that was used (or 0 if that isn't known)
and TYPE is the type that was invalid. */
void
incomplete_type_error (value, type)
tree value;
tree type;
{
const char *type_code_string;
/* Avoid duplicate error message. */
if (TREE_CODE (type) == ERROR_MARK)
return;
if (value != 0 && (TREE_CODE (value) == VAR_DECL
|| TREE_CODE (value) == PARM_DECL))
error ("`%s' has an incomplete type",
IDENTIFIER_POINTER (DECL_NAME (value)));
else
{
retry:
/* We must print an error message. Be clever about what it says. */
switch (TREE_CODE (type))
{
case RECORD_TYPE:
type_code_string = "struct";
break;
case UNION_TYPE:
type_code_string = "union";
break;
case ENUMERAL_TYPE:
type_code_string = "enum";
break;
case VOID_TYPE:
error ("invalid use of void expression");
return;
case ARRAY_TYPE:
if (TYPE_DOMAIN (type))
{
type = TREE_TYPE (type);
goto retry;
}
error ("invalid use of array with unspecified bounds");
return;
default:
abort ();
}
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
error ("invalid use of undefined type `%s %s'",
type_code_string, IDENTIFIER_POINTER (TYPE_NAME (type)));
else
/* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */
error ("invalid use of incomplete typedef `%s'",
IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
}
}
/* Return a variant of TYPE which has all the type qualifiers of LIKE
as well as those of TYPE. */
static tree
qualify_type (type, like)
tree type, like;
{
- return c_build_qualified_type (type, TYPE_QUALS (like));
+ return c_build_qualified_type (type,
+ TYPE_QUALS (type) | TYPE_QUALS (like));
}
/* Return the common type of two types.
We assume that comptypes has already been done and returned 1;
if that isn't so, this may crash. In particular, we assume that qualifiers
match.
This is the type for the result of most arithmetic operations
if the operands have the given two types. */
tree
common_type (t1, t2)
tree t1, t2;
{
register enum tree_code code1;
register enum tree_code code2;
tree attributes;
/* Save time if the two types are the same. */
if (t1 == t2) return t1;
/* If one type is nonsense, use the other. */
if (t1 == error_mark_node)
return t2;
if (t2 == error_mark_node)
return t1;
/* Merge the attributes. */
attributes = merge_machine_type_attributes (t1, t2);
/* Treat an enum type as the unsigned integer type of the same width. */
if (TREE_CODE (t1) == ENUMERAL_TYPE)
t1 = type_for_size (TYPE_PRECISION (t1), 1);
if (TREE_CODE (t2) == ENUMERAL_TYPE)
t2 = type_for_size (TYPE_PRECISION (t2), 1);
code1 = TREE_CODE (t1);
code2 = TREE_CODE (t2);
/* If one type is complex, form the common type of the non-complex
components, then make that complex. Use T1 or T2 if it is the
required type. */
if (code1 == COMPLEX_TYPE || code2 == COMPLEX_TYPE)
{
tree subtype1 = code1 == COMPLEX_TYPE ? TREE_TYPE (t1) : t1;
tree subtype2 = code2 == COMPLEX_TYPE ? TREE_TYPE (t2) : t2;
tree subtype = common_type (subtype1, subtype2);
if (code1 == COMPLEX_TYPE && TREE_TYPE (t1) == subtype)
return build_type_attribute_variant (t1, attributes);
else if (code2 == COMPLEX_TYPE && TREE_TYPE (t2) == subtype)
return build_type_attribute_variant (t2, attributes);
else
return build_type_attribute_variant (build_complex_type (subtype),
attributes);
}
switch (code1)
{
case INTEGER_TYPE:
case REAL_TYPE:
/* If only one is real, use it as the result. */
if (code1 == REAL_TYPE && code2 != REAL_TYPE)
return build_type_attribute_variant (t1, attributes);
if (code2 == REAL_TYPE && code1 != REAL_TYPE)
return build_type_attribute_variant (t2, attributes);
/* Both real or both integers; use the one with greater precision. */
if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2))
return build_type_attribute_variant (t1, attributes);
else if (TYPE_PRECISION (t2) > TYPE_PRECISION (t1))
return build_type_attribute_variant (t2, attributes);
/* Same precision. Prefer longs to ints even when same size. */
if (TYPE_MAIN_VARIANT (t1) == long_unsigned_type_node
|| TYPE_MAIN_VARIANT (t2) == long_unsigned_type_node)
return build_type_attribute_variant (long_unsigned_type_node,
attributes);
if (TYPE_MAIN_VARIANT (t1) == long_integer_type_node
|| TYPE_MAIN_VARIANT (t2) == long_integer_type_node)
{
/* But preserve unsignedness from the other type,
since long cannot hold all the values of an unsigned int. */
if (TREE_UNSIGNED (t1) || TREE_UNSIGNED (t2))
t1 = long_unsigned_type_node;
else
t1 = long_integer_type_node;
return build_type_attribute_variant (t1, attributes);
}
/* Likewise, prefer long double to double even if same size. */
if (TYPE_MAIN_VARIANT (t1) == long_double_type_node
|| TYPE_MAIN_VARIANT (t2) == long_double_type_node)
return build_type_attribute_variant (long_double_type_node,
attributes);
/* Otherwise prefer the unsigned one. */
if (TREE_UNSIGNED (t1))
return build_type_attribute_variant (t1, attributes);
else
return build_type_attribute_variant (t2, attributes);
case POINTER_TYPE:
/* For two pointers, do this recursively on the target type,
and combine the qualifiers of the two types' targets. */
/* This code was turned off; I don't know why.
But ANSI C specifies doing this with the qualifiers.
So I turned it on again. */
{
tree pointed_to_1 = TREE_TYPE (t1);
tree pointed_to_2 = TREE_TYPE (t2);
tree target = common_type (TYPE_MAIN_VARIANT (pointed_to_1),
TYPE_MAIN_VARIANT (pointed_to_2));
t1 = build_pointer_type (c_build_qualified_type
(target,
TYPE_QUALS (pointed_to_1) |
TYPE_QUALS (pointed_to_2)));
return build_type_attribute_variant (t1, attributes);
}
#if 0
t1 = build_pointer_type (common_type (TREE_TYPE (t1), TREE_TYPE (t2)));
return build_type_attribute_variant (t1, attributes);
#endif
case ARRAY_TYPE:
{
tree elt = common_type (TREE_TYPE (t1), TREE_TYPE (t2));
/* Save space: see if the result is identical to one of the args. */
if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1))
return build_type_attribute_variant (t1, attributes);
if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2))
return build_type_attribute_variant (t2, attributes);
/* Merge the element types, and have a size if either arg has one. */
t1 = build_array_type (elt, TYPE_DOMAIN (TYPE_DOMAIN (t1) ? t1 : t2));
return build_type_attribute_variant (t1, attributes);
}
case FUNCTION_TYPE:
/* Function types: prefer the one that specified arg types.
If both do, merge the arg types. Also merge the return types. */
{
tree valtype = common_type (TREE_TYPE (t1), TREE_TYPE (t2));
tree p1 = TYPE_ARG_TYPES (t1);
tree p2 = TYPE_ARG_TYPES (t2);
int len;
tree newargs, n;
int i;
/* Save space: see if the result is identical to one of the args. */
if (valtype == TREE_TYPE (t1) && ! TYPE_ARG_TYPES (t2))
return build_type_attribute_variant (t1, attributes);
if (valtype == TREE_TYPE (t2) && ! TYPE_ARG_TYPES (t1))
return build_type_attribute_variant (t2, attributes);
/* Simple way if one arg fails to specify argument types. */
if (TYPE_ARG_TYPES (t1) == 0)
{
t1 = build_function_type (valtype, TYPE_ARG_TYPES (t2));
return build_type_attribute_variant (t1, attributes);
}
if (TYPE_ARG_TYPES (t2) == 0)
{
t1 = build_function_type (valtype, TYPE_ARG_TYPES (t1));
return build_type_attribute_variant (t1, attributes);
}
/* If both args specify argument types, we must merge the two
lists, argument by argument. */
len = list_length (p1);
newargs = 0;
for (i = 0; i < len; i++)
newargs = tree_cons (NULL_TREE, NULL_TREE, newargs);
n = newargs;
for (; p1;
p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n))
{
/* A null type means arg type is not specified.
Take whatever the other function type has. */
if (TREE_VALUE (p1) == 0)
{
TREE_VALUE (n) = TREE_VALUE (p2);
goto parm_done;
}
if (TREE_VALUE (p2) == 0)
{
TREE_VALUE (n) = TREE_VALUE (p1);
goto parm_done;
}
/* Given wait (union {union wait *u; int *i} *)
and wait (union wait *),
prefer union wait * as type of parm. */
if (TREE_CODE (TREE_VALUE (p1)) == UNION_TYPE
&& TREE_VALUE (p1) != TREE_VALUE (p2))
{
tree memb;
for (memb = TYPE_FIELDS (TREE_VALUE (p1));
memb; memb = TREE_CHAIN (memb))
if (comptypes (TREE_TYPE (memb), TREE_VALUE (p2)))
{
TREE_VALUE (n) = TREE_VALUE (p2);
if (pedantic)
pedwarn ("function types not truly compatible in ANSI C");
goto parm_done;
}
}
if (TREE_CODE (TREE_VALUE (p2)) == UNION_TYPE
&& TREE_VALUE (p2) != TREE_VALUE (p1))
{
tree memb;
for (memb = TYPE_FIELDS (TREE_VALUE (p2));
memb; memb = TREE_CHAIN (memb))
if (comptypes (TREE_TYPE (memb), TREE_VALUE (p1)))
{
TREE_VALUE (n) = TREE_VALUE (p1);
if (pedantic)
pedwarn ("function types not truly compatible in ANSI C");
goto parm_done;
}
}
TREE_VALUE (n) = common_type (TREE_VALUE (p1), TREE_VALUE (p2));
parm_done: ;
}
t1 = build_function_type (valtype, newargs);
/* ... falls through ... */
}
default:
return build_type_attribute_variant (t1, attributes);
}
}
/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
or various other operations. Return 2 if they are compatible
but a warning may be needed if you use them together. */
int
comptypes (type1, type2)
tree type1, type2;
{
register tree t1 = type1;
register tree t2 = type2;
int attrval, val;
/* Suppress errors caused by previously reported errors. */
if (t1 == t2 || !t1 || !t2
|| TREE_CODE (t1) == ERROR_MARK || TREE_CODE (t2) == ERROR_MARK)
return 1;
/* Treat an enum type as the integer type of the same width and
signedness. */
if (TREE_CODE (t1) == ENUMERAL_TYPE)
t1 = type_for_size (TYPE_PRECISION (t1), TREE_UNSIGNED (t1));
if (TREE_CODE (t2) == ENUMERAL_TYPE)
t2 = type_for_size (TYPE_PRECISION (t2), TREE_UNSIGNED (t2));
if (t1 == t2)
return 1;
/* Different classes of types can't be compatible. */
if (TREE_CODE (t1) != TREE_CODE (t2)) return 0;
/* Qualifiers must match. */
if (TYPE_QUALS (t1) != TYPE_QUALS (t2))
return 0;
/* Allow for two different type nodes which have essentially the same
definition. Note that we already checked for equality of the type
qualifiers (just above). */
if (TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
return 1;
#ifndef COMP_TYPE_ATTRIBUTES
#define COMP_TYPE_ATTRIBUTES(t1,t2) 1
#endif
/* 1 if no need for warning yet, 2 if warning cause has been seen. */
if (! (attrval = COMP_TYPE_ATTRIBUTES (t1, t2)))
return 0;
/* 1 if no need for warning yet, 2 if warning cause has been seen. */
val = 0;
switch (TREE_CODE (t1))
{
case POINTER_TYPE:
val = (TREE_TYPE (t1) == TREE_TYPE (t2)
? 1 : comptypes (TREE_TYPE (t1), TREE_TYPE (t2)));
break;
case FUNCTION_TYPE:
val = function_types_compatible_p (t1, t2);
break;
case ARRAY_TYPE:
{
tree d1 = TYPE_DOMAIN (t1);
tree d2 = TYPE_DOMAIN (t2);
val = 1;
/* Target types must match incl. qualifiers. */
if (TREE_TYPE (t1) != TREE_TYPE (t2)
&& 0 == (val = comptypes (TREE_TYPE (t1), TREE_TYPE (t2))))
return 0;
/* Sizes must match unless one is missing or variable. */
if (d1 == 0 || d2 == 0 || d1 == d2
|| TREE_CODE (TYPE_MIN_VALUE (d1)) != INTEGER_CST
|| TREE_CODE (TYPE_MIN_VALUE (d2)) != INTEGER_CST
|| TREE_CODE (TYPE_MAX_VALUE (d1)) != INTEGER_CST
|| TREE_CODE (TYPE_MAX_VALUE (d2)) != INTEGER_CST)
break;
if (! ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (d1))
== TREE_INT_CST_LOW (TYPE_MIN_VALUE (d2)))
&& (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d1))
== TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d2)))
&& (TREE_INT_CST_LOW (TYPE_MAX_VALUE (d1))
== TREE_INT_CST_LOW (TYPE_MAX_VALUE (d2)))
&& (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d1))
== TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d2)))))
val = 0;
break;
}
case RECORD_TYPE:
if (maybe_objc_comptypes (t1, t2, 0) == 1)
val = 1;
break;
default:
break;
}
return attrval == 2 && val == 1 ? 2 : val;
}
/* Return 1 if TTL and TTR are pointers to types that are equivalent,
ignoring their qualifiers. */
static int
comp_target_types (ttl, ttr)
tree ttl, ttr;
{
int val;
/* Give maybe_objc_comptypes a crack at letting these types through. */
if ((val = maybe_objc_comptypes (ttl, ttr, 1)) >= 0)
return val;
val = comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (ttl)),
TYPE_MAIN_VARIANT (TREE_TYPE (ttr)));
if (val == 2 && pedantic)
pedwarn ("types are not quite compatible");
return val;
}
/* Subroutines of `comptypes'. */
/* Return 1 if two function types F1 and F2 are compatible.
If either type specifies no argument types,
the other must specify a fixed number of self-promoting arg types.
Otherwise, if one type specifies only the number of arguments,
the other must specify that number of self-promoting arg types.
Otherwise, the argument types must match. */
static int
function_types_compatible_p (f1, f2)
tree f1, f2;
{
tree args1, args2;
/* 1 if no need for warning yet, 2 if warning cause has been seen. */
int val = 1;
int val1;
if (!(TREE_TYPE (f1) == TREE_TYPE (f2)
|| (val = comptypes (TREE_TYPE (f1), TREE_TYPE (f2)))))
return 0;
args1 = TYPE_ARG_TYPES (f1);
args2 = TYPE_ARG_TYPES (f2);
/* An unspecified parmlist matches any specified parmlist
whose argument types don't need default promotions. */
if (args1 == 0)
{
if (!self_promoting_args_p (args2))
return 0;
/* If one of these types comes from a non-prototype fn definition,
compare that with the other type's arglist.
If they don't match, ask for a warning (but no error). */
if (TYPE_ACTUAL_ARG_TYPES (f1)
&& 1 != type_lists_compatible_p (args2, TYPE_ACTUAL_ARG_TYPES (f1)))
val = 2;
return val;
}
if (args2 == 0)
{
if (!self_promoting_args_p (args1))
return 0;
if (TYPE_ACTUAL_ARG_TYPES (f2)
&& 1 != type_lists_compatible_p (args1, TYPE_ACTUAL_ARG_TYPES (f2)))
val = 2;
return val;
}
/* Both types have argument lists: compare them and propagate results. */
val1 = type_lists_compatible_p (args1, args2);
return val1 != 1 ? val1 : val;
}
/* Check two lists of types for compatibility,
returning 0 for incompatible, 1 for compatible,
or 2 for compatible with warning. */
static int
type_lists_compatible_p (args1, args2)
tree args1, args2;
{
/* 1 if no need for warning yet, 2 if warning cause has been seen. */
int val = 1;
int newval = 0;
while (1)
{
if (args1 == 0 && args2 == 0)
return val;
/* If one list is shorter than the other,
they fail to match. */
if (args1 == 0 || args2 == 0)
return 0;
/* A null pointer instead of a type
means there is supposed to be an argument
but nothing is specified about what type it has.
So match anything that self-promotes. */
if (TREE_VALUE (args1) == 0)
{
if (! self_promoting_type_p (TREE_VALUE (args2)))
return 0;
}
else if (TREE_VALUE (args2) == 0)
{
if (! self_promoting_type_p (TREE_VALUE (args1)))
return 0;
}
else if (! (newval = comptypes (TREE_VALUE (args1), TREE_VALUE (args2))))
{
/* Allow wait (union {union wait *u; int *i} *)
and wait (union wait *) to be compatible. */
if (TREE_CODE (TREE_VALUE (args1)) == UNION_TYPE
&& (TYPE_NAME (TREE_VALUE (args1)) == 0
|| TYPE_TRANSPARENT_UNION (TREE_VALUE (args1)))
&& TREE_CODE (TYPE_SIZE (TREE_VALUE (args1))) == INTEGER_CST
&& tree_int_cst_equal (TYPE_SIZE (TREE_VALUE (args1)),
TYPE_SIZE (TREE_VALUE (args2))))
{
tree memb;
for (memb = TYPE_FIELDS (TREE_VALUE (args1));
memb; memb = TREE_CHAIN (memb))
if (comptypes (TREE_TYPE (memb), TREE_VALUE (args2)))
break;
if (memb == 0)
return 0;
}
else if (TREE_CODE (TREE_VALUE (args2)) == UNION_TYPE
&& (TYPE_NAME (TREE_VALUE (args2)) == 0
|| TYPE_TRANSPARENT_UNION (TREE_VALUE (args2)))
&& TREE_CODE (TYPE_SIZE (TREE_VALUE (args2))) == INTEGER_CST
&& tree_int_cst_equal (TYPE_SIZE (TREE_VALUE (args2)),
TYPE_SIZE (TREE_VALUE (args1))))
{
tree memb;
for (memb = TYPE_FIELDS (TREE_VALUE (args2));
memb; memb = TREE_CHAIN (memb))
if (comptypes (TREE_TYPE (memb), TREE_VALUE (args1)))
break;
if (memb == 0)
return 0;
}
else
return 0;
}
/* comptypes said ok, but record if it said to warn. */
if (newval > val)
val = newval;
args1 = TREE_CHAIN (args1);
args2 = TREE_CHAIN (args2);
}
}
/* Return 1 if PARMS specifies a fixed number of parameters
and none of their types is affected by default promotions. */
int
self_promoting_args_p (parms)
tree parms;
{
register tree t;
for (t = parms; t; t = TREE_CHAIN (t))
{
register tree type = TREE_VALUE (t);
if (TREE_CHAIN (t) == 0 && type != void_type_node)
return 0;
if (type == 0)
return 0;
if (TYPE_MAIN_VARIANT (type) == float_type_node)
return 0;
if (C_PROMOTING_INTEGER_TYPE_P (type))
return 0;
}
return 1;
}
/* Return 1 if TYPE is not affected by default promotions. */
static int
self_promoting_type_p (type)
tree type;
{
if (TYPE_MAIN_VARIANT (type) == float_type_node)
return 0;
if (C_PROMOTING_INTEGER_TYPE_P (type))
return 0;
return 1;
}
/* Return an unsigned type the same as TYPE in other respects. */
tree
unsigned_type (type)
tree type;
{
tree type1 = TYPE_MAIN_VARIANT (type);
if (type1 == signed_char_type_node || type1 == char_type_node)
return unsigned_char_type_node;
if (type1 == integer_type_node)
return unsigned_type_node;
if (type1 == short_integer_type_node)
return short_unsigned_type_node;
if (type1 == long_integer_type_node)
return long_unsigned_type_node;
if (type1 == long_long_integer_type_node)
return long_long_unsigned_type_node;
if (type1 == intDI_type_node)
return unsigned_intDI_type_node;
if (type1 == intSI_type_node)
return unsigned_intSI_type_node;
if (type1 == intHI_type_node)
return unsigned_intHI_type_node;
if (type1 == intQI_type_node)
return unsigned_intQI_type_node;
return signed_or_unsigned_type (1, type);
}
/* Return a signed type the same as TYPE in other respects. */
tree
signed_type (type)
tree type;
{
tree type1 = TYPE_MAIN_VARIANT (type);
if (type1 == unsigned_char_type_node || type1 == char_type_node)
return signed_char_type_node;
if (type1 == unsigned_type_node)
return integer_type_node;
if (type1 == short_unsigned_type_node)
return short_integer_type_node;
if (type1 == long_unsigned_type_node)
return long_integer_type_node;
if (type1 == long_long_unsigned_type_node)
return long_long_integer_type_node;
if (type1 == unsigned_intDI_type_node)
return intDI_type_node;
if (type1 == unsigned_intSI_type_node)
return intSI_type_node;
if (type1 == unsigned_intHI_type_node)
return intHI_type_node;
if (type1 == unsigned_intQI_type_node)
return intQI_type_node;
return signed_or_unsigned_type (0, type);
}
/* Return a type the same as TYPE except unsigned or
signed according to UNSIGNEDP. */
tree
signed_or_unsigned_type (unsignedp, type)
int unsignedp;
tree type;
{
if ((! INTEGRAL_TYPE_P (type) && ! POINTER_TYPE_P (type))
|| TREE_UNSIGNED (type) == unsignedp)
return type;
if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node))
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node))
return unsignedp ? unsigned_type_node : integer_type_node;
if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node))
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node))
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node))
return (unsignedp ? long_long_unsigned_type_node
: long_long_integer_type_node);
return type;
}
/* Compute the value of the `sizeof' operator. */
tree
c_sizeof (type)
tree type;
{
enum tree_code code = TREE_CODE (type);
tree t;
if (code == FUNCTION_TYPE)
{
if (pedantic || warn_pointer_arith)
pedwarn ("sizeof applied to a function type");
return size_int (1);
}
if (code == VOID_TYPE)
{
if (pedantic || warn_pointer_arith)
pedwarn ("sizeof applied to a void type");
return size_int (1);
}
if (code == ERROR_MARK)
return size_int (1);
if (TYPE_SIZE (type) == 0)
{
error ("sizeof applied to an incomplete type");
return size_int (0);
}
/* Convert in case a char is more than one unit. */
t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
size_int (TYPE_PRECISION (char_type_node)));
t = convert (sizetype, t);
/* size_binop does not put the constant in range, so do it now. */
if (TREE_CODE (t) == INTEGER_CST && force_fit_type (t, 0))
TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) = 1;
return t;
}
tree
c_sizeof_nowarn (type)
tree type;
{
enum tree_code code = TREE_CODE (type);
tree t;
if (code == FUNCTION_TYPE
|| code == VOID_TYPE
|| code == ERROR_MARK)
return size_int (1);
if (TYPE_SIZE (type) == 0)
return size_int (0);
/* Convert in case a char is more than one unit. */
t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
size_int (TYPE_PRECISION (char_type_node)));
t = convert (sizetype, t);
force_fit_type (t, 0);
return t;
}
/* Compute the size to increment a pointer by. */
tree
c_size_in_bytes (type)
tree type;
{
enum tree_code code = TREE_CODE (type);
tree t;
if (code == FUNCTION_TYPE)
return size_int (1);
if (code == VOID_TYPE)
return size_int (1);
if (code == ERROR_MARK)
return size_int (1);
if (TYPE_SIZE (type) == 0)
{
error ("arithmetic on pointer to an incomplete type");
return size_int (1);
}
/* Convert in case a char is more than one unit. */
t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
size_int (BITS_PER_UNIT));
t = convert (sizetype, t);
force_fit_type (t, 0);
return t;
}
/* Implement the __alignof keyword: Return the minimum required
alignment of TYPE, measured in bytes. */
tree
c_alignof (type)
tree type;
{
enum tree_code code = TREE_CODE (type);
if (code == FUNCTION_TYPE)
return size_int (FUNCTION_BOUNDARY / BITS_PER_UNIT);
if (code == VOID_TYPE || code == ERROR_MARK)
return size_int (1);
return size_int (TYPE_ALIGN (type) / BITS_PER_UNIT);
}
/* Implement the __alignof keyword: Return the minimum required
alignment of EXPR, measured in bytes. For VAR_DECL's and
FIELD_DECL's return DECL_ALIGN (which can be set from an
"aligned" __attribute__ specification). */
tree
c_alignof_expr (expr)
tree expr;
{
if (TREE_CODE (expr) == VAR_DECL)
return size_int (DECL_ALIGN (expr) / BITS_PER_UNIT);
if (TREE_CODE (expr) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND (expr, 1)))
{
error ("`__alignof' applied to a bit-field");
return size_int (1);
}
else if (TREE_CODE (expr) == COMPONENT_REF
&& TREE_CODE (TREE_OPERAND (expr, 1)) == FIELD_DECL)
return size_int (DECL_ALIGN (TREE_OPERAND (expr, 1)) / BITS_PER_UNIT);
if (TREE_CODE (expr) == INDIRECT_REF)
{
tree t = TREE_OPERAND (expr, 0);
tree best = t;
int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
while (TREE_CODE (t) == NOP_EXPR
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
{
int thisalign;
t = TREE_OPERAND (t, 0);
thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
if (thisalign > bestalign)
best = t, bestalign = thisalign;
}
return c_alignof (TREE_TYPE (TREE_TYPE (best)));
}
else
return c_alignof (TREE_TYPE (expr));
}
/* Return either DECL or its known constant value (if it has one). */
static tree
decl_constant_value (decl)
tree decl;
{
if (/* Don't change a variable array bound or initial value to a constant
in a place where a variable is invalid. */
current_function_decl != 0
&& ! pedantic
&& ! TREE_THIS_VOLATILE (decl)
&& TREE_READONLY (decl) && ! ITERATOR_P (decl)
&& DECL_INITIAL (decl) != 0
&& TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK
/* This is invalid if initial value is not constant.
If it has either a function call, a memory reference,
or a variable, then re-evaluating it could give different results. */
&& TREE_CONSTANT (DECL_INITIAL (decl))
/* Check for cases where this is sub-optimal, even though valid. */
&& TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR
&& DECL_MODE (decl) != BLKmode)
return DECL_INITIAL (decl);
return decl;
}
/* Perform default promotions for C data used in expressions.
Arrays and functions are converted to pointers;
enumeral types or short or char, to int.
In addition, manifest constants symbols are replaced by their values. */
tree
default_conversion (exp)
tree exp;
{
register tree type = TREE_TYPE (exp);
register enum tree_code code = TREE_CODE (type);
/* Constants can be used directly unless they're not loadable. */
if (TREE_CODE (exp) == CONST_DECL)
exp = DECL_INITIAL (exp);
/* Replace a nonvolatile const static variable with its value unless
it is an array, in which case we must be sure that taking the
address of the array produces consistent results. */
else if (optimize && TREE_CODE (exp) == VAR_DECL && code != ARRAY_TYPE)
{
exp = decl_constant_value (exp);
type = TREE_TYPE (exp);
}
/* Strip NON_LVALUE_EXPRs and no-op conversions, since we aren't using as
an lvalue. */
/* Do not use STRIP_NOPS here! It will remove conversions from pointer
to integer and cause infinite recursion. */
while (TREE_CODE (exp) == NON_LVALUE_EXPR
|| (TREE_CODE (exp) == NOP_EXPR
&& TREE_TYPE (TREE_OPERAND (exp, 0)) == TREE_TYPE (exp)))
exp = TREE_OPERAND (exp, 0);
/* Normally convert enums to int,
but convert wide enums to something wider. */
if (code == ENUMERAL_TYPE)
{
type = type_for_size (MAX (TYPE_PRECISION (type),
TYPE_PRECISION (integer_type_node)),
((flag_traditional
|| (TYPE_PRECISION (type)
>= TYPE_PRECISION (integer_type_node)))
&& TREE_UNSIGNED (type)));
return convert (type, exp);
}
if (TREE_CODE (exp) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND (exp, 1)))
{
tree width = DECL_SIZE (TREE_OPERAND (exp, 1));
HOST_WIDE_INT low = TREE_INT_CST_LOW (width);
/* If it's thinner than an int, promote it like a
C_PROMOTING_INTEGER_TYPE_P, otherwise leave it alone. */
if (low < TYPE_PRECISION (integer_type_node))
{
if (flag_traditional && TREE_UNSIGNED (type))
return convert (unsigned_type_node, exp);
else
return convert (integer_type_node, exp);
}
}
if (C_PROMOTING_INTEGER_TYPE_P (type))
{
/* Traditionally, unsignedness is preserved in default promotions.
Also preserve unsignedness if not really getting any wider. */
if (TREE_UNSIGNED (type)
&& (flag_traditional
|| TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)))
return convert (unsigned_type_node, exp);
return convert (integer_type_node, exp);
}
if (flag_traditional && !flag_allow_single_precision
&& TYPE_MAIN_VARIANT (type) == float_type_node)
return convert (double_type_node, exp);
if (code == VOID_TYPE)
{
error ("void value not ignored as it ought to be");
return error_mark_node;
}
if (code == FUNCTION_TYPE)
{
return build_unary_op (ADDR_EXPR, exp, 0);
}
if (code == ARRAY_TYPE)
{
register tree adr;
tree restype = TREE_TYPE (type);
tree ptrtype;
int constp = 0;
int volatilep = 0;
if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'r'
|| TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
{
constp = TREE_READONLY (exp);
volatilep = TREE_THIS_VOLATILE (exp);
}
if (TYPE_QUALS (type) || constp || volatilep)
restype
= c_build_qualified_type (restype,
TYPE_QUALS (type)
| (constp * TYPE_QUAL_CONST)
| (volatilep * TYPE_QUAL_VOLATILE));
if (TREE_CODE (exp) == INDIRECT_REF)
return convert (TYPE_POINTER_TO (restype),
TREE_OPERAND (exp, 0));
if (TREE_CODE (exp) == COMPOUND_EXPR)
{
tree op1 = default_conversion (TREE_OPERAND (exp, 1));
return build (COMPOUND_EXPR, TREE_TYPE (op1),
TREE_OPERAND (exp, 0), op1);
}
if (! lvalue_p (exp)
&& ! (TREE_CODE (exp) == CONSTRUCTOR && TREE_STATIC (exp)))
{
error ("invalid use of non-lvalue array");
return error_mark_node;
}
ptrtype = build_pointer_type (restype);
if (TREE_CODE (exp) == VAR_DECL)
{
/* ??? This is not really quite correct
in that the type of the operand of ADDR_EXPR
is not the target type of the type of the ADDR_EXPR itself.
Question is, can this lossage be avoided? */
adr = build1 (ADDR_EXPR, ptrtype, exp);
if (mark_addressable (exp) == 0)
return error_mark_node;
TREE_CONSTANT (adr) = staticp (exp);
TREE_SIDE_EFFECTS (adr) = 0; /* Default would be, same as EXP. */
return adr;
}
/* This way is better for a COMPONENT_REF since it can
simplify the offset for a component. */
adr = build_unary_op (ADDR_EXPR, exp, 1);
return convert (ptrtype, adr);
}
return exp;
}
/* Look up component name in the structure type definition.
If this component name is found indirectly within an anonymous union,
store in *INDIRECT the component which directly contains
that anonymous union. Otherwise, set *INDIRECT to 0. */
static tree
lookup_field (type, component, indirect)
tree type, component;
tree *indirect;
{
tree field;
/* If TYPE_LANG_SPECIFIC is set, then it is a sorted array of pointers
to the field elements. Use a binary search on this array to quickly
find the element. Otherwise, do a linear search. TYPE_LANG_SPECIFIC
will always be set for structures which have many elements. */
if (TYPE_LANG_SPECIFIC (type))
{
int bot, top, half;
tree *field_array = &TYPE_LANG_SPECIFIC (type)->elts[0];
field = TYPE_FIELDS (type);
bot = 0;
top = TYPE_LANG_SPECIFIC (type)->len;
while (top - bot > 1)
{
half = (top - bot + 1) >> 1;
field = field_array[bot+half];
if (DECL_NAME (field) == NULL_TREE)
{
/* Step through all anon unions in linear fashion. */
while (DECL_NAME (field_array[bot]) == NULL_TREE)
{
tree anon = 0, junk;
field = field_array[bot++];
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE
|| TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
anon = lookup_field (TREE_TYPE (field), component, &junk);
if (anon != NULL_TREE)
{
*indirect = field;
return anon;
}
}
/* Entire record is only anon unions. */
if (bot > top)
return NULL_TREE;
/* Restart the binary search, with new lower bound. */
continue;
}
if (DECL_NAME (field) == component)
break;
if (DECL_NAME (field) < component)
bot += half;
else
top = bot + half;
}
if (DECL_NAME (field_array[bot]) == component)
field = field_array[bot];
else if (DECL_NAME (field) != component)
field = 0;
}
else
{
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (DECL_NAME (field) == NULL_TREE)
{
tree junk;
tree anon = 0;
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE
|| TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
anon = lookup_field (TREE_TYPE (field), component, &junk);
if (anon != NULL_TREE)
{
*indirect = field;
return anon;
}
}
if (DECL_NAME (field) == component)
break;
}
}
*indirect = NULL_TREE;
return field;
}
/* Make an expression to refer to the COMPONENT field of
structure or union value DATUM. COMPONENT is an IDENTIFIER_NODE. */
tree
build_component_ref (datum, component)
tree datum, component;
{
register tree type = TREE_TYPE (datum);
register enum tree_code code = TREE_CODE (type);
register tree field = NULL;
register tree ref;
/* If DATUM is a COMPOUND_EXPR or COND_EXPR, move our reference inside it
unless we are not to support things not strictly ANSI. */
switch (TREE_CODE (datum))
{
case COMPOUND_EXPR:
{
tree value = build_component_ref (TREE_OPERAND (datum, 1), component);
return build (COMPOUND_EXPR, TREE_TYPE (value),
TREE_OPERAND (datum, 0), value);
}
case COND_EXPR:
return build_conditional_expr
(TREE_OPERAND (datum, 0),
build_component_ref (TREE_OPERAND (datum, 1), component),
build_component_ref (TREE_OPERAND (datum, 2), component));
default:
break;
}
/* See if there is a field or component with name COMPONENT. */
if (code == RECORD_TYPE || code == UNION_TYPE)
{
tree indirect = 0;
if (TYPE_SIZE (type) == 0)
{
incomplete_type_error (NULL_TREE, type);
return error_mark_node;
}
field = lookup_field (type, component, &indirect);
if (!field)
{
error (code == RECORD_TYPE
? "structure has no member named `%s'"
: "union has no member named `%s'",
IDENTIFIER_POINTER (component));
return error_mark_node;
}
if (TREE_TYPE (field) == error_mark_node)
return error_mark_node;
/* If FIELD was found buried within an anonymous union,
make one COMPONENT_REF to get that anonymous union,
then fall thru to make a second COMPONENT_REF to get FIELD. */
if (indirect != 0)
{
ref = build (COMPONENT_REF, TREE_TYPE (indirect), datum, indirect);
if (TREE_READONLY (datum) || TREE_READONLY (indirect))
TREE_READONLY (ref) = 1;
if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (indirect))
TREE_THIS_VOLATILE (ref) = 1;
datum = ref;
}
ref = build (COMPONENT_REF, TREE_TYPE (field), datum, field);
if (TREE_READONLY (datum) || TREE_READONLY (field))
TREE_READONLY (ref) = 1;
if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field))
TREE_THIS_VOLATILE (ref) = 1;
return ref;
}
else if (code != ERROR_MARK)
error ("request for member `%s' in something not a structure or union",
IDENTIFIER_POINTER (component));
return error_mark_node;
}
/* Given an expression PTR for a pointer, return an expression
for the value pointed to.
ERRORSTRING is the name of the operator to appear in error messages. */
tree
build_indirect_ref (ptr, errorstring)
tree ptr;
const char *errorstring;
{
register tree pointer = default_conversion (ptr);
register tree type = TREE_TYPE (pointer);
if (TREE_CODE (type) == POINTER_TYPE)
{
if (TREE_CODE (pointer) == ADDR_EXPR
&& !flag_volatile
&& (TREE_TYPE (TREE_OPERAND (pointer, 0))
== TREE_TYPE (type)))
return TREE_OPERAND (pointer, 0);
else
{
tree t = TREE_TYPE (type);
register tree ref = build1 (INDIRECT_REF,
TYPE_MAIN_VARIANT (t), pointer);
if (TYPE_SIZE (t) == 0 && TREE_CODE (t) != ARRAY_TYPE)
{
error ("dereferencing pointer to incomplete type");
return error_mark_node;
}
if (TREE_CODE (t) == VOID_TYPE && skip_evaluation == 0)
warning ("dereferencing `void *' pointer");
/* We *must* set TREE_READONLY when dereferencing a pointer to const,
so that we get the proper error message if the result is used
to assign to. Also, &* is supposed to be a no-op.
And ANSI C seems to specify that the type of the result
should be the const type. */
/* A de-reference of a pointer to const is not a const. It is valid
to change it via some other pointer. */
TREE_READONLY (ref) = TYPE_READONLY (t);
TREE_SIDE_EFFECTS (ref)
= TYPE_VOLATILE (t) || TREE_SIDE_EFFECTS (pointer) || flag_volatile;
TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t);
return ref;
}
}
else if (TREE_CODE (pointer) != ERROR_MARK)
error ("invalid type argument of `%s'", errorstring);
return error_mark_node;
}
/* This handles expressions of the form "a[i]", which denotes
an array reference.
This is logically equivalent in C to *(a+i), but we may do it differently.
If A is a variable or a member, we generate a primitive ARRAY_REF.
This avoids forcing the array out of registers, and can work on
arrays that are not lvalues (for example, members of structures returned
by functions). */
tree
build_array_ref (array, index)
tree array, index;
{
if (index == 0)
{
error ("subscript missing in array reference");
return error_mark_node;
}
if (TREE_TYPE (array) == error_mark_node
|| TREE_TYPE (index) == error_mark_node)
return error_mark_node;
if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE
&& TREE_CODE (array) != INDIRECT_REF)
{
tree rval, type;
/* Subscripting with type char is likely to lose
on a machine where chars are signed.
So warn on any machine, but optionally.
Don't warn for unsigned char since that type is safe.
Don't warn for signed char because anyone who uses that
must have done so deliberately. */
if (warn_char_subscripts
&& TYPE_MAIN_VARIANT (TREE_TYPE (index)) == char_type_node)
warning ("array subscript has type `char'");
/* Apply default promotions *after* noticing character types. */
index = default_conversion (index);
/* Require integer *after* promotion, for sake of enums. */
if (TREE_CODE (TREE_TYPE (index)) != INTEGER_TYPE)
{
error ("array subscript is not an integer");
return error_mark_node;
}
/* An array that is indexed by a non-constant
cannot be stored in a register; we must be able to do
address arithmetic on its address.
Likewise an array of elements of variable size. */
if (TREE_CODE (index) != INTEGER_CST
|| (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0
&& TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) != INTEGER_CST))
{
if (mark_addressable (array) == 0)
return error_mark_node;
}
/* An array that is indexed by a constant value which is not within
the array bounds cannot be stored in a register either; because we
would get a crash in store_bit_field/extract_bit_field when trying
to access a non-existent part of the register. */
if (TREE_CODE (index) == INTEGER_CST
&& TYPE_VALUES (TREE_TYPE (array))
&& ! int_fits_type_p (index, TYPE_VALUES (TREE_TYPE (array))))
{
if (mark_addressable (array) == 0)
return error_mark_node;
}
if (pedantic && !lvalue_p (array))
{
if (DECL_REGISTER (array))
pedwarn ("ANSI C forbids subscripting `register' array");
else
pedwarn ("ANSI C forbids subscripting non-lvalue array");
}
if (pedantic)
{
tree foo = array;
while (TREE_CODE (foo) == COMPONENT_REF)
foo = TREE_OPERAND (foo, 0);
if (TREE_CODE (foo) == VAR_DECL && DECL_REGISTER (foo))
pedwarn ("ANSI C forbids subscripting non-lvalue array");
}
type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array)));
rval = build (ARRAY_REF, type, array, index);
/* Array ref is const/volatile if the array elements are
or if the array is. */
TREE_READONLY (rval)
|= (TYPE_READONLY (TREE_TYPE (TREE_TYPE (array)))
| TREE_READONLY (array));
TREE_SIDE_EFFECTS (rval)
|= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
| TREE_SIDE_EFFECTS (array));
TREE_THIS_VOLATILE (rval)
|= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
/* This was added by rms on 16 Nov 91.
It fixes vol struct foo *a; a->elts[1]
in an inline function.
Hope it doesn't break something else. */
| TREE_THIS_VOLATILE (array));
return require_complete_type (fold (rval));
}
{
tree ar = default_conversion (array);
tree ind = default_conversion (index);
/* Do the same warning check as above, but only on the part that's
syntactically the index and only if it is also semantically
the index. */
if (warn_char_subscripts
&& TREE_CODE (TREE_TYPE (index)) == INTEGER_TYPE
&& TYPE_MAIN_VARIANT (TREE_TYPE (index)) == char_type_node)
warning ("subscript has type `char'");
/* Put the integer in IND to simplify error checking. */
if (TREE_CODE (TREE_TYPE (ar)) == INTEGER_TYPE)
{
tree temp = ar;
ar = ind;
ind = temp;
}
if (ar == error_mark_node)
return ar;
if (TREE_CODE (TREE_TYPE (ar)) != POINTER_TYPE
|| TREE_CODE (TREE_TYPE (TREE_TYPE (ar))) == FUNCTION_TYPE)
{
error ("subscripted value is neither array nor pointer");
return error_mark_node;
}
if (TREE_CODE (TREE_TYPE (ind)) != INTEGER_TYPE)
{
error ("array subscript is not an integer");
return error_mark_node;
}
return build_indirect_ref (build_binary_op (PLUS_EXPR, ar, ind, 0),
"array indexing");
}
}
/* Build a function call to function FUNCTION with parameters PARAMS.
PARAMS is a list--a chain of TREE_LIST nodes--in which the
TREE_VALUE of each node is a parameter-expression.
FUNCTION's data type may be a function type or a pointer-to-function. */
tree
build_function_call (function, params)
tree function, params;
{
register tree fntype, fundecl = 0;
register tree coerced_params;
tree name = NULL_TREE, assembler_name = NULL_TREE;
/* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue. */
STRIP_TYPE_NOPS (function);
/* Convert anything with function type to a pointer-to-function. */
if (TREE_CODE (function) == FUNCTION_DECL)
{
name = DECL_NAME (function);
assembler_name = DECL_ASSEMBLER_NAME (function);
/* Differs from default_conversion by not setting TREE_ADDRESSABLE
(because calling an inline function does not mean the function
needs to be separately compiled). */
fntype = build_type_variant (TREE_TYPE (function),
TREE_READONLY (function),
TREE_THIS_VOLATILE (function));
fundecl = function;
function = build1 (ADDR_EXPR, build_pointer_type (fntype), function);
}
else
function = default_conversion (function);
fntype = TREE_TYPE (function);
if (TREE_CODE (fntype) == ERROR_MARK)
return error_mark_node;
if (!(TREE_CODE (fntype) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE))
{
error ("called object is not a function");
return error_mark_node;
}
/* fntype now gets the type of function pointed to. */
fntype = TREE_TYPE (fntype);
/* Convert the parameters to the types declared in the
function prototype, or apply default promotions. */
coerced_params
= convert_arguments (TYPE_ARG_TYPES (fntype), params, name, fundecl);
/* Check for errors in format strings. */
if (warn_format && (name || assembler_name))
check_function_format (name, assembler_name, coerced_params);
/* Recognize certain built-in functions so we can make tree-codes
other than CALL_EXPR. We do this when it enables fold-const.c
to do something useful. */
if (TREE_CODE (function) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL
&& DECL_BUILT_IN (TREE_OPERAND (function, 0)))
switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0)))
{
case BUILT_IN_ABS:
case BUILT_IN_LABS:
case BUILT_IN_FABS:
if (coerced_params == 0)
return integer_zero_node;
return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0);
default:
break;
}
{
register tree result
= build (CALL_EXPR, TREE_TYPE (fntype),
function, coerced_params, NULL_TREE);
TREE_SIDE_EFFECTS (result) = 1;
if (TREE_TYPE (result) == void_type_node)
return result;
return require_complete_type (result);
}
}
/* Convert the argument expressions in the list VALUES
to the types in the list TYPELIST. The result is a list of converted
argument expressions.
If TYPELIST is exhausted, or when an element has NULL as its type,
perform the default conversions.
PARMLIST is the chain of parm decls for the function being called.
It may be 0, if that info is not available.
It is used only for generating error messages.
NAME is an IDENTIFIER_NODE or 0. It is used only for error messages.
This is also where warnings about wrong number of args are generated.
Both VALUES and the returned value are chains of TREE_LIST nodes
with the elements of the list in the TREE_VALUE slots of those nodes. */
static tree
convert_arguments (typelist, values, name, fundecl)
tree typelist, values, name, fundecl;
{
register tree typetail, valtail;
register tree result = NULL;
int parmnum;
/* Scan the given expressions and types, producing individual
converted arguments and pushing them on RESULT in reverse order. */
for (valtail = values, typetail = typelist, parmnum = 0;
valtail;
valtail = TREE_CHAIN (valtail), parmnum++)
{
register tree type = typetail ? TREE_VALUE (typetail) : 0;
register tree val = TREE_VALUE (valtail);
if (type == void_type_node)
{
if (name)
error ("too many arguments to function `%s'",
IDENTIFIER_POINTER (name));
else
error ("too many arguments to function");
break;
}
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
/* Do not use STRIP_NOPS here! We do not want an enumerator with value 0
to convert automatically to a pointer. */
if (TREE_CODE (val) == NON_LVALUE_EXPR)
val = TREE_OPERAND (val, 0);
if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE)
val = default_conversion (val);
val = require_complete_type (val);
if (type != 0)
{
/* Formal parm type is specified by a function prototype. */
tree parmval;
if (TYPE_SIZE (type) == 0)
{
error ("type of formal parameter %d is incomplete", parmnum + 1);
parmval = val;
}
else
{
/* Optionally warn about conversions that
differ from the default conversions. */
if (warn_conversion)
{
int formal_prec = TYPE_PRECISION (type);
if (INTEGRAL_TYPE_P (type)
&& TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
warn_for_assignment ("%s as integer rather than floating due to prototype", (char *) 0, name, parmnum + 1);
else if (TREE_CODE (type) == COMPLEX_TYPE
&& TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
warn_for_assignment ("%s as complex rather than floating due to prototype", (char *) 0, name, parmnum + 1);
else if (TREE_CODE (type) == REAL_TYPE
&& INTEGRAL_TYPE_P (TREE_TYPE (val)))
warn_for_assignment ("%s as floating rather than integer due to prototype", (char *) 0, name, parmnum + 1);
else if (TREE_CODE (type) == REAL_TYPE
&& TREE_CODE (TREE_TYPE (val)) == COMPLEX_TYPE)
warn_for_assignment ("%s as floating rather than complex due to prototype", (char *) 0, name, parmnum + 1);
/* ??? At some point, messages should be written about
conversions between complex types, but that's too messy
to do now. */
else if (TREE_CODE (type) == REAL_TYPE
&& TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
{
/* Warn if any argument is passed as `float',
since without a prototype it would be `double'. */
if (formal_prec == TYPE_PRECISION (float_type_node))
warn_for_assignment ("%s as `float' rather than `double' due to prototype", (char *) 0, name, parmnum + 1);
}
/* Detect integer changing in width or signedness. */
else if (INTEGRAL_TYPE_P (type)
&& INTEGRAL_TYPE_P (TREE_TYPE (val)))
{
tree would_have_been = default_conversion (val);
tree type1 = TREE_TYPE (would_have_been);
if (TREE_CODE (type) == ENUMERAL_TYPE
&& type == TREE_TYPE (val))
/* No warning if function asks for enum
and the actual arg is that enum type. */
;
else if (formal_prec != TYPE_PRECISION (type1))
warn_for_assignment ("%s with different width due to prototype", (char *) 0, name, parmnum + 1);
else if (TREE_UNSIGNED (type) == TREE_UNSIGNED (type1))
;
/* Don't complain if the formal parameter type
is an enum, because we can't tell now whether
the value was an enum--even the same enum. */
else if (TREE_CODE (type) == ENUMERAL_TYPE)
;
else if (TREE_CODE (val) == INTEGER_CST
&& int_fits_type_p (val, type))
/* Change in signedness doesn't matter
if a constant value is unaffected. */
;
/* Likewise for a constant in a NOP_EXPR. */
else if (TREE_CODE (val) == NOP_EXPR
&& TREE_CODE (TREE_OPERAND (val, 0)) == INTEGER_CST
&& int_fits_type_p (TREE_OPERAND (val, 0), type))
;
#if 0 /* We never get such tree structure here. */
else if (TREE_CODE (TREE_TYPE (val)) == ENUMERAL_TYPE
&& int_fits_type_p (TYPE_MIN_VALUE (TREE_TYPE (val)), type)
&& int_fits_type_p (TYPE_MAX_VALUE (TREE_TYPE (val)), type))
/* Change in signedness doesn't matter
if an enum value is unaffected. */
;
#endif
/* If the value is extended from a narrower
unsigned type, it doesn't matter whether we
pass it as signed or unsigned; the value
certainly is the same either way. */
else if (TYPE_PRECISION (TREE_TYPE (val)) < TYPE_PRECISION (type)
&& TREE_UNSIGNED (TREE_TYPE (val)))
;
else if (TREE_UNSIGNED (type))
warn_for_assignment ("%s as unsigned due to prototype", (char *) 0, name, parmnum + 1);
else
warn_for_assignment ("%s as signed due to prototype", (char *) 0, name, parmnum + 1);
}
}
parmval = convert_for_assignment (type, val,
(char *) 0, /* arg passing */
fundecl, name, parmnum + 1);
#ifdef PROMOTE_PROTOTYPES
if ((TREE_CODE (type) == INTEGER_TYPE
|| TREE_CODE (type) == ENUMERAL_TYPE)
&& (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)))
parmval = default_conversion (parmval);
#endif
}
result = tree_cons (NULL_TREE, parmval, result);
}
else if (TREE_CODE (TREE_TYPE (val)) == REAL_TYPE
&& (TYPE_PRECISION (TREE_TYPE (val))
< TYPE_PRECISION (double_type_node)))
/* Convert `float' to `double'. */
result = tree_cons (NULL_TREE, convert (double_type_node, val), result);
else
/* Convert `short' and `char' to full-size `int'. */
result = tree_cons (NULL_TREE, default_conversion (val), result);
if (typetail)
typetail = TREE_CHAIN (typetail);
}
if (typetail != 0 && TREE_VALUE (typetail) != void_type_node)
{
if (name)
error ("too few arguments to function `%s'",
IDENTIFIER_POINTER (name));
else
error ("too few arguments to function");
}
return nreverse (result);
}
/* This is the entry point used by the parser
for binary operators in the input.
In addition to constructing the expression,
we check for operands that were written with other binary operators
in a way that is likely to confuse the user. */
tree
parser_build_binary_op (code, arg1, arg2)
enum tree_code code;
tree arg1, arg2;
{
tree result = build_binary_op (code, arg1, arg2, 1);
char class;
char class1 = TREE_CODE_CLASS (TREE_CODE (arg1));
char class2 = TREE_CODE_CLASS (TREE_CODE (arg2));
enum tree_code code1 = ERROR_MARK;
enum tree_code code2 = ERROR_MARK;
if (class1 == 'e' || class1 == '1'
|| class1 == '2' || class1 == '<')
code1 = C_EXP_ORIGINAL_CODE (arg1);
if (class2 == 'e' || class2 == '1'
|| class2 == '2' || class2 == '<')
code2 = C_EXP_ORIGINAL_CODE (arg2);
/* Check for cases such as x+y<<z which users are likely
to misinterpret. If parens are used, C_EXP_ORIGINAL_CODE
is cleared to prevent these warnings. */
if (warn_parentheses)
{
if (code == LSHIFT_EXPR || code == RSHIFT_EXPR)
{
if (code1 == PLUS_EXPR || code1 == MINUS_EXPR
|| code2 == PLUS_EXPR || code2 == MINUS_EXPR)
warning ("suggest parentheses around + or - inside shift");
}
if (code == TRUTH_ORIF_EXPR)
{
if (code1 == TRUTH_ANDIF_EXPR
|| code2 == TRUTH_ANDIF_EXPR)
warning ("suggest parentheses around && within ||");
}
if (code == BIT_IOR_EXPR)
{
if (code1 == BIT_AND_EXPR || code1 == BIT_XOR_EXPR
|| code1 == PLUS_EXPR || code1 == MINUS_EXPR
|| code2 == BIT_AND_EXPR || code2 == BIT_XOR_EXPR
|| code2 == PLUS_EXPR || code2 == MINUS_EXPR)
warning ("suggest parentheses around arithmetic in operand of |");
/* Check cases like x|y==z */
if (TREE_CODE_CLASS (code1) == '<' || TREE_CODE_CLASS (code2) == '<')
warning ("suggest parentheses around comparison in operand of |");
}
if (code == BIT_XOR_EXPR)
{
if (code1 == BIT_AND_EXPR
|| code1 == PLUS_EXPR || code1 == MINUS_EXPR
|| code2 == BIT_AND_EXPR
|| code2 == PLUS_EXPR || code2 == MINUS_EXPR)
warning ("suggest parentheses around arithmetic in operand of ^");
/* Check cases like x^y==z */
if (TREE_CODE_CLASS (code1) == '<' || TREE_CODE_CLASS (code2) == '<')
warning ("suggest parentheses around comparison in operand of ^");
}
if (code == BIT_AND_EXPR)
{
if (code1 == PLUS_EXPR || code1 == MINUS_EXPR
|| code2 == PLUS_EXPR || code2 == MINUS_EXPR)
warning ("suggest parentheses around + or - in operand of &");
/* Check cases like x&y==z */
if (TREE_CODE_CLASS (code1) == '<' || TREE_CODE_CLASS (code2) == '<')
warning ("suggest parentheses around comparison in operand of &");
}
}
/* Similarly, check for cases like 1<=i<=10 that are probably errors. */
if (TREE_CODE_CLASS (code) == '<' && extra_warnings
&& (TREE_CODE_CLASS (code1) == '<' || TREE_CODE_CLASS (code2) == '<'))
warning ("comparisons like X<=Y<=Z do not have their mathematical meaning");
unsigned_conversion_warning (result, arg1);
unsigned_conversion_warning (result, arg2);
overflow_warning (result);
class = TREE_CODE_CLASS (TREE_CODE (result));
/* Record the code that was specified in the source,
for the sake of warnings about confusing nesting. */
if (class == 'e' || class == '1'
|| class == '2' || class == '<')
C_SET_EXP_ORIGINAL_CODE (result, code);
else
{
int flag = TREE_CONSTANT (result);
/* We used to use NOP_EXPR rather than NON_LVALUE_EXPR
so that convert_for_assignment wouldn't strip it.
That way, we got warnings for things like p = (1 - 1).
But it turns out we should not get those warnings. */
result = build1 (NON_LVALUE_EXPR, TREE_TYPE (result), result);
C_SET_EXP_ORIGINAL_CODE (result, code);
TREE_CONSTANT (result) = flag;
}
return result;
}
/* Build a binary-operation expression without default conversions.
CODE is the kind of expression to build.
This function differs from `build' in several ways:
the data type of the result is computed and recorded in it,
warnings are generated if arg data types are invalid,
special handling for addition and subtraction of pointers is known,
and some optimization is done (operations on narrow ints
are done in the narrower type when that gives the same result).
Constant folding is also done before the result is returned.
Note that the operands will never have enumeral types, or function
or array types, because either they will have the default conversions
performed or they have both just been converted to some other type in which
the arithmetic is to be done. */
tree
build_binary_op (code, orig_op0, orig_op1, convert_p)
enum tree_code code;
tree orig_op0, orig_op1;
int convert_p;
{
tree type0, type1;
register enum tree_code code0, code1;
tree op0, op1;
/* Expression code to give to the expression when it is built.
Normally this is CODE, which is what the caller asked for,
but in some special cases we change it. */
register enum tree_code resultcode = code;
/* Data type in which the computation is to be performed.
In the simplest cases this is the common type of the arguments. */
register tree result_type = NULL;
/* Nonzero means operands have already been type-converted
in whatever way is necessary.
Zero means they need to be converted to RESULT_TYPE. */
int converted = 0;
/* Nonzero means create the expression with this type, rather than
RESULT_TYPE. */
tree build_type = 0;
/* Nonzero means after finally constructing the expression
convert it to this type. */
tree final_type = 0;
/* Nonzero if this is an operation like MIN or MAX which can
safely be computed in short if both args are promoted shorts.
Also implies COMMON.
-1 indicates a bitwise operation; this makes a difference
in the exact conditions for when it is safe to do the operation
in a narrower mode. */
int shorten = 0;
/* Nonzero if this is a comparison operation;
if both args are promoted shorts, compare the original shorts.
Also implies COMMON. */
int short_compare = 0;
/* Nonzero if this is a right-shift operation, which can be computed on the
original short and then promoted if the operand is a promoted short. */
int short_shift = 0;
/* Nonzero means set RESULT_TYPE to the common type of the args. */
int common = 0;
if (convert_p)
{
op0 = default_conversion (orig_op0);
op1 = default_conversion (orig_op1);
}
else
{
op0 = orig_op0;
op1 = orig_op1;
}
type0 = TREE_TYPE (op0);
type1 = TREE_TYPE (op1);
/* The expression codes of the data types of the arguments tell us
whether the arguments are integers, floating, pointers, etc. */
code0 = TREE_CODE (type0);
code1 = TREE_CODE (type1);
/* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue. */
STRIP_TYPE_NOPS (op0);
STRIP_TYPE_NOPS (op1);
/* If an error was already reported for one of the arguments,
avoid reporting another error. */
if (code0 == ERROR_MARK || code1 == ERROR_MARK)
return error_mark_node;
switch (code)
{
case PLUS_EXPR:
/* Handle the pointer + int case. */
if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
return pointer_int_sum (PLUS_EXPR, op0, op1);
else if (code1 == POINTER_TYPE && code0 == INTEGER_TYPE)
return pointer_int_sum (PLUS_EXPR, op1, op0);
else
common = 1;
break;
case MINUS_EXPR:
/* Subtraction of two similar pointers.
We must subtract them as integers, then divide by object size. */
if (code0 == POINTER_TYPE && code1 == POINTER_TYPE
&& comp_target_types (type0, type1))
return pointer_diff (op0, op1);
/* Handle pointer minus int. Just like pointer plus int. */
else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
return pointer_int_sum (MINUS_EXPR, op0, op1);
else
common = 1;
break;
case MULT_EXPR:
common = 1;
break;
case TRUNC_DIV_EXPR:
case CEIL_DIV_EXPR:
case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR:
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
|| code0 == COMPLEX_TYPE)
&& (code1 == INTEGER_TYPE || code1 == REAL_TYPE
|| code1 == COMPLEX_TYPE))
{
if (!(code0 == INTEGER_TYPE && code1 == INTEGER_TYPE))
resultcode = RDIV_EXPR;
else
{
/* Although it would be tempting to shorten always here, that
loses on some targets, since the modulo instruction is
undefined if the quotient can't be represented in the
computation mode. We shorten only if unsigned or if
dividing by something we know != -1. */
shorten = (TREE_UNSIGNED (TREE_TYPE (orig_op0))
|| (TREE_CODE (op1) == INTEGER_CST
&& (TREE_INT_CST_LOW (op1) != -1
|| TREE_INT_CST_HIGH (op1) != -1)));
}
common = 1;
}
break;
case BIT_AND_EXPR:
case BIT_ANDTC_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
shorten = -1;
/* If one operand is a constant, and the other is a short type
that has been converted to an int,
really do the work in the short type and then convert the
result to int. If we are lucky, the constant will be 0 or 1
in the short type, making the entire operation go away. */
if (TREE_CODE (op0) == INTEGER_CST
&& TREE_CODE (op1) == NOP_EXPR
&& TYPE_PRECISION (type1) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op1, 0)))
&& TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op1, 0))))
{
final_type = result_type;
op1 = TREE_OPERAND (op1, 0);
result_type = TREE_TYPE (op1);
}
if (TREE_CODE (op1) == INTEGER_CST
&& TREE_CODE (op0) == NOP_EXPR
&& TYPE_PRECISION (type0) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0)))
&& TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0))))
{
final_type = result_type;
op0 = TREE_OPERAND (op0, 0);
result_type = TREE_TYPE (op0);
}
break;
case TRUNC_MOD_EXPR:
case FLOOR_MOD_EXPR:
if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
{
/* Although it would be tempting to shorten always here, that loses
on some targets, since the modulo instruction is undefined if the
quotient can't be represented in the computation mode. We shorten
only if unsigned or if dividing by something we know != -1. */
shorten = (TREE_UNSIGNED (TREE_TYPE (orig_op0))
|| (TREE_CODE (op1) == INTEGER_CST
&& (TREE_INT_CST_LOW (op1) != -1
|| TREE_INT_CST_HIGH (op1) != -1)));
common = 1;
}
break;
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
case TRUTH_AND_EXPR:
case TRUTH_OR_EXPR:
case TRUTH_XOR_EXPR:
if ((code0 == INTEGER_TYPE || code0 == POINTER_TYPE
|| code0 == REAL_TYPE || code0 == COMPLEX_TYPE)
&& (code1 == INTEGER_TYPE || code1 == POINTER_TYPE
|| code1 == REAL_TYPE || code1 == COMPLEX_TYPE))
{
/* Result of these operations is always an int,
but that does not mean the operands should be
converted to ints! */
result_type = integer_type_node;
op0 = truthvalue_conversion (op0);
op1 = truthvalue_conversion (op1);
converted = 1;
}
break;
/* Shift operations: result has same type as first operand;
always convert second operand to int.
Also set SHORT_SHIFT if shifting rightward. */
case RSHIFT_EXPR:
if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
{
if (TREE_CODE (op1) == INTEGER_CST && skip_evaluation == 0)
{
if (tree_int_cst_sgn (op1) < 0)
warning ("right shift count is negative");
else
{
if (TREE_INT_CST_LOW (op1) | TREE_INT_CST_HIGH (op1))
short_shift = 1;
if (TREE_INT_CST_HIGH (op1) != 0
|| ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
>= TYPE_PRECISION (type0)))
warning ("right shift count >= width of type");
}
}
/* Use the type of the value to be shifted.
This is what most traditional C compilers do. */
result_type = type0;
/* Unless traditional, convert the shift-count to an integer,
regardless of size of value being shifted. */
if (! flag_traditional)
{
if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
op1 = convert (integer_type_node, op1);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
}
break;
case LSHIFT_EXPR:
if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
{
if (TREE_CODE (op1) == INTEGER_CST && skip_evaluation == 0)
{
if (tree_int_cst_sgn (op1) < 0)
warning ("left shift count is negative");
else if (TREE_INT_CST_HIGH (op1) != 0
|| ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
>= TYPE_PRECISION (type0)))
warning ("left shift count >= width of type");
}
/* Use the type of the value to be shifted.
This is what most traditional C compilers do. */
result_type = type0;
/* Unless traditional, convert the shift-count to an integer,
regardless of size of value being shifted. */
if (! flag_traditional)
{
if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
op1 = convert (integer_type_node, op1);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
}
break;
case RROTATE_EXPR:
case LROTATE_EXPR:
if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
{
if (TREE_CODE (op1) == INTEGER_CST && skip_evaluation == 0)
{
if (tree_int_cst_sgn (op1) < 0)
warning ("shift count is negative");
else if (TREE_INT_CST_HIGH (op1) != 0
|| ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
>= TYPE_PRECISION (type0)))
warning ("shift count >= width of type");
}
/* Use the type of the value to be shifted.
This is what most traditional C compilers do. */
result_type = type0;
/* Unless traditional, convert the shift-count to an integer,
regardless of size of value being shifted. */
if (! flag_traditional)
{
if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
op1 = convert (integer_type_node, op1);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
}
break;
case EQ_EXPR:
case NE_EXPR:
/* Result of comparison is always int,
but don't convert the args to int! */
build_type = integer_type_node;
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
|| code0 == COMPLEX_TYPE)
&& (code1 == INTEGER_TYPE || code1 == REAL_TYPE
|| code1 == COMPLEX_TYPE))
short_compare = 1;
else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
{
register tree tt0 = TREE_TYPE (type0);
register tree tt1 = TREE_TYPE (type1);
/* Anything compares with void *. void * compares with anything.
Otherwise, the targets must be compatible
and both must be object or both incomplete. */
if (comp_target_types (type0, type1))
result_type = common_type (type0, type1);
else if (TYPE_MAIN_VARIANT (tt0) == void_type_node)
{
/* op0 != orig_op0 detects the case of something
whose value is 0 but which isn't a valid null ptr const. */
if (pedantic && (!integer_zerop (op0) || op0 != orig_op0)
&& TREE_CODE (tt1) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids comparison of `void *' with function pointer");
}
else if (TYPE_MAIN_VARIANT (tt1) == void_type_node)
{
if (pedantic && (!integer_zerop (op1) || op1 != orig_op1)
&& TREE_CODE (tt0) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids comparison of `void *' with function pointer");
}
else
pedwarn ("comparison of distinct pointer types lacks a cast");
if (result_type == NULL_TREE)
result_type = ptr_type_node;
}
else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST
&& integer_zerop (op1))
result_type = type0;
else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST
&& integer_zerop (op0))
result_type = type1;
else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
{
result_type = type0;
if (! flag_traditional)
pedwarn ("comparison between pointer and integer");
}
else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
{
result_type = type1;
if (! flag_traditional)
pedwarn ("comparison between pointer and integer");
}
break;
case MAX_EXPR:
case MIN_EXPR:
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
&& (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
shorten = 1;
else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
{
if (comp_target_types (type0, type1))
{
result_type = common_type (type0, type1);
if (pedantic
&& TREE_CODE (TREE_TYPE (type0)) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids ordered comparisons of pointers to functions");
}
else
{
result_type = ptr_type_node;
pedwarn ("comparison of distinct pointer types lacks a cast");
}
}
break;
case LE_EXPR:
case GE_EXPR:
case LT_EXPR:
case GT_EXPR:
build_type = integer_type_node;
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
&& (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
short_compare = 1;
else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
{
if (comp_target_types (type0, type1))
{
result_type = common_type (type0, type1);
if ((TYPE_SIZE (TREE_TYPE (type0)) != 0)
!= (TYPE_SIZE (TREE_TYPE (type1)) != 0))
pedwarn ("comparison of complete and incomplete pointers");
else if (pedantic
&& TREE_CODE (TREE_TYPE (type0)) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids ordered comparisons of pointers to functions");
}
else
{
result_type = ptr_type_node;
pedwarn ("comparison of distinct pointer types lacks a cast");
}
}
else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST
&& integer_zerop (op1))
{
result_type = type0;
if (pedantic || extra_warnings)
pedwarn ("ordered comparison of pointer with integer zero");
}
else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST
&& integer_zerop (op0))
{
result_type = type1;
if (pedantic)
pedwarn ("ordered comparison of pointer with integer zero");
}
else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
{
result_type = type0;
if (! flag_traditional)
pedwarn ("comparison between pointer and integer");
}
else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
{
result_type = type1;
if (! flag_traditional)
pedwarn ("comparison between pointer and integer");
}
break;
default:
break;
}
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE || code0 == COMPLEX_TYPE)
&&
(code1 == INTEGER_TYPE || code1 == REAL_TYPE || code1 == COMPLEX_TYPE))
{
int none_complex = (code0 != COMPLEX_TYPE && code1 != COMPLEX_TYPE);
if (shorten || common || short_compare)
result_type = common_type (type0, type1);
/* For certain operations (which identify themselves by shorten != 0)
if both args were extended from the same smaller type,
do the arithmetic in that type and then extend.
shorten !=0 and !=1 indicates a bitwise operation.
For them, this optimization is safe only if
both args are zero-extended or both are sign-extended.
Otherwise, we might change the result.
Eg, (short)-1 | (unsigned short)-1 is (int)-1
but calculated in (unsigned short) it would be (unsigned short)-1. */
if (shorten && none_complex)
{
int unsigned0, unsigned1;
tree arg0 = get_narrower (op0, &unsigned0);
tree arg1 = get_narrower (op1, &unsigned1);
/* UNS is 1 if the operation to be done is an unsigned one. */
int uns = TREE_UNSIGNED (result_type);
tree type;
final_type = result_type;
/* Handle the case that OP0 (or OP1) does not *contain* a conversion
but it *requires* conversion to FINAL_TYPE. */
if ((TYPE_PRECISION (TREE_TYPE (op0))
== TYPE_PRECISION (TREE_TYPE (arg0)))
&& TREE_TYPE (op0) != final_type)
unsigned0 = TREE_UNSIGNED (TREE_TYPE (op0));
if ((TYPE_PRECISION (TREE_TYPE (op1))
== TYPE_PRECISION (TREE_TYPE (arg1)))
&& TREE_TYPE (op1) != final_type)
unsigned1 = TREE_UNSIGNED (TREE_TYPE (op1));
/* Now UNSIGNED0 is 1 if ARG0 zero-extends to FINAL_TYPE. */
/* For bitwise operations, signedness of nominal type
does not matter. Consider only how operands were extended. */
if (shorten == -1)
uns = unsigned0;
/* Note that in all three cases below we refrain from optimizing
an unsigned operation on sign-extended args.
That would not be valid. */
/* Both args variable: if both extended in same way
from same width, do it in that width.
Do it unsigned if args were zero-extended. */
if ((TYPE_PRECISION (TREE_TYPE (arg0))
< TYPE_PRECISION (result_type))
&& (TYPE_PRECISION (TREE_TYPE (arg1))
== TYPE_PRECISION (TREE_TYPE (arg0)))
&& unsigned0 == unsigned1
&& (unsigned0 || !uns))
result_type
= signed_or_unsigned_type (unsigned0,
common_type (TREE_TYPE (arg0), TREE_TYPE (arg1)));
else if (TREE_CODE (arg0) == INTEGER_CST
&& (unsigned1 || !uns)
&& (TYPE_PRECISION (TREE_TYPE (arg1))
< TYPE_PRECISION (result_type))
&& (type = signed_or_unsigned_type (unsigned1,
TREE_TYPE (arg1)),
int_fits_type_p (arg0, type)))
result_type = type;
else if (TREE_CODE (arg1) == INTEGER_CST
&& (unsigned0 || !uns)
&& (TYPE_PRECISION (TREE_TYPE (arg0))
< TYPE_PRECISION (result_type))
&& (type = signed_or_unsigned_type (unsigned0,
TREE_TYPE (arg0)),
int_fits_type_p (arg1, type)))
result_type = type;
}
/* Shifts can be shortened if shifting right. */
if (short_shift)
{
int unsigned_arg;
tree arg0 = get_narrower (op0, &unsigned_arg);
final_type = result_type;
if (arg0 == op0 && final_type == TREE_TYPE (op0))
unsigned_arg = TREE_UNSIGNED (TREE_TYPE (op0));
if (TYPE_PRECISION (TREE_TYPE (arg0)) < TYPE_PRECISION (result_type)
/* We can shorten only if the shift count is less than the
number of bits in the smaller type size. */
&& TREE_INT_CST_HIGH (op1) == 0
&& TYPE_PRECISION (TREE_TYPE (arg0)) > TREE_INT_CST_LOW (op1)
/* If arg is sign-extended and then unsigned-shifted,
we can simulate this with a signed shift in arg's type
only if the extended result is at least twice as wide
as the arg. Otherwise, the shift could use up all the
ones made by sign-extension and bring in zeros.
We can't optimize that case at all, but in most machines
it never happens because available widths are 2**N. */
&& (!TREE_UNSIGNED (final_type)
|| unsigned_arg
|| 2 * TYPE_PRECISION (TREE_TYPE (arg0)) <= TYPE_PRECISION (result_type)))
{
/* Do an unsigned shift if the operand was zero-extended. */
result_type
= signed_or_unsigned_type (unsigned_arg,
TREE_TYPE (arg0));
/* Convert value-to-be-shifted to that type. */
if (TREE_TYPE (op0) != result_type)
op0 = convert (result_type, op0);
converted = 1;
}
}
/* Comparison operations are shortened too but differently.
They identify themselves by setting short_compare = 1. */
if (short_compare)
{
/* Don't write &op0, etc., because that would prevent op0
from being kept in a register.
Instead, make copies of the our local variables and
pass the copies by reference, then copy them back afterward. */
tree xop0 = op0, xop1 = op1, xresult_type = result_type;
enum tree_code xresultcode = resultcode;
tree val
= shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode);
if (val != 0)
return val;
op0 = xop0, op1 = xop1;
converted = 1;
resultcode = xresultcode;
if ((warn_sign_compare < 0 ? extra_warnings : warn_sign_compare != 0)
&& skip_evaluation == 0)
{
int op0_signed = ! TREE_UNSIGNED (TREE_TYPE (orig_op0));
int op1_signed = ! TREE_UNSIGNED (TREE_TYPE (orig_op1));
int unsignedp0, unsignedp1;
tree primop0 = get_narrower (op0, &unsignedp0);
tree primop1 = get_narrower (op1, &unsignedp1);
/* Avoid spurious warnings for comparison with enumerators. */
xop0 = orig_op0;
xop1 = orig_op1;
STRIP_TYPE_NOPS (xop0);
STRIP_TYPE_NOPS (xop1);
/* Give warnings for comparisons between signed and unsigned
quantities that may fail. */
/* Do the checking based on the original operand trees, so that
casts will be considered, but default promotions won't be. */
/* Do not warn if the comparison is being done in a signed type,
since the signed type will only be chosen if it can represent
all the values of the unsigned type. */
if (! TREE_UNSIGNED (result_type))
/* OK */;
/* Do not warn if both operands are unsigned. */
else if (op0_signed == op1_signed)
/* OK */;
/* Do not warn if the signed quantity is an unsuffixed
integer literal (or some static constant expression
involving such literals) and it is non-negative. */
else if ((op0_signed && TREE_CODE (xop0) == INTEGER_CST
&& tree_int_cst_sgn (xop0) >= 0)
|| (op1_signed && TREE_CODE (xop1) == INTEGER_CST
&& tree_int_cst_sgn (xop1) >= 0))
/* OK */;
/* Do not warn if the comparison is an equality operation,
the unsigned quantity is an integral constant and it does
not use the most significant bit of result_type. */
else if ((resultcode == EQ_EXPR || resultcode == NE_EXPR)
&& ((op0_signed && TREE_CODE (xop1) == INTEGER_CST
&& int_fits_type_p (xop1, signed_type (result_type)))
|| (op1_signed && TREE_CODE (xop0) == INTEGER_CST
&& int_fits_type_p (xop0, signed_type (result_type)))))
/* OK */;
else
warning ("comparison between signed and unsigned");
/* Warn if two unsigned values are being compared in a size
larger than their original size, and one (and only one) is the
result of a `~' operator. This comparison will always fail.
Also warn if one operand is a constant, and the constant
does not have all bits set that are set in the ~ operand
when it is extended. */
if ((TREE_CODE (primop0) == BIT_NOT_EXPR)
!= (TREE_CODE (primop1) == BIT_NOT_EXPR))
{
if (TREE_CODE (primop0) == BIT_NOT_EXPR)
primop0 = get_narrower (TREE_OPERAND (primop0, 0),
&unsignedp0);
else
primop1 = get_narrower (TREE_OPERAND (primop1, 0),
&unsignedp1);
if (TREE_CODE (primop0) == INTEGER_CST
|| TREE_CODE (primop1) == INTEGER_CST)
{
tree primop;
long constant, mask;
int unsignedp, bits;
if (TREE_CODE (primop0) == INTEGER_CST)
{
primop = primop1;
unsignedp = unsignedp1;
constant = TREE_INT_CST_LOW (primop0);
}
else
{
primop = primop0;
unsignedp = unsignedp0;
constant = TREE_INT_CST_LOW (primop1);
}
bits = TYPE_PRECISION (TREE_TYPE (primop));
if (bits < TYPE_PRECISION (result_type)
&& bits < HOST_BITS_PER_LONG && unsignedp)
{
mask = (~0L) << bits;
if ((mask & constant) != mask)
warning ("comparison of promoted ~unsigned with constant");
}
}
else if (unsignedp0 && unsignedp1
&& (TYPE_PRECISION (TREE_TYPE (primop0))
< TYPE_PRECISION (result_type))
&& (TYPE_PRECISION (TREE_TYPE (primop1))
< TYPE_PRECISION (result_type)))
warning ("comparison of promoted ~unsigned with unsigned");
}
}
}
}
/* At this point, RESULT_TYPE must be nonzero to avoid an error message.
If CONVERTED is zero, both args will be converted to type RESULT_TYPE.
Then the expression will be built.
It will be given type FINAL_TYPE if that is nonzero;
otherwise, it will be given type RESULT_TYPE. */
if (!result_type)
{
binary_op_error (code);
return error_mark_node;
}
if (! converted)
{
if (TREE_TYPE (op0) != result_type)
op0 = convert (result_type, op0);
if (TREE_TYPE (op1) != result_type)
op1 = convert (result_type, op1);
}
if (build_type == NULL_TREE)
build_type = result_type;
{
register tree result = build (resultcode, build_type, op0, op1);
register tree folded;
folded = fold (result);
if (folded == result)
TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
if (final_type != 0)
return convert (final_type, folded);
return folded;
}
}
/* Return a tree for the sum or difference (RESULTCODE says which)
of pointer PTROP and integer INTOP. */
static tree
pointer_int_sum (resultcode, ptrop, intop)
enum tree_code resultcode;
register tree ptrop, intop;
{
tree size_exp;
register tree result;
register tree folded;
/* The result is a pointer of the same type that is being added. */
register tree result_type = TREE_TYPE (ptrop);
if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)
{
if (pedantic || warn_pointer_arith)
pedwarn ("pointer of type `void *' used in arithmetic");
size_exp = integer_one_node;
}
else if (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE)
{
if (pedantic || warn_pointer_arith)
pedwarn ("pointer to a function used in arithmetic");
size_exp = integer_one_node;
}
else
size_exp = c_size_in_bytes (TREE_TYPE (result_type));
/* If what we are about to multiply by the size of the elements
contains a constant term, apply distributive law
and multiply that constant term separately.
This helps produce common subexpressions. */
if ((TREE_CODE (intop) == PLUS_EXPR || TREE_CODE (intop) == MINUS_EXPR)
&& ! TREE_CONSTANT (intop)
&& TREE_CONSTANT (TREE_OPERAND (intop, 1))
&& TREE_CONSTANT (size_exp)
/* If the constant comes from pointer subtraction,
skip this optimization--it would cause an error. */
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (intop, 0))) == INTEGER_TYPE
/* If the constant is unsigned, and smaller than the pointer size,
then we must skip this optimization. This is because it could cause
an overflow error if the constant is negative but INTOP is not. */
&& (! TREE_UNSIGNED (TREE_TYPE (intop))
|| (TYPE_PRECISION (TREE_TYPE (intop))
== TYPE_PRECISION (TREE_TYPE (ptrop)))))
{
enum tree_code subcode = resultcode;
tree int_type = TREE_TYPE (intop);
if (TREE_CODE (intop) == MINUS_EXPR)
subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR);
/* Convert both subexpression types to the type of intop,
because weird cases involving pointer arithmetic
can result in a sum or difference with different type args. */
ptrop = build_binary_op (subcode, ptrop,
convert (int_type, TREE_OPERAND (intop, 1)), 1);
intop = convert (int_type, TREE_OPERAND (intop, 0));
}
/* Convert the integer argument to a type the same size as sizetype
so the multiply won't overflow spuriously. */
if (TYPE_PRECISION (TREE_TYPE (intop)) != TYPE_PRECISION (sizetype)
|| TREE_UNSIGNED (TREE_TYPE (intop)) != TREE_UNSIGNED (sizetype))
intop = convert (type_for_size (TYPE_PRECISION (sizetype),
TREE_UNSIGNED (sizetype)), intop);
/* Replace the integer argument with a suitable product by the object size.
Do this multiplication as signed, then convert to the appropriate
pointer type (actually unsigned integral). */
intop = convert (result_type,
build_binary_op (MULT_EXPR, intop,
convert (TREE_TYPE (intop), size_exp), 1));
/* Create the sum or difference. */
result = build (resultcode, result_type, ptrop, intop);
folded = fold (result);
if (folded == result)
TREE_CONSTANT (folded) = TREE_CONSTANT (ptrop) & TREE_CONSTANT (intop);
return folded;
}
/* Return a tree for the difference of pointers OP0 and OP1.
The resulting tree has type int. */
static tree
pointer_diff (op0, op1)
register tree op0, op1;
{
register tree result, folded;
tree restype = ptrdiff_type_node;
tree target_type = TREE_TYPE (TREE_TYPE (op0));
if (pedantic || warn_pointer_arith)
{
if (TREE_CODE (target_type) == VOID_TYPE)
pedwarn ("pointer of type `void *' used in subtraction");
if (TREE_CODE (target_type) == FUNCTION_TYPE)
pedwarn ("pointer to a function used in subtraction");
}
/* First do the subtraction as integers;
then drop through to build the divide operator.
Do not do default conversions on the minus operator
in case restype is a short type. */
op0 = build_binary_op (MINUS_EXPR, convert (restype, op0),
convert (restype, op1), 0);
/* This generates an error if op1 is pointer to incomplete type. */
if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (op1))) == 0)
error ("arithmetic on pointer to an incomplete type");
/* This generates an error if op0 is pointer to incomplete type. */
op1 = c_size_in_bytes (target_type);
/* Divide by the size, in easiest possible way. */
result = build (EXACT_DIV_EXPR, restype, op0, convert (restype, op1));
folded = fold (result);
if (folded == result)
TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
return folded;
}
/* Construct and perhaps optimize a tree representation
for a unary operation. CODE, a tree_code, specifies the operation
and XARG is the operand. NOCONVERT nonzero suppresses
the default promotions (such as from short to int). */
tree
build_unary_op (code, xarg, noconvert)
enum tree_code code;
tree xarg;
int noconvert;
{
/* No default_conversion here. It causes trouble for ADDR_EXPR. */
register tree arg = xarg;
register tree argtype = 0;
register enum tree_code typecode = TREE_CODE (TREE_TYPE (arg));
tree val;
if (typecode == ERROR_MARK)
return error_mark_node;
if (typecode == ENUMERAL_TYPE)
typecode = INTEGER_TYPE;
switch (code)
{
case CONVERT_EXPR:
/* This is used for unary plus, because a CONVERT_EXPR
is enough to prevent anybody from looking inside for
associativity, but won't generate any code. */
if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
|| typecode == COMPLEX_TYPE))
{
error ("wrong type argument to unary plus");
return error_mark_node;
}
else if (!noconvert)
arg = default_conversion (arg);
break;
case NEGATE_EXPR:
if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
|| typecode == COMPLEX_TYPE))
{
error ("wrong type argument to unary minus");
return error_mark_node;
}
else if (!noconvert)
arg = default_conversion (arg);
break;
case BIT_NOT_EXPR:
if (typecode == COMPLEX_TYPE)
{
code = CONJ_EXPR;
if (!noconvert)
arg = default_conversion (arg);
}
else if (typecode != INTEGER_TYPE)
{
error ("wrong type argument to bit-complement");
return error_mark_node;
}
else if (!noconvert)
arg = default_conversion (arg);
break;
case ABS_EXPR:
if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
|| typecode == COMPLEX_TYPE))
{
error ("wrong type argument to abs");
return error_mark_node;
}
else if (!noconvert)
arg = default_conversion (arg);
break;
case CONJ_EXPR:
/* Conjugating a real value is a no-op, but allow it anyway. */
if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
|| typecode == COMPLEX_TYPE))
{
error ("wrong type argument to conjugation");
return error_mark_node;
}
else if (!noconvert)
arg = default_conversion (arg);
break;
case TRUTH_NOT_EXPR:
if (typecode != INTEGER_TYPE
&& typecode != REAL_TYPE && typecode != POINTER_TYPE
&& typecode != COMPLEX_TYPE
/* These will convert to a pointer. */
&& typecode != ARRAY_TYPE && typecode != FUNCTION_TYPE)
{
error ("wrong type argument to unary exclamation mark");
return error_mark_node;
}
arg = truthvalue_conversion (arg);
return invert_truthvalue (arg);
case NOP_EXPR:
break;
case REALPART_EXPR:
if (TREE_CODE (arg) == COMPLEX_CST)
return TREE_REALPART (arg);
else if (TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE)
return fold (build1 (REALPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg));
else
return arg;
case IMAGPART_EXPR:
if (TREE_CODE (arg) == COMPLEX_CST)
return TREE_IMAGPART (arg);
else if (TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE)
return fold (build1 (IMAGPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg));
else
return convert (TREE_TYPE (arg), integer_zero_node);
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
/* Handle complex lvalues (when permitted)
by reduction to simpler cases. */
val = unary_complex_lvalue (code, arg);
if (val != 0)
return val;
/* Increment or decrement the real part of the value,
and don't change the imaginary part. */
if (typecode == COMPLEX_TYPE)
{
tree real, imag;
arg = stabilize_reference (arg);
real = build_unary_op (REALPART_EXPR, arg, 1);
imag = build_unary_op (IMAGPART_EXPR, arg, 1);
return build (COMPLEX_EXPR, TREE_TYPE (arg),
build_unary_op (code, real, 1), imag);
}
/* Report invalid types. */
if (typecode != POINTER_TYPE
&& typecode != INTEGER_TYPE && typecode != REAL_TYPE)
{
error (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR
? "wrong type argument to increment"
: "wrong type argument to decrement");
return error_mark_node;
}
{
register tree inc;
tree result_type = TREE_TYPE (arg);
arg = get_unwidened (arg, 0);
argtype = TREE_TYPE (arg);
/* Compute the increment. */
if (typecode == POINTER_TYPE)
{
/* If pointer target is an undefined struct,
we just cannot know how to do the arithmetic. */
if (TYPE_SIZE (TREE_TYPE (result_type)) == 0)
error (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR
? "increment of pointer to unknown structure"
: "decrement of pointer to unknown structure");
else if ((pedantic || warn_pointer_arith)
&& (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE
|| TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE))
pedwarn (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR
? "wrong type argument to increment"
: "wrong type argument to decrement");
inc = c_size_in_bytes (TREE_TYPE (result_type));
}
else
inc = integer_one_node;
inc = convert (argtype, inc);
/* Handle incrementing a cast-expression. */
while (1)
switch (TREE_CODE (arg))
{
case NOP_EXPR:
case CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case FIX_CEIL_EXPR:
pedantic_lvalue_warning (CONVERT_EXPR);
/* If the real type has the same machine representation
as the type it is cast to, we can make better output
by adding directly to the inside of the cast. */
if ((TREE_CODE (TREE_TYPE (arg))
== TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 0))))
&& (TYPE_MODE (TREE_TYPE (arg))
== TYPE_MODE (TREE_TYPE (TREE_OPERAND (arg, 0)))))
arg = TREE_OPERAND (arg, 0);
else
{
tree incremented, modify, value;
arg = stabilize_reference (arg);
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
value = arg;
else
value = save_expr (arg);
incremented = build (((code == PREINCREMENT_EXPR
|| code == POSTINCREMENT_EXPR)
? PLUS_EXPR : MINUS_EXPR),
argtype, value, inc);
TREE_SIDE_EFFECTS (incremented) = 1;
modify = build_modify_expr (arg, NOP_EXPR, incremented);
value = build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value);
TREE_USED (value) = 1;
return value;
}
break;
default:
goto give_up;
}
give_up:
/* Complain about anything else that is not a true lvalue. */
if (!lvalue_or_else (arg, ((code == PREINCREMENT_EXPR
|| code == POSTINCREMENT_EXPR)
? "invalid lvalue in increment"
: "invalid lvalue in decrement")))
return error_mark_node;
/* Report a read-only lvalue. */
if (TREE_READONLY (arg))
readonly_warning (arg,
((code == PREINCREMENT_EXPR
|| code == POSTINCREMENT_EXPR)
? "increment" : "decrement"));
val = build (code, TREE_TYPE (arg), arg, inc);
TREE_SIDE_EFFECTS (val) = 1;
val = convert (result_type, val);
if (TREE_CODE (val) != code)
TREE_NO_UNUSED_WARNING (val) = 1;
return val;
}
case ADDR_EXPR:
/* Note that this operation never does default_conversion
regardless of NOCONVERT. */
/* Let &* cancel out to simplify resulting code. */
if (TREE_CODE (arg) == INDIRECT_REF)
{
/* Don't let this be an lvalue. */
if (lvalue_p (TREE_OPERAND (arg, 0)))
return non_lvalue (TREE_OPERAND (arg, 0));
return TREE_OPERAND (arg, 0);
}
/* For &x[y], return x+y */
if (TREE_CODE (arg) == ARRAY_REF)
{
if (mark_addressable (TREE_OPERAND (arg, 0)) == 0)
return error_mark_node;
return build_binary_op (PLUS_EXPR, TREE_OPERAND (arg, 0),
TREE_OPERAND (arg, 1), 1);
}
/* Handle complex lvalues (when permitted)
by reduction to simpler cases. */
val = unary_complex_lvalue (code, arg);
if (val != 0)
return val;
#if 0 /* Turned off because inconsistent;
float f; *&(int)f = 3.4 stores in int format
whereas (int)f = 3.4 stores in float format. */
/* Address of a cast is just a cast of the address
of the operand of the cast. */
switch (TREE_CODE (arg))
{
case NOP_EXPR:
case CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case FIX_CEIL_EXPR:
if (pedantic)
pedwarn ("ANSI C forbids the address of a cast expression");
return convert (build_pointer_type (TREE_TYPE (arg)),
build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0),
0));
}
#endif
/* Allow the address of a constructor if all the elements
are constant. */
if (TREE_CODE (arg) == CONSTRUCTOR && TREE_CONSTANT (arg))
;
/* Anything not already handled and not a true memory reference
is an error. */
else if (typecode != FUNCTION_TYPE
&& !lvalue_or_else (arg, "invalid lvalue in unary `&'"))
return error_mark_node;
/* Ordinary case; arg is a COMPONENT_REF or a decl. */
argtype = TREE_TYPE (arg);
/* If the lvalue is const or volatile, merge that into the type
to which the address will point. Note that you can't get a
restricted pointer by taking the address of something, so we
only have to deal with `const' and `volatile' here. */
if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'd'
|| TREE_CODE_CLASS (TREE_CODE (arg)) == 'r')
{
if (TREE_READONLY (arg) || TREE_THIS_VOLATILE (arg))
argtype = c_build_type_variant (argtype,
TREE_READONLY (arg),
TREE_THIS_VOLATILE (arg));
}
argtype = build_pointer_type (argtype);
if (mark_addressable (arg) == 0)
return error_mark_node;
{
tree addr;
if (TREE_CODE (arg) == COMPONENT_REF)
{
tree field = TREE_OPERAND (arg, 1);
addr = build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), 0);
if (DECL_C_BIT_FIELD (field))
{
error ("attempt to take address of bit-field structure member `%s'",
IDENTIFIER_POINTER (DECL_NAME (field)));
return error_mark_node;
}
addr = convert (argtype, addr);
if (! integer_zerop (DECL_FIELD_BITPOS (field)))
{
tree offset
= size_binop (EASY_DIV_EXPR, DECL_FIELD_BITPOS (field),
size_int (BITS_PER_UNIT));
int flag = TREE_CONSTANT (addr);
addr = fold (build (PLUS_EXPR, argtype,
addr, convert (argtype, offset)));
TREE_CONSTANT (addr) = flag;
}
}
else
addr = build1 (code, argtype, arg);
/* Address of a static or external variable or
file-scope function counts as a constant. */
if (staticp (arg)
&& ! (TREE_CODE (arg) == FUNCTION_DECL
&& DECL_CONTEXT (arg) != 0))
TREE_CONSTANT (addr) = 1;
return addr;
}
default:
break;
}
if (argtype == 0)
argtype = TREE_TYPE (arg);
return fold (build1 (code, argtype, arg));
}
#if 0
/* If CONVERSIONS is a conversion expression or a nested sequence of such,
convert ARG with the same conversions in the same order
and return the result. */
static tree
convert_sequence (conversions, arg)
tree conversions;
tree arg;
{
switch (TREE_CODE (conversions))
{
case NOP_EXPR:
case CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case FIX_CEIL_EXPR:
return convert (TREE_TYPE (conversions),
convert_sequence (TREE_OPERAND (conversions, 0),
arg));
default:
return arg;
}
}
#endif /* 0 */
/* Return nonzero if REF is an lvalue valid for this language.
Lvalues can be assigned, unless their type has TYPE_READONLY.
Lvalues can have their address taken, unless they have DECL_REGISTER. */
int
lvalue_p (ref)
tree ref;
{
register enum tree_code code = TREE_CODE (ref);
switch (code)
{
case REALPART_EXPR:
case IMAGPART_EXPR:
case COMPONENT_REF:
return lvalue_p (TREE_OPERAND (ref, 0));
case STRING_CST:
return 1;
case INDIRECT_REF:
case ARRAY_REF:
case VAR_DECL:
case PARM_DECL:
case RESULT_DECL:
case ERROR_MARK:
return (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
&& TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE);
case BIND_EXPR:
case RTL_EXPR:
return TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE;
default:
return 0;
}
}
/* Return nonzero if REF is an lvalue valid for this language;
otherwise, print an error message and return zero. */
int
lvalue_or_else (ref, msgid)
tree ref;
const char *msgid;
{
int win = lvalue_p (ref);
if (! win)
error (msgid);
return win;
}
/* Apply unary lvalue-demanding operator CODE to the expression ARG
for certain kinds of expressions which are not really lvalues
but which we can accept as lvalues.
If ARG is not a kind of expression we can handle, return zero. */
static tree
unary_complex_lvalue (code, arg)
enum tree_code code;
tree arg;
{
/* Handle (a, b) used as an "lvalue". */
if (TREE_CODE (arg) == COMPOUND_EXPR)
{
tree real_result = build_unary_op (code, TREE_OPERAND (arg, 1), 0);
/* If this returns a function type, it isn't really being used as
an lvalue, so don't issue a warning about it. */
if (TREE_CODE (TREE_TYPE (arg)) != FUNCTION_TYPE)
pedantic_lvalue_warning (COMPOUND_EXPR);
return build (COMPOUND_EXPR, TREE_TYPE (real_result),
TREE_OPERAND (arg, 0), real_result);
}
/* Handle (a ? b : c) used as an "lvalue". */
if (TREE_CODE (arg) == COND_EXPR)
{
pedantic_lvalue_warning (COND_EXPR);
if (TREE_CODE (TREE_TYPE (arg)) != FUNCTION_TYPE)
pedantic_lvalue_warning (COMPOUND_EXPR);
return (build_conditional_expr
(TREE_OPERAND (arg, 0),
build_unary_op (code, TREE_OPERAND (arg, 1), 0),
build_unary_op (code, TREE_OPERAND (arg, 2), 0)));
}
return 0;
}
/* If pedantic, warn about improper lvalue. CODE is either COND_EXPR
COMPOUND_EXPR, or CONVERT_EXPR (for casts). */
static void
pedantic_lvalue_warning (code)
enum tree_code code;
{
if (pedantic)
pedwarn (code == COND_EXPR
? "ANSI C forbids use of conditional expressions as lvalues"
: code == COMPOUND_EXPR
? "ANSI C forbids use of compound expressions as lvalues"
: "ANSI C forbids use of cast expressions as lvalues");
}
/* Warn about storing in something that is `const'. */
void
readonly_warning (arg, msgid)
tree arg;
const char *msgid;
{
/* Forbid assignments to iterators. */
if (TREE_CODE (arg) == VAR_DECL && ITERATOR_P (arg))
pedwarn ("%s of iterator `%s'", _(msgid),
IDENTIFIER_POINTER (DECL_NAME (arg)));
if (TREE_CODE (arg) == COMPONENT_REF)
{
if (TYPE_READONLY (TREE_TYPE (TREE_OPERAND (arg, 0))))
readonly_warning (TREE_OPERAND (arg, 0), msgid);
else
pedwarn ("%s of read-only member `%s'", _(msgid),
IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (arg, 1))));
}
else if (TREE_CODE (arg) == VAR_DECL)
pedwarn ("%s of read-only variable `%s'", _(msgid),
IDENTIFIER_POINTER (DECL_NAME (arg)));
else
pedwarn ("%s of read-only location", _(msgid));
}
/* Mark EXP saying that we need to be able to take the
address of it; it should not be allocated in a register.
Value is 1 if successful. */
int
mark_addressable (exp)
tree exp;
{
register tree x = exp;
while (1)
switch (TREE_CODE (x))
{
case COMPONENT_REF:
if (DECL_C_BIT_FIELD (TREE_OPERAND (x, 1)))
{
error ("cannot take address of bitfield `%s'",
IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (x, 1))));
return 0;
}
/* ... fall through ... */
case ADDR_EXPR:
case ARRAY_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
x = TREE_OPERAND (x, 0);
break;
case CONSTRUCTOR:
TREE_ADDRESSABLE (x) = 1;
return 1;
case VAR_DECL:
case CONST_DECL:
case PARM_DECL:
case RESULT_DECL:
if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x)
&& DECL_NONLOCAL (x))
{
if (TREE_PUBLIC (x))
{
error ("global register variable `%s' used in nested function",
IDENTIFIER_POINTER (DECL_NAME (x)));
return 0;
}
pedwarn ("register variable `%s' used in nested function",
IDENTIFIER_POINTER (DECL_NAME (x)));
}
else if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x))
{
if (TREE_PUBLIC (x))
{
error ("address of global register variable `%s' requested",
IDENTIFIER_POINTER (DECL_NAME (x)));
return 0;
}
/* If we are making this addressable due to its having
volatile components, give a different error message. Also
handle the case of an unnamed parameter by not trying
to give the name. */
else if (C_TYPE_FIELDS_VOLATILE (TREE_TYPE (x)))
{
error ("cannot put object with volatile field into register");
return 0;
}
pedwarn ("address of register variable `%s' requested",
IDENTIFIER_POINTER (DECL_NAME (x)));
}
put_var_into_stack (x);
/* drops in */
case FUNCTION_DECL:
TREE_ADDRESSABLE (x) = 1;
#if 0 /* poplevel deals with this now. */
if (DECL_CONTEXT (x) == 0)
TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (x)) = 1;
#endif
default:
return 1;
}
}
/* Build and return a conditional expression IFEXP ? OP1 : OP2. */
tree
build_conditional_expr (ifexp, op1, op2)
tree ifexp, op1, op2;
{
register tree type1;
register tree type2;
register enum tree_code code1;
register enum tree_code code2;
register tree result_type = NULL;
tree orig_op1 = op1, orig_op2 = op2;
ifexp = truthvalue_conversion (default_conversion (ifexp));
#if 0 /* Produces wrong result if within sizeof. */
/* Don't promote the operands separately if they promote
the same way. Return the unpromoted type and let the combined
value get promoted if necessary. */
if (TREE_TYPE (op1) == TREE_TYPE (op2)
&& TREE_CODE (TREE_TYPE (op1)) != ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (op1)) != ENUMERAL_TYPE
&& TREE_CODE (TREE_TYPE (op1)) != FUNCTION_TYPE)
{
if (TREE_CODE (ifexp) == INTEGER_CST)
return pedantic_non_lvalue (integer_zerop (ifexp) ? op2 : op1);
return fold (build (COND_EXPR, TREE_TYPE (op1), ifexp, op1, op2));
}
#endif
/* Promote both alternatives. */
if (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE)
op1 = default_conversion (op1);
if (TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE)
op2 = default_conversion (op2);
if (TREE_CODE (ifexp) == ERROR_MARK
|| TREE_CODE (TREE_TYPE (op1)) == ERROR_MARK
|| TREE_CODE (TREE_TYPE (op2)) == ERROR_MARK)
return error_mark_node;
type1 = TREE_TYPE (op1);
code1 = TREE_CODE (type1);
type2 = TREE_TYPE (op2);
code2 = TREE_CODE (type2);
/* Quickly detect the usual case where op1 and op2 have the same type
after promotion. */
if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2))
{
if (type1 == type2)
result_type = type1;
else
result_type = TYPE_MAIN_VARIANT (type1);
}
else if ((code1 == INTEGER_TYPE || code1 == REAL_TYPE)
&& (code2 == INTEGER_TYPE || code2 == REAL_TYPE))
{
result_type = common_type (type1, type2);
}
else if (code1 == VOID_TYPE || code2 == VOID_TYPE)
{
if (pedantic && (code1 != VOID_TYPE || code2 != VOID_TYPE))
pedwarn ("ANSI C forbids conditional expr with only one void side");
result_type = void_type_node;
}
else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE)
{
if (comp_target_types (type1, type2))
result_type = common_type (type1, type2);
else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node
&& TREE_CODE (orig_op1) != NOP_EXPR)
result_type = qualify_type (type2, type1);
else if (integer_zerop (op2) && TREE_TYPE (type2) == void_type_node
&& TREE_CODE (orig_op2) != NOP_EXPR)
result_type = qualify_type (type1, type2);
else if (TYPE_MAIN_VARIANT (TREE_TYPE (type1)) == void_type_node)
{
if (pedantic && TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids conditional expr between `void *' and function pointer");
result_type = qualify_type (type1, type2);
}
else if (TYPE_MAIN_VARIANT (TREE_TYPE (type2)) == void_type_node)
{
if (pedantic && TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids conditional expr between `void *' and function pointer");
result_type = qualify_type (type2, type1);
}
else
{
pedwarn ("pointer type mismatch in conditional expression");
result_type = build_pointer_type (void_type_node);
}
}
else if (code1 == POINTER_TYPE && code2 == INTEGER_TYPE)
{
if (! integer_zerop (op2))
pedwarn ("pointer/integer type mismatch in conditional expression");
else
{
op2 = null_pointer_node;
#if 0 /* The spec seems to say this is permitted. */
if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids conditional expr between 0 and function pointer");
#endif
}
result_type = type1;
}
else if (code2 == POINTER_TYPE && code1 == INTEGER_TYPE)
{
if (!integer_zerop (op1))
pedwarn ("pointer/integer type mismatch in conditional expression");
else
{
op1 = null_pointer_node;
#if 0 /* The spec seems to say this is permitted. */
if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE)
pedwarn ("ANSI C forbids conditional expr between 0 and function pointer");
#endif
}
result_type = type2;
}
if (!result_type)
{
if (flag_cond_mismatch)
result_type = void_type_node;
else
{
error ("type mismatch in conditional expression");
return error_mark_node;
}
}
/* Merge const and volatile flags of the incoming types. */
result_type
= build_type_variant (result_type,
TREE_READONLY (op1) || TREE_READONLY (op2),
TREE_THIS_VOLATILE (op1) || TREE_THIS_VOLATILE (op2));
if (result_type != TREE_TYPE (op1))
op1 = convert_and_check (result_type, op1);
if (result_type != TREE_TYPE (op2))
op2 = convert_and_check (result_type, op2);
#if 0
if (code1 == RECORD_TYPE || code1 == UNION_TYPE)
{
result_type = TREE_TYPE (op1);
if (TREE_CONSTANT (ifexp))
return pedantic_non_lvalue (integer_zerop (ifexp) ? op2 : op1);
if (TYPE_MODE (result_type) == BLKmode)
{
register tree tempvar
= build_decl (VAR_DECL, NULL_TREE, result_type);
register tree xop1 = build_modify_expr (tempvar, op1);
register tree xop2 = build_modify_expr (tempvar, op2);
register tree result = fold (build (COND_EXPR, result_type,
ifexp, xop1, xop2));
layout_decl (tempvar, TYPE_ALIGN (result_type));
/* No way to handle variable-sized objects here.
I fear that the entire handling of BLKmode conditional exprs
needs to be redone. */
if (TREE_CODE (DECL_SIZE (tempvar)) != INTEGER_CST)
abort ();
DECL_RTL (tempvar)
= assign_stack_local (DECL_MODE (tempvar),
(TREE_INT_CST_LOW (DECL_SIZE (tempvar))
+ BITS_PER_UNIT - 1)
/ BITS_PER_UNIT,
0);
TREE_SIDE_EFFECTS (result)
= TREE_SIDE_EFFECTS (ifexp) | TREE_SIDE_EFFECTS (op1)
| TREE_SIDE_EFFECTS (op2);
return build (COMPOUND_EXPR, result_type, result, tempvar);
}
}
#endif /* 0 */
if (TREE_CODE (ifexp) == INTEGER_CST)
return pedantic_non_lvalue (integer_zerop (ifexp) ? op2 : op1);
return fold (build (COND_EXPR, result_type, ifexp, op1, op2));
}
/* Given a list of expressions, return a compound expression
that performs them all and returns the value of the last of them. */
tree
build_compound_expr (list)
tree list;
{
return internal_build_compound_expr (list, TRUE);
}
static tree
internal_build_compound_expr (list, first_p)
tree list;
int first_p;
{
register tree rest;
if (TREE_CHAIN (list) == 0)
{
#if 0 /* If something inside inhibited lvalueness, we should not override. */
/* Consider (x, y+0), which is not an lvalue since y+0 is not. */
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
if (TREE_CODE (list) == NON_LVALUE_EXPR)
list = TREE_OPERAND (list, 0);
#endif
/* Don't let (0, 0) be null pointer constant. */
if (!first_p && integer_zerop (TREE_VALUE (list)))
return non_lvalue (TREE_VALUE (list));
return TREE_VALUE (list);
}
if (TREE_CHAIN (list) != 0 && TREE_CHAIN (TREE_CHAIN (list)) == 0)
{
/* Convert arrays to pointers when there really is a comma operator. */
if (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (list)))) == ARRAY_TYPE)
TREE_VALUE (TREE_CHAIN (list))
= default_conversion (TREE_VALUE (TREE_CHAIN (list)));
}
rest = internal_build_compound_expr (TREE_CHAIN (list), FALSE);
if (! TREE_SIDE_EFFECTS (TREE_VALUE (list)))
{
/* The left-hand operand of a comma expression is like an expression
statement: with -W or -Wunused, we should warn if it doesn't have
any side-effects, unless it was explicitly cast to (void). */
if ((extra_warnings || warn_unused)
&& ! (TREE_CODE (TREE_VALUE (list)) == CONVERT_EXPR
&& TREE_TYPE (TREE_VALUE (list)) == void_type_node))
warning ("left-hand operand of comma expression has no effect");
/* When pedantic, a compound expression can be neither an lvalue
nor an integer constant expression. */
if (! pedantic)
return rest;
}
/* With -Wunused, we should also warn if the left-hand operand does have
side-effects, but computes a value which is not used. For example, in
`foo() + bar(), baz()' the result of the `+' operator is not used,
so we should issue a warning. */
else if (warn_unused)
warn_if_unused_value (TREE_VALUE (list));
return build (COMPOUND_EXPR, TREE_TYPE (rest), TREE_VALUE (list), rest);
}
/* Build an expression representing a cast to type TYPE of expression EXPR. */
tree
build_c_cast (type, expr)
register tree type;
tree expr;
{
register tree value = expr;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
type = TYPE_MAIN_VARIANT (type);
#if 0
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
if (TREE_CODE (value) == NON_LVALUE_EXPR)
value = TREE_OPERAND (value, 0);
#endif
if (TREE_CODE (type) == ARRAY_TYPE)
{
error ("cast specifies array type");
return error_mark_node;
}
if (TREE_CODE (type) == FUNCTION_TYPE)
{
error ("cast specifies function type");
return error_mark_node;
}
if (type == TREE_TYPE (value))
{
if (pedantic)
{
if (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE)
pedwarn ("ANSI C forbids casting nonscalar to the same type");
}
}
else if (TREE_CODE (type) == UNION_TYPE)
{
tree field;
if (TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE)
value = default_conversion (value);
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (field)),
TYPE_MAIN_VARIANT (TREE_TYPE (value))))
break;
if (field)
{
const char *name;
tree t;
if (pedantic)
pedwarn ("ANSI C forbids casts to union type");
if (TYPE_NAME (type) != 0)
{
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
name = IDENTIFIER_POINTER (TYPE_NAME (type));
else
name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
}
else
name = "";
t = digest_init (type, build (CONSTRUCTOR, type, NULL_TREE,
build_tree_list (field, value)),
0, 0);
TREE_CONSTANT (t) = TREE_CONSTANT (value);
return t;
}
error ("cast to union type from type not present in union");
return error_mark_node;
}
else
{
tree otype, ovalue;
/* If casting to void, avoid the error that would come
from default_conversion in the case of a non-lvalue array. */
if (type == void_type_node)
return build1 (CONVERT_EXPR, type, value);
/* Convert functions and arrays to pointers,
but don't convert any other types. */
if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE
|| TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE)
value = default_conversion (value);
otype = TREE_TYPE (value);
/* Optionally warn about potentially worrisome casts. */
if (warn_cast_qual
&& TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (otype) == POINTER_TYPE)
{
/* Go to the innermost object being pointed to. */
tree in_type = type;
tree in_otype = otype;
while (TREE_CODE (in_type) == POINTER_TYPE)
in_type = TREE_TYPE (in_type);
while (TREE_CODE (in_otype) == POINTER_TYPE)
in_otype = TREE_TYPE (in_otype);
if (TYPE_QUALS (in_otype) & ~TYPE_QUALS (in_type))
/* There are qualifiers present in IN_OTYPE that are not
present in IN_TYPE. */
pedwarn ("cast discards qualifiers from pointer target type");
}
/* Warn about possible alignment problems. */
if (STRICT_ALIGNMENT && warn_cast_align
&& TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (otype) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (otype)) != VOID_TYPE
&& TREE_CODE (TREE_TYPE (otype)) != FUNCTION_TYPE
/* Don't warn about opaque types, where the actual alignment
restriction is unknown. */
&& !((TREE_CODE (TREE_TYPE (otype)) == UNION_TYPE
|| TREE_CODE (TREE_TYPE (otype)) == RECORD_TYPE)
&& TYPE_MODE (TREE_TYPE (otype)) == VOIDmode)
&& TYPE_ALIGN (TREE_TYPE (type)) > TYPE_ALIGN (TREE_TYPE (otype)))
warning ("cast increases required alignment of target type");
if (TREE_CODE (type) == INTEGER_TYPE
&& TREE_CODE (otype) == POINTER_TYPE
&& TYPE_PRECISION (type) != TYPE_PRECISION (otype)
&& !TREE_CONSTANT (value))
warning ("cast from pointer to integer of different size");
if (warn_bad_function_cast
&& TREE_CODE (value) == CALL_EXPR
&& TREE_CODE (type) != TREE_CODE (otype))
warning ("cast does not match function type");
if (TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (otype) == INTEGER_TYPE
&& TYPE_PRECISION (type) != TYPE_PRECISION (otype)
#if 0
/* Don't warn about converting 0 to pointer,
provided the 0 was explicit--not cast or made by folding. */
&& !(TREE_CODE (value) == INTEGER_CST && integer_zerop (value))
#endif
/* Don't warn about converting any constant. */
&& !TREE_CONSTANT (value))
warning ("cast to pointer from integer of different size");
ovalue = value;
value = convert (type, value);
/* Ignore any integer overflow caused by the cast. */
if (TREE_CODE (value) == INTEGER_CST)
{
TREE_OVERFLOW (value) = TREE_OVERFLOW (ovalue);
TREE_CONSTANT_OVERFLOW (value) = TREE_CONSTANT_OVERFLOW (ovalue);
}
}
/* Pedantically, don't ley (void *) (FOO *) 0 be a null pointer constant. */
if (pedantic && TREE_CODE (value) == INTEGER_CST
&& TREE_CODE (expr) == INTEGER_CST
&& TREE_CODE (TREE_TYPE (expr)) != INTEGER_TYPE)
value = non_lvalue (value);
/* If pedantic, don't let a cast be an lvalue. */
if (value == expr && pedantic)
value = non_lvalue (value);
return value;
}
/* Build an assignment expression of lvalue LHS from value RHS.
MODIFYCODE is the code for a binary operator that we use
to combine the old value of LHS with RHS to get the new value.
Or else MODIFYCODE is NOP_EXPR meaning do a simple assignment. */
tree
build_modify_expr (lhs, modifycode, rhs)
tree lhs, rhs;
enum tree_code modifycode;
{
register tree result;
tree newrhs;
tree lhstype = TREE_TYPE (lhs);
tree olhstype = lhstype;
/* Types that aren't fully specified cannot be used in assignments. */
lhs = require_complete_type (lhs);
/* Avoid duplicate error messages from operands that had errors. */
if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK)
return error_mark_node;
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
/* Do not use STRIP_NOPS here. We do not want an enumerator
whose value is 0 to count as a null pointer constant. */
if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
rhs = TREE_OPERAND (rhs, 0);
newrhs = rhs;
/* Handle control structure constructs used as "lvalues". */
switch (TREE_CODE (lhs))
{
/* Handle (a, b) used as an "lvalue". */
case COMPOUND_EXPR:
pedantic_lvalue_warning (COMPOUND_EXPR);
newrhs = build_modify_expr (TREE_OPERAND (lhs, 1),
modifycode, rhs);
if (TREE_CODE (newrhs) == ERROR_MARK)
return error_mark_node;
return build (COMPOUND_EXPR, lhstype,
TREE_OPERAND (lhs, 0), newrhs);
/* Handle (a ? b : c) used as an "lvalue". */
case COND_EXPR:
pedantic_lvalue_warning (COND_EXPR);
rhs = save_expr (rhs);
{
/* Produce (a ? (b = rhs) : (c = rhs))
except that the RHS goes through a save-expr
so the code to compute it is only emitted once. */
tree cond
= build_conditional_expr (TREE_OPERAND (lhs, 0),
build_modify_expr (TREE_OPERAND (lhs, 1),
modifycode, rhs),
build_modify_expr (TREE_OPERAND (lhs, 2),
modifycode, rhs));
if (TREE_CODE (cond) == ERROR_MARK)
return cond;
/* Make sure the code to compute the rhs comes out
before the split. */
return build (COMPOUND_EXPR, TREE_TYPE (lhs),
/* But cast it to void to avoid an "unused" error. */
convert (void_type_node, rhs), cond);
}
default:
break;
}
/* If a binary op has been requested, combine the old LHS value with the RHS
producing the value we should actually store into the LHS. */
if (modifycode != NOP_EXPR)
{
lhs = stabilize_reference (lhs);
newrhs = build_binary_op (modifycode, lhs, rhs, 1);
}
/* Handle a cast used as an "lvalue".
We have already performed any binary operator using the value as cast.
Now convert the result to the cast type of the lhs,
and then true type of the lhs and store it there;
then convert result back to the cast type to be the value
of the assignment. */
switch (TREE_CODE (lhs))
{
case NOP_EXPR:
case CONVERT_EXPR:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case FIX_CEIL_EXPR:
if (TREE_CODE (TREE_TYPE (newrhs)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (newrhs)) == FUNCTION_TYPE)
newrhs = default_conversion (newrhs);
{
tree inner_lhs = TREE_OPERAND (lhs, 0);
tree result;
result = build_modify_expr (inner_lhs, NOP_EXPR,
convert (TREE_TYPE (inner_lhs),
convert (lhstype, newrhs)));
if (TREE_CODE (result) == ERROR_MARK)
return result;
pedantic_lvalue_warning (CONVERT_EXPR);
return convert (TREE_TYPE (lhs), result);
}
default:
break;
}
/* Now we have handled acceptable kinds of LHS that are not truly lvalues.
Reject anything strange now. */
if (!lvalue_or_else (lhs, "invalid lvalue in assignment"))
return error_mark_node;
/* Warn about storing in something that is `const'. */
if (TREE_READONLY (lhs) || TYPE_READONLY (lhstype)
|| ((TREE_CODE (lhstype) == RECORD_TYPE
|| TREE_CODE (lhstype) == UNION_TYPE)
&& C_TYPE_FIELDS_READONLY (lhstype)))
readonly_warning (lhs, "assignment");
/* If storing into a structure or union member,
it has probably been given type `int'.
Compute the type that would go with
the actual amount of storage the member occupies. */
if (TREE_CODE (lhs) == COMPONENT_REF
&& (TREE_CODE (lhstype) == INTEGER_TYPE
|| TREE_CODE (lhstype) == REAL_TYPE
|| TREE_CODE (lhstype) == ENUMERAL_TYPE))
lhstype = TREE_TYPE (get_unwidened (lhs, 0));
/* If storing in a field that is in actuality a short or narrower than one,
we must store in the field in its actual type. */
if (lhstype != TREE_TYPE (lhs))
{
lhs = copy_node (lhs);
TREE_TYPE (lhs) = lhstype;
}
/* Convert new value to destination type. */
newrhs = convert_for_assignment (lhstype, newrhs, _("assignment"),
NULL_TREE, NULL_TREE, 0);
if (TREE_CODE (newrhs) == ERROR_MARK)
return error_mark_node;
result = build (MODIFY_EXPR, lhstype, lhs, newrhs);
TREE_SIDE_EFFECTS (result) = 1;
/* If we got the LHS in a different type for storing in,
convert the result back to the nominal type of LHS
so that the value we return always has the same type
as the LHS argument. */
if (olhstype == TREE_TYPE (result))
return result;
return convert_for_assignment (olhstype, result, _("assignment"),
NULL_TREE, NULL_TREE, 0);
}
/* Convert value RHS to type TYPE as preparation for an assignment
to an lvalue of type TYPE.
The real work of conversion is done by `convert'.
The purpose of this function is to generate error messages
for assignments that are not allowed in C.
ERRTYPE is a string to use in error messages:
"assignment", "return", etc. If it is null, this is parameter passing
for a function call (and different error messages are output).
FUNNAME is the name of the function being called,
as an IDENTIFIER_NODE, or null.
PARMNUM is the number of the argument, for printing in error messages. */
static tree
convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum)
tree type, rhs;
const char *errtype;
tree fundecl, funname;
int parmnum;
{
register enum tree_code codel = TREE_CODE (type);
register tree rhstype;
register enum tree_code coder;
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
/* Do not use STRIP_NOPS here. We do not want an enumerator
whose value is 0 to count as a null pointer constant. */
if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
rhs = TREE_OPERAND (rhs, 0);
if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE)
rhs = default_conversion (rhs);
else if (optimize && TREE_CODE (rhs) == VAR_DECL)
rhs = decl_constant_value (rhs);
rhstype = TREE_TYPE (rhs);
coder = TREE_CODE (rhstype);
if (coder == ERROR_MARK)
return error_mark_node;
if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype))
{
overflow_warning (rhs);
/* Check for Objective-C protocols. This will issue a warning if
there are protocol violations. No need to use the return value. */
maybe_objc_comptypes (type, rhstype, 0);
return rhs;
}
if (coder == VOID_TYPE)
{
error ("void value not ignored as it ought to be");
return error_mark_node;
}
/* Arithmetic types all interconvert, and enum is treated like int. */
if ((codel == INTEGER_TYPE || codel == REAL_TYPE || codel == ENUMERAL_TYPE
|| codel == COMPLEX_TYPE)
&& (coder == INTEGER_TYPE || coder == REAL_TYPE || coder == ENUMERAL_TYPE
|| coder == COMPLEX_TYPE))
return convert_and_check (type, rhs);
/* Conversion to a transparent union from its member types.
This applies only to function arguments. */
else if (codel == UNION_TYPE && TYPE_TRANSPARENT_UNION (type) && ! errtype)
{
tree memb_types;
tree marginal_memb_type = 0;
for (memb_types = TYPE_FIELDS (type); memb_types;
memb_types = TREE_CHAIN (memb_types))
{
tree memb_type = TREE_TYPE (memb_types);
if (comptypes (TYPE_MAIN_VARIANT (memb_type),
TYPE_MAIN_VARIANT (rhstype)))
break;
if (TREE_CODE (memb_type) != POINTER_TYPE)
continue;
if (coder == POINTER_TYPE)
{
register tree ttl = TREE_TYPE (memb_type);
register tree ttr = TREE_TYPE (rhstype);
/* Any non-function converts to a [const][volatile] void *
and vice versa; otherwise, targets must be the same.
Meanwhile, the lhs target must have all the qualifiers of
the rhs. */
if (TYPE_MAIN_VARIANT (ttl) == void_type_node
|| TYPE_MAIN_VARIANT (ttr) == void_type_node
|| comp_target_types (memb_type, rhstype))
{
/* If this type won't generate any warnings, use it. */
if (TYPE_QUALS (ttl) == TYPE_QUALS (ttr)
|| ((TREE_CODE (ttr) == FUNCTION_TYPE
&& TREE_CODE (ttl) == FUNCTION_TYPE)
? ((TYPE_QUALS (ttl) | TYPE_QUALS (ttr))
== TYPE_QUALS (ttr))
: ((TYPE_QUALS (ttl) | TYPE_QUALS (ttr))
== TYPE_QUALS (ttl))))
break;
/* Keep looking for a better type, but remember this one. */
if (! marginal_memb_type)
marginal_memb_type = memb_type;
}
}
/* Can convert integer zero to any pointer type. */
if (integer_zerop (rhs)
|| (TREE_CODE (rhs) == NOP_EXPR
&& integer_zerop (TREE_OPERAND (rhs, 0))))
{
rhs = null_pointer_node;
break;
}
}
if (memb_types || marginal_memb_type)
{
if (! memb_types)
{
/* We have only a marginally acceptable member type;
it needs a warning. */
register tree ttl = TREE_TYPE (marginal_memb_type);
register tree ttr = TREE_TYPE (rhstype);
/* Const and volatile mean something different for function
types, so the usual warnings are not appropriate. */
if (TREE_CODE (ttr) == FUNCTION_TYPE
&& TREE_CODE (ttl) == FUNCTION_TYPE)
{
/* Because const and volatile on functions are
restrictions that say the function will not do
certain things, it is okay to use a const or volatile
function where an ordinary one is wanted, but not
vice-versa. */
if (TYPE_QUALS (ttl) & ~TYPE_QUALS (ttr))
warn_for_assignment ("%s makes qualified function pointer from unqualified",
errtype, funname, parmnum);
}
else if (TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl))
warn_for_assignment ("%s discards qualifiers from pointer target type",
errtype, funname,
parmnum);
}
if (pedantic && ! DECL_IN_SYSTEM_HEADER (fundecl))
pedwarn ("ANSI C prohibits argument conversion to union type");
return build1 (NOP_EXPR, type, rhs);
}
}
/* Conversions among pointers */
else if (codel == POINTER_TYPE && coder == POINTER_TYPE)
{
register tree ttl = TREE_TYPE (type);
register tree ttr = TREE_TYPE (rhstype);
/* Any non-function converts to a [const][volatile] void *
and vice versa; otherwise, targets must be the same.
Meanwhile, the lhs target must have all the qualifiers of the rhs. */
if (TYPE_MAIN_VARIANT (ttl) == void_type_node
|| TYPE_MAIN_VARIANT (ttr) == void_type_node
|| comp_target_types (type, rhstype)
|| (unsigned_type (TYPE_MAIN_VARIANT (ttl))
== unsigned_type (TYPE_MAIN_VARIANT (ttr))))
{
if (pedantic
&& ((TYPE_MAIN_VARIANT (ttl) == void_type_node
&& TREE_CODE (ttr) == FUNCTION_TYPE)
||
(TYPE_MAIN_VARIANT (ttr) == void_type_node
/* Check TREE_CODE to catch cases like (void *) (char *) 0
which are not ANSI null ptr constants. */
&& (!integer_zerop (rhs) || TREE_CODE (rhs) == NOP_EXPR)
&& TREE_CODE (ttl) == FUNCTION_TYPE)))
warn_for_assignment ("ANSI forbids %s between function pointer and `void *'",
errtype, funname, parmnum);
/* Const and volatile mean something different for function types,
so the usual warnings are not appropriate. */
else if (TREE_CODE (ttr) != FUNCTION_TYPE
&& TREE_CODE (ttl) != FUNCTION_TYPE)
{
if (TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl))
warn_for_assignment ("%s discards qualifiers from pointer target type",
errtype, funname, parmnum);
/* If this is not a case of ignoring a mismatch in signedness,
no warning. */
else if (TYPE_MAIN_VARIANT (ttl) == void_type_node
|| TYPE_MAIN_VARIANT (ttr) == void_type_node
|| comp_target_types (type, rhstype))
;
/* If there is a mismatch, do warn. */
else if (pedantic)
warn_for_assignment ("pointer targets in %s differ in signedness",
errtype, funname, parmnum);
}
else if (TREE_CODE (ttl) == FUNCTION_TYPE
&& TREE_CODE (ttr) == FUNCTION_TYPE)
{
/* Because const and volatile on functions are restrictions
that say the function will not do certain things,
it is okay to use a const or volatile function
where an ordinary one is wanted, but not vice-versa. */
if (TYPE_QUALS (ttl) & ~TYPE_QUALS (ttr))
warn_for_assignment ("%s makes qualified function pointer from unqualified",
errtype, funname, parmnum);
}
}
else
warn_for_assignment ("%s from incompatible pointer type",
errtype, funname, parmnum);
return convert (type, rhs);
}
else if (codel == POINTER_TYPE && coder == INTEGER_TYPE)
{
/* An explicit constant 0 can convert to a pointer,
or one that results from arithmetic, even including
a cast to integer type. */
if (! (TREE_CODE (rhs) == INTEGER_CST && integer_zerop (rhs))
&&
! (TREE_CODE (rhs) == NOP_EXPR
&& TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == INTEGER_CST
&& integer_zerop (TREE_OPERAND (rhs, 0))))
{
warn_for_assignment ("%s makes pointer from integer without a cast",
errtype, funname, parmnum);
return convert (type, rhs);
}
return null_pointer_node;
}
else if (codel == INTEGER_TYPE && coder == POINTER_TYPE)
{
warn_for_assignment ("%s makes integer from pointer without a cast",
errtype, funname, parmnum);
return convert (type, rhs);
}
if (!errtype)
{
if (funname)
{
tree selector = maybe_building_objc_message_expr ();
if (selector && parmnum > 2)
error ("incompatible type for argument %d of `%s'",
parmnum - 2, IDENTIFIER_POINTER (selector));
else
error ("incompatible type for argument %d of `%s'",
parmnum, IDENTIFIER_POINTER (funname));
}
else
error ("incompatible type for argument %d of indirect function call",
parmnum);
}
else
error ("incompatible types in %s", errtype);
return error_mark_node;
}
/* Print a warning using MSGID.
It gets OPNAME as its one parameter.
If OPNAME is null, it is replaced by "passing arg ARGNUM of `FUNCTION'".
FUNCTION and ARGNUM are handled specially if we are building an
Objective-C selector. */
static void
warn_for_assignment (msgid, opname, function, argnum)
const char *msgid;
const char *opname;
tree function;
int argnum;
{
if (opname == 0)
{
tree selector = maybe_building_objc_message_expr ();
char * new_opname;
if (selector && argnum > 2)
{
function = selector;
argnum -= 2;
}
if (function)
{
/* Function name is known; supply it. */
const char *argstring = _("passing arg %d of `%s'");
new_opname = (char *) alloca (IDENTIFIER_LENGTH (function)
+ strlen (argstring) + 1 + 25
/*%d*/ + 1);
sprintf (new_opname, argstring, argnum,
IDENTIFIER_POINTER (function));
}
else
{
/* Function name unknown (call through ptr); just give arg number.*/
const char *argnofun = _("passing arg %d of pointer to function");
new_opname = (char *) alloca (strlen (argnofun) + 1 + 25 /*%d*/ + 1);
sprintf (new_opname, argnofun, argnum);
}
opname = new_opname;
}
pedwarn (msgid, opname);
}
/* Return nonzero if VALUE is a valid constant-valued expression
for use in initializing a static variable; one that can be an
element of a "constant" initializer.
Return null_pointer_node if the value is absolute;
if it is relocatable, return the variable that determines the relocation.
We assume that VALUE has been folded as much as possible;
therefore, we do not need to check for such things as
arithmetic-combinations of integers. */
tree
initializer_constant_valid_p (value, endtype)
tree value;
tree endtype;
{
switch (TREE_CODE (value))
{
case CONSTRUCTOR:
if ((TREE_CODE (TREE_TYPE (value)) == UNION_TYPE
|| TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE)
&& TREE_CONSTANT (value)
&& CONSTRUCTOR_ELTS (value))
return
initializer_constant_valid_p (TREE_VALUE (CONSTRUCTOR_ELTS (value)),
endtype);
return TREE_STATIC (value) ? null_pointer_node : 0;
case INTEGER_CST:
case REAL_CST:
case STRING_CST:
case COMPLEX_CST:
return null_pointer_node;
case ADDR_EXPR:
return TREE_OPERAND (value, 0);
case NON_LVALUE_EXPR:
return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
case CONVERT_EXPR:
case NOP_EXPR:
/* Allow conversions between pointer types. */
if (TREE_CODE (TREE_TYPE (value)) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE)
return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
/* Allow conversions between real types. */
if (TREE_CODE (TREE_TYPE (value)) == REAL_TYPE
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == REAL_TYPE)
return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
/* Allow length-preserving conversions between integer types. */
if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE
&& (TYPE_PRECISION (TREE_TYPE (value))
== TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
/* Allow conversions between other integer types only if
explicit value. */
if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE)
{
tree inner = initializer_constant_valid_p (TREE_OPERAND (value, 0),
endtype);
if (inner == null_pointer_node)
return null_pointer_node;
return 0;
}
/* Allow (int) &foo provided int is as wide as a pointer. */
if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE
&& (TYPE_PRECISION (TREE_TYPE (value))
>= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
return initializer_constant_valid_p (TREE_OPERAND (value, 0),
endtype);
/* Likewise conversions from int to pointers, but also allow
conversions from 0. */
if (TREE_CODE (TREE_TYPE (value)) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE)
{
if (integer_zerop (TREE_OPERAND (value, 0)))
return null_pointer_node;
else if (TYPE_PRECISION (TREE_TYPE (value))
<= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0))))
return initializer_constant_valid_p (TREE_OPERAND (value, 0),
endtype);
}
/* Allow conversions to union types if the value inside is okay. */
if (TREE_CODE (TREE_TYPE (value)) == UNION_TYPE)
return initializer_constant_valid_p (TREE_OPERAND (value, 0),
endtype);
return 0;
case PLUS_EXPR:
if (TREE_CODE (endtype) == INTEGER_TYPE
&& TYPE_PRECISION (endtype) < POINTER_SIZE)
return 0;
{
tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
endtype);
tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
endtype);
/* If either term is absolute, use the other terms relocation. */
if (valid0 == null_pointer_node)
return valid1;
if (valid1 == null_pointer_node)
return valid0;
return 0;
}
case MINUS_EXPR:
if (TREE_CODE (endtype) == INTEGER_TYPE
&& TYPE_PRECISION (endtype) < POINTER_SIZE)
return 0;
{
tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
endtype);
tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
endtype);
/* Win if second argument is absolute. */
if (valid1 == null_pointer_node)
return valid0;
/* Win if both arguments have the same relocation.
Then the value is absolute. */
if (valid0 == valid1)
return null_pointer_node;
return 0;
}
default:
return 0;
}
}
/* If VALUE is a compound expr all of whose expressions are constant, then
return its value. Otherwise, return error_mark_node.
This is for handling COMPOUND_EXPRs as initializer elements
which is allowed with a warning when -pedantic is specified. */
static tree
valid_compound_expr_initializer (value, endtype)
tree value;
tree endtype;
{
if (TREE_CODE (value) == COMPOUND_EXPR)
{
if (valid_compound_expr_initializer (TREE_OPERAND (value, 0), endtype)
== error_mark_node)
return error_mark_node;
return valid_compound_expr_initializer (TREE_OPERAND (value, 1),
endtype);
}
else if (! TREE_CONSTANT (value)
&& ! initializer_constant_valid_p (value, endtype))
return error_mark_node;
else
return value;
}
/* Perform appropriate conversions on the initial value of a variable,
store it in the declaration DECL,
and print any error messages that are appropriate.
If the init is invalid, store an ERROR_MARK. */
void
store_init_value (decl, init)
tree decl, init;
{
register tree value, type;
/* If variable's type was invalidly declared, just ignore it. */
type = TREE_TYPE (decl);
if (TREE_CODE (type) == ERROR_MARK)
return;
/* Digest the specified initializer into an expression. */
value = digest_init (type, init, TREE_STATIC (decl),
TREE_STATIC (decl) || pedantic);
/* Store the expression if valid; else report error. */
#if 0
/* Note that this is the only place we can detect the error
in a case such as struct foo bar = (struct foo) { x, y };
where there is one initial value which is a constructor expression. */
if (value == error_mark_node)
;
else if (TREE_STATIC (decl) && ! TREE_CONSTANT (value))
{
error ("initializer for static variable is not constant");
value = error_mark_node;
}
else if (TREE_STATIC (decl)
&& initializer_constant_valid_p (value, TREE_TYPE (value)) == 0)
{
error ("initializer for static variable uses complicated arithmetic");
value = error_mark_node;
}
else
{
if (pedantic && TREE_CODE (value) == CONSTRUCTOR)
{
if (! TREE_CONSTANT (value))
pedwarn ("aggregate initializer is not constant");
else if (! TREE_STATIC (value))
pedwarn ("aggregate initializer uses complicated arithmetic");
}
}
#endif
DECL_INITIAL (decl) = value;
/* ANSI wants warnings about out-of-range constant initializers. */
STRIP_TYPE_NOPS (value);
constant_expression_warning (value);
}
/* Methods for storing and printing names for error messages. */
/* Implement a spelling stack that allows components of a name to be pushed
and popped. Each element on the stack is this structure. */
struct spelling
{
int kind;
union
{
int i;
const char *s;
} u;
};
#define SPELLING_STRING 1
#define SPELLING_MEMBER 2
#define SPELLING_BOUNDS 3
static struct spelling *spelling; /* Next stack element (unused). */
static struct spelling *spelling_base; /* Spelling stack base. */
static int spelling_size; /* Size of the spelling stack. */
/* Macros to save and restore the spelling stack around push_... functions.
Alternative to SAVE_SPELLING_STACK. */
#define SPELLING_DEPTH() (spelling - spelling_base)
#define RESTORE_SPELLING_DEPTH(depth) (spelling = spelling_base + depth)
/* Save and restore the spelling stack around arbitrary C code. */
#define SAVE_SPELLING_DEPTH(code) \
{ \
int __depth = SPELLING_DEPTH (); \
code; \
RESTORE_SPELLING_DEPTH (__depth); \
}
/* Push an element on the spelling stack with type KIND and assign VALUE
to MEMBER. */
#define PUSH_SPELLING(KIND, VALUE, MEMBER) \
{ \
int depth = SPELLING_DEPTH (); \
\
if (depth >= spelling_size) \
{ \
spelling_size += 10; \
if (spelling_base == 0) \
spelling_base \
= (struct spelling *) xmalloc (spelling_size * sizeof (struct spelling)); \
else \
spelling_base \
= (struct spelling *) xrealloc (spelling_base, \
spelling_size * sizeof (struct spelling)); \
RESTORE_SPELLING_DEPTH (depth); \
} \
\
spelling->kind = (KIND); \
spelling->MEMBER = (VALUE); \
spelling++; \
}
/* Push STRING on the stack. Printed literally. */
static void
push_string (string)
const char *string;
{
PUSH_SPELLING (SPELLING_STRING, string, u.s);
}
/* Push a member name on the stack. Printed as '.' STRING. */
static void
push_member_name (decl)
tree decl;
{
const char *string
= DECL_NAME (decl) ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<anonymous>";
PUSH_SPELLING (SPELLING_MEMBER, string, u.s);
}
/* Push an array bounds on the stack. Printed as [BOUNDS]. */
static void
push_array_bounds (bounds)
int bounds;
{
PUSH_SPELLING (SPELLING_BOUNDS, bounds, u.i);
}
/* Compute the maximum size in bytes of the printed spelling. */
static int
spelling_length ()
{
register int size = 0;
register struct spelling *p;
for (p = spelling_base; p < spelling; p++)
{
if (p->kind == SPELLING_BOUNDS)
size += 25;
else
size += strlen (p->u.s) + 1;
}
return size;
}
/* Print the spelling to BUFFER and return it. */
static char *
print_spelling (buffer)
register char *buffer;
{
register char *d = buffer;
register struct spelling *p;
for (p = spelling_base; p < spelling; p++)
if (p->kind == SPELLING_BOUNDS)
{
sprintf (d, "[%d]", p->u.i);
d += strlen (d);
}
else
{
register const char *s;
if (p->kind == SPELLING_MEMBER)
*d++ = '.';
for (s = p->u.s; (*d = *s++); d++)
;
}
*d++ = '\0';
return buffer;
}
/* Issue an error message for a bad initializer component.
MSGID identifies the message.
The component name is taken from the spelling stack. */
void
error_init (msgid)
const char *msgid;
{
char *ofwhat;
error (msgid);
ofwhat = print_spelling ((char *) alloca (spelling_length () + 1));
if (*ofwhat)
error ("(near initialization for `%s')", ofwhat);
}
/* Issue a pedantic warning for a bad initializer component.
MSGID identifies the message.
The component name is taken from the spelling stack. */
void
pedwarn_init (msgid)
const char *msgid;
{
char *ofwhat;
pedwarn (msgid);
ofwhat = print_spelling ((char *) alloca (spelling_length () + 1));
if (*ofwhat)
pedwarn ("(near initialization for `%s')", ofwhat);
}
/* Issue a warning for a bad initializer component.
MSGID identifies the message.
The component name is taken from the spelling stack. */
static void
warning_init (msgid)
const char *msgid;
{
char *ofwhat;
warning (msgid);
ofwhat = print_spelling ((char *) alloca (spelling_length () + 1));
if (*ofwhat)
warning ("(near initialization for `%s')", ofwhat);
}
/* Digest the parser output INIT as an initializer for type TYPE.
Return a C expression of type TYPE to represent the initial value.
The arguments REQUIRE_CONSTANT and CONSTRUCTOR_CONSTANT request errors
if non-constant initializers or elements are seen. CONSTRUCTOR_CONSTANT
applies only to elements of constructors. */
static tree
digest_init (type, init, require_constant, constructor_constant)
tree type, init;
int require_constant, constructor_constant;
{
enum tree_code code = TREE_CODE (type);
tree inside_init = init;
if (init == error_mark_node)
return init;
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
/* Do not use STRIP_NOPS here. We do not want an enumerator
whose value is 0 to count as a null pointer constant. */
if (TREE_CODE (init) == NON_LVALUE_EXPR)
inside_init = TREE_OPERAND (init, 0);
/* Initialization of an array of chars from a string constant
optionally enclosed in braces. */
if (code == ARRAY_TYPE)
{
tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type));
if ((typ1 == char_type_node
|| typ1 == signed_char_type_node
|| typ1 == unsigned_char_type_node
|| typ1 == unsigned_wchar_type_node
|| typ1 == signed_wchar_type_node)
&& ((inside_init && TREE_CODE (inside_init) == STRING_CST)))
{
if (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (inside_init)),
TYPE_MAIN_VARIANT (type)))
return inside_init;
if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (inside_init)))
!= char_type_node)
&& TYPE_PRECISION (typ1) == TYPE_PRECISION (char_type_node))
{
error_init ("char-array initialized from wide string");
return error_mark_node;
}
if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (inside_init)))
== char_type_node)
&& TYPE_PRECISION (typ1) != TYPE_PRECISION (char_type_node))
{
error_init ("int-array initialized from non-wide string");
return error_mark_node;
}
TREE_TYPE (inside_init) = type;
if (TYPE_DOMAIN (type) != 0
&& TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
{
register int size = TREE_INT_CST_LOW (TYPE_SIZE (type));
size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
/* Subtract 1 (or sizeof (wchar_t))
because it's ok to ignore the terminating null char
that is counted in the length of the constant. */
if (size < TREE_STRING_LENGTH (inside_init)
- (TYPE_PRECISION (typ1) != TYPE_PRECISION (char_type_node)
? TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT
: 1))
pedwarn_init ("initializer-string for array of chars is too long");
}
return inside_init;
}
}
/* Any type can be initialized
from an expression of the same type, optionally with braces. */
if (inside_init && TREE_TYPE (inside_init) != 0
&& (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (inside_init)),
TYPE_MAIN_VARIANT (type))
|| (code == ARRAY_TYPE
&& comptypes (TREE_TYPE (inside_init), type))
|| (code == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (inside_init)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (inside_init)) == FUNCTION_TYPE)
&& comptypes (TREE_TYPE (TREE_TYPE (inside_init)),
TREE_TYPE (type)))))
{
if (code == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (inside_init)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (inside_init)) == FUNCTION_TYPE))
inside_init = default_conversion (inside_init);
else if (code == ARRAY_TYPE && TREE_CODE (inside_init) != STRING_CST
&& TREE_CODE (inside_init) != CONSTRUCTOR)
{
error_init ("array initialized from non-constant array expression");
return error_mark_node;
}
if (optimize && TREE_CODE (inside_init) == VAR_DECL)
inside_init = decl_constant_value (inside_init);
/* Compound expressions can only occur here if -pedantic or
-pedantic-errors is specified. In the later case, we always want
an error. In the former case, we simply want a warning. */
if (require_constant && pedantic
&& TREE_CODE (inside_init) == COMPOUND_EXPR)
{
inside_init
= valid_compound_expr_initializer (inside_init,
TREE_TYPE (inside_init));
if (inside_init == error_mark_node)
error_init ("initializer element is not constant");
else
pedwarn_init ("initializer element is not constant");
if (flag_pedantic_errors)
inside_init = error_mark_node;
}
else if (require_constant && ! TREE_CONSTANT (inside_init))
{
error_init ("initializer element is not constant");
inside_init = error_mark_node;
}
else if (require_constant
&& initializer_constant_valid_p (inside_init, TREE_TYPE (inside_init)) == 0)
{
error_init ("initializer element is not computable at load time");
inside_init = error_mark_node;
}
return inside_init;
}
/* Handle scalar types, including conversions. */
if (code == INTEGER_TYPE || code == REAL_TYPE || code == POINTER_TYPE
|| code == ENUMERAL_TYPE || code == COMPLEX_TYPE)
{
/* Note that convert_for_assignment calls default_conversion
for arrays and functions. We must not call it in the
case where inside_init is a null pointer constant. */
inside_init
= convert_for_assignment (type, init, _("initialization"),
NULL_TREE, NULL_TREE, 0);
if (require_constant && ! TREE_CONSTANT (inside_init))
{
error_init ("initializer element is not constant");
inside_init = error_mark_node;
}
else if (require_constant
&& initializer_constant_valid_p (inside_init, TREE_TYPE (inside_init)) == 0)
{
error_init ("initializer element is not computable at load time");
inside_init = error_mark_node;
}
return inside_init;
}
/* Come here only for records and arrays. */
if (TYPE_SIZE (type) && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
{
error_init ("variable-sized object may not be initialized");
return error_mark_node;
}
/* Traditionally, you can write struct foo x = 0;
and it initializes the first element of x to 0. */
if (flag_traditional)
{
tree top = 0, prev = 0, otype = type;
while (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == ARRAY_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE
|| TREE_CODE (type) == UNION_TYPE)
{
tree temp = build (CONSTRUCTOR, type, NULL_TREE, NULL_TREE);
if (prev == 0)
top = temp;
else
TREE_OPERAND (prev, 1) = build_tree_list (NULL_TREE, temp);
prev = temp;
if (TREE_CODE (type) == ARRAY_TYPE)
type = TREE_TYPE (type);
else if (TYPE_FIELDS (type))
type = TREE_TYPE (TYPE_FIELDS (type));
else
{
error_init ("invalid initializer");
return error_mark_node;
}
}
if (otype != type)
{
TREE_OPERAND (prev, 1)
= build_tree_list (NULL_TREE,
digest_init (type, init, require_constant,
constructor_constant));
return top;
}
else
return error_mark_node;
}
error_init ("invalid initializer");
return error_mark_node;
}
/* Handle initializers that use braces. */
/* Type of object we are accumulating a constructor for.
This type is always a RECORD_TYPE, UNION_TYPE or ARRAY_TYPE. */
static tree constructor_type;
/* For a RECORD_TYPE or UNION_TYPE, this is the chain of fields
left to fill. */
static tree constructor_fields;
/* For an ARRAY_TYPE, this is the specified index
at which to store the next element we get.
This is a special INTEGER_CST node that we modify in place. */
static tree constructor_index;
/* For an ARRAY_TYPE, this is the end index of the range
to initialize with the next element, or NULL in the ordinary case
where the element is used just once. */
static tree constructor_range_end;
/* For an ARRAY_TYPE, this is the maximum index. */
static tree constructor_max_index;
/* For a RECORD_TYPE, this is the first field not yet written out. */
static tree constructor_unfilled_fields;
/* For an ARRAY_TYPE, this is the index of the first element
not yet written out.
This is a special INTEGER_CST node that we modify in place. */
static tree constructor_unfilled_index;
/* In a RECORD_TYPE, the byte index of the next consecutive field.
This is so we can generate gaps between fields, when appropriate.
This is a special INTEGER_CST node that we modify in place. */
static tree constructor_bit_index;
/* If we are saving up the elements rather than allocating them,
this is the list of elements so far (in reverse order,
most recent first). */
static tree constructor_elements;
/* 1 if so far this constructor's elements are all compile-time constants. */
static int constructor_constant;
/* 1 if so far this constructor's elements are all valid address constants. */
static int constructor_simple;
/* 1 if this constructor is erroneous so far. */
static int constructor_erroneous;
/* 1 if have called defer_addressed_constants. */
static int constructor_subconstants_deferred;
/* Structure for managing pending initializer elements, organized as an
AVL tree. */
struct init_node
{
struct init_node *left, *right;
struct init_node *parent;
int balance;
tree purpose;
tree value;
};
/* Tree of pending elements at this constructor level.
These are elements encountered out of order
which belong at places we haven't reached yet in actually
writing the output. */
static struct init_node *constructor_pending_elts;
/* The SPELLING_DEPTH of this constructor. */
static int constructor_depth;
/* 0 if implicitly pushing constructor levels is allowed. */
int constructor_no_implicit = 0; /* 0 for C; 1 for some other languages. */
static int require_constant_value;
static int require_constant_elements;
/* 1 if it is ok to output this constructor as we read it.
0 means must accumulate a CONSTRUCTOR expression. */
static int constructor_incremental;
/* DECL node for which an initializer is being read.
0 means we are reading a constructor expression
such as (struct foo) {...}. */
static tree constructor_decl;
/* start_init saves the ASMSPEC arg here for really_start_incremental_init. */
static char *constructor_asmspec;
/* Nonzero if this is an initializer for a top-level decl. */
static int constructor_top_level;
/* This stack has a level for each implicit or explicit level of
structuring in the initializer, including the outermost one. It
saves the values of most of the variables above. */
struct constructor_stack
{
struct constructor_stack *next;
tree type;
tree fields;
tree index;
tree range_end;
tree max_index;
tree unfilled_index;
tree unfilled_fields;
tree bit_index;
tree elements;
int offset;
struct init_node *pending_elts;
int depth;
/* If nonzero, this value should replace the entire
constructor at this level. */
tree replacement_value;
char constant;
char simple;
char implicit;
char incremental;
char erroneous;
char outer;
};
struct constructor_stack *constructor_stack;
/* This stack records separate initializers that are nested.
Nested initializers can't happen in ANSI C, but GNU C allows them
in cases like { ... (struct foo) { ... } ... }. */
struct initializer_stack
{
struct initializer_stack *next;
tree decl;
char *asmspec;
struct constructor_stack *constructor_stack;
tree elements;
struct spelling *spelling;
struct spelling *spelling_base;
int spelling_size;
char top_level;
char incremental;
char require_constant_value;
char require_constant_elements;
char deferred;
};
struct initializer_stack *initializer_stack;
/* Prepare to parse and output the initializer for variable DECL. */
void
start_init (decl, asmspec_tree, top_level)
tree decl;
tree asmspec_tree;
int top_level;
{
const char *locus;
struct initializer_stack *p
= (struct initializer_stack *) xmalloc (sizeof (struct initializer_stack));
char *asmspec = 0;
if (asmspec_tree)
asmspec = TREE_STRING_POINTER (asmspec_tree);
p->decl = constructor_decl;
p->asmspec = constructor_asmspec;
p->incremental = constructor_incremental;
p->require_constant_value = require_constant_value;
p->require_constant_elements = require_constant_elements;
p->constructor_stack = constructor_stack;
p->elements = constructor_elements;
p->spelling = spelling;
p->spelling_base = spelling_base;
p->spelling_size = spelling_size;
p->deferred = constructor_subconstants_deferred;
p->top_level = constructor_top_level;
p->next = initializer_stack;
initializer_stack = p;
constructor_decl = decl;
constructor_incremental = top_level;
constructor_asmspec = asmspec;
constructor_subconstants_deferred = 0;
constructor_top_level = top_level;
if (decl != 0)
{
require_constant_value = TREE_STATIC (decl);
require_constant_elements
= ((TREE_STATIC (decl) || pedantic)
/* For a scalar, you can always use any value to initialize,
even within braces. */
&& (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
|| TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE
|| TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE));
locus = IDENTIFIER_POINTER (DECL_NAME (decl));
constructor_incremental |= TREE_STATIC (decl);
}
else
{
require_constant_value = 0;
require_constant_elements = 0;
locus = "(anonymous)";
}
constructor_stack = 0;
missing_braces_mentioned = 0;
spelling_base = 0;
spelling_size = 0;
RESTORE_SPELLING_DEPTH (0);
if (locus)
push_string (locus);
}
void
finish_init ()
{
struct initializer_stack *p = initializer_stack;
/* Output subconstants (string constants, usually)
that were referenced within this initializer and saved up.
Must do this if and only if we called defer_addressed_constants. */
if (constructor_subconstants_deferred)
output_deferred_addressed_constants ();
/* Free the whole constructor stack of this initializer. */
while (constructor_stack)
{
struct constructor_stack *q = constructor_stack;
constructor_stack = q->next;
free (q);
}
/* Pop back to the data of the outer initializer (if any). */
constructor_decl = p->decl;
constructor_asmspec = p->asmspec;
constructor_incremental = p->incremental;
require_constant_value = p->require_constant_value;
require_constant_elements = p->require_constant_elements;
constructor_stack = p->constructor_stack;
constructor_elements = p->elements;
spelling = p->spelling;
spelling_base = p->spelling_base;
spelling_size = p->spelling_size;
constructor_subconstants_deferred = p->deferred;
constructor_top_level = p->top_level;
initializer_stack = p->next;
free (p);
}
/* Call here when we see the initializer is surrounded by braces.
This is instead of a call to push_init_level;
it is matched by a call to pop_init_level.
TYPE is the type to initialize, for a constructor expression.
For an initializer for a decl, TYPE is zero. */
void
really_start_incremental_init (type)
tree type;
{
struct constructor_stack *p
= (struct constructor_stack *) xmalloc (sizeof (struct constructor_stack));
if (type == 0)
type = TREE_TYPE (constructor_decl);
/* Turn off constructor_incremental if type is a struct with bitfields.
Do this before the first push, so that the corrected value
is available in finish_init. */
check_init_type_bitfields (type);
p->type = constructor_type;
p->fields = constructor_fields;
p->index = constructor_index;
p->range_end = constructor_range_end;
p->max_index = constructor_max_index;
p->unfilled_index = constructor_unfilled_index;
p->unfilled_fields = constructor_unfilled_fields;
p->bit_index = constructor_bit_index;
p->elements = constructor_elements;
p->constant = constructor_constant;
p->simple = constructor_simple;
p->erroneous = constructor_erroneous;
p->pending_elts = constructor_pending_elts;
p->depth = constructor_depth;
p->replacement_value = 0;
p->implicit = 0;
p->incremental = constructor_incremental;
p->outer = 0;
p->next = 0;
constructor_stack = p;
constructor_constant = 1;
constructor_simple = 1;
constructor_depth = SPELLING_DEPTH ();
constructor_elements = 0;
constructor_pending_elts = 0;
constructor_type = type;
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
constructor_fields = TYPE_FIELDS (constructor_type);
/* Skip any nameless bit fields at the beginning. */
while (constructor_fields != 0 && DECL_C_BIT_FIELD (constructor_fields)
&& DECL_NAME (constructor_fields) == 0)
constructor_fields = TREE_CHAIN (constructor_fields);
constructor_unfilled_fields = constructor_fields;
constructor_bit_index = copy_node (integer_zero_node);
TREE_TYPE (constructor_bit_index) = sbitsizetype;
}
else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
constructor_range_end = 0;
if (TYPE_DOMAIN (constructor_type))
{
constructor_max_index
= TYPE_MAX_VALUE (TYPE_DOMAIN (constructor_type));
constructor_index
= copy_node (TYPE_MIN_VALUE (TYPE_DOMAIN (constructor_type)));
}
else
constructor_index = copy_node (integer_zero_node);
constructor_unfilled_index = copy_node (constructor_index);
}
else
{
/* Handle the case of int x = {5}; */
constructor_fields = constructor_type;
constructor_unfilled_fields = constructor_type;
}
if (constructor_incremental)
{
int momentary = suspend_momentary ();
push_obstacks_nochange ();
if (TREE_PERMANENT (constructor_decl))
end_temporary_allocation ();
make_decl_rtl (constructor_decl, constructor_asmspec,
constructor_top_level);
assemble_variable (constructor_decl, constructor_top_level, 0, 1);
pop_obstacks ();
resume_momentary (momentary);
}
if (constructor_incremental)
{
defer_addressed_constants ();
constructor_subconstants_deferred = 1;
}
}
/* Push down into a subobject, for initialization.
If this is for an explicit set of braces, IMPLICIT is 0.
If it is because the next element belongs at a lower level,
IMPLICIT is 1. */
void
push_init_level (implicit)
int implicit;
{
struct constructor_stack *p;
/* If we've exhausted any levels that didn't have braces,
pop them now. */
while (constructor_stack->implicit)
{
if ((TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
&& constructor_fields == 0)
process_init_element (pop_init_level (1));
else if (TREE_CODE (constructor_type) == ARRAY_TYPE
&& tree_int_cst_lt (constructor_max_index, constructor_index))
process_init_element (pop_init_level (1));
else
break;
}
/* Structure elements may require alignment. Do this now if necessary
for the subaggregate, and if it comes next in sequence. Don't do
this for subaggregates that will go on the pending list. */
if (constructor_incremental && constructor_type != 0
&& TREE_CODE (constructor_type) == RECORD_TYPE && constructor_fields
&& constructor_fields == constructor_unfilled_fields)
{
/* Advance to offset of this element. */
if (! tree_int_cst_equal (constructor_bit_index,
DECL_FIELD_BITPOS (constructor_fields)))
{
/* By using unsigned arithmetic, the result will be correct even
in case of overflows, if BITS_PER_UNIT is a power of two. */
unsigned next = (TREE_INT_CST_LOW
(DECL_FIELD_BITPOS (constructor_fields))
/ (unsigned)BITS_PER_UNIT);
unsigned here = (TREE_INT_CST_LOW (constructor_bit_index)
/ (unsigned)BITS_PER_UNIT);
assemble_zeros ((next - here)
* (unsigned)BITS_PER_UNIT
/ (unsigned)BITS_PER_UNIT);
}
/* Indicate that we have now filled the structure up to the current
field. */
constructor_unfilled_fields = constructor_fields;
}
p = (struct constructor_stack *) xmalloc (sizeof (struct constructor_stack));
p->type = constructor_type;
p->fields = constructor_fields;
p->index = constructor_index;
p->range_end = constructor_range_end;
p->max_index = constructor_max_index;
p->unfilled_index = constructor_unfilled_index;
p->unfilled_fields = constructor_unfilled_fields;
p->bit_index = constructor_bit_index;
p->elements = constructor_elements;
p->constant = constructor_constant;
p->simple = constructor_simple;
p->erroneous = constructor_erroneous;
p->pending_elts = constructor_pending_elts;
p->depth = constructor_depth;
p->replacement_value = 0;
p->implicit = implicit;
p->incremental = constructor_incremental;
p->outer = 0;
p->next = constructor_stack;
constructor_stack = p;
constructor_constant = 1;
constructor_simple = 1;
constructor_depth = SPELLING_DEPTH ();
constructor_elements = 0;
constructor_pending_elts = 0;
/* Don't die if an entire brace-pair level is superfluous
in the containing level. */
if (constructor_type == 0)
;
else if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
/* Don't die if there are extra init elts at the end. */
if (constructor_fields == 0)
constructor_type = 0;
else
{
constructor_type = TREE_TYPE (constructor_fields);
push_member_name (constructor_fields);
constructor_depth++;
if (constructor_fields != constructor_unfilled_fields)
constructor_incremental = 0;
}
}
else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
constructor_type = TREE_TYPE (constructor_type);
push_array_bounds (TREE_INT_CST_LOW (constructor_index));
constructor_depth++;
if (! tree_int_cst_equal (constructor_index, constructor_unfilled_index)
|| constructor_range_end != 0)
constructor_incremental = 0;
}
if (constructor_type == 0)
{
error_init ("extra brace group at end of initializer");
constructor_fields = 0;
constructor_unfilled_fields = 0;
return;
}
/* Turn off constructor_incremental if type is a struct with bitfields. */
check_init_type_bitfields (constructor_type);
if (implicit && warn_missing_braces && !missing_braces_mentioned)
{
missing_braces_mentioned = 1;
warning_init ("missing braces around initializer");
}
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
constructor_fields = TYPE_FIELDS (constructor_type);
/* Skip any nameless bit fields at the beginning. */
while (constructor_fields != 0 && DECL_C_BIT_FIELD (constructor_fields)
&& DECL_NAME (constructor_fields) == 0)
constructor_fields = TREE_CHAIN (constructor_fields);
constructor_unfilled_fields = constructor_fields;
constructor_bit_index = copy_node (integer_zero_node);
TREE_TYPE (constructor_bit_index) = sbitsizetype;
}
else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
constructor_range_end = 0;
if (TYPE_DOMAIN (constructor_type))
{
constructor_max_index
= TYPE_MAX_VALUE (TYPE_DOMAIN (constructor_type));
constructor_index
= copy_node (TYPE_MIN_VALUE (TYPE_DOMAIN (constructor_type)));
}
else
constructor_index = copy_node (integer_zero_node);
constructor_unfilled_index = copy_node (constructor_index);
}
else
{
warning_init ("braces around scalar initializer");
constructor_fields = constructor_type;
constructor_unfilled_fields = constructor_type;
}
}
/* Don't read a struct incrementally if it has any bitfields,
because the incremental reading code doesn't know how to
handle bitfields yet. */
static void
check_init_type_bitfields (type)
tree type;
{
if (TREE_CODE (type) == RECORD_TYPE)
{
tree tail;
for (tail = TYPE_FIELDS (type); tail;
tail = TREE_CHAIN (tail))
{
if (DECL_C_BIT_FIELD (tail))
{
constructor_incremental = 0;
break;
}
check_init_type_bitfields (TREE_TYPE (tail));
}
}
else if (TREE_CODE (type) == UNION_TYPE)
{
tree tail = TYPE_FIELDS (type);
if (tail && DECL_C_BIT_FIELD (tail))
/* We also use the nonincremental algorithm for initiliazation
of unions whose first member is a bitfield, becuase the
incremental algorithm has no code for dealing with
bitfields. */
constructor_incremental = 0;
}
else if (TREE_CODE (type) == ARRAY_TYPE)
check_init_type_bitfields (TREE_TYPE (type));
}
/* At the end of an implicit or explicit brace level,
finish up that level of constructor.
If we were outputting the elements as they are read, return 0
from inner levels (process_init_element ignores that),
but return error_mark_node from the outermost level
(that's what we want to put in DECL_INITIAL).
Otherwise, return a CONSTRUCTOR expression. */
tree
pop_init_level (implicit)
int implicit;
{
struct constructor_stack *p;
int size = 0;
tree constructor = 0;
if (implicit == 0)
{
/* When we come to an explicit close brace,
pop any inner levels that didn't have explicit braces. */
while (constructor_stack->implicit)
process_init_element (pop_init_level (1));
}
p = constructor_stack;
if (constructor_type != 0)
size = int_size_in_bytes (constructor_type);
/* Warn when some struct elements are implicitly initialized to zero. */
if (extra_warnings
&& constructor_type
&& TREE_CODE (constructor_type) == RECORD_TYPE
&& constructor_unfilled_fields)
{
push_member_name (constructor_unfilled_fields);
warning_init ("missing initializer");
RESTORE_SPELLING_DEPTH (constructor_depth);
}
/* Now output all pending elements. */
output_pending_init_elements (1);
#if 0 /* c-parse.in warns about {}. */
/* In ANSI, each brace level must have at least one element. */
if (! implicit && pedantic
&& (TREE_CODE (constructor_type) == ARRAY_TYPE
? integer_zerop (constructor_unfilled_index)
: constructor_unfilled_fields == TYPE_FIELDS (constructor_type)))
pedwarn_init ("empty braces in initializer");
#endif
/* Pad out the end of the structure. */
if (p->replacement_value)
{
/* If this closes a superfluous brace pair,
just pass out the element between them. */
constructor = p->replacement_value;
/* If this is the top level thing within the initializer,
and it's for a variable, then since we already called
assemble_variable, we must output the value now. */
if (p->next == 0 && constructor_decl != 0
&& constructor_incremental)
{
constructor = digest_init (constructor_type, constructor,
require_constant_value,
require_constant_elements);
/* If initializing an array of unknown size,
determine the size now. */
if (TREE_CODE (constructor_type) == ARRAY_TYPE
&& TYPE_DOMAIN (constructor_type) == 0)
{
int failure;
int momentary_p;
push_obstacks_nochange ();
if (TREE_PERMANENT (constructor_type))
end_temporary_allocation ();
momentary_p = suspend_momentary ();
/* We shouldn't have an incomplete array type within
some other type. */
if (constructor_stack->next)
abort ();
failure
= complete_array_type (constructor_type,
constructor, 0);
if (failure)
abort ();
size = int_size_in_bytes (constructor_type);
resume_momentary (momentary_p);
pop_obstacks ();
}
output_constant (constructor, size);
}
}
else if (constructor_type == 0)
;
else if (TREE_CODE (constructor_type) != RECORD_TYPE
&& TREE_CODE (constructor_type) != UNION_TYPE
&& TREE_CODE (constructor_type) != ARRAY_TYPE
&& ! constructor_incremental)
{
/* A nonincremental scalar initializer--just return
the element, after verifying there is just one. */
if (constructor_elements == 0)
{
error_init ("empty scalar initializer");
constructor = error_mark_node;
}
else if (TREE_CHAIN (constructor_elements) != 0)
{
error_init ("extra elements in scalar initializer");
constructor = TREE_VALUE (constructor_elements);
}
else
constructor = TREE_VALUE (constructor_elements);
}
else if (! constructor_incremental)
{
if (constructor_erroneous)
constructor = error_mark_node;
else
{
int momentary = suspend_momentary ();
constructor = build (CONSTRUCTOR, constructor_type, NULL_TREE,
nreverse (constructor_elements));
if (constructor_constant)
TREE_CONSTANT (constructor) = 1;
if (constructor_constant && constructor_simple)
TREE_STATIC (constructor) = 1;
resume_momentary (momentary);
}
}
else
{
tree filled;
int momentary = suspend_momentary ();
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
/* Find the offset of the end of that field. */
filled = size_binop (CEIL_DIV_EXPR,
constructor_bit_index,
size_int (BITS_PER_UNIT));
}
else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
/* If initializing an array of unknown size,
determine the size now. */
if (TREE_CODE (constructor_type) == ARRAY_TYPE
&& TYPE_DOMAIN (constructor_type) == 0)
{
tree maxindex
= size_binop (MINUS_EXPR,
constructor_unfilled_index,
integer_one_node);
push_obstacks_nochange ();
if (TREE_PERMANENT (constructor_type))
end_temporary_allocation ();
maxindex = copy_node (maxindex);
TYPE_DOMAIN (constructor_type) = build_index_type (maxindex);
TREE_TYPE (maxindex) = TYPE_DOMAIN (constructor_type);
/* TYPE_MAX_VALUE is always one less than the number of elements
in the array, because we start counting at zero. Therefore,
warn only if the value is less than zero. */
if (pedantic
&& (tree_int_cst_sgn (TYPE_MAX_VALUE (TYPE_DOMAIN (constructor_type)))
< 0))
error_with_decl (constructor_decl,
"zero or negative array size `%s'");
layout_type (constructor_type);
size = int_size_in_bytes (constructor_type);
pop_obstacks ();
}
filled = size_binop (MULT_EXPR, constructor_unfilled_index,
size_in_bytes (TREE_TYPE (constructor_type)));
}
else
filled = 0;
if (filled != 0)
assemble_zeros (size - TREE_INT_CST_LOW (filled));
resume_momentary (momentary);
}
constructor_type = p->type;
constructor_fields = p->fields;
constructor_index = p->index;
constructor_range_end = p->range_end;
constructor_max_index = p->max_index;
constructor_unfilled_index = p->unfilled_index;
constructor_unfilled_fields = p->unfilled_fields;
constructor_bit_index = p->bit_index;
constructor_elements = p->elements;
constructor_constant = p->constant;
constructor_simple = p->simple;
constructor_erroneous = p->erroneous;
constructor_pending_elts = p->pending_elts;
constructor_depth = p->depth;
constructor_incremental = p->incremental;
RESTORE_SPELLING_DEPTH (constructor_depth);
constructor_stack = p->next;
free (p);
if (constructor == 0)
{
if (constructor_stack == 0)
return error_mark_node;
return NULL_TREE;
}
return constructor;
}
/* Within an array initializer, specify the next index to be initialized.
FIRST is that index. If LAST is nonzero, then initialize a range
of indices, running from FIRST through LAST. */
void
set_init_index (first, last)
tree first, last;
{
while ((TREE_CODE (first) == NOP_EXPR
|| TREE_CODE (first) == CONVERT_EXPR
|| TREE_CODE (first) == NON_LVALUE_EXPR)
&& (TYPE_MODE (TREE_TYPE (first))
== TYPE_MODE (TREE_TYPE (TREE_OPERAND (first, 0)))))
(first) = TREE_OPERAND (first, 0);
if (last)
while ((TREE_CODE (last) == NOP_EXPR
|| TREE_CODE (last) == CONVERT_EXPR
|| TREE_CODE (last) == NON_LVALUE_EXPR)
&& (TYPE_MODE (TREE_TYPE (last))
== TYPE_MODE (TREE_TYPE (TREE_OPERAND (last, 0)))))
(last) = TREE_OPERAND (last, 0);
if (TREE_CODE (first) != INTEGER_CST)
error_init ("nonconstant array index in initializer");
else if (last != 0 && TREE_CODE (last) != INTEGER_CST)
error_init ("nonconstant array index in initializer");
else if (! constructor_unfilled_index)
error_init ("array index in non-array initializer");
else if (tree_int_cst_lt (first, constructor_unfilled_index))
error_init ("duplicate array index in initializer");
else
{
TREE_INT_CST_LOW (constructor_index) = TREE_INT_CST_LOW (first);
TREE_INT_CST_HIGH (constructor_index) = TREE_INT_CST_HIGH (first);
if (last != 0 && tree_int_cst_lt (last, first))
error_init ("empty index range in initializer");
else
{
if (pedantic)
pedwarn ("ANSI C forbids specifying element to initialize");
constructor_range_end = last;
}
}
}
/* Within a struct initializer, specify the next field to be initialized. */
void
set_init_label (fieldname)
tree fieldname;
{
tree tail;
int passed = 0;
/* Don't die if an entire brace-pair level is superfluous
in the containing level. */
if (constructor_type == 0)
return;
for (tail = TYPE_FIELDS (constructor_type); tail;
tail = TREE_CHAIN (tail))
{
if (tail == constructor_unfilled_fields)
passed = 1;
if (DECL_NAME (tail) == fieldname)
break;
}
if (tail == 0)
error ("unknown field `%s' specified in initializer",
IDENTIFIER_POINTER (fieldname));
else if (!passed)
error ("field `%s' already initialized",
IDENTIFIER_POINTER (fieldname));
else
{
constructor_fields = tail;
if (pedantic)
pedwarn ("ANSI C forbids specifying structure member to initialize");
}
}
/* Add a new initializer to the tree of pending initializers. PURPOSE
indentifies the initializer, either array index or field in a structure.
VALUE is the value of that index or field. */
static void
add_pending_init (purpose, value)
tree purpose, value;
{
struct init_node *p, **q, *r;
q = &constructor_pending_elts;
p = 0;
if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
while (*q != 0)
{
p = *q;
if (tree_int_cst_lt (purpose, p->purpose))
q = &p->left;
else if (tree_int_cst_lt (p->purpose, purpose))
q = &p->right;
else
abort ();
}
}
else
{
while (*q != NULL)
{
p = *q;
if (tree_int_cst_lt (DECL_FIELD_BITPOS (purpose),
DECL_FIELD_BITPOS (p->purpose)))
q = &p->left;
else if (tree_int_cst_lt (DECL_FIELD_BITPOS (p->purpose),
DECL_FIELD_BITPOS (purpose)))
q = &p->right;
else
abort ();
}
}
r = (struct init_node *) oballoc (sizeof (struct init_node));
r->purpose = purpose;
r->value = value;
*q = r;
r->parent = p;
r->left = 0;
r->right = 0;
r->balance = 0;
while (p)
{
struct init_node *s;
if (r == p->left)
{
if (p->balance == 0)
p->balance = -1;
else if (p->balance < 0)
{
if (r->balance < 0)
{
/* L rotation. */
p->left = r->right;
if (p->left)
p->left->parent = p;
r->right = p;
p->balance = 0;
r->balance = 0;
s = p->parent;
p->parent = r;
r->parent = s;
if (s)
{
if (s->left == p)
s->left = r;
else
s->right = r;
}
else
constructor_pending_elts = r;
}
else
{
/* LR rotation. */
struct init_node *t = r->right;
r->right = t->left;
if (r->right)
r->right->parent = r;
t->left = r;
p->left = t->right;
if (p->left)
p->left->parent = p;
t->right = p;
p->balance = t->balance < 0;
r->balance = -(t->balance > 0);
t->balance = 0;
s = p->parent;
p->parent = t;
r->parent = t;
t->parent = s;
if (s)
{
if (s->left == p)
s->left = t;
else
s->right = t;
}
else
constructor_pending_elts = t;
}
break;
}
else
{
/* p->balance == +1; growth of left side balances the node. */
p->balance = 0;
break;
}
}
else /* r == p->right */
{
if (p->balance == 0)
/* Growth propagation from right side. */
p->balance++;
else if (p->balance > 0)
{
if (r->balance > 0)
{
/* R rotation. */
p->right = r->left;
if (p->right)
p->right->parent = p;
r->left = p;
p->balance = 0;
r->balance = 0;
s = p->parent;
p->parent = r;
r->parent = s;
if (s)
{
if (s->left == p)
s->left = r;
else
s->right = r;
}
else
constructor_pending_elts = r;
}
else /* r->balance == -1 */
{
/* RL rotation */
struct init_node *t = r->left;
r->left = t->right;
if (r->left)
r->left->parent = r;
t->right = r;
p->right = t->left;
if (p->right)
p->right->parent = p;
t->left = p;
r->balance = (t->balance < 0);
p->balance = -(t->balance > 0);
t->balance = 0;
s = p->parent;
p->parent = t;
r->parent = t;
t->parent = s;
if (s)
{
if (s->left == p)
s->left = t;
else
s->right = t;
}
else
constructor_pending_elts = t;
}
break;
}
else
{
/* p->balance == -1; growth of right side balances the node. */
p->balance = 0;
break;
}
}
r = p;
p = p->parent;
}
}
/* Return nonzero if FIELD is equal to the index of a pending initializer. */
static int
pending_init_member (field)
tree field;
{
struct init_node *p;
p = constructor_pending_elts;
if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
while (p)
{
if (tree_int_cst_equal (field, p->purpose))
return 1;
else if (tree_int_cst_lt (field, p->purpose))
p = p->left;
else
p = p->right;
}
}
else
{
while (p)
{
if (field == p->purpose)
return 1;
else if (tree_int_cst_lt (DECL_FIELD_BITPOS (field),
DECL_FIELD_BITPOS (p->purpose)))
p = p->left;
else
p = p->right;
}
}
return 0;
}
/* "Output" the next constructor element.
At top level, really output it to assembler code now.
Otherwise, collect it in a list from which we will make a CONSTRUCTOR.
TYPE is the data type that the containing data type wants here.
FIELD is the field (a FIELD_DECL) or the index that this element fills.
PENDING if non-nil means output pending elements that belong
right after this element. (PENDING is normally 1;
it is 0 while outputting pending elements, to avoid recursion.) */
static void
output_init_element (value, type, field, pending)
tree value, type, field;
int pending;
{
int duplicate = 0;
if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE
|| (TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE
&& !(TREE_CODE (value) == STRING_CST
&& TREE_CODE (type) == ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE)
&& !comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (value)),
TYPE_MAIN_VARIANT (type))))
value = default_conversion (value);
if (value == error_mark_node)
constructor_erroneous = 1;
else if (!TREE_CONSTANT (value))
constructor_constant = 0;
else if (initializer_constant_valid_p (value, TREE_TYPE (value)) == 0
|| ((TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
&& DECL_C_BIT_FIELD (field)
&& TREE_CODE (value) != INTEGER_CST))
constructor_simple = 0;
if (require_constant_value && ! TREE_CONSTANT (value))
{
error_init ("initializer element is not constant");
value = error_mark_node;
}
else if (require_constant_elements
&& initializer_constant_valid_p (value, TREE_TYPE (value)) == 0)
{
error_init ("initializer element is not computable at load time");
value = error_mark_node;
}
/* If this element duplicates one on constructor_pending_elts,
print a message and ignore it. Don't do this when we're
processing elements taken off constructor_pending_elts,
because we'd always get spurious errors. */
if (pending)
{
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE
|| TREE_CODE (constructor_type) == ARRAY_TYPE)
{
if (pending_init_member (field))
{
error_init ("duplicate initializer");
duplicate = 1;
}
}
}
/* If this element doesn't come next in sequence,
put it on constructor_pending_elts. */
if (TREE_CODE (constructor_type) == ARRAY_TYPE
&& !tree_int_cst_equal (field, constructor_unfilled_index))
{
if (! duplicate)
/* The copy_node is needed in case field is actually
constructor_index, which is modified in place. */
add_pending_init (copy_node (field),
digest_init (type, value, require_constant_value,
require_constant_elements));
}
else if (TREE_CODE (constructor_type) == RECORD_TYPE
&& field != constructor_unfilled_fields)
{
/* We do this for records but not for unions. In a union,
no matter which field is specified, it can be initialized
right away since it starts at the beginning of the union. */
if (!duplicate)
add_pending_init (field,
digest_init (type, value, require_constant_value,
require_constant_elements));
}
else
{
/* Otherwise, output this element either to
constructor_elements or to the assembler file. */
if (!duplicate)
{
if (! constructor_incremental)
{
if (field && TREE_CODE (field) == INTEGER_CST)
field = copy_node (field);
constructor_elements
= tree_cons (field, digest_init (type, value,
require_constant_value,
require_constant_elements),
constructor_elements);
}
else
{
/* Structure elements may require alignment.
Do this, if necessary. */
if (TREE_CODE (constructor_type) == RECORD_TYPE)
{
/* Advance to offset of this element. */
if (! tree_int_cst_equal (constructor_bit_index,
DECL_FIELD_BITPOS (field)))
{
/* By using unsigned arithmetic, the result will be
correct even in case of overflows, if BITS_PER_UNIT
is a power of two. */
unsigned next = (TREE_INT_CST_LOW
(DECL_FIELD_BITPOS (field))
/ (unsigned)BITS_PER_UNIT);
unsigned here = (TREE_INT_CST_LOW
(constructor_bit_index)
/ (unsigned)BITS_PER_UNIT);
assemble_zeros ((next - here)
* (unsigned)BITS_PER_UNIT
/ (unsigned)BITS_PER_UNIT);
}
}
output_constant (digest_init (type, value,
require_constant_value,
require_constant_elements),
int_size_in_bytes (type));
/* For a record or union,
keep track of end position of last field. */
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
tree temp = size_binop (PLUS_EXPR, DECL_FIELD_BITPOS (field),
DECL_SIZE (field));
TREE_INT_CST_LOW (constructor_bit_index)
= TREE_INT_CST_LOW (temp);
TREE_INT_CST_HIGH (constructor_bit_index)
= TREE_INT_CST_HIGH (temp);
}
}
}
/* Advance the variable that indicates sequential elements output. */
if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
tree tem = size_binop (PLUS_EXPR, constructor_unfilled_index,
integer_one_node);
TREE_INT_CST_LOW (constructor_unfilled_index)
= TREE_INT_CST_LOW (tem);
TREE_INT_CST_HIGH (constructor_unfilled_index)
= TREE_INT_CST_HIGH (tem);
}
else if (TREE_CODE (constructor_type) == RECORD_TYPE)
constructor_unfilled_fields = TREE_CHAIN (constructor_unfilled_fields);
else if (TREE_CODE (constructor_type) == UNION_TYPE)
constructor_unfilled_fields = 0;
/* Now output any pending elements which have become next. */
if (pending)
output_pending_init_elements (0);
}
}
/* Output any pending elements which have become next.
As we output elements, constructor_unfilled_{fields,index}
advances, which may cause other elements to become next;
if so, they too are output.
If ALL is 0, we return when there are
no more pending elements to output now.
If ALL is 1, we output space as necessary so that
we can output all the pending elements. */
static void
output_pending_init_elements (all)
int all;
{
struct init_node *elt = constructor_pending_elts;
tree next;
retry:
/* Look thru the whole pending tree.
If we find an element that should be output now,
output it. Otherwise, set NEXT to the element
that comes first among those still pending. */
next = 0;
while (elt)
{
if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
if (tree_int_cst_equal (elt->purpose,
constructor_unfilled_index))
output_init_element (elt->value,
TREE_TYPE (constructor_type),
constructor_unfilled_index, 0);
else if (tree_int_cst_lt (constructor_unfilled_index,
elt->purpose))
{
/* Advance to the next smaller node. */
if (elt->left)
elt = elt->left;
else
{
/* We have reached the smallest node bigger than the
current unfilled index. Fill the space first. */
next = elt->purpose;
break;
}
}
else
{
/* Advance to the next bigger node. */
if (elt->right)
elt = elt->right;
else
{
/* We have reached the biggest node in a subtree. Find
the parent of it, which is the next bigger node. */
while (elt->parent && elt->parent->right == elt)
elt = elt->parent;
elt = elt->parent;
if (elt && tree_int_cst_lt (constructor_unfilled_index,
elt->purpose))
{
next = elt->purpose;
break;
}
}
}
}
else if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
/* If the current record is complete we are done. */
if (constructor_unfilled_fields == 0)
break;
if (elt->purpose == constructor_unfilled_fields)
{
output_init_element (elt->value,
TREE_TYPE (constructor_unfilled_fields),
constructor_unfilled_fields,
0);
}
else if (tree_int_cst_lt (DECL_FIELD_BITPOS (constructor_unfilled_fields),
DECL_FIELD_BITPOS (elt->purpose)))
{
/* Advance to the next smaller node. */
if (elt->left)
elt = elt->left;
else
{
/* We have reached the smallest node bigger than the
current unfilled field. Fill the space first. */
next = elt->purpose;
break;
}
}
else
{
/* Advance to the next bigger node. */
if (elt->right)
elt = elt->right;
else
{
/* We have reached the biggest node in a subtree. Find
the parent of it, which is the next bigger node. */
while (elt->parent && elt->parent->right == elt)
elt = elt->parent;
elt = elt->parent;
if (elt
&& tree_int_cst_lt (DECL_FIELD_BITPOS (constructor_unfilled_fields),
DECL_FIELD_BITPOS (elt->purpose)))
{
next = elt->purpose;
break;
}
}
}
}
}
/* Ordinarily return, but not if we want to output all
and there are elements left. */
if (! (all && next != 0))
return;
/* Generate space up to the position of NEXT. */
if (constructor_incremental)
{
tree filled;
tree nextpos_tree = size_int (0);
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
{
tree tail;
/* Find the last field written out, if any. */
for (tail = TYPE_FIELDS (constructor_type); tail;
tail = TREE_CHAIN (tail))
if (TREE_CHAIN (tail) == constructor_unfilled_fields)
break;
if (tail)
/* Find the offset of the end of that field. */
filled = size_binop (CEIL_DIV_EXPR,
size_binop (PLUS_EXPR,
DECL_FIELD_BITPOS (tail),
DECL_SIZE (tail)),
size_int (BITS_PER_UNIT));
else
filled = size_int (0);
nextpos_tree = size_binop (CEIL_DIV_EXPR,
DECL_FIELD_BITPOS (next),
size_int (BITS_PER_UNIT));
TREE_INT_CST_HIGH (constructor_bit_index)
= TREE_INT_CST_HIGH (DECL_FIELD_BITPOS (next));
TREE_INT_CST_LOW (constructor_bit_index)
= TREE_INT_CST_LOW (DECL_FIELD_BITPOS (next));
constructor_unfilled_fields = next;
}
else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
filled = size_binop (MULT_EXPR, constructor_unfilled_index,
size_in_bytes (TREE_TYPE (constructor_type)));
nextpos_tree
= size_binop (MULT_EXPR, next,
size_in_bytes (TREE_TYPE (constructor_type)));
TREE_INT_CST_LOW (constructor_unfilled_index)
= TREE_INT_CST_LOW (next);
TREE_INT_CST_HIGH (constructor_unfilled_index)
= TREE_INT_CST_HIGH (next);
}
else
filled = 0;
if (filled)
{
int nextpos = TREE_INT_CST_LOW (nextpos_tree);
assemble_zeros (nextpos - TREE_INT_CST_LOW (filled));
}
}
else
{
/* If it's not incremental, just skip over the gap,
so that after jumping to retry we will output the next
successive element. */
if (TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
constructor_unfilled_fields = next;
else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
TREE_INT_CST_LOW (constructor_unfilled_index)
= TREE_INT_CST_LOW (next);
TREE_INT_CST_HIGH (constructor_unfilled_index)
= TREE_INT_CST_HIGH (next);
}
}
/* ELT now points to the node in the pending tree with the next
initializer to output. */
goto retry;
}
/* Add one non-braced element to the current constructor level.
This adjusts the current position within the constructor's type.
This may also start or terminate implicit levels
to handle a partly-braced initializer.
Once this has found the correct level for the new element,
it calls output_init_element.
Note: if we are incrementally outputting this constructor,
this function may be called with a null argument
representing a sub-constructor that was already incrementally output.
When that happens, we output nothing, but we do the bookkeeping
to skip past that element of the current constructor. */
void
process_init_element (value)
tree value;
{
tree orig_value = value;
int string_flag = value != 0 && TREE_CODE (value) == STRING_CST;
/* Handle superfluous braces around string cst as in
char x[] = {"foo"}; */
if (string_flag
&& constructor_type
&& TREE_CODE (constructor_type) == ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (constructor_type)) == INTEGER_TYPE
&& integer_zerop (constructor_unfilled_index))
{
constructor_stack->replacement_value = value;
return;
}
if (constructor_stack->replacement_value != 0)
{
error_init ("excess elements in struct initializer");
return;
}
/* Ignore elements of a brace group if it is entirely superfluous
and has already been diagnosed. */
if (constructor_type == 0)
return;
/* If we've exhausted any levels that didn't have braces,
pop them now. */
while (constructor_stack->implicit)
{
if ((TREE_CODE (constructor_type) == RECORD_TYPE
|| TREE_CODE (constructor_type) == UNION_TYPE)
&& constructor_fields == 0)
process_init_element (pop_init_level (1));
else if (TREE_CODE (constructor_type) == ARRAY_TYPE
&& (constructor_max_index == 0
|| tree_int_cst_lt (constructor_max_index,
constructor_index)))
process_init_element (pop_init_level (1));
else
break;
}
while (1)
{
if (TREE_CODE (constructor_type) == RECORD_TYPE)
{
tree fieldtype;
enum tree_code fieldcode;
if (constructor_fields == 0)
{
pedwarn_init ("excess elements in struct initializer");
break;
}
fieldtype = TREE_TYPE (constructor_fields);
if (fieldtype != error_mark_node)
fieldtype = TYPE_MAIN_VARIANT (fieldtype);
fieldcode = TREE_CODE (fieldtype);
/* Accept a string constant to initialize a subarray. */
if (value != 0
&& fieldcode == ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (fieldtype)) == INTEGER_TYPE
&& string_flag)
value = orig_value;
/* Otherwise, if we have come to a subaggregate,
and we don't have an element of its type, push into it. */
else if (value != 0 && !constructor_no_implicit
&& value != error_mark_node
&& TYPE_MAIN_VARIANT (TREE_TYPE (value)) != fieldtype
&& (fieldcode == RECORD_TYPE || fieldcode == ARRAY_TYPE
|| fieldcode == UNION_TYPE))
{
push_init_level (1);
continue;
}
if (value)
{
push_member_name (constructor_fields);
output_init_element (value, fieldtype, constructor_fields, 1);
RESTORE_SPELLING_DEPTH (constructor_depth);
}
else
/* Do the bookkeeping for an element that was
directly output as a constructor. */
{
/* For a record, keep track of end position of last field. */
tree temp = size_binop (PLUS_EXPR,
DECL_FIELD_BITPOS (constructor_fields),
DECL_SIZE (constructor_fields));
TREE_INT_CST_LOW (constructor_bit_index)
= TREE_INT_CST_LOW (temp);
TREE_INT_CST_HIGH (constructor_bit_index)
= TREE_INT_CST_HIGH (temp);
constructor_unfilled_fields = TREE_CHAIN (constructor_fields);
}
constructor_fields = TREE_CHAIN (constructor_fields);
/* Skip any nameless bit fields at the beginning. */
while (constructor_fields != 0
&& DECL_C_BIT_FIELD (constructor_fields)
&& DECL_NAME (constructor_fields) == 0)
constructor_fields = TREE_CHAIN (constructor_fields);
break;
}
if (TREE_CODE (constructor_type) == UNION_TYPE)
{
tree fieldtype;
enum tree_code fieldcode;
if (constructor_fields == 0)
{
pedwarn_init ("excess elements in union initializer");
break;
}
fieldtype = TREE_TYPE (constructor_fields);
if (fieldtype != error_mark_node)
fieldtype = TYPE_MAIN_VARIANT (fieldtype);
fieldcode = TREE_CODE (fieldtype);
/* Accept a string constant to initialize a subarray. */
if (value != 0
&& fieldcode == ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (fieldtype)) == INTEGER_TYPE
&& string_flag)
value = orig_value;
/* Otherwise, if we have come to a subaggregate,
and we don't have an element of its type, push into it. */
else if (value != 0 && !constructor_no_implicit
&& value != error_mark_node
&& TYPE_MAIN_VARIANT (TREE_TYPE (value)) != fieldtype
&& (fieldcode == RECORD_TYPE || fieldcode == ARRAY_TYPE
|| fieldcode == UNION_TYPE))
{
push_init_level (1);
continue;
}
if (value)
{
push_member_name (constructor_fields);
output_init_element (value, fieldtype, constructor_fields, 1);
RESTORE_SPELLING_DEPTH (constructor_depth);
}
else
/* Do the bookkeeping for an element that was
directly output as a constructor. */
{
TREE_INT_CST_LOW (constructor_bit_index)
= TREE_INT_CST_LOW (DECL_SIZE (constructor_fields));
TREE_INT_CST_HIGH (constructor_bit_index)
= TREE_INT_CST_HIGH (DECL_SIZE (constructor_fields));
constructor_unfilled_fields = TREE_CHAIN (constructor_fields);
}
constructor_fields = 0;
break;
}
if (TREE_CODE (constructor_type) == ARRAY_TYPE)
{
tree elttype = TYPE_MAIN_VARIANT (TREE_TYPE (constructor_type));
enum tree_code eltcode = TREE_CODE (elttype);
/* Accept a string constant to initialize a subarray. */
if (value != 0
&& eltcode == ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (elttype)) == INTEGER_TYPE
&& string_flag)
value = orig_value;
/* Otherwise, if we have come to a subaggregate,
and we don't have an element of its type, push into it. */
else if (value != 0 && !constructor_no_implicit
&& value != error_mark_node
&& TYPE_MAIN_VARIANT (TREE_TYPE (value)) != elttype
&& (eltcode == RECORD_TYPE || eltcode == ARRAY_TYPE
|| eltcode == UNION_TYPE))
{
push_init_level (1);
continue;
}
if (constructor_max_index != 0
&& tree_int_cst_lt (constructor_max_index, constructor_index))
{
pedwarn_init ("excess elements in array initializer");
break;
}
/* In the case of [LO .. HI] = VALUE, only evaluate VALUE once. */
if (constructor_range_end)
{
if (constructor_max_index != 0
&& tree_int_cst_lt (constructor_max_index,
constructor_range_end))
{
pedwarn_init ("excess elements in array initializer");
TREE_INT_CST_HIGH (constructor_range_end)
= TREE_INT_CST_HIGH (constructor_max_index);
TREE_INT_CST_LOW (constructor_range_end)
= TREE_INT_CST_LOW (constructor_max_index);
}
value = save_expr (value);
}
/* Now output the actual element.
Ordinarily, output once.
If there is a range, repeat it till we advance past the range. */
do
{
tree tem;
if (value)
{
push_array_bounds (TREE_INT_CST_LOW (constructor_index));
output_init_element (value, elttype, constructor_index, 1);
RESTORE_SPELLING_DEPTH (constructor_depth);
}
tem = size_binop (PLUS_EXPR, constructor_index,
integer_one_node);
TREE_INT_CST_LOW (constructor_index) = TREE_INT_CST_LOW (tem);
TREE_INT_CST_HIGH (constructor_index) = TREE_INT_CST_HIGH (tem);
if (!value)
/* If we are doing the bookkeeping for an element that was
directly output as a constructor,
we must update constructor_unfilled_index. */
{
TREE_INT_CST_LOW (constructor_unfilled_index)
= TREE_INT_CST_LOW (constructor_index);
TREE_INT_CST_HIGH (constructor_unfilled_index)
= TREE_INT_CST_HIGH (constructor_index);
}
}
while (! (constructor_range_end == 0
|| tree_int_cst_lt (constructor_range_end,
constructor_index)));
break;
}
/* Handle the sole element allowed in a braced initializer
for a scalar variable. */
if (constructor_fields == 0)
{
pedwarn_init ("excess elements in scalar initializer");
break;
}
if (value)
output_init_element (value, constructor_type, NULL_TREE, 1);
constructor_fields = 0;
break;
}
/* If the (lexically) previous elments are not now saved,
we can discard the storage for them. */
if (constructor_incremental && constructor_pending_elts == 0 && value != 0
&& constructor_stack == 0)
clear_momentary ();
}
/* Expand an ASM statement with operands, handling output operands
that are not variables or INDIRECT_REFS by transforming such
cases into cases that expand_asm_operands can handle.
Arguments are same as for expand_asm_operands. */
void
c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
tree string, outputs, inputs, clobbers;
int vol;
char *filename;
int line;
{
int noutputs = list_length (outputs);
register int i;
/* o[I] is the place that output number I should be written. */
register tree *o = (tree *) alloca (noutputs * sizeof (tree));
register tree tail;
if (TREE_CODE (string) == ADDR_EXPR)
string = TREE_OPERAND (string, 0);
if (TREE_CODE (string) != STRING_CST)
{
error ("asm template is not a string constant");
return;
}
/* Record the contents of OUTPUTS before it is modified. */
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
o[i] = TREE_VALUE (tail);
/* Perform default conversions on array and function inputs. */
/* Don't do this for other types--
it would screw up operands expected to be in memory. */
for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), i++)
if (TREE_CODE (TREE_TYPE (TREE_VALUE (tail))) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (TREE_VALUE (tail))) == FUNCTION_TYPE)
TREE_VALUE (tail) = default_conversion (TREE_VALUE (tail));
/* Generate the ASM_OPERANDS insn;
store into the TREE_VALUEs of OUTPUTS some trees for
where the values were actually stored. */
expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line);
/* Copy all the intermediate outputs into the specified outputs. */
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
if (o[i] != TREE_VALUE (tail))
{
expand_expr (build_modify_expr (o[i], NOP_EXPR, TREE_VALUE (tail)),
NULL_RTX, VOIDmode, EXPAND_NORMAL);
free_temp_slots ();
}
/* Detect modification of read-only values.
(Otherwise done by build_modify_expr.) */
else
{
tree type = TREE_TYPE (o[i]);
if (TREE_READONLY (o[i])
|| TYPE_READONLY (type)
|| ((TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE)
&& C_TYPE_FIELDS_READONLY (type)))
readonly_warning (o[i], "modification by `asm'");
}
}
/* Those MODIFY_EXPRs could do autoincrements. */
emit_queue ();
}
/* Expand a C `return' statement.
RETVAL is the expression for what to return,
or a null pointer for `return;' with no value. */
void
c_expand_return (retval)
tree retval;
{
tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl));
if (TREE_THIS_VOLATILE (current_function_decl))
warning ("function declared `noreturn' has a `return' statement");
if (!retval)
{
current_function_returns_null = 1;
if (warn_return_type && valtype != 0 && TREE_CODE (valtype) != VOID_TYPE)
warning ("`return' with no value, in function returning non-void");
expand_null_return ();
}
else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE)
{
current_function_returns_null = 1;
if (pedantic || TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE)
pedwarn ("`return' with a value, in function returning void");
expand_return (retval);
}
else
{
tree t = convert_for_assignment (valtype, retval, _("return"),
NULL_TREE, NULL_TREE, 0);
tree res = DECL_RESULT (current_function_decl);
tree inner;
if (t == error_mark_node)
return;
inner = t = convert (TREE_TYPE (res), t);
/* Strip any conversions, additions, and subtractions, and see if
we are returning the address of a local variable. Warn if so. */
while (1)
{
switch (TREE_CODE (inner))
{
case NOP_EXPR: case NON_LVALUE_EXPR: case CONVERT_EXPR:
case PLUS_EXPR:
inner = TREE_OPERAND (inner, 0);
continue;
case MINUS_EXPR:
/* If the second operand of the MINUS_EXPR has a pointer
type (or is converted from it), this may be valid, so
don't give a warning. */
{
tree op1 = TREE_OPERAND (inner, 1);
while (! POINTER_TYPE_P (TREE_TYPE (op1))
&& (TREE_CODE (op1) == NOP_EXPR
|| TREE_CODE (op1) == NON_LVALUE_EXPR
|| TREE_CODE (op1) == CONVERT_EXPR))
op1 = TREE_OPERAND (op1, 0);
if (POINTER_TYPE_P (TREE_TYPE (op1)))
break;
inner = TREE_OPERAND (inner, 0);
continue;
}
case ADDR_EXPR:
inner = TREE_OPERAND (inner, 0);
while (TREE_CODE_CLASS (TREE_CODE (inner)) == 'r')
inner = TREE_OPERAND (inner, 0);
if (TREE_CODE (inner) == VAR_DECL
&& ! DECL_EXTERNAL (inner)
&& ! TREE_STATIC (inner)
&& DECL_CONTEXT (inner) == current_function_decl)
warning ("function returns address of local variable");
break;
default:
break;
}
break;
}
t = build (MODIFY_EXPR, TREE_TYPE (res), res, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_return (t);
current_function_returns_value = 1;
}
}
/* Start a C switch statement, testing expression EXP.
Return EXP if it is valid, an error node otherwise. */
tree
c_expand_start_case (exp)
tree exp;
{
register enum tree_code code = TREE_CODE (TREE_TYPE (exp));
tree type = TREE_TYPE (exp);
if (code != INTEGER_TYPE && code != ENUMERAL_TYPE && code != ERROR_MARK)
{
error ("switch quantity not an integer");
exp = error_mark_node;
}
else
{
tree index;
type = TYPE_MAIN_VARIANT (TREE_TYPE (exp));
if (warn_traditional
&& (type == long_integer_type_node
|| type == long_unsigned_type_node))
pedwarn ("`long' switch expression not converted to `int' in ANSI C");
exp = default_conversion (exp);
type = TREE_TYPE (exp);
index = get_unwidened (exp, NULL_TREE);
/* We can't strip a conversion from a signed type to an unsigned,
because if we did, int_fits_type_p would do the wrong thing
when checking case values for being in range,
and it's too hard to do the right thing. */
if (TREE_UNSIGNED (TREE_TYPE (exp))
== TREE_UNSIGNED (TREE_TYPE (index)))
exp = index;
}
expand_start_case (1, exp, type, "switch statement");
return exp;
}
Index: head/contrib/gcc/collect2.c
===================================================================
--- head/contrib/gcc/collect2.c (revision 52750)
+++ head/contrib/gcc/collect2.c (revision 52751)
@@ -1,3801 +1,3804 @@
/* Collect static initialization info into data structures that can be
traversed by C++ initialization and finalization routines.
Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc.
Contributed by Chris Smith (csmith@convex.com).
Heavily modified by Michael Meissner (meissner@cygnus.com),
Per Bothner (bothner@cygnus.com), and John Gilmore (gnu@cygnus.com).
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. */
/* Build tables of static constructors and destructors and run ld. */
#include "config.h"
#include "system.h"
#include <signal.h>
#ifdef vfork /* Autoconf may define this to fork for us. */
# define VFORK_STRING "fork"
#else
# define VFORK_STRING "vfork"
#endif
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
#ifdef VMS
#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
#endif /* VMS */
#define COLLECT
#include "collect2.h"
#include "demangle.h"
#include "obstack.h"
#include "intl.h"
/* Obstack allocation and deallocation routines. */
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
extern char *make_temp_file PROTO ((char *));
/* On certain systems, we have code that works by scanning the object file
directly. But this code uses system-specific header files and library
functions, so turn it off in a cross-compiler. Likewise, the names of
the utilities are not correct for a cross-compiler; we have to hope that
cross-versions are in the proper directories. */
#ifdef CROSS_COMPILE
#undef SUNOS4_SHARED_LIBRARIES
#undef OBJECT_FORMAT_COFF
#undef OBJECT_FORMAT_ROSE
#undef MD_EXEC_PREFIX
#undef REAL_LD_FILE_NAME
#undef REAL_NM_FILE_NAME
#undef REAL_STRIP_FILE_NAME
#endif
/* If we cannot use a special method, use the ordinary one:
run nm to find what symbols are present.
In a cross-compiler, this means you need a cross nm,
but that is not quite as unpleasant as special headers. */
#if !defined (OBJECT_FORMAT_COFF) && !defined (OBJECT_FORMAT_ROSE)
#define OBJECT_FORMAT_NONE
#endif
#ifdef OBJECT_FORMAT_COFF
#include <a.out.h>
#include <ar.h>
#ifdef UMAX
#include <sgs.h>
#endif
/* Many versions of ldfcn.h define these. */
#ifdef FREAD
#undef FREAD
#undef FWRITE
#endif
#include <ldfcn.h>
/* Some systems have an ISCOFF macro, but others do not. In some cases
the macro may be wrong. MY_ISCOFF is defined in tm.h files for machines
that either do not have an ISCOFF macro in /usr/include or for those
where it is wrong. */
#ifndef MY_ISCOFF
#define MY_ISCOFF(X) ISCOFF (X)
#endif
#endif /* OBJECT_FORMAT_COFF */
#ifdef OBJECT_FORMAT_ROSE
#ifdef _OSF_SOURCE
#define USE_MMAP
#endif
#ifdef USE_MMAP
#include <sys/mman.h>
#endif
#include <unistd.h>
#include <mach_o_format.h>
#include <mach_o_header.h>
#include <mach_o_vals.h>
#include <mach_o_types.h>
#endif /* OBJECT_FORMAT_ROSE */
#ifdef OBJECT_FORMAT_NONE
/* Default flags to pass to nm. */
#ifndef NM_FLAGS
#define NM_FLAGS "-n"
#endif
#endif /* OBJECT_FORMAT_NONE */
/* Some systems use __main in a way incompatible with its use in gcc, in these
cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
give the same symbol without quotes for an alternative entry point. You
must define both, or neither. */
#ifndef NAME__MAIN
#define NAME__MAIN "__main"
#define SYMBOL__MAIN __main
#endif
/* This must match tree.h. */
#define DEFAULT_INIT_PRIORITY 65535
#if defined (LDD_SUFFIX) || SUNOS4_SHARED_LIBRARIES
#define SCAN_LIBRARIES
#endif
#ifdef USE_COLLECT2
int do_collecting = 1;
#else
int do_collecting = 0;
#endif
/* Linked lists of constructor and destructor names. */
struct id
{
struct id *next;
int sequence;
char name[1];
};
struct head
{
struct id *first;
struct id *last;
int number;
};
/* Enumeration giving which pass this is for scanning the program file. */
enum pass {
PASS_FIRST, /* without constructors */
PASS_OBJ, /* individual objects */
PASS_LIB, /* looking for shared libraries */
PASS_SECOND /* with constructors linked in */
};
extern char *version_string;
int vflag; /* true if -v */
static int rflag; /* true if -r */
static int strip_flag; /* true if -s */
#ifdef COLLECT_EXPORT_LIST
static int export_flag; /* true if -bE */
static int aix64_flag; /* true if -b64 */
#endif
int debug; /* true if -debug */
static int shared_obj; /* true if -shared */
static char *c_file; /* <xxx>.c for constructor/destructor list. */
static char *o_file; /* <xxx>.o for constructor/destructor list. */
#ifdef COLLECT_EXPORT_LIST
static char *export_file; /* <xxx>.x for AIX export list. */
static char *import_file; /* <xxx>.p for AIX import list. */
#endif
char *ldout; /* File for ld errors. */
static char *output_file; /* Output file for ld. */
static char *nm_file_name; /* pathname of nm */
#ifdef LDD_SUFFIX
static char *ldd_file_name; /* pathname of ldd (or equivalent) */
#endif
static char *strip_file_name; /* pathname of strip */
char *c_file_name; /* pathname of gcc */
static char *initname, *fininame; /* names of init and fini funcs */
static struct head constructors; /* list of constructors found */
static struct head destructors; /* list of destructors found */
#ifdef COLLECT_EXPORT_LIST
static struct head exports; /* list of exported symbols */
static struct head imports; /* list of imported symbols */
static struct head undefined; /* list of undefined symbols */
#endif
static struct head frame_tables; /* list of frame unwind info tables */
struct obstack temporary_obstack;
struct obstack permanent_obstack;
char * temporary_firstobj;
/* Holds the return value of pexecute. */
int pexecute_pid;
/* Defined in the automatically-generated underscore.c. */
extern int prepends_underscore;
extern FILE *fdopen ();
#ifndef GET_ENV_PATH_LIST
#define GET_ENV_PATH_LIST(VAR,NAME) do { (VAR) = getenv (NAME); } while (0)
#endif
/* Structure to hold all the directories in which to search for files to
execute. */
struct prefix_list
{
char *prefix; /* String to prepend to the path. */
struct prefix_list *next; /* Next in linked list. */
};
struct path_prefix
{
struct prefix_list *plist; /* List of prefixes to try */
int max_len; /* Max length of a prefix in PLIST */
char *name; /* Name of this list (used in config stuff) */
};
#ifdef COLLECT_EXPORT_LIST
/* Lists to keep libraries to be scanned for global constructors/destructors. */
static struct head libs; /* list of libraries */
static struct path_prefix cmdline_lib_dirs; /* directories specified with -L */
static struct path_prefix libpath_lib_dirs; /* directories in LIBPATH */
static struct path_prefix *libpaths[3] = {&cmdline_lib_dirs,
&libpath_lib_dirs, NULL};
static char *libexts[3] = {"a", "so", NULL}; /* possible library extentions */
#endif
void error PVPROTO((const char *, ...)) ATTRIBUTE_PRINTF_1;
void fatal PVPROTO((const char *, ...))
ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
void fatal_perror PVPROTO((const char *, ...))
ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
static char *my_strerror PROTO((int));
static const char *my_strsignal PROTO((int));
static void handler PROTO((int));
static int is_ctor_dtor PROTO((char *));
static char *find_a_file PROTO((struct path_prefix *, char *));
static void add_prefix PROTO((struct path_prefix *, char *));
static void prefix_from_env PROTO((char *, struct path_prefix *));
static void prefix_from_string PROTO((char *, struct path_prefix *));
static void do_wait PROTO((char *));
static void fork_execute PROTO((char *, char **));
static void maybe_unlink PROTO((char *));
static void add_to_list PROTO((struct head *, char *));
static int extract_init_priority PROTO((char *));
static void sort_ids PROTO((struct head *));
static void write_list PROTO((FILE *, char *, struct id *));
#ifdef COLLECT_EXPORT_LIST
static void dump_list PROTO((FILE *, char *, struct id *));
#endif
#if 0
static void dump_prefix_list PROTO((FILE *, char *, struct prefix_list *));
#endif
static void write_list_with_asm PROTO((FILE *, char *, struct id *));
static void write_c_file PROTO((FILE *, char *));
static void scan_prog_file PROTO((char *, enum pass));
#ifdef SCAN_LIBRARIES
static void scan_libraries PROTO((char *));
#endif
#ifdef COLLECT_EXPORT_LIST
static int is_in_list PROTO((char *, struct id *));
static void write_export_file PROTO((FILE *));
static void write_import_file PROTO((FILE *));
static char *resolve_lib_name PROTO((char *));
static int use_import_list PROTO((char *));
static int ignore_library PROTO((char *));
#endif
#ifdef NO_DUP2
int
dup2 (oldfd, newfd)
int oldfd;
int newfd;
{
int fdtmp[256];
int fdx = 0;
int fd;
if (oldfd == newfd)
return oldfd;
close (newfd);
while ((fd = dup (oldfd)) != newfd && fd >= 0) /* good enough for low fd's */
fdtmp[fdx++] = fd;
while (fdx > 0)
close (fdtmp[--fdx]);
return fd;
}
#endif
static char *
my_strerror (e)
int e;
{
#ifdef HAVE_STRERROR
return strerror (e);
#else
if (!e)
return "";
if (e > 0 && e < sys_nerr)
return sys_errlist[e];
return "errno = ?";
#endif
}
static const char *
my_strsignal (s)
int s;
{
#ifdef HAVE_STRSIGNAL
return strsignal (s);
#else
if (s >= 0 && s < NSIG)
{
# ifdef NO_SYS_SIGLIST
static char buffer[30];
sprintf (buffer, "Unknown signal %d", s);
return buffer;
# else
return sys_siglist[s];
# endif
}
else
return NULL;
#endif /* HAVE_STRSIGNAL */
}
/* Delete tempfiles and exit function. */
void
collect_exit (status)
int status;
{
if (c_file != 0 && c_file[0])
maybe_unlink (c_file);
if (o_file != 0 && o_file[0])
maybe_unlink (o_file);
#ifdef COLLECT_EXPORT_LIST
if (export_file != 0 && export_file[0])
maybe_unlink (export_file);
if (import_file != 0 && import_file[0])
maybe_unlink (import_file);
#endif
if (ldout != 0 && ldout[0])
{
dump_file (ldout);
maybe_unlink (ldout);
}
if (status != 0 && output_file != 0 && output_file[0])
maybe_unlink (output_file);
exit (status);
}
/* Notify user of a non-error. */
void
notice VPROTO((char *msgid, ...))
{
#ifndef ANSI_PROTOTYPES
char *msgid;
#endif
va_list ap;
VA_START (ap, msgid);
#ifndef ANSI_PROTOTYPES
msgid = va_arg (ap, char *);
#endif
vfprintf (stderr, _(msgid), ap);
va_end (ap);
}
/* Die when sys call fails. */
void
fatal_perror VPROTO((const char * msgid, ...))
{
#ifndef ANSI_PROTOTYPES
const char *msgid;
#endif
int e = errno;
va_list ap;
VA_START (ap, msgid);
#ifndef ANSI_PROTOTYPES
msgid = va_arg (ap, const char *);
#endif
fprintf (stderr, "collect2: ");
vfprintf (stderr, _(msgid), ap);
fprintf (stderr, ": %s\n", my_strerror (e));
va_end (ap);
collect_exit (FATAL_EXIT_CODE);
}
/* Just die. */
void
fatal VPROTO((const char * msgid, ...))
{
#ifndef ANSI_PROTOTYPES
const char *msgid;
#endif
va_list ap;
VA_START (ap, msgid);
#ifndef ANSI_PROTOTYPES
msgid = va_arg (ap, const char *);
#endif
fprintf (stderr, "collect2: ");
vfprintf (stderr, _(msgid), ap);
fprintf (stderr, "\n");
va_end (ap);
collect_exit (FATAL_EXIT_CODE);
}
/* Write error message. */
void
error VPROTO((const char * msgid, ...))
{
#ifndef ANSI_PROTOTYPES
const char * msgid;
#endif
va_list ap;
VA_START (ap, msgid);
#ifndef ANSI_PROTOTYPES
msgid = va_arg (ap, const char *);
#endif
fprintf (stderr, "collect2: ");
vfprintf (stderr, _(msgid), ap);
fprintf (stderr, "\n");
va_end(ap);
}
/* In case obstack is linked in, and abort is defined to fancy_abort,
provide a default entry. */
void
fancy_abort ()
{
fatal ("internal error");
}
static void
handler (signo)
int signo;
{
if (c_file != 0 && c_file[0])
maybe_unlink (c_file);
if (o_file != 0 && o_file[0])
maybe_unlink (o_file);
if (ldout != 0 && ldout[0])
maybe_unlink (ldout);
#ifdef COLLECT_EXPORT_LIST
if (export_file != 0 && export_file[0])
maybe_unlink (export_file);
if (import_file != 0 && import_file[0])
maybe_unlink (import_file);
#endif
signal (signo, SIG_DFL);
kill (getpid (), signo);
}
PTR
xcalloc (size1, size2)
size_t size1, size2;
{
PTR ptr = (PTR) calloc (size1, size2);
if (!ptr)
fatal ("out of memory");
return ptr;
}
PTR
xmalloc (size)
size_t size;
{
PTR ptr = (PTR) malloc (size);
if (!ptr)
fatal ("out of memory");
return ptr;
}
PTR
xrealloc (old, size)
PTR old;
size_t size;
{
register PTR ptr;
if (old)
ptr = (PTR) realloc (old, size);
else
ptr = (PTR) malloc (size);
if (ptr == 0)
fatal ("virtual memory exhausted");
return ptr;
}
int
file_exists (name)
char *name;
{
return access (name, R_OK) == 0;
}
/* Make a copy of a string INPUT with size SIZE. */
char *
xstrdup (input)
const char *input;
{
register size_t len = strlen (input) + 1;
register char *output = xmalloc (len);
memcpy (output, input, len);
return output;
}
/* Parse a reasonable subset of shell quoting syntax. */
static char *
extract_string (pp)
char **pp;
{
char *p = *pp;
int backquote = 0;
int inside = 0;
for (;;)
{
char c = *p;
if (c == '\0')
break;
++p;
if (backquote)
obstack_1grow (&temporary_obstack, c);
else if (! inside && c == ' ')
break;
else if (! inside && c == '\\')
backquote = 1;
else if (c == '\'')
inside = !inside;
else
obstack_1grow (&temporary_obstack, c);
}
obstack_1grow (&temporary_obstack, '\0');
*pp = p;
return obstack_finish (&temporary_obstack);
}
void
dump_file (name)
char *name;
{
FILE *stream = fopen (name, "r");
int no_demangle = !! getenv ("COLLECT_NO_DEMANGLE");
if (stream == 0)
return;
while (1)
{
int c;
while (c = getc (stream),
c != EOF && (ISALNUM (c) || c == '_' || c == '$' || c == '.'))
obstack_1grow (&temporary_obstack, c);
if (obstack_object_size (&temporary_obstack) > 0)
{
char *word, *p, *result;
obstack_1grow (&temporary_obstack, '\0');
word = obstack_finish (&temporary_obstack);
if (*word == '.')
++word, putc ('.', stderr);
p = word;
if (*p == '_' && prepends_underscore)
++p;
if (no_demangle)
result = 0;
else
result = cplus_demangle (p, DMGL_PARAMS | DMGL_ANSI);
if (result)
{
int diff;
fputs (result, stderr);
diff = strlen (word) - strlen (result);
while (diff > 0)
--diff, putc (' ', stderr);
while (diff < 0 && c == ' ')
++diff, c = getc (stream);
free (result);
}
else
fputs (word, stderr);
fflush (stderr);
obstack_free (&temporary_obstack, temporary_firstobj);
}
if (c == EOF)
break;
putc (c, stderr);
}
fclose (stream);
}
/* Decide whether the given symbol is:
a constructor (1), a destructor (2), or neither (0). */
static int
is_ctor_dtor (s)
char *s;
{
struct names { char *name; int len; int ret; int two_underscores; };
register struct names *p;
register int ch;
register char *orig_s = s;
static struct names special[] = {
#ifdef NO_DOLLAR_IN_LABEL
#ifdef NO_DOT_IN_LABEL
{ "GLOBAL__I_", sizeof ("GLOBAL__I_")-1, 1, 0 },
{ "GLOBAL__D_", sizeof ("GLOBAL__D_")-1, 2, 0 },
{ "GLOBAL__F_", sizeof ("GLOBAL__F_")-1, 5, 0 },
#else
{ "GLOBAL_.I.", sizeof ("GLOBAL_.I.")-1, 1, 0 },
{ "GLOBAL_.D.", sizeof ("GLOBAL_.D.")-1, 2, 0 },
{ "GLOBAL_.F.", sizeof ("GLOBAL_.F.")-1, 5, 0 },
#endif
#else
{ "GLOBAL_$I$", sizeof ("GLOBAL_$I$")-1, 1, 0 },
{ "GLOBAL_$D$", sizeof ("GLOBAL_$D$")-1, 2, 0 },
{ "GLOBAL_$F$", sizeof ("GLOBAL_$F$")-1, 5, 0 },
#endif
{ "GLOBAL__FI_", sizeof ("GLOBAL__FI_")-1, 3, 0 },
{ "GLOBAL__FD_", sizeof ("GLOBAL__FD_")-1, 4, 0 },
#ifdef CFRONT_LOSSAGE /* Do not collect cfront initialization functions.
cfront has its own linker procedure to collect them;
if collect2 gets them too, they get collected twice
when the cfront procedure is run and the compiler used
for linking happens to be GCC. */
{ "sti__", sizeof ("sti__")-1, 1, 1 },
{ "std__", sizeof ("std__")-1, 2, 1 },
#endif /* CFRONT_LOSSAGE */
{ NULL, 0, 0, 0 }
};
while ((ch = *s) == '_')
++s;
if (s == orig_s)
return 0;
for (p = &special[0]; p->len > 0; p++)
{
if (ch == p->name[0]
&& (!p->two_underscores || ((s - orig_s) >= 2))
&& strncmp(s, p->name, p->len) == 0)
{
return p->ret;
}
}
return 0;
}
/* Routine to add variables to the environment. */
#ifndef HAVE_PUTENV
int
putenv (str)
char *str;
{
#ifndef VMS /* nor about VMS */
extern char **environ;
char **old_environ = environ;
char **envp;
int num_envs = 0;
int name_len = 1;
char *p = str;
int ch;
while ((ch = *p++) != '\0' && ch != '=')
name_len++;
if (!ch)
abort ();
/* Search for replacing an existing environment variable, and
count the number of total environment variables. */
for (envp = old_environ; *envp; envp++)
{
num_envs++;
if (!strncmp (str, *envp, name_len))
{
*envp = str;
return 0;
}
}
/* Add a new environment variable */
environ = (char **) xmalloc (sizeof (char *) * (num_envs+2));
*environ = str;
bcopy ((char *) old_environ, (char *) (environ + 1),
sizeof (char *) * (num_envs+1));
return 0;
#endif /* VMS */
}
#endif /* HAVE_PUTENV */
/* By default, colon separates directories in a path. */
#ifndef PATH_SEPARATOR
#define PATH_SEPARATOR ':'
#endif
/* We maintain two prefix lists: one from COMPILER_PATH environment variable
and one from the PATH variable. */
static struct path_prefix cpath, path;
#ifdef CROSS_COMPILE
/* This is the name of the target machine. We use it to form the name
of the files to execute. */
static char *target_machine = TARGET_MACHINE;
#endif
/* Search for NAME using prefix list PPREFIX. We only look for executable
files.
Return 0 if not found, otherwise return its name, allocated with malloc. */
static char *
find_a_file (pprefix, name)
struct path_prefix *pprefix;
char *name;
{
char *temp;
struct prefix_list *pl;
int len = pprefix->max_len + strlen (name) + 1;
if (debug)
fprintf (stderr, "Looking for '%s'\n", name);
#ifdef EXECUTABLE_SUFFIX
len += strlen (EXECUTABLE_SUFFIX);
#endif
temp = xmalloc (len);
/* Determine the filename to execute (special case for absolute paths). */
if (*name == '/'
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
|| (*name && name[1] == ':')
#endif
)
{
if (access (name, X_OK) == 0)
{
strcpy (temp, name);
if (debug)
fprintf (stderr, " - found: absolute path\n");
return temp;
}
#ifdef EXECUTABLE_SUFFIX
/* Some systems have a suffix for executable files.
So try appending that. */
strcpy (temp, name);
strcat (temp, EXECUTABLE_SUFFIX);
if (access (temp, X_OK) == 0)
return temp;
#endif
if (debug)
fprintf (stderr, " - failed to locate using absolute path\n");
}
else
for (pl = pprefix->plist; pl; pl = pl->next)
{
strcpy (temp, pl->prefix);
strcat (temp, name);
if (access (temp, X_OK) == 0)
return temp;
#ifdef EXECUTABLE_SUFFIX
/* Some systems have a suffix for executable files.
So try appending that. */
strcat (temp, EXECUTABLE_SUFFIX);
if (access (temp, X_OK) == 0)
return temp;
#endif
}
if (debug && pprefix->plist == NULL)
fprintf (stderr, " - failed: no entries in prefix list\n");
free (temp);
return 0;
}
/* Add an entry for PREFIX to prefix list PPREFIX. */
static void
add_prefix (pprefix, prefix)
struct path_prefix *pprefix;
char *prefix;
{
struct prefix_list *pl, **prev;
int len;
if (pprefix->plist)
{
for (pl = pprefix->plist; pl->next; pl = pl->next)
;
prev = &pl->next;
}
else
prev = &pprefix->plist;
/* Keep track of the longest prefix */
len = strlen (prefix);
if (len > pprefix->max_len)
pprefix->max_len = len;
pl = (struct prefix_list *) xmalloc (sizeof (struct prefix_list));
pl->prefix = xstrdup (prefix);
if (*prev)
pl->next = *prev;
else
pl->next = (struct prefix_list *) 0;
*prev = pl;
}
/* Take the value of the environment variable ENV, break it into a path, and
add of the entries to PPREFIX. */
static void
prefix_from_env (env, pprefix)
char *env;
struct path_prefix *pprefix;
{
char *p;
GET_ENV_PATH_LIST (p, env);
if (p)
prefix_from_string (p, pprefix);
}
static void
prefix_from_string (p, pprefix)
char *p;
struct path_prefix *pprefix;
{
char *startp, *endp;
char *nstore = (char *) xmalloc (strlen (p) + 3);
if (debug)
fprintf (stderr, "Convert string '%s' into prefixes, separator = '%c'\n", p, PATH_SEPARATOR);
startp = endp = p;
while (1)
{
if (*endp == PATH_SEPARATOR || *endp == 0)
{
strncpy (nstore, startp, endp-startp);
if (endp == startp)
{
strcpy (nstore, "./");
}
else if (endp[-1] != '/')
{
nstore[endp-startp] = '/';
nstore[endp-startp+1] = 0;
}
else
nstore[endp-startp] = 0;
if (debug)
fprintf (stderr, " - add prefix: %s\n", nstore);
add_prefix (pprefix, nstore);
if (*endp == 0)
break;
endp = startp = endp + 1;
}
else
endp++;
}
}
/* Main program. */
int
main (argc, argv)
int argc;
char *argv[];
{
char *ld_suffix = "ld";
char *full_ld_suffix = ld_suffix;
char *real_ld_suffix = "real-ld";
char *collect_ld_suffix = "collect-ld";
char *nm_suffix = "nm";
char *full_nm_suffix = nm_suffix;
char *gnm_suffix = "gnm";
char *full_gnm_suffix = gnm_suffix;
#ifdef LDD_SUFFIX
char *ldd_suffix = LDD_SUFFIX;
char *full_ldd_suffix = ldd_suffix;
#endif
char *strip_suffix = "strip";
char *full_strip_suffix = strip_suffix;
char *gstrip_suffix = "gstrip";
char *full_gstrip_suffix = gstrip_suffix;
char *arg;
FILE *outf;
#ifdef COLLECT_EXPORT_LIST
FILE *exportf;
FILE *importf;
#endif
char *ld_file_name;
char *p;
char **c_argv;
char **c_ptr;
char **ld1_argv;
char **ld1;
char **ld2_argv;
char **ld2;
char **object_lst;
char **object;
int first_file;
int num_c_args = argc+9;
#if defined (COLLECT2_HOST_INITIALIZATION)
/* Perform system dependant initialization, if neccessary. */
COLLECT2_HOST_INITIALIZATION;
#endif
#ifdef HAVE_LC_MESSAGES
setlocale (LC_MESSAGES, "");
#endif
(void) bindtextdomain (PACKAGE, localedir);
(void) textdomain (PACKAGE);
/* Do not invoke xcalloc before this point, since locale needs to be
set first, in case a diagnostic is issued. */
ld1 = ld1_argv = (char **) xcalloc (sizeof (char *), argc+3);
ld2 = ld2_argv = (char **) xcalloc (sizeof (char *), argc+6);
object = object_lst = (char **) xcalloc (sizeof (char *), argc);
#ifdef DEBUG
debug = 1;
#endif
/* Parse command line early for instances of -debug. This allows
the debug flag to be set before functions like find_a_file()
are called. */
{
int i;
for (i = 1; argv[i] != NULL; i ++)
if (! strcmp (argv[i], "-debug"))
debug = 1;
vflag = debug;
}
#ifndef DEFAULT_A_OUT_NAME
output_file = "a.out";
#else
output_file = DEFAULT_A_OUT_NAME;
#endif
obstack_begin (&temporary_obstack, 0);
obstack_begin (&permanent_obstack, 0);
temporary_firstobj = (char *) obstack_alloc (&temporary_obstack, 0);
current_demangling_style = gnu_demangling;
p = getenv ("COLLECT_GCC_OPTIONS");
while (p && *p)
{
char *q = extract_string (&p);
if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
num_c_args++;
}
obstack_free (&temporary_obstack, temporary_firstobj);
++num_c_args;
c_ptr = c_argv = (char **) xcalloc (sizeof (char *), num_c_args);
if (argc < 2)
fatal ("no arguments");
#ifdef SIGQUIT
if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
signal (SIGQUIT, handler);
#endif
if (signal (SIGINT, SIG_IGN) != SIG_IGN)
signal (SIGINT, handler);
#ifdef SIGALRM
if (signal (SIGALRM, SIG_IGN) != SIG_IGN)
signal (SIGALRM, handler);
#endif
#ifdef SIGHUP
if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
signal (SIGHUP, handler);
#endif
if (signal (SIGSEGV, SIG_IGN) != SIG_IGN)
signal (SIGSEGV, handler);
#ifdef SIGBUS
if (signal (SIGBUS, SIG_IGN) != SIG_IGN)
signal (SIGBUS, handler);
#endif
/* Extract COMPILER_PATH and PATH into our prefix list. */
prefix_from_env ("COMPILER_PATH", &cpath);
prefix_from_env ("PATH", &path);
#ifdef CROSS_COMPILE
/* If we look for a program in the compiler directories, we just use
the short name, since these directories are already system-specific.
But it we look for a program in the system directories, we need to
qualify the program name with the target machine. */
full_ld_suffix
= xcalloc (strlen (ld_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_ld_suffix, target_machine);
strcat (full_ld_suffix, "-");
strcat (full_ld_suffix, ld_suffix);
#if 0
full_gld_suffix
= xcalloc (strlen (gld_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_gld_suffix, target_machine);
strcat (full_gld_suffix, "-");
strcat (full_gld_suffix, gld_suffix);
#endif
full_nm_suffix
= xcalloc (strlen (nm_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_nm_suffix, target_machine);
strcat (full_nm_suffix, "-");
strcat (full_nm_suffix, nm_suffix);
full_gnm_suffix
= xcalloc (strlen (gnm_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_gnm_suffix, target_machine);
strcat (full_gnm_suffix, "-");
strcat (full_gnm_suffix, gnm_suffix);
#ifdef LDD_SUFFIX
full_ldd_suffix
= xcalloc (strlen (ldd_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_ldd_suffix, target_machine);
strcat (full_ldd_suffix, "-");
strcat (full_ldd_suffix, ldd_suffix);
#endif
full_strip_suffix
= xcalloc (strlen (strip_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_strip_suffix, target_machine);
strcat (full_strip_suffix, "-");
strcat (full_strip_suffix, strip_suffix);
full_gstrip_suffix
= xcalloc (strlen (gstrip_suffix) + strlen (target_machine) + 2, 1);
strcpy (full_gstrip_suffix, target_machine);
strcat (full_gstrip_suffix, "-");
strcat (full_gstrip_suffix, gstrip_suffix);
#endif /* CROSS_COMPILE */
/* Try to discover a valid linker/nm/strip to use. */
/* Maybe we know the right file to use (if not cross). */
ld_file_name = 0;
#ifdef DEFAULT_LINKER
if (access (DEFAULT_LINKER, X_OK) == 0)
ld_file_name = DEFAULT_LINKER;
if (ld_file_name == 0)
#endif
#ifdef REAL_LD_FILE_NAME
ld_file_name = find_a_file (&path, REAL_LD_FILE_NAME);
if (ld_file_name == 0)
#endif
/* Search the (target-specific) compiler dirs for ld'. */
ld_file_name = find_a_file (&cpath, real_ld_suffix);
/* Likewise for `collect-ld'. */
if (ld_file_name == 0)
ld_file_name = find_a_file (&cpath, collect_ld_suffix);
/* Search the compiler directories for `ld'. We have protection against
recursive calls in find_a_file. */
if (ld_file_name == 0)
ld_file_name = find_a_file (&cpath, ld_suffix);
/* Search the ordinary system bin directories
for `ld' (if native linking) or `TARGET-ld' (if cross). */
if (ld_file_name == 0)
ld_file_name = find_a_file (&path, full_ld_suffix);
#ifdef REAL_NM_FILE_NAME
nm_file_name = find_a_file (&path, REAL_NM_FILE_NAME);
if (nm_file_name == 0)
#endif
nm_file_name = find_a_file (&cpath, gnm_suffix);
if (nm_file_name == 0)
nm_file_name = find_a_file (&path, full_gnm_suffix);
if (nm_file_name == 0)
nm_file_name = find_a_file (&cpath, nm_suffix);
if (nm_file_name == 0)
nm_file_name = find_a_file (&path, full_nm_suffix);
#ifdef LDD_SUFFIX
ldd_file_name = find_a_file (&cpath, ldd_suffix);
if (ldd_file_name == 0)
ldd_file_name = find_a_file (&path, full_ldd_suffix);
#endif
#ifdef REAL_STRIP_FILE_NAME
strip_file_name = find_a_file (&path, REAL_STRIP_FILE_NAME);
if (strip_file_name == 0)
#endif
strip_file_name = find_a_file (&cpath, gstrip_suffix);
if (strip_file_name == 0)
strip_file_name = find_a_file (&path, full_gstrip_suffix);
if (strip_file_name == 0)
strip_file_name = find_a_file (&cpath, strip_suffix);
if (strip_file_name == 0)
strip_file_name = find_a_file (&path, full_strip_suffix);
/* Determine the full path name of the C compiler to use. */
c_file_name = getenv ("COLLECT_GCC");
if (c_file_name == 0)
{
#ifdef CROSS_COMPILE
c_file_name = xcalloc (sizeof ("gcc-") + strlen (target_machine) + 1, 1);
strcpy (c_file_name, target_machine);
strcat (c_file_name, "-gcc");
#else
c_file_name = "gcc";
#endif
}
p = find_a_file (&cpath, c_file_name);
/* Here it should be safe to use the system search path since we should have
already qualified the name of the compiler when it is needed. */
if (p == 0)
p = find_a_file (&path, c_file_name);
if (p)
c_file_name = p;
*ld1++ = *ld2++ = ld_file_name;
/* Make temp file names. */
c_file = make_temp_file (".c");
o_file = make_temp_file (".o");
#ifdef COLLECT_EXPORT_LIST
export_file = make_temp_file (".x");
import_file = make_temp_file (".p");
#endif
ldout = make_temp_file (".ld");
*c_ptr++ = c_file_name;
*c_ptr++ = "-x";
*c_ptr++ = "c";
*c_ptr++ = "-c";
*c_ptr++ = "-o";
*c_ptr++ = o_file;
#ifdef COLLECT_EXPORT_LIST
/* Generate a list of directories from LIBPATH. */
prefix_from_env ("LIBPATH", &libpath_lib_dirs);
/* Add to this list also two standard directories where
AIX loader always searches for libraries. */
add_prefix (&libpath_lib_dirs, "/lib");
add_prefix (&libpath_lib_dirs, "/usr/lib");
#endif
/* Get any options that the upper GCC wants to pass to the sub-GCC.
AIX support needs to know if -shared has been specified before
parsing commandline arguments. */
p = getenv ("COLLECT_GCC_OPTIONS");
while (p && *p)
{
char *q = extract_string (&p);
if (*q == '-' && (q[1] == 'm' || q[1] == 'f'))
*c_ptr++ = obstack_copy0 (&permanent_obstack, q, strlen (q));
if (strcmp (q, "-EL") == 0 || strcmp (q, "-EB") == 0)
*c_ptr++ = obstack_copy0 (&permanent_obstack, q, strlen (q));
if (strncmp (q, "-shared", sizeof ("-shared") - 1) == 0)
shared_obj = 1;
}
obstack_free (&temporary_obstack, temporary_firstobj);
*c_ptr++ = "-fno-exceptions";
/* !!! When GCC calls collect2,
it does not know whether it is calling collect2 or ld.
So collect2 cannot meaningfully understand any options
except those ld understands.
If you propose to make GCC pass some other option,
just imagine what will happen if ld is really ld!!! */
/* Parse arguments. Remember output file spec, pass the rest to ld. */
/* After the first file, put in the c++ rt0. */
first_file = 1;
while ((arg = *++argv) != (char *) 0)
{
*ld1++ = *ld2++ = arg;
if (arg[0] == '-')
{
switch (arg[1])
{
#ifdef COLLECT_EXPORT_LIST
/* We want to disable automatic exports on AIX when user
explicitly puts an export list in command line */
case 'b':
if (arg[2] == 'E' || strncmp (&arg[2], "export", 6) == 0)
export_flag = 1;
else if (arg[2] == '6' && arg[3] == '4')
aix64_flag = 1;
break;
#endif
case 'd':
if (!strcmp (arg, "-debug"))
{
/* Already parsed. */
ld1--;
ld2--;
}
break;
case 'l':
if (first_file)
{
/* place o_file BEFORE this argument! */
first_file = 0;
ld2--;
*ld2++ = o_file;
*ld2++ = arg;
}
#ifdef COLLECT_EXPORT_LIST
{
/* Resolving full library name. */
char *s = resolve_lib_name (arg+2);
/* If we will use an import list for this library,
we should exclude it from ld args. */
if (use_import_list (s))
{
ld1--;
ld2--;
}
/* Saving a full library name. */
add_to_list (&libs, s);
}
#endif
break;
#ifdef COLLECT_EXPORT_LIST
/* Saving directories where to search for libraries. */
case 'L':
add_prefix (&cmdline_lib_dirs, arg+2);
break;
#endif
case 'o':
if (arg[2] == '\0')
output_file = *ld1++ = *ld2++ = *++argv;
else
output_file = &arg[2];
break;
case 'r':
if (arg[2] == '\0')
rflag = 1;
break;
case 's':
if (arg[2] == '\0' && do_collecting)
{
/* We must strip after the nm run, otherwise C++ linking
will not work. Thus we strip in the second ld run, or
else with strip if there is no second ld run. */
strip_flag = 1;
ld1--;
}
break;
case 'v':
if (arg[2] == '\0')
vflag = 1;
break;
}
}
else if ((p = rindex (arg, '.')) != (char *) 0
&& (strcmp (p, ".o") == 0 || strcmp (p, ".a") == 0
|| strcmp (p, ".so") == 0))
{
if (first_file)
{
first_file = 0;
if (p[1] == 'o')
*ld2++ = o_file;
else
{
/* place o_file BEFORE this argument! */
ld2--;
*ld2++ = o_file;
*ld2++ = arg;
}
}
if (p[1] == 'o')
*object++ = arg;
#ifdef COLLECT_EXPORT_LIST
/* libraries can be specified directly, i.e. without -l flag. */
else
{
/* If we will use an import list for this library,
we should exclude it from ld args. */
if (use_import_list (arg))
{
ld1--;
ld2--;
}
/* Saving a full library name. */
add_to_list (&libs, arg);
}
#endif
}
}
#ifdef COLLECT_EXPORT_LIST
/* This is added only for debugging purposes. */
if (debug)
{
fprintf (stderr, "List of libraries:\n");
dump_list (stderr, "\t", libs.first);
}
/* The AIX linker will discard static constructors in object files if
nothing else in the file is referenced, so look at them first. */
{
char **export_object_lst = object_lst;
while (export_object_lst < object)
scan_prog_file (*export_object_lst++, PASS_OBJ);
}
{
struct id *list = libs.first;
for (; list; list = list->next)
scan_prog_file (list->name, PASS_FIRST);
}
{
char *buf1 = alloca (strlen (export_file) + 5);
char *buf2 = alloca (strlen (import_file) + 5);
sprintf (buf1, "-bE:%s", export_file);
sprintf (buf2, "-bI:%s", import_file);
*ld1++ = buf1;
*ld2++ = buf1;
*ld1++ = buf2;
*ld2++ = buf2;
exportf = fopen (export_file, "w");
if (exportf == (FILE *) 0)
fatal_perror ("fopen %s", export_file);
write_export_file (exportf);
if (fclose (exportf))
fatal_perror ("fclose %s", export_file);
importf = fopen (import_file, "w");
if (importf == (FILE *) 0)
fatal_perror ("%s", import_file);
write_import_file (importf);
if (fclose (importf))
fatal_perror ("fclose %s", import_file);
}
#endif
*c_ptr++ = c_file;
*object = *c_ptr = *ld1 = (char *) 0;
if (vflag)
{
notice ("collect2 version %s", version_string);
#ifdef TARGET_VERSION
TARGET_VERSION;
#endif
fprintf (stderr, "\n");
}
if (debug)
{
char *ptr;
fprintf (stderr, "ld_file_name = %s\n",
(ld_file_name ? ld_file_name : "not found"));
fprintf (stderr, "c_file_name = %s\n",
(c_file_name ? c_file_name : "not found"));
fprintf (stderr, "nm_file_name = %s\n",
(nm_file_name ? nm_file_name : "not found"));
#ifdef LDD_SUFFIX
fprintf (stderr, "ldd_file_name = %s\n",
(ldd_file_name ? ldd_file_name : "not found"));
#endif
fprintf (stderr, "strip_file_name = %s\n",
(strip_file_name ? strip_file_name : "not found"));
fprintf (stderr, "c_file = %s\n",
(c_file ? c_file : "not found"));
fprintf (stderr, "o_file = %s\n",
(o_file ? o_file : "not found"));
ptr = getenv ("COLLECT_GCC_OPTIONS");
if (ptr)
fprintf (stderr, "COLLECT_GCC_OPTIONS = %s\n", ptr);
ptr = getenv ("COLLECT_GCC");
if (ptr)
fprintf (stderr, "COLLECT_GCC = %s\n", ptr);
ptr = getenv ("COMPILER_PATH");
if (ptr)
fprintf (stderr, "COMPILER_PATH = %s\n", ptr);
ptr = getenv ("LIBRARY_PATH");
if (ptr)
fprintf (stderr, "LIBRARY_PATH = %s\n", ptr);
fprintf (stderr, "\n");
}
/* Load the program, searching all libraries and attempting to provide
undefined symbols from repository information. */
/* On AIX we do this later. */
#ifndef COLLECT_EXPORT_LIST
- do_tlink (ld1_argv, object_lst);
+ do_tlink (ld1_argv, object_lst);
#endif
/* If -r or they will be run via some other method, do not build the
constructor or destructor list, just return now. */
if (rflag
#ifndef COLLECT_EXPORT_LIST
|| ! do_collecting
#endif
)
{
#ifdef COLLECT_EXPORT_LIST
+ /* Do the link we avoided above if we are exiting. */
+ do_tlink (ld1_argv, object_lst);
+
/* But make sure we delete the export file we may have created. */
if (export_file != 0 && export_file[0])
maybe_unlink (export_file);
if (import_file != 0 && import_file[0])
maybe_unlink (import_file);
#endif
maybe_unlink (c_file);
maybe_unlink (o_file);
return 0;
}
/* Examine the namelist with nm and search it for static constructors
and destructors to call.
Write the constructor and destructor tables to a .s file and reload. */
/* On AIX we already done scanning for global constructors/destructors. */
#ifndef COLLECT_EXPORT_LIST
scan_prog_file (output_file, PASS_FIRST);
#endif
#ifdef SCAN_LIBRARIES
scan_libraries (output_file);
#endif
if (debug)
{
notice ("%d constructor(s) found\n", constructors.number);
notice ("%d destructor(s) found\n", destructors.number);
notice ("%d frame table(s) found\n", frame_tables.number);
}
if (constructors.number == 0 && destructors.number == 0
&& frame_tables.number == 0
#if defined (SCAN_LIBRARIES) || defined (COLLECT_EXPORT_LIST)
/* If we will be running these functions ourselves, we want to emit
stubs into the shared library so that we do not have to relink
dependent programs when we add static objects. */
&& ! shared_obj
#endif
)
{
#ifdef COLLECT_EXPORT_LIST
/* Doing tlink without additional code generation */
do_tlink (ld1_argv, object_lst);
#endif
/* Strip now if it was requested on the command line. */
if (strip_flag)
{
char **strip_argv = (char **) xcalloc (sizeof (char *), 3);
strip_argv[0] = strip_file_name;
strip_argv[1] = output_file;
strip_argv[2] = (char *) 0;
fork_execute ("strip", strip_argv);
}
#ifdef COLLECT_EXPORT_LIST
maybe_unlink (export_file);
maybe_unlink (import_file);
#endif
maybe_unlink (c_file);
maybe_unlink (o_file);
return 0;
}
/* Sort ctor and dtor lists by priority. */
sort_ids (&constructors);
sort_ids (&destructors);
maybe_unlink(output_file);
outf = fopen (c_file, "w");
if (outf == (FILE *) 0)
fatal_perror ("fopen %s", c_file);
write_c_file (outf, c_file);
if (fclose (outf))
fatal_perror ("fclose %s", c_file);
/* Tell the linker that we have initializer and finalizer functions. */
#ifdef LD_INIT_SWITCH
*ld2++ = LD_INIT_SWITCH;
*ld2++ = initname;
*ld2++ = LD_FINI_SWITCH;
*ld2++ = fininame;
#endif
*ld2 = (char*) 0;
#ifdef COLLECT_EXPORT_LIST
if (shared_obj)
{
add_to_list (&exports, initname);
add_to_list (&exports, fininame);
add_to_list (&exports, "_GLOBAL__DI");
add_to_list (&exports, "_GLOBAL__DD");
exportf = fopen (export_file, "w");
if (exportf == (FILE *) 0)
fatal_perror ("fopen %s", export_file);
write_export_file (exportf);
if (fclose (exportf))
fatal_perror ("fclose %s", export_file);
}
#endif
if (debug)
{
fprintf (stderr, "\n========== output_file = %s, c_file = %s\n",
output_file, c_file);
write_c_file (stderr, "stderr");
fprintf (stderr, "========== end of c_file\n\n");
#ifdef COLLECT_EXPORT_LIST
fprintf (stderr, "\n========== export_file = %s\n", export_file);
write_export_file (stderr);
fprintf (stderr, "========== end of export_file\n\n");
#endif
}
/* Assemble the constructor and destructor tables.
Link the tables in with the rest of the program. */
fork_execute ("gcc", c_argv);
#ifdef COLLECT_EXPORT_LIST
/* On AIX we must call tlink because of possible templates resolution */
do_tlink (ld2_argv, object_lst);
#else
/* Otherwise, simply call ld because tlink is already done */
fork_execute ("ld", ld2_argv);
/* Let scan_prog_file do any final mods (OSF/rose needs this for
constructors/destructors in shared libraries. */
scan_prog_file (output_file, PASS_SECOND);
#endif
maybe_unlink (c_file);
maybe_unlink (o_file);
#ifdef COLLECT_EXPORT_LIST
maybe_unlink (export_file);
maybe_unlink (import_file);
#endif
return 0;
}
/* Wait for a process to finish, and exit if a non-zero status is found. */
int
collect_wait (prog)
char *prog;
{
int status;
pwait (pexecute_pid, &status, 0);
if (status)
{
if (WIFSIGNALED (status))
{
int sig = WTERMSIG (status);
error ((status & 0200
? "%s terminated with signal %d [%s]"
: "%s terminated with signal %d [%s], core dumped"),
prog,
sig,
my_strsignal(sig));
collect_exit (FATAL_EXIT_CODE);
}
if (WIFEXITED (status))
return WEXITSTATUS (status);
}
return 0;
}
static void
do_wait (prog)
char *prog;
{
int ret = collect_wait (prog);
if (ret != 0)
{
error ("%s returned %d exit status", prog, ret);
collect_exit (ret);
}
}
/* Execute a program, and wait for the reply. */
void
collect_execute (prog, argv, redir)
char *prog;
char **argv;
char *redir;
{
char *errmsg_fmt;
char *errmsg_arg;
int redir_handle = -1;
int stdout_save = -1;
int stderr_save = -1;
if (vflag || debug)
{
char **p_argv;
char *str;
if (argv[0])
fprintf (stderr, "%s", argv[0]);
else
notice ("[cannot find %s]", prog);
for (p_argv = &argv[1]; (str = *p_argv) != (char *) 0; p_argv++)
fprintf (stderr, " %s", str);
fprintf (stderr, "\n");
}
fflush (stdout);
fflush (stderr);
/* If we cannot find a program we need, complain error. Do this here
since we might not end up needing something that we could not find. */
if (argv[0] == 0)
fatal ("cannot find `%s'", prog);
if (redir)
{
/* Open response file. */
redir_handle = open (redir, O_WRONLY | O_TRUNC | O_CREAT);
/* Duplicate the stdout and stderr file handles
so they can be restored later. */
stdout_save = dup (STDOUT_FILENO);
if (stdout_save == -1)
fatal_perror ("redirecting stdout: %s", redir);
stderr_save = dup (STDERR_FILENO);
if (stderr_save == -1)
fatal_perror ("redirecting stdout: %s", redir);
/* Redirect stdout & stderr to our response file. */
dup2 (redir_handle, STDOUT_FILENO);
dup2 (redir_handle, STDERR_FILENO);
}
pexecute_pid = pexecute (argv[0], argv, argv[0], NULL,
&errmsg_fmt, &errmsg_arg,
(PEXECUTE_FIRST | PEXECUTE_LAST | PEXECUTE_SEARCH));
if (redir)
{
/* Restore stdout and stderr to their previous settings. */
dup2 (stdout_save, STDOUT_FILENO);
dup2 (stderr_save, STDERR_FILENO);
/* Close reponse file. */
close (redir_handle);
}
if (pexecute_pid == -1)
fatal_perror (errmsg_fmt, errmsg_arg);
}
static void
fork_execute (prog, argv)
char *prog;
char **argv;
{
collect_execute (prog, argv, NULL);
do_wait (prog);
}
/* Unlink a file unless we are debugging. */
static void
maybe_unlink (file)
char *file;
{
if (!debug)
unlink (file);
else
notice ("[Leaving %s]\n", file);
}
static long sequence_number = 0;
/* Add a name to a linked list. */
static void
add_to_list (head_ptr, name)
struct head *head_ptr;
char *name;
{
struct id *newid
= (struct id *) xcalloc (sizeof (struct id) + strlen (name), 1);
struct id *p;
strcpy (newid->name, name);
if (head_ptr->first)
head_ptr->last->next = newid;
else
head_ptr->first = newid;
/* Check for duplicate symbols. */
for (p = head_ptr->first;
strcmp (name, p->name) != 0;
p = p->next)
;
if (p != newid)
{
head_ptr->last->next = 0;
free (newid);
return;
}
newid->sequence = ++sequence_number;
head_ptr->last = newid;
head_ptr->number++;
}
/* Grab the init priority number from an init function name that
looks like "_GLOBAL_.I.12345.foo". */
static int
extract_init_priority (name)
char *name;
{
int pos = 0, pri;
while (name[pos] == '_')
++pos;
pos += 10; /* strlen ("GLOBAL__X_") */
/* Extract init_p number from ctor/dtor name. */
pri = atoi (name + pos);
return pri ? pri : DEFAULT_INIT_PRIORITY;
}
/* Insertion sort the ids from ctor/dtor list HEAD_PTR in descending order.
ctors will be run from right to left, dtors from left to right. */
static void
sort_ids (head_ptr)
struct head *head_ptr;
{
/* id holds the current element to insert. id_next holds the next
element to insert. id_ptr iterates through the already sorted elements
looking for the place to insert id. */
struct id *id, *id_next, **id_ptr;
id = head_ptr->first;
/* We don't have any sorted elements yet. */
head_ptr->first = NULL;
for (; id; id = id_next)
{
id_next = id->next;
id->sequence = extract_init_priority (id->name);
for (id_ptr = &(head_ptr->first); ; id_ptr = &((*id_ptr)->next))
if (*id_ptr == NULL
/* If the sequence numbers are the same, we put the id from the
file later on the command line later in the list. */
|| id->sequence > (*id_ptr)->sequence
/* Hack: do lexical compare, too.
|| (id->sequence == (*id_ptr)->sequence
&& strcmp (id->name, (*id_ptr)->name) > 0) */
)
{
id->next = *id_ptr;
*id_ptr = id;
break;
}
}
/* Now set the sequence numbers properly so write_c_file works. */
for (id = head_ptr->first; id; id = id->next)
id->sequence = ++sequence_number;
}
/* Write: `prefix', the names on list LIST, `suffix'. */
static void
write_list (stream, prefix, list)
FILE *stream;
char *prefix;
struct id *list;
{
while (list)
{
fprintf (stream, "%sx%d,\n", prefix, list->sequence);
list = list->next;
}
}
#ifdef COLLECT_EXPORT_LIST
/* This function is really used only on AIX, but may be useful. */
static int
is_in_list (prefix, list)
char *prefix;
struct id *list;
{
while (list)
{
if (!strcmp (prefix, list->name)) return 1;
list = list->next;
}
return 0;
}
#endif
/* Added for debugging purpose. */
#ifdef COLLECT_EXPORT_LIST
static void
dump_list (stream, prefix, list)
FILE *stream;
char *prefix;
struct id *list;
{
while (list)
{
fprintf (stream, "%s%s,\n", prefix, list->name);
list = list->next;
}
}
#endif
#if 0
static void
dump_prefix_list (stream, prefix, list)
FILE *stream;
char *prefix;
struct prefix_list *list;
{
while (list)
{
fprintf (stream, "%s%s,\n", prefix, list->prefix);
list = list->next;
}
}
#endif
static void
write_list_with_asm (stream, prefix, list)
FILE *stream;
char *prefix;
struct id *list;
{
while (list)
{
fprintf (stream, "%sx%d __asm__ (\"%s\");\n",
prefix, list->sequence, list->name);
list = list->next;
}
}
/* Write out the constructor and destructor tables statically (for a shared
object), along with the functions to execute them. */
static void
write_c_file_stat (stream, name)
FILE *stream;
char *name;
{
char *prefix, *p, *q;
int frames = (frame_tables.number > 0);
/* Figure out name of output_file, stripping off .so version. */
p = rindex (output_file, '/');
if (p == 0)
p = (char *) output_file;
else
p++;
q = p;
while (q)
{
q = index (q,'.');
if (q == 0)
{
q = p + strlen (p);
break;
}
else
{
if (strncmp (q, ".so", 3) == 0)
{
q += 3;
break;
}
else
q++;
}
}
/* q points to null at end of the string (or . of the .so version) */
prefix = xmalloc (q - p + 1);
strncpy (prefix, p, q - p);
prefix[q - p] = 0;
for (q = prefix; *q; q++)
if (!ISALNUM ((unsigned char)*q))
*q = '_';
if (debug)
notice ("\nwrite_c_file - output name is %s, prefix is %s\n",
output_file, prefix);
#define INIT_NAME_FORMAT "_GLOBAL__FI_%s"
initname = xmalloc (strlen (prefix) + sizeof (INIT_NAME_FORMAT) - 2);
sprintf (initname, INIT_NAME_FORMAT, prefix);
#define FINI_NAME_FORMAT "_GLOBAL__FD_%s"
fininame = xmalloc (strlen (prefix) + sizeof (FINI_NAME_FORMAT) - 2);
sprintf (fininame, FINI_NAME_FORMAT, prefix);
free (prefix);
/* Write the tables as C code */
fprintf (stream, "static int count;\n");
fprintf (stream, "typedef void entry_pt();\n");
write_list_with_asm (stream, "extern entry_pt ", constructors.first);
if (frames)
{
write_list_with_asm (stream, "extern void *", frame_tables.first);
fprintf (stream, "\tstatic void *frame_table[] = {\n");
write_list (stream, "\t\t&", frame_tables.first);
fprintf (stream, "\t0\n};\n");
/* This must match what's in frame.h. */
fprintf (stream, "struct object {\n");
fprintf (stream, " void *pc_begin;\n");
fprintf (stream, " void *pc_end;\n");
fprintf (stream, " void *fde_begin;\n");
fprintf (stream, " void *fde_array;\n");
fprintf (stream, " __SIZE_TYPE__ count;\n");
fprintf (stream, " struct object *next;\n");
fprintf (stream, "};\n");
fprintf (stream, "extern void __register_frame_info_table (void *, struct object *);\n");
fprintf (stream, "extern void *__deregister_frame_info (void *);\n");
fprintf (stream, "static void reg_frame () {\n");
fprintf (stream, "\tstatic struct object ob;\n");
fprintf (stream, "\t__register_frame_info_table (frame_table, &ob);\n");
fprintf (stream, "\t}\n");
fprintf (stream, "static void dereg_frame () {\n");
fprintf (stream, "\t__deregister_frame_info (frame_table);\n");
fprintf (stream, "\t}\n");
}
fprintf (stream, "void %s() {\n", initname);
if (constructors.number > 0 || frames)
{
fprintf (stream, "\tstatic entry_pt *ctors[] = {\n");
write_list (stream, "\t\t", constructors.first);
if (frames)
fprintf (stream, "\treg_frame,\n");
fprintf (stream, "\t};\n");
fprintf (stream, "\tentry_pt **p;\n");
fprintf (stream, "\tif (count++ != 0) return;\n");
fprintf (stream, "\tp = ctors + %d;\n", constructors.number + frames);
fprintf (stream, "\twhile (p > ctors) (*--p)();\n");
}
else
fprintf (stream, "\t++count;\n");
fprintf (stream, "}\n");
write_list_with_asm (stream, "extern entry_pt ", destructors.first);
fprintf (stream, "void %s() {\n", fininame);
if (destructors.number > 0 || frames)
{
fprintf (stream, "\tstatic entry_pt *dtors[] = {\n");
write_list (stream, "\t\t", destructors.first);
if (frames)
fprintf (stream, "\tdereg_frame,\n");
fprintf (stream, "\t};\n");
fprintf (stream, "\tentry_pt **p;\n");
fprintf (stream, "\tif (--count != 0) return;\n");
fprintf (stream, "\tp = dtors;\n");
fprintf (stream, "\twhile (p < dtors + %d) (*p++)();\n",
destructors.number + frames);
}
fprintf (stream, "}\n");
if (shared_obj)
{
fprintf (stream, "void _GLOBAL__DI() {\n\t%s();\n}\n", initname);
fprintf (stream, "void _GLOBAL__DD() {\n\t%s();\n}\n", fininame);
}
}
/* Write the constructor/destructor tables. */
#ifndef LD_INIT_SWITCH
static void
write_c_file_glob (stream, name)
FILE *stream;
char *name;
{
/* Write the tables as C code */
int frames = (frame_tables.number > 0);
fprintf (stream, "typedef void entry_pt();\n\n");
write_list_with_asm (stream, "extern entry_pt ", constructors.first);
if (frames)
{
write_list_with_asm (stream, "extern void *", frame_tables.first);
fprintf (stream, "\tstatic void *frame_table[] = {\n");
write_list (stream, "\t\t&", frame_tables.first);
fprintf (stream, "\t0\n};\n");
/* This must match what's in frame.h. */
fprintf (stream, "struct object {\n");
fprintf (stream, " void *pc_begin;\n");
fprintf (stream, " void *pc_end;\n");
fprintf (stream, " void *fde_begin;\n");
fprintf (stream, " void *fde_array;\n");
fprintf (stream, " __SIZE_TYPE__ count;\n");
fprintf (stream, " struct object *next;\n");
fprintf (stream, "};\n");
fprintf (stream, "extern void __register_frame_info_table (void *, struct object *);\n");
fprintf (stream, "extern void *__deregister_frame_info (void *);\n");
fprintf (stream, "static void reg_frame () {\n");
fprintf (stream, "\tstatic struct object ob;\n");
fprintf (stream, "\t__register_frame_info_table (frame_table, &ob);\n");
fprintf (stream, "\t}\n");
fprintf (stream, "static void dereg_frame () {\n");
fprintf (stream, "\t__deregister_frame_info (frame_table);\n");
fprintf (stream, "\t}\n");
}
fprintf (stream, "\nentry_pt * __CTOR_LIST__[] = {\n");
fprintf (stream, "\t(entry_pt *) %d,\n", constructors.number + frames);
write_list (stream, "\t", constructors.first);
if (frames)
fprintf (stream, "\treg_frame,\n");
fprintf (stream, "\t0\n};\n\n");
write_list_with_asm (stream, "extern entry_pt ", destructors.first);
fprintf (stream, "\nentry_pt * __DTOR_LIST__[] = {\n");
fprintf (stream, "\t(entry_pt *) %d,\n", destructors.number + frames);
write_list (stream, "\t", destructors.first);
if (frames)
fprintf (stream, "\tdereg_frame,\n");
fprintf (stream, "\t0\n};\n\n");
fprintf (stream, "extern entry_pt %s;\n", NAME__MAIN);
fprintf (stream, "entry_pt *__main_reference = %s;\n\n", NAME__MAIN);
}
#endif /* ! LD_INIT_SWITCH */
static void
write_c_file (stream, name)
FILE *stream;
char *name;
{
fprintf (stream, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
#ifndef LD_INIT_SWITCH
if (! shared_obj)
write_c_file_glob (stream, name);
else
#endif
write_c_file_stat (stream, name);
fprintf (stream, "#ifdef __cplusplus\n}\n#endif\n");
}
#ifdef COLLECT_EXPORT_LIST
static void
write_export_file (stream)
FILE *stream;
{
struct id *list = exports.first;
for (; list; list = list->next)
fprintf (stream, "%s\n", list->name);
}
static void
write_import_file (stream)
FILE *stream;
{
struct id *list = imports.first;
fprintf (stream, "%s\n", "#! .");
for (; list; list = list->next)
fprintf (stream, "%s\n", list->name);
}
#endif
#ifdef OBJECT_FORMAT_NONE
/* Generic version to scan the name list of the loaded program for
the symbols g++ uses for static constructors and destructors.
The constructor table begins at __CTOR_LIST__ and contains a count
of the number of pointers (or -1 if the constructors are built in a
separate section by the linker), followed by the pointers to the
constructor functions, terminated with a null pointer. The
destructor table has the same format, and begins at __DTOR_LIST__. */
static void
scan_prog_file (prog_name, which_pass)
char *prog_name;
enum pass which_pass;
{
void (*int_handler) ();
void (*quit_handler) ();
char *nm_argv[4];
int pid;
int argc = 0;
int pipe_fd[2];
char *p, buf[1024];
FILE *inf;
if (which_pass == PASS_SECOND)
return;
/* If we do not have an `nm', complain. */
if (nm_file_name == 0)
fatal ("cannot find `nm'");
nm_argv[argc++] = nm_file_name;
if (NM_FLAGS[0] != '\0')
nm_argv[argc++] = NM_FLAGS;
nm_argv[argc++] = prog_name;
nm_argv[argc++] = (char *) 0;
if (pipe (pipe_fd) < 0)
fatal_perror ("pipe");
inf = fdopen (pipe_fd[0], "r");
if (inf == (FILE *) 0)
fatal_perror ("fdopen");
/* Trace if needed. */
if (vflag)
{
char **p_argv;
char *str;
for (p_argv = &nm_argv[0]; (str = *p_argv) != (char *) 0; p_argv++)
fprintf (stderr, " %s", str);
fprintf (stderr, "\n");
}
fflush (stdout);
fflush (stderr);
/* Spawn child nm on pipe */
pid = vfork ();
if (pid == -1)
fatal_perror (VFORK_STRING);
if (pid == 0) /* child context */
{
/* setup stdout */
if (dup2 (pipe_fd[1], 1) < 0)
fatal_perror ("dup2 %d 1", pipe_fd[1]);
if (close (pipe_fd[0]) < 0)
fatal_perror ("close %d", pipe_fd[0]);
if (close (pipe_fd[1]) < 0)
fatal_perror ("close %d", pipe_fd[1]);
execv (nm_file_name, nm_argv);
fatal_perror ("execvp %s", nm_file_name);
}
/* Parent context from here on. */
int_handler = (void (*) ())signal (SIGINT, SIG_IGN);
#ifdef SIGQUIT
quit_handler = (void (*) ())signal (SIGQUIT, SIG_IGN);
#endif
if (close (pipe_fd[1]) < 0)
fatal_perror ("close %d", pipe_fd[1]);
if (debug)
fprintf (stderr, "\nnm output with constructors/destructors.\n");
/* Read each line of nm output. */
while (fgets (buf, sizeof buf, inf) != (char *) 0)
{
int ch, ch2;
char *name, *end;
/* If it contains a constructor or destructor name, add the name
to the appropriate list. */
for (p = buf; (ch = *p) != '\0' && ch != '\n' && ch != '_'; p++)
if (ch == ' ' && p[1] == 'U' && p[2] == ' ')
break;
if (ch != '_')
continue;
name = p;
/* Find the end of the symbol name.
Do not include `|', because Encore nm can tack that on the end. */
for (end = p; (ch2 = *end) != '\0' && !ISSPACE (ch2) && ch2 != '|';
end++)
continue;
*end = '\0';
switch (is_ctor_dtor (name))
{
case 1:
if (which_pass != PASS_LIB)
add_to_list (&constructors, name);
break;
case 2:
if (which_pass != PASS_LIB)
add_to_list (&destructors, name);
break;
case 3:
if (which_pass != PASS_LIB)
fatal ("init function found in object %s", prog_name);
#ifndef LD_INIT_SWITCH
add_to_list (&constructors, name);
#endif
break;
case 4:
if (which_pass != PASS_LIB)
fatal ("fini function found in object %s", prog_name);
#ifndef LD_FINI_SWITCH
add_to_list (&destructors, name);
#endif
break;
case 5:
if (which_pass != PASS_LIB)
add_to_list (&frame_tables, name);
break;
default: /* not a constructor or destructor */
continue;
}
if (debug)
fprintf (stderr, "\t%s\n", buf);
}
if (debug)
fprintf (stderr, "\n");
if (fclose (inf) != 0)
fatal_perror ("fclose");
do_wait (nm_file_name);
signal (SIGINT, int_handler);
#ifdef SIGQUIT
signal (SIGQUIT, quit_handler);
#endif
}
#if SUNOS4_SHARED_LIBRARIES
/* Routines to scan the SunOS 4 _DYNAMIC structure to find shared libraries
that the output file depends upon and their initialization/finalization
routines, if any. */
#include <a.out.h>
#include <fcntl.h>
#include <link.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <unistd.h>
#include <sys/dir.h>
/* pointers to the object file */
unsigned object; /* address of memory mapped file */
unsigned objsize; /* size of memory mapped to file */
char * code; /* pointer to code segment */
char * data; /* pointer to data segment */
struct nlist *symtab; /* pointer to symbol table */
struct link_dynamic *ld;
struct link_dynamic_2 *ld_2;
struct head libraries;
/* Map the file indicated by NAME into memory and store its address. */
static void
mapfile (name)
char *name;
{
int fp;
struct stat s;
if ((fp = open (name, O_RDONLY)) == -1)
fatal ("unable to open file '%s'", name);
if (fstat (fp, &s) == -1)
fatal ("unable to stat file '%s'", name);
objsize = s.st_size;
object = (unsigned) mmap (0, objsize, PROT_READ|PROT_WRITE, MAP_PRIVATE,
fp, 0);
if (object == -1)
fatal ("unable to mmap file '%s'", name);
close (fp);
}
/* Helpers for locatelib. */
static char *libname;
static int
libselect (d)
struct direct *d;
{
return (strncmp (libname, d->d_name, strlen (libname)) == 0);
}
/* If one file has an additional numeric extension past LIBNAME, then put
that one first in the sort. If both files have additional numeric
extensions, then put the one with the higher number first in the sort.
We must verify that the extension is numeric, because Sun saves the
original versions of patched libraries with a .FCS extension. Files with
invalid extensions must go last in the sort, so that they will not be used. */
static int
libcompare (d1, d2)
struct direct **d1, **d2;
{
int i1, i2 = strlen (libname);
char *e1 = (*d1)->d_name + i2;
char *e2 = (*d2)->d_name + i2;
while (*e1 && *e2 && *e1 == '.' && *e2 == '.'
&& e1[1] && ISDIGIT (e1[1]) && e2[1] && ISDIGIT (e2[1]))
{
++e1;
++e2;
i1 = strtol (e1, &e1, 10);
i2 = strtol (e2, &e2, 10);
if (i1 != i2)
return i1 - i2;
}
if (*e1)
{
/* It has a valid numeric extension, prefer this one. */
if (*e1 == '.' && e1[1] && ISDIGIT (e1[1]))
return 1;
/* It has a invalid numeric extension, must prefer the other one. */
else
return -1;
}
else if (*e2)
{
/* It has a valid numeric extension, prefer this one. */
if (*e2 == '.' && e2[1] && ISDIGIT (e2[1]))
return -1;
/* It has a invalid numeric extension, must prefer the other one. */
else
return 1;
}
else
return 0;
}
/* Given the name NAME of a dynamic dependency, find its pathname and add
it to the list of libraries. */
static void
locatelib (name)
char *name;
{
static char **l;
static int cnt;
char buf[MAXPATHLEN];
char *p, *q;
char **pp;
if (l == 0)
{
char *ld_rules;
char *ldr = 0;
/* counting elements in array, need 1 extra for null */
cnt = 1;
ld_rules = (char *) (ld_2->ld_rules + code);
if (ld_rules)
{
cnt++;
for (; *ld_rules != 0; ld_rules++)
if (*ld_rules == ':')
cnt++;
ld_rules = (char *) (ld_2->ld_rules + code);
ldr = (char *) malloc (strlen (ld_rules) + 1);
strcpy (ldr, ld_rules);
}
p = getenv ("LD_LIBRARY_PATH");
q = 0;
if (p)
{
cnt++;
for (q = p ; *q != 0; q++)
if (*q == ':')
cnt++;
q = (char *) malloc (strlen (p) + 1);
strcpy (q, p);
}
l = (char **) malloc ((cnt + 3) * sizeof (char *));
pp = l;
if (ldr)
{
*pp++ = ldr;
for (; *ldr != 0; ldr++)
if (*ldr == ':')
{
*ldr++ = 0;
*pp++ = ldr;
}
}
if (q)
{
*pp++ = q;
for (; *q != 0; q++)
if (*q == ':')
{
*q++ = 0;
*pp++ = q;
}
}
/* built in directories are /lib, /usr/lib, and /usr/local/lib */
*pp++ = "/lib";
*pp++ = "/usr/lib";
*pp++ = "/usr/local/lib";
*pp = 0;
}
libname = name;
for (pp = l; *pp != 0 ; pp++)
{
struct direct **namelist;
int entries;
if ((entries = scandir (*pp, &namelist, libselect, libcompare)) > 0)
{
sprintf (buf, "%s/%s", *pp, namelist[entries - 1]->d_name);
add_to_list (&libraries, buf);
if (debug)
fprintf (stderr, "%s\n", buf);
break;
}
}
if (*pp == 0)
{
if (debug)
notice ("not found\n");
else
fatal ("dynamic dependency %s not found", name);
}
}
/* Scan the _DYNAMIC structure of the output file to find shared libraries
that it depends upon and any constructors or destructors they contain. */
static void
scan_libraries (prog_name)
char *prog_name;
{
struct exec *header;
char *base;
struct link_object *lo;
char buff[MAXPATHLEN];
struct id *list;
mapfile (prog_name);
header = (struct exec *)object;
if (N_BADMAG (*header))
fatal ("bad magic number in file '%s'", prog_name);
if (header->a_dynamic == 0)
return;
code = (char *) (N_TXTOFF (*header) + (long) header);
data = (char *) (N_DATOFF (*header) + (long) header);
symtab = (struct nlist *) (N_SYMOFF (*header) + (long) header);
if (header->a_magic == ZMAGIC && header->a_entry == 0x20)
{
/* shared object */
ld = (struct link_dynamic *) (symtab->n_value + code);
base = code;
}
else
{
/* executable */
ld = (struct link_dynamic *) data;
base = code-PAGSIZ;
}
if (debug)
notice ("dynamic dependencies.\n");
ld_2 = (struct link_dynamic_2 *) ((long) ld->ld_un.ld_2 + (long)base);
for (lo = (struct link_object *) ld_2->ld_need; lo;
lo = (struct link_object *) lo->lo_next)
{
char *name;
lo = (struct link_object *) ((long) lo + code);
name = (char *) (code + lo->lo_name);
if (lo->lo_library)
{
if (debug)
fprintf (stderr, "\t-l%s.%d => ", name, lo->lo_major);
sprintf (buff, "lib%s.so.%d.%d", name, lo->lo_major, lo->lo_minor);
locatelib (buff);
}
else
{
if (debug)
fprintf (stderr, "\t%s\n", name);
add_to_list (&libraries, name);
}
}
if (debug)
fprintf (stderr, "\n");
/* now iterate through the library list adding their symbols to
the list. */
for (list = libraries.first; list; list = list->next)
scan_prog_file (list->name, PASS_LIB);
}
#else /* SUNOS4_SHARED_LIBRARIES */
#ifdef LDD_SUFFIX
/* Use the List Dynamic Dependencies program to find shared libraries that
the output file depends upon and their initialization/finalization
routines, if any. */
static void
scan_libraries (prog_name)
char *prog_name;
{
static struct head libraries; /* list of shared libraries found */
struct id *list;
void (*int_handler) ();
void (*quit_handler) ();
char *ldd_argv[4];
int pid;
int argc = 0;
int pipe_fd[2];
char buf[1024];
FILE *inf;
/* If we do not have an `ldd', complain. */
if (ldd_file_name == 0)
{
error ("cannot find `ldd'");
return;
}
ldd_argv[argc++] = ldd_file_name;
ldd_argv[argc++] = prog_name;
ldd_argv[argc++] = (char *) 0;
if (pipe (pipe_fd) < 0)
fatal_perror ("pipe");
inf = fdopen (pipe_fd[0], "r");
if (inf == (FILE *) 0)
fatal_perror ("fdopen");
/* Trace if needed. */
if (vflag)
{
char **p_argv;
char *str;
for (p_argv = &ldd_argv[0]; (str = *p_argv) != (char *) 0; p_argv++)
fprintf (stderr, " %s", str);
fprintf (stderr, "\n");
}
fflush (stdout);
fflush (stderr);
/* Spawn child ldd on pipe */
pid = vfork ();
if (pid == -1)
fatal_perror (VFORK_STRING);
if (pid == 0) /* child context */
{
/* setup stdout */
if (dup2 (pipe_fd[1], 1) < 0)
fatal_perror ("dup2 %d 1", pipe_fd[1]);
if (close (pipe_fd[0]) < 0)
fatal_perror ("close %d", pipe_fd[0]);
if (close (pipe_fd[1]) < 0)
fatal_perror ("close %d", pipe_fd[1]);
execv (ldd_file_name, ldd_argv);
fatal_perror ("execv %s", ldd_file_name);
}
/* Parent context from here on. */
int_handler = (void (*) ()) signal (SIGINT, SIG_IGN);
#ifdef SIGQUIT
quit_handler = (void (*) ()) signal (SIGQUIT, SIG_IGN);
#endif
if (close (pipe_fd[1]) < 0)
fatal_perror ("close %d", pipe_fd[1]);
if (debug)
notice ("\nldd output with constructors/destructors.\n");
/* Read each line of ldd output. */
while (fgets (buf, sizeof buf, inf) != (char *) 0)
{
int ch, ch2;
char *name, *end, *p = buf;
/* Extract names of libraries and add to list. */
PARSE_LDD_OUTPUT (p);
if (p == 0)
continue;
name = p;
if (strncmp (name, "not found", sizeof ("not found") - 1) == 0)
fatal ("dynamic dependency %s not found", buf);
/* Find the end of the symbol name. */
for (end = p;
(ch2 = *end) != '\0' && ch2 != '\n' && !ISSPACE (ch2) && ch2 != '|';
end++)
continue;
*end = '\0';
if (access (name, R_OK) == 0)
add_to_list (&libraries, name);
else
fatal ("unable to open dynamic dependency '%s'", buf);
if (debug)
fprintf (stderr, "\t%s\n", buf);
}
if (debug)
fprintf (stderr, "\n");
if (fclose (inf) != 0)
fatal_perror ("fclose");
do_wait (ldd_file_name);
signal (SIGINT, int_handler);
#ifdef SIGQUIT
signal (SIGQUIT, quit_handler);
#endif
/* now iterate through the library list adding their symbols to
the list. */
for (list = libraries.first; list; list = list->next)
scan_prog_file (list->name, PASS_LIB);
}
#endif /* LDD_SUFFIX */
#endif /* SUNOS4_SHARED_LIBRARIES */
#endif /* OBJECT_FORMAT_NONE */
/*
* COFF specific stuff.
*/
#ifdef OBJECT_FORMAT_COFF
#if defined(EXTENDED_COFF)
# define GCC_SYMBOLS(X) (SYMHEADER(X).isymMax + SYMHEADER(X).iextMax)
# define GCC_SYMENT SYMR
# define GCC_OK_SYMBOL(X) ((X).st == stProc || (X).st == stGlobal)
# define GCC_SYMINC(X) (1)
# define GCC_SYMZERO(X) (SYMHEADER(X).isymMax)
# define GCC_CHECK_HDR(X) (PSYMTAB(X) != 0)
#else
# define GCC_SYMBOLS(X) (HEADER(ldptr).f_nsyms)
# define GCC_SYMENT SYMENT
# define GCC_OK_SYMBOL(X) \
(((X).n_sclass == C_EXT) && \
((X).n_scnum > N_UNDEF) && \
(((X).n_type & N_TMASK) == (DT_NON << N_BTSHFT) || \
((X).n_type & N_TMASK) == (DT_FCN << N_BTSHFT)))
# define GCC_UNDEF_SYMBOL(X) \
(((X).n_sclass == C_EXT) && ((X).n_scnum == N_UNDEF))
# define GCC_SYMINC(X) ((X).n_numaux+1)
# define GCC_SYMZERO(X) 0
# define GCC_CHECK_HDR(X) \
((HEADER (X).f_magic == U802TOCMAGIC && ! aix64_flag) \
|| (HEADER (X).f_magic == 0757 && aix64_flag))
#endif
extern char *ldgetname ();
/* COFF version to scan the name list of the loaded program for
the symbols g++ uses for static constructors and destructors.
The constructor table begins at __CTOR_LIST__ and contains a count
of the number of pointers (or -1 if the constructors are built in a
separate section by the linker), followed by the pointers to the
constructor functions, terminated with a null pointer. The
destructor table has the same format, and begins at __DTOR_LIST__. */
static void
scan_prog_file (prog_name, which_pass)
char *prog_name;
enum pass which_pass;
{
LDFILE *ldptr = NULL;
int sym_index, sym_count;
int is_shared = 0;
#ifdef COLLECT_EXPORT_LIST
/* Should we generate an import list for given prog_name? */
int import_flag = (which_pass == PASS_OBJ ? 0 : use_import_list (prog_name));
#endif
if (which_pass != PASS_FIRST && which_pass != PASS_OBJ)
return;
#ifdef COLLECT_EXPORT_LIST
/* We do not need scanning for some standard C libraries. */
if (which_pass == PASS_FIRST && ignore_library (prog_name))
return;
/* On AIX we have a loop, because there is not much difference
between an object and an archive. This trick allows us to
eliminate scan_libraries() function. */
do
{
#endif
if ((ldptr = ldopen (prog_name, ldptr)) != NULL)
{
if (! MY_ISCOFF (HEADER (ldptr).f_magic))
fatal ("%s: not a COFF file", prog_name);
if (GCC_CHECK_HDR (ldptr))
{
sym_count = GCC_SYMBOLS (ldptr);
sym_index = GCC_SYMZERO (ldptr);
#ifdef COLLECT_EXPORT_LIST
/* Is current archive member a shared object? */
is_shared = HEADER (ldptr).f_flags & F_SHROBJ;
#endif
while (sym_index < sym_count)
{
GCC_SYMENT symbol;
if (ldtbread (ldptr, sym_index, &symbol) <= 0)
break;
sym_index += GCC_SYMINC (symbol);
if (GCC_OK_SYMBOL (symbol))
{
char *name;
if ((name = ldgetname (ldptr, &symbol)) == NULL)
continue; /* should never happen */
#ifdef XCOFF_DEBUGGING_INFO
/* All AIX function names have a duplicate entry
beginning with a dot. */
if (*name == '.')
++name;
#endif
switch (is_ctor_dtor (name))
{
case 1:
if (! is_shared) add_to_list (&constructors, name);
#ifdef COLLECT_EXPORT_LIST
if (which_pass == PASS_OBJ)
add_to_list (&exports, name);
/* If this symbol was undefined and we are building
an import list, we should add a symbol to this
list. */
else
if (import_flag
&& is_in_list (name, undefined.first))
add_to_list (&imports, name);
#endif
break;
case 2:
if (! is_shared) add_to_list (&destructors, name);
#ifdef COLLECT_EXPORT_LIST
if (which_pass == PASS_OBJ)
add_to_list (&exports, name);
/* If this symbol was undefined and we are building
an import list, we should add a symbol to this
list. */
else
if (import_flag
&& is_in_list (name, undefined.first))
add_to_list (&imports, name);
#endif
break;
#ifdef COLLECT_EXPORT_LIST
case 3:
if (is_shared)
add_to_list (&constructors, name);
break;
case 4:
if (is_shared)
add_to_list (&destructors, name);
break;
#endif
case 5:
if (! is_shared)
add_to_list (&frame_tables, name);
break;
default: /* not a constructor or destructor */
#ifdef COLLECT_EXPORT_LIST
/* If we are building a shared object on AIX we need
to explicitly export all global symbols or add
them to import list. */
if (shared_obj)
{
if (which_pass == PASS_OBJ && (! export_flag))
add_to_list (&exports, name);
else if (! is_shared && which_pass == PASS_FIRST
&& import_flag
&& is_in_list(name, undefined.first))
add_to_list (&imports, name);
}
#endif
continue;
}
#if !defined(EXTENDED_COFF)
if (debug)
fprintf (stderr, "\tsec=%d class=%d type=%s%o %s\n",
symbol.n_scnum, symbol.n_sclass,
(symbol.n_type ? "0" : ""), symbol.n_type,
name);
#else
if (debug)
fprintf (stderr,
"\tiss = %5d, value = %5ld, index = %5d, name = %s\n",
symbol.iss, (long) symbol.value, symbol.index, name);
#endif
}
#ifdef COLLECT_EXPORT_LIST
/* If we are building a shared object we should collect
information about undefined symbols for later
import list generation. */
else if (shared_obj && GCC_UNDEF_SYMBOL (symbol))
{
char *name;
if ((name = ldgetname (ldptr, &symbol)) == NULL)
continue; /* should never happen */
/* All AIX function names have a duplicate entry
beginning with a dot. */
if (*name == '.')
++name;
add_to_list (&undefined, name);
}
#endif
}
}
#ifdef COLLECT_EXPORT_LIST
else
{
/* If archive contains both 32-bit and 64-bit objects,
we want to skip objects in other mode so mismatch normal. */
if (debug)
fprintf (stderr, "%s : magic=%o aix64=%d mismatch\n",
prog_name, HEADER (ldptr).f_magic, aix64_flag);
}
#endif
}
else
{
fatal ("%s: cannot open as COFF file", prog_name);
}
#ifdef COLLECT_EXPORT_LIST
/* On AIX loop continues while there are more members in archive. */
}
while (ldclose (ldptr) == FAILURE);
#else
/* Otherwise we simply close ldptr. */
(void) ldclose(ldptr);
#endif
}
#ifdef COLLECT_EXPORT_LIST
/* This new function is used to decide whether we should
generate import list for an object or to use it directly. */
static int
use_import_list (prog_name)
char *prog_name;
{
char *p;
/* If we do not build a shared object then import list should not be used. */
if (! shared_obj) return 0;
/* Currently we check only for libgcc, but this can be changed in future. */
p = strstr (prog_name, "libgcc.a");
if (p != 0 && (strlen (p) == sizeof ("libgcc.a") - 1))
return 1;
return 0;
}
/* Given a library name without "lib" prefix, this function
returns a full library name including a path. */
static char *
resolve_lib_name (name)
char *name;
{
char *lib_buf;
int i, j, l = 0;
for (i = 0; libpaths[i]; i++)
if (libpaths[i]->max_len > l)
l = libpaths[i]->max_len;
lib_buf = xmalloc (l + strlen(name) + 10);
for (i = 0; libpaths[i]; i++)
{
struct prefix_list *list = libpaths[i]->plist;
for (; list; list = list->next)
{
for (j = 0; libexts[j]; j++)
{
/* The following lines are needed because path_prefix list
may contain directories both with trailing '/' and
without it. */
char *p = "";
if (list->prefix[strlen(list->prefix)-1] != '/')
p = "/";
sprintf (lib_buf, "%s%slib%s.%s",
list->prefix, p, name, libexts[j]);
if (debug) fprintf (stderr, "searching for: %s\n", lib_buf);
if (file_exists (lib_buf))
{
if (debug) fprintf (stderr, "found: %s\n", lib_buf);
return (lib_buf);
}
}
}
}
if (debug)
fprintf (stderr, "not found\n");
else
fatal ("Library lib%s not found", name);
return (NULL);
}
/* Array of standard AIX libraries which should not
be scanned for ctors/dtors. */
static char* aix_std_libs[] = {
"/unix",
"/lib/libc.a",
"/lib/libc_r.a",
"/usr/lib/libc.a",
"/usr/lib/libc_r.a",
"/usr/lib/threads/libc.a",
"/usr/ccs/lib/libc.a",
"/usr/ccs/lib/libc_r.a",
NULL
};
/* This function checks the filename and returns 1
if this name matches the location of a standard AIX library. */
static int
ignore_library (name)
char *name;
{
char **p = &aix_std_libs[0];
while (*p++ != NULL)
if (! strcmp (name, *p)) return 1;
return 0;
}
#endif
#endif /* OBJECT_FORMAT_COFF */
/*
* OSF/rose specific stuff.
*/
#ifdef OBJECT_FORMAT_ROSE
/* Union of the various load commands */
typedef union load_union
{
ldc_header_t hdr; /* common header */
load_cmd_map_command_t map; /* map indexing other load cmds */
interpreter_command_t iprtr; /* interpreter pathname */
strings_command_t str; /* load commands strings section */
region_command_t region; /* region load command */
reloc_command_t reloc; /* relocation section */
package_command_t pkg; /* package load command */
symbols_command_t sym; /* symbol sections */
entry_command_t ent; /* program start section */
gen_info_command_t info; /* object information */
func_table_command_t func; /* function constructors/destructors */
} load_union_t;
/* Structure to point to load command and data section in memory. */
typedef struct load_all
{
load_union_t *load; /* load command */
char *section; /* pointer to section */
} load_all_t;
/* Structure to contain information about a file mapped into memory. */
struct file_info
{
char *start; /* start of map */
char *name; /* filename */
long size; /* size of the file */
long rounded_size; /* size rounded to page boundary */
int fd; /* file descriptor */
int rw; /* != 0 if opened read/write */
int use_mmap; /* != 0 if mmap'ed */
};
extern int decode_mach_o_hdr ();
extern int encode_mach_o_hdr ();
static void add_func_table PROTO((mo_header_t *, load_all_t *,
symbol_info_t *, int));
static void print_header PROTO((mo_header_t *));
static void print_load_command PROTO((load_union_t *, size_t, int));
static void bad_header PROTO((int));
static struct file_info *read_file PROTO((char *, int, int));
static void end_file PROTO((struct file_info *));
/* OSF/rose specific version to scan the name list of the loaded
program for the symbols g++ uses for static constructors and
destructors.
The constructor table begins at __CTOR_LIST__ and contains a count
of the number of pointers (or -1 if the constructors are built in a
separate section by the linker), followed by the pointers to the
constructor functions, terminated with a null pointer. The
destructor table has the same format, and begins at __DTOR_LIST__. */
static void
scan_prog_file (prog_name, which_pass)
char *prog_name;
enum pass which_pass;
{
char *obj;
mo_header_t hdr;
load_all_t *load_array;
load_all_t *load_end;
load_all_t *load_cmd;
int symbol_load_cmds;
off_t offset;
int i;
int num_syms;
int status;
char *str_sect;
struct file_info *obj_file;
int prog_fd;
mo_lcid_t cmd_strings = -1;
symbol_info_t *main_sym = 0;
int rw = (which_pass != PASS_FIRST);
prog_fd = open (prog_name, (rw) ? O_RDWR : O_RDONLY);
if (prog_fd < 0)
fatal_perror ("open %s", prog_name);
obj_file = read_file (prog_name, prog_fd, rw);
obj = obj_file->start;
status = decode_mach_o_hdr (obj, MO_SIZEOF_RAW_HDR, MOH_HEADER_VERSION, &hdr);
if (status != MO_HDR_CONV_SUCCESS)
bad_header (status);
/* Do some basic sanity checks. Note we explicitly use the big endian magic number,
since the hardware will automatically swap bytes for us on loading little endian
integers. */
#ifndef CROSS_COMPILE
if (hdr.moh_magic != MOH_MAGIC_MSB
|| hdr.moh_header_version != MOH_HEADER_VERSION
|| hdr.moh_byte_order != OUR_BYTE_ORDER
|| hdr.moh_data_rep_id != OUR_DATA_REP_ID
|| hdr.moh_cpu_type != OUR_CPU_TYPE
|| hdr.moh_cpu_subtype != OUR_CPU_SUBTYPE
|| hdr.moh_vendor_type != OUR_VENDOR_TYPE)
{
fatal ("incompatibilities between object file & expected values");
}
#endif
if (debug)
print_header (&hdr);
offset = hdr.moh_first_cmd_off;
load_end = load_array
= (load_all_t *) xcalloc (sizeof (load_all_t), hdr.moh_n_load_cmds + 2);
/* Build array of load commands, calculating the offsets */
for (i = 0; i < hdr.moh_n_load_cmds; i++)
{
load_union_t *load_hdr; /* load command header */
load_cmd = load_end++;
load_hdr = (load_union_t *) (obj + offset);
/* If modifying the program file, copy the header. */
if (rw)
{
load_union_t *ptr = (load_union_t *) xmalloc (load_hdr->hdr.ldci_cmd_size);
bcopy ((char *)load_hdr, (char *)ptr, load_hdr->hdr.ldci_cmd_size);
load_hdr = ptr;
/* null out old command map, because we will rewrite at the end. */
if (ptr->hdr.ldci_cmd_type == LDC_CMD_MAP)
{
cmd_strings = ptr->map.lcm_ld_cmd_strings;
ptr->hdr.ldci_cmd_type = LDC_UNDEFINED;
}
}
load_cmd->load = load_hdr;
if (load_hdr->hdr.ldci_section_off > 0)
load_cmd->section = obj + load_hdr->hdr.ldci_section_off;
if (debug)
print_load_command (load_hdr, offset, i);
offset += load_hdr->hdr.ldci_cmd_size;
}
/* If the last command is the load command map and is not undefined,
decrement the count of load commands. */
if (rw && load_end[-1].load->hdr.ldci_cmd_type == LDC_UNDEFINED)
{
load_end--;
hdr.moh_n_load_cmds--;
}
/* Go through and process each symbol table section. */
symbol_load_cmds = 0;
for (load_cmd = load_array; load_cmd < load_end; load_cmd++)
{
load_union_t *load_hdr = load_cmd->load;
if (load_hdr->hdr.ldci_cmd_type == LDC_SYMBOLS)
{
symbol_load_cmds++;
if (debug)
{
char *kind = "unknown";
switch (load_hdr->sym.symc_kind)
{
case SYMC_IMPORTS: kind = "imports"; break;
case SYMC_DEFINED_SYMBOLS: kind = "defined"; break;
case SYMC_STABS: kind = "stabs"; break;
}
notice ("\nProcessing symbol table #%d, offset = 0x%.8lx, kind = %s\n",
symbol_load_cmds, load_hdr->hdr.ldci_section_off, kind);
}
if (load_hdr->sym.symc_kind != SYMC_DEFINED_SYMBOLS)
continue;
str_sect = load_array[load_hdr->sym.symc_strings_section].section;
if (str_sect == (char *) 0)
fatal ("string section missing");
if (load_cmd->section == (char *) 0)
fatal ("section pointer missing");
num_syms = load_hdr->sym.symc_nentries;
for (i = 0; i < num_syms; i++)
{
symbol_info_t *sym = ((symbol_info_t *) load_cmd->section) + i;
char *name = sym->si_name.symbol_name + str_sect;
if (name[0] != '_')
continue;
if (rw)
{
char *n = name + strlen (name) - strlen (NAME__MAIN);
if ((n - name) < 0 || strcmp (n, NAME__MAIN))
continue;
while (n != name)
if (*--n != '_')
continue;
main_sym = sym;
}
else
{
switch (is_ctor_dtor (name))
{
case 1:
add_to_list (&constructors, name);
break;
case 2:
add_to_list (&destructors, name);
break;
default: /* not a constructor or destructor */
continue;
}
}
if (debug)
fprintf (stderr, "\ttype = 0x%.4x, sc = 0x%.2x, flags = 0x%.8x, name = %.30s\n",
sym->si_type, sym->si_sc_type, sym->si_flags, name);
}
}
}
if (symbol_load_cmds == 0)
fatal ("no symbol table found");
/* Update the program file now, rewrite header and load commands. At present,
we assume that there is enough space after the last load command to insert
one more. Since the first section written out is page aligned, and the
number of load commands is small, this is ok for the present. */
if (rw)
{
load_union_t *load_map;
size_t size;
if (cmd_strings == -1)
fatal ("no cmd_strings found");
/* Add __main to initializer list.
If we are building a program instead of a shared library, do not
do anything, since in the current version, you cannot do mallocs
and such in the constructors. */
if (main_sym != (symbol_info_t *) 0
&& ((hdr.moh_flags & MOH_EXECABLE_F) == 0))
add_func_table (&hdr, load_array, main_sym, FNTC_INITIALIZATION);
if (debug)
notice ("\nUpdating header and load commands.\n\n");
hdr.moh_n_load_cmds++;
size = sizeof (load_cmd_map_command_t) + (sizeof (mo_offset_t) * (hdr.moh_n_load_cmds - 1));
/* Create new load command map. */
if (debug)
notice ("load command map, %d cmds, new size %ld.\n",
(int) hdr.moh_n_load_cmds, (long) size);
load_map = (load_union_t *) xcalloc (1, size);
load_map->map.ldc_header.ldci_cmd_type = LDC_CMD_MAP;
load_map->map.ldc_header.ldci_cmd_size = size;
load_map->map.lcm_ld_cmd_strings = cmd_strings;
load_map->map.lcm_nentries = hdr.moh_n_load_cmds;
load_array[hdr.moh_n_load_cmds-1].load = load_map;
offset = hdr.moh_first_cmd_off;
for (i = 0; i < hdr.moh_n_load_cmds; i++)
{
load_map->map.lcm_map[i] = offset;
if (load_array[i].load->hdr.ldci_cmd_type == LDC_CMD_MAP)
hdr.moh_load_map_cmd_off = offset;
offset += load_array[i].load->hdr.ldci_cmd_size;
}
hdr.moh_sizeofcmds = offset - MO_SIZEOF_RAW_HDR;
if (debug)
print_header (&hdr);
/* Write header */
status = encode_mach_o_hdr (&hdr, obj, MO_SIZEOF_RAW_HDR);
if (status != MO_HDR_CONV_SUCCESS)
bad_header (status);
if (debug)
notice ("writing load commands.\n\n");
/* Write load commands */
offset = hdr.moh_first_cmd_off;
for (i = 0; i < hdr.moh_n_load_cmds; i++)
{
load_union_t *load_hdr = load_array[i].load;
size_t size = load_hdr->hdr.ldci_cmd_size;
if (debug)
print_load_command (load_hdr, offset, i);
bcopy ((char *) load_hdr, (char *) (obj + offset), size);
offset += size;
}
}
end_file (obj_file);
if (close (prog_fd))
fatal_perror ("close %s", prog_name);
if (debug)
fprintf (stderr, "\n");
}
/* Add a function table to the load commands to call a function
on initiation or termination of the process. */
static void
add_func_table (hdr_p, load_array, sym, type)
mo_header_t *hdr_p; /* pointer to global header */
load_all_t *load_array; /* array of ptrs to load cmds */
symbol_info_t *sym; /* pointer to symbol entry */
int type; /* fntc_type value */
{
/* Add a new load command. */
int num_cmds = ++hdr_p->moh_n_load_cmds;
int load_index = num_cmds - 1;
size_t size = sizeof (func_table_command_t) + sizeof (mo_addr_t);
load_union_t *ptr = xcalloc (1, size);
load_all_t *load_cmd;
int i;
/* Set the unresolved address bit in the header to force the loader to be
used, since kernel exec does not call the initialization functions. */
hdr_p->moh_flags |= MOH_UNRESOLVED_F;
load_cmd = &load_array[load_index];
load_cmd->load = ptr;
load_cmd->section = (char *) 0;
/* Fill in func table load command. */
ptr->func.ldc_header.ldci_cmd_type = LDC_FUNC_TABLE;
ptr->func.ldc_header.ldci_cmd_size = size;
ptr->func.ldc_header.ldci_section_off = 0;
ptr->func.ldc_header.ldci_section_len = 0;
ptr->func.fntc_type = type;
ptr->func.fntc_nentries = 1;
/* copy address, turn it from abs. address to (region,offset) if necessary. */
/* Is the symbol already expressed as (region, offset)? */
if ((sym->si_flags & SI_ABSOLUTE_VALUE_F) == 0)
{
ptr->func.fntc_entry_loc[i].adr_lcid = sym->si_value.def_val.adr_lcid;
ptr->func.fntc_entry_loc[i].adr_sctoff = sym->si_value.def_val.adr_sctoff;
}
/* If not, figure out which region it's in. */
else
{
mo_vm_addr_t addr = sym->si_value.abs_val;
int found = 0;
for (i = 0; i < load_index; i++)
{
if (load_array[i].load->hdr.ldci_cmd_type == LDC_REGION)
{
region_command_t *region_ptr = &load_array[i].load->region;
if ((region_ptr->regc_flags & REG_ABS_ADDR_F) != 0
&& addr >= region_ptr->regc_addr.vm_addr
&& addr <= region_ptr->regc_addr.vm_addr + region_ptr->regc_vm_size)
{
ptr->func.fntc_entry_loc[0].adr_lcid = i;
ptr->func.fntc_entry_loc[0].adr_sctoff = addr - region_ptr->regc_addr.vm_addr;
found++;
break;
}
}
}
if (!found)
fatal ("could not convert 0x%l.8x into a region", addr);
}
if (debug)
notice ("%s function, region %d, offset = %ld (0x%.8lx)\n",
type == FNTC_INITIALIZATION ? "init" : "term",
(int) ptr->func.fntc_entry_loc[i].adr_lcid,
(long) ptr->func.fntc_entry_loc[i].adr_sctoff,
(long) ptr->func.fntc_entry_loc[i].adr_sctoff);
}
/* Print the global header for an OSF/rose object. */
static void
print_header (hdr_ptr)
mo_header_t *hdr_ptr;
{
fprintf (stderr, "\nglobal header:\n");
fprintf (stderr, "\tmoh_magic = 0x%.8lx\n", hdr_ptr->moh_magic);
fprintf (stderr, "\tmoh_major_version = %d\n", (int)hdr_ptr->moh_major_version);
fprintf (stderr, "\tmoh_minor_version = %d\n", (int)hdr_ptr->moh_minor_version);
fprintf (stderr, "\tmoh_header_version = %d\n", (int)hdr_ptr->moh_header_version);
fprintf (stderr, "\tmoh_max_page_size = %d\n", (int)hdr_ptr->moh_max_page_size);
fprintf (stderr, "\tmoh_byte_order = %d\n", (int)hdr_ptr->moh_byte_order);
fprintf (stderr, "\tmoh_data_rep_id = %d\n", (int)hdr_ptr->moh_data_rep_id);
fprintf (stderr, "\tmoh_cpu_type = %d\n", (int)hdr_ptr->moh_cpu_type);
fprintf (stderr, "\tmoh_cpu_subtype = %d\n", (int)hdr_ptr->moh_cpu_subtype);
fprintf (stderr, "\tmoh_vendor_type = %d\n", (int)hdr_ptr->moh_vendor_type);
fprintf (stderr, "\tmoh_load_map_cmd_off = %d\n", (int)hdr_ptr->moh_load_map_cmd_off);
fprintf (stderr, "\tmoh_first_cmd_off = %d\n", (int)hdr_ptr->moh_first_cmd_off);
fprintf (stderr, "\tmoh_sizeofcmds = %d\n", (int)hdr_ptr->moh_sizeofcmds);
fprintf (stderr, "\tmon_n_load_cmds = %d\n", (int)hdr_ptr->moh_n_load_cmds);
fprintf (stderr, "\tmoh_flags = 0x%.8lx", (long)hdr_ptr->moh_flags);
if (hdr_ptr->moh_flags & MOH_RELOCATABLE_F)
fprintf (stderr, ", relocatable");
if (hdr_ptr->moh_flags & MOH_LINKABLE_F)
fprintf (stderr, ", linkable");
if (hdr_ptr->moh_flags & MOH_EXECABLE_F)
fprintf (stderr, ", execable");
if (hdr_ptr->moh_flags & MOH_EXECUTABLE_F)
fprintf (stderr, ", executable");
if (hdr_ptr->moh_flags & MOH_UNRESOLVED_F)
fprintf (stderr, ", unresolved");
fprintf (stderr, "\n\n");
return;
}
/* Print a short summary of a load command. */
static void
print_load_command (load_hdr, offset, number)
load_union_t *load_hdr;
size_t offset;
int number;
{
mo_long_t type = load_hdr->hdr.ldci_cmd_type;
char *type_str = (char *) 0;
switch (type)
{
case LDC_UNDEFINED: type_str = "UNDEFINED"; break;
case LDC_CMD_MAP: type_str = "CMD_MAP"; break;
case LDC_INTERPRETER: type_str = "INTERPRETER"; break;
case LDC_STRINGS: type_str = "STRINGS"; break;
case LDC_REGION: type_str = "REGION"; break;
case LDC_RELOC: type_str = "RELOC"; break;
case LDC_PACKAGE: type_str = "PACKAGE"; break;
case LDC_SYMBOLS: type_str = "SYMBOLS"; break;
case LDC_ENTRY: type_str = "ENTRY"; break;
case LDC_FUNC_TABLE: type_str = "FUNC_TABLE"; break;
case LDC_GEN_INFO: type_str = "GEN_INFO"; break;
}
fprintf (stderr,
"cmd %2d, sz: 0x%.2lx, coff: 0x%.3lx, doff: 0x%.6lx, dlen: 0x%.6lx",
number,
(long) load_hdr->hdr.ldci_cmd_size,
(long) offset,
(long) load_hdr->hdr.ldci_section_off,
(long) load_hdr->hdr.ldci_section_len);
if (type_str == (char *) 0)
fprintf (stderr, ", ty: unknown (%ld)\n", (long) type);
else if (type != LDC_REGION)
fprintf (stderr, ", ty: %s\n", type_str);
else
{
char *region = "";
switch (load_hdr->region.regc_usage_type)
{
case REG_TEXT_T: region = ", .text"; break;
case REG_DATA_T: region = ", .data"; break;
case REG_BSS_T: region = ", .bss"; break;
case REG_GLUE_T: region = ", .glue"; break;
#if defined (REG_RDATA_T) && defined (REG_SDATA_T) && defined (REG_SBSS_T) /*mips*/
case REG_RDATA_T: region = ", .rdata"; break;
case REG_SDATA_T: region = ", .sdata"; break;
case REG_SBSS_T: region = ", .sbss"; break;
#endif
}
fprintf (stderr, ", ty: %s, vaddr: 0x%.8lx, vlen: 0x%.6lx%s\n",
type_str,
(long) load_hdr->region.regc_vm_addr,
(long) load_hdr->region.regc_vm_size,
region);
}
return;
}
/* Fatal error when {en,de}code_mach_o_header fails. */
static void
bad_header (status)
int status;
{
switch (status)
{
case MO_ERROR_BAD_MAGIC: fatal ("bad magic number");
case MO_ERROR_BAD_HDR_VERS: fatal ("bad header version");
case MO_ERROR_BAD_RAW_HDR_VERS: fatal ("bad raw header version");
case MO_ERROR_BUF2SML: fatal ("raw header buffer too small");
case MO_ERROR_OLD_RAW_HDR_FILE: fatal ("old raw header file");
case MO_ERROR_UNSUPPORTED_VERS: fatal ("unsupported version");
default:
fatal ("unknown {de,en}code_mach_o_hdr return value %d", status);
}
}
/* Read a file into a memory buffer. */
static struct file_info *
read_file (name, fd, rw)
char *name; /* filename */
int fd; /* file descriptor */
int rw; /* read/write */
{
struct stat stat_pkt;
struct file_info *p = (struct file_info *) xcalloc (sizeof (struct file_info), 1);
#ifdef USE_MMAP
static int page_size;
#endif
if (fstat (fd, &stat_pkt) < 0)
fatal_perror ("fstat %s", name);
p->name = name;
p->size = stat_pkt.st_size;
p->rounded_size = stat_pkt.st_size;
p->fd = fd;
p->rw = rw;
#ifdef USE_MMAP
if (debug)
fprintf (stderr, "mmap %s, %s\n", name, (rw) ? "read/write" : "read-only");
if (page_size == 0)
page_size = sysconf (_SC_PAGE_SIZE);
p->rounded_size = ((p->size + page_size - 1) / page_size) * page_size;
p->start = mmap ((caddr_t) 0,
(rw) ? p->rounded_size : p->size,
(rw) ? (PROT_READ | PROT_WRITE) : PROT_READ,
MAP_FILE | MAP_VARIABLE | MAP_SHARED,
fd,
0L);
if (p->start != (char *) 0 && p->start != (char *) -1)
p->use_mmap = 1;
else
#endif /* USE_MMAP */
{
long len;
if (debug)
fprintf (stderr, "read %s\n", name);
p->use_mmap = 0;
p->start = xmalloc (p->size);
if (lseek (fd, 0L, SEEK_SET) < 0)
fatal_perror ("lseek %s 0", name);
len = read (fd, p->start, p->size);
if (len < 0)
fatal_perror ("read %s", name);
if (len != p->size)
fatal ("read %ld bytes, expected %ld, from %s", len, p->size, name);
}
return p;
}
/* Do anything necessary to write a file back from memory. */
static void
end_file (ptr)
struct file_info *ptr; /* file information block */
{
#ifdef USE_MMAP
if (ptr->use_mmap)
{
if (ptr->rw)
{
if (debug)
fprintf (stderr, "msync %s\n", ptr->name);
if (msync (ptr->start, ptr->rounded_size, MS_ASYNC))
fatal_perror ("msync %s", ptr->name);
}
if (debug)
fprintf (stderr, "munmap %s\n", ptr->name);
if (munmap (ptr->start, ptr->size))
fatal_perror ("munmap %s", ptr->name);
}
else
#endif /* USE_MMAP */
{
if (ptr->rw)
{
long len;
if (debug)
fprintf (stderr, "write %s\n", ptr->name);
if (lseek (ptr->fd, 0L, SEEK_SET) < 0)
fatal_perror ("lseek %s 0", ptr->name);
len = write (ptr->fd, ptr->start, ptr->size);
if (len < 0)
fatal_perror ("write %s", ptr->name);
if (len != ptr->size)
fatal ("wrote %ld bytes, expected %ld, to %s", len, ptr->size, ptr->name);
}
free (ptr->start);
}
free (ptr);
}
#endif /* OBJECT_FORMAT_ROSE */
Index: head/contrib/gcc/combine.c
===================================================================
--- head/contrib/gcc/combine.c (revision 52750)
+++ head/contrib/gcc/combine.c (revision 52751)
@@ -1,12038 +1,11991 @@
/* Optimize by combining instructions for GNU compiler.
Copyright (C) 1987, 88, 92-98, 1999 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 module is essentially the "combiner" phase of the U. of Arizona
Portable Optimizer, but redone to work on our list-structured
representation for RTL instead of their string representation.
The LOG_LINKS of each insn identify the most recent assignment
to each REG used in the insn. It is a list of previous insns,
each of which contains a SET for a REG that is used in this insn
and not used or set in between. LOG_LINKs never cross basic blocks.
They were set up by the preceding pass (lifetime analysis).
We try to combine each pair of insns joined by a logical link.
We also try to combine triples of insns A, B and C when
C has a link back to B and B has a link back to A.
LOG_LINKS does not have links for use of the CC0. They don't
need to, because the insn that sets the CC0 is always immediately
before the insn that tests it. So we always regard a branch
insn as having a logical link to the preceding insn. The same is true
for an insn explicitly using CC0.
We check (with use_crosses_set_p) to avoid combining in such a way
as to move a computation to a place where its value would be different.
Combination is done by mathematically substituting the previous
insn(s) values for the regs they set into the expressions in
the later insns that refer to these regs. If the result is a valid insn
for our target machine, according to the machine description,
we install it, delete the earlier insns, and update the data flow
information (LOG_LINKS and REG_NOTES) for what we did.
There are a few exceptions where the dataflow information created by
flow.c aren't completely updated:
- reg_live_length is not updated
- reg_n_refs is not adjusted in the rare case when a register is
no longer required in a computation
- there are extremely rare cases (see distribute_regnotes) when a
REG_DEAD note is lost
- a LOG_LINKS entry that refers to an insn with multiple SETs may be
removed because there is no way to know which register it was
linking
To simplify substitution, we combine only when the earlier insn(s)
consist of only a single assignment. To simplify updating afterward,
we never combine when a subroutine call appears in the middle.
Since we do not represent assignments to CC0 explicitly except when that
is all an insn does, there is no LOG_LINKS entry in an insn that uses
the condition code for the insn that set the condition code.
Fortunately, these two insns must be consecutive.
Therefore, every JUMP_INSN is taken to have an implicit logical link
to the preceding insn. This is not quite right, since non-jumps can
also use the condition code; but in practice such insns would not
combine anyway. */
#include "config.h"
#include "system.h"
#include "rtl.h" /* stdio.h must precede rtl.h for FFS. */
#include "flags.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "insn-config.h"
/* Include expr.h after insn-config.h so we get HAVE_conditional_move. */
#include "expr.h"
#include "insn-flags.h"
#include "insn-codes.h"
#include "insn-attr.h"
#include "recog.h"
#include "real.h"
#include "toplev.h"
/* It is not safe to use ordinary gen_lowpart in combine.
Use gen_lowpart_for_combine instead. See comments there. */
#define gen_lowpart dont_use_gen_lowpart_you_dummy
/* Number of attempts to combine instructions in this function. */
static int combine_attempts;
/* Number of attempts that got as far as substitution in this function. */
static int combine_merges;
/* Number of instructions combined with added SETs in this function. */
static int combine_extras;
/* Number of instructions combined in this function. */
static int combine_successes;
/* Totals over entire compilation. */
static int total_attempts, total_merges, total_extras, total_successes;
/* Define a default value for REVERSIBLE_CC_MODE.
We can never assume that a condition code mode is safe to reverse unless
the md tells us so. */
#ifndef REVERSIBLE_CC_MODE
#define REVERSIBLE_CC_MODE(MODE) 0
#endif
/* Vector mapping INSN_UIDs to cuids.
The cuids are like uids but increase monotonically always.
Combine always uses cuids so that it can compare them.
But actually renumbering the uids, which we used to do,
proves to be a bad idea because it makes it hard to compare
the dumps produced by earlier passes with those from later passes. */
static int *uid_cuid;
static int max_uid_cuid;
/* Get the cuid of an insn. */
#define INSN_CUID(INSN) \
(INSN_UID (INSN) > max_uid_cuid ? insn_cuid (INSN) : uid_cuid[INSN_UID (INSN)])
/* Maximum register number, which is the size of the tables below. */
static int combine_max_regno;
/* Record last point of death of (hard or pseudo) register n. */
static rtx *reg_last_death;
/* Record last point of modification of (hard or pseudo) register n. */
static rtx *reg_last_set;
/* Record the cuid of the last insn that invalidated memory
(anything that writes memory, and subroutine calls, but not pushes). */
static int mem_last_set;
/* Record the cuid of the last CALL_INSN
so we can tell whether a potential combination crosses any calls. */
static int last_call_cuid;
/* When `subst' is called, this is the insn that is being modified
(by combining in a previous insn). The PATTERN of this insn
is still the old pattern partially modified and it should not be
looked at, but this may be used to examine the successors of the insn
to judge whether a simplification is valid. */
static rtx subst_insn;
/* This is an insn that belongs before subst_insn, but is not currently
on the insn chain. */
static rtx subst_prev_insn;
/* This is the lowest CUID that `subst' is currently dealing with.
get_last_value will not return a value if the register was set at or
after this CUID. If not for this mechanism, we could get confused if
I2 or I1 in try_combine were an insn that used the old value of a register
to obtain a new value. In that case, we might erroneously get the
new value of the register when we wanted the old one. */
static int subst_low_cuid;
/* This contains any hard registers that are used in newpat; reg_dead_at_p
must consider all these registers to be always live. */
static HARD_REG_SET newpat_used_regs;
/* This is an insn to which a LOG_LINKS entry has been added. If this
insn is the earlier than I2 or I3, combine should rescan starting at
that location. */
static rtx added_links_insn;
/* Basic block number of the block in which we are performing combines. */
static int this_basic_block;
/* The next group of arrays allows the recording of the last value assigned
to (hard or pseudo) register n. We use this information to see if a
operation being processed is redundant given a prior operation performed
on the register. For example, an `and' with a constant is redundant if
all the zero bits are already known to be turned off.
We use an approach similar to that used by cse, but change it in the
following ways:
(1) We do not want to reinitialize at each label.
(2) It is useful, but not critical, to know the actual value assigned
to a register. Often just its form is helpful.
Therefore, we maintain the following arrays:
reg_last_set_value the last value assigned
reg_last_set_label records the value of label_tick when the
register was assigned
reg_last_set_table_tick records the value of label_tick when a
value using the register is assigned
reg_last_set_invalid set to non-zero when it is not valid
to use the value of this register in some
register's value
To understand the usage of these tables, it is important to understand
the distinction between the value in reg_last_set_value being valid
and the register being validly contained in some other expression in the
table.
Entry I in reg_last_set_value is valid if it is non-zero, and either
reg_n_sets[i] is 1 or reg_last_set_label[i] == label_tick.
Register I may validly appear in any expression returned for the value
of another register if reg_n_sets[i] is 1. It may also appear in the
value for register J if reg_last_set_label[i] < reg_last_set_label[j] or
reg_last_set_invalid[j] is zero.
If an expression is found in the table containing a register which may
not validly appear in an expression, the register is replaced by
something that won't match, (clobber (const_int 0)).
reg_last_set_invalid[i] is set non-zero when register I is being assigned
to and reg_last_set_table_tick[i] == label_tick. */
/* Record last value assigned to (hard or pseudo) register n. */
static rtx *reg_last_set_value;
/* Record the value of label_tick when the value for register n is placed in
reg_last_set_value[n]. */
static int *reg_last_set_label;
/* Record the value of label_tick when an expression involving register n
is placed in reg_last_set_value. */
static int *reg_last_set_table_tick;
/* Set non-zero if references to register n in expressions should not be
used. */
static char *reg_last_set_invalid;
/* Incremented for each label. */
static int label_tick;
/* Some registers that are set more than once and used in more than one
basic block are nevertheless always set in similar ways. For example,
a QImode register may be loaded from memory in two places on a machine
where byte loads zero extend.
We record in the following array what we know about the nonzero
bits of a register, specifically which bits are known to be zero.
If an entry is zero, it means that we don't know anything special. */
static unsigned HOST_WIDE_INT *reg_nonzero_bits;
/* Mode used to compute significance in reg_nonzero_bits. It is the largest
integer mode that can fit in HOST_BITS_PER_WIDE_INT. */
static enum machine_mode nonzero_bits_mode;
/* Nonzero if we know that a register has some leading bits that are always
equal to the sign bit. */
static char *reg_sign_bit_copies;
/* Nonzero when reg_nonzero_bits and reg_sign_bit_copies can be safely used.
It is zero while computing them and after combine has completed. This
former test prevents propagating values based on previously set values,
which can be incorrect if a variable is modified in a loop. */
static int nonzero_sign_valid;
/* These arrays are maintained in parallel with reg_last_set_value
and are used to store the mode in which the register was last set,
the bits that were known to be zero when it was last set, and the
number of sign bits copies it was known to have when it was last set. */
static enum machine_mode *reg_last_set_mode;
static unsigned HOST_WIDE_INT *reg_last_set_nonzero_bits;
static char *reg_last_set_sign_bit_copies;
/* Record one modification to rtl structure
to be undone by storing old_contents into *where.
is_int is 1 if the contents are an int. */
struct undo
{
struct undo *next;
int is_int;
union {rtx r; int i;} old_contents;
union {rtx *r; int *i;} where;
};
/* Record a bunch of changes to be undone, up to MAX_UNDO of them.
num_undo says how many are currently recorded.
storage is nonzero if we must undo the allocation of new storage.
The value of storage is what to pass to obfree.
other_insn is nonzero if we have modified some other insn in the process
of working on subst_insn. It must be verified too.
previous_undos is the value of undobuf.undos when we started processing
this substitution. This will prevent gen_rtx_combine from re-used a piece
from the previous expression. Doing so can produce circular rtl
structures. */
struct undobuf
{
char *storage;
struct undo *undos;
struct undo *frees;
struct undo *previous_undos;
rtx other_insn;
};
static struct undobuf undobuf;
/* Substitute NEWVAL, an rtx expression, into INTO, a place in some
insn. The substitution can be undone by undo_all. If INTO is already
set to NEWVAL, do not record this change. Because computing NEWVAL might
also call SUBST, we have to compute it before we put anything into
the undo table. */
#define SUBST(INTO, NEWVAL) \
do { rtx _new = (NEWVAL); \
struct undo *_buf; \
\
if (undobuf.frees) \
_buf = undobuf.frees, undobuf.frees = _buf->next; \
else \
_buf = (struct undo *) xmalloc (sizeof (struct undo)); \
\
_buf->is_int = 0; \
_buf->where.r = &INTO; \
_buf->old_contents.r = INTO; \
INTO = _new; \
if (_buf->old_contents.r == INTO) \
_buf->next = undobuf.frees, undobuf.frees = _buf; \
else \
_buf->next = undobuf.undos, undobuf.undos = _buf; \
} while (0)
/* Similar to SUBST, but NEWVAL is an int expression. Note that substitution
for the value of a HOST_WIDE_INT value (including CONST_INT) is
not safe. */
#define SUBST_INT(INTO, NEWVAL) \
do { struct undo *_buf; \
\
if (undobuf.frees) \
_buf = undobuf.frees, undobuf.frees = _buf->next; \
else \
_buf = (struct undo *) xmalloc (sizeof (struct undo)); \
\
_buf->is_int = 1; \
_buf->where.i = (int *) &INTO; \
_buf->old_contents.i = INTO; \
INTO = NEWVAL; \
if (_buf->old_contents.i == INTO) \
_buf->next = undobuf.frees, undobuf.frees = _buf; \
else \
_buf->next = undobuf.undos, undobuf.undos = _buf; \
} while (0)
/* Number of times the pseudo being substituted for
was found and replaced. */
static int n_occurrences;
static void init_reg_last_arrays PROTO((void));
static void setup_incoming_promotions PROTO((void));
static void set_nonzero_bits_and_sign_copies PROTO((rtx, rtx));
static int can_combine_p PROTO((rtx, rtx, rtx, rtx, rtx *, rtx *));
static int sets_function_arg_p PROTO((rtx));
static int combinable_i3pat PROTO((rtx, rtx *, rtx, rtx, int, rtx *));
static rtx try_combine PROTO((rtx, rtx, rtx));
static void undo_all PROTO((void));
static rtx *find_split_point PROTO((rtx *, rtx));
static rtx subst PROTO((rtx, rtx, rtx, int, int));
static rtx simplify_rtx PROTO((rtx, enum machine_mode, int, int));
static rtx simplify_if_then_else PROTO((rtx));
static rtx simplify_set PROTO((rtx));
static rtx simplify_logical PROTO((rtx, int));
static rtx expand_compound_operation PROTO((rtx));
static rtx expand_field_assignment PROTO((rtx));
static rtx make_extraction PROTO((enum machine_mode, rtx, int, rtx, int,
int, int, int));
static rtx extract_left_shift PROTO((rtx, int));
static rtx make_compound_operation PROTO((rtx, enum rtx_code));
static int get_pos_from_mask PROTO((unsigned HOST_WIDE_INT, int *));
static rtx force_to_mode PROTO((rtx, enum machine_mode,
unsigned HOST_WIDE_INT, rtx, int));
static rtx if_then_else_cond PROTO((rtx, rtx *, rtx *));
static rtx known_cond PROTO((rtx, enum rtx_code, rtx, rtx));
static int rtx_equal_for_field_assignment_p PROTO((rtx, rtx));
static rtx make_field_assignment PROTO((rtx));
static rtx apply_distributive_law PROTO((rtx));
static rtx simplify_and_const_int PROTO((rtx, enum machine_mode, rtx,
unsigned HOST_WIDE_INT));
static unsigned HOST_WIDE_INT nonzero_bits PROTO((rtx, enum machine_mode));
static int num_sign_bit_copies PROTO((rtx, enum machine_mode));
static int merge_outer_ops PROTO((enum rtx_code *, HOST_WIDE_INT *,
enum rtx_code, HOST_WIDE_INT,
enum machine_mode, int *));
static rtx simplify_shift_const PROTO((rtx, enum rtx_code, enum machine_mode,
rtx, int));
static int recog_for_combine PROTO((rtx *, rtx, rtx *));
static rtx gen_lowpart_for_combine PROTO((enum machine_mode, rtx));
static rtx gen_rtx_combine PVPROTO((enum rtx_code code, enum machine_mode mode,
...));
static rtx gen_binary PROTO((enum rtx_code, enum machine_mode,
rtx, rtx));
static rtx gen_unary PROTO((enum rtx_code, enum machine_mode,
enum machine_mode, rtx));
static enum rtx_code simplify_comparison PROTO((enum rtx_code, rtx *, rtx *));
static int reversible_comparison_p PROTO((rtx));
static void update_table_tick PROTO((rtx));
static void record_value_for_reg PROTO((rtx, rtx, rtx));
static void record_dead_and_set_regs_1 PROTO((rtx, rtx));
static void record_dead_and_set_regs PROTO((rtx));
static int get_last_value_validate PROTO((rtx *, rtx, int, int));
static rtx get_last_value PROTO((rtx));
static int use_crosses_set_p PROTO((rtx, int));
static void reg_dead_at_p_1 PROTO((rtx, rtx));
static int reg_dead_at_p PROTO((rtx, rtx));
static void move_deaths PROTO((rtx, rtx, int, rtx, rtx *));
static int reg_bitfield_target_p PROTO((rtx, rtx));
static void distribute_notes PROTO((rtx, rtx, rtx, rtx, rtx, rtx));
static void distribute_links PROTO((rtx));
static void mark_used_regs_combine PROTO((rtx));
static int insn_cuid PROTO((rtx));
/* Main entry point for combiner. F is the first insn of the function.
NREGS is the first unused pseudo-reg number. */
void
combine_instructions (f, nregs)
rtx f;
int nregs;
{
register rtx insn, next;
#ifdef HAVE_cc0
register rtx prev;
#endif
register int i;
register rtx links, nextlinks;
combine_attempts = 0;
combine_merges = 0;
combine_extras = 0;
combine_successes = 0;
undobuf.undos = undobuf.previous_undos = 0;
combine_max_regno = nregs;
reg_nonzero_bits
= (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
reg_sign_bit_copies = (char *) alloca (nregs * sizeof (char));
bzero ((char *) reg_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
bzero (reg_sign_bit_copies, nregs * sizeof (char));
reg_last_death = (rtx *) alloca (nregs * sizeof (rtx));
reg_last_set = (rtx *) alloca (nregs * sizeof (rtx));
reg_last_set_value = (rtx *) alloca (nregs * sizeof (rtx));
reg_last_set_table_tick = (int *) alloca (nregs * sizeof (int));
reg_last_set_label = (int *) alloca (nregs * sizeof (int));
reg_last_set_invalid = (char *) alloca (nregs * sizeof (char));
reg_last_set_mode
= (enum machine_mode *) alloca (nregs * sizeof (enum machine_mode));
reg_last_set_nonzero_bits
= (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
reg_last_set_sign_bit_copies
= (char *) alloca (nregs * sizeof (char));
init_reg_last_arrays ();
init_recog_no_volatile ();
/* Compute maximum uid value so uid_cuid can be allocated. */
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
if (INSN_UID (insn) > i)
i = INSN_UID (insn);
uid_cuid = (int *) alloca ((i + 1) * sizeof (int));
max_uid_cuid = i;
nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
/* Don't use reg_nonzero_bits when computing it. This can cause problems
when, for example, we have j <<= 1 in a loop. */
nonzero_sign_valid = 0;
/* Compute the mapping from uids to cuids.
Cuids are numbers assigned to insns, like uids,
except that cuids increase monotonically through the code.
Scan all SETs and see if we can deduce anything about what
bits are known to be zero for some registers and how many copies
of the sign bit are known to exist for those registers.
Also set any known values so that we can use it while searching
for what bits are known to be set. */
label_tick = 1;
/* We need to initialize it here, because record_dead_and_set_regs may call
get_last_value. */
subst_prev_insn = NULL_RTX;
setup_incoming_promotions ();
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
uid_cuid[INSN_UID (insn)] = ++i;
subst_low_cuid = i;
subst_insn = insn;
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies);
record_dead_and_set_regs (insn);
#ifdef AUTO_INC_DEC
for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
if (REG_NOTE_KIND (links) == REG_INC)
set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX);
#endif
}
if (GET_CODE (insn) == CODE_LABEL)
label_tick++;
}
nonzero_sign_valid = 1;
/* Now scan all the insns in forward order. */
this_basic_block = -1;
label_tick = 1;
last_call_cuid = 0;
mem_last_set = 0;
init_reg_last_arrays ();
setup_incoming_promotions ();
for (insn = f; insn; insn = next ? next : NEXT_INSN (insn))
{
next = 0;
/* If INSN starts a new basic block, update our basic block number. */
if (this_basic_block + 1 < n_basic_blocks
&& BLOCK_HEAD (this_basic_block + 1) == insn)
this_basic_block++;
if (GET_CODE (insn) == CODE_LABEL)
label_tick++;
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* Try this insn with each insn it links back to. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
if ((next = try_combine (insn, XEXP (links, 0), NULL_RTX)) != 0)
goto retry;
/* Try each sequence of three linked insns ending with this one. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
for (nextlinks = LOG_LINKS (XEXP (links, 0)); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, XEXP (links, 0),
XEXP (nextlinks, 0))) != 0)
goto retry;
#ifdef HAVE_cc0
/* Try to combine a jump insn that uses CC0
with a preceding insn that sets CC0, and maybe with its
logical predecessor as well.
This is how we make decrement-and-branch insns.
We need this special code because data flow connections
via CC0 do not get entered in LOG_LINKS. */
if (GET_CODE (insn) == JUMP_INSN
&& (prev = prev_nonnote_insn (insn)) != 0
&& GET_CODE (prev) == INSN
&& sets_cc0_p (PATTERN (prev)))
{
if ((next = try_combine (insn, prev, NULL_RTX)) != 0)
goto retry;
for (nextlinks = LOG_LINKS (prev); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, prev,
XEXP (nextlinks, 0))) != 0)
goto retry;
}
/* Do the same for an insn that explicitly references CC0. */
if (GET_CODE (insn) == INSN
&& (prev = prev_nonnote_insn (insn)) != 0
&& GET_CODE (prev) == INSN
&& sets_cc0_p (PATTERN (prev))
&& GET_CODE (PATTERN (insn)) == SET
&& reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn))))
{
if ((next = try_combine (insn, prev, NULL_RTX)) != 0)
goto retry;
for (nextlinks = LOG_LINKS (prev); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, prev,
XEXP (nextlinks, 0))) != 0)
goto retry;
}
/* Finally, see if any of the insns that this insn links to
explicitly references CC0. If so, try this insn, that insn,
and its predecessor if it sets CC0. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
if (GET_CODE (XEXP (links, 0)) == INSN
&& GET_CODE (PATTERN (XEXP (links, 0))) == SET
&& reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (XEXP (links, 0))))
&& (prev = prev_nonnote_insn (XEXP (links, 0))) != 0
&& GET_CODE (prev) == INSN
&& sets_cc0_p (PATTERN (prev))
&& (next = try_combine (insn, XEXP (links, 0), prev)) != 0)
goto retry;
#endif
/* Try combining an insn with two different insns whose results it
uses. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
for (nextlinks = XEXP (links, 1); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, XEXP (links, 0),
XEXP (nextlinks, 0))) != 0)
goto retry;
if (GET_CODE (insn) != NOTE)
record_dead_and_set_regs (insn);
retry:
;
}
}
total_attempts += combine_attempts;
total_merges += combine_merges;
total_extras += combine_extras;
total_successes += combine_successes;
nonzero_sign_valid = 0;
/* Make recognizer allow volatile MEMs again. */
init_recog ();
}
/* Wipe the reg_last_xxx arrays in preparation for another pass. */
static void
init_reg_last_arrays ()
{
int nregs = combine_max_regno;
bzero ((char *) reg_last_death, nregs * sizeof (rtx));
bzero ((char *) reg_last_set, nregs * sizeof (rtx));
bzero ((char *) reg_last_set_value, nregs * sizeof (rtx));
bzero ((char *) reg_last_set_table_tick, nregs * sizeof (int));
bzero ((char *) reg_last_set_label, nregs * sizeof (int));
bzero (reg_last_set_invalid, nregs * sizeof (char));
bzero ((char *) reg_last_set_mode, nregs * sizeof (enum machine_mode));
bzero ((char *) reg_last_set_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
bzero (reg_last_set_sign_bit_copies, nregs * sizeof (char));
}
/* Set up any promoted values for incoming argument registers. */
static void
setup_incoming_promotions ()
{
#ifdef PROMOTE_FUNCTION_ARGS
int regno;
rtx reg;
enum machine_mode mode;
int unsignedp;
rtx first = get_insns ();
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (FUNCTION_ARG_REGNO_P (regno)
&& (reg = promoted_input_arg (regno, &mode, &unsignedp)) != 0)
{
record_value_for_reg
(reg, first, gen_rtx_fmt_e ((unsignedp ? ZERO_EXTEND
: SIGN_EXTEND),
GET_MODE (reg),
gen_rtx_CLOBBER (mode, const0_rtx)));
}
#endif
}
/* Called via note_stores. If X is a pseudo that is narrower than
HOST_BITS_PER_WIDE_INT and is being set, record what bits are known zero.
If we are setting only a portion of X and we can't figure out what
portion, assume all bits will be used since we don't know what will
be happening.
Similarly, set how many bits of X are known to be copies of the sign bit
at all locations in the function. This is the smallest number implied
by any set of X. */
static void
set_nonzero_bits_and_sign_copies (x, set)
rtx x;
rtx set;
{
int num;
if (GET_CODE (x) == REG
&& REGNO (x) >= FIRST_PSEUDO_REGISTER
/* If this register is undefined at the start of the file, we can't
say what its contents were. */
&& ! REGNO_REG_SET_P (BASIC_BLOCK (0)->global_live_at_start, REGNO (x))
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
{
if (set == 0 || GET_CODE (set) == CLOBBER)
{
reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
reg_sign_bit_copies[REGNO (x)] = 1;
return;
}
/* If this is a complex assignment, see if we can convert it into a
simple assignment. */
set = expand_field_assignment (set);
/* If this is a simple assignment, or we have a paradoxical SUBREG,
set what we know about X. */
if (SET_DEST (set) == x
|| (GET_CODE (SET_DEST (set)) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (SET_DEST (set)))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (set)))))
&& SUBREG_REG (SET_DEST (set)) == x))
{
rtx src = SET_SRC (set);
#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
/* If X is narrower than a word and SRC is a non-negative
constant that would appear negative in the mode of X,
sign-extend it for use in reg_nonzero_bits because some
machines (maybe most) will actually do the sign-extension
and this is the conservative approach.
??? For 2.5, try to tighten up the MD files in this regard
instead of this kludge. */
if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD
&& GET_CODE (src) == CONST_INT
&& INTVAL (src) > 0
&& 0 != (INTVAL (src)
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
src = GEN_INT (INTVAL (src)
| ((HOST_WIDE_INT) (-1)
<< GET_MODE_BITSIZE (GET_MODE (x))));
#endif
reg_nonzero_bits[REGNO (x)]
|= nonzero_bits (src, nonzero_bits_mode);
num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x));
if (reg_sign_bit_copies[REGNO (x)] == 0
|| reg_sign_bit_copies[REGNO (x)] > num)
reg_sign_bit_copies[REGNO (x)] = num;
}
else
{
reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
reg_sign_bit_copies[REGNO (x)] = 1;
}
}
}
/* See if INSN can be combined into I3. PRED and SUCC are optionally
insns that were previously combined into I3 or that will be combined
into the merger of INSN and I3.
Return 0 if the combination is not allowed for any reason.
If the combination is allowed, *PDEST will be set to the single
destination of INSN and *PSRC to the single source, and this function
will return 1. */
static int
can_combine_p (insn, i3, pred, succ, pdest, psrc)
rtx insn;
rtx i3;
rtx pred ATTRIBUTE_UNUSED;
rtx succ;
rtx *pdest, *psrc;
{
int i;
rtx set = 0, src, dest;
rtx p;
#ifdef AUTO_INC_DEC
rtx link;
#endif
int all_adjacent = (succ ? (next_active_insn (insn) == succ
&& next_active_insn (succ) == i3)
: next_active_insn (insn) == i3);
/* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0.
or a PARALLEL consisting of such a SET and CLOBBERs.
If INSN has CLOBBER parallel parts, ignore them for our processing.
By definition, these happen during the execution of the insn. When it
is merged with another insn, all bets are off. If they are, in fact,
needed and aren't also supplied in I3, they may be added by
recog_for_combine. Otherwise, it won't match.
We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED
note.
Get the source and destination of INSN. If more than one, can't
combine. */
if (GET_CODE (PATTERN (insn)) == SET)
set = PATTERN (insn);
else if (GET_CODE (PATTERN (insn)) == PARALLEL
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
{
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
rtx elt = XVECEXP (PATTERN (insn), 0, i);
switch (GET_CODE (elt))
{
/* This is important to combine floating point insns
for the SH4 port. */
case USE:
/* Combining an isolated USE doesn't make sense.
We depend here on combinable_i3_pat to reject them. */
/* The code below this loop only verifies that the inputs of
the SET in INSN do not change. We call reg_set_between_p
to verify that the REG in the USE does not change betweeen
I3 and INSN.
If the USE in INSN was for a pseudo register, the matching
insn pattern will likely match any register; combining this
with any other USE would only be safe if we knew that the
used registers have identical values, or if there was
something to tell them apart, e.g. different modes. For
now, we forgo such compilcated tests and simply disallow
combining of USES of pseudo registers with any other USE. */
if (GET_CODE (XEXP (elt, 0)) == REG
&& GET_CODE (PATTERN (i3)) == PARALLEL)
{
rtx i3pat = PATTERN (i3);
int i = XVECLEN (i3pat, 0) - 1;
int regno = REGNO (XEXP (elt, 0));
do
{
rtx i3elt = XVECEXP (i3pat, 0, i);
if (GET_CODE (i3elt) == USE
&& GET_CODE (XEXP (i3elt, 0)) == REG
&& (REGNO (XEXP (i3elt, 0)) == regno
? reg_set_between_p (XEXP (elt, 0),
PREV_INSN (insn), i3)
: regno >= FIRST_PSEUDO_REGISTER))
return 0;
}
while (--i >= 0);
}
break;
/* We can ignore CLOBBERs. */
case CLOBBER:
break;
case SET:
/* Ignore SETs whose result isn't used but not those that
have side-effects. */
if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt))
&& ! side_effects_p (elt))
break;
/* If we have already found a SET, this is a second one and
so we cannot combine with this insn. */
if (set)
return 0;
set = elt;
break;
default:
/* Anything else means we can't combine. */
return 0;
}
}
if (set == 0
/* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs,
so don't do anything with it. */
|| GET_CODE (SET_SRC (set)) == ASM_OPERANDS)
return 0;
}
else
return 0;
if (set == 0)
return 0;
set = expand_field_assignment (set);
src = SET_SRC (set), dest = SET_DEST (set);
/* Don't eliminate a store in the stack pointer. */
if (dest == stack_pointer_rtx
/* If we couldn't eliminate a field assignment, we can't combine. */
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART
/* Don't combine with an insn that sets a register to itself if it has
a REG_EQUAL note. This may be part of a REG_NO_CONFLICT sequence. */
|| (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX))
/* Can't merge a function call. */
|| GET_CODE (src) == CALL
/* Don't eliminate a function call argument. */
|| (GET_CODE (i3) == CALL_INSN
&& (find_reg_fusage (i3, USE, dest)
|| (GET_CODE (dest) == REG
&& REGNO (dest) < FIRST_PSEUDO_REGISTER
&& global_regs[REGNO (dest)])))
/* Don't substitute into an incremented register. */
|| FIND_REG_INC_NOTE (i3, dest)
|| (succ && FIND_REG_INC_NOTE (succ, dest))
#if 0
/* Don't combine the end of a libcall into anything. */
/* ??? This gives worse code, and appears to be unnecessary, since no
pass after flow uses REG_LIBCALL/REG_RETVAL notes. Local-alloc does
use REG_RETVAL notes for noconflict blocks, but other code here
makes sure that those insns don't disappear. */
|| find_reg_note (insn, REG_RETVAL, NULL_RTX)
#endif
/* Make sure that DEST is not used after SUCC but before I3. */
|| (succ && ! all_adjacent
&& reg_used_between_p (dest, succ, i3))
/* Make sure that the value that is to be substituted for the register
does not use any registers whose values alter in between. However,
If the insns are adjacent, a use can't cross a set even though we
think it might (this can happen for a sequence of insns each setting
the same destination; reg_last_set of that register might point to
a NOTE). If INSN has a REG_EQUIV note, the register is always
equivalent to the memory so the substitution is valid even if there
are intervening stores. Also, don't move a volatile asm or
UNSPEC_VOLATILE across any other insns. */
|| (! all_adjacent
&& (((GET_CODE (src) != MEM
|| ! find_reg_note (insn, REG_EQUIV, src))
&& use_crosses_set_p (src, INSN_CUID (insn)))
|| (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src))
|| GET_CODE (src) == UNSPEC_VOLATILE))
/* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get
better register allocation by not doing the combine. */
|| find_reg_note (i3, REG_NO_CONFLICT, dest)
|| (succ && find_reg_note (succ, REG_NO_CONFLICT, dest))
/* Don't combine across a CALL_INSN, because that would possibly
change whether the life span of some REGs crosses calls or not,
and it is a pain to update that information.
Exception: if source is a constant, moving it later can't hurt.
Accept that special case, because it helps -fforce-addr a lot. */
|| (INSN_CUID (insn) < last_call_cuid && ! CONSTANT_P (src)))
return 0;
/* DEST must either be a REG or CC0. */
if (GET_CODE (dest) == REG)
{
/* If register alignment is being enforced for multi-word items in all
cases except for parameters, it is possible to have a register copy
insn referencing a hard register that is not allowed to contain the
mode being copied and which would not be valid as an operand of most
insns. Eliminate this problem by not combining with such an insn.
Also, on some machines we don't want to extend the life of a hard
register.
This is the same test done in can_combine except that we don't test
if SRC is a CALL operation to permit a hard register with
SMALL_REGISTER_CLASSES, and that we have to take all_adjacent
into account. */
if (GET_CODE (src) == REG
&& ((REGNO (dest) < FIRST_PSEUDO_REGISTER
&& ! HARD_REGNO_MODE_OK (REGNO (dest), GET_MODE (dest)))
/* Don't extend the life of a hard register unless it is
user variable (if we have few registers) or it can't
fit into the desired register (meaning something special
is going on).
Also avoid substituting a return register into I3, because
reload can't handle a conflict with constraints of other
inputs. */
|| (REGNO (src) < FIRST_PSEUDO_REGISTER
&& (! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src))
|| (SMALL_REGISTER_CLASSES
&& ((! all_adjacent && ! REG_USERVAR_P (src))
|| (FUNCTION_VALUE_REGNO_P (REGNO (src))
&& ! REG_USERVAR_P (src))))))))
return 0;
}
else if (GET_CODE (dest) != CC0)
return 0;
/* Don't substitute for a register intended as a clobberable operand.
Similarly, don't substitute an expression containing a register that
will be clobbered in I3. */
if (GET_CODE (PATTERN (i3)) == PARALLEL)
for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER
&& (reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0),
src)
|| rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest)))
return 0;
/* If INSN contains anything volatile, or is an `asm' (whether volatile
or not), reject, unless nothing volatile comes between it and I3 */
if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src))
{
/* Make sure succ doesn't contain a volatile reference. */
if (succ != 0 && volatile_refs_p (PATTERN (succ)))
return 0;
for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& p != succ && volatile_refs_p (PATTERN (p)))
return 0;
}
/* If INSN is an asm, and DEST is a hard register, reject, since it has
to be an explicit register variable, and was chosen for a reason. */
if (GET_CODE (src) == ASM_OPERANDS
&& GET_CODE (dest) == REG && REGNO (dest) < FIRST_PSEUDO_REGISTER)
return 0;
/* If there are any volatile insns between INSN and I3, reject, because
they might affect machine state. */
for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& p != succ && volatile_insn_p (PATTERN (p)))
return 0;
/* If INSN or I2 contains an autoincrement or autodecrement,
make sure that register is not used between there and I3,
and not already used in I3 either.
Also insist that I3 not be a jump; if it were one
and the incremented register were spilled, we would lose. */
#ifdef AUTO_INC_DEC
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_INC
&& (GET_CODE (i3) == JUMP_INSN
|| reg_used_between_p (XEXP (link, 0), insn, i3)
|| reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3))))
return 0;
#endif
#ifdef HAVE_cc0
/* Don't combine an insn that follows a CC0-setting insn.
An insn that uses CC0 must not be separated from the one that sets it.
We do, however, allow I2 to follow a CC0-setting insn if that insn
is passed as I1; in that case it will be deleted also.
We also allow combining in this case if all the insns are adjacent
because that would leave the two CC0 insns adjacent as well.
It would be more logical to test whether CC0 occurs inside I1 or I2,
but that would be much slower, and this ought to be equivalent. */
p = prev_nonnote_insn (insn);
if (p && p != pred && GET_CODE (p) == INSN && sets_cc0_p (PATTERN (p))
&& ! all_adjacent)
return 0;
#endif
/* If we get here, we have passed all the tests and the combination is
to be allowed. */
*pdest = dest;
*psrc = src;
return 1;
}
/* Check if PAT is an insn - or a part of it - used to set up an
argument for a function in a hard register. */
static int
sets_function_arg_p (pat)
rtx pat;
{
int i;
rtx inner_dest;
switch (GET_CODE (pat))
{
case INSN:
return sets_function_arg_p (PATTERN (pat));
case PARALLEL:
for (i = XVECLEN (pat, 0); --i >= 0;)
if (sets_function_arg_p (XVECEXP (pat, 0, i)))
return 1;
break;
case SET:
inner_dest = SET_DEST (pat);
while (GET_CODE (inner_dest) == STRICT_LOW_PART
|| GET_CODE (inner_dest) == SUBREG
|| GET_CODE (inner_dest) == ZERO_EXTRACT)
inner_dest = XEXP (inner_dest, 0);
return (GET_CODE (inner_dest) == REG
&& REGNO (inner_dest) < FIRST_PSEUDO_REGISTER
&& FUNCTION_ARG_REGNO_P (REGNO (inner_dest)));
default:
break;
}
return 0;
}
/* LOC is the location within I3 that contains its pattern or the component
of a PARALLEL of the pattern. We validate that it is valid for combining.
One problem is if I3 modifies its output, as opposed to replacing it
entirely, we can't allow the output to contain I2DEST or I1DEST as doing
so would produce an insn that is not equivalent to the original insns.
Consider:
(set (reg:DI 101) (reg:DI 100))
(set (subreg:SI (reg:DI 101) 0) <foo>)
This is NOT equivalent to:
(parallel [(set (subreg:SI (reg:DI 100) 0) <foo>)
(set (reg:DI 101) (reg:DI 100))])
Not only does this modify 100 (in which case it might still be valid
if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100.
We can also run into a problem if I2 sets a register that I1
uses and I1 gets directly substituted into I3 (not via I2). In that
case, we would be getting the wrong value of I2DEST into I3, so we
must reject the combination. This case occurs when I2 and I1 both
feed into I3, rather than when I1 feeds into I2, which feeds into I3.
If I1_NOT_IN_SRC is non-zero, it means that finding I1 in the source
of a SET must prevent combination from occurring.
On machines where SMALL_REGISTER_CLASSES is non-zero, we don't combine
if the destination of a SET is a hard register that isn't a user
variable.
Before doing the above check, we first try to expand a field assignment
into a set of logical operations.
If PI3_DEST_KILLED is non-zero, it is a pointer to a location in which
we place a register that is both set and used within I3. If more than one
such register is detected, we fail.
Return 1 if the combination is valid, zero otherwise. */
static int
combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed)
rtx i3;
rtx *loc;
rtx i2dest;
rtx i1dest;
int i1_not_in_src;
rtx *pi3dest_killed;
{
rtx x = *loc;
if (GET_CODE (x) == SET)
{
rtx set = expand_field_assignment (x);
rtx dest = SET_DEST (set);
rtx src = SET_SRC (set);
rtx inner_dest = dest;
#if 0
rtx inner_src = src;
#endif
SUBST (*loc, set);
while (GET_CODE (inner_dest) == STRICT_LOW_PART
|| GET_CODE (inner_dest) == SUBREG
|| GET_CODE (inner_dest) == ZERO_EXTRACT)
inner_dest = XEXP (inner_dest, 0);
/* We probably don't need this any more now that LIMIT_RELOAD_CLASS
was added. */
#if 0
while (GET_CODE (inner_src) == STRICT_LOW_PART
|| GET_CODE (inner_src) == SUBREG
|| GET_CODE (inner_src) == ZERO_EXTRACT)
inner_src = XEXP (inner_src, 0);
/* If it is better that two different modes keep two different pseudos,
avoid combining them. This avoids producing the following pattern
on a 386:
(set (subreg:SI (reg/v:QI 21) 0)
(lshiftrt:SI (reg/v:SI 20)
(const_int 24)))
If that were made, reload could not handle the pair of
reg 20/21, since it would try to get any GENERAL_REGS
but some of them don't handle QImode. */
if (rtx_equal_p (inner_src, i2dest)
&& GET_CODE (inner_dest) == REG
&& ! MODES_TIEABLE_P (GET_MODE (i2dest), GET_MODE (inner_dest)))
return 0;
#endif
/* Check for the case where I3 modifies its output, as
discussed above. */
if ((inner_dest != dest
&& (reg_overlap_mentioned_p (i2dest, inner_dest)
|| (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest))))
/* This is the same test done in can_combine_p except that we
allow a hard register with SMALL_REGISTER_CLASSES if SRC is a
CALL operation. Moreover, we can't test all_adjacent; we don't
have to, since this instruction will stay in place, thus we are
not considering increasing the lifetime of INNER_DEST.
Also, if this insn sets a function argument, combining it with
something that might need a spill could clobber a previous
function argument; the all_adjacent test in can_combine_p also
checks this; here, we do a more specific test for this case. */
|| (GET_CODE (inner_dest) == REG
&& REGNO (inner_dest) < FIRST_PSEUDO_REGISTER
&& (! HARD_REGNO_MODE_OK (REGNO (inner_dest),
GET_MODE (inner_dest))
|| (SMALL_REGISTER_CLASSES && GET_CODE (src) != CALL
&& ! REG_USERVAR_P (inner_dest)
&& (FUNCTION_VALUE_REGNO_P (REGNO (inner_dest))
|| (FUNCTION_ARG_REGNO_P (REGNO (inner_dest))
&& i3 != 0
&& sets_function_arg_p (prev_nonnote_insn (i3)))))))
|| (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src)))
return 0;
/* If DEST is used in I3, it is being killed in this insn,
so record that for later.
Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the
STACK_POINTER_REGNUM, since these are always considered to be
live. Similarly for ARG_POINTER_REGNUM if it is fixed. */
if (pi3dest_killed && GET_CODE (dest) == REG
&& reg_referenced_p (dest, PATTERN (i3))
&& REGNO (dest) != FRAME_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& REGNO (dest) != HARD_FRAME_POINTER_REGNUM
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& (REGNO (dest) != ARG_POINTER_REGNUM
|| ! fixed_regs [REGNO (dest)])
#endif
&& REGNO (dest) != STACK_POINTER_REGNUM)
{
if (*pi3dest_killed)
return 0;
*pi3dest_killed = dest;
}
}
else if (GET_CODE (x) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (x, 0); i++)
if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest,
i1_not_in_src, pi3dest_killed))
return 0;
}
return 1;
}
/* Try to combine the insns I1 and I2 into I3.
Here I1 and I2 appear earlier than I3.
I1 can be zero; then we combine just I2 into I3.
It we are combining three insns and the resulting insn is not recognized,
try splitting it into two insns. If that happens, I2 and I3 are retained
and I1 is pseudo-deleted by turning it into a NOTE. Otherwise, I1 and I2
are pseudo-deleted.
Return 0 if the combination does not work. Then nothing is changed.
If we did the combination, return the insn at which combine should
resume scanning. */
static rtx
try_combine (i3, i2, i1)
register rtx i3, i2, i1;
{
/* New patterns for I3 and I3, respectively. */
rtx newpat, newi2pat = 0;
/* Indicates need to preserve SET in I1 or I2 in I3 if it is not dead. */
int added_sets_1, added_sets_2;
/* Total number of SETs to put into I3. */
int total_sets;
/* Nonzero is I2's body now appears in I3. */
int i2_is_used;
/* INSN_CODEs for new I3, new I2, and user of condition code. */
int insn_code_number, i2_code_number, other_code_number;
/* Contains I3 if the destination of I3 is used in its source, which means
that the old life of I3 is being killed. If that usage is placed into
I2 and not in I3, a REG_DEAD note must be made. */
rtx i3dest_killed = 0;
/* SET_DEST and SET_SRC of I2 and I1. */
rtx i2dest, i2src, i1dest = 0, i1src = 0;
/* PATTERN (I2), or a copy of it in certain cases. */
rtx i2pat;
/* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */
int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0;
int i1_feeds_i3 = 0;
/* Notes that must be added to REG_NOTES in I3 and I2. */
rtx new_i3_notes, new_i2_notes;
/* Notes that we substituted I3 into I2 instead of the normal case. */
int i3_subst_into_i2 = 0;
/* Notes that I1, I2 or I3 is a MULT operation. */
int have_mult = 0;
int maxreg;
rtx temp;
register rtx link;
int i;
/* If any of I1, I2, and I3 isn't really an insn, we can't do anything.
This can occur when flow deletes an insn that it has merged into an
auto-increment address. We also can't do anything if I3 has a
REG_LIBCALL note since we don't want to disrupt the contiguity of a
libcall. */
if (GET_RTX_CLASS (GET_CODE (i3)) != 'i'
|| GET_RTX_CLASS (GET_CODE (i2)) != 'i'
|| (i1 && GET_RTX_CLASS (GET_CODE (i1)) != 'i')
#if 0
/* ??? This gives worse code, and appears to be unnecessary, since no
pass after flow uses REG_LIBCALL/REG_RETVAL notes. */
|| find_reg_note (i3, REG_LIBCALL, NULL_RTX)
#endif
)
return 0;
combine_attempts++;
undobuf.undos = undobuf.previous_undos = 0;
undobuf.other_insn = 0;
/* Save the current high-water-mark so we can free storage if we didn't
accept this combination. */
undobuf.storage = (char *) oballoc (0);
/* Reset the hard register usage information. */
CLEAR_HARD_REG_SET (newpat_used_regs);
/* If I1 and I2 both feed I3, they can be in any order. To simplify the
code below, set I1 to be the earlier of the two insns. */
if (i1 && INSN_CUID (i1) > INSN_CUID (i2))
temp = i1, i1 = i2, i2 = temp;
added_links_insn = 0;
/* First check for one important special-case that the code below will
not handle. Namely, the case where I1 is zero, I2 has multiple sets,
and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case,
we may be able to replace that destination with the destination of I3.
This occurs in the common code where we compute both a quotient and
remainder into a structure, in which case we want to do the computation
directly into the structure to avoid register-register copies.
We make very conservative checks below and only try to handle the
most common cases of this. For example, we only handle the case
where I2 and I3 are adjacent to avoid making difficult register
usage tests. */
if (i1 == 0 && GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET
&& GET_CODE (SET_SRC (PATTERN (i3))) == REG
&& REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
&& (! SMALL_REGISTER_CLASSES
|| (GET_CODE (SET_DEST (PATTERN (i3))) != REG
|| REGNO (SET_DEST (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
|| REG_USERVAR_P (SET_DEST (PATTERN (i3)))))
&& find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3)))
&& GET_CODE (PATTERN (i2)) == PARALLEL
&& ! side_effects_p (SET_DEST (PATTERN (i3)))
/* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code
below would need to check what is inside (and reg_overlap_mentioned_p
doesn't support those codes anyway). Don't allow those destinations;
the resulting insn isn't likely to be recognized anyway. */
&& GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART
&& ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)),
SET_DEST (PATTERN (i3)))
&& next_real_insn (i2) == i3)
{
rtx p2 = PATTERN (i2);
/* Make sure that the destination of I3,
which we are going to substitute into one output of I2,
is not used within another output of I2. We must avoid making this:
(parallel [(set (mem (reg 69)) ...)
(set (reg 69) ...)])
which is not well-defined as to order of actions.
(Besides, reload can't handle output reloads for this.)
The problem can also happen if the dest of I3 is a memory ref,
if another dest in I2 is an indirect memory ref. */
for (i = 0; i < XVECLEN (p2, 0); i++)
if ((GET_CODE (XVECEXP (p2, 0, i)) == SET
|| GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER)
&& reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)),
SET_DEST (XVECEXP (p2, 0, i))))
break;
if (i == XVECLEN (p2, 0))
for (i = 0; i < XVECLEN (p2, 0); i++)
if (SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3)))
{
combine_merges++;
subst_insn = i3;
subst_low_cuid = INSN_CUID (i2);
added_sets_2 = added_sets_1 = 0;
i2dest = SET_SRC (PATTERN (i3));
/* Replace the dest in I2 with our dest and make the resulting
insn the new pattern for I3. Then skip to where we
validate the pattern. Everything was set up above. */
SUBST (SET_DEST (XVECEXP (p2, 0, i)),
SET_DEST (PATTERN (i3)));
newpat = p2;
i3_subst_into_i2 = 1;
goto validate_replacement;
}
}
#ifndef HAVE_cc0
/* If we have no I1 and I2 looks like:
(parallel [(set (reg:CC X) (compare:CC OP (const_int 0)))
(set Y OP)])
make up a dummy I1 that is
(set Y OP)
and change I2 to be
(set (reg:CC X) (compare:CC Y (const_int 0)))
(We can ignore any trailing CLOBBERs.)
This undoes a previous combination and allows us to match a branch-and-
decrement insn. */
if (i1 == 0 && GET_CODE (PATTERN (i2)) == PARALLEL
&& XVECLEN (PATTERN (i2), 0) >= 2
&& GET_CODE (XVECEXP (PATTERN (i2), 0, 0)) == SET
&& (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0))))
== MODE_CC)
&& GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE
&& XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx
&& GET_CODE (XVECEXP (PATTERN (i2), 0, 1)) == SET
&& GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 1))) == REG
&& rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0),
SET_SRC (XVECEXP (PATTERN (i2), 0, 1))))
{
for (i = XVECLEN (PATTERN (i2), 0) - 1; i >= 2; i--)
if (GET_CODE (XVECEXP (PATTERN (i2), 0, i)) != CLOBBER)
break;
if (i == 1)
{
/* We make I1 with the same INSN_UID as I2. This gives it
the same INSN_CUID for value tracking. Our fake I1 will
never appear in the insn stream so giving it the same INSN_UID
as I2 will not cause a problem. */
subst_prev_insn = i1
= gen_rtx_INSN (VOIDmode, INSN_UID (i2), NULL_RTX, i2,
XVECEXP (PATTERN (i2), 0, 1), -1, NULL_RTX,
NULL_RTX);
SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0));
SUBST (XEXP (SET_SRC (PATTERN (i2)), 0),
SET_DEST (PATTERN (i1)));
}
}
#endif
/* Verify that I2 and I1 are valid for combining. */
if (! can_combine_p (i2, i3, i1, NULL_RTX, &i2dest, &i2src)
|| (i1 && ! can_combine_p (i1, i3, NULL_RTX, i2, &i1dest, &i1src)))
{
undo_all ();
return 0;
}
/* Record whether I2DEST is used in I2SRC and similarly for the other
cases. Knowing this will help in register status updating below. */
i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src);
i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src);
i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src);
/* See if I1 directly feeds into I3. It does if I1DEST is not used
in I2SRC. */
i1_feeds_i3 = i1 && ! reg_overlap_mentioned_p (i1dest, i2src);
/* Ensure that I3's pattern can be the destination of combines. */
if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest,
i1 && i2dest_in_i1src && i1_feeds_i3,
&i3dest_killed))
{
undo_all ();
return 0;
}
/* See if any of the insns is a MULT operation. Unless one is, we will
reject a combination that is, since it must be slower. Be conservative
here. */
if (GET_CODE (i2src) == MULT
|| (i1 != 0 && GET_CODE (i1src) == MULT)
|| (GET_CODE (PATTERN (i3)) == SET
&& GET_CODE (SET_SRC (PATTERN (i3))) == MULT))
have_mult = 1;
/* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd.
We used to do this EXCEPT in one case: I3 has a post-inc in an
output operand. However, that exception can give rise to insns like
mov r3,(r3)+
which is a famous insn on the PDP-11 where the value of r3 used as the
source was model-dependent. Avoid this sort of thing. */
#if 0
if (!(GET_CODE (PATTERN (i3)) == SET
&& GET_CODE (SET_SRC (PATTERN (i3))) == REG
&& GET_CODE (SET_DEST (PATTERN (i3))) == MEM
&& (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC
|| GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC)))
/* It's not the exception. */
#endif
#ifdef AUTO_INC_DEC
for (link = REG_NOTES (i3); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_INC
&& (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2))
|| (i1 != 0
&& reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1)))))
{
undo_all ();
return 0;
}
#endif
/* See if the SETs in I1 or I2 need to be kept around in the merged
instruction: whenever the value set there is still needed past I3.
For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3.
For the SET in I1, we have two cases: If I1 and I2 independently
feed into I3, the set in I1 needs to be kept around if I1DEST dies
or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set
in I1 needs to be kept around unless I1DEST dies or is set in either
I2 or I3. We can distinguish these cases by seeing if I2SRC mentions
I1DEST. If so, we know I1 feeds into I2. */
added_sets_2 = ! dead_or_set_p (i3, i2dest);
added_sets_1
= i1 && ! (i1_feeds_i3 ? dead_or_set_p (i3, i1dest)
: (dead_or_set_p (i3, i1dest) || dead_or_set_p (i2, i1dest)));
/* If the set in I2 needs to be kept around, we must make a copy of
PATTERN (I2), so that when we substitute I1SRC for I1DEST in
PATTERN (I2), we are only substituting for the original I1DEST, not into
an already-substituted copy. This also prevents making self-referential
rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to
I2DEST. */
i2pat = (GET_CODE (PATTERN (i2)) == PARALLEL
? gen_rtx_SET (VOIDmode, i2dest, i2src)
: PATTERN (i2));
if (added_sets_2)
i2pat = copy_rtx (i2pat);
combine_merges++;
/* Substitute in the latest insn for the regs set by the earlier ones. */
maxreg = max_reg_num ();
subst_insn = i3;
/* It is possible that the source of I2 or I1 may be performing an
unneeded operation, such as a ZERO_EXTEND of something that is known
to have the high part zero. Handle that case by letting subst look at
the innermost one of them.
Another way to do this would be to have a function that tries to
simplify a single insn instead of merging two or more insns. We don't
do this because of the potential of infinite loops and because
of the potential extra memory required. However, doing it the way
we are is a bit of a kludge and doesn't catch all cases.
But only do this if -fexpensive-optimizations since it slows things down
and doesn't usually win. */
if (flag_expensive_optimizations)
{
/* Pass pc_rtx so no substitutions are done, just simplifications.
The cases that we are interested in here do not involve the few
cases were is_replaced is checked. */
if (i1)
{
subst_low_cuid = INSN_CUID (i1);
i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0);
}
else
{
subst_low_cuid = INSN_CUID (i2);
i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0);
}
undobuf.previous_undos = undobuf.undos;
}
#ifndef HAVE_cc0
/* Many machines that don't use CC0 have insns that can both perform an
arithmetic operation and set the condition code. These operations will
be represented as a PARALLEL with the first element of the vector
being a COMPARE of an arithmetic operation with the constant zero.
The second element of the vector will set some pseudo to the result
of the same arithmetic operation. If we simplify the COMPARE, we won't
match such a pattern and so will generate an extra insn. Here we test
for this case, where both the comparison and the operation result are
needed, and make the PARALLEL by just replacing I2DEST in I3SRC with
I2SRC. Later we will make the PARALLEL that contains I2. */
if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET
&& GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE
&& XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx
&& rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest))
{
#ifdef EXTRA_CC_MODES
rtx *cc_use;
enum machine_mode compare_mode;
#endif
newpat = PATTERN (i3);
SUBST (XEXP (SET_SRC (newpat), 0), i2src);
i2_is_used = 1;
#ifdef EXTRA_CC_MODES
/* See if a COMPARE with the operand we substituted in should be done
with the mode that is currently being used. If not, do the same
processing we do in `subst' for a SET; namely, if the destination
is used only once, try to replace it with a register of the proper
mode and also replace the COMPARE. */
if (undobuf.other_insn == 0
&& (cc_use = find_single_use (SET_DEST (newpat), i3,
&undobuf.other_insn))
&& ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use),
i2src, const0_rtx))
!= GET_MODE (SET_DEST (newpat))))
{
int regno = REGNO (SET_DEST (newpat));
rtx new_dest = gen_rtx_REG (compare_mode, regno);
if (regno < FIRST_PSEUDO_REGISTER
|| (REG_N_SETS (regno) == 1 && ! added_sets_2
&& ! REG_USERVAR_P (SET_DEST (newpat))))
{
if (regno >= FIRST_PSEUDO_REGISTER)
SUBST (regno_reg_rtx[regno], new_dest);
SUBST (SET_DEST (newpat), new_dest);
SUBST (XEXP (*cc_use, 0), new_dest);
SUBST (SET_SRC (newpat),
gen_rtx_combine (COMPARE, compare_mode,
i2src, const0_rtx));
}
else
undobuf.other_insn = 0;
}
#endif
}
else
#endif
{
n_occurrences = 0; /* `subst' counts here */
/* If I1 feeds into I2 (not into I3) and I1DEST is in I1SRC, we
need to make a unique copy of I2SRC each time we substitute it
to avoid self-referential rtl. */
subst_low_cuid = INSN_CUID (i2);
newpat = subst (PATTERN (i3), i2dest, i2src, 0,
! i1_feeds_i3 && i1dest_in_i1src);
undobuf.previous_undos = undobuf.undos;
/* Record whether i2's body now appears within i3's body. */
i2_is_used = n_occurrences;
}
/* If we already got a failure, don't try to do more. Otherwise,
try to substitute in I1 if we have it. */
if (i1 && GET_CODE (newpat) != CLOBBER)
{
/* Before we can do this substitution, we must redo the test done
above (see detailed comments there) that ensures that I1DEST
isn't mentioned in any SETs in NEWPAT that are field assignments. */
if (! combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX,
0, NULL_PTR))
{
undo_all ();
return 0;
}
n_occurrences = 0;
subst_low_cuid = INSN_CUID (i1);
newpat = subst (newpat, i1dest, i1src, 0, 0);
undobuf.previous_undos = undobuf.undos;
}
/* Fail if an autoincrement side-effect has been duplicated. Be careful
to count all the ways that I2SRC and I1SRC can be used. */
if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0
&& i2_is_used + added_sets_2 > 1)
|| (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0
&& (n_occurrences + added_sets_1 + (added_sets_2 && ! i1_feeds_i3)
> 1))
/* Fail if we tried to make a new register (we used to abort, but there's
really no reason to). */
|| max_reg_num () != maxreg
/* Fail if we couldn't do something and have a CLOBBER. */
|| GET_CODE (newpat) == CLOBBER
/* Fail if this new pattern is a MULT and we didn't have one before
at the outer level. */
|| (GET_CODE (newpat) == SET && GET_CODE (SET_SRC (newpat)) == MULT
&& ! have_mult))
{
undo_all ();
return 0;
}
/* If the actions of the earlier insns must be kept
in addition to substituting them into the latest one,
we must make a new PARALLEL for the latest insn
to hold additional the SETs. */
if (added_sets_1 || added_sets_2)
{
combine_extras++;
if (GET_CODE (newpat) == PARALLEL)
{
rtvec old = XVEC (newpat, 0);
total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2;
newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets));
bcopy ((char *) &old->elem[0], (char *) XVEC (newpat, 0)->elem,
sizeof (old->elem[0]) * old->num_elem);
}
else
{
rtx old = newpat;
total_sets = 1 + added_sets_1 + added_sets_2;
newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets));
XVECEXP (newpat, 0, 0) = old;
}
if (added_sets_1)
XVECEXP (newpat, 0, --total_sets)
= (GET_CODE (PATTERN (i1)) == PARALLEL
? gen_rtx_SET (VOIDmode, i1dest, i1src) : PATTERN (i1));
if (added_sets_2)
{
/* If there is no I1, use I2's body as is. We used to also not do
the subst call below if I2 was substituted into I3,
but that could lose a simplification. */
if (i1 == 0)
XVECEXP (newpat, 0, --total_sets) = i2pat;
else
/* See comment where i2pat is assigned. */
XVECEXP (newpat, 0, --total_sets)
= subst (i2pat, i1dest, i1src, 0, 0);
}
}
/* We come here when we are replacing a destination in I2 with the
destination of I3. */
validate_replacement:
/* Note which hard regs this insn has as inputs. */
mark_used_regs_combine (newpat);
/* Is the result of combination a valid instruction? */
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
/* If the result isn't valid, see if it is a PARALLEL of two SETs where
the second SET's destination is a register that is unused. In that case,
we just need the first SET. This can occur when simplifying a divmod
insn. We *must* test for this case here because the code below that
splits two independent SETs doesn't handle this case correctly when it
updates the register status. Also check the case where the first
SET's destination is unused. That would not cause incorrect code, but
does cause an unneeded insn to remain. */
if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == REG
&& find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 1)))
&& ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 1)))
&& asm_noperands (newpat) < 0)
{
newpat = XVECEXP (newpat, 0, 0);
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
}
else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) == REG
&& find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 0)))
&& ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 0)))
&& asm_noperands (newpat) < 0)
{
newpat = XVECEXP (newpat, 0, 1);
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
}
/* If we were combining three insns and the result is a simple SET
with no ASM_OPERANDS that wasn't recognized, try to split it into two
insns. There are two ways to do this. It can be split using a
machine-specific method (like when you have an addition of a large
constant) or by combine in the function find_split_point. */
if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET
&& asm_noperands (newpat) < 0)
{
rtx m_split, *split;
rtx ni2dest = i2dest;
/* See if the MD file can split NEWPAT. If it can't, see if letting it
use I2DEST as a scratch register will help. In the latter case,
convert I2DEST to the mode of the source of NEWPAT if we can. */
m_split = split_insns (newpat, i3);
/* We can only use I2DEST as a scratch reg if it doesn't overlap any
inputs of NEWPAT. */
/* ??? If I2DEST is not safe, and I1DEST exists, then it would be
possible to try that as a scratch reg. This would require adding
more code to make it work though. */
if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat))
{
/* If I2DEST is a hard register or the only use of a pseudo,
we can change its mode. */
if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest)
&& GET_MODE (SET_DEST (newpat)) != VOIDmode
&& GET_CODE (i2dest) == REG
&& (REGNO (i2dest) < FIRST_PSEUDO_REGISTER
|| (REG_N_SETS (REGNO (i2dest)) == 1 && ! added_sets_2
&& ! REG_USERVAR_P (i2dest))))
ni2dest = gen_rtx_REG (GET_MODE (SET_DEST (newpat)),
REGNO (i2dest));
m_split = split_insns
(gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, newpat,
gen_rtx_CLOBBER (VOIDmode,
ni2dest))),
i3);
}
if (m_split && GET_CODE (m_split) == SEQUENCE
&& XVECLEN (m_split, 0) == 2
&& (next_real_insn (i2) == i3
|| ! use_crosses_set_p (PATTERN (XVECEXP (m_split, 0, 0)),
INSN_CUID (i2))))
{
rtx i2set, i3set;
rtx newi3pat = PATTERN (XVECEXP (m_split, 0, 1));
newi2pat = PATTERN (XVECEXP (m_split, 0, 0));
i3set = single_set (XVECEXP (m_split, 0, 1));
i2set = single_set (XVECEXP (m_split, 0, 0));
/* In case we changed the mode of I2DEST, replace it in the
pseudo-register table here. We can't do it above in case this
code doesn't get executed and we do a split the other way. */
if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest);
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
/* If I2 or I3 has multiple SETs, we won't know how to track
register status, so don't use these insns. If I2's destination
is used between I2 and I3, we also can't use these insns. */
if (i2_code_number >= 0 && i2set && i3set
&& (next_real_insn (i2) == i3
|| ! reg_used_between_p (SET_DEST (i2set), i2, i3)))
insn_code_number = recog_for_combine (&newi3pat, i3,
&new_i3_notes);
if (insn_code_number >= 0)
newpat = newi3pat;
/* It is possible that both insns now set the destination of I3.
If so, we must show an extra use of it. */
if (insn_code_number >= 0)
{
rtx new_i3_dest = SET_DEST (i3set);
rtx new_i2_dest = SET_DEST (i2set);
while (GET_CODE (new_i3_dest) == ZERO_EXTRACT
|| GET_CODE (new_i3_dest) == STRICT_LOW_PART
|| GET_CODE (new_i3_dest) == SUBREG)
new_i3_dest = XEXP (new_i3_dest, 0);
while (GET_CODE (new_i2_dest) == ZERO_EXTRACT
|| GET_CODE (new_i2_dest) == STRICT_LOW_PART
|| GET_CODE (new_i2_dest) == SUBREG)
new_i2_dest = XEXP (new_i2_dest, 0);
if (GET_CODE (new_i3_dest) == REG
&& GET_CODE (new_i2_dest) == REG
&& REGNO (new_i3_dest) == REGNO (new_i2_dest))
REG_N_SETS (REGNO (new_i2_dest))++;
}
}
/* If we can split it and use I2DEST, go ahead and see if that
helps things be recognized. Verify that none of the registers
are set between I2 and I3. */
if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0
#ifdef HAVE_cc0
&& GET_CODE (i2dest) == REG
#endif
/* We need I2DEST in the proper mode. If it is a hard register
or the only use of a pseudo, we can change its mode. */
&& (GET_MODE (*split) == GET_MODE (i2dest)
|| GET_MODE (*split) == VOIDmode
|| REGNO (i2dest) < FIRST_PSEUDO_REGISTER
|| (REG_N_SETS (REGNO (i2dest)) == 1 && ! added_sets_2
&& ! REG_USERVAR_P (i2dest)))
&& (next_real_insn (i2) == i3
|| ! use_crosses_set_p (*split, INSN_CUID (i2)))
/* We can't overwrite I2DEST if its value is still used by
NEWPAT. */
&& ! reg_referenced_p (i2dest, newpat))
{
rtx newdest = i2dest;
enum rtx_code split_code = GET_CODE (*split);
enum machine_mode split_mode = GET_MODE (*split);
/* Get NEWDEST as a register in the proper mode. We have already
validated that we can do this. */
if (GET_MODE (i2dest) != split_mode && split_mode != VOIDmode)
{
newdest = gen_rtx_REG (split_mode, REGNO (i2dest));
if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
SUBST (regno_reg_rtx[REGNO (i2dest)], newdest);
}
/* If *SPLIT is a (mult FOO (const_int pow2)), convert it to
an ASHIFT. This can occur if it was inside a PLUS and hence
appeared to be a memory address. This is a kludge. */
if (split_code == MULT
&& GET_CODE (XEXP (*split, 1)) == CONST_INT
&& (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0)
{
SUBST (*split, gen_rtx_combine (ASHIFT, split_mode,
XEXP (*split, 0), GEN_INT (i)));
/* Update split_code because we may not have a multiply
anymore. */
split_code = GET_CODE (*split);
}
#ifdef INSN_SCHEDULING
/* If *SPLIT is a paradoxical SUBREG, when we split it, it should
be written as a ZERO_EXTEND. */
if (split_code == SUBREG && GET_CODE (SUBREG_REG (*split)) == MEM)
SUBST (*split, gen_rtx_combine (ZERO_EXTEND, split_mode,
XEXP (*split, 0)));
#endif
newi2pat = gen_rtx_combine (SET, VOIDmode, newdest, *split);
SUBST (*split, newdest);
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
/* If the split point was a MULT and we didn't have one before,
don't use one now. */
if (i2_code_number >= 0 && ! (split_code == MULT && ! have_mult))
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
}
}
/* Check for a case where we loaded from memory in a narrow mode and
then sign extended it, but we need both registers. In that case,
we have a PARALLEL with both loads from the same memory location.
We can split this into a load from memory followed by a register-register
copy. This saves at least one insn, more if register allocation can
eliminate the copy.
We cannot do this if the destination of the second assignment is
a register that we have already assumed is zero-extended. Similarly
for a SUBREG of such a register. */
else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
&& GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)),
XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0))
&& ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)),
INSN_CUID (i2))
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART
&& ! (temp = SET_DEST (XVECEXP (newpat, 0, 1)),
(GET_CODE (temp) == REG
&& reg_nonzero_bits[REGNO (temp)] != 0
&& GET_MODE_BITSIZE (GET_MODE (temp)) < BITS_PER_WORD
&& GET_MODE_BITSIZE (GET_MODE (temp)) < HOST_BITS_PER_INT
&& (reg_nonzero_bits[REGNO (temp)]
!= GET_MODE_MASK (word_mode))))
&& ! (GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == SUBREG
&& (temp = SUBREG_REG (SET_DEST (XVECEXP (newpat, 0, 1))),
(GET_CODE (temp) == REG
&& reg_nonzero_bits[REGNO (temp)] != 0
&& GET_MODE_BITSIZE (GET_MODE (temp)) < BITS_PER_WORD
&& GET_MODE_BITSIZE (GET_MODE (temp)) < HOST_BITS_PER_INT
&& (reg_nonzero_bits[REGNO (temp)]
!= GET_MODE_MASK (word_mode)))))
&& ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)),
SET_SRC (XVECEXP (newpat, 0, 1)))
&& ! find_reg_note (i3, REG_UNUSED,
SET_DEST (XVECEXP (newpat, 0, 0))))
{
rtx ni2dest;
newi2pat = XVECEXP (newpat, 0, 0);
ni2dest = SET_DEST (XVECEXP (newpat, 0, 0));
newpat = XVECEXP (newpat, 0, 1);
SUBST (SET_SRC (newpat),
gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest));
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
if (i2_code_number >= 0)
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
if (insn_code_number >= 0)
{
rtx insn;
rtx link;
/* If we will be able to accept this, we have made a change to the
destination of I3. This can invalidate a LOG_LINKS pointing
to I3. No other part of combine.c makes such a transformation.
The new I3 will have a destination that was previously the
destination of I1 or I2 and which was used in i2 or I3. Call
distribute_links to make a LOG_LINK from the next use of
that destination. */
PATTERN (i3) = newpat;
distribute_links (gen_rtx_INSN_LIST (VOIDmode, i3, NULL_RTX));
/* I3 now uses what used to be its destination and which is
now I2's destination. That means we need a LOG_LINK from
I3 to I2. But we used to have one, so we still will.
However, some later insn might be using I2's dest and have
a LOG_LINK pointing at I3. We must remove this link.
The simplest way to remove the link is to point it at I1,
which we know will be a NOTE. */
for (insn = NEXT_INSN (i3);
insn && (this_basic_block == n_basic_blocks - 1
|| insn != BLOCK_HEAD (this_basic_block + 1));
insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_referenced_p (ni2dest, PATTERN (insn)))
{
for (link = LOG_LINKS (insn); link;
link = XEXP (link, 1))
if (XEXP (link, 0) == i3)
XEXP (link, 0) = i1;
break;
}
}
}
}
/* Similarly, check for a case where we have a PARALLEL of two independent
SETs but we started with three insns. In this case, we can do the sets
as two separate insns. This case occurs when some SET allows two
other insns to combine, but the destination of that SET is still live. */
else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
&& GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != STRICT_LOW_PART
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART
&& ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)),
INSN_CUID (i2))
/* Don't pass sets with (USE (MEM ...)) dests to the following. */
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != USE
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != USE
&& ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)),
XVECEXP (newpat, 0, 0))
&& ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)),
XVECEXP (newpat, 0, 1)))
{
/* Normally, it doesn't matter which of the two is done first,
but it does if one references cc0. In that case, it has to
be first. */
#ifdef HAVE_cc0
if (reg_referenced_p (cc0_rtx, XVECEXP (newpat, 0, 0)))
{
newi2pat = XVECEXP (newpat, 0, 0);
newpat = XVECEXP (newpat, 0, 1);
}
else
#endif
{
newi2pat = XVECEXP (newpat, 0, 1);
newpat = XVECEXP (newpat, 0, 0);
}
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
if (i2_code_number >= 0)
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
}
/* If it still isn't recognized, fail and change things back the way they
were. */
if ((insn_code_number < 0
/* Is the result a reasonable ASM_OPERANDS? */
&& (! check_asm_operands (newpat) || added_sets_1 || added_sets_2)))
{
undo_all ();
return 0;
}
/* If we had to change another insn, make sure it is valid also. */
if (undobuf.other_insn)
{
rtx other_pat = PATTERN (undobuf.other_insn);
rtx new_other_notes;
rtx note, next;
CLEAR_HARD_REG_SET (newpat_used_regs);
other_code_number = recog_for_combine (&other_pat, undobuf.other_insn,
&new_other_notes);
if (other_code_number < 0 && ! check_asm_operands (other_pat))
{
undo_all ();
return 0;
}
PATTERN (undobuf.other_insn) = other_pat;
/* If any of the notes in OTHER_INSN were REG_UNUSED, ensure that they
are still valid. Then add any non-duplicate notes added by
recog_for_combine. */
for (note = REG_NOTES (undobuf.other_insn); note; note = next)
{
next = XEXP (note, 1);
if (REG_NOTE_KIND (note) == REG_UNUSED
&& ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn)))
{
if (GET_CODE (XEXP (note, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (note, 0)))--;
remove_note (undobuf.other_insn, note);
}
}
for (note = new_other_notes; note; note = XEXP (note, 1))
if (GET_CODE (XEXP (note, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (note, 0)))++;
distribute_notes (new_other_notes, undobuf.other_insn,
undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX);
}
/* We now know that we can do this combination. Merge the insns and
update the status of registers and LOG_LINKS. */
{
rtx i3notes, i2notes, i1notes = 0;
rtx i3links, i2links, i1links = 0;
rtx midnotes = 0;
register int regno;
/* Compute which registers we expect to eliminate. newi2pat may be setting
either i3dest or i2dest, so we must check it. Also, i1dest may be the
same as i3dest, in which case newi2pat may be setting i1dest. */
rtx elim_i2 = ((newi2pat && reg_set_p (i2dest, newi2pat))
|| i2dest_in_i2src || i2dest_in_i1src
? 0 : i2dest);
rtx elim_i1 = (i1 == 0 || i1dest_in_i1src
|| (newi2pat && reg_set_p (i1dest, newi2pat))
? 0 : i1dest);
/* Get the old REG_NOTES and LOG_LINKS from all our insns and
clear them. */
i3notes = REG_NOTES (i3), i3links = LOG_LINKS (i3);
i2notes = REG_NOTES (i2), i2links = LOG_LINKS (i2);
if (i1)
i1notes = REG_NOTES (i1), i1links = LOG_LINKS (i1);
/* Ensure that we do not have something that should not be shared but
occurs multiple times in the new insns. Check this by first
resetting all the `used' flags and then copying anything is shared. */
reset_used_flags (i3notes);
reset_used_flags (i2notes);
reset_used_flags (i1notes);
reset_used_flags (newpat);
reset_used_flags (newi2pat);
if (undobuf.other_insn)
reset_used_flags (PATTERN (undobuf.other_insn));
i3notes = copy_rtx_if_shared (i3notes);
i2notes = copy_rtx_if_shared (i2notes);
i1notes = copy_rtx_if_shared (i1notes);
newpat = copy_rtx_if_shared (newpat);
newi2pat = copy_rtx_if_shared (newi2pat);
if (undobuf.other_insn)
reset_used_flags (PATTERN (undobuf.other_insn));
INSN_CODE (i3) = insn_code_number;
PATTERN (i3) = newpat;
if (undobuf.other_insn)
INSN_CODE (undobuf.other_insn) = other_code_number;
/* We had one special case above where I2 had more than one set and
we replaced a destination of one of those sets with the destination
of I3. In that case, we have to update LOG_LINKS of insns later
in this basic block. Note that this (expensive) case is rare.
Also, in this case, we must pretend that all REG_NOTEs for I2
actually came from I3, so that REG_UNUSED notes from I2 will be
properly handled. */
if (i3_subst_into_i2)
{
for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++)
if (GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) == REG
&& SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest
&& ! find_reg_note (i2, REG_UNUSED,
SET_DEST (XVECEXP (PATTERN (i2), 0, i))))
for (temp = NEXT_INSN (i2);
temp && (this_basic_block == n_basic_blocks - 1
|| BLOCK_HEAD (this_basic_block) != temp);
temp = NEXT_INSN (temp))
if (temp != i3 && GET_RTX_CLASS (GET_CODE (temp)) == 'i')
for (link = LOG_LINKS (temp); link; link = XEXP (link, 1))
if (XEXP (link, 0) == i2)
XEXP (link, 0) = i3;
if (i3notes)
{
rtx link = i3notes;
while (XEXP (link, 1))
link = XEXP (link, 1);
XEXP (link, 1) = i2notes;
}
else
i3notes = i2notes;
i2notes = 0;
}
LOG_LINKS (i3) = 0;
REG_NOTES (i3) = 0;
LOG_LINKS (i2) = 0;
REG_NOTES (i2) = 0;
if (newi2pat)
{
INSN_CODE (i2) = i2_code_number;
PATTERN (i2) = newi2pat;
}
else
{
PUT_CODE (i2, NOTE);
NOTE_LINE_NUMBER (i2) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (i2) = 0;
}
if (i1)
{
LOG_LINKS (i1) = 0;
REG_NOTES (i1) = 0;
PUT_CODE (i1, NOTE);
NOTE_LINE_NUMBER (i1) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (i1) = 0;
}
/* Get death notes for everything that is now used in either I3 or
I2 and used to die in a previous insn. If we built two new
patterns, move from I1 to I2 then I2 to I3 so that we get the
proper movement on registers that I2 modifies. */
if (newi2pat)
{
move_deaths (newi2pat, NULL_RTX, INSN_CUID (i1), i2, &midnotes);
move_deaths (newpat, newi2pat, INSN_CUID (i1), i3, &midnotes);
}
else
move_deaths (newpat, NULL_RTX, i1 ? INSN_CUID (i1) : INSN_CUID (i2),
i3, &midnotes);
/* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */
if (i3notes)
distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX,
elim_i2, elim_i1);
if (i2notes)
distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX,
elim_i2, elim_i1);
if (i1notes)
distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX,
elim_i2, elim_i1);
if (midnotes)
distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
elim_i2, elim_i1);
/* Distribute any notes added to I2 or I3 by recog_for_combine. We
know these are REG_UNUSED and want them to go to the desired insn,
so we always pass it as i3. We have not counted the notes in
reg_n_deaths yet, so we need to do so now. */
if (newi2pat && new_i2_notes)
{
for (temp = new_i2_notes; temp; temp = XEXP (temp, 1))
if (GET_CODE (XEXP (temp, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (temp, 0)))++;
distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX);
}
if (new_i3_notes)
{
for (temp = new_i3_notes; temp; temp = XEXP (temp, 1))
if (GET_CODE (XEXP (temp, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (temp, 0)))++;
distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX);
}
/* If I3DEST was used in I3SRC, it really died in I3. We may need to
put a REG_DEAD note for it somewhere. If NEWI2PAT exists and sets
I3DEST, the death must be somewhere before I2, not I3. If we passed I3
in that case, it might delete I2. Similarly for I2 and I1.
Show an additional death due to the REG_DEAD note we make here. If
we discard it in distribute_notes, we will decrement it again. */
if (i3dest_killed)
{
if (GET_CODE (i3dest_killed) == REG)
REG_N_DEATHS (REGNO (i3dest_killed))++;
if (newi2pat && reg_set_p (i3dest_killed, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed,
NULL_RTX),
NULL_RTX, i2, NULL_RTX, elim_i2, elim_i1);
else
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed,
NULL_RTX),
NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
elim_i2, elim_i1);
}
if (i2dest_in_i2src)
{
if (GET_CODE (i2dest) == REG)
REG_N_DEATHS (REGNO (i2dest))++;
if (newi2pat && reg_set_p (i2dest, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX),
NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
else
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX),
NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
NULL_RTX, NULL_RTX);
}
if (i1dest_in_i1src)
{
if (GET_CODE (i1dest) == REG)
REG_N_DEATHS (REGNO (i1dest))++;
if (newi2pat && reg_set_p (i1dest, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX),
NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
else
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX),
NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
NULL_RTX, NULL_RTX);
}
distribute_links (i3links);
distribute_links (i2links);
distribute_links (i1links);
if (GET_CODE (i2dest) == REG)
{
rtx link;
rtx i2_insn = 0, i2_val = 0, set;
/* The insn that used to set this register doesn't exist, and
this life of the register may not exist either. See if one of
I3's links points to an insn that sets I2DEST. If it does,
that is now the last known value for I2DEST. If we don't update
this and I2 set the register to a value that depended on its old
contents, we will get confused. If this insn is used, thing
will be set correctly in combine_instructions. */
for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
if ((set = single_set (XEXP (link, 0))) != 0
&& rtx_equal_p (i2dest, SET_DEST (set)))
i2_insn = XEXP (link, 0), i2_val = SET_SRC (set);
record_value_for_reg (i2dest, i2_insn, i2_val);
/* If the reg formerly set in I2 died only once and that was in I3,
zero its use count so it won't make `reload' do any work. */
if (! added_sets_2
&& (newi2pat == 0 || ! reg_mentioned_p (i2dest, newi2pat))
&& ! i2dest_in_i2src)
{
regno = REGNO (i2dest);
REG_N_SETS (regno)--;
if (REG_N_SETS (regno) == 0
&& ! REGNO_REG_SET_P (BASIC_BLOCK (0)->global_live_at_start,
regno))
REG_N_REFS (regno) = 0;
}
}
if (i1 && GET_CODE (i1dest) == REG)
{
rtx link;
rtx i1_insn = 0, i1_val = 0, set;
for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
if ((set = single_set (XEXP (link, 0))) != 0
&& rtx_equal_p (i1dest, SET_DEST (set)))
i1_insn = XEXP (link, 0), i1_val = SET_SRC (set);
record_value_for_reg (i1dest, i1_insn, i1_val);
regno = REGNO (i1dest);
if (! added_sets_1 && ! i1dest_in_i1src)
{
REG_N_SETS (regno)--;
if (REG_N_SETS (regno) == 0
&& ! REGNO_REG_SET_P (BASIC_BLOCK (0)->global_live_at_start,
regno))
REG_N_REFS (regno) = 0;
}
}
/* Update reg_nonzero_bits et al for any changes that may have been made
to this insn. */
note_stores (newpat, set_nonzero_bits_and_sign_copies);
if (newi2pat)
note_stores (newi2pat, set_nonzero_bits_and_sign_copies);
/* If I3 is now an unconditional jump, ensure that it has a
BARRIER following it since it may have initially been a
conditional jump. It may also be the last nonnote insn. */
if ((GET_CODE (newpat) == RETURN || simplejump_p (i3))
&& ((temp = next_nonnote_insn (i3)) == NULL_RTX
|| GET_CODE (temp) != BARRIER))
emit_barrier_after (i3);
}
combine_successes++;
/* Clear this here, so that subsequent get_last_value calls are not
affected. */
subst_prev_insn = NULL_RTX;
if (added_links_insn
&& (newi2pat == 0 || INSN_CUID (added_links_insn) < INSN_CUID (i2))
&& INSN_CUID (added_links_insn) < INSN_CUID (i3))
return added_links_insn;
else
return newi2pat ? i2 : i3;
}
/* Undo all the modifications recorded in undobuf. */
static void
undo_all ()
{
struct undo *undo, *next;
for (undo = undobuf.undos; undo; undo = next)
{
next = undo->next;
if (undo->is_int)
*undo->where.i = undo->old_contents.i;
else
*undo->where.r = undo->old_contents.r;
undo->next = undobuf.frees;
undobuf.frees = undo;
}
obfree (undobuf.storage);
undobuf.undos = undobuf.previous_undos = 0;
/* Clear this here, so that subsequent get_last_value calls are not
affected. */
subst_prev_insn = NULL_RTX;
}
/* Find the innermost point within the rtx at LOC, possibly LOC itself,
where we have an arithmetic expression and return that point. LOC will
be inside INSN.
try_combine will call this function to see if an insn can be split into
two insns. */
static rtx *
find_split_point (loc, insn)
rtx *loc;
rtx insn;
{
rtx x = *loc;
enum rtx_code code = GET_CODE (x);
rtx *split;
int len = 0, pos, unsignedp;
rtx inner;
/* First special-case some codes. */
switch (code)
{
case SUBREG:
#ifdef INSN_SCHEDULING
/* If we are making a paradoxical SUBREG invalid, it becomes a split
point. */
if (GET_CODE (SUBREG_REG (x)) == MEM)
return loc;
#endif
return find_split_point (&SUBREG_REG (x), insn);
case MEM:
#ifdef HAVE_lo_sum
/* If we have (mem (const ..)) or (mem (symbol_ref ...)), split it
using LO_SUM and HIGH. */
if (GET_CODE (XEXP (x, 0)) == CONST
|| GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
{
SUBST (XEXP (x, 0),
gen_rtx_combine (LO_SUM, Pmode,
gen_rtx_combine (HIGH, Pmode, XEXP (x, 0)),
XEXP (x, 0)));
return &XEXP (XEXP (x, 0), 0);
}
#endif
/* If we have a PLUS whose second operand is a constant and the
address is not valid, perhaps will can split it up using
the machine-specific way to split large constants. We use
the first pseudo-reg (one of the virtual regs) as a placeholder;
it will not remain in the result. */
if (GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& ! memory_address_p (GET_MODE (x), XEXP (x, 0)))
{
rtx reg = regno_reg_rtx[FIRST_PSEUDO_REGISTER];
rtx seq = split_insns (gen_rtx_SET (VOIDmode, reg, XEXP (x, 0)),
subst_insn);
/* This should have produced two insns, each of which sets our
placeholder. If the source of the second is a valid address,
we can make put both sources together and make a split point
in the middle. */
if (seq && XVECLEN (seq, 0) == 2
&& GET_CODE (XVECEXP (seq, 0, 0)) == INSN
&& GET_CODE (PATTERN (XVECEXP (seq, 0, 0))) == SET
&& SET_DEST (PATTERN (XVECEXP (seq, 0, 0))) == reg
&& ! reg_mentioned_p (reg,
SET_SRC (PATTERN (XVECEXP (seq, 0, 0))))
&& GET_CODE (XVECEXP (seq, 0, 1)) == INSN
&& GET_CODE (PATTERN (XVECEXP (seq, 0, 1))) == SET
&& SET_DEST (PATTERN (XVECEXP (seq, 0, 1))) == reg
&& memory_address_p (GET_MODE (x),
SET_SRC (PATTERN (XVECEXP (seq, 0, 1)))))
{
rtx src1 = SET_SRC (PATTERN (XVECEXP (seq, 0, 0)));
rtx src2 = SET_SRC (PATTERN (XVECEXP (seq, 0, 1)));
/* Replace the placeholder in SRC2 with SRC1. If we can
find where in SRC2 it was placed, that can become our
split point and we can replace this address with SRC2.
Just try two obvious places. */
src2 = replace_rtx (src2, reg, src1);
split = 0;
if (XEXP (src2, 0) == src1)
split = &XEXP (src2, 0);
else if (GET_RTX_FORMAT (GET_CODE (XEXP (src2, 0)))[0] == 'e'
&& XEXP (XEXP (src2, 0), 0) == src1)
split = &XEXP (XEXP (src2, 0), 0);
if (split)
{
SUBST (XEXP (x, 0), src2);
return split;
}
}
/* If that didn't work, perhaps the first operand is complex and
needs to be computed separately, so make a split point there.
This will occur on machines that just support REG + CONST
and have a constant moved through some previous computation. */
else if (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) != 'o'
&& ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG
&& (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (XEXP (x, 0), 0))))
== 'o')))
return &XEXP (XEXP (x, 0), 0);
}
break;
case SET:
#ifdef HAVE_cc0
/* If SET_DEST is CC0 and SET_SRC is not an operand, a COMPARE, or a
ZERO_EXTRACT, the most likely reason why this doesn't match is that
we need to put the operand into a register. So split at that
point. */
if (SET_DEST (x) == cc0_rtx
&& GET_CODE (SET_SRC (x)) != COMPARE
&& GET_CODE (SET_SRC (x)) != ZERO_EXTRACT
&& GET_RTX_CLASS (GET_CODE (SET_SRC (x))) != 'o'
&& ! (GET_CODE (SET_SRC (x)) == SUBREG
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) == 'o'))
return &SET_SRC (x);
#endif
/* See if we can split SET_SRC as it stands. */
split = find_split_point (&SET_SRC (x), insn);
if (split && split != &SET_SRC (x))
return split;
/* See if we can split SET_DEST as it stands. */
split = find_split_point (&SET_DEST (x), insn);
if (split && split != &SET_DEST (x))
return split;
/* See if this is a bitfield assignment with everything constant. If
so, this is an IOR of an AND, so split it into that. */
if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)))
<= HOST_BITS_PER_WIDE_INT)
&& GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT
&& GET_CODE (XEXP (SET_DEST (x), 2)) == CONST_INT
&& GET_CODE (SET_SRC (x)) == CONST_INT
&& ((INTVAL (XEXP (SET_DEST (x), 1))
+ INTVAL (XEXP (SET_DEST (x), 2)))
<= GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0))))
&& ! side_effects_p (XEXP (SET_DEST (x), 0)))
{
int pos = INTVAL (XEXP (SET_DEST (x), 2));
int len = INTVAL (XEXP (SET_DEST (x), 1));
int src = INTVAL (SET_SRC (x));
rtx dest = XEXP (SET_DEST (x), 0);
enum machine_mode mode = GET_MODE (dest);
unsigned HOST_WIDE_INT mask = ((HOST_WIDE_INT) 1 << len) - 1;
if (BITS_BIG_ENDIAN)
pos = GET_MODE_BITSIZE (mode) - len - pos;
if ((unsigned HOST_WIDE_INT) src == mask)
SUBST (SET_SRC (x),
gen_binary (IOR, mode, dest, GEN_INT (src << pos)));
else
SUBST (SET_SRC (x),
gen_binary (IOR, mode,
gen_binary (AND, mode, dest,
GEN_INT (~ (mask << pos)
& GET_MODE_MASK (mode))),
GEN_INT (src << pos)));
SUBST (SET_DEST (x), dest);
split = find_split_point (&SET_SRC (x), insn);
if (split && split != &SET_SRC (x))
return split;
}
/* Otherwise, see if this is an operation that we can split into two.
If so, try to split that. */
code = GET_CODE (SET_SRC (x));
switch (code)
{
case AND:
/* If we are AND'ing with a large constant that is only a single
bit and the result is only being used in a context where we
need to know if it is zero or non-zero, replace it with a bit
extraction. This will avoid the large constant, which might
have taken more than one insn to make. If the constant were
not a valid argument to the AND but took only one insn to make,
this is no worse, but if it took more than one insn, it will
be better. */
if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
&& GET_CODE (XEXP (SET_SRC (x), 0)) == REG
&& (pos = exact_log2 (INTVAL (XEXP (SET_SRC (x), 1)))) >= 7
&& GET_CODE (SET_DEST (x)) == REG
&& (split = find_single_use (SET_DEST (x), insn, NULL_PTR)) != 0
&& (GET_CODE (*split) == EQ || GET_CODE (*split) == NE)
&& XEXP (*split, 0) == SET_DEST (x)
&& XEXP (*split, 1) == const0_rtx)
{
rtx extraction = make_extraction (GET_MODE (SET_DEST (x)),
XEXP (SET_SRC (x), 0),
pos, NULL_RTX, 1, 1, 0, 0);
if (extraction != 0)
{
SUBST (SET_SRC (x), extraction);
return find_split_point (loc, insn);
}
}
break;
case NE:
/* if STORE_FLAG_VALUE is -1, this is (NE X 0) and only one bit of X
is known to be on, this can be converted into a NEG of a shift. */
if (STORE_FLAG_VALUE == -1 && XEXP (SET_SRC (x), 1) == const0_rtx
&& GET_MODE (SET_SRC (x)) == GET_MODE (XEXP (SET_SRC (x), 0))
&& 1 <= (pos = exact_log2
(nonzero_bits (XEXP (SET_SRC (x), 0),
GET_MODE (XEXP (SET_SRC (x), 0))))))
{
enum machine_mode mode = GET_MODE (XEXP (SET_SRC (x), 0));
SUBST (SET_SRC (x),
gen_rtx_combine (NEG, mode,
gen_rtx_combine (LSHIFTRT, mode,
XEXP (SET_SRC (x), 0),
GEN_INT (pos))));
split = find_split_point (&SET_SRC (x), insn);
if (split && split != &SET_SRC (x))
return split;
}
break;
case SIGN_EXTEND:
inner = XEXP (SET_SRC (x), 0);
/* We can't optimize if either mode is a partial integer
mode as we don't know how many bits are significant
in those modes. */
if (GET_MODE_CLASS (GET_MODE (inner)) == MODE_PARTIAL_INT
|| GET_MODE_CLASS (GET_MODE (SET_SRC (x))) == MODE_PARTIAL_INT)
break;
pos = 0;
len = GET_MODE_BITSIZE (GET_MODE (inner));
unsignedp = 0;
break;
case SIGN_EXTRACT:
case ZERO_EXTRACT:
if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
&& GET_CODE (XEXP (SET_SRC (x), 2)) == CONST_INT)
{
inner = XEXP (SET_SRC (x), 0);
len = INTVAL (XEXP (SET_SRC (x), 1));
pos = INTVAL (XEXP (SET_SRC (x), 2));
if (BITS_BIG_ENDIAN)
pos = GET_MODE_BITSIZE (GET_MODE (inner)) - len - pos;
unsignedp = (code == ZERO_EXTRACT);
}
break;
default:
break;
}
if (len && pos >= 0 && pos + len <= GET_MODE_BITSIZE (GET_MODE (inner)))
{
enum machine_mode mode = GET_MODE (SET_SRC (x));
/* For unsigned, we have a choice of a shift followed by an
AND or two shifts. Use two shifts for field sizes where the
constant might be too large. We assume here that we can
always at least get 8-bit constants in an AND insn, which is
true for every current RISC. */
if (unsignedp && len <= 8)
{
SUBST (SET_SRC (x),
gen_rtx_combine
(AND, mode,
gen_rtx_combine (LSHIFTRT, mode,
gen_lowpart_for_combine (mode, inner),
GEN_INT (pos)),
GEN_INT (((HOST_WIDE_INT) 1 << len) - 1)));
split = find_split_point (&SET_SRC (x), insn);
if (split && split != &SET_SRC (x))
return split;
}
else
{
SUBST (SET_SRC (x),
gen_rtx_combine
(unsignedp ? LSHIFTRT : ASHIFTRT, mode,
gen_rtx_combine (ASHIFT, mode,
gen_lowpart_for_combine (mode, inner),
GEN_INT (GET_MODE_BITSIZE (mode)
- len - pos)),
GEN_INT (GET_MODE_BITSIZE (mode) - len)));
split = find_split_point (&SET_SRC (x), insn);
if (split && split != &SET_SRC (x))
return split;
}
}
/* See if this is a simple operation with a constant as the second
operand. It might be that this constant is out of range and hence
could be used as a split point. */
if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2'
|| GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c'
|| GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<')
&& CONSTANT_P (XEXP (SET_SRC (x), 1))
&& (GET_RTX_CLASS (GET_CODE (XEXP (SET_SRC (x), 0))) == 'o'
|| (GET_CODE (XEXP (SET_SRC (x), 0)) == SUBREG
&& (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (SET_SRC (x), 0))))
== 'o'))))
return &XEXP (SET_SRC (x), 1);
/* Finally, see if this is a simple operation with its first operand
not in a register. The operation might require this operand in a
register, so return it as a split point. We can always do this
because if the first operand were another operation, we would have
already found it as a split point. */
if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2'
|| GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c'
|| GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<'
|| GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '1')
&& ! register_operand (XEXP (SET_SRC (x), 0), VOIDmode))
return &XEXP (SET_SRC (x), 0);
return 0;
case AND:
case IOR:
/* We write NOR as (and (not A) (not B)), but if we don't have a NOR,
it is better to write this as (not (ior A B)) so we can split it.
Similarly for IOR. */
if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT)
{
SUBST (*loc,
gen_rtx_combine (NOT, GET_MODE (x),
gen_rtx_combine (code == IOR ? AND : IOR,
GET_MODE (x),
XEXP (XEXP (x, 0), 0),
XEXP (XEXP (x, 1), 0))));
return find_split_point (loc, insn);
}
/* Many RISC machines have a large set of logical insns. If the
second operand is a NOT, put it first so we will try to split the
other operand first. */
if (GET_CODE (XEXP (x, 1)) == NOT)
{
rtx tem = XEXP (x, 0);
SUBST (XEXP (x, 0), XEXP (x, 1));
SUBST (XEXP (x, 1), tem);
}
break;
default:
break;
}
/* Otherwise, select our actions depending on our rtx class. */
switch (GET_RTX_CLASS (code))
{
case 'b': /* This is ZERO_EXTRACT and SIGN_EXTRACT. */
case '3':
split = find_split_point (&XEXP (x, 2), insn);
if (split)
return split;
/* ... fall through ... */
case '2':
case 'c':
case '<':
split = find_split_point (&XEXP (x, 1), insn);
if (split)
return split;
/* ... fall through ... */
case '1':
/* Some machines have (and (shift ...) ...) insns. If X is not
an AND, but XEXP (X, 0) is, use it as our split point. */
if (GET_CODE (x) != AND && GET_CODE (XEXP (x, 0)) == AND)
return &XEXP (x, 0);
split = find_split_point (&XEXP (x, 0), insn);
if (split)
return split;
return loc;
}
/* Otherwise, we don't have a split point. */
return 0;
}
/* Throughout X, replace FROM with TO, and return the result.
The result is TO if X is FROM;
otherwise the result is X, but its contents may have been modified.
If they were modified, a record was made in undobuf so that
undo_all will (among other things) return X to its original state.
If the number of changes necessary is too much to record to undo,
the excess changes are not made, so the result is invalid.
The changes already made can still be undone.
undobuf.num_undo is incremented for such changes, so by testing that
the caller can tell whether the result is valid.
`n_occurrences' is incremented each time FROM is replaced.
IN_DEST is non-zero if we are processing the SET_DEST of a SET.
UNIQUE_COPY is non-zero if each substitution must be unique. We do this
by copying if `n_occurrences' is non-zero. */
static rtx
subst (x, from, to, in_dest, unique_copy)
register rtx x, from, to;
int in_dest;
int unique_copy;
{
register enum rtx_code code = GET_CODE (x);
enum machine_mode op0_mode = VOIDmode;
register char *fmt;
register int len, i;
rtx new;
/* Two expressions are equal if they are identical copies of a shared
RTX or if they are both registers with the same register number
and mode. */
#define COMBINE_RTX_EQUAL_P(X,Y) \
((X) == (Y) \
|| (GET_CODE (X) == REG && GET_CODE (Y) == REG \
&& REGNO (X) == REGNO (Y) && GET_MODE (X) == GET_MODE (Y)))
if (! in_dest && COMBINE_RTX_EQUAL_P (x, from))
{
n_occurrences++;
return (unique_copy && n_occurrences > 1 ? copy_rtx (to) : to);
}
/* If X and FROM are the same register but different modes, they will
not have been seen as equal above. However, flow.c will make a
LOG_LINKS entry for that case. If we do nothing, we will try to
rerecognize our original insn and, when it succeeds, we will
delete the feeding insn, which is incorrect.
So force this insn not to match in this (rare) case. */
if (! in_dest && code == REG && GET_CODE (from) == REG
&& REGNO (x) == REGNO (from))
return gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
/* If this is an object, we are done unless it is a MEM or LO_SUM, both
of which may contain things that can be combined. */
if (code != MEM && code != LO_SUM && GET_RTX_CLASS (code) == 'o')
return x;
/* It is possible to have a subexpression appear twice in the insn.
Suppose that FROM is a register that appears within TO.
Then, after that subexpression has been scanned once by `subst',
the second time it is scanned, TO may be found. If we were
to scan TO here, we would find FROM within it and create a
self-referent rtl structure which is completely wrong. */
if (COMBINE_RTX_EQUAL_P (x, to))
return to;
/* Parallel asm_operands need special attention because all of the
inputs are shared across the arms. Furthermore, unsharing the
rtl results in recognition failures. Failure to handle this case
specially can result in circular rtl.
Solve this by doing a normal pass across the first entry of the
parallel, and only processing the SET_DESTs of the subsequent
entries. Ug. */
if (code == PARALLEL
&& GET_CODE (XVECEXP (x, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (x, 0, 0))) == ASM_OPERANDS)
{
new = subst (XVECEXP (x, 0, 0), from, to, 0, unique_copy);
/* If this substitution failed, this whole thing fails. */
if (GET_CODE (new) == CLOBBER
&& XEXP (new, 0) == const0_rtx)
return new;
SUBST (XVECEXP (x, 0, 0), new);
for (i = XVECLEN (x, 0) - 1; i >= 1; i--)
{
rtx dest = SET_DEST (XVECEXP (x, 0, i));
if (GET_CODE (dest) != REG
&& GET_CODE (dest) != CC0
&& GET_CODE (dest) != PC)
{
new = subst (dest, from, to, 0, unique_copy);
/* If this substitution failed, this whole thing fails. */
if (GET_CODE (new) == CLOBBER
&& XEXP (new, 0) == const0_rtx)
return new;
SUBST (SET_DEST (XVECEXP (x, 0, i)), new);
}
}
}
else
{
len = GET_RTX_LENGTH (code);
fmt = GET_RTX_FORMAT (code);
/* We don't need to process a SET_DEST that is a register, CC0,
or PC, so set up to skip this common case. All other cases
where we want to suppress replacing something inside a
SET_SRC are handled via the IN_DEST operand. */
if (code == SET
&& (GET_CODE (SET_DEST (x)) == REG
|| GET_CODE (SET_DEST (x)) == CC0
|| GET_CODE (SET_DEST (x)) == PC))
fmt = "ie";
/* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a
constant. */
if (fmt[0] == 'e')
op0_mode = GET_MODE (XEXP (x, 0));
for (i = 0; i < len; i++)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
{
if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from))
{
new = (unique_copy && n_occurrences
? copy_rtx (to) : to);
n_occurrences++;
}
else
{
new = subst (XVECEXP (x, i, j), from, to, 0,
unique_copy);
/* If this substitution failed, this whole thing
fails. */
if (GET_CODE (new) == CLOBBER
&& XEXP (new, 0) == const0_rtx)
return new;
}
SUBST (XVECEXP (x, i, j), new);
}
}
else if (fmt[i] == 'e')
{
if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from))
{
/* In general, don't install a subreg involving two
modes not tieable. It can worsen register
allocation, and can even make invalid reload
insns, since the reg inside may need to be copied
from in the outside mode, and that may be invalid
if it is an fp reg copied in integer mode.
We allow two exceptions to this: It is valid if
it is inside another SUBREG and the mode of that
SUBREG and the mode of the inside of TO is
tieable and it is valid if X is a SET that copies
FROM to CC0. */
if (GET_CODE (to) == SUBREG
&& ! MODES_TIEABLE_P (GET_MODE (to),
GET_MODE (SUBREG_REG (to)))
&& ! (code == SUBREG
&& MODES_TIEABLE_P (GET_MODE (x),
GET_MODE (SUBREG_REG (to))))
#ifdef HAVE_cc0
&& ! (code == SET && i == 1 && XEXP (x, 0) == cc0_rtx)
#endif
)
return gen_rtx_CLOBBER (VOIDmode, const0_rtx);
new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
n_occurrences++;
}
else
/* If we are in a SET_DEST, suppress most cases unless we
have gone inside a MEM, in which case we want to
simplify the address. We assume here that things that
are actually part of the destination have their inner
parts in the first expression. This is true for SUBREG,
STRICT_LOW_PART, and ZERO_EXTRACT, which are the only
things aside from REG and MEM that should appear in a
SET_DEST. */
new = subst (XEXP (x, i), from, to,
(((in_dest
&& (code == SUBREG || code == STRICT_LOW_PART
|| code == ZERO_EXTRACT))
|| code == SET)
&& i == 0), unique_copy);
/* If we found that we will have to reject this combination,
indicate that by returning the CLOBBER ourselves, rather than
an expression containing it. This will speed things up as
well as prevent accidents where two CLOBBERs are considered
to be equal, thus producing an incorrect simplification. */
if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx)
return new;
SUBST (XEXP (x, i), new);
}
}
}
/* Try to simplify X. If the simplification changed the code, it is likely
that further simplification will help, so loop, but limit the number
of repetitions that will be performed. */
for (i = 0; i < 4; i++)
{
/* If X is sufficiently simple, don't bother trying to do anything
with it. */
if (code != CONST_INT && code != REG && code != CLOBBER)
x = simplify_rtx (x, op0_mode, i == 3, in_dest);
if (GET_CODE (x) == code)
break;
code = GET_CODE (x);
/* We no longer know the original mode of operand 0 since we
have changed the form of X) */
op0_mode = VOIDmode;
}
return x;
}
/* Simplify X, a piece of RTL. We just operate on the expression at the
outer level; call `subst' to simplify recursively. Return the new
expression.
OP0_MODE is the original mode of XEXP (x, 0); LAST is nonzero if this
will be the iteration even if an expression with a code different from
X is returned; IN_DEST is nonzero if we are inside a SET_DEST. */
static rtx
simplify_rtx (x, op0_mode, last, in_dest)
rtx x;
enum machine_mode op0_mode;
int last;
int in_dest;
{
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
rtx temp;
int i;
/* If this is a commutative operation, put a constant last and a complex
expression first. We don't need to do this for comparisons here. */
if (GET_RTX_CLASS (code) == 'c'
&& ((CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT)
|| (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == 'o'
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o')
|| (GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o'
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o')))
{
temp = XEXP (x, 0);
SUBST (XEXP (x, 0), XEXP (x, 1));
SUBST (XEXP (x, 1), temp);
}
/* If this is a PLUS, MINUS, or MULT, and the first operand is the
sign extension of a PLUS with a constant, reverse the order of the sign
extension and the addition. Note that this not the same as the original
code, but overflow is undefined for signed values. Also note that the
PLUS will have been partially moved "inside" the sign-extension, so that
the first operand of X will really look like:
(ashiftrt (plus (ashift A C4) C5) C4).
We convert this to
(plus (ashiftrt (ashift A C4) C2) C4)
and replace the first operand of X with that expression. Later parts
of this function may simplify the expression further.
For example, if we start with (mult (sign_extend (plus A C1)) C2),
we swap the SIGN_EXTEND and PLUS. Later code will apply the
distributive law to produce (plus (mult (sign_extend X) C1) C3).
We do this to simplify address expressions. */
if ((code == PLUS || code == MINUS || code == MULT)
&& GET_CODE (XEXP (x, 0)) == ASHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS
&& GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ASHIFT
&& GET_CODE (XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1)) == CONST_INT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1) == XEXP (XEXP (x, 0), 1)
&& GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
&& (temp = simplify_binary_operation (ASHIFTRT, mode,
XEXP (XEXP (XEXP (x, 0), 0), 1),
XEXP (XEXP (x, 0), 1))) != 0)
{
rtx new
= simplify_shift_const (NULL_RTX, ASHIFT, mode,
XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 0),
INTVAL (XEXP (XEXP (x, 0), 1)));
new = simplify_shift_const (NULL_RTX, ASHIFTRT, mode, new,
INTVAL (XEXP (XEXP (x, 0), 1)));
SUBST (XEXP (x, 0), gen_binary (PLUS, mode, new, temp));
}
/* If this is a simple operation applied to an IF_THEN_ELSE, try
applying it to the arms of the IF_THEN_ELSE. This often simplifies
things. Check for cases where both arms are testing the same
condition.
Don't do anything if all operands are very simple. */
if (((GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c'
|| GET_RTX_CLASS (code) == '<')
&& ((GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o'
&& ! (GET_CODE (XEXP (x, 0)) == SUBREG
&& (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0))))
== 'o')))
|| (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o'
&& ! (GET_CODE (XEXP (x, 1)) == SUBREG
&& (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 1))))
== 'o')))))
|| (GET_RTX_CLASS (code) == '1'
&& ((GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o'
&& ! (GET_CODE (XEXP (x, 0)) == SUBREG
&& (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0))))
== 'o'))))))
{
rtx cond, true, false;
cond = if_then_else_cond (x, &true, &false);
if (cond != 0
/* If everything is a comparison, what we have is highly unlikely
to be simpler, so don't use it. */
&& ! (GET_RTX_CLASS (code) == '<'
&& (GET_RTX_CLASS (GET_CODE (true)) == '<'
|| GET_RTX_CLASS (GET_CODE (false)) == '<')))
{
rtx cop1 = const0_rtx;
enum rtx_code cond_code = simplify_comparison (NE, &cond, &cop1);
if (cond_code == NE && GET_RTX_CLASS (GET_CODE (cond)) == '<')
return x;
/* Simplify the alternative arms; this may collapse the true and
false arms to store-flag values. */
true = subst (true, pc_rtx, pc_rtx, 0, 0);
false = subst (false, pc_rtx, pc_rtx, 0, 0);
/* Restarting if we generate a store-flag expression will cause
us to loop. Just drop through in this case. */
/* If the result values are STORE_FLAG_VALUE and zero, we can
just make the comparison operation. */
if (true == const_true_rtx && false == const0_rtx)
x = gen_binary (cond_code, mode, cond, cop1);
else if (true == const0_rtx && false == const_true_rtx)
x = gen_binary (reverse_condition (cond_code), mode, cond, cop1);
/* Likewise, we can make the negate of a comparison operation
if the result values are - STORE_FLAG_VALUE and zero. */
else if (GET_CODE (true) == CONST_INT
&& INTVAL (true) == - STORE_FLAG_VALUE
&& false == const0_rtx)
x = gen_unary (NEG, mode, mode,
gen_binary (cond_code, mode, cond, cop1));
else if (GET_CODE (false) == CONST_INT
&& INTVAL (false) == - STORE_FLAG_VALUE
&& true == const0_rtx)
x = gen_unary (NEG, mode, mode,
gen_binary (reverse_condition (cond_code),
mode, cond, cop1));
else
return gen_rtx_IF_THEN_ELSE (mode,
gen_binary (cond_code, VOIDmode,
cond, cop1),
true, false);
code = GET_CODE (x);
op0_mode = VOIDmode;
}
}
/* Try to fold this expression in case we have constants that weren't
present before. */
temp = 0;
switch (GET_RTX_CLASS (code))
{
case '1':
temp = simplify_unary_operation (code, mode, XEXP (x, 0), op0_mode);
break;
case '<':
temp = simplify_relational_operation (code, op0_mode,
XEXP (x, 0), XEXP (x, 1));
#ifdef FLOAT_STORE_FLAG_VALUE
if (temp != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
temp = ((temp == const0_rtx) ? CONST0_RTX (GET_MODE (x))
: immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, GET_MODE (x)));
#endif
break;
case 'c':
case '2':
temp = simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
break;
case 'b':
case '3':
temp = simplify_ternary_operation (code, mode, op0_mode, XEXP (x, 0),
XEXP (x, 1), XEXP (x, 2));
break;
}
if (temp)
x = temp, code = GET_CODE (temp);
/* First see if we can apply the inverse distributive law. */
if (code == PLUS || code == MINUS
|| code == AND || code == IOR || code == XOR)
{
x = apply_distributive_law (x);
code = GET_CODE (x);
}
/* If CODE is an associative operation not otherwise handled, see if we
can associate some operands. This can win if they are constants or
if they are logically related (i.e. (a & b) & a. */
if ((code == PLUS || code == MINUS
|| code == MULT || code == AND || code == IOR || code == XOR
|| code == DIV || code == UDIV
|| code == SMAX || code == SMIN || code == UMAX || code == UMIN)
&& INTEGRAL_MODE_P (mode))
{
if (GET_CODE (XEXP (x, 0)) == code)
{
rtx other = XEXP (XEXP (x, 0), 0);
rtx inner_op0 = XEXP (XEXP (x, 0), 1);
rtx inner_op1 = XEXP (x, 1);
rtx inner;
/* Make sure we pass the constant operand if any as the second
one if this is a commutative operation. */
if (CONSTANT_P (inner_op0) && GET_RTX_CLASS (code) == 'c')
{
rtx tem = inner_op0;
inner_op0 = inner_op1;
inner_op1 = tem;
}
inner = simplify_binary_operation (code == MINUS ? PLUS
: code == DIV ? MULT
: code == UDIV ? MULT
: code,
mode, inner_op0, inner_op1);
/* For commutative operations, try the other pair if that one
didn't simplify. */
if (inner == 0 && GET_RTX_CLASS (code) == 'c')
{
other = XEXP (XEXP (x, 0), 1);
inner = simplify_binary_operation (code, mode,
XEXP (XEXP (x, 0), 0),
XEXP (x, 1));
}
if (inner)
return gen_binary (code, mode, other, inner);
}
}
/* A little bit of algebraic simplification here. */
switch (code)
{
case MEM:
/* Ensure that our address has any ASHIFTs converted to MULT in case
address-recognizing predicates are called later. */
temp = make_compound_operation (XEXP (x, 0), MEM);
SUBST (XEXP (x, 0), temp);
break;
case SUBREG:
/* (subreg:A (mem:B X) N) becomes a modified MEM unless the SUBREG
is paradoxical. If we can't do that safely, then it becomes
something nonsensical so that this combination won't take place. */
if (GET_CODE (SUBREG_REG (x)) == MEM
&& (GET_MODE_SIZE (mode)
<= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
{
rtx inner = SUBREG_REG (x);
int endian_offset = 0;
/* Don't change the mode of the MEM
if that would change the meaning of the address. */
if (MEM_VOLATILE_P (SUBREG_REG (x))
|| mode_dependent_address_p (XEXP (inner, 0)))
return gen_rtx_CLOBBER (mode, const0_rtx);
if (BYTES_BIG_ENDIAN)
{
if (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
endian_offset += UNITS_PER_WORD - GET_MODE_SIZE (mode);
if (GET_MODE_SIZE (GET_MODE (inner)) < UNITS_PER_WORD)
endian_offset -= (UNITS_PER_WORD
- GET_MODE_SIZE (GET_MODE (inner)));
}
/* Note if the plus_constant doesn't make a valid address
then this combination won't be accepted. */
x = gen_rtx_MEM (mode,
plus_constant (XEXP (inner, 0),
(SUBREG_WORD (x) * UNITS_PER_WORD
+ endian_offset)));
RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (inner);
MEM_COPY_ATTRIBUTES (x, inner);
return x;
}
/* If we are in a SET_DEST, these other cases can't apply. */
if (in_dest)
return x;
/* Changing mode twice with SUBREG => just change it once,
or not at all if changing back to starting mode. */
if (GET_CODE (SUBREG_REG (x)) == SUBREG)
{
if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x)))
&& SUBREG_WORD (x) == 0 && SUBREG_WORD (SUBREG_REG (x)) == 0)
return SUBREG_REG (SUBREG_REG (x));
SUBST_INT (SUBREG_WORD (x),
SUBREG_WORD (x) + SUBREG_WORD (SUBREG_REG (x)));
SUBST (SUBREG_REG (x), SUBREG_REG (SUBREG_REG (x)));
}
/* SUBREG of a hard register => just change the register number
and/or mode. If the hard register is not valid in that mode,
suppress this combination. If the hard register is the stack,
frame, or argument pointer, leave this as a SUBREG. */
if (GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
&& REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& REGNO (SUBREG_REG (x)) != HARD_FRAME_POINTER_REGNUM
#endif
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM
#endif
&& REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM)
{
if (HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (x)) + SUBREG_WORD (x),
mode))
return gen_rtx_REG (mode,
REGNO (SUBREG_REG (x)) + SUBREG_WORD (x));
else
return gen_rtx_CLOBBER (mode, const0_rtx);
}
/* For a constant, try to pick up the part we want. Handle a full
word and low-order part. Only do this if we are narrowing
the constant; if it is being widened, we have no idea what
the extra bits will have been set to. */
if (CONSTANT_P (SUBREG_REG (x)) && op0_mode != VOIDmode
&& GET_MODE_SIZE (mode) == UNITS_PER_WORD
&& GET_MODE_SIZE (op0_mode) > UNITS_PER_WORD
&& GET_MODE_CLASS (mode) == MODE_INT)
{
temp = operand_subword (SUBREG_REG (x), SUBREG_WORD (x),
0, op0_mode);
if (temp)
return temp;
}
/* If we want a subreg of a constant, at offset 0,
take the low bits. On a little-endian machine, that's
always valid. On a big-endian machine, it's valid
only if the constant's mode fits in one word. Note that we
cannot use subreg_lowpart_p since SUBREG_REG may be VOIDmode. */
if (CONSTANT_P (SUBREG_REG (x))
&& ((GET_MODE_SIZE (op0_mode) <= UNITS_PER_WORD
|| ! WORDS_BIG_ENDIAN)
? SUBREG_WORD (x) == 0
: (SUBREG_WORD (x)
== ((GET_MODE_SIZE (op0_mode)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
/ UNITS_PER_WORD)))
&& GET_MODE_SIZE (mode) <= GET_MODE_SIZE (op0_mode)
&& (! WORDS_BIG_ENDIAN
|| GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD))
return gen_lowpart_for_combine (mode, SUBREG_REG (x));
/* A paradoxical SUBREG of a VOIDmode constant is the same constant,
since we are saying that the high bits don't matter. */
if (CONSTANT_P (SUBREG_REG (x)) && GET_MODE (SUBREG_REG (x)) == VOIDmode
&& GET_MODE_SIZE (mode) > GET_MODE_SIZE (op0_mode))
return SUBREG_REG (x);
/* Note that we cannot do any narrowing for non-constants since
we might have been counting on using the fact that some bits were
zero. We now do this in the SET. */
break;
case NOT:
/* (not (plus X -1)) can become (neg X). */
if (GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 1) == constm1_rtx)
return gen_rtx_combine (NEG, mode, XEXP (XEXP (x, 0), 0));
/* Similarly, (not (neg X)) is (plus X -1). */
if (GET_CODE (XEXP (x, 0)) == NEG)
return gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0),
constm1_rtx);
/* (not (xor X C)) for C constant is (xor X D) with D = ~ C. */
if (GET_CODE (XEXP (x, 0)) == XOR
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& (temp = simplify_unary_operation (NOT, mode,
XEXP (XEXP (x, 0), 1),
mode)) != 0)
return gen_binary (XOR, mode, XEXP (XEXP (x, 0), 0), temp);
/* (not (ashift 1 X)) is (rotate ~1 X). We used to do this for operands
other than 1, but that is not valid. We could do a similar
simplification for (not (lshiftrt C X)) where C is just the sign bit,
but this doesn't seem common enough to bother with. */
if (GET_CODE (XEXP (x, 0)) == ASHIFT
&& XEXP (XEXP (x, 0), 0) == const1_rtx)
return gen_rtx_ROTATE (mode, gen_unary (NOT, mode, mode, const1_rtx),
XEXP (XEXP (x, 0), 1));
if (GET_CODE (XEXP (x, 0)) == SUBREG
&& subreg_lowpart_p (XEXP (x, 0))
&& (GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (x, 0)))))
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == ASHIFT
&& XEXP (SUBREG_REG (XEXP (x, 0)), 0) == const1_rtx)
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (XEXP (x, 0)));
x = gen_rtx_ROTATE (inner_mode,
gen_unary (NOT, inner_mode, inner_mode,
const1_rtx),
XEXP (SUBREG_REG (XEXP (x, 0)), 1));
return gen_lowpart_for_combine (mode, x);
}
/* If STORE_FLAG_VALUE is -1, (not (comparison foo bar)) can be done by
reversing the comparison code if valid. */
if (STORE_FLAG_VALUE == -1
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& reversible_comparison_p (XEXP (x, 0)))
return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))),
mode, XEXP (XEXP (x, 0), 0),
XEXP (XEXP (x, 0), 1));
/* (ashiftrt foo C) where C is the number of bits in FOO minus 1
is (lt foo (const_int 0)) if STORE_FLAG_VALUE is -1, so we can
perform the above simplification. */
if (STORE_FLAG_VALUE == -1
&& XEXP (x, 1) == const1_rtx
&& GET_CODE (XEXP (x, 0)) == ASHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1)
return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx);
/* Apply De Morgan's laws to reduce number of patterns for machines
with negating logical insns (and-not, nand, etc.). If result has
only one NOT, put it first, since that is how the patterns are
coded. */
if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND)
{
rtx in1 = XEXP (XEXP (x, 0), 0), in2 = XEXP (XEXP (x, 0), 1);
if (GET_CODE (in1) == NOT)
in1 = XEXP (in1, 0);
else
in1 = gen_rtx_combine (NOT, GET_MODE (in1), in1);
if (GET_CODE (in2) == NOT)
in2 = XEXP (in2, 0);
else if (GET_CODE (in2) == CONST_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
in2 = GEN_INT (GET_MODE_MASK (mode) & ~ INTVAL (in2));
else
in2 = gen_rtx_combine (NOT, GET_MODE (in2), in2);
if (GET_CODE (in2) == NOT)
{
rtx tem = in2;
in2 = in1; in1 = tem;
}
return gen_rtx_combine (GET_CODE (XEXP (x, 0)) == IOR ? AND : IOR,
mode, in1, in2);
}
break;
case NEG:
/* (neg (plus X 1)) can become (not X). */
if (GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 1) == const1_rtx)
return gen_rtx_combine (NOT, mode, XEXP (XEXP (x, 0), 0));
/* Similarly, (neg (not X)) is (plus X 1). */
if (GET_CODE (XEXP (x, 0)) == NOT)
return plus_constant (XEXP (XEXP (x, 0), 0), 1);
/* (neg (minus X Y)) can become (minus Y X). */
if (GET_CODE (XEXP (x, 0)) == MINUS
&& (! FLOAT_MODE_P (mode)
/* x-y != -(y-x) with IEEE floating point. */
|| TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| flag_fast_math))
return gen_binary (MINUS, mode, XEXP (XEXP (x, 0), 1),
XEXP (XEXP (x, 0), 0));
/* (neg (xor A 1)) is (plus A -1) if A is known to be either 0 or 1. */
if (GET_CODE (XEXP (x, 0)) == XOR && XEXP (XEXP (x, 0), 1) == const1_rtx
&& nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1)
return gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx);
/* NEG commutes with ASHIFT since it is multiplication. Only do this
if we can then eliminate the NEG (e.g.,
if the operand is a constant). */
if (GET_CODE (XEXP (x, 0)) == ASHIFT)
{
temp = simplify_unary_operation (NEG, mode,
XEXP (XEXP (x, 0), 0), mode);
if (temp)
{
SUBST (XEXP (XEXP (x, 0), 0), temp);
return XEXP (x, 0);
}
}
temp = expand_compound_operation (XEXP (x, 0));
/* For C equal to the width of MODE minus 1, (neg (ashiftrt X C)) can be
replaced by (lshiftrt X C). This will convert
(neg (sign_extract X 1 Y)) to (zero_extract X 1 Y). */
if (GET_CODE (temp) == ASHIFTRT
&& GET_CODE (XEXP (temp, 1)) == CONST_INT
&& INTVAL (XEXP (temp, 1)) == GET_MODE_BITSIZE (mode) - 1)
return simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0),
INTVAL (XEXP (temp, 1)));
/* If X has only a single bit that might be nonzero, say, bit I, convert
(neg X) to (ashiftrt (ashift X C-I) C-I) where C is the bitsize of
MODE minus 1. This will convert (neg (zero_extract X 1 Y)) to
(sign_extract X 1 Y). But only do this if TEMP isn't a register
or a SUBREG of one since we'd be making the expression more
complex if it was just a register. */
if (GET_CODE (temp) != REG
&& ! (GET_CODE (temp) == SUBREG
&& GET_CODE (SUBREG_REG (temp)) == REG)
&& (i = exact_log2 (nonzero_bits (temp, mode))) >= 0)
{
rtx temp1 = simplify_shift_const
(NULL_RTX, ASHIFTRT, mode,
simplify_shift_const (NULL_RTX, ASHIFT, mode, temp,
GET_MODE_BITSIZE (mode) - 1 - i),
GET_MODE_BITSIZE (mode) - 1 - i);
/* If all we did was surround TEMP with the two shifts, we
haven't improved anything, so don't use it. Otherwise,
we are better off with TEMP1. */
if (GET_CODE (temp1) != ASHIFTRT
|| GET_CODE (XEXP (temp1, 0)) != ASHIFT
|| XEXP (XEXP (temp1, 0), 0) != temp)
return temp1;
}
break;
case TRUNCATE:
/* We can't handle truncation to a partial integer mode here
because we don't know the real bitsize of the partial
integer mode. */
if (GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
break;
if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))))
SUBST (XEXP (x, 0),
force_to_mode (XEXP (x, 0), GET_MODE (XEXP (x, 0)),
GET_MODE_MASK (mode), NULL_RTX, 0));
/* (truncate:SI ({sign,zero}_extend:DI foo:SI)) == foo:SI. */
if ((GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
|| GET_CODE (XEXP (x, 0)) == ZERO_EXTEND)
&& GET_MODE (XEXP (XEXP (x, 0), 0)) == mode)
return XEXP (XEXP (x, 0), 0);
/* (truncate:SI (OP:DI ({sign,zero}_extend:DI foo:SI))) is
(OP:SI foo:SI) if OP is NEG or ABS. */
if ((GET_CODE (XEXP (x, 0)) == ABS
|| GET_CODE (XEXP (x, 0)) == NEG)
&& (GET_CODE (XEXP (XEXP (x, 0), 0)) == SIGN_EXTEND
|| GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND)
&& GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == mode)
return gen_unary (GET_CODE (XEXP (x, 0)), mode, mode,
XEXP (XEXP (XEXP (x, 0), 0), 0));
/* (truncate:SI (subreg:DI (truncate:SI X) 0)) is
(truncate:SI x). */
if (GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == TRUNCATE
&& subreg_lowpart_p (XEXP (x, 0)))
return SUBREG_REG (XEXP (x, 0));
/* If we know that the value is already truncated, we can
replace the TRUNCATE with a SUBREG if TRULY_NOOP_TRUNCATION is
nonzero for the corresponding modes. */
if (TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))
&& num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
>= GET_MODE_BITSIZE (mode) + 1)
return gen_lowpart_for_combine (mode, XEXP (x, 0));
/* A truncate of a comparison can be replaced with a subreg if
STORE_FLAG_VALUE permits. This is like the previous test,
but it works even if the comparison is done in a mode larger
than HOST_BITS_PER_WIDE_INT. */
if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& ((HOST_WIDE_INT) STORE_FLAG_VALUE &~ GET_MODE_MASK (mode)) == 0)
return gen_lowpart_for_combine (mode, XEXP (x, 0));
/* Similarly, a truncate of a register whose value is a
comparison can be replaced with a subreg if STORE_FLAG_VALUE
permits. */
if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& ((HOST_WIDE_INT) STORE_FLAG_VALUE &~ GET_MODE_MASK (mode)) == 0
&& (temp = get_last_value (XEXP (x, 0)))
&& GET_RTX_CLASS (GET_CODE (temp)) == '<')
return gen_lowpart_for_combine (mode, XEXP (x, 0));
break;
case FLOAT_TRUNCATE:
/* (float_truncate:SF (float_extend:DF foo:SF)) = foo:SF. */
if (GET_CODE (XEXP (x, 0)) == FLOAT_EXTEND
&& GET_MODE (XEXP (XEXP (x, 0), 0)) == mode)
return XEXP (XEXP (x, 0), 0);
/* (float_truncate:SF (OP:DF (float_extend:DF foo:sf))) is
(OP:SF foo:SF) if OP is NEG or ABS. */
if ((GET_CODE (XEXP (x, 0)) == ABS
|| GET_CODE (XEXP (x, 0)) == NEG)
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == FLOAT_EXTEND
&& GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == mode)
return gen_unary (GET_CODE (XEXP (x, 0)), mode, mode,
XEXP (XEXP (XEXP (x, 0), 0), 0));
/* (float_truncate:SF (subreg:DF (float_truncate:SF X) 0))
is (float_truncate:SF x). */
if (GET_CODE (XEXP (x, 0)) == SUBREG
&& subreg_lowpart_p (XEXP (x, 0))
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == FLOAT_TRUNCATE)
return SUBREG_REG (XEXP (x, 0));
break;
#ifdef HAVE_cc0
case COMPARE:
/* Convert (compare FOO (const_int 0)) to FOO unless we aren't
using cc0, in which case we want to leave it as a COMPARE
so we can distinguish it from a register-register-copy. */
if (XEXP (x, 1) == const0_rtx)
return XEXP (x, 0);
/* In IEEE floating point, x-0 is not the same as x. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0)))
|| flag_fast_math)
&& XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 0))))
return XEXP (x, 0);
break;
#endif
case CONST:
/* (const (const X)) can become (const X). Do it this way rather than
returning the inner CONST since CONST can be shared with a
REG_EQUAL note. */
if (GET_CODE (XEXP (x, 0)) == CONST)
SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
break;
#ifdef HAVE_lo_sum
case LO_SUM:
/* Convert (lo_sum (high FOO) FOO) to FOO. This is necessary so we
can add in an offset. find_split_point will split this address up
again if it doesn't match. */
if (GET_CODE (XEXP (x, 0)) == HIGH
&& rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)))
return XEXP (x, 1);
break;
#endif
case PLUS:
/* If we have (plus (plus (A const) B)), associate it so that CONST is
outermost. That's because that's the way indexed addresses are
supposed to appear. This code used to check many more cases, but
they are now checked elsewhere. */
if (GET_CODE (XEXP (x, 0)) == PLUS
&& CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1)))
return gen_binary (PLUS, mode,
gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0),
XEXP (x, 1)),
XEXP (XEXP (x, 0), 1));
/* (plus (xor (and <foo> (const_int pow2 - 1)) <c>) <-c>)
when c is (const_int (pow2 + 1) / 2) is a sign extension of a
bit-field and can be replaced by either a sign_extend or a
sign_extract. The `and' may be a zero_extend. */
if (GET_CODE (XEXP (x, 0)) == XOR
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) == - INTVAL (XEXP (XEXP (x, 0), 1))
&& (i = exact_log2 (INTVAL (XEXP (XEXP (x, 0), 1)))) >= 0
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& ((GET_CODE (XEXP (XEXP (x, 0), 0)) == AND
&& GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
&& (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))
== ((HOST_WIDE_INT) 1 << (i + 1)) - 1))
|| (GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)))
== i + 1))))
return simplify_shift_const
(NULL_RTX, ASHIFTRT, mode,
simplify_shift_const (NULL_RTX, ASHIFT, mode,
XEXP (XEXP (XEXP (x, 0), 0), 0),
GET_MODE_BITSIZE (mode) - (i + 1)),
GET_MODE_BITSIZE (mode) - (i + 1));
/* (plus (comparison A B) C) can become (neg (rev-comp A B)) if
C is 1 and STORE_FLAG_VALUE is -1 or if C is -1 and STORE_FLAG_VALUE
is 1. This produces better code than the alternative immediately
below. */
if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& reversible_comparison_p (XEXP (x, 0))
&& ((STORE_FLAG_VALUE == -1 && XEXP (x, 1) == const1_rtx)
|| (STORE_FLAG_VALUE == 1 && XEXP (x, 1) == constm1_rtx)))
return
gen_unary (NEG, mode, mode,
gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))),
mode, XEXP (XEXP (x, 0), 0),
XEXP (XEXP (x, 0), 1)));
/* If only the low-order bit of X is possibly nonzero, (plus x -1)
can become (ashiftrt (ashift (xor x 1) C) C) where C is
the bitsize of the mode - 1. This allows simplification of
"a = (b & 8) == 0;" */
if (XEXP (x, 1) == constm1_rtx
&& GET_CODE (XEXP (x, 0)) != REG
&& ! (GET_CODE (XEXP (x,0)) == SUBREG
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)
&& nonzero_bits (XEXP (x, 0), mode) == 1)
return simplify_shift_const (NULL_RTX, ASHIFTRT, mode,
simplify_shift_const (NULL_RTX, ASHIFT, mode,
gen_rtx_combine (XOR, mode,
XEXP (x, 0), const1_rtx),
GET_MODE_BITSIZE (mode) - 1),
GET_MODE_BITSIZE (mode) - 1);
/* If we are adding two things that have no bits in common, convert
the addition into an IOR. This will often be further simplified,
for example in cases like ((a & 1) + (a & 2)), which can
become a & 3. */
if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (XEXP (x, 0), mode)
& nonzero_bits (XEXP (x, 1), mode)) == 0)
return gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1));
break;
case MINUS:
/* If STORE_FLAG_VALUE is 1, (minus 1 (comparison foo bar)) can be done
by reversing the comparison code if valid. */
if (STORE_FLAG_VALUE == 1
&& XEXP (x, 0) == const1_rtx
&& GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<'
&& reversible_comparison_p (XEXP (x, 1)))
return gen_binary (reverse_condition (GET_CODE (XEXP (x, 1))),
mode, XEXP (XEXP (x, 1), 0),
XEXP (XEXP (x, 1), 1));
/* (minus <foo> (and <foo> (const_int -pow2))) becomes
(and <foo> (const_int pow2-1)) */
if (GET_CODE (XEXP (x, 1)) == AND
&& GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
&& exact_log2 (- INTVAL (XEXP (XEXP (x, 1), 1))) >= 0
&& rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)))
return simplify_and_const_int (NULL_RTX, mode, XEXP (x, 0),
- INTVAL (XEXP (XEXP (x, 1), 1)) - 1);
/* Canonicalize (minus A (plus B C)) to (minus (minus A B) C) for
integers. */
if (GET_CODE (XEXP (x, 1)) == PLUS && INTEGRAL_MODE_P (mode))
return gen_binary (MINUS, mode,
gen_binary (MINUS, mode, XEXP (x, 0),
XEXP (XEXP (x, 1), 0)),
XEXP (XEXP (x, 1), 1));
break;
case MULT:
/* If we have (mult (plus A B) C), apply the distributive law and then
the inverse distributive law to see if things simplify. This
occurs mostly in addresses, often when unrolling loops. */
if (GET_CODE (XEXP (x, 0)) == PLUS)
{
x = apply_distributive_law
(gen_binary (PLUS, mode,
gen_binary (MULT, mode,
XEXP (XEXP (x, 0), 0), XEXP (x, 1)),
gen_binary (MULT, mode,
XEXP (XEXP (x, 0), 1), XEXP (x, 1))));
if (GET_CODE (x) != MULT)
return x;
}
break;
case UDIV:
/* If this is a divide by a power of two, treat it as a shift if
its first operand is a shift. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0
&& (GET_CODE (XEXP (x, 0)) == ASHIFT
|| GET_CODE (XEXP (x, 0)) == LSHIFTRT
|| GET_CODE (XEXP (x, 0)) == ASHIFTRT
|| GET_CODE (XEXP (x, 0)) == ROTATE
|| GET_CODE (XEXP (x, 0)) == ROTATERT))
return simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (x, 0), i);
break;
case EQ: case NE:
case GT: case GTU: case GE: case GEU:
case LT: case LTU: case LE: case LEU:
/* If the first operand is a condition code, we can't do anything
with it. */
if (GET_CODE (XEXP (x, 0)) == COMPARE
|| (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) != MODE_CC
#ifdef HAVE_cc0
&& XEXP (x, 0) != cc0_rtx
#endif
))
{
rtx op0 = XEXP (x, 0);
rtx op1 = XEXP (x, 1);
enum rtx_code new_code;
if (GET_CODE (op0) == COMPARE)
op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
/* Simplify our comparison, if possible. */
new_code = simplify_comparison (code, &op0, &op1);
/* If STORE_FLAG_VALUE is 1, we can convert (ne x 0) to simply X
if only the low-order bit is possibly nonzero in X (such as when
X is a ZERO_EXTRACT of one bit). Similarly, we can convert EQ to
(xor X 1) or (minus 1 X); we use the former. Finally, if X is
known to be either 0 or -1, NE becomes a NEG and EQ becomes
(plus X 1).
Remove any ZERO_EXTRACT we made when thinking this was a
comparison. It may now be simpler to use, e.g., an AND. If a
ZERO_EXTRACT is indeed appropriate, it will be placed back by
the call to make_compound_operation in the SET case. */
if (STORE_FLAG_VALUE == 1
&& new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx && nonzero_bits (op0, mode) == 1)
return gen_lowpart_for_combine (mode,
expand_compound_operation (op0));
else if (STORE_FLAG_VALUE == 1
&& new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& (num_sign_bit_copies (op0, mode)
== GET_MODE_BITSIZE (mode)))
{
op0 = expand_compound_operation (op0);
return gen_unary (NEG, mode, mode,
gen_lowpart_for_combine (mode, op0));
}
else if (STORE_FLAG_VALUE == 1
&& new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& nonzero_bits (op0, mode) == 1)
{
op0 = expand_compound_operation (op0);
return gen_binary (XOR, mode,
gen_lowpart_for_combine (mode, op0),
const1_rtx);
}
else if (STORE_FLAG_VALUE == 1
&& new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& (num_sign_bit_copies (op0, mode)
== GET_MODE_BITSIZE (mode)))
{
op0 = expand_compound_operation (op0);
return plus_constant (gen_lowpart_for_combine (mode, op0), 1);
}
/* If STORE_FLAG_VALUE is -1, we have cases similar to
those above. */
if (STORE_FLAG_VALUE == -1
&& new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& (num_sign_bit_copies (op0, mode)
== GET_MODE_BITSIZE (mode)))
return gen_lowpart_for_combine (mode,
expand_compound_operation (op0));
else if (STORE_FLAG_VALUE == -1
&& new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& nonzero_bits (op0, mode) == 1)
{
op0 = expand_compound_operation (op0);
return gen_unary (NEG, mode, mode,
gen_lowpart_for_combine (mode, op0));
}
else if (STORE_FLAG_VALUE == -1
&& new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& (num_sign_bit_copies (op0, mode)
== GET_MODE_BITSIZE (mode)))
{
op0 = expand_compound_operation (op0);
return gen_unary (NOT, mode, mode,
gen_lowpart_for_combine (mode, op0));
}
/* If X is 0/1, (eq X 0) is X-1. */
else if (STORE_FLAG_VALUE == -1
&& new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
&& op1 == const0_rtx
&& nonzero_bits (op0, mode) == 1)
{
op0 = expand_compound_operation (op0);
return plus_constant (gen_lowpart_for_combine (mode, op0), -1);
}
/* If STORE_FLAG_VALUE says to just test the sign bit and X has just
one bit that might be nonzero, we can convert (ne x 0) to
(ashift x c) where C puts the bit in the sign bit. Remove any
AND with STORE_FLAG_VALUE when we are done, since we are only
going to test the sign bit. */
if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
== (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE(mode)-1))
&& op1 == const0_rtx
&& mode == GET_MODE (op0)
&& (i = exact_log2 (nonzero_bits (op0, mode))) >= 0)
{
x = simplify_shift_const (NULL_RTX, ASHIFT, mode,
expand_compound_operation (op0),
GET_MODE_BITSIZE (mode) - 1 - i);
if (GET_CODE (x) == AND && XEXP (x, 1) == const_true_rtx)
return XEXP (x, 0);
else
return x;
}
/* If the code changed, return a whole new comparison. */
if (new_code != code)
return gen_rtx_combine (new_code, mode, op0, op1);
/* Otherwise, keep this operation, but maybe change its operands.
This also converts (ne (compare FOO BAR) 0) to (ne FOO BAR). */
SUBST (XEXP (x, 0), op0);
SUBST (XEXP (x, 1), op1);
}
break;
case IF_THEN_ELSE:
return simplify_if_then_else (x);
case ZERO_EXTRACT:
case SIGN_EXTRACT:
case ZERO_EXTEND:
case SIGN_EXTEND:
/* If we are processing SET_DEST, we are done. */
if (in_dest)
return x;
return expand_compound_operation (x);
case SET:
return simplify_set (x);
case AND:
case IOR:
case XOR:
return simplify_logical (x, last);
case ABS:
/* (abs (neg <foo>)) -> (abs <foo>) */
if (GET_CODE (XEXP (x, 0)) == NEG)
SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
/* If the mode of the operand is VOIDmode (i.e. if it is ASM_OPERANDS),
do nothing. */
if (GET_MODE (XEXP (x, 0)) == VOIDmode)
break;
/* If operand is something known to be positive, ignore the ABS. */
if (GET_CODE (XEXP (x, 0)) == FFS || GET_CODE (XEXP (x, 0)) == ABS
|| ((GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
<= HOST_BITS_PER_WIDE_INT)
&& ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))
== 0)))
return XEXP (x, 0);
/* If operand is known to be only -1 or 0, convert ABS to NEG. */
if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode))
return gen_rtx_combine (NEG, mode, XEXP (x, 0));
break;
case FFS:
/* (ffs (*_extend <X>)) = (ffs <X>) */
if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
|| GET_CODE (XEXP (x, 0)) == ZERO_EXTEND)
SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
break;
case FLOAT:
/* (float (sign_extend <X>)) = (float <X>). */
if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)
SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
break;
case ASHIFT:
case LSHIFTRT:
case ASHIFTRT:
case ROTATE:
case ROTATERT:
/* If this is a shift by a constant amount, simplify it. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
return simplify_shift_const (x, code, mode, XEXP (x, 0),
INTVAL (XEXP (x, 1)));
#ifdef SHIFT_COUNT_TRUNCATED
else if (SHIFT_COUNT_TRUNCATED && GET_CODE (XEXP (x, 1)) != REG)
SUBST (XEXP (x, 1),
force_to_mode (XEXP (x, 1), GET_MODE (x),
((HOST_WIDE_INT) 1
<< exact_log2 (GET_MODE_BITSIZE (GET_MODE (x))))
- 1,
NULL_RTX, 0));
#endif
break;
default:
break;
}
return x;
}
/* Simplify X, an IF_THEN_ELSE expression. Return the new expression. */
static rtx
simplify_if_then_else (x)
rtx x;
{
enum machine_mode mode = GET_MODE (x);
rtx cond = XEXP (x, 0);
rtx true = XEXP (x, 1);
rtx false = XEXP (x, 2);
enum rtx_code true_code = GET_CODE (cond);
int comparison_p = GET_RTX_CLASS (true_code) == '<';
rtx temp;
int i;
/* Simplify storing of the truth value. */
if (comparison_p && true == const_true_rtx && false == const0_rtx)
return gen_binary (true_code, mode, XEXP (cond, 0), XEXP (cond, 1));
/* Also when the truth value has to be reversed. */
if (comparison_p && reversible_comparison_p (cond)
&& true == const0_rtx && false == const_true_rtx)
return gen_binary (reverse_condition (true_code),
mode, XEXP (cond, 0), XEXP (cond, 1));
/* Sometimes we can simplify the arm of an IF_THEN_ELSE if a register used
in it is being compared against certain values. Get the true and false
comparisons and see if that says anything about the value of each arm. */
if (comparison_p && reversible_comparison_p (cond)
&& GET_CODE (XEXP (cond, 0)) == REG)
{
HOST_WIDE_INT nzb;
rtx from = XEXP (cond, 0);
enum rtx_code false_code = reverse_condition (true_code);
rtx true_val = XEXP (cond, 1);
rtx false_val = true_val;
int swapped = 0;
/* If FALSE_CODE is EQ, swap the codes and arms. */
if (false_code == EQ)
{
swapped = 1, true_code = EQ, false_code = NE;
temp = true, true = false, false = temp;
}
/* If we are comparing against zero and the expression being tested has
only a single bit that might be nonzero, that is its value when it is
not equal to zero. Similarly if it is known to be -1 or 0. */
if (true_code == EQ && true_val == const0_rtx
&& exact_log2 (nzb = nonzero_bits (from, GET_MODE (from))) >= 0)
false_code = EQ, false_val = GEN_INT (nzb);
else if (true_code == EQ && true_val == const0_rtx
&& (num_sign_bit_copies (from, GET_MODE (from))
== GET_MODE_BITSIZE (GET_MODE (from))))
false_code = EQ, false_val = constm1_rtx;
/* Now simplify an arm if we know the value of the register in the
branch and it is used in the arm. Be careful due to the potential
of locally-shared RTL. */
if (reg_mentioned_p (from, true))
true = subst (known_cond (copy_rtx (true), true_code, from, true_val),
pc_rtx, pc_rtx, 0, 0);
if (reg_mentioned_p (from, false))
false = subst (known_cond (copy_rtx (false), false_code,
from, false_val),
pc_rtx, pc_rtx, 0, 0);
SUBST (XEXP (x, 1), swapped ? false : true);
SUBST (XEXP (x, 2), swapped ? true : false);
true = XEXP (x, 1), false = XEXP (x, 2), true_code = GET_CODE (cond);
}
/* If we have (if_then_else FOO (pc) (label_ref BAR)) and FOO can be
reversed, do so to avoid needing two sets of patterns for
subtract-and-branch insns. Similarly if we have a constant in the true
arm, the false arm is the same as the first operand of the comparison, or
the false arm is more complicated than the true arm. */
if (comparison_p && reversible_comparison_p (cond)
&& (true == pc_rtx
|| (CONSTANT_P (true)
&& GET_CODE (false) != CONST_INT && false != pc_rtx)
|| true == const0_rtx
|| (GET_RTX_CLASS (GET_CODE (true)) == 'o'
&& GET_RTX_CLASS (GET_CODE (false)) != 'o')
|| (GET_CODE (true) == SUBREG
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (true))) == 'o'
&& GET_RTX_CLASS (GET_CODE (false)) != 'o')
|| reg_mentioned_p (true, false)
|| rtx_equal_p (false, XEXP (cond, 0))))
{
true_code = reverse_condition (true_code);
SUBST (XEXP (x, 0),
gen_binary (true_code, GET_MODE (cond), XEXP (cond, 0),
XEXP (cond, 1)));
SUBST (XEXP (x, 1), false);
SUBST (XEXP (x, 2), true);
temp = true, true = false, false = temp, cond = XEXP (x, 0);
/* It is possible that the conditional has been simplified out. */
true_code = GET_CODE (cond);
comparison_p = GET_RTX_CLASS (true_code) == '<';
}
/* If the two arms are identical, we don't need the comparison. */
if (rtx_equal_p (true, false) && ! side_effects_p (cond))
return true;
/* Convert a == b ? b : a to "a". */
if (true_code == EQ && ! side_effects_p (cond)
&& rtx_equal_p (XEXP (cond, 0), false)
&& rtx_equal_p (XEXP (cond, 1), true))
return false;
else if (true_code == NE && ! side_effects_p (cond)
&& rtx_equal_p (XEXP (cond, 0), true)
&& rtx_equal_p (XEXP (cond, 1), false))
return true;
/* Look for cases where we have (abs x) or (neg (abs X)). */
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_CODE (false) == NEG
&& rtx_equal_p (true, XEXP (false, 0))
&& comparison_p
&& rtx_equal_p (true, XEXP (cond, 0))
&& ! side_effects_p (true))
switch (true_code)
{
case GT:
case GE:
return gen_unary (ABS, mode, mode, true);
case LT:
case LE:
return gen_unary (NEG, mode, mode, gen_unary (ABS, mode, mode, true));
default:
break;
}
/* Look for MIN or MAX. */
if ((! FLOAT_MODE_P (mode) || flag_fast_math)
&& comparison_p
&& rtx_equal_p (XEXP (cond, 0), true)
&& rtx_equal_p (XEXP (cond, 1), false)
&& ! side_effects_p (cond))
switch (true_code)
{
case GE:
case GT:
return gen_binary (SMAX, mode, true, false);
case LE:
case LT:
return gen_binary (SMIN, mode, true, false);
case GEU:
case GTU:
return gen_binary (UMAX, mode, true, false);
case LEU:
case LTU:
return gen_binary (UMIN, mode, true, false);
default:
break;
}
/* If we have (if_then_else COND (OP Z C1) Z) and OP is an identity when its
second operand is zero, this can be done as (OP Z (mult COND C2)) where
C2 = C1 * STORE_FLAG_VALUE. Similarly if OP has an outer ZERO_EXTEND or
SIGN_EXTEND as long as Z is already extended (so we don't destroy it).
We can do this kind of thing in some cases when STORE_FLAG_VALUE is
neither 1 or -1, but it isn't worth checking for. */
if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& comparison_p && mode != VOIDmode && ! side_effects_p (x))
{
rtx t = make_compound_operation (true, SET);
rtx f = make_compound_operation (false, SET);
rtx cond_op0 = XEXP (cond, 0);
rtx cond_op1 = XEXP (cond, 1);
enum rtx_code op, extend_op = NIL;
enum machine_mode m = mode;
rtx z = 0, c1;
if ((GET_CODE (t) == PLUS || GET_CODE (t) == MINUS
|| GET_CODE (t) == IOR || GET_CODE (t) == XOR
|| GET_CODE (t) == ASHIFT
|| GET_CODE (t) == LSHIFTRT || GET_CODE (t) == ASHIFTRT)
&& rtx_equal_p (XEXP (t, 0), f))
c1 = XEXP (t, 1), op = GET_CODE (t), z = f;
/* If an identity-zero op is commutative, check whether there
would be a match if we swapped the operands. */
else if ((GET_CODE (t) == PLUS || GET_CODE (t) == IOR
|| GET_CODE (t) == XOR)
&& rtx_equal_p (XEXP (t, 1), f))
c1 = XEXP (t, 0), op = GET_CODE (t), z = f;
else if (GET_CODE (t) == SIGN_EXTEND
&& (GET_CODE (XEXP (t, 0)) == PLUS
|| GET_CODE (XEXP (t, 0)) == MINUS
|| GET_CODE (XEXP (t, 0)) == IOR
|| GET_CODE (XEXP (t, 0)) == XOR
|| GET_CODE (XEXP (t, 0)) == ASHIFT
|| GET_CODE (XEXP (t, 0)) == LSHIFTRT
|| GET_CODE (XEXP (t, 0)) == ASHIFTRT)
&& GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG
&& subreg_lowpart_p (XEXP (XEXP (t, 0), 0))
&& rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f)
&& (num_sign_bit_copies (f, GET_MODE (f))
> (GET_MODE_BITSIZE (mode)
- GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (t, 0), 0))))))
{
c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0));
extend_op = SIGN_EXTEND;
m = GET_MODE (XEXP (t, 0));
}
else if (GET_CODE (t) == SIGN_EXTEND
&& (GET_CODE (XEXP (t, 0)) == PLUS
|| GET_CODE (XEXP (t, 0)) == IOR
|| GET_CODE (XEXP (t, 0)) == XOR)
&& GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG
&& subreg_lowpart_p (XEXP (XEXP (t, 0), 1))
&& rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f)
&& (num_sign_bit_copies (f, GET_MODE (f))
> (GET_MODE_BITSIZE (mode)
- GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (t, 0), 1))))))
{
c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0));
extend_op = SIGN_EXTEND;
m = GET_MODE (XEXP (t, 0));
}
else if (GET_CODE (t) == ZERO_EXTEND
&& (GET_CODE (XEXP (t, 0)) == PLUS
|| GET_CODE (XEXP (t, 0)) == MINUS
|| GET_CODE (XEXP (t, 0)) == IOR
|| GET_CODE (XEXP (t, 0)) == XOR
|| GET_CODE (XEXP (t, 0)) == ASHIFT
|| GET_CODE (XEXP (t, 0)) == LSHIFTRT
|| GET_CODE (XEXP (t, 0)) == ASHIFTRT)
&& GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& subreg_lowpart_p (XEXP (XEXP (t, 0), 0))
&& rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f)
&& ((nonzero_bits (f, GET_MODE (f))
& ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (t, 0), 0))))
== 0))
{
c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0));
extend_op = ZERO_EXTEND;
m = GET_MODE (XEXP (t, 0));
}
else if (GET_CODE (t) == ZERO_EXTEND
&& (GET_CODE (XEXP (t, 0)) == PLUS
|| GET_CODE (XEXP (t, 0)) == IOR
|| GET_CODE (XEXP (t, 0)) == XOR)
&& GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& subreg_lowpart_p (XEXP (XEXP (t, 0), 1))
&& rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f)
&& ((nonzero_bits (f, GET_MODE (f))
& ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (t, 0), 1))))
== 0))
{
c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0));
extend_op = ZERO_EXTEND;
m = GET_MODE (XEXP (t, 0));
}
if (z)
{
temp = subst (gen_binary (true_code, m, cond_op0, cond_op1),
pc_rtx, pc_rtx, 0, 0);
temp = gen_binary (MULT, m, temp,
gen_binary (MULT, m, c1, const_true_rtx));
temp = subst (temp, pc_rtx, pc_rtx, 0, 0);
temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp);
if (extend_op != NIL)
temp = gen_unary (extend_op, mode, m, temp);
return temp;
}
}
/* If we have (if_then_else (ne A 0) C1 0) and either A is known to be 0 or
1 and C1 is a single bit or A is known to be 0 or -1 and C1 is the
negation of a single bit, we can convert this operation to a shift. We
can actually do this more generally, but it doesn't seem worth it. */
if (true_code == NE && XEXP (cond, 1) == const0_rtx
&& false == const0_rtx && GET_CODE (true) == CONST_INT
&& ((1 == nonzero_bits (XEXP (cond, 0), mode)
&& (i = exact_log2 (INTVAL (true))) >= 0)
|| ((num_sign_bit_copies (XEXP (cond, 0), mode)
== GET_MODE_BITSIZE (mode))
&& (i = exact_log2 (- INTVAL (true))) >= 0)))
return
simplify_shift_const (NULL_RTX, ASHIFT, mode,
gen_lowpart_for_combine (mode, XEXP (cond, 0)), i);
return x;
}
/* Simplify X, a SET expression. Return the new expression. */
static rtx
simplify_set (x)
rtx x;
{
rtx src = SET_SRC (x);
rtx dest = SET_DEST (x);
enum machine_mode mode
= GET_MODE (src) != VOIDmode ? GET_MODE (src) : GET_MODE (dest);
rtx other_insn;
rtx *cc_use;
/* (set (pc) (return)) gets written as (return). */
if (GET_CODE (dest) == PC && GET_CODE (src) == RETURN)
return src;
/* Now that we know for sure which bits of SRC we are using, see if we can
simplify the expression for the object knowing that we only need the
low-order bits. */
if (GET_MODE_CLASS (mode) == MODE_INT)
src = force_to_mode (src, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
/* If we are setting CC0 or if the source is a COMPARE, look for the use of
the comparison result and try to simplify it unless we already have used
undobuf.other_insn. */
if ((GET_CODE (src) == COMPARE
#ifdef HAVE_cc0
|| dest == cc0_rtx
#endif
)
&& (cc_use = find_single_use (dest, subst_insn, &other_insn)) != 0
&& (undobuf.other_insn == 0 || other_insn == undobuf.other_insn)
&& GET_RTX_CLASS (GET_CODE (*cc_use)) == '<'
&& rtx_equal_p (XEXP (*cc_use, 0), dest))
{
enum rtx_code old_code = GET_CODE (*cc_use);
enum rtx_code new_code;
rtx op0, op1;
int other_changed = 0;
enum machine_mode compare_mode = GET_MODE (dest);
if (GET_CODE (src) == COMPARE)
op0 = XEXP (src, 0), op1 = XEXP (src, 1);
else
op0 = src, op1 = const0_rtx;
/* Simplify our comparison, if possible. */
new_code = simplify_comparison (old_code, &op0, &op1);
#ifdef EXTRA_CC_MODES
/* If this machine has CC modes other than CCmode, check to see if we
need to use a different CC mode here. */
compare_mode = SELECT_CC_MODE (new_code, op0, op1);
#endif /* EXTRA_CC_MODES */
#if !defined (HAVE_cc0) && defined (EXTRA_CC_MODES)
/* If the mode changed, we have to change SET_DEST, the mode in the
compare, and the mode in the place SET_DEST is used. If SET_DEST is
a hard register, just build new versions with the proper mode. If it
is a pseudo, we lose unless it is only time we set the pseudo, in
which case we can safely change its mode. */
if (compare_mode != GET_MODE (dest))
{
int regno = REGNO (dest);
rtx new_dest = gen_rtx_REG (compare_mode, regno);
if (regno < FIRST_PSEUDO_REGISTER
|| (REG_N_SETS (regno) == 1 && ! REG_USERVAR_P (dest)))
{
if (regno >= FIRST_PSEUDO_REGISTER)
SUBST (regno_reg_rtx[regno], new_dest);
SUBST (SET_DEST (x), new_dest);
SUBST (XEXP (*cc_use, 0), new_dest);
other_changed = 1;
dest = new_dest;
}
}
#endif
/* If the code changed, we have to build a new comparison in
undobuf.other_insn. */
if (new_code != old_code)
{
unsigned HOST_WIDE_INT mask;
SUBST (*cc_use, gen_rtx_combine (new_code, GET_MODE (*cc_use),
dest, const0_rtx));
/* If the only change we made was to change an EQ into an NE or
vice versa, OP0 has only one bit that might be nonzero, and OP1
is zero, check if changing the user of the condition code will
produce a valid insn. If it won't, we can keep the original code
in that insn by surrounding our operation with an XOR. */
if (((old_code == NE && new_code == EQ)
|| (old_code == EQ && new_code == NE))
&& ! other_changed && op1 == const0_rtx
&& GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
&& exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0))) >= 0)
{
rtx pat = PATTERN (other_insn), note = 0;
if ((recog_for_combine (&pat, other_insn, &note) < 0
&& ! check_asm_operands (pat)))
{
PUT_CODE (*cc_use, old_code);
other_insn = 0;
op0 = gen_binary (XOR, GET_MODE (op0), op0, GEN_INT (mask));
}
}
other_changed = 1;
}
if (other_changed)
undobuf.other_insn = other_insn;
#ifdef HAVE_cc0
/* If we are now comparing against zero, change our source if
needed. If we do not use cc0, we always have a COMPARE. */
if (op1 == const0_rtx && dest == cc0_rtx)
{
SUBST (SET_SRC (x), op0);
src = op0;
}
else
#endif
/* Otherwise, if we didn't previously have a COMPARE in the
correct mode, we need one. */
if (GET_CODE (src) != COMPARE || GET_MODE (src) != compare_mode)
{
SUBST (SET_SRC (x),
gen_rtx_combine (COMPARE, compare_mode, op0, op1));
src = SET_SRC (x);
}
else
{
/* Otherwise, update the COMPARE if needed. */
SUBST (XEXP (src, 0), op0);
SUBST (XEXP (src, 1), op1);
}
}
else
{
/* Get SET_SRC in a form where we have placed back any
compound expressions. Then do the checks below. */
src = make_compound_operation (src, SET);
SUBST (SET_SRC (x), src);
}
/* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some operation,
and X being a REG or (subreg (reg)), we may be able to convert this to
(set (subreg:m2 x) (op)).
We can always do this if M1 is narrower than M2 because that means that
we only care about the low bits of the result.
However, on machines without WORD_REGISTER_OPERATIONS defined, we cannot
perform a narrower operation than requested since the high-order bits will
be undefined. On machine where it is defined, this transformation is safe
as long as M1 and M2 have the same number of words. */
if (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (src))) != 'o'
&& (((GET_MODE_SIZE (GET_MODE (src)) + (UNITS_PER_WORD - 1))
/ UNITS_PER_WORD)
== ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
#ifndef WORD_REGISTER_OPERATIONS
&& (GET_MODE_SIZE (GET_MODE (src))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))
#endif
#ifdef CLASS_CANNOT_CHANGE_SIZE
&& ! (GET_CODE (dest) == REG && REGNO (dest) < FIRST_PSEUDO_REGISTER
&& (TEST_HARD_REG_BIT
(reg_class_contents[(int) CLASS_CANNOT_CHANGE_SIZE],
REGNO (dest)))
&& (GET_MODE_SIZE (GET_MODE (src))
!= GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))
#endif
&& (GET_CODE (dest) == REG
|| (GET_CODE (dest) == SUBREG
&& GET_CODE (SUBREG_REG (dest)) == REG)))
{
SUBST (SET_DEST (x),
gen_lowpart_for_combine (GET_MODE (SUBREG_REG (src)),
dest));
SUBST (SET_SRC (x), SUBREG_REG (src));
src = SET_SRC (x), dest = SET_DEST (x);
}
#ifdef LOAD_EXTEND_OP
/* If we have (set FOO (subreg:M (mem:N BAR) 0)) with M wider than N, this
would require a paradoxical subreg. Replace the subreg with a
zero_extend to avoid the reload that would otherwise be required. */
if (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)
&& LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (src))) != NIL
&& SUBREG_WORD (src) == 0
&& (GET_MODE_SIZE (GET_MODE (src))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))
&& GET_CODE (SUBREG_REG (src)) == MEM)
{
SUBST (SET_SRC (x),
gen_rtx_combine (LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (src))),
GET_MODE (src), XEXP (src, 0)));
src = SET_SRC (x);
}
#endif
/* If we don't have a conditional move, SET_SRC is an IF_THEN_ELSE, and we
are comparing an item known to be 0 or -1 against 0, use a logical
operation instead. Check for one of the arms being an IOR of the other
arm with some value. We compute three terms to be IOR'ed together. In
practice, at most two will be nonzero. Then we do the IOR's. */
if (GET_CODE (dest) != PC
&& GET_CODE (src) == IF_THEN_ELSE
&& GET_MODE_CLASS (GET_MODE (src)) == MODE_INT
&& (GET_CODE (XEXP (src, 0)) == EQ || GET_CODE (XEXP (src, 0)) == NE)
&& XEXP (XEXP (src, 0), 1) == const0_rtx
&& GET_MODE (src) == GET_MODE (XEXP (XEXP (src, 0), 0))
#ifdef HAVE_conditional_move
&& ! can_conditionally_move_p (GET_MODE (src))
#endif
&& (num_sign_bit_copies (XEXP (XEXP (src, 0), 0),
GET_MODE (XEXP (XEXP (src, 0), 0)))
== GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (src, 0), 0))))
&& ! side_effects_p (src))
{
rtx true = (GET_CODE (XEXP (src, 0)) == NE
? XEXP (src, 1) : XEXP (src, 2));
rtx false = (GET_CODE (XEXP (src, 0)) == NE
? XEXP (src, 2) : XEXP (src, 1));
rtx term1 = const0_rtx, term2, term3;
if (GET_CODE (true) == IOR && rtx_equal_p (XEXP (true, 0), false))
term1 = false, true = XEXP (true, 1), false = const0_rtx;
else if (GET_CODE (true) == IOR
&& rtx_equal_p (XEXP (true, 1), false))
term1 = false, true = XEXP (true, 0), false = const0_rtx;
else if (GET_CODE (false) == IOR
&& rtx_equal_p (XEXP (false, 0), true))
term1 = true, false = XEXP (false, 1), true = const0_rtx;
else if (GET_CODE (false) == IOR
&& rtx_equal_p (XEXP (false, 1), true))
term1 = true, false = XEXP (false, 0), true = const0_rtx;
term2 = gen_binary (AND, GET_MODE (src), XEXP (XEXP (src, 0), 0), true);
term3 = gen_binary (AND, GET_MODE (src),
gen_unary (NOT, GET_MODE (src), GET_MODE (src),
XEXP (XEXP (src, 0), 0)),
false);
SUBST (SET_SRC (x),
gen_binary (IOR, GET_MODE (src),
gen_binary (IOR, GET_MODE (src), term1, term2),
term3));
src = SET_SRC (x);
}
/* If either SRC or DEST is a CLOBBER of (const_int 0), make this
whole thing fail. */
if (GET_CODE (src) == CLOBBER && XEXP (src, 0) == const0_rtx)
return src;
else if (GET_CODE (dest) == CLOBBER && XEXP (dest, 0) == const0_rtx)
return dest;
else
/* Convert this into a field assignment operation, if possible. */
return make_field_assignment (x);
}
/* Simplify, X, and AND, IOR, or XOR operation, and return the simplified
result. LAST is nonzero if this is the last retry. */
static rtx
simplify_logical (x, last)
rtx x;
int last;
{
enum machine_mode mode = GET_MODE (x);
rtx op0 = XEXP (x, 0);
rtx op1 = XEXP (x, 1);
switch (GET_CODE (x))
{
case AND:
/* Convert (A ^ B) & A to A & (~ B) since the latter is often a single
insn (and may simplify more). */
if (GET_CODE (op0) == XOR
&& rtx_equal_p (XEXP (op0, 0), op1)
&& ! side_effects_p (op1))
x = gen_binary (AND, mode,
gen_unary (NOT, mode, mode, XEXP (op0, 1)), op1);
if (GET_CODE (op0) == XOR
&& rtx_equal_p (XEXP (op0, 1), op1)
&& ! side_effects_p (op1))
x = gen_binary (AND, mode,
gen_unary (NOT, mode, mode, XEXP (op0, 0)), op1);
/* Similarly for (~ (A ^ B)) & A. */
if (GET_CODE (op0) == NOT
&& GET_CODE (XEXP (op0, 0)) == XOR
&& rtx_equal_p (XEXP (XEXP (op0, 0), 0), op1)
&& ! side_effects_p (op1))
x = gen_binary (AND, mode, XEXP (XEXP (op0, 0), 1), op1);
if (GET_CODE (op0) == NOT
&& GET_CODE (XEXP (op0, 0)) == XOR
&& rtx_equal_p (XEXP (XEXP (op0, 0), 1), op1)
&& ! side_effects_p (op1))
x = gen_binary (AND, mode, XEXP (XEXP (op0, 0), 0), op1);
if (GET_CODE (op1) == CONST_INT)
{
x = simplify_and_const_int (x, mode, op0, INTVAL (op1));
/* If we have (ior (and (X C1) C2)) and the next restart would be
the last, simplify this by making C1 as small as possible
and then exit. */
if (last
&& GET_CODE (x) == IOR && GET_CODE (op0) == AND
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& GET_CODE (op1) == CONST_INT)
return gen_binary (IOR, mode,
gen_binary (AND, mode, XEXP (op0, 0),
GEN_INT (INTVAL (XEXP (op0, 1))
& ~ INTVAL (op1))), op1);
if (GET_CODE (x) != AND)
return x;
if (GET_RTX_CLASS (GET_CODE (x)) == 'c'
|| GET_RTX_CLASS (GET_CODE (x)) == '2')
op0 = XEXP (x, 0), op1 = XEXP (x, 1);
}
/* Convert (A | B) & A to A. */
if (GET_CODE (op0) == IOR
&& (rtx_equal_p (XEXP (op0, 0), op1)
|| rtx_equal_p (XEXP (op0, 1), op1))
&& ! side_effects_p (XEXP (op0, 0))
&& ! side_effects_p (XEXP (op0, 1)))
return op1;
/* In the following group of tests (and those in case IOR below),
we start with some combination of logical operations and apply
the distributive law followed by the inverse distributive law.
Most of the time, this results in no change. However, if some of
the operands are the same or inverses of each other, simplifications
will result.
For example, (and (ior A B) (not B)) can occur as the result of
expanding a bit field assignment. When we apply the distributive
law to this, we get (ior (and (A (not B))) (and (B (not B)))),
which then simplifies to (and (A (not B))).
If we have (and (ior A B) C), apply the distributive law and then
the inverse distributive law to see if things simplify. */
if (GET_CODE (op0) == IOR || GET_CODE (op0) == XOR)
{
x = apply_distributive_law
(gen_binary (GET_CODE (op0), mode,
gen_binary (AND, mode, XEXP (op0, 0), op1),
gen_binary (AND, mode, XEXP (op0, 1), op1)));
if (GET_CODE (x) != AND)
return x;
}
if (GET_CODE (op1) == IOR || GET_CODE (op1) == XOR)
return apply_distributive_law
(gen_binary (GET_CODE (op1), mode,
gen_binary (AND, mode, XEXP (op1, 0), op0),
gen_binary (AND, mode, XEXP (op1, 1), op0)));
/* Similarly, taking advantage of the fact that
(and (not A) (xor B C)) == (xor (ior A B) (ior A C)) */
if (GET_CODE (op0) == NOT && GET_CODE (op1) == XOR)
return apply_distributive_law
(gen_binary (XOR, mode,
gen_binary (IOR, mode, XEXP (op0, 0), XEXP (op1, 0)),
gen_binary (IOR, mode, XEXP (op0, 0), XEXP (op1, 1))));
else if (GET_CODE (op1) == NOT && GET_CODE (op0) == XOR)
return apply_distributive_law
(gen_binary (XOR, mode,
gen_binary (IOR, mode, XEXP (op1, 0), XEXP (op0, 0)),
gen_binary (IOR, mode, XEXP (op1, 0), XEXP (op0, 1))));
break;
case IOR:
/* (ior A C) is C if all bits of A that might be nonzero are on in C. */
if (GET_CODE (op1) == CONST_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (op0, mode) & ~ INTVAL (op1)) == 0)
return op1;
/* Convert (A & B) | A to A. */
if (GET_CODE (op0) == AND
&& (rtx_equal_p (XEXP (op0, 0), op1)
|| rtx_equal_p (XEXP (op0, 1), op1))
&& ! side_effects_p (XEXP (op0, 0))
&& ! side_effects_p (XEXP (op0, 1)))
return op1;
/* If we have (ior (and A B) C), apply the distributive law and then
the inverse distributive law to see if things simplify. */
if (GET_CODE (op0) == AND)
{
x = apply_distributive_law
(gen_binary (AND, mode,
gen_binary (IOR, mode, XEXP (op0, 0), op1),
gen_binary (IOR, mode, XEXP (op0, 1), op1)));
if (GET_CODE (x) != IOR)
return x;
}
if (GET_CODE (op1) == AND)
{
x = apply_distributive_law
(gen_binary (AND, mode,
gen_binary (IOR, mode, XEXP (op1, 0), op0),
gen_binary (IOR, mode, XEXP (op1, 1), op0)));
if (GET_CODE (x) != IOR)
return x;
}
/* Convert (ior (ashift A CX) (lshiftrt A CY)) where CX+CY equals the
mode size to (rotate A CX). */
if (((GET_CODE (op0) == ASHIFT && GET_CODE (op1) == LSHIFTRT)
|| (GET_CODE (op1) == ASHIFT && GET_CODE (op0) == LSHIFTRT))
&& rtx_equal_p (XEXP (op0, 0), XEXP (op1, 0))
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& GET_CODE (XEXP (op1, 1)) == CONST_INT
&& (INTVAL (XEXP (op0, 1)) + INTVAL (XEXP (op1, 1))
== GET_MODE_BITSIZE (mode)))
return gen_rtx_ROTATE (mode, XEXP (op0, 0),
(GET_CODE (op0) == ASHIFT
? XEXP (op0, 1) : XEXP (op1, 1)));
/* If OP0 is (ashiftrt (plus ...) C), it might actually be
a (sign_extend (plus ...)). If so, OP1 is a CONST_INT, and the PLUS
does not affect any of the bits in OP1, it can really be done
as a PLUS and we can associate. We do this by seeing if OP1
can be safely shifted left C bits. */
if (GET_CODE (op1) == CONST_INT && GET_CODE (op0) == ASHIFTRT
&& GET_CODE (XEXP (op0, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT)
{
int count = INTVAL (XEXP (op0, 1));
HOST_WIDE_INT mask = INTVAL (op1) << count;
if (mask >> count == INTVAL (op1)
&& (mask & nonzero_bits (XEXP (op0, 0), mode)) == 0)
{
SUBST (XEXP (XEXP (op0, 0), 1),
GEN_INT (INTVAL (XEXP (XEXP (op0, 0), 1)) | mask));
return op0;
}
}
break;
case XOR:
/* Convert (XOR (NOT x) (NOT y)) to (XOR x y).
Also convert (XOR (NOT x) y) to (NOT (XOR x y)), similarly for
(NOT y). */
{
int num_negated = 0;
if (GET_CODE (op0) == NOT)
num_negated++, op0 = XEXP (op0, 0);
if (GET_CODE (op1) == NOT)
num_negated++, op1 = XEXP (op1, 0);
if (num_negated == 2)
{
SUBST (XEXP (x, 0), op0);
SUBST (XEXP (x, 1), op1);
}
else if (num_negated == 1)
return gen_unary (NOT, mode, mode, gen_binary (XOR, mode, op0, op1));
}
/* Convert (xor (and A B) B) to (and (not A) B). The latter may
correspond to a machine insn or result in further simplifications
if B is a constant. */
if (GET_CODE (op0) == AND
&& rtx_equal_p (XEXP (op0, 1), op1)
&& ! side_effects_p (op1))
return gen_binary (AND, mode,
gen_unary (NOT, mode, mode, XEXP (op0, 0)),
op1);
else if (GET_CODE (op0) == AND
&& rtx_equal_p (XEXP (op0, 0), op1)
&& ! side_effects_p (op1))
return gen_binary (AND, mode,
gen_unary (NOT, mode, mode, XEXP (op0, 1)),
op1);
/* (xor (comparison foo bar) (const_int 1)) can become the reversed
comparison if STORE_FLAG_VALUE is 1. */
if (STORE_FLAG_VALUE == 1
&& op1 == const1_rtx
&& GET_RTX_CLASS (GET_CODE (op0)) == '<'
&& reversible_comparison_p (op0))
return gen_rtx_combine (reverse_condition (GET_CODE (op0)),
mode, XEXP (op0, 0), XEXP (op0, 1));
/* (lshiftrt foo C) where C is the number of bits in FOO minus 1
is (lt foo (const_int 0)), so we can perform the above
simplification if STORE_FLAG_VALUE is 1. */
if (STORE_FLAG_VALUE == 1
&& op1 == const1_rtx
&& GET_CODE (op0) == LSHIFTRT
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) == GET_MODE_BITSIZE (mode) - 1)
return gen_rtx_combine (GE, mode, XEXP (op0, 0), const0_rtx);
/* (xor (comparison foo bar) (const_int sign-bit))
when STORE_FLAG_VALUE is the sign bit. */
if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
== (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
&& op1 == const_true_rtx
&& GET_RTX_CLASS (GET_CODE (op0)) == '<'
&& reversible_comparison_p (op0))
return gen_rtx_combine (reverse_condition (GET_CODE (op0)),
mode, XEXP (op0, 0), XEXP (op0, 1));
break;
default:
abort ();
}
return x;
}
/* We consider ZERO_EXTRACT, SIGN_EXTRACT, and SIGN_EXTEND as "compound
operations" because they can be replaced with two more basic operations.
ZERO_EXTEND is also considered "compound" because it can be replaced with
an AND operation, which is simpler, though only one operation.
The function expand_compound_operation is called with an rtx expression
and will convert it to the appropriate shifts and AND operations,
simplifying at each stage.
The function make_compound_operation is called to convert an expression
consisting of shifts and ANDs into the equivalent compound expression.
It is the inverse of this function, loosely speaking. */
static rtx
expand_compound_operation (x)
rtx x;
{
int pos = 0, len;
int unsignedp = 0;
int modewidth;
rtx tem;
switch (GET_CODE (x))
{
case ZERO_EXTEND:
unsignedp = 1;
case SIGN_EXTEND:
/* We can't necessarily use a const_int for a multiword mode;
it depends on implicitly extending the value.
Since we don't know the right way to extend it,
we can't tell whether the implicit way is right.
Even for a mode that is no wider than a const_int,
we can't win, because we need to sign extend one of its bits through
the rest of it, and we don't know which bit. */
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
return x;
/* Return if (subreg:MODE FROM 0) is not a safe replacement for
(zero_extend:MODE FROM) or (sign_extend:MODE FROM). It is for any MEM
because (SUBREG (MEM...)) is guaranteed to cause the MEM to be
reloaded. If not for that, MEM's would very rarely be safe.
Reject MODEs bigger than a word, because we might not be able
to reference a two-register group starting with an arbitrary register
(and currently gen_lowpart might crash for a SUBREG). */
if (GET_MODE_SIZE (GET_MODE (XEXP (x, 0))) > UNITS_PER_WORD)
return x;
len = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)));
/* If the inner object has VOIDmode (the only way this can happen
is if it is a ASM_OPERANDS), we can't do anything since we don't
know how much masking to do. */
if (len == 0)
return x;
break;
case ZERO_EXTRACT:
unsignedp = 1;
case SIGN_EXTRACT:
/* If the operand is a CLOBBER, just return it. */
if (GET_CODE (XEXP (x, 0)) == CLOBBER)
return XEXP (x, 0);
if (GET_CODE (XEXP (x, 1)) != CONST_INT
|| GET_CODE (XEXP (x, 2)) != CONST_INT
|| GET_MODE (XEXP (x, 0)) == VOIDmode)
return x;
len = INTVAL (XEXP (x, 1));
pos = INTVAL (XEXP (x, 2));
/* If this goes outside the object being extracted, replace the object
with a (use (mem ...)) construct that only combine understands
and is used only for this purpose. */
if (len + pos > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))
SUBST (XEXP (x, 0), gen_rtx_USE (GET_MODE (x), XEXP (x, 0)));
if (BITS_BIG_ENDIAN)
pos = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - len - pos;
break;
default:
return x;
}
/* We can optimize some special cases of ZERO_EXTEND. */
if (GET_CODE (x) == ZERO_EXTEND)
{
/* (zero_extend:DI (truncate:SI foo:DI)) is just foo:DI if we
know that the last value didn't have any inappropriate bits
set. */
if (GET_CODE (XEXP (x, 0)) == TRUNCATE
&& GET_MODE (XEXP (XEXP (x, 0), 0)) == GET_MODE (x)
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (XEXP (XEXP (x, 0), 0), GET_MODE (x))
& ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
return XEXP (XEXP (x, 0), 0);
/* Likewise for (zero_extend:DI (subreg:SI foo:DI 0)). */
if (GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_MODE (SUBREG_REG (XEXP (x, 0))) == GET_MODE (x)
&& subreg_lowpart_p (XEXP (x, 0))
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (SUBREG_REG (XEXP (x, 0)), GET_MODE (x))
& ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
return SUBREG_REG (XEXP (x, 0));
/* (zero_extend:DI (truncate:SI foo:DI)) is just foo:DI when foo
is a comparison and STORE_FLAG_VALUE permits. This is like
the first case, but it works even when GET_MODE (x) is larger
than HOST_WIDE_INT. */
if (GET_CODE (XEXP (x, 0)) == TRUNCATE
&& GET_MODE (XEXP (XEXP (x, 0), 0)) == GET_MODE (x)
&& GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) == '<'
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
<= HOST_BITS_PER_WIDE_INT)
&& ((HOST_WIDE_INT) STORE_FLAG_VALUE
& ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
return XEXP (XEXP (x, 0), 0);
/* Likewise for (zero_extend:DI (subreg:SI foo:DI 0)). */
if (GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_MODE (SUBREG_REG (XEXP (x, 0))) == GET_MODE (x)
&& subreg_lowpart_p (XEXP (x, 0))
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == '<'
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
<= HOST_BITS_PER_WIDE_INT)
&& ((HOST_WIDE_INT) STORE_FLAG_VALUE
& ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
return SUBREG_REG (XEXP (x, 0));
/* If sign extension is cheaper than zero extension, then use it
if we know that no extraneous bits are set, and that the high
bit is not set. */
if (flag_expensive_optimizations
&& ((GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
&& ((nonzero_bits (XEXP (x, 0), GET_MODE (x))
& ~ (((unsigned HOST_WIDE_INT)
GET_MODE_MASK (GET_MODE (XEXP (x, 0))))
>> 1))
== 0))
|| (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
<= HOST_BITS_PER_WIDE_INT)
&& (((HOST_WIDE_INT) STORE_FLAG_VALUE
& ~ (((unsigned HOST_WIDE_INT)
GET_MODE_MASK (GET_MODE (XEXP (x, 0))))
>> 1))
== 0))))
{
rtx temp = gen_rtx_SIGN_EXTEND (GET_MODE (x), XEXP (x, 0));
if (rtx_cost (temp, SET) < rtx_cost (x, SET))
return expand_compound_operation (temp);
}
}
/* If we reach here, we want to return a pair of shifts. The inner
shift is a left shift of BITSIZE - POS - LEN bits. The outer
shift is a right shift of BITSIZE - LEN bits. It is arithmetic or
logical depending on the value of UNSIGNEDP.
If this was a ZERO_EXTEND or ZERO_EXTRACT, this pair of shifts will be
converted into an AND of a shift.
We must check for the case where the left shift would have a negative
count. This can happen in a case like (x >> 31) & 255 on machines
that can't shift by a constant. On those machines, we would first
combine the shift with the AND to produce a variable-position
extraction. Then the constant of 31 would be substituted in to produce
a such a position. */
modewidth = GET_MODE_BITSIZE (GET_MODE (x));
if (modewidth >= pos - len)
tem = simplify_shift_const (NULL_RTX, unsignedp ? LSHIFTRT : ASHIFTRT,
GET_MODE (x),
simplify_shift_const (NULL_RTX, ASHIFT,
GET_MODE (x),
XEXP (x, 0),
modewidth - pos - len),
modewidth - len);
else if (unsignedp && len < HOST_BITS_PER_WIDE_INT)
tem = simplify_and_const_int (NULL_RTX, GET_MODE (x),
simplify_shift_const (NULL_RTX, LSHIFTRT,
GET_MODE (x),
XEXP (x, 0), pos),
((HOST_WIDE_INT) 1 << len) - 1);
else
/* Any other cases we can't handle. */
return x;
/* If we couldn't do this for some reason, return the original
expression. */
if (GET_CODE (tem) == CLOBBER)
return x;
return tem;
}
/* X is a SET which contains an assignment of one object into
a part of another (such as a bit-field assignment, STRICT_LOW_PART,
or certain SUBREGS). If possible, convert it into a series of
logical operations.
We half-heartedly support variable positions, but do not at all
support variable lengths. */
static rtx
expand_field_assignment (x)
rtx x;
{
rtx inner;
rtx pos; /* Always counts from low bit. */
int len;
rtx mask;
enum machine_mode compute_mode;
/* Loop until we find something we can't simplify. */
while (1)
{
if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
&& GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG)
{
inner = SUBREG_REG (XEXP (SET_DEST (x), 0));
len = GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)));
pos = GEN_INT (BITS_PER_WORD * SUBREG_WORD (XEXP (SET_DEST (x), 0)));
}
else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
&& GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT)
{
inner = XEXP (SET_DEST (x), 0);
len = INTVAL (XEXP (SET_DEST (x), 1));
pos = XEXP (SET_DEST (x), 2);
/* If the position is constant and spans the width of INNER,
surround INNER with a USE to indicate this. */
if (GET_CODE (pos) == CONST_INT
&& INTVAL (pos) + len > GET_MODE_BITSIZE (GET_MODE (inner)))
inner = gen_rtx_USE (GET_MODE (SET_DEST (x)), inner);
if (BITS_BIG_ENDIAN)
{
if (GET_CODE (pos) == CONST_INT)
pos = GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner)) - len
- INTVAL (pos));
else if (GET_CODE (pos) == MINUS
&& GET_CODE (XEXP (pos, 1)) == CONST_INT
&& (INTVAL (XEXP (pos, 1))
== GET_MODE_BITSIZE (GET_MODE (inner)) - len))
/* If position is ADJUST - X, new position is X. */
pos = XEXP (pos, 0);
else
pos = gen_binary (MINUS, GET_MODE (pos),
GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner))
- len),
pos);
}
}
/* A SUBREG between two modes that occupy the same numbers of words
can be done by moving the SUBREG to the source. */
else if (GET_CODE (SET_DEST (x)) == SUBREG
&& (((GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
== ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x))))
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))
{
x = gen_rtx_SET (VOIDmode, SUBREG_REG (SET_DEST (x)),
gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_DEST (x))),
SET_SRC (x)));
continue;
}
else
break;
while (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner))
inner = SUBREG_REG (inner);
compute_mode = GET_MODE (inner);
/* Don't attempt bitwise arithmetic on non-integral modes. */
if (! INTEGRAL_MODE_P (compute_mode))
{
enum machine_mode imode;
/* Something is probably seriously wrong if this matches. */
if (! FLOAT_MODE_P (compute_mode))
break;
/* Try to find an integral mode to pun with. */
imode = mode_for_size (GET_MODE_BITSIZE (compute_mode), MODE_INT, 0);
if (imode == BLKmode)
break;
compute_mode = imode;
inner = gen_lowpart_for_combine (imode, inner);
}
/* Compute a mask of LEN bits, if we can do this on the host machine. */
if (len < HOST_BITS_PER_WIDE_INT)
mask = GEN_INT (((HOST_WIDE_INT) 1 << len) - 1);
else
break;
/* Now compute the equivalent expression. Make a copy of INNER
for the SET_DEST in case it is a MEM into which we will substitute;
we don't want shared RTL in that case. */
x = gen_rtx_SET (VOIDmode, copy_rtx (inner),
gen_binary (IOR, compute_mode,
gen_binary (AND, compute_mode,
gen_unary (NOT, compute_mode,
compute_mode,
gen_binary (ASHIFT,
compute_mode,
mask, pos)),
inner),
gen_binary (ASHIFT, compute_mode,
gen_binary (AND, compute_mode,
gen_lowpart_for_combine
(compute_mode,
SET_SRC (x)),
mask),
pos)));
}
return x;
}
/* Return an RTX for a reference to LEN bits of INNER. If POS_RTX is nonzero,
it is an RTX that represents a variable starting position; otherwise,
POS is the (constant) starting bit position (counted from the LSB).
INNER may be a USE. This will occur when we started with a bitfield
that went outside the boundary of the object in memory, which is
allowed on most machines. To isolate this case, we produce a USE
whose mode is wide enough and surround the MEM with it. The only
code that understands the USE is this routine. If it is not removed,
it will cause the resulting insn not to match.
UNSIGNEDP is non-zero for an unsigned reference and zero for a
signed reference.
IN_DEST is non-zero if this is a reference in the destination of a
SET. This is used when a ZERO_ or SIGN_EXTRACT isn't needed. If non-zero,
a STRICT_LOW_PART will be used, if zero, ZERO_EXTEND or SIGN_EXTEND will
be used.
IN_COMPARE is non-zero if we are in a COMPARE. This means that a
ZERO_EXTRACT should be built even for bits starting at bit 0.
MODE is the desired mode of the result (if IN_DEST == 0).
The result is an RTX for the extraction or NULL_RTX if the target
can't handle it. */
static rtx
make_extraction (mode, inner, pos, pos_rtx, len,
unsignedp, in_dest, in_compare)
enum machine_mode mode;
rtx inner;
int pos;
rtx pos_rtx;
int len;
int unsignedp;
int in_dest, in_compare;
{
/* This mode describes the size of the storage area
to fetch the overall value from. Within that, we
ignore the POS lowest bits, etc. */
enum machine_mode is_mode = GET_MODE (inner);
enum machine_mode inner_mode;
enum machine_mode wanted_inner_mode = byte_mode;
enum machine_mode wanted_inner_reg_mode = word_mode;
enum machine_mode pos_mode = word_mode;
enum machine_mode extraction_mode = word_mode;
enum machine_mode tmode = mode_for_size (len, MODE_INT, 1);
int spans_byte = 0;
rtx new = 0;
rtx orig_pos_rtx = pos_rtx;
int orig_pos;
/* Get some information about INNER and get the innermost object. */
if (GET_CODE (inner) == USE)
/* (use:SI (mem:QI foo)) stands for (mem:SI foo). */
/* We don't need to adjust the position because we set up the USE
to pretend that it was a full-word object. */
spans_byte = 1, inner = XEXP (inner, 0);
else if (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner))
{
/* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...),
consider just the QI as the memory to extract from.
The subreg adds or removes high bits; its mode is
irrelevant to the meaning of this extraction,
since POS and LEN count from the lsb. */
if (GET_CODE (SUBREG_REG (inner)) == MEM)
is_mode = GET_MODE (SUBREG_REG (inner));
inner = SUBREG_REG (inner);
}
inner_mode = GET_MODE (inner);
if (pos_rtx && GET_CODE (pos_rtx) == CONST_INT)
pos = INTVAL (pos_rtx), pos_rtx = 0;
/* See if this can be done without an extraction. We never can if the
width of the field is not the same as that of some integer mode. For
registers, we can only avoid the extraction if the position is at the
low-order bit and this is either not in the destination or we have the
appropriate STRICT_LOW_PART operation available.
For MEM, we can avoid an extract if the field starts on an appropriate
boundary and we can change the mode of the memory reference. However,
we cannot directly access the MEM if we have a USE and the underlying
MEM is not TMODE. This combination means that MEM was being used in a
context where bits outside its mode were being referenced; that is only
valid in bit-field insns. */
if (tmode != BLKmode
&& ! (spans_byte && inner_mode != tmode)
&& ((pos_rtx == 0 && (pos % BITS_PER_WORD) == 0
&& GET_CODE (inner) != MEM
&& (! in_dest
|| (GET_CODE (inner) == REG
&& (movstrict_optab->handlers[(int) tmode].insn_code
!= CODE_FOR_nothing))))
|| (GET_CODE (inner) == MEM && pos_rtx == 0
&& (pos
% (STRICT_ALIGNMENT ? GET_MODE_ALIGNMENT (tmode)
: BITS_PER_UNIT)) == 0
/* We can't do this if we are widening INNER_MODE (it
may not be aligned, for one thing). */
&& GET_MODE_BITSIZE (inner_mode) >= GET_MODE_BITSIZE (tmode)
&& (inner_mode == tmode
|| (! mode_dependent_address_p (XEXP (inner, 0))
&& ! MEM_VOLATILE_P (inner))))))
{
/* If INNER is a MEM, make a new MEM that encompasses just the desired
field. If the original and current mode are the same, we need not
adjust the offset. Otherwise, we do if bytes big endian.
If INNER is not a MEM, get a piece consisting of just the field
of interest (in this case POS % BITS_PER_WORD must be 0). */
if (GET_CODE (inner) == MEM)
{
int offset;
/* POS counts from lsb, but make OFFSET count in memory order. */
if (BYTES_BIG_ENDIAN)
offset = (GET_MODE_BITSIZE (is_mode) - len - pos) / BITS_PER_UNIT;
else
offset = pos / BITS_PER_UNIT;
new = gen_rtx_MEM (tmode, plus_constant (XEXP (inner, 0), offset));
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (inner);
MEM_COPY_ATTRIBUTES (new, inner);
}
else if (GET_CODE (inner) == REG)
{
/* We can't call gen_lowpart_for_combine here since we always want
a SUBREG and it would sometimes return a new hard register. */
if (tmode != inner_mode)
new = gen_rtx_SUBREG (tmode, inner,
(WORDS_BIG_ENDIAN
&& GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD
? (((GET_MODE_SIZE (inner_mode)
- GET_MODE_SIZE (tmode))
/ UNITS_PER_WORD)
- pos / BITS_PER_WORD)
: pos / BITS_PER_WORD));
else
new = inner;
}
else
new = force_to_mode (inner, tmode,
len >= HOST_BITS_PER_WIDE_INT
? GET_MODE_MASK (tmode)
: ((HOST_WIDE_INT) 1 << len) - 1,
NULL_RTX, 0);
/* If this extraction is going into the destination of a SET,
make a STRICT_LOW_PART unless we made a MEM. */
if (in_dest)
return (GET_CODE (new) == MEM ? new
: (GET_CODE (new) != SUBREG
? gen_rtx_CLOBBER (tmode, const0_rtx)
: gen_rtx_combine (STRICT_LOW_PART, VOIDmode, new)));
/* Otherwise, sign- or zero-extend unless we already are in the
proper mode. */
return (mode == tmode ? new
: gen_rtx_combine (unsignedp ? ZERO_EXTEND : SIGN_EXTEND,
mode, new));
}
/* Unless this is a COMPARE or we have a funny memory reference,
don't do anything with zero-extending field extracts starting at
the low-order bit since they are simple AND operations. */
if (pos_rtx == 0 && pos == 0 && ! in_dest
&& ! in_compare && ! spans_byte && unsignedp)
return 0;
/* Unless we are allowed to span bytes, reject this if we would be
spanning bytes or if the position is not a constant and the length
is not 1. In all other cases, we would only be going outside
out object in cases when an original shift would have been
undefined. */
if (! spans_byte
&& ((pos_rtx == 0 && pos + len > GET_MODE_BITSIZE (is_mode))
|| (pos_rtx != 0 && len != 1)))
return 0;
/* Get the mode to use should INNER not be a MEM, the mode for the position,
and the mode for the result. */
#ifdef HAVE_insv
if (in_dest)
{
wanted_inner_reg_mode
= (insn_operand_mode[(int) CODE_FOR_insv][0] == VOIDmode
? word_mode
: insn_operand_mode[(int) CODE_FOR_insv][0]);
pos_mode = (insn_operand_mode[(int) CODE_FOR_insv][2] == VOIDmode
? word_mode : insn_operand_mode[(int) CODE_FOR_insv][2]);
extraction_mode = (insn_operand_mode[(int) CODE_FOR_insv][3] == VOIDmode
? word_mode
: insn_operand_mode[(int) CODE_FOR_insv][3]);
}
#endif
#ifdef HAVE_extzv
if (! in_dest && unsignedp)
{
wanted_inner_reg_mode
= (insn_operand_mode[(int) CODE_FOR_extzv][1] == VOIDmode
? word_mode
: insn_operand_mode[(int) CODE_FOR_extzv][1]);
pos_mode = (insn_operand_mode[(int) CODE_FOR_extzv][3] == VOIDmode
? word_mode : insn_operand_mode[(int) CODE_FOR_extzv][3]);
extraction_mode = (insn_operand_mode[(int) CODE_FOR_extzv][0] == VOIDmode
? word_mode
: insn_operand_mode[(int) CODE_FOR_extzv][0]);
}
#endif
#ifdef HAVE_extv
if (! in_dest && ! unsignedp)
{
wanted_inner_reg_mode
= (insn_operand_mode[(int) CODE_FOR_extv][1] == VOIDmode
? word_mode
: insn_operand_mode[(int) CODE_FOR_extv][1]);
pos_mode = (insn_operand_mode[(int) CODE_FOR_extv][3] == VOIDmode
? word_mode : insn_operand_mode[(int) CODE_FOR_extv][3]);
extraction_mode = (insn_operand_mode[(int) CODE_FOR_extv][0] == VOIDmode
? word_mode
: insn_operand_mode[(int) CODE_FOR_extv][0]);
}
#endif
/* Never narrow an object, since that might not be safe. */
if (mode != VOIDmode
&& GET_MODE_SIZE (extraction_mode) < GET_MODE_SIZE (mode))
extraction_mode = mode;
if (pos_rtx && GET_MODE (pos_rtx) != VOIDmode
&& GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx)))
pos_mode = GET_MODE (pos_rtx);
/* If this is not from memory, the desired mode is wanted_inner_reg_mode;
if we have to change the mode of memory and cannot, the desired mode is
EXTRACTION_MODE. */
if (GET_CODE (inner) != MEM)
wanted_inner_mode = wanted_inner_reg_mode;
else if (inner_mode != wanted_inner_mode
&& (mode_dependent_address_p (XEXP (inner, 0))
|| MEM_VOLATILE_P (inner)))
wanted_inner_mode = extraction_mode;
orig_pos = pos;
if (BITS_BIG_ENDIAN)
{
/* POS is passed as if BITS_BIG_ENDIAN == 0, so we need to convert it to
BITS_BIG_ENDIAN style. If position is constant, compute new
position. Otherwise, build subtraction.
Note that POS is relative to the mode of the original argument.
If it's a MEM we need to recompute POS relative to that.
However, if we're extracting from (or inserting into) a register,
we want to recompute POS relative to wanted_inner_mode. */
int width = (GET_CODE (inner) == MEM
? GET_MODE_BITSIZE (is_mode)
: GET_MODE_BITSIZE (wanted_inner_mode));
if (pos_rtx == 0)
pos = width - len - pos;
else
pos_rtx
= gen_rtx_combine (MINUS, GET_MODE (pos_rtx),
GEN_INT (width - len), pos_rtx);
/* POS may be less than 0 now, but we check for that below.
Note that it can only be less than 0 if GET_CODE (inner) != MEM. */
}
/* If INNER has a wider mode, make it smaller. If this is a constant
extract, try to adjust the byte to point to the byte containing
the value. */
if (wanted_inner_mode != VOIDmode
&& GET_MODE_SIZE (wanted_inner_mode) < GET_MODE_SIZE (is_mode)
&& ((GET_CODE (inner) == MEM
&& (inner_mode == wanted_inner_mode
|| (! mode_dependent_address_p (XEXP (inner, 0))
&& ! MEM_VOLATILE_P (inner))))))
{
int offset = 0;
/* The computations below will be correct if the machine is big
endian in both bits and bytes or little endian in bits and bytes.
If it is mixed, we must adjust. */
/* If bytes are big endian and we had a paradoxical SUBREG, we must
adjust OFFSET to compensate. */
if (BYTES_BIG_ENDIAN
&& ! spans_byte
&& GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode))
offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode);
/* If this is a constant position, we can move to the desired byte. */
if (pos_rtx == 0)
{
offset += pos / BITS_PER_UNIT;
pos %= GET_MODE_BITSIZE (wanted_inner_mode);
}
if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
&& ! spans_byte
&& is_mode != wanted_inner_mode)
offset = (GET_MODE_SIZE (is_mode)
- GET_MODE_SIZE (wanted_inner_mode) - offset);
if (offset != 0 || inner_mode != wanted_inner_mode)
{
rtx newmem = gen_rtx_MEM (wanted_inner_mode,
plus_constant (XEXP (inner, 0), offset));
RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (inner);
MEM_COPY_ATTRIBUTES (newmem, inner);
inner = newmem;
}
}
/* If INNER is not memory, we can always get it into the proper mode. If we
are changing its mode, POS must be a constant and smaller than the size
of the new mode. */
else if (GET_CODE (inner) != MEM)
{
if (GET_MODE (inner) != wanted_inner_mode
&& (pos_rtx != 0
|| orig_pos + len > GET_MODE_BITSIZE (wanted_inner_mode)))
return 0;
inner = force_to_mode (inner, wanted_inner_mode,
pos_rtx
|| len + orig_pos >= HOST_BITS_PER_WIDE_INT
? GET_MODE_MASK (wanted_inner_mode)
: (((HOST_WIDE_INT) 1 << len) - 1) << orig_pos,
NULL_RTX, 0);
}
/* Adjust mode of POS_RTX, if needed. If we want a wider mode, we
have to zero extend. Otherwise, we can just use a SUBREG. */
if (pos_rtx != 0
&& GET_MODE_SIZE (pos_mode) > GET_MODE_SIZE (GET_MODE (pos_rtx)))
pos_rtx = gen_rtx_combine (ZERO_EXTEND, pos_mode, pos_rtx);
else if (pos_rtx != 0
&& GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx)))
pos_rtx = gen_lowpart_for_combine (pos_mode, pos_rtx);
/* Make POS_RTX unless we already have it and it is correct. If we don't
have a POS_RTX but we do have an ORIG_POS_RTX, the latter must
be a CONST_INT. */
if (pos_rtx == 0 && orig_pos_rtx != 0 && INTVAL (orig_pos_rtx) == pos)
pos_rtx = orig_pos_rtx;
else if (pos_rtx == 0)
pos_rtx = GEN_INT (pos);
/* Make the required operation. See if we can use existing rtx. */
new = gen_rtx_combine (unsignedp ? ZERO_EXTRACT : SIGN_EXTRACT,
extraction_mode, inner, GEN_INT (len), pos_rtx);
if (! in_dest)
new = gen_lowpart_for_combine (mode, new);
return new;
}
/* See if X contains an ASHIFT of COUNT or more bits that can be commuted
with any other operations in X. Return X without that shift if so. */
static rtx
extract_left_shift (x, count)
rtx x;
int count;
{
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
rtx tem;
switch (code)
{
case ASHIFT:
/* This is the shift itself. If it is wide enough, we will return
either the value being shifted if the shift count is equal to
COUNT or a shift for the difference. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= count)
return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0),
INTVAL (XEXP (x, 1)) - count);
break;
case NEG: case NOT:
if ((tem = extract_left_shift (XEXP (x, 0), count)) != 0)
return gen_unary (code, mode, mode, tem);
break;
case PLUS: case IOR: case XOR: case AND:
/* If we can safely shift this constant and we find the inner shift,
make a new operation. */
if (GET_CODE (XEXP (x,1)) == CONST_INT
&& (INTVAL (XEXP (x, 1)) & ((((HOST_WIDE_INT) 1 << count)) - 1)) == 0
&& (tem = extract_left_shift (XEXP (x, 0), count)) != 0)
return gen_binary (code, mode, tem,
GEN_INT (INTVAL (XEXP (x, 1)) >> count));
break;
default:
break;
}
return 0;
}
/* Look at the expression rooted at X. Look for expressions
equivalent to ZERO_EXTRACT, SIGN_EXTRACT, ZERO_EXTEND, SIGN_EXTEND.
Form these expressions.
Return the new rtx, usually just X.
Also, for machines like the Vax that don't have logical shift insns,
try to convert logical to arithmetic shift operations in cases where
they are equivalent. This undoes the canonicalizations to logical
shifts done elsewhere.
We try, as much as possible, to re-use rtl expressions to save memory.
IN_CODE says what kind of expression we are processing. Normally, it is
SET. In a memory address (inside a MEM, PLUS or minus, the latter two
being kludges), it is MEM. When processing the arguments of a comparison
or a COMPARE against zero, it is COMPARE. */
static rtx
make_compound_operation (x, in_code)
rtx x;
enum rtx_code in_code;
{
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
int mode_width = GET_MODE_BITSIZE (mode);
rtx rhs, lhs;
enum rtx_code next_code;
int i;
rtx new = 0;
rtx tem;
char *fmt;
/* Select the code to be used in recursive calls. Once we are inside an
address, we stay there. If we have a comparison, set to COMPARE,
but once inside, go back to our default of SET. */
next_code = (code == MEM || code == PLUS || code == MINUS ? MEM
: ((code == COMPARE || GET_RTX_CLASS (code) == '<')
&& XEXP (x, 1) == const0_rtx) ? COMPARE
: in_code == COMPARE ? SET : in_code);
/* Process depending on the code of this operation. If NEW is set
non-zero, it will be returned. */
switch (code)
{
case ASHIFT:
/* Convert shifts by constants into multiplications if inside
an address. */
if (in_code == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
&& INTVAL (XEXP (x, 1)) >= 0)
{
new = make_compound_operation (XEXP (x, 0), next_code);
new = gen_rtx_combine (MULT, mode, new,
GEN_INT ((HOST_WIDE_INT) 1
<< INTVAL (XEXP (x, 1))));
}
break;
case AND:
/* If the second operand is not a constant, we can't do anything
with it. */
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
break;
/* If the constant is a power of two minus one and the first operand
is a logical right shift, make an extraction. */
if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
&& (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
{
new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
new = make_extraction (mode, new, 0, XEXP (XEXP (x, 0), 1), i, 1,
0, in_code == COMPARE);
}
/* Same as previous, but for (subreg (lshiftrt ...)) in first op. */
else if (GET_CODE (XEXP (x, 0)) == SUBREG
&& subreg_lowpart_p (XEXP (x, 0))
&& GET_CODE (SUBREG_REG (XEXP (x, 0))) == LSHIFTRT
&& (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
{
new = make_compound_operation (XEXP (SUBREG_REG (XEXP (x, 0)), 0),
next_code);
new = make_extraction (GET_MODE (SUBREG_REG (XEXP (x, 0))), new, 0,
XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1,
0, in_code == COMPARE);
}
/* Same as previous, but for (xor/ior (lshiftrt...) (lshiftrt...)). */
else if ((GET_CODE (XEXP (x, 0)) == XOR
|| GET_CODE (XEXP (x, 0)) == IOR)
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == LSHIFTRT
&& (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
{
/* Apply the distributive law, and then try to make extractions. */
new = gen_rtx_combine (GET_CODE (XEXP (x, 0)), mode,
gen_rtx_AND (mode, XEXP (XEXP (x, 0), 0),
XEXP (x, 1)),
gen_rtx_AND (mode, XEXP (XEXP (x, 0), 1),
XEXP (x, 1)));
new = make_compound_operation (new, in_code);
}
/* If we are have (and (rotate X C) M) and C is larger than the number
of bits in M, this is an extraction. */
else if (GET_CODE (XEXP (x, 0)) == ROTATE
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0
&& i <= INTVAL (XEXP (XEXP (x, 0), 1)))
{
new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
new = make_extraction (mode, new,
(GET_MODE_BITSIZE (mode)
- INTVAL (XEXP (XEXP (x, 0), 1))),
NULL_RTX, i, 1, 0, in_code == COMPARE);
}
/* On machines without logical shifts, if the operand of the AND is
a logical shift and our mask turns off all the propagated sign
bits, we can replace the logical shift with an arithmetic shift. */
else if (ashr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
&& (lshr_optab->handlers[(int) mode].insn_code
== CODE_FOR_nothing)
&& GET_CODE (XEXP (x, 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
&& INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT)
{
unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
mask >>= INTVAL (XEXP (XEXP (x, 0), 1));
if ((INTVAL (XEXP (x, 1)) & ~mask) == 0)
SUBST (XEXP (x, 0),
gen_rtx_combine (ASHIFTRT, mode,
make_compound_operation (XEXP (XEXP (x, 0), 0),
next_code),
XEXP (XEXP (x, 0), 1)));
}
/* If the constant is one less than a power of two, this might be
representable by an extraction even if no shift is present.
If it doesn't end up being a ZERO_EXTEND, we will ignore it unless
we are in a COMPARE. */
else if ((i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
new = make_extraction (mode,
make_compound_operation (XEXP (x, 0),
next_code),
0, NULL_RTX, i, 1, 0, in_code == COMPARE);
/* If we are in a comparison and this is an AND with a power of two,
convert this into the appropriate bit extract. */
else if (in_code == COMPARE
&& (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0)
new = make_extraction (mode,
make_compound_operation (XEXP (x, 0),
next_code),
i, NULL_RTX, 1, 1, 0, 1);
break;
case LSHIFTRT:
/* If the sign bit is known to be zero, replace this with an
arithmetic shift. */
if (ashr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing
&& lshr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0)
{
new = gen_rtx_combine (ASHIFTRT, mode,
make_compound_operation (XEXP (x, 0),
next_code),
XEXP (x, 1));
break;
}
/* ... fall through ... */
case ASHIFTRT:
lhs = XEXP (x, 0);
rhs = XEXP (x, 1);
/* If we have (ashiftrt (ashift foo C1) C2) with C2 >= C1,
this is a SIGN_EXTRACT. */
if (GET_CODE (rhs) == CONST_INT
&& GET_CODE (lhs) == ASHIFT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT
&& INTVAL (rhs) >= INTVAL (XEXP (lhs, 1)))
{
new = make_compound_operation (XEXP (lhs, 0), next_code);
new = make_extraction (mode, new,
INTVAL (rhs) - INTVAL (XEXP (lhs, 1)),
NULL_RTX, mode_width - INTVAL (rhs),
code == LSHIFTRT, 0, in_code == COMPARE);
}
/* See if we have operations between an ASHIFTRT and an ASHIFT.
If so, try to merge the shifts into a SIGN_EXTEND. We could
also do this for some cases of SIGN_EXTRACT, but it doesn't
seem worth the effort; the case checked for occurs on Alpha. */
if (GET_RTX_CLASS (GET_CODE (lhs)) != 'o'
&& ! (GET_CODE (lhs) == SUBREG
&& (GET_RTX_CLASS (GET_CODE (SUBREG_REG (lhs))) == 'o'))
&& GET_CODE (rhs) == CONST_INT
&& INTVAL (rhs) < HOST_BITS_PER_WIDE_INT
&& (new = extract_left_shift (lhs, INTVAL (rhs))) != 0)
new = make_extraction (mode, make_compound_operation (new, next_code),
0, NULL_RTX, mode_width - INTVAL (rhs),
code == LSHIFTRT, 0, in_code == COMPARE);
break;
case SUBREG:
/* Call ourselves recursively on the inner expression. If we are
narrowing the object and it has a different RTL code from
what it originally did, do this SUBREG as a force_to_mode. */
tem = make_compound_operation (SUBREG_REG (x), in_code);
if (GET_CODE (tem) != GET_CODE (SUBREG_REG (x))
&& GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (tem))
&& subreg_lowpart_p (x))
{
rtx newer = force_to_mode (tem, mode,
GET_MODE_MASK (mode), NULL_RTX, 0);
/* If we have something other than a SUBREG, we might have
done an expansion, so rerun outselves. */
if (GET_CODE (newer) != SUBREG)
newer = make_compound_operation (newer, in_code);
return newer;
}
/* If this is a paradoxical subreg, and the new code is a sign or
zero extension, omit the subreg and widen the extension. If it
is a regular subreg, we can still get rid of the subreg by not
widening so much, or in fact removing the extension entirely. */
if ((GET_CODE (tem) == SIGN_EXTEND
|| GET_CODE (tem) == ZERO_EXTEND)
&& subreg_lowpart_p (x))
{
if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (tem))
|| (GET_MODE_SIZE (mode) >
GET_MODE_SIZE (GET_MODE (XEXP (tem, 0)))))
tem = gen_rtx_combine (GET_CODE (tem), mode, XEXP (tem, 0));
else
tem = gen_lowpart_for_combine (mode, XEXP (tem, 0));
return tem;
}
break;
default:
break;
}
if (new)
{
x = gen_lowpart_for_combine (mode, new);
code = GET_CODE (x);
}
/* Now recursively process each operand of this operation. */
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < GET_RTX_LENGTH (code); i++)
if (fmt[i] == 'e')
{
new = make_compound_operation (XEXP (x, i), next_code);
SUBST (XEXP (x, i), new);
}
return x;
}
/* Given M see if it is a value that would select a field of bits
within an item, but not the entire word. Return -1 if not.
Otherwise, return the starting position of the field, where 0 is the
low-order bit.
*PLEN is set to the length of the field. */
static int
get_pos_from_mask (m, plen)
unsigned HOST_WIDE_INT m;
int *plen;
{
/* Get the bit number of the first 1 bit from the right, -1 if none. */
int pos = exact_log2 (m & - m);
if (pos < 0)
return -1;
/* Now shift off the low-order zero bits and see if we have a power of
two minus 1. */
*plen = exact_log2 ((m >> pos) + 1);
if (*plen <= 0)
return -1;
return pos;
}
/* See if X can be simplified knowing that we will only refer to it in
MODE and will only refer to those bits that are nonzero in MASK.
If other bits are being computed or if masking operations are done
that select a superset of the bits in MASK, they can sometimes be
ignored.
Return a possibly simplified expression, but always convert X to
MODE. If X is a CONST_INT, AND the CONST_INT with MASK.
Also, if REG is non-zero and X is a register equal in value to REG,
replace X with REG.
If JUST_SELECT is nonzero, don't optimize by noticing that bits in MASK
are all off in X. This is used when X will be complemented, by either
NOT, NEG, or XOR. */
static rtx
force_to_mode (x, mode, mask, reg, just_select)
rtx x;
enum machine_mode mode;
unsigned HOST_WIDE_INT mask;
rtx reg;
int just_select;
{
enum rtx_code code = GET_CODE (x);
int next_select = just_select || code == XOR || code == NOT || code == NEG;
enum machine_mode op_mode;
unsigned HOST_WIDE_INT fuller_mask, nonzero;
rtx op0, op1, temp;
/* If this is a CALL or ASM_OPERANDS, don't do anything. Some of the
code below will do the wrong thing since the mode of such an
expression is VOIDmode.
Also do nothing if X is a CLOBBER; this can happen if X was
the return value from a call to gen_lowpart_for_combine. */
if (code == CALL || code == ASM_OPERANDS || code == CLOBBER)
return x;
/* We want to perform the operation is its present mode unless we know
that the operation is valid in MODE, in which case we do the operation
in MODE. */
op_mode = ((GET_MODE_CLASS (mode) == GET_MODE_CLASS (GET_MODE (x))
&& code_to_optab[(int) code] != 0
&& (code_to_optab[(int) code]->handlers[(int) mode].insn_code
!= CODE_FOR_nothing))
? mode : GET_MODE (x));
/* It is not valid to do a right-shift in a narrower mode
than the one it came in with. */
if ((code == LSHIFTRT || code == ASHIFTRT)
&& GET_MODE_BITSIZE (mode) < GET_MODE_BITSIZE (GET_MODE (x)))
op_mode = GET_MODE (x);
/* Truncate MASK to fit OP_MODE. */
if (op_mode)
mask &= GET_MODE_MASK (op_mode);
/* When we have an arithmetic operation, or a shift whose count we
do not know, we need to assume that all bit the up to the highest-order
bit in MASK will be needed. This is how we form such a mask. */
if (op_mode)
fuller_mask = (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT
? GET_MODE_MASK (op_mode)
: ((HOST_WIDE_INT) 1 << (floor_log2 (mask) + 1)) - 1);
else
fuller_mask = ~ (HOST_WIDE_INT) 0;
/* Determine what bits of X are guaranteed to be (non)zero. */
nonzero = nonzero_bits (x, mode);
/* If none of the bits in X are needed, return a zero. */
if (! just_select && (nonzero & mask) == 0)
return const0_rtx;
/* If X is a CONST_INT, return a new one. Do this here since the
test below will fail. */
if (GET_CODE (x) == CONST_INT)
{
HOST_WIDE_INT cval = INTVAL (x) & mask;
int width = GET_MODE_BITSIZE (mode);
/* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
number, sign extend it. */
if (width > 0 && width < HOST_BITS_PER_WIDE_INT
&& (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
cval |= (HOST_WIDE_INT) -1 << width;
return GEN_INT (cval);
}
/* If X is narrower than MODE and we want all the bits in X's mode, just
get X in the proper mode. */
if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)
&& (GET_MODE_MASK (GET_MODE (x)) & ~ mask) == 0)
return gen_lowpart_for_combine (mode, x);
/* If we aren't changing the mode, X is not a SUBREG, and all zero bits in
MASK are already known to be zero in X, we need not do anything. */
if (GET_MODE (x) == mode && code != SUBREG && (~ mask & nonzero) == 0)
return x;
switch (code)
{
case CLOBBER:
/* If X is a (clobber (const_int)), return it since we know we are
generating something that won't match. */
return x;
case USE:
/* X is a (use (mem ..)) that was made from a bit-field extraction that
spanned the boundary of the MEM. If we are now masking so it is
within that boundary, we don't need the USE any more. */
if (! BITS_BIG_ENDIAN
&& (mask & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
break;
case SIGN_EXTEND:
case ZERO_EXTEND:
case ZERO_EXTRACT:
case SIGN_EXTRACT:
x = expand_compound_operation (x);
if (GET_CODE (x) != code)
return force_to_mode (x, mode, mask, reg, next_select);
break;
case REG:
if (reg != 0 && (rtx_equal_p (get_last_value (reg), x)
|| rtx_equal_p (reg, get_last_value (x))))
x = reg;
break;
case SUBREG:
if (subreg_lowpart_p (x)
/* We can ignore the effect of this SUBREG if it narrows the mode or
if the constant masks to zero all the bits the mode doesn't
have. */
&& ((GET_MODE_SIZE (GET_MODE (x))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
|| (0 == (mask
& GET_MODE_MASK (GET_MODE (x))
& ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))))))
return force_to_mode (SUBREG_REG (x), mode, mask, reg, next_select);
break;
case AND:
/* If this is an AND with a constant, convert it into an AND
whose constant is the AND of that constant with MASK. If it
remains an AND of MASK, delete it since it is redundant. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{
x = simplify_and_const_int (x, op_mode, XEXP (x, 0),
mask & INTVAL (XEXP (x, 1)));
/* If X is still an AND, see if it is an AND with a mask that
is just some low-order bits. If so, and it is MASK, we don't
need it. */
if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (XEXP (x, 1)) == mask)
x = XEXP (x, 0);
/* If it remains an AND, try making another AND with the bits
in the mode mask that aren't in MASK turned on. If the
constant in the AND is wide enough, this might make a
cheaper constant. */
if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
&& GET_MODE_MASK (GET_MODE (x)) != mask
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
{
HOST_WIDE_INT cval = (INTVAL (XEXP (x, 1))
| (GET_MODE_MASK (GET_MODE (x)) & ~ mask));
int width = GET_MODE_BITSIZE (GET_MODE (x));
rtx y;
/* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
number, sign extend it. */
if (width > 0 && width < HOST_BITS_PER_WIDE_INT
&& (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
cval |= (HOST_WIDE_INT) -1 << width;
y = gen_binary (AND, GET_MODE (x), XEXP (x, 0), GEN_INT (cval));
if (rtx_cost (y, SET) < rtx_cost (x, SET))
x = y;
}
break;
}
goto binop;
case PLUS:
/* In (and (plus FOO C1) M), if M is a mask that just turns off
low-order bits (as in an alignment operation) and FOO is already
aligned to that boundary, mask C1 to that boundary as well.
This may eliminate that PLUS and, later, the AND. */
{
int width = GET_MODE_BITSIZE (mode);
unsigned HOST_WIDE_INT smask = mask;
/* If MODE is narrower than HOST_WIDE_INT and mask is a negative
number, sign extend it. */
if (width < HOST_BITS_PER_WIDE_INT
&& (smask & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
smask |= (HOST_WIDE_INT) -1 << width;
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& exact_log2 (- smask) >= 0)
{
#ifdef STACK_BIAS
if (STACK_BIAS
&& (XEXP (x, 0) == stack_pointer_rtx
|| XEXP (x, 0) == frame_pointer_rtx))
{
int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
unsigned HOST_WIDE_INT sp_mask = GET_MODE_MASK (mode);
sp_mask &= ~ (sp_alignment - 1);
if ((sp_mask & ~ smask) == 0
&& ((INTVAL (XEXP (x, 1)) - STACK_BIAS) & ~ smask) != 0)
return force_to_mode (plus_constant (XEXP (x, 0),
((INTVAL (XEXP (x, 1)) -
STACK_BIAS) & smask)
+ STACK_BIAS),
mode, smask, reg, next_select);
}
#endif
if ((nonzero_bits (XEXP (x, 0), mode) & ~ smask) == 0
&& (INTVAL (XEXP (x, 1)) & ~ smask) != 0)
return force_to_mode (plus_constant (XEXP (x, 0),
(INTVAL (XEXP (x, 1))
& smask)),
mode, smask, reg, next_select);
}
}
/* ... fall through ... */
case MINUS:
case MULT:
/* For PLUS, MINUS and MULT, we need any bits less significant than the
most significant bit in MASK since carries from those bits will
affect the bits we are interested in. */
mask = fuller_mask;
goto binop;
case IOR:
case XOR:
/* If X is (ior (lshiftrt FOO C1) C2), try to commute the IOR and
LSHIFTRT so we end up with an (and (lshiftrt (ior ...) ...) ...)
operation which may be a bitfield extraction. Ensure that the
constant we form is not wider than the mode of X. */
if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
&& INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& ((INTVAL (XEXP (XEXP (x, 0), 1))
+ floor_log2 (INTVAL (XEXP (x, 1))))
< GET_MODE_BITSIZE (GET_MODE (x)))
&& (INTVAL (XEXP (x, 1))
& ~ nonzero_bits (XEXP (x, 0), GET_MODE (x))) == 0)
{
temp = GEN_INT ((INTVAL (XEXP (x, 1)) & mask)
<< INTVAL (XEXP (XEXP (x, 0), 1)));
temp = gen_binary (GET_CODE (x), GET_MODE (x),
XEXP (XEXP (x, 0), 0), temp);
x = gen_binary (LSHIFTRT, GET_MODE (x), temp,
XEXP (XEXP (x, 0), 1));
return force_to_mode (x, mode, mask, reg, next_select);
}
binop:
/* For most binary operations, just propagate into the operation and
change the mode if we have an operation of that mode. */
op0 = gen_lowpart_for_combine (op_mode,
force_to_mode (XEXP (x, 0), mode, mask,
reg, next_select));
op1 = gen_lowpart_for_combine (op_mode,
force_to_mode (XEXP (x, 1), mode, mask,
reg, next_select));
/* If OP1 is a CONST_INT and X is an IOR or XOR, clear bits outside
MASK since OP1 might have been sign-extended but we never want
to turn on extra bits, since combine might have previously relied
on them being off. */
if (GET_CODE (op1) == CONST_INT && (code == IOR || code == XOR)
&& (INTVAL (op1) & mask) != 0)
op1 = GEN_INT (INTVAL (op1) & mask);
if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
x = gen_binary (code, op_mode, op0, op1);
break;
case ASHIFT:
/* For left shifts, do the same, but just for the first operand.
However, we cannot do anything with shifts where we cannot
guarantee that the counts are smaller than the size of the mode
because such a count will have a different meaning in a
wider mode. */
if (! (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode))
&& ! (GET_MODE (XEXP (x, 1)) != VOIDmode
&& (nonzero_bits (XEXP (x, 1), GET_MODE (XEXP (x, 1)))
< (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode))))
break;
/* If the shift count is a constant and we can do arithmetic in
the mode of the shift, refine which bits we need. Otherwise, use the
conservative form of the mask. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (op_mode)
&& GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
mask >>= INTVAL (XEXP (x, 1));
else
mask = fuller_mask;
op0 = gen_lowpart_for_combine (op_mode,
force_to_mode (XEXP (x, 0), op_mode,
mask, reg, next_select));
if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
x = gen_binary (code, op_mode, op0, XEXP (x, 1));
break;
case LSHIFTRT:
/* Here we can only do something if the shift count is a constant,
this shift constant is valid for the host, and we can do arithmetic
in OP_MODE. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
&& GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
{
rtx inner = XEXP (x, 0);
/* Select the mask of the bits we need for the shift operand. */
mask <<= INTVAL (XEXP (x, 1));
/* We can only change the mode of the shift if we can do arithmetic
in the mode of the shift and MASK is no wider than the width of
OP_MODE. */
if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT
|| (mask & ~ GET_MODE_MASK (op_mode)) != 0)
op_mode = GET_MODE (x);
inner = force_to_mode (inner, op_mode, mask, reg, next_select);
if (GET_MODE (x) != op_mode || inner != XEXP (x, 0))
x = gen_binary (LSHIFTRT, op_mode, inner, XEXP (x, 1));
}
/* If we have (and (lshiftrt FOO C1) C2) where the combination of the
shift and AND produces only copies of the sign bit (C2 is one less
than a power of two), we can do this with just a shift. */
if (GET_CODE (x) == LSHIFTRT
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& ((INTVAL (XEXP (x, 1))
+ num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
>= GET_MODE_BITSIZE (GET_MODE (x)))
&& exact_log2 (mask + 1) >= 0
&& (num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
>= exact_log2 (mask + 1)))
x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0),
GEN_INT (GET_MODE_BITSIZE (GET_MODE (x))
- exact_log2 (mask + 1)));
break;
case ASHIFTRT:
/* If we are just looking for the sign bit, we don't need this shift at
all, even if it has a variable count. */
if (GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
&& (mask == ((unsigned HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
/* If this is a shift by a constant, get a mask that contains those bits
that are not copies of the sign bit. We then have two cases: If
MASK only includes those bits, this can be a logical shift, which may
allow simplifications. If MASK is a single-bit field not within
those bits, we are requesting a copy of the sign bit and hence can
shift the sign bit to the appropriate location. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
{
int i = -1;
/* If the considered data is wider then HOST_WIDE_INT, we can't
represent a mask for all its bits in a single scalar.
But we only care about the lower bits, so calculate these. */
if (GET_MODE_BITSIZE (GET_MODE (x)) > HOST_BITS_PER_WIDE_INT)
{
nonzero = ~ (HOST_WIDE_INT) 0;
/* GET_MODE_BITSIZE (GET_MODE (x)) - INTVAL (XEXP (x, 1))
is the number of bits a full-width mask would have set.
We need only shift if these are fewer than nonzero can
hold. If not, we must keep all bits set in nonzero. */
if (GET_MODE_BITSIZE (GET_MODE (x)) - INTVAL (XEXP (x, 1))
< HOST_BITS_PER_WIDE_INT)
nonzero >>= INTVAL (XEXP (x, 1))
+ HOST_BITS_PER_WIDE_INT
- GET_MODE_BITSIZE (GET_MODE (x)) ;
}
else
{
nonzero = GET_MODE_MASK (GET_MODE (x));
nonzero >>= INTVAL (XEXP (x, 1));
}
if ((mask & ~ nonzero) == 0
|| (i = exact_log2 (mask)) >= 0)
{
x = simplify_shift_const
(x, LSHIFTRT, GET_MODE (x), XEXP (x, 0),
i < 0 ? INTVAL (XEXP (x, 1))
: GET_MODE_BITSIZE (GET_MODE (x)) - 1 - i);
if (GET_CODE (x) != ASHIFTRT)
return force_to_mode (x, mode, mask, reg, next_select);
}
}
/* If MASK is 1, convert this to a LSHIFTRT. This can be done
even if the shift count isn't a constant. */
if (mask == 1)
x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0), XEXP (x, 1));
/* If this is a sign-extension operation that just affects bits
we don't care about, remove it. Be sure the call above returned
something that is still a shift. */
if ((GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ASHIFTRT)
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& (INTVAL (XEXP (x, 1))
<= GET_MODE_BITSIZE (GET_MODE (x)) - (floor_log2 (mask) + 1))
&& GET_CODE (XEXP (x, 0)) == ASHIFT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (x, 0), 1)) == INTVAL (XEXP (x, 1)))
return force_to_mode (XEXP (XEXP (x, 0), 0), mode, mask,
reg, next_select);
break;
case ROTATE:
case ROTATERT:
/* If the shift count is constant and we can do computations
in the mode of X, compute where the bits we care about are.
Otherwise, we can't do anything. Don't change the mode of
the shift or propagate MODE into the shift, though. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0)
{
temp = simplify_binary_operation (code == ROTATE ? ROTATERT : ROTATE,
GET_MODE (x), GEN_INT (mask),
XEXP (x, 1));
if (temp && GET_CODE(temp) == CONST_INT)
SUBST (XEXP (x, 0),
force_to_mode (XEXP (x, 0), GET_MODE (x),
INTVAL (temp), reg, next_select));
}
break;
case NEG:
/* If we just want the low-order bit, the NEG isn't needed since it
won't change the low-order bit. */
if (mask == 1)
return force_to_mode (XEXP (x, 0), mode, mask, reg, just_select);
/* We need any bits less significant than the most significant bit in
MASK since carries from those bits will affect the bits we are
interested in. */
mask = fuller_mask;
goto unop;
case NOT:
/* (not FOO) is (xor FOO CONST), so if FOO is an LSHIFTRT, we can do the
same as the XOR case above. Ensure that the constant we form is not
wider than the mode of X. */
if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
&& (INTVAL (XEXP (XEXP (x, 0), 1)) + floor_log2 (mask)
< GET_MODE_BITSIZE (GET_MODE (x)))
&& INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT)
{
temp = GEN_INT (mask << INTVAL (XEXP (XEXP (x, 0), 1)));
temp = gen_binary (XOR, GET_MODE (x), XEXP (XEXP (x, 0), 0), temp);
x = gen_binary (LSHIFTRT, GET_MODE (x), temp, XEXP (XEXP (x, 0), 1));
return force_to_mode (x, mode, mask, reg, next_select);
}
/* (and (not FOO) CONST) is (not (or FOO (not CONST))), so we must
use the full mask inside the NOT. */
mask = fuller_mask;
unop:
op0 = gen_lowpart_for_combine (op_mode,
force_to_mode (XEXP (x, 0), mode, mask,
reg, next_select));
if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
x = gen_unary (code, op_mode, op_mode, op0);
break;
case NE:
/* (and (ne FOO 0) CONST) can be (and FOO CONST) if CONST is included
in STORE_FLAG_VALUE and FOO has a single bit that might be nonzero,
which is equal to STORE_FLAG_VALUE. */
if ((mask & ~ STORE_FLAG_VALUE) == 0 && XEXP (x, 1) == const0_rtx
&& exact_log2 (nonzero_bits (XEXP (x, 0), mode)) >= 0
&& nonzero_bits (XEXP (x, 0), mode) == STORE_FLAG_VALUE)
return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
break;
case IF_THEN_ELSE:
/* We have no way of knowing if the IF_THEN_ELSE can itself be
written in a narrower mode. We play it safe and do not do so. */
SUBST (XEXP (x, 1),
gen_lowpart_for_combine (GET_MODE (x),
force_to_mode (XEXP (x, 1), mode,
mask, reg, next_select)));
SUBST (XEXP (x, 2),
gen_lowpart_for_combine (GET_MODE (x),
force_to_mode (XEXP (x, 2), mode,
mask, reg,next_select)));
break;
default:
break;
}
/* Ensure we return a value of the proper mode. */
return gen_lowpart_for_combine (mode, x);
}
/* Return nonzero if X is an expression that has one of two values depending on
whether some other value is zero or nonzero. In that case, we return the
value that is being tested, *PTRUE is set to the value if the rtx being
returned has a nonzero value, and *PFALSE is set to the other alternative.
If we return zero, we set *PTRUE and *PFALSE to X. */
static rtx
if_then_else_cond (x, ptrue, pfalse)
rtx x;
rtx *ptrue, *pfalse;
{
enum machine_mode mode = GET_MODE (x);
enum rtx_code code = GET_CODE (x);
int size = GET_MODE_BITSIZE (mode);
rtx cond0, cond1, true0, true1, false0, false1;
unsigned HOST_WIDE_INT nz;
/* If this is a unary operation whose operand has one of two values, apply
our opcode to compute those values. */
if (GET_RTX_CLASS (code) == '1'
&& (cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0)) != 0)
{
*ptrue = gen_unary (code, mode, GET_MODE (XEXP (x, 0)), true0);
*pfalse = gen_unary (code, mode, GET_MODE (XEXP (x, 0)), false0);
return cond0;
}
/* If this is a COMPARE, do nothing, since the IF_THEN_ELSE we would
make can't possibly match and would suppress other optimizations. */
else if (code == COMPARE)
;
/* If this is a binary operation, see if either side has only one of two
values. If either one does or if both do and they are conditional on
the same value, compute the new true and false values. */
else if (GET_RTX_CLASS (code) == 'c' || GET_RTX_CLASS (code) == '2'
|| GET_RTX_CLASS (code) == '<')
{
cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0);
cond1 = if_then_else_cond (XEXP (x, 1), &true1, &false1);
if ((cond0 != 0 || cond1 != 0)
&& ! (cond0 != 0 && cond1 != 0 && ! rtx_equal_p (cond0, cond1)))
{
/* If if_then_else_cond returned zero, then true/false are the
same rtl. We must copy one of them to prevent invalid rtl
sharing. */
if (cond0 == 0)
true0 = copy_rtx (true0);
else if (cond1 == 0)
true1 = copy_rtx (true1);
*ptrue = gen_binary (code, mode, true0, true1);
*pfalse = gen_binary (code, mode, false0, false1);
return cond0 ? cond0 : cond1;
}
/* See if we have PLUS, IOR, XOR, MINUS or UMAX, where one of the
operands is zero when the other is non-zero, and vice-versa,
and STORE_FLAG_VALUE is 1 or -1. */
if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& (code == PLUS || code == IOR || code == XOR || code == MINUS
|| code == UMAX)
&& GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT)
{
rtx op0 = XEXP (XEXP (x, 0), 1);
rtx op1 = XEXP (XEXP (x, 1), 1);
cond0 = XEXP (XEXP (x, 0), 0);
cond1 = XEXP (XEXP (x, 1), 0);
if (GET_RTX_CLASS (GET_CODE (cond0)) == '<'
&& GET_RTX_CLASS (GET_CODE (cond1)) == '<'
&& reversible_comparison_p (cond1)
&& ((GET_CODE (cond0) == reverse_condition (GET_CODE (cond1))
&& rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
&& rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
|| ((swap_condition (GET_CODE (cond0))
== reverse_condition (GET_CODE (cond1)))
&& rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1))
&& rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0))))
&& ! side_effects_p (x))
{
*ptrue = gen_binary (MULT, mode, op0, const_true_rtx);
*pfalse = gen_binary (MULT, mode,
(code == MINUS
? gen_unary (NEG, mode, mode, op1) : op1),
const_true_rtx);
return cond0;
}
}
/* Similarly for MULT, AND and UMIN, execpt that for these the result
is always zero. */
if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& (code == MULT || code == AND || code == UMIN)
&& GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT)
{
cond0 = XEXP (XEXP (x, 0), 0);
cond1 = XEXP (XEXP (x, 1), 0);
if (GET_RTX_CLASS (GET_CODE (cond0)) == '<'
&& GET_RTX_CLASS (GET_CODE (cond1)) == '<'
&& reversible_comparison_p (cond1)
&& ((GET_CODE (cond0) == reverse_condition (GET_CODE (cond1))
&& rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
&& rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
|| ((swap_condition (GET_CODE (cond0))
== reverse_condition (GET_CODE (cond1)))
&& rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1))
&& rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0))))
&& ! side_effects_p (x))
{
*ptrue = *pfalse = const0_rtx;
return cond0;
}
}
}
else if (code == IF_THEN_ELSE)
{
/* If we have IF_THEN_ELSE already, extract the condition and
canonicalize it if it is NE or EQ. */
cond0 = XEXP (x, 0);
*ptrue = XEXP (x, 1), *pfalse = XEXP (x, 2);
if (GET_CODE (cond0) == NE && XEXP (cond0, 1) == const0_rtx)
return XEXP (cond0, 0);
else if (GET_CODE (cond0) == EQ && XEXP (cond0, 1) == const0_rtx)
{
*ptrue = XEXP (x, 2), *pfalse = XEXP (x, 1);
return XEXP (cond0, 0);
}
else
return cond0;
}
/* If X is a normal SUBREG with both inner and outer modes integral,
we can narrow both the true and false values of the inner expression,
if there is a condition. */
else if (code == SUBREG && GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
&& GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
&& 0 != (cond0 = if_then_else_cond (SUBREG_REG (x),
&true0, &false0)))
{
*ptrue = force_to_mode (true0, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
*pfalse
= force_to_mode (false0, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
return cond0;
}
/* If X is a constant, this isn't special and will cause confusions
if we treat it as such. Likewise if it is equivalent to a constant. */
else if (CONSTANT_P (x)
|| ((cond0 = get_last_value (x)) != 0 && CONSTANT_P (cond0)))
;
/* If X is known to be either 0 or -1, those are the true and
false values when testing X. */
else if (num_sign_bit_copies (x, mode) == size)
{
*ptrue = constm1_rtx, *pfalse = const0_rtx;
return x;
}
/* Likewise for 0 or a single bit. */
else if (exact_log2 (nz = nonzero_bits (x, mode)) >= 0)
{
*ptrue = GEN_INT (nz), *pfalse = const0_rtx;
return x;
}
/* Otherwise fail; show no condition with true and false values the same. */
*ptrue = *pfalse = x;
return 0;
}
/* Return the value of expression X given the fact that condition COND
is known to be true when applied to REG as its first operand and VAL
as its second. X is known to not be shared and so can be modified in
place.
We only handle the simplest cases, and specifically those cases that
arise with IF_THEN_ELSE expressions. */
static rtx
known_cond (x, cond, reg, val)
rtx x;
enum rtx_code cond;
rtx reg, val;
{
enum rtx_code code = GET_CODE (x);
rtx temp;
char *fmt;
int i, j;
if (side_effects_p (x))
return x;
if (cond == EQ && rtx_equal_p (x, reg))
return val;
/* If X is (abs REG) and we know something about REG's relationship
with zero, we may be able to simplify this. */
if (code == ABS && rtx_equal_p (XEXP (x, 0), reg) && val == const0_rtx)
switch (cond)
{
case GE: case GT: case EQ:
return XEXP (x, 0);
case LT: case LE:
return gen_unary (NEG, GET_MODE (XEXP (x, 0)), GET_MODE (XEXP (x, 0)),
XEXP (x, 0));
default:
break;
}
/* The only other cases we handle are MIN, MAX, and comparisons if the
operands are the same as REG and VAL. */
else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c')
{
if (rtx_equal_p (XEXP (x, 0), val))
cond = swap_condition (cond), temp = val, val = reg, reg = temp;
if (rtx_equal_p (XEXP (x, 0), reg) && rtx_equal_p (XEXP (x, 1), val))
{
if (GET_RTX_CLASS (code) == '<')
return (comparison_dominates_p (cond, code) ? const_true_rtx
: (comparison_dominates_p (cond,
reverse_condition (code))
? const0_rtx : x));
else if (code == SMAX || code == SMIN
|| code == UMIN || code == UMAX)
{
int unsignedp = (code == UMIN || code == UMAX);
if (code == SMAX || code == UMAX)
cond = reverse_condition (cond);
switch (cond)
{
case GE: case GT:
return unsignedp ? x : XEXP (x, 1);
case LE: case LT:
return unsignedp ? x : XEXP (x, 0);
case GEU: case GTU:
return unsignedp ? XEXP (x, 1) : x;
case LEU: case LTU:
return unsignedp ? XEXP (x, 0) : x;
default:
break;
}
}
}
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
SUBST (XEXP (x, i), known_cond (XEXP (x, i), cond, reg, val));
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
SUBST (XVECEXP (x, i, j), known_cond (XVECEXP (x, i, j),
cond, reg, val));
}
return x;
}
/* See if X and Y are equal for the purposes of seeing if we can rewrite an
assignment as a field assignment. */
static int
rtx_equal_for_field_assignment_p (x, y)
rtx x;
rtx y;
{
if (x == y || rtx_equal_p (x, y))
return 1;
if (x == 0 || y == 0 || GET_MODE (x) != GET_MODE (y))
return 0;
/* Check for a paradoxical SUBREG of a MEM compared with the MEM.
Note that all SUBREGs of MEM are paradoxical; otherwise they
would have been rewritten. */
if (GET_CODE (x) == MEM && GET_CODE (y) == SUBREG
&& GET_CODE (SUBREG_REG (y)) == MEM
&& rtx_equal_p (SUBREG_REG (y),
gen_lowpart_for_combine (GET_MODE (SUBREG_REG (y)), x)))
return 1;
if (GET_CODE (y) == MEM && GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == MEM
&& rtx_equal_p (SUBREG_REG (x),
gen_lowpart_for_combine (GET_MODE (SUBREG_REG (x)), y)))
return 1;
/* We used to see if get_last_value of X and Y were the same but that's
not correct. In one direction, we'll cause the assignment to have
the wrong destination and in the case, we'll import a register into this
insn that might have already have been dead. So fail if none of the
above cases are true. */
return 0;
}
/* See if X, a SET operation, can be rewritten as a bit-field assignment.
Return that assignment if so.
We only handle the most common cases. */
static rtx
make_field_assignment (x)
rtx x;
{
rtx dest = SET_DEST (x);
rtx src = SET_SRC (x);
rtx assign;
rtx rhs, lhs;
HOST_WIDE_INT c1;
int pos, len;
rtx other;
enum machine_mode mode;
/* If SRC was (and (not (ashift (const_int 1) POS)) DEST), this is
a clear of a one-bit field. We will have changed it to
(and (rotate (const_int -2) POS) DEST), so check for that. Also check
for a SUBREG. */
if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == ROTATE
&& GET_CODE (XEXP (XEXP (src, 0), 0)) == CONST_INT
&& INTVAL (XEXP (XEXP (src, 0), 0)) == -2
&& rtx_equal_for_field_assignment_p (dest, XEXP (src, 1)))
{
assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
1, 1, 1, 0);
if (assign != 0)
return gen_rtx_SET (VOIDmode, assign, const0_rtx);
return x;
}
else if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG
&& subreg_lowpart_p (XEXP (src, 0))
&& (GET_MODE_SIZE (GET_MODE (XEXP (src, 0)))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (src, 0)))))
&& GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE
&& INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2
&& rtx_equal_for_field_assignment_p (dest, XEXP (src, 1)))
{
assign = make_extraction (VOIDmode, dest, 0,
XEXP (SUBREG_REG (XEXP (src, 0)), 1),
1, 1, 1, 0);
if (assign != 0)
return gen_rtx_SET (VOIDmode, assign, const0_rtx);
return x;
}
/* If SRC is (ior (ashift (const_int 1) POS) DEST), this is a set of a
one-bit field. */
else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == ASHIFT
&& XEXP (XEXP (src, 0), 0) == const1_rtx
&& rtx_equal_for_field_assignment_p (dest, XEXP (src, 1)))
{
assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
1, 1, 1, 0);
if (assign != 0)
return gen_rtx_SET (VOIDmode, assign, const1_rtx);
return x;
}
/* The other case we handle is assignments into a constant-position
field. They look like (ior/xor (and DEST C1) OTHER). If C1 represents
a mask that has all one bits except for a group of zero bits and
OTHER is known to have zeros where C1 has ones, this is such an
assignment. Compute the position and length from C1. Shift OTHER
to the appropriate position, force it to the required mode, and
make the extraction. Check for the AND in both operands. */
if (GET_CODE (src) != IOR && GET_CODE (src) != XOR)
return x;
rhs = expand_compound_operation (XEXP (src, 0));
lhs = expand_compound_operation (XEXP (src, 1));
if (GET_CODE (rhs) == AND
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT
&& rtx_equal_for_field_assignment_p (XEXP (rhs, 0), dest))
c1 = INTVAL (XEXP (rhs, 1)), other = lhs;
else if (GET_CODE (lhs) == AND
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT
&& rtx_equal_for_field_assignment_p (XEXP (lhs, 0), dest))
c1 = INTVAL (XEXP (lhs, 1)), other = rhs;
else
return x;
pos = get_pos_from_mask ((~ c1) & GET_MODE_MASK (GET_MODE (dest)), &len);
if (pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (dest))
|| GET_MODE_BITSIZE (GET_MODE (dest)) > HOST_BITS_PER_WIDE_INT
|| (c1 & nonzero_bits (other, GET_MODE (dest))) != 0)
return x;
assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0);
if (assign == 0)
return x;
/* The mode to use for the source is the mode of the assignment, or of
what is inside a possible STRICT_LOW_PART. */
mode = (GET_CODE (assign) == STRICT_LOW_PART
? GET_MODE (XEXP (assign, 0)) : GET_MODE (assign));
/* Shift OTHER right POS places and make it the source, restricting it
to the proper length and mode. */
src = force_to_mode (simplify_shift_const (NULL_RTX, LSHIFTRT,
GET_MODE (src), other, pos),
mode,
GET_MODE_BITSIZE (mode) >= HOST_BITS_PER_WIDE_INT
? GET_MODE_MASK (mode)
: ((HOST_WIDE_INT) 1 << len) - 1,
dest, 0);
return gen_rtx_combine (SET, VOIDmode, assign, src);
}
/* See if X is of the form (+ (* a c) (* b c)) and convert to (* (+ a b) c)
if so. */
static rtx
apply_distributive_law (x)
rtx x;
{
enum rtx_code code = GET_CODE (x);
rtx lhs, rhs, other;
rtx tem;
enum rtx_code inner_code;
/* Distributivity is not true for floating point.
It can change the value. So don't do it.
-- rms and moshier@world.std.com. */
if (FLOAT_MODE_P (GET_MODE (x)))
return x;
/* The outer operation can only be one of the following: */
if (code != IOR && code != AND && code != XOR
&& code != PLUS && code != MINUS)
return x;
lhs = XEXP (x, 0), rhs = XEXP (x, 1);
/* If either operand is a primitive we can't do anything, so get out
fast. */
if (GET_RTX_CLASS (GET_CODE (lhs)) == 'o'
|| GET_RTX_CLASS (GET_CODE (rhs)) == 'o')
return x;
lhs = expand_compound_operation (lhs);
rhs = expand_compound_operation (rhs);
inner_code = GET_CODE (lhs);
if (inner_code != GET_CODE (rhs))
return x;
/* See if the inner and outer operations distribute. */
switch (inner_code)
{
case LSHIFTRT:
case ASHIFTRT:
case AND:
case IOR:
/* These all distribute except over PLUS. */
if (code == PLUS || code == MINUS)
return x;
break;
case MULT:
if (code != PLUS && code != MINUS)
return x;
break;
case ASHIFT:
/* This is also a multiply, so it distributes over everything. */
break;
case SUBREG:
/* Non-paradoxical SUBREGs distributes over all operations, provided
the inner modes and word numbers are the same, this is an extraction
of a low-order part, we don't convert an fp operation to int or
vice versa, and we would not be converting a single-word
operation into a multi-word operation. The latter test is not
required, but it prevents generating unneeded multi-word operations.
Some of the previous tests are redundant given the latter test, but
are retained because they are required for correctness.
We produce the result slightly differently in this case. */
if (GET_MODE (SUBREG_REG (lhs)) != GET_MODE (SUBREG_REG (rhs))
|| SUBREG_WORD (lhs) != SUBREG_WORD (rhs)
|| ! subreg_lowpart_p (lhs)
|| (GET_MODE_CLASS (GET_MODE (lhs))
!= GET_MODE_CLASS (GET_MODE (SUBREG_REG (lhs))))
|| (GET_MODE_SIZE (GET_MODE (lhs))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))))
|| GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))) > UNITS_PER_WORD)
return x;
tem = gen_binary (code, GET_MODE (SUBREG_REG (lhs)),
SUBREG_REG (lhs), SUBREG_REG (rhs));
return gen_lowpart_for_combine (GET_MODE (x), tem);
default:
return x;
}
/* Set LHS and RHS to the inner operands (A and B in the example
above) and set OTHER to the common operand (C in the example).
These is only one way to do this unless the inner operation is
commutative. */
if (GET_RTX_CLASS (inner_code) == 'c'
&& rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 0)))
other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 1);
else if (GET_RTX_CLASS (inner_code) == 'c'
&& rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 1)))
other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 0);
else if (GET_RTX_CLASS (inner_code) == 'c'
&& rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 0)))
other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 1);
else if (rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 1)))
other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 0);
else
return x;
/* Form the new inner operation, seeing if it simplifies first. */
tem = gen_binary (code, GET_MODE (x), lhs, rhs);
/* There is one exception to the general way of distributing:
(a ^ b) | (a ^ c) -> (~a) & (b ^ c) */
if (code == XOR && inner_code == IOR)
{
inner_code = AND;
other = gen_unary (NOT, GET_MODE (x), GET_MODE (x), other);
}
/* We may be able to continuing distributing the result, so call
ourselves recursively on the inner operation before forming the
outer operation, which we return. */
return gen_binary (inner_code, GET_MODE (x),
apply_distributive_law (tem), other);
}
/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done
in MODE.
Return an equivalent form, if different from X. Otherwise, return X. If
X is zero, we are to always construct the equivalent form. */
static rtx
simplify_and_const_int (x, mode, varop, constop)
rtx x;
enum machine_mode mode;
rtx varop;
unsigned HOST_WIDE_INT constop;
{
unsigned HOST_WIDE_INT nonzero;
int width = GET_MODE_BITSIZE (mode);
int i;
/* Simplify VAROP knowing that we will be only looking at some of the
bits in it. */
varop = force_to_mode (varop, mode, constop, NULL_RTX, 0);
/* If VAROP is a CLOBBER, we will fail so return it; if it is a
CONST_INT, we are done. */
if (GET_CODE (varop) == CLOBBER || GET_CODE (varop) == CONST_INT)
return varop;
/* See what bits may be nonzero in VAROP. Unlike the general case of
a call to nonzero_bits, here we don't care about bits outside
MODE. */
nonzero = nonzero_bits (varop, mode) & GET_MODE_MASK (mode);
/* 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
&& (nonzero & ((HOST_WIDE_INT) 1 << (width - 1))))
nonzero |= ((HOST_WIDE_INT) (-1) << width);
/* Turn off all bits in the constant that are known to already be zero.
Thus, if the AND isn't needed at all, we will have CONSTOP == NONZERO_BITS
which is tested below. */
constop &= nonzero;
/* If we don't have any bits left, return zero. */
if (constop == 0)
return const0_rtx;
/* If VAROP is a NEG of something known to be zero or 1 and CONSTOP is
a power of two, we can replace this with a ASHIFT. */
if (GET_CODE (varop) == NEG && nonzero_bits (XEXP (varop, 0), mode) == 1
&& (i = exact_log2 (constop)) >= 0)
return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (varop, 0), i);
/* If VAROP is an IOR or XOR, apply the AND to both branches of the IOR
or XOR, then try to apply the distributive law. This may eliminate
operations if either branch can be simplified because of the AND.
It may also make some cases more complex, but those cases probably
won't match a pattern either with or without this. */
if (GET_CODE (varop) == IOR || GET_CODE (varop) == XOR)
return
gen_lowpart_for_combine
(mode,
apply_distributive_law
(gen_binary (GET_CODE (varop), GET_MODE (varop),
simplify_and_const_int (NULL_RTX, GET_MODE (varop),
XEXP (varop, 0), constop),
simplify_and_const_int (NULL_RTX, GET_MODE (varop),
XEXP (varop, 1), constop))));
/* Get VAROP in MODE. Try to get a SUBREG if not. Don't make a new SUBREG
if we already had one (just check for the simplest cases). */
if (x && GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_MODE (XEXP (x, 0)) == mode
&& SUBREG_REG (XEXP (x, 0)) == varop)
varop = XEXP (x, 0);
else
varop = gen_lowpart_for_combine (mode, varop);
/* If we can't make the SUBREG, try to return what we were given. */
if (GET_CODE (varop) == CLOBBER)
return x ? x : varop;
/* If we are only masking insignificant bits, return VAROP. */
if (constop == nonzero)
x = varop;
/* Otherwise, return an AND. See how much, if any, of X we can use. */
else if (x == 0 || GET_CODE (x) != AND || GET_MODE (x) != mode)
x = gen_binary (AND, mode, varop, GEN_INT (constop));
else
{
if (GET_CODE (XEXP (x, 1)) != CONST_INT
|| (unsigned HOST_WIDE_INT) INTVAL (XEXP (x, 1)) != constop)
SUBST (XEXP (x, 1), GEN_INT (constop));
SUBST (XEXP (x, 0), varop);
}
return x;
}
/* We let num_sign_bit_copies recur into nonzero_bits as that is useful.
We don't let nonzero_bits recur into num_sign_bit_copies, because that
is less useful. We can't allow both, because that results in exponential
run time recursion. There is a nullstone testcase that triggered
this. This macro avoids accidental uses of num_sign_bit_copies. */
#define num_sign_bit_copies()
/* Given an expression, X, compute which bits in X can be non-zero.
We don't care about bits outside of those defined in MODE.
For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is
a shift, AND, or zero_extract, we can do better. */
static unsigned HOST_WIDE_INT
nonzero_bits (x, mode)
rtx x;
enum machine_mode mode;
{
unsigned HOST_WIDE_INT nonzero = GET_MODE_MASK (mode);
unsigned HOST_WIDE_INT inner_nz;
enum rtx_code code;
int mode_width = GET_MODE_BITSIZE (mode);
rtx tem;
/* For floating-point values, assume all bits are needed. */
if (FLOAT_MODE_P (GET_MODE (x)) || FLOAT_MODE_P (mode))
return nonzero;
/* If X is wider than MODE, use its mode instead. */
if (GET_MODE_BITSIZE (GET_MODE (x)) > mode_width)
{
mode = GET_MODE (x);
nonzero = GET_MODE_MASK (mode);
mode_width = GET_MODE_BITSIZE (mode);
}
if (mode_width > HOST_BITS_PER_WIDE_INT)
/* Our only callers in this case look for single bit values. So
just return the mode mask. Those tests will then be false. */
return nonzero;
#ifndef WORD_REGISTER_OPERATIONS
/* If MODE is wider than X, but both are a single word for both the host
and target machines, we can compute this from which bits of the
object might be nonzero in its own mode, taking into account the fact
that on many CISC machines, accessing an object in a wider mode
causes the high-order bits to become undefined. So they are
not known to be zero. */
if (GET_MODE (x) != VOIDmode && GET_MODE (x) != mode
&& GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD
&& GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
&& GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (GET_MODE (x)))
{
nonzero &= nonzero_bits (x, GET_MODE (x));
nonzero |= GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x));
return nonzero;
}
#endif
code = GET_CODE (x);
switch (code)
{
case REG:
#ifdef POINTERS_EXTEND_UNSIGNED
/* If pointers extend unsigned and this is a pointer in Pmode, say that
all the bits above ptr_mode are known to be zero. */
if (POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode
&& REGNO_POINTER_FLAG (REGNO (x)))
nonzero &= GET_MODE_MASK (ptr_mode);
#endif
#ifdef STACK_BOUNDARY
/* If this is the stack pointer, we may know something about its
alignment. If PUSH_ROUNDING is defined, it is possible for the
stack to be momentarily aligned only to that amount, so we pick
the least alignment. */
/* We can't check for arg_pointer_rtx here, because it is not
guaranteed to have as much alignment as the stack pointer.
In particular, in the Irix6 n64 ABI, the stack has 128 bit
alignment but the argument pointer has only 64 bit alignment. */
if ((x == frame_pointer_rtx
|| x == stack_pointer_rtx
|| x == hard_frame_pointer_rtx
|| (REGNO (x) >= FIRST_VIRTUAL_REGISTER
&& REGNO (x) <= LAST_VIRTUAL_REGISTER))
#ifdef STACK_BIAS
&& !STACK_BIAS
#endif
)
{
int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
#ifdef PUSH_ROUNDING
if (REGNO (x) == STACK_POINTER_REGNUM)
sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment);
#endif
/* We must return here, otherwise we may get a worse result from
one of the choices below. There is nothing useful below as
far as the stack pointer is concerned. */
return nonzero &= ~ (sp_alignment - 1);
}
#endif
/* If X is a register whose nonzero bits value is current, use it.
Otherwise, if X is a register whose value we can find, use that
value. Otherwise, use the previously-computed global nonzero bits
for this register. */
if (reg_last_set_value[REGNO (x)] != 0
&& reg_last_set_mode[REGNO (x)] == mode
&& (REG_N_SETS (REGNO (x)) == 1
|| reg_last_set_label[REGNO (x)] == label_tick)
&& INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid)
return reg_last_set_nonzero_bits[REGNO (x)];
tem = get_last_value (x);
if (tem)
{
#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
/* If X is narrower than MODE and TEM is a non-negative
constant that would appear negative in the mode of X,
sign-extend it for use in reg_nonzero_bits because some
machines (maybe most) will actually do the sign-extension
and this is the conservative approach.
??? For 2.5, try to tighten up the MD files in this regard
instead of this kludge. */
if (GET_MODE_BITSIZE (GET_MODE (x)) < mode_width
&& GET_CODE (tem) == CONST_INT
&& INTVAL (tem) > 0
&& 0 != (INTVAL (tem)
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
tem = GEN_INT (INTVAL (tem)
| ((HOST_WIDE_INT) (-1)
<< GET_MODE_BITSIZE (GET_MODE (x))));
#endif
return nonzero_bits (tem, mode);
}
else if (nonzero_sign_valid && reg_nonzero_bits[REGNO (x)])
return reg_nonzero_bits[REGNO (x)] & nonzero;
else
return nonzero;
case CONST_INT:
#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
/* If X is negative in MODE, sign-extend the value. */
if (INTVAL (x) > 0 && mode_width < BITS_PER_WORD
&& 0 != (INTVAL (x) & ((HOST_WIDE_INT) 1 << (mode_width - 1))))
return (INTVAL (x) | ((HOST_WIDE_INT) (-1) << mode_width));
#endif
return INTVAL (x);
case MEM:
#ifdef LOAD_EXTEND_OP
/* In many, if not most, RISC machines, reading a byte from memory
zeros the rest of the register. Noticing that fact saves a lot
of extra zero-extends. */
if (LOAD_EXTEND_OP (GET_MODE (x)) == ZERO_EXTEND)
nonzero &= GET_MODE_MASK (GET_MODE (x));
#endif
break;
case EQ: case NE:
case GT: case GTU:
case LT: case LTU:
case GE: case GEU:
case LE: case LEU:
/* If this produces an integer result, we know which bits are set.
Code here used to clear bits outside the mode of X, but that is
now done above. */
if (GET_MODE_CLASS (mode) == MODE_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT)
nonzero = STORE_FLAG_VALUE;
break;
case NEG:
#if 0
/* Disabled to avoid exponential mutual recursion between nonzero_bits
and num_sign_bit_copies. */
if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
== GET_MODE_BITSIZE (GET_MODE (x)))
nonzero = 1;
#endif
if (GET_MODE_SIZE (GET_MODE (x)) < mode_width)
nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
break;
case ABS:
#if 0
/* Disabled to avoid exponential mutual recursion between nonzero_bits
and num_sign_bit_copies. */
if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
== GET_MODE_BITSIZE (GET_MODE (x)))
nonzero = 1;
#endif
break;
case TRUNCATE:
nonzero &= (nonzero_bits (XEXP (x, 0), mode) & GET_MODE_MASK (mode));
break;
case ZERO_EXTEND:
nonzero &= nonzero_bits (XEXP (x, 0), mode);
if (GET_MODE (XEXP (x, 0)) != VOIDmode)
nonzero &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
break;
case SIGN_EXTEND:
/* If the sign bit is known clear, this is the same as ZERO_EXTEND.
Otherwise, show all the bits in the outer mode but not the inner
may be non-zero. */
inner_nz = nonzero_bits (XEXP (x, 0), mode);
if (GET_MODE (XEXP (x, 0)) != VOIDmode)
{
inner_nz &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
if (inner_nz
& (((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))))
inner_nz |= (GET_MODE_MASK (mode)
& ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0))));
}
nonzero &= inner_nz;
break;
case AND:
nonzero &= (nonzero_bits (XEXP (x, 0), mode)
& nonzero_bits (XEXP (x, 1), mode));
break;
case XOR: case IOR:
case UMIN: case UMAX: case SMIN: case SMAX:
nonzero &= (nonzero_bits (XEXP (x, 0), mode)
| nonzero_bits (XEXP (x, 1), mode));
break;
case PLUS: case MINUS:
case MULT:
case DIV: case UDIV:
case MOD: case UMOD:
/* We can apply the rules of arithmetic to compute the number of
high- and low-order zero bits of these operations. We start by
computing the width (position of the highest-order non-zero bit)
and the number of low-order zero bits for each value. */
{
unsigned HOST_WIDE_INT nz0 = nonzero_bits (XEXP (x, 0), mode);
unsigned HOST_WIDE_INT nz1 = nonzero_bits (XEXP (x, 1), mode);
int width0 = floor_log2 (nz0) + 1;
int width1 = floor_log2 (nz1) + 1;
int low0 = floor_log2 (nz0 & -nz0);
int low1 = floor_log2 (nz1 & -nz1);
HOST_WIDE_INT op0_maybe_minusp
= (nz0 & ((HOST_WIDE_INT) 1 << (mode_width - 1)));
HOST_WIDE_INT op1_maybe_minusp
= (nz1 & ((HOST_WIDE_INT) 1 << (mode_width - 1)));
int result_width = mode_width;
int result_low = 0;
switch (code)
{
case PLUS:
#ifdef STACK_BIAS
if (STACK_BIAS
&& (XEXP (x, 0) == stack_pointer_rtx
|| XEXP (x, 0) == frame_pointer_rtx)
&& GET_CODE (XEXP (x, 1)) == CONST_INT)
{
int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
nz0 = (GET_MODE_MASK (mode) & ~ (sp_alignment - 1));
nz1 = INTVAL (XEXP (x, 1)) - STACK_BIAS;
width0 = floor_log2 (nz0) + 1;
width1 = floor_log2 (nz1) + 1;
low0 = floor_log2 (nz0 & -nz0);
low1 = floor_log2 (nz1 & -nz1);
}
#endif
result_width = MAX (width0, width1) + 1;
result_low = MIN (low0, low1);
break;
case MINUS:
result_low = MIN (low0, low1);
break;
case MULT:
result_width = width0 + width1;
result_low = low0 + low1;
break;
case DIV:
if (! op0_maybe_minusp && ! op1_maybe_minusp)
result_width = width0;
break;
case UDIV:
result_width = width0;
break;
case MOD:
if (! op0_maybe_minusp && ! op1_maybe_minusp)
result_width = MIN (width0, width1);
result_low = MIN (low0, low1);
break;
case UMOD:
result_width = MIN (width0, width1);
result_low = MIN (low0, low1);
break;
default:
abort ();
}
if (result_width < mode_width)
nonzero &= ((HOST_WIDE_INT) 1 << result_width) - 1;
if (result_low > 0)
nonzero &= ~ (((HOST_WIDE_INT) 1 << result_low) - 1);
}
break;
case ZERO_EXTRACT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
nonzero &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1;
break;
case SUBREG:
/* If this is a SUBREG formed for a promoted variable that has
been zero-extended, we know that at least the high-order bits
are zero, though others might be too. */
if (SUBREG_PROMOTED_VAR_P (x) && SUBREG_PROMOTED_UNSIGNED_P (x))
nonzero = (GET_MODE_MASK (GET_MODE (x))
& nonzero_bits (SUBREG_REG (x), GET_MODE (x)));
/* If the inner mode is a single word for both the host and target
machines, we can compute this from which bits of the inner
object might be nonzero. */
if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) <= BITS_PER_WORD
&& (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
<= HOST_BITS_PER_WIDE_INT))
{
nonzero &= nonzero_bits (SUBREG_REG (x), mode);
#if defined (WORD_REGISTER_OPERATIONS) && defined (LOAD_EXTEND_OP)
/* If this is a typical RISC machine, we only have to worry
about the way loads are extended. */
if (LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) == SIGN_EXTEND
? (nonzero
& (1L << (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) - 1)))
: LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) != ZERO_EXTEND)
#endif
{
/* On many CISC machines, accessing an object in a wider mode
causes the high-order bits to become undefined. So they are
not known to be zero. */
if (GET_MODE_SIZE (GET_MODE (x))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
nonzero |= (GET_MODE_MASK (GET_MODE (x))
& ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
}
}
break;
case ASHIFTRT:
case LSHIFTRT:
case ASHIFT:
case ROTATE:
/* The nonzero bits are in two classes: any bits within MODE
that aren't in GET_MODE (x) are always significant. The rest of the
nonzero bits are those that are significant in the operand of
the shift when shifted the appropriate number of bits. This
shows that high-order bits are cleared by the right shift and
low-order bits by left shifts. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
{
enum machine_mode inner_mode = GET_MODE (x);
int width = GET_MODE_BITSIZE (inner_mode);
int count = INTVAL (XEXP (x, 1));
unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (inner_mode);
unsigned HOST_WIDE_INT op_nonzero = nonzero_bits (XEXP (x, 0), mode);
unsigned HOST_WIDE_INT inner = op_nonzero & mode_mask;
unsigned HOST_WIDE_INT outer = 0;
if (mode_width > width)
outer = (op_nonzero & nonzero & ~ mode_mask);
if (code == LSHIFTRT)
inner >>= count;
else if (code == ASHIFTRT)
{
inner >>= count;
/* If the sign bit may have been nonzero before the shift, we
need to mark all the places it could have been copied to
by the shift as possibly nonzero. */
if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count)))
inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count);
}
else if (code == ASHIFT)
inner <<= count;
else
inner = ((inner << (count % width)
| (inner >> (width - (count % width)))) & mode_mask);
nonzero &= (outer | inner);
}
break;
case FFS:
/* This is at most the number of bits in the mode. */
nonzero = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1;
break;
case IF_THEN_ELSE:
nonzero &= (nonzero_bits (XEXP (x, 1), mode)
| nonzero_bits (XEXP (x, 2), mode));
break;
default:
break;
}
return nonzero;
}
/* See the macro definition above. */
#undef num_sign_bit_copies
/* Return the number of bits at the high-order end of X that are known to
be equal to the sign bit. X will be used in mode MODE; if MODE is
VOIDmode, X will be used in its own mode. The returned value will always
be between 1 and the number of bits in MODE. */
static int
num_sign_bit_copies (x, mode)
rtx x;
enum machine_mode mode;
{
enum rtx_code code = GET_CODE (x);
int bitwidth;
int num0, num1, result;
unsigned HOST_WIDE_INT nonzero;
rtx tem;
/* If we weren't given a mode, use the mode of X. If the mode is still
VOIDmode, we don't know anything. Likewise if one of the modes is
floating-point. */
if (mode == VOIDmode)
mode = GET_MODE (x);
if (mode == VOIDmode || FLOAT_MODE_P (mode) || FLOAT_MODE_P (GET_MODE (x)))
return 1;
bitwidth = GET_MODE_BITSIZE (mode);
/* For a smaller object, just ignore the high bits. */
if (bitwidth < GET_MODE_BITSIZE (GET_MODE (x)))
return MAX (1, (num_sign_bit_copies (x, GET_MODE (x))
- (GET_MODE_BITSIZE (GET_MODE (x)) - bitwidth)));
if (GET_MODE (x) != VOIDmode && bitwidth > GET_MODE_BITSIZE (GET_MODE (x)))
{
#ifndef WORD_REGISTER_OPERATIONS
/* If this machine does not do all register operations on the entire
register and MODE is wider than the mode of X, we can say nothing
at all about the high-order bits. */
return 1;
#else
/* Likewise on machines that do, if the mode of the object is smaller
than a word and loads of that size don't sign extend, we can say
nothing about the high order bits. */
if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD
#ifdef LOAD_EXTEND_OP
&& LOAD_EXTEND_OP (GET_MODE (x)) != SIGN_EXTEND
#endif
)
return 1;
#endif
}
switch (code)
{
case REG:
#ifdef POINTERS_EXTEND_UNSIGNED
/* If pointers extend signed and this is a pointer in Pmode, say that
all the bits above ptr_mode are known to be sign bit copies. */
if (! POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode && mode == Pmode
&& REGNO_POINTER_FLAG (REGNO (x)))
return GET_MODE_BITSIZE (Pmode) - GET_MODE_BITSIZE (ptr_mode) + 1;
#endif
if (reg_last_set_value[REGNO (x)] != 0
&& reg_last_set_mode[REGNO (x)] == mode
&& (REG_N_SETS (REGNO (x)) == 1
|| reg_last_set_label[REGNO (x)] == label_tick)
&& INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid)
return reg_last_set_sign_bit_copies[REGNO (x)];
tem = get_last_value (x);
if (tem != 0)
return num_sign_bit_copies (tem, mode);
if (nonzero_sign_valid && reg_sign_bit_copies[REGNO (x)] != 0)
return reg_sign_bit_copies[REGNO (x)];
break;
case MEM:
#ifdef LOAD_EXTEND_OP
/* Some RISC machines sign-extend all loads of smaller than a word. */
if (LOAD_EXTEND_OP (GET_MODE (x)) == SIGN_EXTEND)
return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1);
#endif
break;
case CONST_INT:
/* If the constant is negative, take its 1's complement and remask.
Then see how many zero bits we have. */
nonzero = INTVAL (x) & GET_MODE_MASK (mode);
if (bitwidth <= HOST_BITS_PER_WIDE_INT
&& (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
nonzero = (~ nonzero) & GET_MODE_MASK (mode);
return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1);
case SUBREG:
/* If this is a SUBREG for a promoted object that is sign-extended
and we are looking at it in a wider mode, we know that at least the
high-order bits are known to be sign bit copies. */
if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x))
return MAX (bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1,
num_sign_bit_copies (SUBREG_REG (x), mode));
/* For a smaller object, just ignore the high bits. */
if (bitwidth <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))))
{
num0 = num_sign_bit_copies (SUBREG_REG (x), VOIDmode);
return MAX (1, (num0
- (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
- bitwidth)));
}
#ifdef WORD_REGISTER_OPERATIONS
#ifdef LOAD_EXTEND_OP
/* For paradoxical SUBREGs on machines where all register operations
affect the entire register, just look inside. Note that we are
passing MODE to the recursive call, so the number of sign bit copies
will remain relative to that mode, not the inner mode. */
/* This works only if loads sign extend. Otherwise, if we get a
reload for the inner part, it may be loaded from the stack, and
then we lose all sign bit copies that existed before the store
to the stack. */
if ((GET_MODE_SIZE (GET_MODE (x))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
&& LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) == SIGN_EXTEND)
return num_sign_bit_copies (SUBREG_REG (x), mode);
#endif
#endif
break;
case SIGN_EXTRACT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
return MAX (1, bitwidth - INTVAL (XEXP (x, 1)));
break;
case SIGN_EXTEND:
return (bitwidth - GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+ num_sign_bit_copies (XEXP (x, 0), VOIDmode));
case TRUNCATE:
/* For a smaller object, just ignore the high bits. */
num0 = num_sign_bit_copies (XEXP (x, 0), VOIDmode);
return MAX (1, (num0 - (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
- bitwidth)));
case NOT:
return num_sign_bit_copies (XEXP (x, 0), mode);
case ROTATE: case ROTATERT:
/* If we are rotating left by a number of bits less than the number
of sign bit copies, we can just subtract that amount from the
number. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0 && INTVAL (XEXP (x, 1)) < bitwidth)
{
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
return MAX (1, num0 - (code == ROTATE ? INTVAL (XEXP (x, 1))
: bitwidth - INTVAL (XEXP (x, 1))));
}
break;
case NEG:
/* In general, this subtracts one sign bit copy. But if the value
is known to be positive, the number of sign bit copies is the
same as that of the input. Finally, if the input has just one bit
that might be nonzero, all the bits are copies of the sign bit. */
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
if (bitwidth > HOST_BITS_PER_WIDE_INT)
return num0 > 1 ? num0 - 1 : 1;
nonzero = nonzero_bits (XEXP (x, 0), mode);
if (nonzero == 1)
return bitwidth;
if (num0 > 1
&& (((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero))
num0--;
return num0;
case IOR: case AND: case XOR:
case SMIN: case SMAX: case UMIN: case UMAX:
/* Logical operations will preserve the number of sign-bit copies.
MIN and MAX operations always return one of the operands. */
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
num1 = num_sign_bit_copies (XEXP (x, 1), mode);
return MIN (num0, num1);
case PLUS: case MINUS:
/* For addition and subtraction, we can have a 1-bit carry. However,
if we are subtracting 1 from a positive number, there will not
be such a carry. Furthermore, if the positive number is known to
be 0 or 1, we know the result is either -1 or 0. */
if (code == PLUS && XEXP (x, 1) == constm1_rtx
&& bitwidth <= HOST_BITS_PER_WIDE_INT)
{
nonzero = nonzero_bits (XEXP (x, 0), mode);
if ((((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero) == 0)
return (nonzero == 1 || nonzero == 0 ? bitwidth
: bitwidth - floor_log2 (nonzero) - 1);
}
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
num1 = num_sign_bit_copies (XEXP (x, 1), mode);
return MAX (1, MIN (num0, num1) - 1);
case MULT:
/* The number of bits of the product is the sum of the number of
bits of both terms. However, unless one of the terms if known
to be positive, we must allow for an additional bit since negating
a negative number can remove one sign bit copy. */
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
num1 = num_sign_bit_copies (XEXP (x, 1), mode);
result = bitwidth - (bitwidth - num0) - (bitwidth - num1);
if (result > 0
&& (bitwidth > HOST_BITS_PER_WIDE_INT
|| (((nonzero_bits (XEXP (x, 0), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
&& ((nonzero_bits (XEXP (x, 1), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))))
result--;
return MAX (1, result);
case UDIV:
/* The result must be <= the first operand. If the first operand
has the high bit set, we know nothing about the number of sign
bit copies. */
if (bitwidth > HOST_BITS_PER_WIDE_INT)
return 1;
else if ((nonzero_bits (XEXP (x, 0), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
return 1;
else
return num_sign_bit_copies (XEXP (x, 0), mode);
case UMOD:
/* The result must be <= the scond operand. */
return num_sign_bit_copies (XEXP (x, 1), mode);
case DIV:
/* Similar to unsigned division, except that we have to worry about
the case where the divisor is negative, in which case we have
to add 1. */
result = num_sign_bit_copies (XEXP (x, 0), mode);
if (result > 1
&& (bitwidth > HOST_BITS_PER_WIDE_INT
|| (nonzero_bits (XEXP (x, 1), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))
result--;
return result;
case MOD:
result = num_sign_bit_copies (XEXP (x, 1), mode);
if (result > 1
&& (bitwidth > HOST_BITS_PER_WIDE_INT
|| (nonzero_bits (XEXP (x, 1), mode)
& ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))
result--;
return result;
case ASHIFTRT:
/* Shifts by a constant add to the number of bits equal to the
sign bit. */
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) > 0)
num0 = MIN (bitwidth, num0 + INTVAL (XEXP (x, 1)));
return num0;
case ASHIFT:
/* Left shifts destroy copies. */
if (GET_CODE (XEXP (x, 1)) != CONST_INT
|| INTVAL (XEXP (x, 1)) < 0
|| INTVAL (XEXP (x, 1)) >= bitwidth)
return 1;
num0 = num_sign_bit_copies (XEXP (x, 0), mode);
return MAX (1, num0 - INTVAL (XEXP (x, 1)));
case IF_THEN_ELSE:
num0 = num_sign_bit_copies (XEXP (x, 1), mode);
num1 = num_sign_bit_copies (XEXP (x, 2), mode);
return MIN (num0, num1);
case EQ: case NE: case GE: case GT: case LE: case LT:
case GEU: case GTU: case LEU: case LTU:
if (STORE_FLAG_VALUE == -1)
return bitwidth;
break;
default:
break;
}
/* If we haven't been able to figure it out by one of the above rules,
see if some of the high-order bits are known to be zero. If so,
count those bits and return one less than that amount. If we can't
safely compute the mask for this mode, always return BITWIDTH. */
if (bitwidth > HOST_BITS_PER_WIDE_INT)
return 1;
nonzero = nonzero_bits (x, mode);
return (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))
? 1 : bitwidth - floor_log2 (nonzero) - 1);
}
/* Return the number of "extended" bits there are in X, when interpreted
as a quantity in MODE whose signedness is indicated by UNSIGNEDP. For
unsigned quantities, this is the number of high-order zero bits.
For signed quantities, this is the number of copies of the sign bit
minus 1. In both case, this function returns the number of "spare"
bits. For example, if two quantities for which this function returns
at least 1 are added, the addition is known not to overflow.
This function will always return 0 unless called during combine, which
implies that it must be called from a define_split. */
int
extended_count (x, mode, unsignedp)
rtx x;
enum machine_mode mode;
int unsignedp;
{
if (nonzero_sign_valid == 0)
return 0;
return (unsignedp
? (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& (GET_MODE_BITSIZE (mode) - 1
- floor_log2 (nonzero_bits (x, mode))))
: num_sign_bit_copies (x, mode) - 1);
}
/* This function is called from `simplify_shift_const' to merge two
outer operations. Specifically, we have already found that we need
to perform operation *POP0 with constant *PCONST0 at the outermost
position. We would now like to also perform OP1 with constant CONST1
(with *POP0 being done last).
Return 1 if we can do the operation and update *POP0 and *PCONST0 with
the resulting operation. *PCOMP_P is set to 1 if we would need to
complement the innermost operand, otherwise it is unchanged.
MODE is the mode in which the operation will be done. No bits outside
the width of this mode matter. It is assumed that the width of this mode
is smaller than or equal to HOST_BITS_PER_WIDE_INT.
If *POP0 or OP1 are NIL, it means no operation is required. Only NEG, PLUS,
IOR, XOR, and AND are supported. We may set *POP0 to SET if the proper
result is simply *PCONST0.
If the resulting operation cannot be expressed as one operation, we
return 0 and do not change *POP0, *PCONST0, and *PCOMP_P. */
static int
merge_outer_ops (pop0, pconst0, op1, const1, mode, pcomp_p)
enum rtx_code *pop0;
HOST_WIDE_INT *pconst0;
enum rtx_code op1;
HOST_WIDE_INT const1;
enum machine_mode mode;
int *pcomp_p;
{
enum rtx_code op0 = *pop0;
HOST_WIDE_INT const0 = *pconst0;
int width = GET_MODE_BITSIZE (mode);
const0 &= GET_MODE_MASK (mode);
const1 &= GET_MODE_MASK (mode);
/* If OP0 is an AND, clear unimportant bits in CONST1. */
if (op0 == AND)
const1 &= const0;
/* If OP0 or OP1 is NIL, this is easy. Similarly if they are the same or
if OP0 is SET. */
if (op1 == NIL || op0 == SET)
return 1;
else if (op0 == NIL)
op0 = op1, const0 = const1;
else if (op0 == op1)
{
switch (op0)
{
case AND:
const0 &= const1;
break;
case IOR:
const0 |= const1;
break;
case XOR:
const0 ^= const1;
break;
case PLUS:
const0 += const1;
break;
case NEG:
op0 = NIL;
break;
default:
break;
}
}
/* Otherwise, if either is a PLUS or NEG, we can't do anything. */
else if (op0 == PLUS || op1 == PLUS || op0 == NEG || op1 == NEG)
return 0;
/* If the two constants aren't the same, we can't do anything. The
remaining six cases can all be done. */
else if (const0 != const1)
return 0;
else
switch (op0)
{
case IOR:
if (op1 == AND)
/* (a & b) | b == b */
op0 = SET;
else /* op1 == XOR */
/* (a ^ b) | b == a | b */
{;}
break;
case XOR:
if (op1 == AND)
/* (a & b) ^ b == (~a) & b */
op0 = AND, *pcomp_p = 1;
else /* op1 == IOR */
/* (a | b) ^ b == a & ~b */
op0 = AND, *pconst0 = ~ const0;
break;
case AND:
if (op1 == IOR)
/* (a | b) & b == b */
op0 = SET;
else /* op1 == XOR */
/* (a ^ b) & b) == (~a) & b */
*pcomp_p = 1;
break;
default:
break;
}
/* Check for NO-OP cases. */
const0 &= GET_MODE_MASK (mode);
if (const0 == 0
&& (op0 == IOR || op0 == XOR || op0 == PLUS))
op0 = NIL;
else if (const0 == 0 && op0 == AND)
op0 = SET;
else if ((unsigned HOST_WIDE_INT) const0 == GET_MODE_MASK (mode)
&& op0 == AND)
op0 = NIL;
/* 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
&& (const0 & ((HOST_WIDE_INT) 1 << (width - 1))))
const0 |= ((HOST_WIDE_INT) (-1) << width);
*pop0 = op0;
*pconst0 = const0;
return 1;
}
/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift.
The result of the shift is RESULT_MODE. X, if non-zero, is an expression
that we started with.
The shift is normally computed in the widest mode we find in VAROP, as
long as it isn't a different number of words than RESULT_MODE. Exceptions
are ASHIFTRT and ROTATE, which are always done in their original mode, */
static rtx
simplify_shift_const (x, code, result_mode, varop, count)
rtx x;
enum rtx_code code;
enum machine_mode result_mode;
rtx varop;
int count;
{
enum rtx_code orig_code = code;
int orig_count = count;
enum machine_mode mode = result_mode;
enum machine_mode shift_mode, tmode;
int mode_words
= (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
/* We form (outer_op (code varop count) (outer_const)). */
enum rtx_code outer_op = NIL;
HOST_WIDE_INT outer_const = 0;
rtx const_rtx;
int complement_p = 0;
rtx new;
/* If we were given an invalid count, don't do anything except exactly
what was requested. */
if (count < 0 || count > GET_MODE_BITSIZE (mode))
{
if (x)
return x;
return gen_rtx_fmt_ee (code, mode, varop, GEN_INT (count));
}
/* Unless one of the branches of the `if' in this loop does a `continue',
we will `break' the loop after the `if'. */
while (count != 0)
{
/* If we have an operand of (clobber (const_int 0)), just return that
value. */
if (GET_CODE (varop) == CLOBBER)
return varop;
/* If we discovered we had to complement VAROP, leave. Making a NOT
here would cause an infinite loop. */
if (complement_p)
break;
/* Convert ROTATERT to ROTATE. */
if (code == ROTATERT)
code = ROTATE, count = GET_MODE_BITSIZE (result_mode) - count;
/* We need to determine what mode we will do the shift in. If the
shift is a right shift or a ROTATE, we must always do it in the mode
it was originally done in. Otherwise, we can do it in MODE, the
widest mode encountered. */
shift_mode
= (code == ASHIFTRT || code == LSHIFTRT || code == ROTATE
? result_mode : mode);
/* Handle cases where the count is greater than the size of the mode
minus 1. For ASHIFT, use the size minus one as the count (this can
occur when simplifying (lshiftrt (ashiftrt ..))). For rotates,
take the count modulo the size. For other shifts, the result is
zero.
Since these shifts are being produced by the compiler by combining
multiple operations, each of which are defined, we know what the
result is supposed to be. */
if (count > GET_MODE_BITSIZE (shift_mode) - 1)
{
if (code == ASHIFTRT)
count = GET_MODE_BITSIZE (shift_mode) - 1;
else if (code == ROTATE || code == ROTATERT)
count %= GET_MODE_BITSIZE (shift_mode);
else
{
/* We can't simply return zero because there may be an
outer op. */
varop = const0_rtx;
count = 0;
break;
}
}
/* Negative counts are invalid and should not have been made (a
programmer-specified negative count should have been handled
above). */
else if (count < 0)
abort ();
/* An arithmetic right shift of a quantity known to be -1 or 0
is a no-op. */
if (code == ASHIFTRT
&& (num_sign_bit_copies (varop, shift_mode)
== GET_MODE_BITSIZE (shift_mode)))
{
count = 0;
break;
}
/* If we are doing an arithmetic right shift and discarding all but
the sign bit copies, this is equivalent to doing a shift by the
bitsize minus one. Convert it into that shift because it will often
allow other simplifications. */
if (code == ASHIFTRT
&& (count + num_sign_bit_copies (varop, shift_mode)
>= GET_MODE_BITSIZE (shift_mode)))
count = GET_MODE_BITSIZE (shift_mode) - 1;
/* We simplify the tests below and elsewhere by converting
ASHIFTRT to LSHIFTRT if we know the sign bit is clear.
`make_compound_operation' will convert it to a ASHIFTRT for
those machines (such as Vax) that don't have a LSHIFTRT. */
if (GET_MODE_BITSIZE (shift_mode) <= HOST_BITS_PER_WIDE_INT
&& code == ASHIFTRT
&& ((nonzero_bits (varop, shift_mode)
& ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (shift_mode) - 1)))
== 0))
code = LSHIFTRT;
switch (GET_CODE (varop))
{
case SIGN_EXTEND:
case ZERO_EXTEND:
case SIGN_EXTRACT:
case ZERO_EXTRACT:
new = expand_compound_operation (varop);
if (new != varop)
{
varop = new;
continue;
}
break;
case MEM:
/* If we have (xshiftrt (mem ...) C) and C is MODE_WIDTH
minus the width of a smaller mode, we can do this with a
SIGN_EXTEND or ZERO_EXTEND from the narrower memory location. */
if ((code == ASHIFTRT || code == LSHIFTRT)
&& ! mode_dependent_address_p (XEXP (varop, 0))
&& ! MEM_VOLATILE_P (varop)
&& (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count,
MODE_INT, 1)) != BLKmode)
{
if (BYTES_BIG_ENDIAN)
new = gen_rtx_MEM (tmode, XEXP (varop, 0));
else
new = gen_rtx_MEM (tmode,
plus_constant (XEXP (varop, 0),
count / BITS_PER_UNIT));
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (varop);
MEM_COPY_ATTRIBUTES (new, varop);
varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND
: ZERO_EXTEND, mode, new);
count = 0;
continue;
}
break;
case USE:
/* Similar to the case above, except that we can only do this if
the resulting mode is the same as that of the underlying
MEM and adjust the address depending on the *bits* endianness
because of the way that bit-field extract insns are defined. */
if ((code == ASHIFTRT || code == LSHIFTRT)
&& (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count,
MODE_INT, 1)) != BLKmode
&& tmode == GET_MODE (XEXP (varop, 0)))
{
if (BITS_BIG_ENDIAN)
new = XEXP (varop, 0);
else
{
new = copy_rtx (XEXP (varop, 0));
SUBST (XEXP (new, 0),
plus_constant (XEXP (new, 0),
count / BITS_PER_UNIT));
}
varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND
: ZERO_EXTEND, mode, new);
count = 0;
continue;
}
break;
case SUBREG:
/* If VAROP is a SUBREG, strip it as long as the inner operand has
the same number of words as what we've seen so far. Then store
the widest mode in MODE. */
if (subreg_lowpart_p (varop)
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))
> GET_MODE_SIZE (GET_MODE (varop)))
&& (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))
+ (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
== mode_words))
{
varop = SUBREG_REG (varop);
if (GET_MODE_SIZE (GET_MODE (varop)) > GET_MODE_SIZE (mode))
mode = GET_MODE (varop);
continue;
}
break;
case MULT:
/* Some machines use MULT instead of ASHIFT because MULT
is cheaper. But it is still better on those machines to
merge two shifts into one. */
if (GET_CODE (XEXP (varop, 1)) == CONST_INT
&& exact_log2 (INTVAL (XEXP (varop, 1))) >= 0)
{
varop = gen_binary (ASHIFT, GET_MODE (varop), XEXP (varop, 0),
GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1)))));;
continue;
}
break;
case UDIV:
/* Similar, for when divides are cheaper. */
if (GET_CODE (XEXP (varop, 1)) == CONST_INT
&& exact_log2 (INTVAL (XEXP (varop, 1))) >= 0)
{
varop = gen_binary (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0),
GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1)))));
continue;
}
break;
case ASHIFTRT:
/* If we are extracting just the sign bit of an arithmetic right
shift, that shift is not needed. */
if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1)
{
varop = XEXP (varop, 0);
continue;
}
/* ... fall through ... */
case LSHIFTRT:
case ASHIFT:
case ROTATE:
/* Here we have two nested shifts. The result is usually the
AND of a new shift with a mask. We compute the result below. */
if (GET_CODE (XEXP (varop, 1)) == CONST_INT
&& INTVAL (XEXP (varop, 1)) >= 0
&& INTVAL (XEXP (varop, 1)) < GET_MODE_BITSIZE (GET_MODE (varop))
&& GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
{
enum rtx_code first_code = GET_CODE (varop);
int first_count = INTVAL (XEXP (varop, 1));
unsigned HOST_WIDE_INT mask;
rtx mask_rtx;
/* We have one common special case. We can't do any merging if
the inner code is an ASHIFTRT of a smaller mode. However, if
we have (ashift:M1 (subreg:M1 (ashiftrt:M2 FOO C1) 0) C2)
with C2 == GET_MODE_BITSIZE (M1) - GET_MODE_BITSIZE (M2),
we can convert it to
(ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0 C2) C3) C1).
This simplifies certain SIGN_EXTEND operations. */
if (code == ASHIFT && first_code == ASHIFTRT
&& (GET_MODE_BITSIZE (result_mode)
- GET_MODE_BITSIZE (GET_MODE (varop))) == count)
{
/* C3 has the low-order C1 bits zero. */
mask = (GET_MODE_MASK (mode)
& ~ (((HOST_WIDE_INT) 1 << first_count) - 1));
varop = simplify_and_const_int (NULL_RTX, result_mode,
XEXP (varop, 0), mask);
varop = simplify_shift_const (NULL_RTX, ASHIFT, result_mode,
varop, count);
count = first_count;
code = ASHIFTRT;
continue;
}
/* If this was (ashiftrt (ashift foo C1) C2) and FOO has more
than C1 high-order bits equal to the sign bit, we can convert
this to either an ASHIFT or a ASHIFTRT depending on the
two counts.
We cannot do this if VAROP's mode is not SHIFT_MODE. */
if (code == ASHIFTRT && first_code == ASHIFT
&& GET_MODE (varop) == shift_mode
&& (num_sign_bit_copies (XEXP (varop, 0), shift_mode)
> first_count))
{
count -= first_count;
if (count < 0)
count = - count, code = ASHIFT;
varop = XEXP (varop, 0);
continue;
}
/* There are some cases we can't do. If CODE is ASHIFTRT,
we can only do this if FIRST_CODE is also ASHIFTRT.
We can't do the case when CODE is ROTATE and FIRST_CODE is
ASHIFTRT.
If the mode of this shift is not the mode of the outer shift,
we can't do this if either shift is a right shift or ROTATE.
Finally, we can't do any of these if the mode is too wide
unless the codes are the same.
Handle the case where the shift codes are the same
first. */
if (code == first_code)
{
if (GET_MODE (varop) != result_mode
&& (code == ASHIFTRT || code == LSHIFTRT
|| code == ROTATE))
break;
count += first_count;
varop = XEXP (varop, 0);
continue;
}
if (code == ASHIFTRT
|| (code == ROTATE && first_code == ASHIFTRT)
|| GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT
|| (GET_MODE (varop) != result_mode
&& (first_code == ASHIFTRT || first_code == LSHIFTRT
|| first_code == ROTATE
|| code == ROTATE)))
break;
/* To compute the mask to apply after the shift, shift the
nonzero bits of the inner shift the same way the
outer shift will. */
mask_rtx = GEN_INT (nonzero_bits (varop, GET_MODE (varop)));
mask_rtx
= simplify_binary_operation (code, result_mode, mask_rtx,
GEN_INT (count));
/* Give up if we can't compute an outer operation to use. */
if (mask_rtx == 0
|| GET_CODE (mask_rtx) != CONST_INT
|| ! merge_outer_ops (&outer_op, &outer_const, AND,
INTVAL (mask_rtx),
result_mode, &complement_p))
break;
/* If the shifts are in the same direction, we add the
counts. Otherwise, we subtract them. */
if ((code == ASHIFTRT || code == LSHIFTRT)
== (first_code == ASHIFTRT || first_code == LSHIFTRT))
count += first_count;
else
count -= first_count;
/* If COUNT is positive, the new shift is usually CODE,
except for the two exceptions below, in which case it is
FIRST_CODE. If the count is negative, FIRST_CODE should
always be used */
if (count > 0
&& ((first_code == ROTATE && code == ASHIFT)
|| (first_code == ASHIFTRT && code == LSHIFTRT)))
code = first_code;
else if (count < 0)
code = first_code, count = - count;
varop = XEXP (varop, 0);
continue;
}
/* If we have (A << B << C) for any shift, we can convert this to
(A << C << B). This wins if A is a constant. Only try this if
B is not a constant. */
else if (GET_CODE (varop) == code
&& GET_CODE (XEXP (varop, 1)) != CONST_INT
&& 0 != (new
= simplify_binary_operation (code, mode,
XEXP (varop, 0),
GEN_INT (count))))
{
varop = gen_rtx_combine (code, mode, new, XEXP (varop, 1));
count = 0;
continue;
}
break;
case NOT:
/* Make this fit the case below. */
varop = gen_rtx_combine (XOR, mode, XEXP (varop, 0),
GEN_INT (GET_MODE_MASK (mode)));
continue;
case IOR:
case AND:
case XOR:
/* If we have (xshiftrt (ior (plus X (const_int -1)) X) C)
with C the size of VAROP - 1 and the shift is logical if
STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1,
we have an (le X 0) operation. If we have an arithmetic shift
and STORE_FLAG_VALUE is 1 or we have a logical shift with
STORE_FLAG_VALUE of -1, we have a (neg (le X 0)) operation. */
if (GET_CODE (varop) == IOR && GET_CODE (XEXP (varop, 0)) == PLUS
&& XEXP (XEXP (varop, 0), 1) == constm1_rtx
&& (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& (code == LSHIFTRT || code == ASHIFTRT)
&& count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1
&& rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
{
count = 0;
varop = gen_rtx_combine (LE, GET_MODE (varop), XEXP (varop, 1),
const0_rtx);
if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT)
varop = gen_rtx_combine (NEG, GET_MODE (varop), varop);
continue;
}
/* If we have (shift (logical)), move the logical to the outside
to allow it to possibly combine with another logical and the
shift to combine with another shift. This also canonicalizes to
what a ZERO_EXTRACT looks like. Also, some machines have
(and (shift)) insns. */
if (GET_CODE (XEXP (varop, 1)) == CONST_INT
&& (new = simplify_binary_operation (code, result_mode,
XEXP (varop, 1),
GEN_INT (count))) != 0
&& GET_CODE(new) == CONST_INT
&& merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop),
INTVAL (new), result_mode, &complement_p))
{
varop = XEXP (varop, 0);
continue;
}
/* If we can't do that, try to simplify the shift in each arm of the
logical expression, make a new logical expression, and apply
the inverse distributive law. */
{
rtx lhs = simplify_shift_const (NULL_RTX, code, shift_mode,
XEXP (varop, 0), count);
rtx rhs = simplify_shift_const (NULL_RTX, code, shift_mode,
XEXP (varop, 1), count);
varop = gen_binary (GET_CODE (varop), shift_mode, lhs, rhs);
varop = apply_distributive_law (varop);
count = 0;
}
break;
case EQ:
/* convert (lshiftrt (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE
says that the sign bit can be tested, FOO has mode MODE, C is
GET_MODE_BITSIZE (MODE) - 1, and FOO has only its low-order bit
that may be nonzero. */
if (code == LSHIFTRT
&& XEXP (varop, 1) == const0_rtx
&& GET_MODE (XEXP (varop, 0)) == result_mode
&& count == GET_MODE_BITSIZE (result_mode) - 1
&& GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
&& ((STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (result_mode) - 1))))
&& nonzero_bits (XEXP (varop, 0), result_mode) == 1
&& merge_outer_ops (&outer_op, &outer_const, XOR,
(HOST_WIDE_INT) 1, result_mode,
&complement_p))
{
varop = XEXP (varop, 0);
count = 0;
continue;
}
break;
case NEG:
/* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less
than the number of bits in the mode is equivalent to A. */
if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1
&& nonzero_bits (XEXP (varop, 0), result_mode) == 1)
{
varop = XEXP (varop, 0);
count = 0;
continue;
}
/* NEG commutes with ASHIFT since it is multiplication. Move the
NEG outside to allow shifts to combine. */
if (code == ASHIFT
&& merge_outer_ops (&outer_op, &outer_const, NEG,
(HOST_WIDE_INT) 0, result_mode,
&complement_p))
{
varop = XEXP (varop, 0);
continue;
}
break;
case PLUS:
/* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C
is one less than the number of bits in the mode is
equivalent to (xor A 1). */
if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1
&& XEXP (varop, 1) == constm1_rtx
&& nonzero_bits (XEXP (varop, 0), result_mode) == 1
&& merge_outer_ops (&outer_op, &outer_const, XOR,
(HOST_WIDE_INT) 1, result_mode,
&complement_p))
{
count = 0;
varop = XEXP (varop, 0);
continue;
}
/* If we have (xshiftrt (plus FOO BAR) C), and the only bits
that might be nonzero in BAR are those being shifted out and those
bits are known zero in FOO, we can replace the PLUS with FOO.
Similarly in the other operand order. This code occurs when
we are computing the size of a variable-size array. */
if ((code == ASHIFTRT || code == LSHIFTRT)
&& count < HOST_BITS_PER_WIDE_INT
&& nonzero_bits (XEXP (varop, 1), result_mode) >> count == 0
&& (nonzero_bits (XEXP (varop, 1), result_mode)
& nonzero_bits (XEXP (varop, 0), result_mode)) == 0)
{
varop = XEXP (varop, 0);
continue;
}
else if ((code == ASHIFTRT || code == LSHIFTRT)
&& count < HOST_BITS_PER_WIDE_INT
&& GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
&& 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
>> count)
&& 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
& nonzero_bits (XEXP (varop, 1),
result_mode)))
{
varop = XEXP (varop, 1);
continue;
}
/* (ashift (plus foo C) N) is (plus (ashift foo N) C'). */
if (code == ASHIFT
&& GET_CODE (XEXP (varop, 1)) == CONST_INT
&& (new = simplify_binary_operation (ASHIFT, result_mode,
XEXP (varop, 1),
GEN_INT (count))) != 0
&& GET_CODE(new) == CONST_INT
&& merge_outer_ops (&outer_op, &outer_const, PLUS,
INTVAL (new), result_mode, &complement_p))
{
varop = XEXP (varop, 0);
continue;
}
break;
case MINUS:
/* If we have (xshiftrt (minus (ashiftrt X C)) X) C)
with C the size of VAROP - 1 and the shift is logical if
STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1,
we have a (gt X 0) operation. If the shift is arithmetic with
STORE_FLAG_VALUE of 1 or logical with STORE_FLAG_VALUE == -1,
we have a (neg (gt X 0)) operation. */
if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
&& GET_CODE (XEXP (varop, 0)) == ASHIFTRT
&& count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1
&& (code == LSHIFTRT || code == ASHIFTRT)
&& GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (varop, 0), 1)) == count
&& rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
{
count = 0;
varop = gen_rtx_combine (GT, GET_MODE (varop), XEXP (varop, 1),
const0_rtx);
if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT)
varop = gen_rtx_combine (NEG, GET_MODE (varop), varop);
continue;
}
break;
case TRUNCATE:
/* Change (lshiftrt (truncate (lshiftrt))) to (truncate (lshiftrt))
if the truncate does not affect the value. */
if (code == LSHIFTRT
&& GET_CODE (XEXP (varop, 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
&& (INTVAL (XEXP (XEXP (varop, 0), 1))
>= (GET_MODE_BITSIZE (GET_MODE (XEXP (varop, 0)))
- GET_MODE_BITSIZE (GET_MODE (varop)))))
{
rtx varop_inner = XEXP (varop, 0);
varop_inner = gen_rtx_combine (LSHIFTRT,
GET_MODE (varop_inner),
XEXP (varop_inner, 0),
GEN_INT (count + INTVAL (XEXP (varop_inner, 1))));
varop = gen_rtx_combine (TRUNCATE, GET_MODE (varop),
varop_inner);
count = 0;
continue;
}
break;
default:
break;
}
break;
}
/* We need to determine what mode to do the shift in. If the shift is
a right shift or ROTATE, we must always do it in the mode it was
originally done in. Otherwise, we can do it in MODE, the widest mode
encountered. The code we care about is that of the shift that will
actually be done, not the shift that was originally requested. */
shift_mode
= (code == ASHIFTRT || code == LSHIFTRT || code == ROTATE
? result_mode : mode);
/* We have now finished analyzing the shift. The result should be
a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places. If
OUTER_OP is non-NIL, it is an operation that needs to be applied
to the result of the shift. OUTER_CONST is the relevant constant,
but we must turn off all bits turned off in the shift.
If we were passed a value for X, see if we can use any pieces of
it. If not, make new rtx. */
if (x && GET_RTX_CLASS (GET_CODE (x)) == '2'
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) == count)
const_rtx = XEXP (x, 1);
else
const_rtx = GEN_INT (count);
if (x && GET_CODE (XEXP (x, 0)) == SUBREG
&& GET_MODE (XEXP (x, 0)) == shift_mode
&& SUBREG_REG (XEXP (x, 0)) == varop)
varop = XEXP (x, 0);
else if (GET_MODE (varop) != shift_mode)
varop = gen_lowpart_for_combine (shift_mode, varop);
/* If we can't make the SUBREG, try to return what we were given. */
if (GET_CODE (varop) == CLOBBER)
return x ? x : varop;
new = simplify_binary_operation (code, shift_mode, varop, const_rtx);
if (new != 0)
x = new;
else
{
if (x == 0 || GET_CODE (x) != code || GET_MODE (x) != shift_mode)
x = gen_rtx_combine (code, shift_mode, varop, const_rtx);
SUBST (XEXP (x, 0), varop);
SUBST (XEXP (x, 1), const_rtx);
}
/* If we have an outer operation and we just made a shift, it is
possible that we could have simplified the shift were it not
for the outer operation. So try to do the simplification
recursively. */
if (outer_op != NIL && GET_CODE (x) == code
&& GET_CODE (XEXP (x, 1)) == CONST_INT)
x = simplify_shift_const (x, code, shift_mode, XEXP (x, 0),
INTVAL (XEXP (x, 1)));
/* If we were doing a LSHIFTRT in a wider mode than it was originally,
turn off all the bits that the shift would have turned off. */
if (orig_code == LSHIFTRT && result_mode != shift_mode)
x = simplify_and_const_int (NULL_RTX, shift_mode, x,
GET_MODE_MASK (result_mode) >> orig_count);
/* Do the remainder of the processing in RESULT_MODE. */
x = gen_lowpart_for_combine (result_mode, x);
/* If COMPLEMENT_P is set, we have to complement X before doing the outer
operation. */
if (complement_p)
x = gen_unary (NOT, result_mode, result_mode, x);
if (outer_op != NIL)
{
if (GET_MODE_BITSIZE (result_mode) < HOST_BITS_PER_WIDE_INT)
{
int width = GET_MODE_BITSIZE (result_mode);
outer_const &= GET_MODE_MASK (result_mode);
/* 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
&& (outer_const & ((HOST_WIDE_INT) 1 << (width - 1))))
outer_const |= ((HOST_WIDE_INT) (-1) << width);
}
if (outer_op == AND)
x = simplify_and_const_int (NULL_RTX, result_mode, x, outer_const);
else if (outer_op == SET)
/* This means that we have determined that the result is
equivalent to a constant. This should be rare. */
x = GEN_INT (outer_const);
else if (GET_RTX_CLASS (outer_op) == '1')
x = gen_unary (outer_op, result_mode, result_mode, x);
else
x = gen_binary (outer_op, result_mode, x, GEN_INT (outer_const));
}
return x;
}
/* Like recog, but we receive the address of a pointer to a new pattern.
We try to match the rtx that the pointer points to.
If that fails, we may try to modify or replace the pattern,
storing the replacement into the same pointer object.
Modifications include deletion or addition of CLOBBERs.
PNOTES is a pointer to a location where any REG_UNUSED notes added for
the CLOBBERs are placed.
The value is the final insn code from the pattern ultimately matched,
or -1. */
static int
recog_for_combine (pnewpat, insn, pnotes)
rtx *pnewpat;
rtx insn;
rtx *pnotes;
{
register rtx pat = *pnewpat;
int insn_code_number;
int num_clobbers_to_add = 0;
int i;
rtx notes = 0;
/* If PAT is a PARALLEL, check to see if it contains the CLOBBER
we use to indicate that something didn't match. If we find such a
thing, force rejection. */
if (GET_CODE (pat) == PARALLEL)
for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER
&& XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx)
return -1;
/* Is the result of combination a valid instruction? */
insn_code_number = recog (pat, insn, &num_clobbers_to_add);
/* If it isn't, there is the possibility that we previously had an insn
that clobbered some register as a side effect, but the combined
insn doesn't need to do that. So try once more without the clobbers
unless this represents an ASM insn. */
if (insn_code_number < 0 && ! check_asm_operands (pat)
&& GET_CODE (pat) == PARALLEL)
{
int pos;
for (pos = 0, i = 0; i < XVECLEN (pat, 0); i++)
if (GET_CODE (XVECEXP (pat, 0, i)) != CLOBBER)
{
if (i != pos)
SUBST (XVECEXP (pat, 0, pos), XVECEXP (pat, 0, i));
pos++;
}
SUBST_INT (XVECLEN (pat, 0), pos);
if (pos == 1)
pat = XVECEXP (pat, 0, 0);
insn_code_number = recog (pat, insn, &num_clobbers_to_add);
}
/* If we had any clobbers to add, make a new pattern than contains
them. Then check to make sure that all of them are dead. */
if (num_clobbers_to_add)
{
rtx newpat = gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (GET_CODE (pat) == PARALLEL
? XVECLEN (pat, 0) + num_clobbers_to_add
: num_clobbers_to_add + 1));
if (GET_CODE (pat) == PARALLEL)
for (i = 0; i < XVECLEN (pat, 0); i++)
XVECEXP (newpat, 0, i) = XVECEXP (pat, 0, i);
else
XVECEXP (newpat, 0, 0) = pat;
add_clobbers (newpat, insn_code_number);
for (i = XVECLEN (newpat, 0) - num_clobbers_to_add;
i < XVECLEN (newpat, 0); i++)
{
if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) == REG
&& ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn))
return -1;
notes = gen_rtx_EXPR_LIST (REG_UNUSED,
XEXP (XVECEXP (newpat, 0, i), 0), notes);
}
pat = newpat;
}
*pnewpat = pat;
*pnotes = notes;
return insn_code_number;
}
/* Like gen_lowpart but for use by combine. In combine it is not possible
to create any new pseudoregs. However, it is safe to create
invalid memory addresses, because combine will try to recognize
them and all they will do is make the combine attempt fail.
If for some reason this cannot do its job, an rtx
(clobber (const_int 0)) is returned.
An insn containing that will not be recognized. */
#undef gen_lowpart
static rtx
gen_lowpart_for_combine (mode, x)
enum machine_mode mode;
register rtx x;
{
rtx result;
if (GET_MODE (x) == mode)
return x;
/* We can only support MODE being wider than a word if X is a
constant integer or has a mode the same size. */
if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
&& ! ((GET_MODE (x) == VOIDmode
&& (GET_CODE (x) == CONST_INT
|| GET_CODE (x) == CONST_DOUBLE))
|| GET_MODE_SIZE (GET_MODE (x)) == GET_MODE_SIZE (mode)))
return gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
/* X might be a paradoxical (subreg (mem)). In that case, gen_lowpart
won't know what to do. So we will strip off the SUBREG here and
process normally. */
if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM)
{
x = SUBREG_REG (x);
if (GET_MODE (x) == mode)
return x;
}
result = gen_lowpart_common (mode, x);
if (result != 0
&& GET_CODE (result) == SUBREG
&& GET_CODE (SUBREG_REG (result)) == REG
&& REGNO (SUBREG_REG (result)) >= FIRST_PSEUDO_REGISTER
&& (GET_MODE_SIZE (GET_MODE (result))
!= GET_MODE_SIZE (GET_MODE (SUBREG_REG (result)))))
REG_CHANGES_SIZE (REGNO (SUBREG_REG (result))) = 1;
if (result)
return result;
if (GET_CODE (x) == MEM)
{
register int offset = 0;
rtx new;
/* Refuse to work on a volatile memory ref or one with a mode-dependent
address. */
if (MEM_VOLATILE_P (x) || mode_dependent_address_p (XEXP (x, 0)))
return gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
/* If we want to refer to something bigger than the original memref,
generate a perverse subreg instead. That will force a reload
of the original memref X. */
if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode))
return gen_rtx_SUBREG (mode, x, 0);
if (WORDS_BIG_ENDIAN)
offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
if (BYTES_BIG_ENDIAN)
{
/* Adjust the address so that the address-after-the-data is
unchanged. */
offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
- MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
}
new = gen_rtx_MEM (mode, plus_constant (XEXP (x, 0), offset));
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
MEM_COPY_ATTRIBUTES (new, x);
return new;
}
/* If X is a comparison operator, rewrite it in a new mode. This
probably won't match, but may allow further simplifications. */
else if (GET_RTX_CLASS (GET_CODE (x)) == '<')
return gen_rtx_combine (GET_CODE (x), mode, XEXP (x, 0), XEXP (x, 1));
/* If we couldn't simplify X any other way, just enclose it in a
SUBREG. Normally, this SUBREG won't match, but some patterns may
include an explicit SUBREG or we may simplify it further in combine. */
else
{
int word = 0;
if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
word = ((GET_MODE_SIZE (GET_MODE (x))
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
/ UNITS_PER_WORD);
return gen_rtx_SUBREG (mode, x, word);
}
}
/* Make an rtx expression. This is a subset of gen_rtx and only supports
expressions of 1, 2, or 3 operands, each of which are rtx expressions.
If the identical expression was previously in the insn (in the undobuf),
it will be returned. Only if it is not found will a new expression
be made. */
/*VARARGS2*/
static rtx
gen_rtx_combine VPROTO((enum rtx_code code, enum machine_mode mode, ...))
{
#ifndef ANSI_PROTOTYPES
enum rtx_code code;
enum machine_mode mode;
#endif
va_list p;
int n_args;
rtx args[3];
int j;
char *fmt;
rtx rt;
struct undo *undo;
VA_START (p, mode);
#ifndef ANSI_PROTOTYPES
code = va_arg (p, enum rtx_code);
mode = va_arg (p, enum machine_mode);
#endif
n_args = GET_RTX_LENGTH (code);
fmt = GET_RTX_FORMAT (code);
if (n_args == 0 || n_args > 3)
abort ();
/* Get each arg and verify that it is supposed to be an expression. */
for (j = 0; j < n_args; j++)
{
if (*fmt++ != 'e')
abort ();
args[j] = va_arg (p, rtx);
}
/* See if this is in undobuf. Be sure we don't use objects that came
from another insn; this could produce circular rtl structures. */
for (undo = undobuf.undos; undo != undobuf.previous_undos; undo = undo->next)
if (!undo->is_int
&& GET_CODE (undo->old_contents.r) == code
&& GET_MODE (undo->old_contents.r) == mode)
{
for (j = 0; j < n_args; j++)
if (XEXP (undo->old_contents.r, j) != args[j])
break;
if (j == n_args)
return undo->old_contents.r;
}
/* Otherwise make a new rtx. We know we have 1, 2, or 3 args.
Use rtx_alloc instead of gen_rtx because it's faster on RISC. */
rt = rtx_alloc (code);
PUT_MODE (rt, mode);
XEXP (rt, 0) = args[0];
if (n_args > 1)
{
XEXP (rt, 1) = args[1];
if (n_args > 2)
XEXP (rt, 2) = args[2];
}
return rt;
}
/* These routines make binary and unary operations by first seeing if they
fold; if not, a new expression is allocated. */
static rtx
gen_binary (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
rtx result;
rtx tem;
if (GET_RTX_CLASS (code) == 'c'
&& (GET_CODE (op0) == CONST_INT
|| (CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)))
tem = op0, op0 = op1, op1 = tem;
if (GET_RTX_CLASS (code) == '<')
{
enum machine_mode op_mode = GET_MODE (op0);
/* Strip the COMPARE from (REL_OP (compare X Y) 0) to get
just (REL_OP X Y). */
if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
{
op1 = XEXP (op0, 1);
op0 = XEXP (op0, 0);
op_mode = GET_MODE (op0);
}
if (op_mode == VOIDmode)
op_mode = GET_MODE (op1);
result = simplify_relational_operation (code, op_mode, op0, op1);
}
else
result = simplify_binary_operation (code, mode, op0, op1);
if (result)
return result;
/* Put complex operands first and constants second. */
if (GET_RTX_CLASS (code) == 'c'
&& ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
|| (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
&& GET_RTX_CLASS (GET_CODE (op1)) != 'o')
|| (GET_CODE (op0) == SUBREG
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
&& GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
return gen_rtx_combine (code, mode, op1, op0);
/* If we are turning off bits already known off in OP0, we need not do
an AND. */
else if (code == AND && GET_CODE (op1) == CONST_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (op0, mode) & ~ INTVAL (op1)) == 0)
return op0;
return gen_rtx_combine (code, mode, op0, op1);
}
static rtx
gen_unary (code, mode, op0_mode, op0)
enum rtx_code code;
enum machine_mode mode, op0_mode;
rtx op0;
{
rtx result = simplify_unary_operation (code, mode, op0, op0_mode);
if (result)
return result;
return gen_rtx_combine (code, mode, op0);
}
/* Simplify a comparison between *POP0 and *POP1 where CODE is the
comparison code that will be tested.
The result is a possibly different comparison code to use. *POP0 and
*POP1 may be updated.
It is possible that we might detect that a comparison is either always
true or always false. However, we do not perform general constant
folding in combine, so this knowledge isn't useful. Such tautologies
should have been detected earlier. Hence we ignore all such cases. */
static enum rtx_code
simplify_comparison (code, pop0, pop1)
enum rtx_code code;
rtx *pop0;
rtx *pop1;
{
rtx op0 = *pop0;
rtx op1 = *pop1;
rtx tem, tem1;
int i;
enum machine_mode mode, tmode;
/* Try a few ways of applying the same transformation to both operands. */
while (1)
{
#ifndef WORD_REGISTER_OPERATIONS
/* The test below this one won't handle SIGN_EXTENDs on these machines,
so check specially. */
if (code != GTU && code != GEU && code != LTU && code != LEU
&& GET_CODE (op0) == ASHIFTRT && GET_CODE (op1) == ASHIFTRT
&& GET_CODE (XEXP (op0, 0)) == ASHIFT
&& GET_CODE (XEXP (op1, 0)) == ASHIFT
&& GET_CODE (XEXP (XEXP (op0, 0), 0)) == SUBREG
&& GET_CODE (XEXP (XEXP (op1, 0), 0)) == SUBREG
&& (GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0)))
== GET_MODE (SUBREG_REG (XEXP (XEXP (op1, 0), 0))))
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& GET_CODE (XEXP (op1, 1)) == CONST_INT
&& GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
&& GET_CODE (XEXP (XEXP (op1, 0), 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (op1, 1))
&& INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (XEXP (op0, 0), 1))
&& INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (XEXP (op1, 0), 1))
&& (INTVAL (XEXP (op0, 1))
== (GET_MODE_BITSIZE (GET_MODE (op0))
- (GET_MODE_BITSIZE
(GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0))))))))
{
op0 = SUBREG_REG (XEXP (XEXP (op0, 0), 0));
op1 = SUBREG_REG (XEXP (XEXP (op1, 0), 0));
}
#endif
/* If both operands are the same constant shift, see if we can ignore the
shift. We can if the shift is a rotate or if the bits shifted out of
this shift are known to be zero for both inputs and if the type of
comparison is compatible with the shift. */
if (GET_CODE (op0) == GET_CODE (op1)
&& GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
&& ((GET_CODE (op0) == ROTATE && (code == NE || code == EQ))
|| ((GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFT)
&& (code != GT && code != LT && code != GE && code != LE))
|| (GET_CODE (op0) == ASHIFTRT
&& (code != GTU && code != LTU
&& code != GEU && code != GEU)))
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) >= 0
&& INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT
&& XEXP (op0, 1) == XEXP (op1, 1))
{
enum machine_mode mode = GET_MODE (op0);
unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
int shift_count = INTVAL (XEXP (op0, 1));
if (GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFTRT)
mask &= (mask >> shift_count) << shift_count;
else if (GET_CODE (op0) == ASHIFT)
mask = (mask & (mask << shift_count)) >> shift_count;
if ((nonzero_bits (XEXP (op0, 0), mode) & ~ mask) == 0
&& (nonzero_bits (XEXP (op1, 0), mode) & ~ mask) == 0)
op0 = XEXP (op0, 0), op1 = XEXP (op1, 0);
else
break;
}
/* If both operands are AND's of a paradoxical SUBREG by constant, the
SUBREGs are of the same mode, and, in both cases, the AND would
be redundant if the comparison was done in the narrower mode,
do the comparison in the narrower mode (e.g., we are AND'ing with 1
and the operand's possibly nonzero bits are 0xffffff01; in that case
if we only care about QImode, we don't need the AND). This case
occurs if the output mode of an scc insn is not SImode and
STORE_FLAG_VALUE == 1 (e.g., the 386).
Similarly, check for a case where the AND's are ZERO_EXTEND
operations from some narrower mode even though a SUBREG is not
present. */
else if (GET_CODE (op0) == AND && GET_CODE (op1) == AND
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& GET_CODE (XEXP (op1, 1)) == CONST_INT)
{
rtx inner_op0 = XEXP (op0, 0);
rtx inner_op1 = XEXP (op1, 0);
HOST_WIDE_INT c0 = INTVAL (XEXP (op0, 1));
HOST_WIDE_INT c1 = INTVAL (XEXP (op1, 1));
int changed = 0;
if (GET_CODE (inner_op0) == SUBREG && GET_CODE (inner_op1) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (inner_op0))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner_op0))))
&& (GET_MODE (SUBREG_REG (inner_op0))
== GET_MODE (SUBREG_REG (inner_op1)))
&& (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (inner_op0)))
<= HOST_BITS_PER_WIDE_INT)
&& (0 == ((~c0) & nonzero_bits (SUBREG_REG (inner_op0),
GET_MODE (SUBREG_REG (inner_op0)))))
&& (0 == ((~c1) & nonzero_bits (SUBREG_REG (inner_op1),
GET_MODE (SUBREG_REG (inner_op1))))))
{
op0 = SUBREG_REG (inner_op0);
op1 = SUBREG_REG (inner_op1);
/* The resulting comparison is always unsigned since we masked
off the original sign bit. */
code = unsigned_condition (code);
changed = 1;
}
else if (c0 == c1)
for (tmode = GET_CLASS_NARROWEST_MODE
(GET_MODE_CLASS (GET_MODE (op0)));
tmode != GET_MODE (op0); tmode = GET_MODE_WIDER_MODE (tmode))
if ((unsigned HOST_WIDE_INT) c0 == GET_MODE_MASK (tmode))
{
op0 = gen_lowpart_for_combine (tmode, inner_op0);
op1 = gen_lowpart_for_combine (tmode, inner_op1);
code = unsigned_condition (code);
changed = 1;
break;
}
if (! changed)
break;
}
/* If both operands are NOT, we can strip off the outer operation
and adjust the comparison code for swapped operands; similarly for
NEG, except that this must be an equality comparison. */
else if ((GET_CODE (op0) == NOT && GET_CODE (op1) == NOT)
|| (GET_CODE (op0) == NEG && GET_CODE (op1) == NEG
&& (code == EQ || code == NE)))
op0 = XEXP (op0, 0), op1 = XEXP (op1, 0), code = swap_condition (code);
else
break;
}
/* If the first operand is a constant, swap the operands and adjust the
comparison code appropriately, but don't do this if the second operand
is already a constant integer. */
if (CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
{
tem = op0, op0 = op1, op1 = tem;
code = swap_condition (code);
}
/* We now enter a loop during which we will try to simplify the comparison.
For the most part, we only are concerned with comparisons with zero,
but some things may really be comparisons with zero but not start
out looking that way. */
while (GET_CODE (op1) == CONST_INT)
{
enum machine_mode mode = GET_MODE (op0);
int mode_width = GET_MODE_BITSIZE (mode);
unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
int equality_comparison_p;
int sign_bit_comparison_p;
int unsigned_comparison_p;
HOST_WIDE_INT const_op;
/* We only want to handle integral modes. This catches VOIDmode,
CCmode, and the floating-point modes. An exception is that we
can handle VOIDmode if OP0 is a COMPARE or a comparison
operation. */
if (GET_MODE_CLASS (mode) != MODE_INT
&& ! (mode == VOIDmode
&& (GET_CODE (op0) == COMPARE
|| GET_RTX_CLASS (GET_CODE (op0)) == '<')))
break;
/* Get the constant we are comparing against and turn off all bits
not on in our mode. */
const_op = INTVAL (op1);
if (mode_width <= HOST_BITS_PER_WIDE_INT)
const_op &= mask;
/* If we are comparing against a constant power of two and the value
being compared can only have that single bit nonzero (e.g., it was
`and'ed with that bit), we can replace this with a comparison
with zero. */
if (const_op
&& (code == EQ || code == NE || code == GE || code == GEU
|| code == LT || code == LTU)
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& exact_log2 (const_op) >= 0
&& nonzero_bits (op0, mode) == (unsigned HOST_WIDE_INT) const_op)
{
code = (code == EQ || code == GE || code == GEU ? NE : EQ);
op1 = const0_rtx, const_op = 0;
}
/* Similarly, if we are comparing a value known to be either -1 or
0 with -1, change it to the opposite comparison against zero. */
if (const_op == -1
&& (code == EQ || code == NE || code == GT || code == LE
|| code == GEU || code == LTU)
&& num_sign_bit_copies (op0, mode) == mode_width)
{
code = (code == EQ || code == LE || code == GEU ? NE : EQ);
op1 = const0_rtx, const_op = 0;
}
/* Do some canonicalizations based on the comparison code. We prefer
comparisons against zero and then prefer equality comparisons.
If we can reduce the size of a constant, we will do that too. */
switch (code)
{
case LT:
/* < C is equivalent to <= (C - 1) */
if (const_op > 0)
{
const_op -= 1;
op1 = GEN_INT (const_op);
code = LE;
/* ... fall through to LE case below. */
}
else
break;
case LE:
/* <= C is equivalent to < (C + 1); we do this for C < 0 */
if (const_op < 0)
{
const_op += 1;
op1 = GEN_INT (const_op);
code = LT;
}
/* If we are doing a <= 0 comparison on a value known to have
a zero sign bit, we can replace this with == 0. */
else if (const_op == 0
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (op0, mode)
& ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)
code = EQ;
break;
case GE:
/* >= C is equivalent to > (C - 1). */
if (const_op > 0)
{
const_op -= 1;
op1 = GEN_INT (const_op);
code = GT;
/* ... fall through to GT below. */
}
else
break;
case GT:
/* > C is equivalent to >= (C + 1); we do this for C < 0*/
if (const_op < 0)
{
const_op += 1;
op1 = GEN_INT (const_op);
code = GE;
}
/* If we are doing a > 0 comparison on a value known to have
a zero sign bit, we can replace this with != 0. */
else if (const_op == 0
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (op0, mode)
& ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)
code = NE;
break;
case LTU:
/* < C is equivalent to <= (C - 1). */
if (const_op > 0)
{
const_op -= 1;
op1 = GEN_INT (const_op);
code = LEU;
/* ... fall through ... */
}
/* (unsigned) < 0x80000000 is equivalent to >= 0. */
else if ((mode_width <= HOST_BITS_PER_WIDE_INT)
&& (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1)))
{
const_op = 0, op1 = const0_rtx;
code = GE;
break;
}
else
break;
case LEU:
/* unsigned <= 0 is equivalent to == 0 */
if (const_op == 0)
code = EQ;
/* (unsigned) <= 0x7fffffff is equivalent to >= 0. */
else if ((mode_width <= HOST_BITS_PER_WIDE_INT)
&& (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1))
{
const_op = 0, op1 = const0_rtx;
code = GE;
}
break;
case GEU:
/* >= C is equivalent to < (C - 1). */
if (const_op > 1)
{
const_op -= 1;
op1 = GEN_INT (const_op);
code = GTU;
/* ... fall through ... */
}
/* (unsigned) >= 0x80000000 is equivalent to < 0. */
else if ((mode_width <= HOST_BITS_PER_WIDE_INT)
&& (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1)))
{
const_op = 0, op1 = const0_rtx;
code = LT;
break;
}
else
break;
case GTU:
/* unsigned > 0 is equivalent to != 0 */
if (const_op == 0)
code = NE;
/* (unsigned) > 0x7fffffff is equivalent to < 0. */
else if ((mode_width <= HOST_BITS_PER_WIDE_INT)
&& (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1))
{
const_op = 0, op1 = const0_rtx;
code = LT;
}
break;
default:
break;
}
/* Compute some predicates to simplify code below. */
equality_comparison_p = (code == EQ || code == NE);
sign_bit_comparison_p = ((code == LT || code == GE) && const_op == 0);
unsigned_comparison_p = (code == LTU || code == LEU || code == GTU
|| code == LEU);
/* If this is a sign bit comparison and we can do arithmetic in
MODE, say that we will only be needing the sign bit of OP0. */
if (sign_bit_comparison_p
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
op0 = force_to_mode (op0, mode,
((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (mode) - 1)),
NULL_RTX, 0);
/* Now try cases based on the opcode of OP0. If none of the cases
does a "continue", we exit this loop immediately after the
switch. */
switch (GET_CODE (op0))
{
case ZERO_EXTRACT:
/* If we are extracting a single bit from a variable position in
a constant that has only a single bit set and are comparing it
with zero, we can convert this into an equality comparison
between the position and the location of the single bit. */
if (GET_CODE (XEXP (op0, 0)) == CONST_INT
&& XEXP (op0, 1) == const1_rtx
&& equality_comparison_p && const_op == 0
&& (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0)
{
if (BITS_BIG_ENDIAN)
{
#ifdef HAVE_extzv
mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
if (mode == VOIDmode)
mode = word_mode;
i = (GET_MODE_BITSIZE (mode) - 1 - i);
#else
i = BITS_PER_WORD - 1 - i;
#endif
}
op0 = XEXP (op0, 2);
op1 = GEN_INT (i);
const_op = i;
/* Result is nonzero iff shift count is equal to I. */
code = reverse_condition (code);
continue;
}
/* ... fall through ... */
case SIGN_EXTRACT:
tem = expand_compound_operation (op0);
if (tem != op0)
{
op0 = tem;
continue;
}
break;
case NOT:
/* If testing for equality, we can take the NOT of the constant. */
if (equality_comparison_p
&& (tem = simplify_unary_operation (NOT, mode, op1, mode)) != 0)
{
op0 = XEXP (op0, 0);
op1 = tem;
continue;
}
/* If just looking at the sign bit, reverse the sense of the
comparison. */
if (sign_bit_comparison_p)
{
op0 = XEXP (op0, 0);
code = (code == GE ? LT : GE);
continue;
}
break;
case NEG:
/* If testing for equality, we can take the NEG of the constant. */
if (equality_comparison_p
&& (tem = simplify_unary_operation (NEG, mode, op1, mode)) != 0)
{
op0 = XEXP (op0, 0);
op1 = tem;
continue;
}
/* The remaining cases only apply to comparisons with zero. */
if (const_op != 0)
break;
/* When X is ABS or is known positive,
(neg X) is < 0 if and only if X != 0. */
if (sign_bit_comparison_p
&& (GET_CODE (XEXP (op0, 0)) == ABS
|| (mode_width <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (XEXP (op0, 0), mode)
& ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)))
{
op0 = XEXP (op0, 0);
code = (code == LT ? NE : EQ);
continue;
}
/* If we have NEG of something whose two high-order bits are the
same, we know that "(-a) < 0" is equivalent to "a > 0". */
if (num_sign_bit_copies (op0, mode) >= 2)
{
op0 = XEXP (op0, 0);
code = swap_condition (code);
continue;
}
break;
case ROTATE:
/* If we are testing equality and our count is a constant, we
can perform the inverse operation on our RHS. */
if (equality_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT
&& (tem = simplify_binary_operation (ROTATERT, mode,
op1, XEXP (op0, 1))) != 0)
{
op0 = XEXP (op0, 0);
op1 = tem;
continue;
}
/* If we are doing a < 0 or >= 0 comparison, it means we are testing
a particular bit. Convert it to an AND of a constant of that
bit. This will be converted into a ZERO_EXTRACT. */
if (const_op == 0 && sign_bit_comparison_p
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT)
{
op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0),
((HOST_WIDE_INT) 1
<< (mode_width - 1
- INTVAL (XEXP (op0, 1)))));
code = (code == LT ? NE : EQ);
continue;
}
/* ... fall through ... */
case ABS:
/* ABS is ignorable inside an equality comparison with zero. */
if (const_op == 0 && equality_comparison_p)
{
op0 = XEXP (op0, 0);
continue;
}
break;
case SIGN_EXTEND:
/* Can simplify (compare (zero/sign_extend FOO) CONST)
to (compare FOO CONST) if CONST fits in FOO's mode and we
are either testing inequality or have an unsigned comparison
with ZERO_EXTEND or a signed comparison with SIGN_EXTEND. */
if (! unsigned_comparison_p
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
<= HOST_BITS_PER_WIDE_INT)
&& ((unsigned HOST_WIDE_INT) const_op
< (((unsigned HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - 1)))))
{
op0 = XEXP (op0, 0);
continue;
}
break;
case SUBREG:
/* Check for the case where we are comparing A - C1 with C2,
both constants are smaller than 1/2 the maximum positive
value in MODE, and the comparison is equality or unsigned.
In that case, if A is either zero-extended to MODE or has
sufficient sign bits so that the high-order bit in MODE
is a copy of the sign in the inner mode, we can prove that it is
safe to do the operation in the wider mode. This simplifies
many range checks. */
if (mode_width <= HOST_BITS_PER_WIDE_INT
&& subreg_lowpart_p (op0)
&& GET_CODE (SUBREG_REG (op0)) == PLUS
&& GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT
&& INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0
&& (- INTVAL (XEXP (SUBREG_REG (op0), 1))
< (HOST_WIDE_INT)(GET_MODE_MASK (mode) / 2))
&& (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2
&& (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0),
GET_MODE (SUBREG_REG (op0)))
& ~ GET_MODE_MASK (mode))
|| (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0),
GET_MODE (SUBREG_REG (op0)))
> (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
- GET_MODE_BITSIZE (mode)))))
{
op0 = SUBREG_REG (op0);
continue;
}
/* If the inner mode is narrower and we are extracting the low part,
we can treat the SUBREG as if it were a ZERO_EXTEND. */
if (subreg_lowpart_p (op0)
&& GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) < mode_width)
/* Fall through */ ;
else
break;
/* ... fall through ... */
case ZERO_EXTEND:
if ((unsigned_comparison_p || equality_comparison_p)
&& (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
<= HOST_BITS_PER_WIDE_INT)
&& ((unsigned HOST_WIDE_INT) const_op
< GET_MODE_MASK (GET_MODE (XEXP (op0, 0)))))
{
op0 = XEXP (op0, 0);
continue;
}
break;
case PLUS:
/* (eq (plus X A) B) -> (eq X (minus B A)). We can only do
this for equality comparisons due to pathological cases involving
overflows. */
if (equality_comparison_p
&& 0 != (tem = simplify_binary_operation (MINUS, mode,
op1, XEXP (op0, 1))))
{
op0 = XEXP (op0, 0);
op1 = tem;
continue;
}
/* (plus (abs X) (const_int -1)) is < 0 if and only if X == 0. */
if (const_op == 0 && XEXP (op0, 1) == constm1_rtx
&& GET_CODE (XEXP (op0, 0)) == ABS && sign_bit_comparison_p)
{
op0 = XEXP (XEXP (op0, 0), 0);
code = (code == LT ? EQ : NE);
continue;
}
break;
case MINUS:
/* (eq (minus A B) C) -> (eq A (plus B C)) or
(eq B (minus A C)), whichever simplifies. We can only do
this for equality comparisons due to pathological cases involving
overflows. */
if (equality_comparison_p
&& 0 != (tem = simplify_binary_operation (PLUS, mode,
XEXP (op0, 1), op1)))
{
op0 = XEXP (op0, 0);
op1 = tem;
continue;
}
if (equality_comparison_p
&& 0 != (tem = simplify_binary_operation (MINUS, mode,
XEXP (op0, 0), op1)))
{
op0 = XEXP (op0, 1);
op1 = tem;
continue;
}
/* The sign bit of (minus (ashiftrt X C) X), where C is the number
of bits in X minus 1, is one iff X > 0. */
if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == ASHIFTRT
&& GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (op0, 0), 1)) == mode_width - 1
&& rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1)))
{
op0 = XEXP (op0, 1);
code = (code == GE ? LE : GT);
continue;
}
break;
case XOR:
/* (eq (xor A B) C) -> (eq A (xor B C)). This is a simplification
if C is zero or B is a constant. */
if (equality_comparison_p
&& 0 != (tem = simplify_binary_operation (XOR, mode,
XEXP (op0, 1), op1)))
{
op0 = XEXP (op0, 0);
op1 = tem;
continue;
}
break;
case EQ: case NE:
case LT: case LTU: case LE: case LEU:
case GT: case GTU: case GE: case GEU:
/* We can't do anything if OP0 is a condition code value, rather
than an actual data value. */
if (const_op != 0
#ifdef HAVE_cc0
|| XEXP (op0, 0) == cc0_rtx
#endif
|| GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC)
break;
/* Get the two operands being compared. */
if (GET_CODE (XEXP (op0, 0)) == COMPARE)
tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1);
else
tem = XEXP (op0, 0), tem1 = XEXP (op0, 1);
/* Check for the cases where we simply want the result of the
earlier test or the opposite of that result. */
if (code == NE
|| (code == EQ && reversible_comparison_p (op0))
|| (GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
&& (STORE_FLAG_VALUE
& (((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (op0)) - 1))))
&& (code == LT
|| (code == GE && reversible_comparison_p (op0)))))
{
code = (code == LT || code == NE
? GET_CODE (op0) : reverse_condition (GET_CODE (op0)));
op0 = tem, op1 = tem1;
continue;
}
break;
case IOR:
/* The sign bit of (ior (plus X (const_int -1)) X) is non-zero
iff X <= 0. */
if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == PLUS
&& XEXP (XEXP (op0, 0), 1) == constm1_rtx
&& rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1)))
{
op0 = XEXP (op0, 1);
code = (code == GE ? GT : LE);
continue;
}
break;
case AND:
/* Convert (and (xshift 1 X) Y) to (and (lshiftrt Y X) 1). This
will be converted to a ZERO_EXTRACT later. */
if (const_op == 0 && equality_comparison_p
&& GET_CODE (XEXP (op0, 0)) == ASHIFT
&& XEXP (XEXP (op0, 0), 0) == const1_rtx)
{
op0 = simplify_and_const_int
(op0, mode, gen_rtx_combine (LSHIFTRT, mode,
XEXP (op0, 1),
XEXP (XEXP (op0, 0), 1)),
(HOST_WIDE_INT) 1);
continue;
}
/* If we are comparing (and (lshiftrt X C1) C2) for equality with
zero and X is a comparison and C1 and C2 describe only bits set
in STORE_FLAG_VALUE, we can compare with X. */
if (const_op == 0 && equality_comparison_p
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& GET_CODE (XEXP (op0, 0)) == LSHIFTRT
&& GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
&& INTVAL (XEXP (XEXP (op0, 0), 1)) >= 0
&& INTVAL (XEXP (XEXP (op0, 0), 1)) < HOST_BITS_PER_WIDE_INT)
{
mask = ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode))
<< INTVAL (XEXP (XEXP (op0, 0), 1)));
if ((~ STORE_FLAG_VALUE & mask) == 0
&& (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (op0, 0), 0))) == '<'
|| ((tem = get_last_value (XEXP (XEXP (op0, 0), 0))) != 0
&& GET_RTX_CLASS (GET_CODE (tem)) == '<')))
{
op0 = XEXP (XEXP (op0, 0), 0);
continue;
}
}
/* If we are doing an equality comparison of an AND of a bit equal
to the sign bit, replace this with a LT or GE comparison of
the underlying value. */
if (equality_comparison_p
&& const_op == 0
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode))
== (unsigned HOST_WIDE_INT) 1 << (mode_width - 1)))
{
op0 = XEXP (op0, 0);
code = (code == EQ ? GE : LT);
continue;
}
/* If this AND operation is really a ZERO_EXTEND from a narrower
mode, the constant fits within that mode, and this is either an
equality or unsigned comparison, try to do this comparison in
the narrower mode. */
if ((equality_comparison_p || unsigned_comparison_p)
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& (i = exact_log2 ((INTVAL (XEXP (op0, 1))
& GET_MODE_MASK (mode))
+ 1)) >= 0
&& const_op >> i == 0
&& (tmode = mode_for_size (i, MODE_INT, 1)) != BLKmode)
{
op0 = gen_lowpart_for_combine (tmode, XEXP (op0, 0));
continue;
}
/* If this is (and:M1 (subreg:M2 X 0) (const_int C1)) where C1 fits
in both M1 and M2 and the SUBREG is either paradoxical or
represents the low part, permute the SUBREG and the AND and
try again. */
if (GET_CODE (XEXP (op0, 0)) == SUBREG
&& ((mode_width
>= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))))
#ifdef WORD_REGISTER_OPERATIONS
|| subreg_lowpart_p (XEXP (op0, 0))
#endif
)
#ifndef WORD_REGISTER_OPERATIONS
/* It is unsafe to commute the AND into the SUBREG if the SUBREG
is paradoxical and WORD_REGISTER_OPERATIONS is not defined.
As originally written the upper bits have a defined value
due to the AND operation. However, if we commute the AND
inside the SUBREG then they no longer have defined values
and the meaning of the code has been changed. */
&& (GET_MODE_SIZE (GET_MODE (XEXP (op0, 0)))
<= GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))))
#endif
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0))))
<= HOST_BITS_PER_WIDE_INT)
&& (INTVAL (XEXP (op0, 1)) & ~ mask) == 0
&& 0 == (~ GET_MODE_MASK (GET_MODE (SUBREG_REG (XEXP (op0, 0))))
& INTVAL (XEXP (op0, 1)))
&& (unsigned HOST_WIDE_INT) INTVAL (XEXP (op0, 1)) != mask
&& ((unsigned HOST_WIDE_INT) INTVAL (XEXP (op0, 1))
!= GET_MODE_MASK (GET_MODE (SUBREG_REG (XEXP (op0, 0))))))
{
op0
= gen_lowpart_for_combine
(mode,
gen_binary (AND, GET_MODE (SUBREG_REG (XEXP (op0, 0))),
SUBREG_REG (XEXP (op0, 0)), XEXP (op0, 1)));
continue;
}
break;
case ASHIFT:
/* If we have (compare (ashift FOO N) (const_int C)) and
the high order N bits of FOO (N+1 if an inequality comparison)
are known to be zero, we can do this by comparing FOO with C
shifted right N bits so long as the low-order N bits of C are
zero. */
if (GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) >= 0
&& ((INTVAL (XEXP (op0, 1)) + ! equality_comparison_p)
< HOST_BITS_PER_WIDE_INT)
&& ((const_op
& (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0)
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (XEXP (op0, 0), mode)
& ~ (mask >> (INTVAL (XEXP (op0, 1))
+ ! equality_comparison_p))) == 0)
{
const_op >>= INTVAL (XEXP (op0, 1));
op1 = GEN_INT (const_op);
op0 = XEXP (op0, 0);
continue;
}
/* If we are doing a sign bit comparison, it means we are testing
a particular bit. Convert it to the appropriate AND. */
if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT)
{
op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0),
((HOST_WIDE_INT) 1
<< (mode_width - 1
- INTVAL (XEXP (op0, 1)))));
code = (code == LT ? NE : EQ);
continue;
}
/* If this an equality comparison with zero and we are shifting
the low bit to the sign bit, we can convert this to an AND of the
low-order bit. */
if (const_op == 0 && equality_comparison_p
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) == mode_width - 1)
{
op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0),
(HOST_WIDE_INT) 1);
continue;
}
break;
case ASHIFTRT:
/* If this is an equality comparison with zero, we can do this
as a logical shift, which might be much simpler. */
if (equality_comparison_p && const_op == 0
&& GET_CODE (XEXP (op0, 1)) == CONST_INT)
{
op0 = simplify_shift_const (NULL_RTX, LSHIFTRT, mode,
XEXP (op0, 0),
INTVAL (XEXP (op0, 1)));
continue;
}
/* If OP0 is a sign extension and CODE is not an unsigned comparison,
do the comparison in a narrower mode. */
if (! unsigned_comparison_p
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& GET_CODE (XEXP (op0, 0)) == ASHIFT
&& XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1)
&& (tmode = mode_for_size (mode_width - INTVAL (XEXP (op0, 1)),
MODE_INT, 1)) != BLKmode
&& ((unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (tmode)
|| ((unsigned HOST_WIDE_INT) - const_op
<= GET_MODE_MASK (tmode))))
{
op0 = gen_lowpart_for_combine (tmode, XEXP (XEXP (op0, 0), 0));
continue;
}
/* ... fall through ... */
case LSHIFTRT:
/* If we have (compare (xshiftrt FOO N) (const_int C)) and
the low order N bits of FOO are known to be zero, we can do this
by comparing FOO with C shifted left N bits so long as no
overflow occurs. */
if (GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) >= 0
&& INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT
&& mode_width <= HOST_BITS_PER_WIDE_INT
&& (nonzero_bits (XEXP (op0, 0), mode)
& (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0
&& (const_op == 0
|| (floor_log2 (const_op) + INTVAL (XEXP (op0, 1))
< mode_width)))
{
const_op <<= INTVAL (XEXP (op0, 1));
op1 = GEN_INT (const_op);
op0 = XEXP (op0, 0);
continue;
}
/* If we are using this shift to extract just the sign bit, we
can replace this with an LT or GE comparison. */
if (const_op == 0
&& (equality_comparison_p || sign_bit_comparison_p)
&& GET_CODE (XEXP (op0, 1)) == CONST_INT
&& INTVAL (XEXP (op0, 1)) == mode_width - 1)
{
op0 = XEXP (op0, 0);
code = (code == NE || code == GT ? LT : GE);
continue;
}
break;
default:
break;
}
break;
}
/* Now make any compound operations involved in this comparison. Then,
check for an outmost SUBREG on OP0 that is not doing anything or is
paradoxical. The latter case can only occur when it is known that the
"extra" bits will be zero. Therefore, it is safe to remove the SUBREG.
We can never remove a SUBREG for a non-equality comparison because the
sign bit is in a different place in the underlying object. */
op0 = make_compound_operation (op0, op1 == const0_rtx ? COMPARE : SET);
op1 = make_compound_operation (op1, SET);
if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0)
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
&& (code == NE || code == EQ)
&& ((GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))))
{
op0 = SUBREG_REG (op0);
op1 = gen_lowpart_for_combine (GET_MODE (op0), op1);
}
else if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0)
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
&& (code == NE || code == EQ)
&& (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
<= HOST_BITS_PER_WIDE_INT)
&& (nonzero_bits (SUBREG_REG (op0), GET_MODE (SUBREG_REG (op0)))
& ~ GET_MODE_MASK (GET_MODE (op0))) == 0
&& (tem = gen_lowpart_for_combine (GET_MODE (SUBREG_REG (op0)),
op1),
(nonzero_bits (tem, GET_MODE (SUBREG_REG (op0)))
& ~ GET_MODE_MASK (GET_MODE (op0))) == 0))
op0 = SUBREG_REG (op0), op1 = tem;
/* We now do the opposite procedure: Some machines don't have compare
insns in all modes. If OP0's mode is an integer mode smaller than a
word and we can't do a compare in that mode, see if there is a larger
mode for which we can do the compare. There are a number of cases in
which we can use the wider mode. */
mode = GET_MODE (op0);
if (mode != VOIDmode && GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD
&& cmp_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
for (tmode = GET_MODE_WIDER_MODE (mode);
(tmode != VOIDmode
&& GET_MODE_BITSIZE (tmode) <= HOST_BITS_PER_WIDE_INT);
tmode = GET_MODE_WIDER_MODE (tmode))
if (cmp_optab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
{
/* If the only nonzero bits in OP0 and OP1 are those in the
narrower mode and this is an equality or unsigned comparison,
we can use the wider mode. Similarly for sign-extended
values, in which case it is true for all comparisons. */
if (((code == EQ || code == NE
|| code == GEU || code == GTU || code == LEU || code == LTU)
&& (nonzero_bits (op0, tmode) & ~ GET_MODE_MASK (mode)) == 0
&& (nonzero_bits (op1, tmode) & ~ GET_MODE_MASK (mode)) == 0)
|| ((num_sign_bit_copies (op0, tmode)
> GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode))
&& (num_sign_bit_copies (op1, tmode)
> GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode))))
{
op0 = gen_lowpart_for_combine (tmode, op0);
op1 = gen_lowpart_for_combine (tmode, op1);
break;
}
/* If this is a test for negative, we can make an explicit
test of the sign bit. */
if (op1 == const0_rtx && (code == LT || code == GE)
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
{
op0 = gen_binary (AND, tmode,
gen_lowpart_for_combine (tmode, op0),
GEN_INT ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (mode) - 1)));
code = (code == LT) ? NE : EQ;
break;
}
}
#ifdef CANONICALIZE_COMPARISON
/* If this machine only supports a subset of valid comparisons, see if we
can convert an unsupported one into a supported one. */
CANONICALIZE_COMPARISON (code, op0, op1);
#endif
*pop0 = op0;
*pop1 = op1;
return code;
}
/* Return 1 if we know that X, a comparison operation, is not operating
on a floating-point value or is EQ or NE, meaning that we can safely
reverse it. */
static int
reversible_comparison_p (x)
rtx x;
{
if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| flag_fast_math
|| GET_CODE (x) == NE || GET_CODE (x) == EQ)
return 1;
switch (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))))
{
case MODE_INT:
case MODE_PARTIAL_INT:
case MODE_COMPLEX_INT:
return 1;
case MODE_CC:
/* If the mode of the condition codes tells us that this is safe,
we need look no further. */
if (REVERSIBLE_CC_MODE (GET_MODE (XEXP (x, 0))))
return 1;
/* Otherwise try and find where the condition codes were last set and
use that. */
x = get_last_value (XEXP (x, 0));
return (x && GET_CODE (x) == COMPARE
&& ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0))));
default:
return 0;
}
}
/* Utility function for following routine. Called when X is part of a value
being stored into reg_last_set_value. Sets reg_last_set_table_tick
for each register mentioned. Similar to mention_regs in cse.c */
static void
update_table_tick (x)
rtx x;
{
register enum rtx_code code = GET_CODE (x);
register char *fmt = GET_RTX_FORMAT (code);
register int i;
if (code == REG)
{
int regno = REGNO (x);
int endregno = regno + (regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
for (i = regno; i < endregno; i++)
reg_last_set_table_tick[i] = label_tick;
return;
}
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
/* Note that we can't have an "E" in values stored; see
get_last_value_validate. */
if (fmt[i] == 'e')
update_table_tick (XEXP (x, i));
}
/* Record that REG is set to VALUE in insn INSN. If VALUE is zero, we
are saying that the register is clobbered and we no longer know its
value. If INSN is zero, don't update reg_last_set; this is only permitted
with VALUE also zero and is used to invalidate the register. */
static void
record_value_for_reg (reg, insn, value)
rtx reg;
rtx insn;
rtx value;
{
int regno = REGNO (reg);
int endregno = regno + (regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (regno, GET_MODE (reg)) : 1);
int i;
/* If VALUE contains REG and we have a previous value for REG, substitute
the previous value. */
if (value && insn && reg_overlap_mentioned_p (reg, value))
{
rtx tem;
/* Set things up so get_last_value is allowed to see anything set up to
our insn. */
subst_low_cuid = INSN_CUID (insn);
tem = get_last_value (reg);
if (tem)
value = replace_rtx (copy_rtx (value), reg, tem);
}
/* For each register modified, show we don't know its value, that
we don't know about its bitwise content, that its value has been
updated, and that we don't know the location of the death of the
register. */
for (i = regno; i < endregno; i ++)
{
if (insn)
reg_last_set[i] = insn;
reg_last_set_value[i] = 0;
reg_last_set_mode[i] = 0;
reg_last_set_nonzero_bits[i] = 0;
reg_last_set_sign_bit_copies[i] = 0;
reg_last_death[i] = 0;
}
/* Mark registers that are being referenced in this value. */
if (value)
update_table_tick (value);
/* Now update the status of each register being set.
If someone is using this register in this block, set this register
to invalid since we will get confused between the two lives in this
basic block. This makes using this register always invalid. In cse, we
scan the table to invalidate all entries using this register, but this
is too much work for us. */
for (i = regno; i < endregno; i++)
{
reg_last_set_label[i] = label_tick;
if (value && reg_last_set_table_tick[i] == label_tick)
reg_last_set_invalid[i] = 1;
else
reg_last_set_invalid[i] = 0;
}
/* The value being assigned might refer to X (like in "x++;"). In that
case, we must replace it with (clobber (const_int 0)) to prevent
infinite loops. */
if (value && ! get_last_value_validate (&value, insn,
reg_last_set_label[regno], 0))
{
value = copy_rtx (value);
if (! get_last_value_validate (&value, insn,
reg_last_set_label[regno], 1))
value = 0;
}
/* For the main register being modified, update the value, the mode, the
nonzero bits, and the number of sign bit copies. */
reg_last_set_value[regno] = value;
if (value)
{
subst_low_cuid = INSN_CUID (insn);
reg_last_set_mode[regno] = GET_MODE (reg);
reg_last_set_nonzero_bits[regno] = nonzero_bits (value, GET_MODE (reg));
reg_last_set_sign_bit_copies[regno]
= num_sign_bit_copies (value, GET_MODE (reg));
}
}
/* Used for communication between the following two routines. */
static rtx record_dead_insn;
/* Called via note_stores from record_dead_and_set_regs to handle one
SET or CLOBBER in an insn. */
static void
record_dead_and_set_regs_1 (dest, setter)
rtx dest, setter;
{
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
{
/* If we are setting the whole register, we know its value. Otherwise
show that we don't know the value. We can handle SUBREG in
some cases. */
if (GET_CODE (setter) == SET && dest == SET_DEST (setter))
record_value_for_reg (dest, record_dead_insn, SET_SRC (setter));
else if (GET_CODE (setter) == SET
&& GET_CODE (SET_DEST (setter)) == SUBREG
&& SUBREG_REG (SET_DEST (setter)) == dest
&& GET_MODE_BITSIZE (GET_MODE (dest)) <= BITS_PER_WORD
&& subreg_lowpart_p (SET_DEST (setter)))
record_value_for_reg (dest, record_dead_insn,
gen_lowpart_for_combine (GET_MODE (dest),
SET_SRC (setter)));
else
record_value_for_reg (dest, record_dead_insn, NULL_RTX);
}
else if (GET_CODE (dest) == MEM
/* Ignore pushes, they clobber nothing. */
&& ! push_operand (dest, GET_MODE (dest)))
mem_last_set = INSN_CUID (record_dead_insn);
}
/* Update the records of when each REG was most recently set or killed
for the things done by INSN. This is the last thing done in processing
INSN in the combiner loop.
We update reg_last_set, reg_last_set_value, reg_last_set_mode,
reg_last_set_nonzero_bits, reg_last_set_sign_bit_copies, reg_last_death,
and also the similar information mem_last_set (which insn most recently
modified memory) and last_call_cuid (which insn was the most recent
subroutine call). */
static void
record_dead_and_set_regs (insn)
rtx insn;
{
register rtx link;
int i;
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
{
if (REG_NOTE_KIND (link) == REG_DEAD
&& GET_CODE (XEXP (link, 0)) == REG)
{
int regno = REGNO (XEXP (link, 0));
int endregno
= regno + (regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (regno, GET_MODE (XEXP (link, 0)))
: 1);
for (i = regno; i < endregno; i++)
reg_last_death[i] = insn;
}
else if (REG_NOTE_KIND (link) == REG_INC)
record_value_for_reg (XEXP (link, 0), insn, NULL_RTX);
}
if (GET_CODE (insn) == CALL_INSN)
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i])
{
reg_last_set_value[i] = 0;
reg_last_set_mode[i] = 0;
reg_last_set_nonzero_bits[i] = 0;
reg_last_set_sign_bit_copies[i] = 0;
reg_last_death[i] = 0;
}
last_call_cuid = mem_last_set = INSN_CUID (insn);
}
record_dead_insn = insn;
note_stores (PATTERN (insn), record_dead_and_set_regs_1);
}
/* Utility routine for the following function. Verify that all the registers
mentioned in *LOC are valid when *LOC was part of a value set when
label_tick == TICK. Return 0 if some are not.
If REPLACE is non-zero, replace the invalid reference with
(clobber (const_int 0)) and return 1. This replacement is useful because
we often can get useful information about the form of a value (e.g., if
it was produced by a shift that always produces -1 or 0) even though
we don't know exactly what registers it was produced from. */
static int
get_last_value_validate (loc, insn, tick, replace)
rtx *loc;
rtx insn;
int tick;
int replace;
{
rtx x = *loc;
char *fmt = GET_RTX_FORMAT (GET_CODE (x));
int len = GET_RTX_LENGTH (GET_CODE (x));
int i;
if (GET_CODE (x) == REG)
{
int regno = REGNO (x);
int endregno = regno + (regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
int j;
for (j = regno; j < endregno; j++)
if (reg_last_set_invalid[j]
/* If this is a pseudo-register that was only set once, it is
always valid. */
|| (! (regno >= FIRST_PSEUDO_REGISTER && REG_N_SETS (regno) == 1)
&& reg_last_set_label[j] > tick))
{
if (replace)
*loc = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
return replace;
}
return 1;
}
/* If this is a memory reference, make sure that there were
no stores after it that might have clobbered the value. We don't
have alias info, so we assume any store invalidates it. */
else if (GET_CODE (x) == MEM && ! RTX_UNCHANGING_P (x)
&& INSN_CUID (insn) <= mem_last_set)
{
if (replace)
*loc = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
return replace;
}
for (i = 0; i < len; i++)
if ((fmt[i] == 'e'
&& get_last_value_validate (&XEXP (x, i), insn, tick, replace) == 0)
/* Don't bother with these. They shouldn't occur anyway. */
|| fmt[i] == 'E')
return 0;
/* If we haven't found a reason for it to be invalid, it is valid. */
return 1;
}
/* Get the last value assigned to X, if known. Some registers
in the value may be replaced with (clobber (const_int 0)) if their value
is known longer known reliably. */
static rtx
get_last_value (x)
rtx x;
{
int regno;
rtx value;
/* If this is a non-paradoxical SUBREG, get the value of its operand and
then convert it to the desired mode. If this is a paradoxical SUBREG,
we cannot predict what values the "extra" bits might have. */
if (GET_CODE (x) == SUBREG
&& subreg_lowpart_p (x)
&& (GET_MODE_SIZE (GET_MODE (x))
<= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
&& (value = get_last_value (SUBREG_REG (x))) != 0)
return gen_lowpart_for_combine (GET_MODE (x), value);
if (GET_CODE (x) != REG)
return 0;
regno = REGNO (x);
value = reg_last_set_value[regno];
/* If we don't have a value or if it isn't for this basic block,
return 0. */
if (value == 0
|| (REG_N_SETS (regno) != 1
&& reg_last_set_label[regno] != label_tick))
return 0;
/* If the value was set in a later insn than the ones we are processing,
- we can't use it even if the register was only set once, but make a quick
- check to see if the previous insn set it to something. This is commonly
- the case when the same pseudo is used by repeated insns.
-
- This does not work if there exists an instruction which is temporarily
- not on the insn chain. */
-
+ we can't use it even if the register was only set once. */
if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid)
- {
- rtx insn, set;
-
- /* We can not do anything useful in this case, because there is
- an instruction which is not on the insn chain. */
- if (subst_prev_insn)
- return 0;
-
- /* Skip over USE insns. They are not useful here, and they may have
- been made by combine, in which case they do not have a INSN_CUID
- value. We can't use prev_real_insn, because that would incorrectly
- take us backwards across labels. Skip over BARRIERs also, since
- they could have been made by combine. If we see one, we must be
- optimizing dead code, so it doesn't matter what we do. */
- for (insn = prev_nonnote_insn (subst_insn);
- insn && ((GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == USE)
- || GET_CODE (insn) == BARRIER
- || INSN_CUID (insn) >= subst_low_cuid);
- insn = prev_nonnote_insn (insn))
- ;
-
- if (insn
- && (set = single_set (insn)) != 0
- && rtx_equal_p (SET_DEST (set), x))
- {
- value = SET_SRC (set);
-
- /* Make sure that VALUE doesn't reference X. Replace any
- explicit references with a CLOBBER. If there are any remaining
- references (rare), don't use the value. */
-
- if (reg_mentioned_p (x, value))
- value = replace_rtx (copy_rtx (value), x,
- gen_rtx_CLOBBER (GET_MODE (x), const0_rtx));
-
- if (reg_overlap_mentioned_p (x, value))
- return 0;
- }
- else
- return 0;
- }
+ return 0;
/* If the value has all its registers valid, return it. */
if (get_last_value_validate (&value, reg_last_set[regno],
reg_last_set_label[regno], 0))
return value;
/* Otherwise, make a copy and replace any invalid register with
(clobber (const_int 0)). If that fails for some reason, return 0. */
value = copy_rtx (value);
if (get_last_value_validate (&value, reg_last_set[regno],
reg_last_set_label[regno], 1))
return value;
return 0;
}
/* Return nonzero if expression X refers to a REG or to memory
that is set in an instruction more recent than FROM_CUID. */
static int
use_crosses_set_p (x, from_cuid)
register rtx x;
int from_cuid;
{
register char *fmt;
register int i;
register enum rtx_code code = GET_CODE (x);
if (code == REG)
{
register int regno = REGNO (x);
int endreg = regno + (regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
#ifdef PUSH_ROUNDING
/* Don't allow uses of the stack pointer to be moved,
because we don't know whether the move crosses a push insn. */
if (regno == STACK_POINTER_REGNUM)
return 1;
#endif
for (;regno < endreg; regno++)
if (reg_last_set[regno]
&& INSN_CUID (reg_last_set[regno]) > from_cuid)
return 1;
return 0;
}
if (code == MEM && mem_last_set > from_cuid)
return 1;
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid))
return 1;
}
else if (fmt[i] == 'e'
&& use_crosses_set_p (XEXP (x, i), from_cuid))
return 1;
}
return 0;
}
/* Define three variables used for communication between the following
routines. */
static int reg_dead_regno, reg_dead_endregno;
static int reg_dead_flag;
/* Function called via note_stores from reg_dead_at_p.
If DEST is within [reg_dead_regno, reg_dead_endregno), set
reg_dead_flag to 1 if X is a CLOBBER and to -1 it is a SET. */
static void
reg_dead_at_p_1 (dest, x)
rtx dest;
rtx x;
{
int regno, endregno;
if (GET_CODE (dest) != REG)
return;
regno = REGNO (dest);
endregno = regno + (regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (regno, GET_MODE (dest)) : 1);
if (reg_dead_endregno > regno && reg_dead_regno < endregno)
reg_dead_flag = (GET_CODE (x) == CLOBBER) ? 1 : -1;
}
/* Return non-zero if REG is known to be dead at INSN.
We scan backwards from INSN. If we hit a REG_DEAD note or a CLOBBER
referencing REG, it is dead. If we hit a SET referencing REG, it is
live. Otherwise, see if it is live or dead at the start of the basic
block we are in. Hard regs marked as being live in NEWPAT_USED_REGS
must be assumed to be always live. */
static int
reg_dead_at_p (reg, insn)
rtx reg;
rtx insn;
{
int block, i;
/* Set variables for reg_dead_at_p_1. */
reg_dead_regno = REGNO (reg);
reg_dead_endregno = reg_dead_regno + (reg_dead_regno < FIRST_PSEUDO_REGISTER
? HARD_REGNO_NREGS (reg_dead_regno,
GET_MODE (reg))
: 1);
reg_dead_flag = 0;
/* Check that reg isn't mentioned in NEWPAT_USED_REGS. */
if (reg_dead_regno < FIRST_PSEUDO_REGISTER)
{
for (i = reg_dead_regno; i < reg_dead_endregno; i++)
if (TEST_HARD_REG_BIT (newpat_used_regs, i))
return 0;
}
/* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, label, or
beginning of function. */
for (; insn && GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != BARRIER;
insn = prev_nonnote_insn (insn))
{
note_stores (PATTERN (insn), reg_dead_at_p_1);
if (reg_dead_flag)
return reg_dead_flag == 1 ? 1 : 0;
if (find_regno_note (insn, REG_DEAD, reg_dead_regno))
return 1;
}
/* Get the basic block number that we were in. */
if (insn == 0)
block = 0;
else
{
for (block = 0; block < n_basic_blocks; block++)
if (insn == BLOCK_HEAD (block))
break;
if (block == n_basic_blocks)
return 0;
}
for (i = reg_dead_regno; i < reg_dead_endregno; i++)
if (REGNO_REG_SET_P (BASIC_BLOCK (block)->global_live_at_start, i))
return 0;
return 1;
}
/* Note hard registers in X that are used. This code is similar to
that in flow.c, but much simpler since we don't care about pseudos. */
static void
mark_used_regs_combine (x)
rtx x;
{
register RTX_CODE code = GET_CODE (x);
register int regno;
int i;
switch (code)
{
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
case CONST_DOUBLE:
case PC:
case ADDR_VEC:
case ADDR_DIFF_VEC:
case ASM_INPUT:
#ifdef HAVE_cc0
/* CC0 must die in the insn after it is set, so we don't need to take
special note of it here. */
case CC0:
#endif
return;
case CLOBBER:
/* If we are clobbering a MEM, mark any hard registers inside the
address as used. */
if (GET_CODE (XEXP (x, 0)) == MEM)
mark_used_regs_combine (XEXP (XEXP (x, 0), 0));
return;
case REG:
regno = REGNO (x);
/* A hard reg in a wide mode may really be multiple registers.
If so, mark all of them just like the first. */
if (regno < FIRST_PSEUDO_REGISTER)
{
/* None of this applies to the stack, frame or arg pointers */
if (regno == STACK_POINTER_REGNUM
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
|| regno == HARD_FRAME_POINTER_REGNUM
#endif
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
|| (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
|| regno == FRAME_POINTER_REGNUM)
return;
i = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (i-- > 0)
SET_HARD_REG_BIT (newpat_used_regs, regno + i);
}
return;
case SET:
{
/* If setting a MEM, or a SUBREG of a MEM, then note any hard regs in
the address. */
register rtx testreg = SET_DEST (x);
while (GET_CODE (testreg) == SUBREG
|| GET_CODE (testreg) == ZERO_EXTRACT
|| GET_CODE (testreg) == SIGN_EXTRACT
|| GET_CODE (testreg) == STRICT_LOW_PART)
testreg = XEXP (testreg, 0);
if (GET_CODE (testreg) == MEM)
mark_used_regs_combine (XEXP (testreg, 0));
mark_used_regs_combine (SET_SRC (x));
}
return;
default:
break;
}
/* Recursively scan the operands of this expression. */
{
register char *fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
mark_used_regs_combine (XEXP (x, i));
else if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
mark_used_regs_combine (XVECEXP (x, i, j));
}
}
}
}
/* Remove register number REGNO from the dead registers list of INSN.
Return the note used to record the death, if there was one. */
rtx
remove_death (regno, insn)
int regno;
rtx insn;
{
register rtx note = find_regno_note (insn, REG_DEAD, regno);
if (note)
{
REG_N_DEATHS (regno)--;
remove_note (insn, note);
}
return note;
}
/* For each register (hardware or pseudo) used within expression X, if its
death is in an instruction with cuid between FROM_CUID (inclusive) and
TO_INSN (exclusive), put a REG_DEAD note for that register in the
list headed by PNOTES.
That said, don't move registers killed by maybe_kill_insn.
This is done when X is being merged by combination into TO_INSN. These
notes will then be distributed as needed. */
static void
move_deaths (x, maybe_kill_insn, from_cuid, to_insn, pnotes)
rtx x;
rtx maybe_kill_insn;
int from_cuid;
rtx to_insn;
rtx *pnotes;
{
register char *fmt;
register int len, i;
register enum rtx_code code = GET_CODE (x);
if (code == REG)
{
register int regno = REGNO (x);
register rtx where_dead = reg_last_death[regno];
register rtx before_dead, after_dead;
/* Don't move the register if it gets killed in between from and to */
if (maybe_kill_insn && reg_set_p (x, maybe_kill_insn)
&& !reg_referenced_p (x, maybe_kill_insn))
return;
/* WHERE_DEAD could be a USE insn made by combine, so first we
make sure that we have insns with valid INSN_CUID values. */
before_dead = where_dead;
while (before_dead && INSN_UID (before_dead) > max_uid_cuid)
before_dead = PREV_INSN (before_dead);
after_dead = where_dead;
while (after_dead && INSN_UID (after_dead) > max_uid_cuid)
after_dead = NEXT_INSN (after_dead);
if (before_dead && after_dead
&& INSN_CUID (before_dead) >= from_cuid
&& (INSN_CUID (after_dead) < INSN_CUID (to_insn)
|| (where_dead != after_dead
&& INSN_CUID (after_dead) == INSN_CUID (to_insn))))
{
rtx note = remove_death (regno, where_dead);
/* It is possible for the call above to return 0. This can occur
when reg_last_death points to I2 or I1 that we combined with.
In that case make a new note.
We must also check for the case where X is a hard register
and NOTE is a death note for a range of hard registers
including X. In that case, we must put REG_DEAD notes for
the remaining registers in place of NOTE. */
if (note != 0 && regno < FIRST_PSEUDO_REGISTER
&& (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
> GET_MODE_SIZE (GET_MODE (x))))
{
int deadregno = REGNO (XEXP (note, 0));
int deadend
= (deadregno + HARD_REGNO_NREGS (deadregno,
GET_MODE (XEXP (note, 0))));
int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
int i;
for (i = deadregno; i < deadend; i++)
if (i < regno || i >= ourend)
REG_NOTES (where_dead)
= gen_rtx_EXPR_LIST (REG_DEAD,
gen_rtx_REG (reg_raw_mode[i], i),
REG_NOTES (where_dead));
}
/* If we didn't find any note, or if we found a REG_DEAD note that
covers only part of the given reg, and we have a multi-reg hard
register, then to be safe we must check for REG_DEAD notes
for each register other than the first. They could have
their own REG_DEAD notes lying around. */
else if ((note == 0
|| (note != 0
&& (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
< GET_MODE_SIZE (GET_MODE (x)))))
&& regno < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
{
int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
int i, offset;
rtx oldnotes = 0;
if (note)
offset = HARD_REGNO_NREGS (regno, GET_MODE (XEXP (note, 0)));
else
offset = 1;
for (i = regno + offset; i < ourend; i++)
move_deaths (gen_rtx_REG (reg_raw_mode[i], i),
maybe_kill_insn, from_cuid, to_insn, &oldnotes);
}
if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x))
{
XEXP (note, 1) = *pnotes;
*pnotes = note;
}
else
*pnotes = gen_rtx_EXPR_LIST (REG_DEAD, x, *pnotes);
REG_N_DEATHS (regno)++;
}
return;
}
else if (GET_CODE (x) == SET)
{
rtx dest = SET_DEST (x);
move_deaths (SET_SRC (x), maybe_kill_insn, from_cuid, to_insn, pnotes);
/* In the case of a ZERO_EXTRACT, a STRICT_LOW_PART, or a SUBREG
that accesses one word of a multi-word item, some
piece of everything register in the expression is used by
this insn, so remove any old death. */
if (GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == STRICT_LOW_PART
|| (GET_CODE (dest) == SUBREG
&& (((GET_MODE_SIZE (GET_MODE (dest))
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD)
== ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
+ UNITS_PER_WORD - 1) / UNITS_PER_WORD))))
{
move_deaths (dest, maybe_kill_insn, from_cuid, to_insn, pnotes);
return;
}
/* If this is some other SUBREG, we know it replaces the entire
value, so use that as the destination. */
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
/* If this is a MEM, adjust deaths of anything used in the address.
For a REG (the only other possibility), the entire value is
being replaced so the old value is not used in this insn. */
if (GET_CODE (dest) == MEM)
move_deaths (XEXP (dest, 0), maybe_kill_insn, from_cuid,
to_insn, pnotes);
return;
}
else if (GET_CODE (x) == CLOBBER)
return;
len = GET_RTX_LENGTH (code);
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < len; i++)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
move_deaths (XVECEXP (x, i, j), maybe_kill_insn, from_cuid,
to_insn, pnotes);
}
else if (fmt[i] == 'e')
move_deaths (XEXP (x, i), maybe_kill_insn, from_cuid, to_insn, pnotes);
}
}
/* Return 1 if X is the target of a bit-field assignment in BODY, the
pattern of an insn. X must be a REG. */
static int
reg_bitfield_target_p (x, body)
rtx x;
rtx body;
{
int i;
if (GET_CODE (body) == SET)
{
rtx dest = SET_DEST (body);
rtx target;
int regno, tregno, endregno, endtregno;
if (GET_CODE (dest) == ZERO_EXTRACT)
target = XEXP (dest, 0);
else if (GET_CODE (dest) == STRICT_LOW_PART)
target = SUBREG_REG (XEXP (dest, 0));
else
return 0;
if (GET_CODE (target) == SUBREG)
target = SUBREG_REG (target);
if (GET_CODE (target) != REG)
return 0;
tregno = REGNO (target), regno = REGNO (x);
if (tregno >= FIRST_PSEUDO_REGISTER || regno >= FIRST_PSEUDO_REGISTER)
return target == x;
endtregno = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (target));
endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
return endregno > tregno && regno < endtregno;
}
else if (GET_CODE (body) == PARALLEL)
for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
if (reg_bitfield_target_p (x, XVECEXP (body, 0, i)))
return 1;
return 0;
}
/* Given a chain of REG_NOTES originally from FROM_INSN, try to place them
as appropriate. I3 and I2 are the insns resulting from the combination
insns including FROM (I2 may be zero).
ELIM_I2 and ELIM_I1 are either zero or registers that we know will
not need REG_DEAD notes because they are being substituted for. This
saves searching in the most common cases.
Each note in the list is either ignored or placed on some insns, depending
on the type of note. */
static void
distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
rtx notes;
rtx from_insn;
rtx i3, i2;
rtx elim_i2, elim_i1;
{
rtx note, next_note;
rtx tem;
for (note = notes; note; note = next_note)
{
rtx place = 0, place2 = 0;
/* If this NOTE references a pseudo register, ensure it references
the latest copy of that register. */
if (XEXP (note, 0) && GET_CODE (XEXP (note, 0)) == REG
&& REGNO (XEXP (note, 0)) >= FIRST_PSEUDO_REGISTER)
XEXP (note, 0) = regno_reg_rtx[REGNO (XEXP (note, 0))];
next_note = XEXP (note, 1);
switch (REG_NOTE_KIND (note))
{
case REG_BR_PROB:
case REG_EXEC_COUNT:
/* Doesn't matter much where we put this, as long as it's somewhere.
It is preferable to keep these notes on branches, which is most
likely to be i3. */
place = i3;
break;
case REG_EH_REGION:
/* This note must remain with the call. It should not be possible
for both I2 and I3 to be a call. */
if (GET_CODE (i3) == CALL_INSN)
place = i3;
else if (i2 && GET_CODE (i2) == CALL_INSN)
place = i2;
else
abort ();
break;
case REG_UNUSED:
/* Any clobbers for i3 may still exist, and so we must process
REG_UNUSED notes from that insn.
Any clobbers from i2 or i1 can only exist if they were added by
recog_for_combine. In that case, recog_for_combine created the
necessary REG_UNUSED notes. Trying to keep any original
REG_UNUSED notes from these insns can cause incorrect output
if it is for the same register as the original i3 dest.
In that case, we will notice that the register is set in i3,
and then add a REG_UNUSED note for the destination of i3, which
is wrong. However, it is possible to have REG_UNUSED notes from
i2 or i1 for register which were both used and clobbered, so
we keep notes from i2 or i1 if they will turn into REG_DEAD
notes. */
/* If this register is set or clobbered in I3, put the note there
unless there is one already. */
if (reg_set_p (XEXP (note, 0), PATTERN (i3)))
{
if (from_insn != i3)
break;
if (! (GET_CODE (XEXP (note, 0)) == REG
? find_regno_note (i3, REG_UNUSED, REGNO (XEXP (note, 0)))
: find_reg_note (i3, REG_UNUSED, XEXP (note, 0))))
place = i3;
}
/* Otherwise, if this register is used by I3, then this register
now dies here, so we must put a REG_DEAD note here unless there
is one already. */
else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))
&& ! (GET_CODE (XEXP (note, 0)) == REG
? find_regno_note (i3, REG_DEAD, REGNO (XEXP (note, 0)))
: find_reg_note (i3, REG_DEAD, XEXP (note, 0))))
{
PUT_REG_NOTE_KIND (note, REG_DEAD);
place = i3;
}
break;
case REG_EQUAL:
case REG_EQUIV:
case REG_NONNEG:
case REG_NOALIAS:
/* These notes say something about results of an insn. We can
only support them if they used to be on I3 in which case they
remain on I3. Otherwise they are ignored.
If the note refers to an expression that is not a constant, we
must also ignore the note since we cannot tell whether the
equivalence is still true. It might be possible to do
slightly better than this (we only have a problem if I2DEST
or I1DEST is present in the expression), but it doesn't
seem worth the trouble. */
if (from_insn == i3
&& (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0))))
place = i3;
break;
case REG_INC:
case REG_NO_CONFLICT:
/* These notes say something about how a register is used. They must
be present on any use of the register in I2 or I3. */
if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3)))
place = i3;
if (i2 && reg_mentioned_p (XEXP (note, 0), PATTERN (i2)))
{
if (place)
place2 = i2;
else
place = i2;
}
break;
case REG_LABEL:
/* This can show up in several ways -- either directly in the
pattern, or hidden off in the constant pool with (or without?)
a REG_EQUAL note. */
/* ??? Ignore the without-reg_equal-note problem for now. */
if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3))
|| ((tem = find_reg_note (i3, REG_EQUAL, NULL_RTX))
&& GET_CODE (XEXP (tem, 0)) == LABEL_REF
&& XEXP (XEXP (tem, 0), 0) == XEXP (note, 0)))
place = i3;
if (i2
&& (reg_mentioned_p (XEXP (note, 0), PATTERN (i2))
|| ((tem = find_reg_note (i2, REG_EQUAL, NULL_RTX))
&& GET_CODE (XEXP (tem, 0)) == LABEL_REF
&& XEXP (XEXP (tem, 0), 0) == XEXP (note, 0))))
{
if (place)
place2 = i2;
else
place = i2;
}
break;
case REG_WAS_0:
/* It is too much trouble to try to see if this note is still
correct in all situations. It is better to simply delete it. */
break;
case REG_RETVAL:
/* If the insn previously containing this note still exists,
put it back where it was. Otherwise move it to the previous
insn. Adjust the corresponding REG_LIBCALL note. */
if (GET_CODE (from_insn) != NOTE)
place = from_insn;
else
{
tem = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX);
place = prev_real_insn (from_insn);
if (tem && place)
XEXP (tem, 0) = place;
}
break;
case REG_LIBCALL:
/* This is handled similarly to REG_RETVAL. */
if (GET_CODE (from_insn) != NOTE)
place = from_insn;
else
{
tem = find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX);
place = next_real_insn (from_insn);
if (tem && place)
XEXP (tem, 0) = place;
}
break;
case REG_DEAD:
/* If the register is used as an input in I3, it dies there.
Similarly for I2, if it is non-zero and adjacent to I3.
If the register is not used as an input in either I3 or I2
and it is not one of the registers we were supposed to eliminate,
there are two possibilities. We might have a non-adjacent I2
or we might have somehow eliminated an additional register
from a computation. For example, we might have had A & B where
we discover that B will always be zero. In this case we will
eliminate the reference to A.
In both cases, we must search to see if we can find a previous
use of A and put the death note there. */
if (from_insn
&& GET_CODE (from_insn) == CALL_INSN
&& find_reg_fusage (from_insn, USE, XEXP (note, 0)))
place = from_insn;
else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)))
place = i3;
else if (i2 != 0 && next_nonnote_insn (i2) == i3
&& reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
place = i2;
if (XEXP (note, 0) == elim_i2 || XEXP (note, 0) == elim_i1)
break;
/* If the register is used in both I2 and I3 and it dies in I3,
we might have added another reference to it. If reg_n_refs
was 2, bump it to 3. This has to be correct since the
register must have been set somewhere. The reason this is
done is because local-alloc.c treats 2 references as a
special case. */
if (place == i3 && i2 != 0 && GET_CODE (XEXP (note, 0)) == REG
&& REG_N_REFS (REGNO (XEXP (note, 0)))== 2
&& reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
REG_N_REFS (REGNO (XEXP (note, 0))) = 3;
if (place == 0)
{
for (tem = prev_nonnote_insn (i3);
place == 0 && tem
&& (GET_CODE (tem) == INSN || GET_CODE (tem) == CALL_INSN);
tem = prev_nonnote_insn (tem))
{
/* If the register is being set at TEM, see if that is all
TEM is doing. If so, delete TEM. Otherwise, make this
into a REG_UNUSED note instead. */
if (reg_set_p (XEXP (note, 0), PATTERN (tem)))
{
rtx set = single_set (tem);
rtx inner_dest = 0;
#ifdef HAVE_cc0
rtx cc0_setter = NULL_RTX;
#endif
if (set != 0)
for (inner_dest = SET_DEST (set);
GET_CODE (inner_dest) == STRICT_LOW_PART
|| GET_CODE (inner_dest) == SUBREG
|| GET_CODE (inner_dest) == ZERO_EXTRACT;
inner_dest = XEXP (inner_dest, 0))
;
/* Verify that it was the set, and not a clobber that
modified the register.
CC0 targets must be careful to maintain setter/user
pairs. If we cannot delete the setter due to side
effects, mark the user with an UNUSED note instead
of deleting it. */
if (set != 0 && ! side_effects_p (SET_SRC (set))
&& rtx_equal_p (XEXP (note, 0), inner_dest)
#ifdef HAVE_cc0
&& (! reg_mentioned_p (cc0_rtx, SET_SRC (set))
|| ((cc0_setter = prev_cc0_setter (tem)) != NULL
&& sets_cc0_p (PATTERN (cc0_setter)) > 0))
#endif
)
{
/* Move the notes and links of TEM elsewhere.
This might delete other dead insns recursively.
First set the pattern to something that won't use
any register. */
PATTERN (tem) = pc_rtx;
distribute_notes (REG_NOTES (tem), tem, tem,
NULL_RTX, NULL_RTX, NULL_RTX);
distribute_links (LOG_LINKS (tem));
PUT_CODE (tem, NOTE);
NOTE_LINE_NUMBER (tem) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (tem) = 0;
#ifdef HAVE_cc0
/* Delete the setter too. */
if (cc0_setter)
{
PATTERN (cc0_setter) = pc_rtx;
distribute_notes (REG_NOTES (cc0_setter),
cc0_setter, cc0_setter,
NULL_RTX, NULL_RTX, NULL_RTX);
distribute_links (LOG_LINKS (cc0_setter));
PUT_CODE (cc0_setter, NOTE);
NOTE_LINE_NUMBER (cc0_setter) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (cc0_setter) = 0;
}
#endif
}
/* If the register is both set and used here, put the
REG_DEAD note here, but place a REG_UNUSED note
here too unless there already is one. */
else if (reg_referenced_p (XEXP (note, 0),
PATTERN (tem)))
{
place = tem;
if (! find_regno_note (tem, REG_UNUSED,
REGNO (XEXP (note, 0))))
REG_NOTES (tem)
= gen_rtx_EXPR_LIST (REG_UNUSED,
XEXP (note, 0),
REG_NOTES (tem));
}
else
{
PUT_REG_NOTE_KIND (note, REG_UNUSED);
/* If there isn't already a REG_UNUSED note, put one
here. */
if (! find_regno_note (tem, REG_UNUSED,
REGNO (XEXP (note, 0))))
place = tem;
break;
}
}
else if (reg_referenced_p (XEXP (note, 0), PATTERN (tem))
|| (GET_CODE (tem) == CALL_INSN
&& find_reg_fusage (tem, USE, XEXP (note, 0))))
{
place = tem;
/* If we are doing a 3->2 combination, and we have a
register which formerly died in i3 and was not used
by i2, which now no longer dies in i3 and is used in
i2 but does not die in i2, and place is between i2
and i3, then we may need to move a link from place to
i2. */
if (i2 && INSN_UID (place) <= max_uid_cuid
&& INSN_CUID (place) > INSN_CUID (i2)
&& from_insn && INSN_CUID (from_insn) > INSN_CUID (i2)
&& reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
{
rtx links = LOG_LINKS (place);
LOG_LINKS (place) = 0;
distribute_links (links);
}
break;
}
}
/* If we haven't found an insn for the death note and it
is still a REG_DEAD note, but we have hit a CODE_LABEL,
insert a USE insn for the register at that label and
put the death node there. This prevents problems with
call-state tracking in caller-save.c. */
if (REG_NOTE_KIND (note) == REG_DEAD && place == 0 && tem != 0)
{
place
= emit_insn_after (gen_rtx_USE (VOIDmode, XEXP (note, 0)),
tem);
/* If this insn was emitted between blocks, then update
BLOCK_HEAD of the current block to include it. */
if (BLOCK_END (this_basic_block - 1) == tem)
BLOCK_HEAD (this_basic_block) = place;
}
}
/* If the register is set or already dead at PLACE, we needn't do
anything with this note if it is still a REG_DEAD note.
We can here if it is set at all, not if is it totally replace,
which is what `dead_or_set_p' checks, so also check for it being
set partially. */
if (place && REG_NOTE_KIND (note) == REG_DEAD)
{
int regno = REGNO (XEXP (note, 0));
if (dead_or_set_p (place, XEXP (note, 0))
|| reg_bitfield_target_p (XEXP (note, 0), PATTERN (place)))
{
/* Unless the register previously died in PLACE, clear
reg_last_death. [I no longer understand why this is
being done.] */
if (reg_last_death[regno] != place)
reg_last_death[regno] = 0;
place = 0;
}
else
reg_last_death[regno] = place;
/* If this is a death note for a hard reg that is occupying
multiple registers, ensure that we are still using all
parts of the object. If we find a piece of the object
that is unused, we must add a USE for that piece before
PLACE and put the appropriate REG_DEAD note on it.
An alternative would be to put a REG_UNUSED for the pieces
on the insn that set the register, but that can't be done if
it is not in the same block. It is simpler, though less
efficient, to add the USE insns. */
if (place && regno < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (regno, GET_MODE (XEXP (note, 0))) > 1)
{
int endregno
= regno + HARD_REGNO_NREGS (regno,
GET_MODE (XEXP (note, 0)));
int all_used = 1;
int i;
for (i = regno; i < endregno; i++)
if (! refers_to_regno_p (i, i + 1, PATTERN (place), 0)
&& ! find_regno_fusage (place, USE, i))
{
rtx piece = gen_rtx_REG (reg_raw_mode[i], i);
rtx p;
/* See if we already placed a USE note for this
register in front of PLACE. */
for (p = place;
GET_CODE (PREV_INSN (p)) == INSN
&& GET_CODE (PATTERN (PREV_INSN (p))) == USE;
p = PREV_INSN (p))
if (rtx_equal_p (piece,
XEXP (PATTERN (PREV_INSN (p)), 0)))
{
p = 0;
break;
}
if (p)
{
rtx use_insn
= emit_insn_before (gen_rtx_USE (VOIDmode,
piece),
p);
REG_NOTES (use_insn)
= gen_rtx_EXPR_LIST (REG_DEAD, piece,
REG_NOTES (use_insn));
}
all_used = 0;
}
/* Check for the case where the register dying partially
overlaps the register set by this insn. */
if (all_used)
for (i = regno; i < endregno; i++)
if (dead_or_set_regno_p (place, i))
{
all_used = 0;
break;
}
if (! all_used)
{
/* Put only REG_DEAD notes for pieces that are
still used and that are not already dead or set. */
for (i = regno; i < endregno; i++)
{
rtx piece = gen_rtx_REG (reg_raw_mode[i], i);
if ((reg_referenced_p (piece, PATTERN (place))
|| (GET_CODE (place) == CALL_INSN
&& find_reg_fusage (place, USE, piece)))
&& ! dead_or_set_p (place, piece)
&& ! reg_bitfield_target_p (piece,
PATTERN (place)))
REG_NOTES (place)
= gen_rtx_EXPR_LIST (REG_DEAD,
piece, REG_NOTES (place));
}
place = 0;
}
}
}
break;
default:
/* Any other notes should not be present at this point in the
compilation. */
abort ();
}
if (place)
{
XEXP (note, 1) = REG_NOTES (place);
REG_NOTES (place) = note;
}
else if ((REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED)
&& GET_CODE (XEXP (note, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (note, 0)))--;
if (place2)
{
if ((REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED)
&& GET_CODE (XEXP (note, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (note, 0)))++;
REG_NOTES (place2) = gen_rtx_fmt_ee (GET_CODE (note),
REG_NOTE_KIND (note),
XEXP (note, 0),
REG_NOTES (place2));
}
}
}
/* Similarly to above, distribute the LOG_LINKS that used to be present on
I3, I2, and I1 to new locations. This is also called in one case to
add a link pointing at I3 when I3's destination is changed. */
static void
distribute_links (links)
rtx links;
{
rtx link, next_link;
for (link = links; link; link = next_link)
{
rtx place = 0;
rtx insn;
rtx set, reg;
next_link = XEXP (link, 1);
/* If the insn that this link points to is a NOTE or isn't a single
set, ignore it. In the latter case, it isn't clear what we
can do other than ignore the link, since we can't tell which
register it was for. Such links wouldn't be used by combine
anyway.
It is not possible for the destination of the target of the link to
have been changed by combine. The only potential of this is if we
replace I3, I2, and I1 by I3 and I2. But in that case the
destination of I2 also remains unchanged. */
if (GET_CODE (XEXP (link, 0)) == NOTE
|| (set = single_set (XEXP (link, 0))) == 0)
continue;
reg = SET_DEST (set);
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == SIGN_EXTRACT
|| GET_CODE (reg) == STRICT_LOW_PART)
reg = XEXP (reg, 0);
/* A LOG_LINK is defined as being placed on the first insn that uses
a register and points to the insn that sets the register. Start
searching at the next insn after the target of the link and stop
when we reach a set of the register or the end of the basic block.
Note that this correctly handles the link that used to point from
I3 to I2. Also note that not much searching is typically done here
since most links don't point very far away. */
for (insn = NEXT_INSN (XEXP (link, 0));
(insn && (this_basic_block == n_basic_blocks - 1
|| BLOCK_HEAD (this_basic_block + 1) != insn));
insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_overlap_mentioned_p (reg, PATTERN (insn)))
{
if (reg_referenced_p (reg, PATTERN (insn)))
place = insn;
break;
}
else if (GET_CODE (insn) == CALL_INSN
&& find_reg_fusage (insn, USE, reg))
{
place = insn;
break;
}
/* If we found a place to put the link, place it there unless there
is already a link to the same insn as LINK at that point. */
if (place)
{
rtx link2;
for (link2 = LOG_LINKS (place); link2; link2 = XEXP (link2, 1))
if (XEXP (link2, 0) == XEXP (link, 0))
break;
if (link2 == 0)
{
XEXP (link, 1) = LOG_LINKS (place);
LOG_LINKS (place) = link;
/* Set added_links_insn to the earliest insn we added a
link to. */
if (added_links_insn == 0
|| INSN_CUID (added_links_insn) > INSN_CUID (place))
added_links_insn = place;
}
}
}
}
/* Compute INSN_CUID for INSN, which is an insn made by combine. */
static int
insn_cuid (insn)
rtx insn;
{
while (insn != 0 && INSN_UID (insn) > max_uid_cuid
&& GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE)
insn = NEXT_INSN (insn);
if (INSN_UID (insn) > max_uid_cuid)
abort ();
return INSN_CUID (insn);
}
void
dump_combine_stats (file)
FILE *file;
{
fnotice
(file,
";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n",
combine_attempts, combine_merges, combine_extras, combine_successes);
}
void
dump_combine_total_stats (file)
FILE *file;
{
fnotice
(file,
"\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n",
total_attempts, total_merges, total_extras, total_successes);
}
Index: head/contrib/gcc/config/i386/sol2.h
===================================================================
--- head/contrib/gcc/config/i386/sol2.h (revision 52750)
+++ head/contrib/gcc/config/i386/sol2.h (revision 52751)
@@ -1,98 +1,125 @@
/* Target definitions for GNU compiler for Intel 80386 running Solaris 2
Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
Contributed by Fred Fish (fnf@cygnus.com).
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "i386/sysv4.h"
+/* We use stabs-in-elf for debugging, because that is what the native
+ toolchain uses. */
+#undef PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG
+
+#if ! GAS_REJECTS_MINUS_S
+
+/*
+ Changed from config/svr4.h in the following ways:
+
+ - Removed -Yd (neither the sun bundled assembler nor gas accept it).
+ - Added "-s" so that stabs are not discarded.
+*/
+
+#undef ASM_SPEC
+#define ASM_SPEC \
+ "%{v:-V} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*} -s"
+
+#else /* GAS_REJECTS_MINUS_S */
+
+/* Same as above, except for -s, unsupported by GNU as. */
+#undef ASM_SPEC
+#define ASM_SPEC \
+ "%{v:-V} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Wa,*:%*}"
+
+#endif /* GAS_REJECTS_MINUS_S */
+
/* The Solaris 2.0 x86 linker botches alignment of code sections.
It tries to align to a 16 byte boundary by padding with 0x00000090
ints, rather than 0x90 bytes (nop). This generates trash in the
".init" section since the contribution from crtbegin.o is only 7
bytes. The linker pads it to 16 bytes with a single 0x90 byte, and
two 0x00000090 ints, which generates a segmentation violation when
executed. This macro forces the assembler to do the padding, since
it knows what it is doing. */
#define FORCE_INIT_SECTION_ALIGN asm (ALIGN_ASM_OP ## " 16")
#define FORCE_FINI_SECTION_ALIGN FORCE_INIT_SECTION_ALIGN
/* Add "sun" to the list of symbols defined for SVR4. */
#undef CPP_PREDEFINES
#define CPP_PREDEFINES \
"-Dunix -D__svr4__ -D__SVR4 -Dsun -Asystem(svr4)"
#undef CPP_SPEC
#define CPP_SPEC "%(cpp_cpu) \
%{compat-bsd:-iwithprefixbefore ucbinclude -I/usr/ucbinclude}"
#undef LIB_SPEC
#define LIB_SPEC \
"%{compat-bsd:-lucb -lsocket -lnsl -lelf -laio} %{!shared:%{!symbolic:-lc}}"
#undef ENDFILE_SPEC
#define ENDFILE_SPEC "crtend.o%s %{pg:crtn.o%s}%{!pg:crtn.o%s}"
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "%{!shared: \
%{!symbolic: \
%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}}}}\
%{pg:gmon.o%s} crti.o%s \
%{ansi:values-Xc.o%s} \
%{!ansi: \
%{traditional:values-Xt.o%s} \
%{!traditional:values-Xa.o%s}} \
crtbegin.o%s"
/* This should be the same as in svr4.h, except with -R added. */
#undef LINK_SPEC
#define LINK_SPEC \
"%{h*} %{v:-V} \
%{b} %{Wl,*:%*} \
%{static:-dn -Bstatic} \
%{shared:-G -dy -z text} \
%{symbolic:-Bsymbolic -G -dy -z text} \
%{G:-G} \
%{YP,*} \
%{R*} \
%{compat-bsd: \
%{!YP,*:%{pg:-Y P,/usr/ucblib:/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \
%{!pg:%{p:-Y P,/usr/ucblib:/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \
%{!p:-Y P,/usr/ucblib:/usr/ccs/lib:/usr/lib}}} \
-R /usr/ucblib} \
%{!compat-bsd: \
%{!YP,*:%{pg:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \
%{!pg:%{p:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \
%{!p:-Y P,/usr/ccs/lib:/usr/lib}}}} \
%{Qy:} %{!Qn:-Qy}"
/* This defines which switch letters take arguments.
It is as in svr4.h but with -R added. */
#undef SWITCH_TAKES_ARG
#define SWITCH_TAKES_ARG(CHAR) \
(DEFAULT_SWITCH_TAKES_ARG(CHAR) \
|| (CHAR) == 'R' \
|| (CHAR) == 'h' \
|| (CHAR) == 'z')
#define STDC_0_IN_SYSTEM_HEADERS
#undef LOCAL_LABEL_PREFIX
#define LOCAL_LABEL_PREFIX "."
Index: head/contrib/gcc/config/i386/sol2gas.h
===================================================================
--- head/contrib/gcc/config/i386/sol2gas.h (nonexistent)
+++ head/contrib/gcc/config/i386/sol2gas.h (revision 52751)
@@ -0,0 +1,11 @@
+/* Definitions of target machine for GNU compiler, for SPARC running
+ Solaris 2 with GNU as up to 2.9.5.0.12.
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+*/
+
+#ifndef GAS_DOES_NOT_SUPPORT_MINUS_S
+#define GAS_DOES_NOT_SUPPORT_MINUS_S 1
+#endif
+
+#include "i386/sol2.h"
Property changes on: head/contrib/gcc/config/i386/sol2gas.h
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Index: head/contrib/gcc/config/openbsd.h
===================================================================
--- head/contrib/gcc/config/openbsd.h (revision 52750)
+++ head/contrib/gcc/config/openbsd.h (revision 52751)
@@ -1,308 +1,311 @@
/* Base configuration file for all OpenBSD targets.
Copyright (C) 1999 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. */
/* Common OpenBSD configuration.
All OpenBSD architectures include this file, which is intended as
a repository for common defines.
Some defines are common to all architectures, a few of them are
triggered by OBSD_* guards, so that we won't override architecture
defaults by mistakes.
OBSD_HAS_CORRECT_SPECS:
another mechanism provides correct specs already.
OBSD_NO_DYNAMIC_LIBRARIES:
no implementation of dynamic libraries.
OBSD_OLD_GAS:
older flavor of gas which needs help for PIC.
OBSD_HAS_DECLARE_FUNCTION_NAME, OBSD_HAS_DECLARE_FUNCTION_SIZE,
OBSD_HAS_DECLARE_OBJECT:
PIC support, FUNCTION_NAME/FUNCTION_SIZE are independent, whereas
the corresponding logic for OBJECTS is necessarily coupled.
There are also a few `default' defines such as ASM_WEAKEN_LABEL,
intended as common ground for arch that don't provide
anything suitable. */
/* OPENBSD_NATIVE is defined only when gcc is configured as part of
the OpenBSD source tree, specifically through Makefile.bsd-wrapper.
In such a case the include path can be trimmed as there is no
distinction between system includes and gcc includes. */
/* This configuration method, namely Makefile.bsd-wrapper and
OPENBSD_NATIVE is NOT recommended for building cross-compilers. */
#ifdef OPENBSD_NATIVE
#undef GCC_INCLUDE_DIR
#define GCC_INCLUDE_DIR "/usr/include"
/* The compiler is configured with ONLY the gcc/g++ standard headers. */
#undef INCLUDE_DEFAULTS
#define INCLUDE_DEFAULTS \
{ \
{ GPLUSPLUS_INCLUDE_DIR, "G++", 1, 1 }, \
{ GCC_INCLUDE_DIR, "GCC", 0, 0 }, \
{ 0, 0, 0, 0 } \
}
/* Under OpenBSD, the normal location of the various *crt*.o files is the
/usr/lib directory. */
#define STANDARD_STARTFILE_PREFIX "/usr/lib/"
#endif
/* Controlling the compilation driver. */
/* CPP_SPEC appropriate for OpenBSD. We deal with -posix and -pthread.
XXX the way threads are handling currently is not very satisfying,
since all code must be compiled with -pthread to work.
This two-stage defines makes it easy to pick that for targets that
have subspecs. */
#define OBSD_CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{pthread:-D_POSIX_THREADS}"
/* LIB_SPEC appropriate for OpenBSD. Select the appropriate libc,
depending on profiling and threads. Basically,
-lc(_r)?(_p)?, select _r for threads, and _p for p or pg. */
#define OBSD_LIB_SPEC "-lc%{pthread:_r}%{p:_p}%{!p:%{pg:_p}}"
#ifndef OBSD_HAS_CORRECT_SPECS
#ifndef OBSD_NO_DYNAMIC_LIBRARIES
#undef SWITCH_TAKES_ARG
#define SWITCH_TAKES_ARG(CHAR) \
(DEFAULT_SWITCH_TAKES_ARG (CHAR) \
|| (CHAR) == 'R')
#endif
#undef CPP_SPEC
#define CPP_SPEC OBSD_CPP_SPEC
#ifdef OBSD_OLD_GAS
/* ASM_SPEC appropriate for OpenBSD. For some architectures, OpenBSD
still uses a special flavor of gas that needs to be told when generating
pic code. */
#undef ASM_SPEC
#define ASM_SPEC "%{fpic:-k} %{fPIC:-k -K} %|"
#else
/* Since we use gas, stdin -> - is a good idea, but we don't want to
override native specs just for that. */
#ifndef ASM_SPEC
#define ASM_SPEC "%|"
#endif
#endif
/* LINK_SPEC appropriate for OpenBSD. Support for GCC options
-static, -assert, and -nostdlib. */
#undef LINK_SPEC
#ifdef OBSD_NO_DYNAMIC_LIBRARIES
#define LINK_SPEC \
"%{!nostdlib:%{!r*:%{!e*:-e start}}} -dc -dp %{assert*}"
#else
#define LINK_SPEC \
"%{!nostdlib:%{!r*:%{!e*:-e start}}} -dc -dp %{R*} %{static:-Bstatic} %{assert*}"
#endif
#undef LIB_SPEC
#define LIB_SPEC OBSD_LIB_SPEC
#endif
/* Runtime target specification. */
/* You must redefine CPP_PREDEFINES in any arch specific file. */
#undef CPP_PREDEFINES
/* Implicit calls to library routines. */
/* Use memcpy and memset instead of bcopy and bzero. */
#define TARGET_MEM_FUNCTIONS
/* Miscellaneous parameters. */
/* Tell libgcc2.c that OpenBSD targets support atexit. */
#define HAVE_ATEXIT
/* Controlling debugging info: dbx options. */
/* Don't use the `xsTAG;' construct in DBX output; OpenBSD systems that
use DBX don't support it. */
#define DBX_NO_XREFS
/* Support of shared libraries, mostly imported from svr4.h through netbsd. */
/* Two differences from svr4.h:
- we use . - _func instead of a local label,
- we put extra spaces in expressions such as
.type _func , @function
This is more readable for a human being and confuses c++filt less. */
/* Assembler format: output and generation of labels. */
/* Define the strings used for the .type and .size directives.
These strings generally do not vary from one system running OpenBSD
to another, but if a given system needs to use different pseudo-op
names for these, they may be overridden in the arch specific file. */
/* OpenBSD assembler is hacked to have .type & .size support even in a.out
format object files. Functions size are supported but not activated
- yet (look for GRACE_PERIOD_EXPIRED in gas/config/obj-aout.c). */
+ yet (look for GRACE_PERIOD_EXPIRED in gas/config/obj-aout.c).
+ SET_ASM_OP is needed for attribute alias to work. */
#undef TYPE_ASM_OP
#undef SIZE_ASM_OP
+#undef SET_ASM_OP
#define TYPE_ASM_OP ".type"
#define SIZE_ASM_OP ".size"
+#define SET_ASM_OP ".set"
/* The following macro defines the format used to output the second
operand of the .type assembler directive. */
#undef TYPE_OPERAND_FMT
#define TYPE_OPERAND_FMT "@%s"
/* Provision if extra assembler code is needed to declare a function's result
(taken from svr4, not needed yet actually). */
#ifndef ASM_DECLARE_RESULT
#define ASM_DECLARE_RESULT(FILE, RESULT)
#endif
/* These macros generate the special .type and .size directives which
are used to set the corresponding fields of the linker symbol table
entries under OpenBSD. These macros also have to output the starting
labels for the relevant functions/objects. */
#ifndef OBSD_HAS_DECLARE_FUNCTION_NAME
/* Extra assembler code needed to declare a function properly.
Some assemblers may also need to also have something extra said
about the function's return value. We allow for that here. */
#undef ASM_DECLARE_FUNCTION_NAME
#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
do { \
fprintf (FILE, "\t%s\t", TYPE_ASM_OP); \
assemble_name (FILE, NAME); \
fputs (" , ", FILE); \
fprintf (FILE, TYPE_OPERAND_FMT, "function"); \
putc ('\n', FILE); \
ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \
ASM_OUTPUT_LABEL(FILE, NAME); \
} while (0)
#endif
#ifndef OBSD_HAS_DECLARE_FUNCTION_SIZE
/* Declare the size of a function. */
#undef ASM_DECLARE_FUNCTION_SIZE
#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
do { \
if (!flag_inhibit_size_directive) \
{ \
fprintf (FILE, "\t%s\t", SIZE_ASM_OP); \
assemble_name (FILE, (FNAME)); \
fputs (" , . - ", FILE); \
assemble_name (FILE, (FNAME)); \
putc ('\n', FILE); \
} \
} while (0)
#endif
#ifndef OBSD_HAS_DECLARE_OBJECT
/* Extra assembler code needed to declare an object properly. */
#undef ASM_DECLARE_OBJECT_NAME
#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
do { \
fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \
assemble_name (FILE, NAME); \
fputs (" , ", FILE); \
fprintf (FILE, TYPE_OPERAND_FMT, "object"); \
putc ('\n', FILE); \
size_directive_output = 0; \
if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \
{ \
size_directive_output = 1; \
fprintf (FILE, "\t%s\t", SIZE_ASM_OP); \
assemble_name (FILE, NAME); \
fprintf (FILE, " , %d\n", int_size_in_bytes (TREE_TYPE (DECL)));\
} \
ASM_OUTPUT_LABEL (FILE, NAME); \
} while (0)
/* Output the size directive for a decl in rest_of_decl_compilation
in the case where we did not do so before the initializer.
Once we find the error_mark_node, we know that the value of
size_directive_output was set by ASM_DECLARE_OBJECT_NAME
when it was run for the same decl. */
#undef ASM_FINISH_DECLARE_OBJECT
#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \
do { \
char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \
if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \
&& ! AT_END && TOP_LEVEL \
&& DECL_INITIAL (DECL) == error_mark_node \
&& !size_directive_output) \
{ \
size_directive_output = 1; \
fprintf (FILE, "\t%s\t", SIZE_ASM_OP); \
assemble_name (FILE, name); \
fprintf (FILE, " , %d\n", int_size_in_bytes (TREE_TYPE (DECL)));\
} \
} while (0)
#endif
/* Those are `generic' ways to weaken/globalize a label. We shouldn't need
to override a processor specific definition. Hence, #ifndef ASM_*
In case overriding turns out to be needed, one can always #undef ASM_*
before including this file. */
/* Tell the assembler that a symbol is weak. */
/* Note: netbsd arm32 assembler needs a .globl here. An override may
be needed when/if we go for arm32 support. */
#ifndef ASM_WEAKEN_LABEL
#define ASM_WEAKEN_LABEL(FILE,NAME) \
do { fputs ("\t.weak\t", FILE); assemble_name (FILE, NAME); \
fputc ('\n', FILE); } while (0)
#endif
/* Tell the assembler that a symbol is global. */
#ifndef ASM_GLOBALIZE_LABEL
#define ASM_GLOBALIZE_LABEL(FILE,NAME) \
do { fputs ("\t.globl\t", FILE); assemble_name (FILE, NAME); \
fputc ('\n', FILE); } while(0)
#endif
/* Storage layout. */
/* We don't have to worry about binary compatibility with older C++ code,
but there is a big known bug with vtable thunks which has not been
fixed yet, so DON'T activate it by default. */
/* #define DEFAULT_VTABLE_THUNKS 1 */
/* Otherwise, since we support weak, gthr.h erroneously tries to use
#pragma weak. */
#define GTHREAD_USE_WEAK 0
/* bug work around: we don't want to support #pragma weak, but the current
code layout needs HANDLE_PRAGMA_WEAK asserted for __attribute((weak)) to
work. On the other hand, we don't define HANDLE_PRAGMA_WEAK directly,
as this depends on a few other details as well... */
#define HANDLE_SYSV_PRAGMA
Index: head/contrib/gcc/config/sparc/sparc.c
===================================================================
--- head/contrib/gcc/config/sparc/sparc.c (revision 52750)
+++ head/contrib/gcc/config/sparc/sparc.c (revision 52751)
@@ -1,7843 +1,7843 @@
/* Subroutines for insn-output.c for Sun SPARC.
Copyright (C) 1987, 88, 89, 92-98, 1999 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
at Cygnus Support.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "expr.h"
#include "recog.h"
#include "toplev.h"
/* 1 if the caller has placed an "unimp" insn immediately after the call.
This is used in v8 code when calling a function that returns a structure.
v9 doesn't have this. Be careful to have this test be the same as that
used on the call. */
#define SKIP_CALLERS_UNIMP_P \
(!TARGET_ARCH64 && current_function_returns_struct \
&& ! integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl))) \
&& (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl))) \
== INTEGER_CST))
/* Global variables for machine-dependent things. */
/* Size of frame. Need to know this to emit return insns from leaf procedures.
ACTUAL_FSIZE is set by compute_frame_size() which is called during the
reload pass. This is important as the value is later used in insn
scheduling (to see what can go in a delay slot).
APPARENT_FSIZE is the size of the stack less the register save area and less
the outgoing argument area. It is used when saving call preserved regs. */
static int apparent_fsize;
static int actual_fsize;
/* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */
rtx sparc_compare_op0, sparc_compare_op1;
/* We may need an epilogue if we spill too many registers.
If this is non-zero, then we branch here for the epilogue. */
static rtx leaf_label;
#ifdef LEAF_REGISTERS
/* Vector to say how input registers are mapped to output
registers. FRAME_POINTER_REGNUM cannot be remapped by
this function to eliminate it. You must use -fomit-frame-pointer
to get that. */
char leaf_reg_remap[] =
{ 0, 1, 2, 3, 4, 5, 6, 7,
-1, -1, -1, -1, -1, -1, 14, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
8, 9, 10, 11, 12, 13, -1, 15,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100};
#endif
/* Name of where we pretend to think the frame pointer points.
Normally, this is "%fp", but if we are in a leaf procedure,
this is "%sp+something". We record "something" separately as it may be
too big for reg+constant addressing. */
static const char *frame_base_name;
static int frame_base_offset;
static rtx pic_setup_code PROTO((void));
static void sparc_init_modes PROTO((void));
static int save_regs PROTO((FILE *, int, int, const char *,
int, int, int));
static int restore_regs PROTO((FILE *, int, int, const char *, int, int));
static void build_big_number PROTO((FILE *, int, const char *));
static int function_arg_slotno PROTO((const CUMULATIVE_ARGS *,
enum machine_mode, tree, int, int,
int *, int *));
static int supersparc_adjust_cost PROTO((rtx, rtx, rtx, int));
static int hypersparc_adjust_cost PROTO((rtx, rtx, rtx, int));
static int ultrasparc_adjust_cost PROTO((rtx, rtx, rtx, int));
static void sparc_output_addr_vec PROTO((rtx));
static void sparc_output_addr_diff_vec PROTO((rtx));
static void sparc_output_deferred_case_vectors PROTO((void));
#ifdef DWARF2_DEBUGGING_INFO
extern char *dwarf2out_cfi_label ();
#endif
/* Option handling. */
/* Code model option as passed by user. */
const char *sparc_cmodel_string;
/* Parsed value. */
enum cmodel sparc_cmodel;
/* Record alignment options as passed by user. */
const char *sparc_align_loops_string;
const char *sparc_align_jumps_string;
const char *sparc_align_funcs_string;
/* Parsed values, as a power of two. */
int sparc_align_loops;
int sparc_align_jumps;
int sparc_align_funcs;
struct sparc_cpu_select sparc_select[] =
{
/* switch name, tune arch */
{ (char *)0, "default", 1, 1 },
{ (char *)0, "-mcpu=", 1, 1 },
{ (char *)0, "-mtune=", 1, 0 },
{ 0, 0, 0, 0 }
};
/* CPU type. This is set from TARGET_CPU_DEFAULT and -m{cpu,tune}=xxx. */
enum processor_type sparc_cpu;
/* Validate and override various options, and do some machine dependent
initialization. */
void
sparc_override_options ()
{
static struct code_model {
const char *name;
int value;
} cmodels[] = {
{ "32", CM_32 },
{ "medlow", CM_MEDLOW },
{ "medmid", CM_MEDMID },
{ "medany", CM_MEDANY },
{ "embmedany", CM_EMBMEDANY },
{ 0, 0 }
};
struct code_model *cmodel;
/* Map TARGET_CPU_DEFAULT to value for -m{arch,tune}=. */
static struct cpu_default {
int cpu;
const char *name;
} cpu_default[] = {
/* There must be one entry here for each TARGET_CPU value. */
{ TARGET_CPU_sparc, "cypress" },
{ TARGET_CPU_sparclet, "tsc701" },
{ TARGET_CPU_sparclite, "f930" },
{ TARGET_CPU_v8, "v8" },
{ TARGET_CPU_hypersparc, "hypersparc" },
{ TARGET_CPU_sparclite86x, "sparclite86x" },
{ TARGET_CPU_supersparc, "supersparc" },
{ TARGET_CPU_v9, "v9" },
{ TARGET_CPU_ultrasparc, "ultrasparc" },
{ 0, 0 }
};
struct cpu_default *def;
/* Table of values for -m{cpu,tune}=. */
static struct cpu_table {
const char *name;
enum processor_type processor;
int disable;
int enable;
} cpu_table[] = {
{ "v7", PROCESSOR_V7, MASK_ISA, 0 },
{ "cypress", PROCESSOR_CYPRESS, MASK_ISA, 0 },
{ "v8", PROCESSOR_V8, MASK_ISA, MASK_V8 },
/* TI TMS390Z55 supersparc */
{ "supersparc", PROCESSOR_SUPERSPARC, MASK_ISA, MASK_V8 },
{ "sparclite", PROCESSOR_SPARCLITE, MASK_ISA, MASK_SPARCLITE },
/* The Fujitsu MB86930 is the original sparclite chip, with no fpu.
The Fujitsu MB86934 is the recent sparclite chip, with an fpu. */
{ "f930", PROCESSOR_F930, MASK_ISA|MASK_FPU, MASK_SPARCLITE },
{ "f934", PROCESSOR_F934, MASK_ISA, MASK_SPARCLITE|MASK_FPU },
{ "hypersparc", PROCESSOR_HYPERSPARC, MASK_ISA, MASK_V8|MASK_FPU },
{ "sparclite86x", PROCESSOR_SPARCLITE86X, MASK_ISA|MASK_FPU, MASK_V8 },
{ "sparclet", PROCESSOR_SPARCLET, MASK_ISA, MASK_SPARCLET },
/* TEMIC sparclet */
{ "tsc701", PROCESSOR_TSC701, MASK_ISA, MASK_SPARCLET },
{ "v9", PROCESSOR_V9, MASK_ISA, MASK_V9 },
/* TI ultrasparc */
{ "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V9 },
{ 0, 0, 0, 0 }
};
struct cpu_table *cpu;
struct sparc_cpu_select *sel;
int fpu;
#ifndef SPARC_BI_ARCH
/* Check for unsupported architecture size. */
if (! TARGET_64BIT != DEFAULT_ARCH32_P)
{
error ("%s is not supported by this configuration",
DEFAULT_ARCH32_P ? "-m64" : "-m32");
}
#endif
/* At the moment we don't allow different pointer size and architecture */
if (! TARGET_64BIT != ! TARGET_PTR64)
{
error ("-mptr%d not allowed on -m%d",
TARGET_PTR64 ? 64 : 32, TARGET_64BIT ? 64 : 32);
if (TARGET_64BIT)
target_flags |= MASK_PTR64;
else
target_flags &= ~MASK_PTR64;
}
/* Code model selection. */
sparc_cmodel = SPARC_DEFAULT_CMODEL;
#ifdef SPARC_BI_ARCH
if (TARGET_ARCH32)
sparc_cmodel = CM_32;
#endif
if (sparc_cmodel_string != NULL)
{
if (TARGET_ARCH64)
{
for (cmodel = &cmodels[0]; cmodel->name; cmodel++)
if (strcmp (sparc_cmodel_string, cmodel->name) == 0)
break;
if (cmodel->name == NULL)
error ("bad value (%s) for -mcmodel= switch", sparc_cmodel_string);
else
sparc_cmodel = cmodel->value;
}
else
error ("-mcmodel= is not supported on 32 bit systems");
}
fpu = TARGET_FPU; /* save current -mfpu status */
/* Set the default CPU. */
for (def = &cpu_default[0]; def->name; ++def)
if (def->cpu == TARGET_CPU_DEFAULT)
break;
if (! def->name)
abort ();
sparc_select[0].string = def->name;
for (sel = &sparc_select[0]; sel->name; ++sel)
{
if (sel->string)
{
for (cpu = &cpu_table[0]; cpu->name; ++cpu)
if (! strcmp (sel->string, cpu->name))
{
if (sel->set_tune_p)
sparc_cpu = cpu->processor;
if (sel->set_arch_p)
{
target_flags &= ~cpu->disable;
target_flags |= cpu->enable;
}
break;
}
if (! cpu->name)
error ("bad value (%s) for %s switch", sel->string, sel->name);
}
}
/* If -mfpu or -mno-fpu was explicitly used, don't override with
the processor default. */
if (TARGET_FPU_SET)
target_flags = (target_flags & ~MASK_FPU) | fpu;
/* Use the deprecated v8 insns for sparc64 in 32 bit mode. */
if (TARGET_V9 && TARGET_ARCH32)
target_flags |= MASK_DEPRECATED_V8_INSNS;
/* V8PLUS requires V9, makes no sense in 64 bit mode. */
if (! TARGET_V9 || TARGET_ARCH64)
target_flags &= ~MASK_V8PLUS;
/* Don't use stack biasing in 32 bit mode. */
if (TARGET_ARCH32)
target_flags &= ~MASK_STACK_BIAS;
/* Don't allow -mvis if FPU is disabled. */
if (! TARGET_FPU)
target_flags &= ~MASK_VIS;
/* Validate -malign-loops= value, or provide default. */
if (sparc_align_loops_string)
{
sparc_align_loops = exact_log2 (atoi (sparc_align_loops_string));
if (sparc_align_loops < 2 || sparc_align_loops > 7)
fatal ("-malign-loops=%s is not between 4 and 128 or is not a power of two",
sparc_align_loops_string);
}
else
{
/* ??? This relies on ASM_OUTPUT_ALIGN to not emit the alignment if
its 0. This sounds a bit kludgey. */
sparc_align_loops = 0;
}
/* Validate -malign-jumps= value, or provide default. */
if (sparc_align_jumps_string)
{
sparc_align_jumps = exact_log2 (atoi (sparc_align_jumps_string));
if (sparc_align_jumps < 2 || sparc_align_loops > 7)
fatal ("-malign-jumps=%s is not between 4 and 128 or is not a power of two",
sparc_align_jumps_string);
}
else
{
/* ??? This relies on ASM_OUTPUT_ALIGN to not emit the alignment if
its 0. This sounds a bit kludgey. */
sparc_align_jumps = 0;
}
/* Validate -malign-functions= value, or provide default. */
if (sparc_align_funcs_string)
{
sparc_align_funcs = exact_log2 (atoi (sparc_align_funcs_string));
if (sparc_align_funcs < 2 || sparc_align_loops > 7)
fatal ("-malign-functions=%s is not between 4 and 128 or is not a power of two",
sparc_align_funcs_string);
}
else
sparc_align_funcs = DEFAULT_SPARC_ALIGN_FUNCS;
/* Validate PCC_STRUCT_RETURN. */
if (flag_pcc_struct_return == DEFAULT_PCC_STRUCT_RETURN)
flag_pcc_struct_return = (TARGET_ARCH64 ? 0 : 1);
/* Do various machine dependent initializations. */
sparc_init_modes ();
if ((profile_flag || profile_block_flag)
&& sparc_cmodel != CM_MEDLOW)
{
error ("profiling does not support code models other than medlow");
}
}
/* Miscellaneous utilities. */
/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move
or branch on register contents instructions. */
int
v9_regcmp_p (code)
enum rtx_code code;
{
return (code == EQ || code == NE || code == GE || code == LT
|| code == LE || code == GT);
}
/* Operand constraints. */
/* Return non-zero only if OP is a register of mode MODE,
or const0_rtx. Don't allow const0_rtx if TARGET_LIVE_G0 because
%g0 may contain anything. */
int
reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (TARGET_LIVE_G0)
return 0;
if (op == const0_rtx)
return 1;
if (GET_MODE (op) == VOIDmode && GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& CONST_DOUBLE_LOW (op) == 0)
return 1;
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_CODE (op) == CONST_DOUBLE
&& fp_zero_operand (op))
return 1;
return 0;
}
/* Nonzero if OP is a floating point value with value 0.0. */
int
fp_zero_operand (op)
rtx op;
{
REAL_VALUE_TYPE r;
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
return (REAL_VALUES_EQUAL (r, dconst0) && ! REAL_VALUE_MINUS_ZERO (r));
}
/* Nonzero if OP is an integer register. */
int
intreg_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (register_operand (op, SImode)
|| (TARGET_ARCH64 && register_operand (op, DImode)));
}
/* Nonzero if OP is a floating point condition code register. */
int
fcc_reg_operand (op, mode)
rtx op;
enum machine_mode mode;
{
/* This can happen when recog is called from combine. Op may be a MEM.
Fail instead of calling abort in this case. */
if (GET_CODE (op) != REG)
return 0;
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (mode == VOIDmode
&& (GET_MODE (op) != CCFPmode && GET_MODE (op) != CCFPEmode))
return 0;
#if 0 /* ??? ==> 1 when %fcc0-3 are pseudos first. See gen_compare_reg(). */
if (reg_renumber == 0)
return REGNO (op) >= FIRST_PSEUDO_REGISTER;
return REGNO_OK_FOR_CCFP_P (REGNO (op));
#else
return (unsigned) REGNO (op) - SPARC_FIRST_V9_FCC_REG < 4;
#endif
}
/* Nonzero if OP is an integer or floating point condition code register. */
int
icc_or_fcc_reg_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == REG && REGNO (op) == SPARC_ICC_REG)
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (mode == VOIDmode
&& GET_MODE (op) != CCmode && GET_MODE (op) != CCXmode)
return 0;
return 1;
}
return fcc_reg_operand (op, mode);
}
/* Nonzero if OP can appear as the dest of a RESTORE insn. */
int
restore_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == REG && GET_MODE (op) == mode
&& (REGNO (op) < 8 || (REGNO (op) >= 24 && REGNO (op) < 32)));
}
/* Call insn on SPARC can take a PC-relative constant address, or any regular
memory address. */
int
call_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != MEM)
abort ();
op = XEXP (op, 0);
return (symbolic_operand (op, mode) || memory_address_p (Pmode, op));
}
int
call_operand_address (op, mode)
rtx op;
enum machine_mode mode;
{
return (symbolic_operand (op, mode) || memory_address_p (Pmode, op));
}
/* Returns 1 if OP is either a symbol reference or a sum of a symbol
reference and a constant. */
int
symbolic_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
/* ??? This clause seems to be irrelevant. */
case CONST_DOUBLE:
return GET_MODE (op) == mode;
default:
return 0;
}
}
/* Return truth value of statement that OP is a symbolic memory
operand of mode MODE. */
int
symbolic_memory_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST
|| GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF);
}
/* Return truth value of statement that OP is a LABEL_REF of mode MODE. */
int
label_ref_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) != LABEL_REF)
return 0;
if (GET_MODE (op) != mode)
return 0;
return 1;
}
/* Return 1 if the operand is an argument used in generating pic references
in either the medium/low or medium/anywhere code models of sparc64. */
int
sp64_medium_pic_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
/* Check for (const (minus (symbol_ref:GOT)
(const (minus (label) (pc))))). */
if (GET_CODE (op) != CONST)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != MINUS)
return 0;
if (GET_CODE (XEXP (op, 0)) != SYMBOL_REF)
return 0;
/* ??? Ensure symbol is GOT. */
if (GET_CODE (XEXP (op, 1)) != CONST)
return 0;
if (GET_CODE (XEXP (XEXP (op, 1), 0)) != MINUS)
return 0;
return 1;
}
/* Return 1 if the operand is a data segment reference. This includes
the readonly data segment, or in other words anything but the text segment.
This is needed in the medium/anywhere code model on v9. These values
are accessed with EMBMEDANY_BASE_REG. */
int
data_segment_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
switch (GET_CODE (op))
{
case SYMBOL_REF :
return ! SYMBOL_REF_FLAG (op);
case PLUS :
/* Assume canonical format of symbol + constant.
Fall through. */
case CONST :
return data_segment_operand (XEXP (op, 0));
default :
return 0;
}
}
/* Return 1 if the operand is a text segment reference.
This is needed in the medium/anywhere code model on v9. */
int
text_segment_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
switch (GET_CODE (op))
{
case LABEL_REF :
return 1;
case SYMBOL_REF :
return SYMBOL_REF_FLAG (op);
case PLUS :
/* Assume canonical format of symbol + constant.
Fall through. */
case CONST :
return text_segment_operand (XEXP (op, 0));
default :
return 0;
}
}
/* Return 1 if the operand is either a register or a memory operand that is
not symbolic. */
int
reg_or_nonsymb_mem_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
return 1;
return 0;
}
int
splittable_symbolic_memory_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) != MEM)
return 0;
if (! symbolic_operand (XEXP (op, 0), Pmode))
return 0;
return 1;
}
int
splittable_immediate_memory_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) != MEM)
return 0;
if (! immediate_operand (XEXP (op, 0), Pmode))
return 0;
return 1;
}
/* Return truth value of whether OP is EQ or NE. */
int
eq_or_neq (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == EQ || GET_CODE (op) == NE);
}
/* Return 1 if this is a comparison operator, but not an EQ, NE, GEU,
or LTU for non-floating-point. We handle those specially. */
int
normal_comp_operator (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
enum rtx_code code = GET_CODE (op);
if (GET_RTX_CLASS (code) != '<')
return 0;
if (GET_MODE (XEXP (op, 0)) == CCFPmode
|| GET_MODE (XEXP (op, 0)) == CCFPEmode)
return 1;
return (code != NE && code != EQ && code != GEU && code != LTU);
}
/* Return 1 if this is a comparison operator. This allows the use of
MATCH_OPERATOR to recognize all the branch insns. */
int
noov_compare_op (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
enum rtx_code code = GET_CODE (op);
if (GET_RTX_CLASS (code) != '<')
return 0;
if (GET_MODE (XEXP (op, 0)) == CC_NOOVmode)
/* These are the only branches which work with CC_NOOVmode. */
return (code == EQ || code == NE || code == GE || code == LT);
return 1;
}
/* Nonzero if OP is a comparison operator suitable for use in v9
conditional move or branch on register contents instructions. */
int
v9_regcmp_op (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
enum rtx_code code = GET_CODE (op);
if (GET_RTX_CLASS (code) != '<')
return 0;
return v9_regcmp_p (code);
}
/* Return 1 if this is a SIGN_EXTEND or ZERO_EXTEND operation. */
int
extend_op (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
}
/* Return nonzero if OP is an operator of mode MODE which can set
the condition codes explicitly. We do not include PLUS and MINUS
because these require CC_NOOVmode, which we handle explicitly. */
int
cc_arithop (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == AND
|| GET_CODE (op) == IOR
|| GET_CODE (op) == XOR)
return 1;
return 0;
}
/* Return nonzero if OP is an operator of mode MODE which can bitwise
complement its second operand and set the condition codes explicitly. */
int
cc_arithopn (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
/* XOR is not here because combine canonicalizes (xor (not ...) ...)
and (xor ... (not ...)) to (not (xor ...)). */
return (GET_CODE (op) == AND
|| GET_CODE (op) == IOR);
}
/* Return true if OP is a register, or is a CONST_INT that can fit in a
signed 13 bit immediate field. This is an acceptable SImode operand for
most 3 address instructions. */
int
arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
int val;
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) != CONST_INT)
return 0;
val = INTVAL (op) & 0xffffffff;
return SPARC_SIMM13_P (val);
}
/* Return true if OP is a constant 4096 */
int
arith_4096_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
int val;
if (GET_CODE (op) != CONST_INT)
return 0;
val = INTVAL (op) & 0xffffffff;
return val == 4096;
}
/* Return true if OP is suitable as second operand for add/sub */
int
arith_add_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return arith_operand (op, mode) || arith_4096_operand (op, mode);
}
/* Return true if OP is a CONST_INT or a CONST_DOUBLE which can fit in the
immediate field of OR and XOR instructions. Used for 64-bit
constant formation patterns. */
int
const64_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return ((GET_CODE (op) == CONST_INT
&& SPARC_SIMM13_P (INTVAL (op)))
#if HOST_BITS_PER_WIDE_INT != 64
|| (GET_CODE (op) == CONST_DOUBLE
&& SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))
&& (CONST_DOUBLE_HIGH (op) ==
((CONST_DOUBLE_LOW (op) & 0x80000000) != 0 ?
(HOST_WIDE_INT)0xffffffff : 0)))
#endif
);
}
/* The same, but only for sethi instructions. */
int
const64_high_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return ((GET_CODE (op) == CONST_INT
&& (INTVAL (op) & 0xfffffc00) != 0
&& SPARC_SETHI_P (INTVAL (op))
#if HOST_BITS_PER_WIDE_INT != 64
/* Must be positive on non-64bit host else the
optimizer is fooled into thinking that sethi
sign extends, even though it does not. */
&& INTVAL (op) >= 0
#endif
)
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0xfffffc00) != 0
&& SPARC_SETHI_P (CONST_DOUBLE_LOW (op))));
}
/* Return true if OP is a register, or is a CONST_INT that can fit in a
signed 11 bit immediate field. This is an acceptable SImode operand for
the movcc instructions. */
int
arith11_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && SPARC_SIMM11_P (INTVAL (op))));
}
/* Return true if OP is a register, or is a CONST_INT that can fit in a
signed 10 bit immediate field. This is an acceptable SImode operand for
the movrcc instructions. */
int
arith10_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && SPARC_SIMM10_P (INTVAL (op))));
}
/* Return true if OP is a register, is a CONST_INT that fits in a 13 bit
immediate field, or is a CONST_DOUBLE whose both parts fit in a 13 bit
immediate field.
v9: Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that
can fit in a 13 bit immediate field. This is an acceptable DImode operand
for most 3 address instructions. */
int
arith_double_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && SMALL_INT (op))
|| (! TARGET_ARCH64
&& GET_CODE (op) == CONST_DOUBLE
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_HIGH (op) + 0x1000) < 0x2000)
|| (TARGET_ARCH64
&& GET_CODE (op) == CONST_DOUBLE
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000
&& ((CONST_DOUBLE_HIGH (op) == -1
&& (CONST_DOUBLE_LOW (op) & 0x1000) == 0x1000)
|| (CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0x1000) == 0))));
}
/* Return true if OP is a constant 4096 for DImode on ARCH64 */
int
arith_double_4096_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (TARGET_ARCH64 &&
((GET_CODE (op) == CONST_INT && INTVAL (op) == 4096) ||
(GET_CODE (op) == CONST_DOUBLE &&
CONST_DOUBLE_LOW (op) == 4096 &&
CONST_DOUBLE_HIGH (op) == 0)));
}
/* Return true if OP is suitable as second operand for add/sub in DImode */
int
arith_double_add_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return arith_double_operand (op, mode) || arith_double_4096_operand (op, mode);
}
/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that
can fit in an 11 bit immediate field. This is an acceptable DImode
operand for the movcc instructions. */
/* ??? Replace with arith11_operand? */
int
arith11_double_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_DOUBLE
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x400) < 0x800
&& ((CONST_DOUBLE_HIGH (op) == -1
&& (CONST_DOUBLE_LOW (op) & 0x400) == 0x400)
|| (CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0x400) == 0)))
|| (GET_CODE (op) == CONST_INT
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x400) < 0x800));
}
/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that
can fit in an 10 bit immediate field. This is an acceptable DImode
operand for the movrcc instructions. */
/* ??? Replace with arith10_operand? */
int
arith10_double_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_DOUBLE
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned) (CONST_DOUBLE_LOW (op) + 0x200) < 0x400
&& ((CONST_DOUBLE_HIGH (op) == -1
&& (CONST_DOUBLE_LOW (op) & 0x200) == 0x200)
|| (CONST_DOUBLE_HIGH (op) == 0
&& (CONST_DOUBLE_LOW (op) & 0x200) == 0)))
|| (GET_CODE (op) == CONST_INT
&& (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode)
&& (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x200) < 0x400));
}
/* Return truth value of whether OP is a integer which fits the
range constraining immediate operands in most three-address insns,
which have a 13 bit immediate field. */
int
small_int (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
}
int
small_int_or_double (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return ((GET_CODE (op) == CONST_INT && SMALL_INT (op))
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))));
}
/* Recognize operand values for the umul instruction. That instruction sign
extends immediate values just like all other sparc instructions, but
interprets the extended result as an unsigned number. */
int
uns_small_int (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
#if HOST_BITS_PER_WIDE_INT > 32
/* All allowed constants will fit a CONST_INT. */
return (GET_CODE (op) == CONST_INT
&& ((INTVAL (op) >= 0 && INTVAL (op) < 0x1000)
|| (INTVAL (op) >= 0xFFFFF000
&& INTVAL (op) < 0x100000000)));
#else
return ((GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x1000)
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0
&& (unsigned) CONST_DOUBLE_LOW (op) - 0xFFFFF000 < 0x1000));
#endif
}
int
uns_arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return register_operand (op, mode) || uns_small_int (op, mode);
}
/* Return truth value of statement that OP is a call-clobbered register. */
int
clobbered_register (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == REG && call_used_regs[REGNO (op)]);
}
/* Return 1 if OP is const0_rtx, used for TARGET_LIVE_G0 insns. */
int
zero_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return op == const0_rtx;
}
/* Return 1 if OP is a valid operand for the source of a move insn. */
int
input_operand (op, mode)
rtx op;
enum machine_mode mode;
{
/* If both modes are non-void they must be the same. */
if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op))
return 0;
/* Only a tiny bit of handling for CONSTANT_P_RTX is necessary. */
if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == CONSTANT_P_RTX)
return 1;
/* Allow any one instruction integer constant, and all CONST_INT
variants when we are working in DImode and !arch64. */
if (GET_MODE_CLASS (mode) == MODE_INT
&& ((GET_CODE (op) == CONST_INT
&& ((SPARC_SETHI_P (INTVAL (op))
&& (! TARGET_ARCH64
|| (INTVAL (op) >= 0)
|| mode == SImode))
|| SPARC_SIMM13_P (INTVAL (op))
|| (mode == DImode
&& ! TARGET_ARCH64)))
|| (TARGET_ARCH64
&& GET_CODE (op) == CONST_DOUBLE
&& ((CONST_DOUBLE_HIGH (op) == 0
&& SPARC_SETHI_P (CONST_DOUBLE_LOW (op)))
||
#if HOST_BITS_PER_WIDE_INT == 64
(CONST_DOUBLE_HIGH (op) == 0
&& SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)))
#else
(SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))
&& (((CONST_DOUBLE_LOW (op) & 0x80000000) == 0
&& CONST_DOUBLE_HIGH (op) == 0)
|| (CONST_DOUBLE_HIGH (op) == -1)))
#endif
))))
return 1;
/* If !arch64 and this is a DImode const, allow it so that
the splits can be generated. */
if (! TARGET_ARCH64
&& mode == DImode
&& GET_CODE (op) == CONST_DOUBLE)
return 1;
if (register_operand (op, mode))
return 1;
/* If this is a SUBREG, look inside so that we handle
paradoxical ones. */
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
/* Check for valid MEM forms. */
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (GET_CODE (inside) == LO_SUM)
{
/* We can't allow these because all of the splits
(eventually as they trickle down into DFmode
splits) require offsettable memory references. */
if (! TARGET_V9
&& GET_MODE (op) == TFmode)
return 0;
return (register_operand (XEXP (inside, 0), Pmode)
&& CONSTANT_P (XEXP (inside, 1)));
}
return memory_address_p (mode, inside);
}
return 0;
}
/* We know it can't be done in one insn when we get here,
the movsi expander guarentees this. */
void
sparc_emit_set_const32 (op0, op1)
rtx op0;
rtx op1;
{
enum machine_mode mode = GET_MODE (op0);
rtx temp;
if (GET_CODE (op1) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (op1);
if (SPARC_SETHI_P (value)
|| SPARC_SIMM13_P (value))
abort ();
}
/* Full 2-insn decomposition is needed. */
if (reload_in_progress || reload_completed)
temp = op0;
else
temp = gen_reg_rtx (mode);
if (GET_CODE (op1) == CONST_INT)
{
/* Emit them as real moves instead of a HIGH/LO_SUM,
this way CSE can see everything and reuse intermediate
values if it wants. */
if (TARGET_ARCH64
&& HOST_BITS_PER_WIDE_INT != 64
&& (INTVAL (op1) & 0x80000000) != 0)
{
emit_insn (gen_rtx_SET (VOIDmode,
temp,
gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx,
INTVAL (op1) & 0xfffffc00, 0)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode,
temp,
GEN_INT (INTVAL (op1) & 0xfffffc00)));
}
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_IOR (mode,
temp,
GEN_INT (INTVAL (op1) & 0x3ff))));
}
else
{
/* A symbol, emit in the traditional way. */
emit_insn (gen_rtx_SET (VOIDmode,
temp,
gen_rtx_HIGH (mode,
op1)));
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_LO_SUM (mode,
temp,
op1)));
}
}
/* Sparc-v9 code-model support. */
void
sparc_emit_set_symbolic_const64 (op0, op1, temp1)
rtx op0;
rtx op1;
rtx temp1;
{
switch (sparc_cmodel)
{
case CM_MEDLOW:
/* The range spanned by all instructions in the object is less
than 2^31 bytes (2GB) and the distance from any instruction
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
than 2^31 bytes (2GB).
The executable must be in the low 4TB of the virtual address
space.
sethi %hi(symbol), %temp
or %temp, %lo(symbol), %reg */
emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1)));
emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1)));
break;
case CM_MEDMID:
/* The range spanned by all instructions in the object is less
than 2^31 bytes (2GB) and the distance from any instruction
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
than 2^31 bytes (2GB).
The executable must be in the low 16TB of the virtual address
space.
sethi %h44(symbol), %temp1
or %temp1, %m44(symbol), %temp2
sllx %temp2, 12, %temp3
or %temp3, %l44(symbol), %reg */
emit_insn (gen_seth44 (op0, op1));
emit_insn (gen_setm44 (op0, op0, op1));
emit_insn (gen_rtx_SET (VOIDmode, temp1,
gen_rtx_ASHIFT (DImode, op0, GEN_INT (12))));
emit_insn (gen_setl44 (op0, temp1, op1));
break;
case CM_MEDANY:
/* The range spanned by all instructions in the object is less
than 2^31 bytes (2GB) and the distance from any instruction
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
than 2^31 bytes (2GB).
The executable can be placed anywhere in the virtual address
space.
sethi %hh(symbol), %temp1
sethi %lm(symbol), %temp2
or %temp1, %hm(symbol), %temp3
or %temp2, %lo(symbol), %temp4
sllx %temp3, 32, %temp5
or %temp4, %temp5, %reg */
/* Getting this right wrt. reloading is really tricky.
We _MUST_ have a seperate temporary at this point,
if we don't barf immediately instead of generating
incorrect code. */
if (temp1 == op0)
abort ();
emit_insn (gen_sethh (op0, op1));
emit_insn (gen_setlm (temp1, op1));
emit_insn (gen_sethm (op0, op0, op1));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, op0, GEN_INT (32))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, op0, temp1)));
emit_insn (gen_setlo (op0, op0, op1));
break;
case CM_EMBMEDANY:
/* Old old old backwards compatibility kruft here.
Essentially it is MEDLOW with a fixed 64-bit
virtual base added to all data segment addresses.
Text-segment stuff is computed like MEDANY, we can't
reuse the code above because the relocation knobs
look different.
Data segment: sethi %hi(symbol), %temp1
or %temp1, %lo(symbol), %temp2
add %temp2, EMBMEDANY_BASE_REG, %reg
Text segment: sethi %uhi(symbol), %temp1
sethi %hi(symbol), %temp2
or %temp1, %ulo(symbol), %temp3
or %temp2, %lo(symbol), %temp4
sllx %temp3, 32, %temp5
or %temp4, %temp5, %reg */
if (data_segment_operand (op1, GET_MODE (op1)))
{
emit_insn (gen_embmedany_sethi (temp1, op1));
emit_insn (gen_embmedany_brsum (op0, temp1));
emit_insn (gen_embmedany_losum (op0, op0, op1));
}
else
{
/* Getting this right wrt. reloading is really tricky.
We _MUST_ have a seperate temporary at this point,
so we barf immediately instead of generating
incorrect code. */
if (temp1 == op0)
abort ();
emit_insn (gen_embmedany_textuhi (op0, op1));
emit_insn (gen_embmedany_texthi (temp1, op1));
emit_insn (gen_embmedany_textulo (op0, op0, op1));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, op0, GEN_INT (32))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, op0, temp1)));
emit_insn (gen_embmedany_textlo (op0, op0, op1));
}
break;
default:
abort();
}
}
/* These avoid problems when cross compiling. If we do not
go through all this hair then the optimizer will see
invalid REG_EQUAL notes or in some cases none at all. */
static void sparc_emit_set_safe_HIGH64 PROTO ((rtx, HOST_WIDE_INT));
static rtx gen_safe_SET64 PROTO ((rtx, HOST_WIDE_INT));
static rtx gen_safe_OR64 PROTO ((rtx, HOST_WIDE_INT));
static rtx gen_safe_XOR64 PROTO ((rtx, HOST_WIDE_INT));
#if HOST_BITS_PER_WIDE_INT == 64
#define GEN_HIGHINT64(__x) GEN_INT ((__x) & 0xfffffc00)
#define GEN_INT64(__x) GEN_INT (__x)
#else
#define GEN_HIGHINT64(__x) \
gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, \
(__x) & 0xfffffc00, 0)
#define GEN_INT64(__x) \
gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, \
(__x) & 0xffffffff, \
((__x) & 0x80000000 \
? 0xffffffff : 0))
#endif
/* The optimizer is not to assume anything about exactly
which bits are set for a HIGH, they are unspecified.
Unfortunately this leads to many missed optimizations
during CSE. We mask out the non-HIGH bits, and matches
a plain movdi, to alleviate this problem. */
static void
sparc_emit_set_safe_HIGH64 (dest, val)
rtx dest;
HOST_WIDE_INT val;
{
emit_insn (gen_rtx_SET (VOIDmode, dest, GEN_HIGHINT64 (val)));
}
static rtx
gen_safe_SET64 (dest, val)
rtx dest;
HOST_WIDE_INT val;
{
return gen_rtx_SET (VOIDmode, dest, GEN_INT64 (val));
}
static rtx
gen_safe_OR64 (src, val)
rtx src;
HOST_WIDE_INT val;
{
return gen_rtx_IOR (DImode, src, GEN_INT64 (val));
}
static rtx
gen_safe_XOR64 (src, val)
rtx src;
HOST_WIDE_INT val;
{
return gen_rtx_XOR (DImode, src, GEN_INT64 (val));
}
/* Worker routines for 64-bit constant formation on arch64.
One of the key things to be doing in these emissions is
to create as many temp REGs as possible. This makes it
possible for half-built constants to be used later when
such values are similar to something required later on.
Without doing this, the optimizer cannot see such
opportunities. */
static void sparc_emit_set_const64_quick1
PROTO((rtx, rtx, unsigned HOST_WIDE_INT, int));
static void
sparc_emit_set_const64_quick1 (op0, temp, low_bits, is_neg)
rtx op0;
rtx temp;
unsigned HOST_WIDE_INT low_bits;
int is_neg;
{
unsigned HOST_WIDE_INT high_bits;
if (is_neg)
high_bits = (~low_bits) & 0xffffffff;
else
high_bits = low_bits;
sparc_emit_set_safe_HIGH64 (temp, high_bits);
if (!is_neg)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
}
else
{
/* If we are XOR'ing with -1, then we should emit a one's complement
instead. This way the combiner will notice logical operations
such as ANDN later on and substitute. */
if ((low_bits & 0x3ff) == 0x3ff)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_NOT (DImode, temp)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_XOR64 (temp,
(-0x400 | (low_bits & 0x3ff)))));
}
}
}
static void sparc_emit_set_const64_quick2
PROTO((rtx, rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int));
static void
sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_immediate, shift_count)
rtx op0;
rtx temp;
unsigned HOST_WIDE_INT high_bits;
unsigned HOST_WIDE_INT low_immediate;
int shift_count;
{
rtx temp2 = op0;
if ((high_bits & 0xfffffc00) != 0)
{
sparc_emit_set_safe_HIGH64 (temp, high_bits);
if ((high_bits & ~0xfffffc00) != 0)
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
else
temp2 = temp;
}
else
{
emit_insn (gen_safe_SET64 (temp, high_bits));
temp2 = temp;
}
/* Now shift it up into place. */
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, temp2,
GEN_INT (shift_count))));
/* If there is a low immediate part piece, finish up by
putting that in as well. */
if (low_immediate != 0)
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_safe_OR64 (op0, low_immediate)));
}
static void sparc_emit_set_const64_longway
PROTO((rtx, rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT));
/* Full 64-bit constant decomposition. Even though this is the
'worst' case, we still optimize a few things away. */
static void
sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits)
rtx op0;
rtx temp;
unsigned HOST_WIDE_INT high_bits;
unsigned HOST_WIDE_INT low_bits;
{
rtx sub_temp;
if (reload_in_progress || reload_completed)
sub_temp = op0;
else
sub_temp = gen_reg_rtx (DImode);
if ((high_bits & 0xfffffc00) != 0)
{
sparc_emit_set_safe_HIGH64 (temp, high_bits);
if ((high_bits & ~0xfffffc00) != 0)
emit_insn (gen_rtx_SET (VOIDmode,
sub_temp,
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
else
sub_temp = temp;
}
else
{
emit_insn (gen_safe_SET64 (temp, high_bits));
sub_temp = temp;
}
if (!reload_in_progress && !reload_completed)
{
rtx temp2 = gen_reg_rtx (DImode);
rtx temp3 = gen_reg_rtx (DImode);
rtx temp4 = gen_reg_rtx (DImode);
emit_insn (gen_rtx_SET (VOIDmode, temp4,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (32))));
sparc_emit_set_safe_HIGH64 (temp2, low_bits);
if ((low_bits & ~0xfffffc00) != 0)
{
emit_insn (gen_rtx_SET (VOIDmode, temp3,
gen_safe_OR64 (temp2, (low_bits & 0x3ff))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, temp4, temp3)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_PLUS (DImode, temp4, temp2)));
}
}
else
{
rtx low1 = GEN_INT ((low_bits >> (32 - 12)) & 0xfff);
rtx low2 = GEN_INT ((low_bits >> (32 - 12 - 12)) & 0xfff);
rtx low3 = GEN_INT ((low_bits >> (32 - 12 - 12 - 8)) & 0x0ff);
int to_shift = 12;
/* We are in the middle of reload, so this is really
painful. However we do still make an attempt to
avoid emitting truly stupid code. */
if (low1 != const0_rtx)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (to_shift))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_IOR (DImode, op0, low1)));
sub_temp = op0;
to_shift = 12;
}
else
{
to_shift += 12;
}
if (low2 != const0_rtx)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (to_shift))));
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_IOR (DImode, op0, low2)));
sub_temp = op0;
to_shift = 8;
}
else
{
to_shift += 8;
}
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_ASHIFT (DImode, sub_temp,
GEN_INT (to_shift))));
if (low3 != const0_rtx)
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_IOR (DImode, op0, low3)));
/* phew... */
}
}
/* Analyze a 64-bit constant for certain properties. */
static void analyze_64bit_constant
PROTO((unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
int *, int *, int *));
static void
analyze_64bit_constant (high_bits, low_bits, hbsp, lbsp, abbasp)
unsigned HOST_WIDE_INT high_bits, low_bits;
int *hbsp, *lbsp, *abbasp;
{
int lowest_bit_set, highest_bit_set, all_bits_between_are_set;
int i;
lowest_bit_set = highest_bit_set = -1;
i = 0;
do
{
if ((lowest_bit_set == -1)
&& ((low_bits >> i) & 1))
lowest_bit_set = i;
if ((highest_bit_set == -1)
&& ((high_bits >> (32 - i - 1)) & 1))
highest_bit_set = (64 - i - 1);
}
while (++i < 32
&& ((highest_bit_set == -1)
|| (lowest_bit_set == -1)));
if (i == 32)
{
i = 0;
do
{
if ((lowest_bit_set == -1)
&& ((high_bits >> i) & 1))
lowest_bit_set = i + 32;
if ((highest_bit_set == -1)
&& ((low_bits >> (32 - i - 1)) & 1))
highest_bit_set = 32 - i - 1;
}
while (++i < 32
&& ((highest_bit_set == -1)
|| (lowest_bit_set == -1)));
}
/* If there are no bits set this should have gone out
as one instruction! */
if (lowest_bit_set == -1
|| highest_bit_set == -1)
abort ();
all_bits_between_are_set = 1;
for (i = lowest_bit_set; i <= highest_bit_set; i++)
{
if (i < 32)
{
if ((low_bits & (1 << i)) != 0)
continue;
}
else
{
if ((high_bits & (1 << (i - 32))) != 0)
continue;
}
all_bits_between_are_set = 0;
break;
}
*hbsp = highest_bit_set;
*lbsp = lowest_bit_set;
*abbasp = all_bits_between_are_set;
}
static int const64_is_2insns
PROTO((unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT));
static int
const64_is_2insns (high_bits, low_bits)
unsigned HOST_WIDE_INT high_bits, low_bits;
{
int highest_bit_set, lowest_bit_set, all_bits_between_are_set;
if (high_bits == 0
|| high_bits == 0xffffffff)
return 1;
analyze_64bit_constant (high_bits, low_bits,
&highest_bit_set, &lowest_bit_set,
&all_bits_between_are_set);
if ((highest_bit_set == 63
|| lowest_bit_set == 0)
&& all_bits_between_are_set != 0)
return 1;
if ((highest_bit_set - lowest_bit_set) < 21)
return 1;
return 0;
}
static unsigned HOST_WIDE_INT create_simple_focus_bits
PROTO((unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
int, int));
static unsigned HOST_WIDE_INT
create_simple_focus_bits (high_bits, low_bits, lowest_bit_set, shift)
unsigned HOST_WIDE_INT high_bits, low_bits;
int lowest_bit_set, shift;
{
HOST_WIDE_INT hi, lo;
if (lowest_bit_set < 32)
{
lo = (low_bits >> lowest_bit_set) << shift;
hi = ((high_bits << (32 - lowest_bit_set)) << shift);
}
else
{
lo = 0;
hi = ((high_bits >> (lowest_bit_set - 32)) << shift);
}
if (hi & lo)
abort ();
return (hi | lo);
}
/* Here we are sure to be arch64 and this is an integer constant
being loaded into a register. Emit the most efficient
insn sequence possible. Detection of all the 1-insn cases
has been done already. */
void
sparc_emit_set_const64 (op0, op1)
rtx op0;
rtx op1;
{
unsigned HOST_WIDE_INT high_bits, low_bits;
int lowest_bit_set, highest_bit_set;
int all_bits_between_are_set;
rtx temp;
/* Sanity check that we know what we are working with. */
if (! TARGET_ARCH64
|| GET_CODE (op0) != REG
|| (REGNO (op0) >= SPARC_FIRST_FP_REG
&& REGNO (op0) <= SPARC_LAST_V9_FP_REG))
abort ();
if (reload_in_progress || reload_completed)
temp = op0;
else
temp = gen_reg_rtx (DImode);
if (GET_CODE (op1) != CONST_DOUBLE
&& GET_CODE (op1) != CONST_INT)
{
sparc_emit_set_symbolic_const64 (op0, op1, temp);
return;
}
if (GET_CODE (op1) == CONST_DOUBLE)
{
#if HOST_BITS_PER_WIDE_INT == 64
high_bits = (CONST_DOUBLE_LOW (op1) >> 32) & 0xffffffff;
low_bits = CONST_DOUBLE_LOW (op1) & 0xffffffff;
#else
high_bits = CONST_DOUBLE_HIGH (op1);
low_bits = CONST_DOUBLE_LOW (op1);
#endif
}
else
{
#if HOST_BITS_PER_WIDE_INT == 64
high_bits = ((INTVAL (op1) >> 32) & 0xffffffff);
low_bits = (INTVAL (op1) & 0xffffffff);
#else
high_bits = ((INTVAL (op1) < 0) ?
0xffffffff :
0x00000000);
low_bits = INTVAL (op1);
#endif
}
/* low_bits bits 0 --> 31
high_bits bits 32 --> 63 */
analyze_64bit_constant (high_bits, low_bits,
&highest_bit_set, &lowest_bit_set,
&all_bits_between_are_set);
/* First try for a 2-insn sequence. */
/* These situations are preferred because the optimizer can
* do more things with them:
* 1) mov -1, %reg
* sllx %reg, shift, %reg
* 2) mov -1, %reg
* srlx %reg, shift, %reg
* 3) mov some_small_const, %reg
* sllx %reg, shift, %reg
*/
if (((highest_bit_set == 63
|| lowest_bit_set == 0)
&& all_bits_between_are_set != 0)
|| ((highest_bit_set - lowest_bit_set) < 12))
{
HOST_WIDE_INT the_const = -1;
int shift = lowest_bit_set;
if ((highest_bit_set != 63
&& lowest_bit_set != 0)
|| all_bits_between_are_set == 0)
{
the_const =
create_simple_focus_bits (high_bits, low_bits,
lowest_bit_set, 0);
}
else if (lowest_bit_set == 0)
shift = -(63 - highest_bit_set);
if (! SPARC_SIMM13_P (the_const))
abort ();
emit_insn (gen_safe_SET64 (temp, the_const));
if (shift > 0)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_ASHIFT (DImode,
temp,
GEN_INT (shift))));
else if (shift < 0)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_LSHIFTRT (DImode,
temp,
GEN_INT (-shift))));
else
abort ();
return;
}
/* Now a range of 22 or less bits set somewhere.
* 1) sethi %hi(focus_bits), %reg
* sllx %reg, shift, %reg
* 2) sethi %hi(focus_bits), %reg
* srlx %reg, shift, %reg
*/
if ((highest_bit_set - lowest_bit_set) < 21)
{
unsigned HOST_WIDE_INT focus_bits =
create_simple_focus_bits (high_bits, low_bits,
lowest_bit_set, 10);
if (! SPARC_SETHI_P (focus_bits))
abort ();
sparc_emit_set_safe_HIGH64 (temp, focus_bits);
/* If lowest_bit_set == 10 then a sethi alone could have done it. */
if (lowest_bit_set < 10)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_LSHIFTRT (DImode, temp,
GEN_INT (10 - lowest_bit_set))));
else if (lowest_bit_set > 10)
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_rtx_ASHIFT (DImode, temp,
GEN_INT (lowest_bit_set - 10))));
else
abort ();
return;
}
/* 1) sethi %hi(low_bits), %reg
* or %reg, %lo(low_bits), %reg
* 2) sethi %hi(~low_bits), %reg
* xor %reg, %lo(-0x400 | (low_bits & 0x3ff)), %reg
*/
if (high_bits == 0
|| high_bits == 0xffffffff)
{
sparc_emit_set_const64_quick1 (op0, temp, low_bits,
(high_bits == 0xffffffff));
return;
}
/* Now, try 3-insn sequences. */
/* 1) sethi %hi(high_bits), %reg
* or %reg, %lo(high_bits), %reg
* sllx %reg, 32, %reg
*/
if (low_bits == 0)
{
sparc_emit_set_const64_quick2 (op0, temp, high_bits, 0, 32);
return;
}
/* We may be able to do something quick
when the constant is negated, so try that. */
if (const64_is_2insns ((~high_bits) & 0xffffffff,
(~low_bits) & 0xfffffc00))
{
/* NOTE: The trailing bits get XOR'd so we need the
non-negated bits, not the negated ones. */
unsigned HOST_WIDE_INT trailing_bits = low_bits & 0x3ff;
if ((((~high_bits) & 0xffffffff) == 0
&& ((~low_bits) & 0x80000000) == 0)
|| (((~high_bits) & 0xffffffff) == 0xffffffff
&& ((~low_bits) & 0x80000000) != 0))
{
int fast_int = (~low_bits & 0xffffffff);
if ((SPARC_SETHI_P (fast_int)
&& (~high_bits & 0xffffffff) == 0)
|| SPARC_SIMM13_P (fast_int))
emit_insn (gen_safe_SET64 (temp, fast_int));
else
sparc_emit_set_const64 (temp, GEN_INT64 (fast_int));
}
else
{
rtx negated_const;
#if HOST_BITS_PER_WIDE_INT == 64
negated_const = GEN_INT (((~low_bits) & 0xfffffc00) |
(((HOST_WIDE_INT)((~high_bits) & 0xffffffff))<<32));
#else
negated_const = gen_rtx_CONST_DOUBLE (DImode, const0_rtx,
(~low_bits) & 0xfffffc00,
(~high_bits) & 0xffffffff);
#endif
sparc_emit_set_const64 (temp, negated_const);
}
/* If we are XOR'ing with -1, then we should emit a one's complement
instead. This way the combiner will notice logical operations
such as ANDN later on and substitute. */
if (trailing_bits == 0x3ff)
{
emit_insn (gen_rtx_SET (VOIDmode, op0,
gen_rtx_NOT (DImode, temp)));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode,
op0,
gen_safe_XOR64 (temp,
(-0x400 | trailing_bits))));
}
return;
}
/* 1) sethi %hi(xxx), %reg
* or %reg, %lo(xxx), %reg
* sllx %reg, yyy, %reg
*
* ??? This is just a generalized version of the low_bits==0
* thing above, FIXME...
*/
if ((highest_bit_set - lowest_bit_set) < 32)
{
unsigned HOST_WIDE_INT focus_bits =
create_simple_focus_bits (high_bits, low_bits,
lowest_bit_set, 0);
/* We can't get here in this state. */
if (highest_bit_set < 32
|| lowest_bit_set >= 32)
abort ();
/* So what we know is that the set bits straddle the
middle of the 64-bit word. */
sparc_emit_set_const64_quick2 (op0, temp,
focus_bits, 0,
lowest_bit_set);
return;
}
/* 1) sethi %hi(high_bits), %reg
* or %reg, %lo(high_bits), %reg
* sllx %reg, 32, %reg
* or %reg, low_bits, %reg
*/
if (SPARC_SIMM13_P(low_bits)
&& ((int)low_bits > 0))
{
sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_bits, 32);
return;
}
/* The easiest way when all else fails, is full decomposition. */
#if 0
printf ("sparc_emit_set_const64: Hard constant [%08lx%08lx] neg[%08lx%08lx]\n",
high_bits, low_bits, ~high_bits, ~low_bits);
#endif
sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits);
}
/* X and Y are two things to compare using CODE. Emit the compare insn and
return the rtx for the cc reg in the proper mode. */
rtx
gen_compare_reg (code, x, y)
enum rtx_code code;
rtx x, y;
{
enum machine_mode mode = SELECT_CC_MODE (code, x, y);
rtx cc_reg;
/* ??? We don't have movcc patterns so we cannot generate pseudo regs for the
fcc regs (cse can't tell they're really call clobbered regs and will
remove a duplicate comparison even if there is an intervening function
call - it will then try to reload the cc reg via an int reg which is why
we need the movcc patterns). It is possible to provide the movcc
patterns by using the ldxfsr/stxfsr v9 insns. I tried it: you need two
registers (say %g1,%g5) and it takes about 6 insns. A better fix would be
to tell cse that CCFPE mode registers (even pseudos) are call
clobbered. */
/* ??? This is an experiment. Rather than making changes to cse which may
or may not be easy/clean, we do our own cse. This is possible because
we will generate hard registers. Cse knows they're call clobbered (it
doesn't know the same thing about pseudos). If we guess wrong, no big
deal, but if we win, great! */
if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
#if 1 /* experiment */
{
int reg;
/* We cycle through the registers to ensure they're all exercised. */
static int next_fcc_reg = 0;
/* Previous x,y for each fcc reg. */
static rtx prev_args[4][2];
/* Scan prev_args for x,y. */
for (reg = 0; reg < 4; reg++)
if (prev_args[reg][0] == x && prev_args[reg][1] == y)
break;
if (reg == 4)
{
reg = next_fcc_reg;
prev_args[reg][0] = x;
prev_args[reg][1] = y;
next_fcc_reg = (next_fcc_reg + 1) & 3;
}
cc_reg = gen_rtx_REG (mode, reg + SPARC_FIRST_V9_FCC_REG);
}
#else
cc_reg = gen_reg_rtx (mode);
#endif /* ! experiment */
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
cc_reg = gen_rtx_REG (mode, SPARC_FCC_REG);
else
cc_reg = gen_rtx_REG (mode, SPARC_ICC_REG);
emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
gen_rtx_COMPARE (mode, x, y)));
return cc_reg;
}
/* This function is used for v9 only.
CODE is the code for an Scc's comparison.
OPERANDS[0] is the target of the Scc insn.
OPERANDS[1] is the value we compare against const0_rtx (which hasn't
been generated yet).
This function is needed to turn
(set (reg:SI 110)
(gt (reg:CCX 100 %icc)
(const_int 0)))
into
(set (reg:SI 110)
(gt:DI (reg:CCX 100 %icc)
(const_int 0)))
IE: The instruction recognizer needs to see the mode of the comparison to
find the right instruction. We could use "gt:DI" right in the
define_expand, but leaving it out allows us to handle DI, SI, etc.
We refer to the global sparc compare operands sparc_compare_op0 and
sparc_compare_op1. */
int
gen_v9_scc (compare_code, operands)
enum rtx_code compare_code;
register rtx *operands;
{
rtx temp, op0, op1;
if (! TARGET_ARCH64
&& (GET_MODE (sparc_compare_op0) == DImode
|| GET_MODE (operands[0]) == DImode))
return 0;
/* Handle the case where operands[0] == sparc_compare_op0.
We "early clobber" the result. */
if (REGNO (operands[0]) == REGNO (sparc_compare_op0))
{
op0 = gen_reg_rtx (GET_MODE (sparc_compare_op0));
emit_move_insn (op0, sparc_compare_op0);
}
else
op0 = sparc_compare_op0;
/* For consistency in the following. */
op1 = sparc_compare_op1;
/* Try to use the movrCC insns. */
if (TARGET_ARCH64
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
&& op1 == const0_rtx
&& v9_regcmp_p (compare_code))
{
/* Special case for op0 != 0. This can be done with one instruction if
operands[0] == sparc_compare_op0. We don't assume they are equal
now though. */
if (compare_code == NE
&& GET_MODE (operands[0]) == DImode
&& GET_MODE (op0) == DImode)
{
emit_insn (gen_rtx_SET (VOIDmode, operands[0], op0));
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_IF_THEN_ELSE (DImode,
gen_rtx_fmt_ee (compare_code, DImode,
op0, const0_rtx),
const1_rtx,
operands[0])));
return 1;
}
emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
if (GET_MODE (op0) != DImode)
{
temp = gen_reg_rtx (DImode);
convert_move (temp, op0, 0);
}
else
temp = op0;
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
gen_rtx_fmt_ee (compare_code, DImode,
temp, const0_rtx),
const1_rtx,
operands[0])));
return 1;
}
else
{
operands[1] = gen_compare_reg (compare_code, op0, op1);
switch (GET_MODE (operands[1]))
{
case CCmode :
case CCXmode :
case CCFPEmode :
case CCFPmode :
break;
default :
abort ();
}
emit_insn (gen_rtx_SET (VOIDmode, operands[0], const0_rtx));
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
gen_rtx_fmt_ee (compare_code,
GET_MODE (operands[1]),
operands[1], const0_rtx),
const1_rtx, operands[0])));
return 1;
}
}
/* Emit a conditional jump insn for the v9 architecture using comparison code
CODE and jump target LABEL.
This function exists to take advantage of the v9 brxx insns. */
void
emit_v9_brxx_insn (code, op0, label)
enum rtx_code code;
rtx op0, label;
{
emit_jump_insn (gen_rtx_SET (VOIDmode,
pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx_fmt_ee (code, GET_MODE (op0),
op0, const0_rtx),
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx)));
}
/* Return nonzero if a return peephole merging return with
setting of output register is ok. */
int
leaf_return_peephole_ok ()
{
return (actual_fsize == 0);
}
/* Return nonzero if TRIAL can go into the function epilogue's
delay slot. SLOT is the slot we are trying to fill. */
int
eligible_for_epilogue_delay (trial, slot)
rtx trial;
int slot;
{
rtx pat, src;
if (slot >= 1)
return 0;
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
return 0;
if (get_attr_length (trial) != 1)
return 0;
/* If %g0 is live, there are lots of things we can't handle.
Rather than trying to find them all now, let's punt and only
optimize things as necessary. */
if (TARGET_LIVE_G0)
return 0;
/* In the case of a true leaf function, anything can go into the delay slot.
A delay slot only exists however if the frame size is zero, otherwise
we will put an insn to adjust the stack after the return. */
if (current_function_uses_only_leaf_regs)
{
if (leaf_return_peephole_ok ())
return ((get_attr_in_uncond_branch_delay (trial)
== IN_BRANCH_DELAY_TRUE));
return 0;
}
/* If only trivial `restore' insns work, nothing can go in the
delay slot. */
else if (TARGET_BROKEN_SAVERESTORE)
return 0;
pat = PATTERN (trial);
/* Otherwise, only operations which can be done in tandem with
a `restore' insn can go into the delay slot. */
if (GET_CODE (SET_DEST (pat)) != REG
|| REGNO (SET_DEST (pat)) >= 32
|| REGNO (SET_DEST (pat)) < 24)
return 0;
/* The set of insns matched here must agree precisely with the set of
patterns paired with a RETURN in sparc.md. */
src = SET_SRC (pat);
/* This matches "*return_[qhs]i" or even "*return_di" on TARGET_ARCH64. */
if (arith_operand (src, GET_MODE (src)))
{
if (TARGET_ARCH64)
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
else
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
}
/* This matches "*return_di". */
else if (arith_double_operand (src, GET_MODE (src)))
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
/* This matches "*return_sf_no_fpu". */
else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode)
&& register_operand (src, SFmode))
return 1;
/* This matches "*return_addsi". */
else if (GET_CODE (src) == PLUS
&& arith_operand (XEXP (src, 0), SImode)
&& arith_operand (XEXP (src, 1), SImode)
&& (register_operand (XEXP (src, 0), SImode)
|| register_operand (XEXP (src, 1), SImode)))
return 1;
/* This matches "*return_adddi". */
else if (GET_CODE (src) == PLUS
&& arith_double_operand (XEXP (src, 0), DImode)
&& arith_double_operand (XEXP (src, 1), DImode)
&& (register_operand (XEXP (src, 0), DImode)
|| register_operand (XEXP (src, 1), DImode)))
return 1;
return 0;
}
static int
check_return_regs (x)
rtx x;
{
switch (GET_CODE (x))
{
case REG:
return IN_OR_GLOBAL_P (x);
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 1;
case SET:
case IOR:
case AND:
case XOR:
case PLUS:
case MINUS:
if (check_return_regs (XEXP (x, 1)) == 0)
return 0;
case NOT:
case NEG:
case MEM:
return check_return_regs (XEXP (x, 0));
default:
return 0;
}
}
/* Return 1 if TRIAL references only in and global registers. */
int
eligible_for_return_delay (trial)
rtx trial;
{
if (GET_CODE (PATTERN (trial)) != SET)
return 0;
return check_return_regs (PATTERN (trial));
}
int
short_branch (uid1, uid2)
int uid1, uid2;
{
unsigned int delta = insn_addresses[uid1] - insn_addresses[uid2];
if (delta + 1024 < 2048)
return 1;
/* warning ("long branch, distance %d", delta); */
return 0;
}
/* Return non-zero if REG is not used after INSN.
We assume REG is a reload reg, and therefore does
not live past labels or calls or jumps. */
int
reg_unused_after (reg, insn)
rtx reg;
rtx insn;
{
enum rtx_code code, prev_code = UNKNOWN;
while ((insn = NEXT_INSN (insn)))
{
if (prev_code == CALL_INSN && call_used_regs[REGNO (reg)])
return 1;
code = GET_CODE (insn);
if (GET_CODE (insn) == CODE_LABEL)
return 1;
if (GET_RTX_CLASS (code) == 'i')
{
rtx set = single_set (insn);
int in_src = set && reg_overlap_mentioned_p (reg, SET_SRC (set));
if (set && in_src)
return 0;
if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
return 1;
if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
return 0;
}
prev_code = code;
}
return 1;
}
/* The table we use to reference PIC data. */
static rtx global_offset_table;
/* The function we use to get at it. */
static rtx get_pc_symbol;
static char get_pc_symbol_name[256];
/* Ensure that we are not using patterns that are not OK with PIC. */
int
check_pic (i)
int i;
{
switch (flag_pic)
{
case 1:
if (GET_CODE (recog_operand[i]) == SYMBOL_REF
|| (GET_CODE (recog_operand[i]) == CONST
&& ! (GET_CODE (XEXP (recog_operand[i], 0)) == MINUS
&& (XEXP (XEXP (recog_operand[i], 0), 0)
== global_offset_table)
&& (GET_CODE (XEXP (XEXP (recog_operand[i], 0), 1))
== CONST))))
abort ();
case 2:
default:
return 1;
}
}
/* Return true if X is an address which needs a temporary register when
reloaded while generating PIC code. */
int
pic_address_needs_scratch (x)
rtx x;
{
/* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
return 1;
return 0;
}
/* Legitimize PIC addresses. If the address is already position-independent,
we return ORIG. Newly generated position-independent addresses go into a
reg. This is REG if non zero, otherwise we allocate register(s) as
necessary. */
rtx
legitimize_pic_address (orig, mode, reg)
rtx orig;
enum machine_mode mode ATTRIBUTE_UNUSED;
rtx reg;
{
if (GET_CODE (orig) == SYMBOL_REF)
{
rtx pic_ref, address;
rtx insn;
if (reg == 0)
{
if (reload_in_progress || reload_completed)
abort ();
else
reg = gen_reg_rtx (Pmode);
}
if (flag_pic == 2)
{
/* If not during reload, allocate another temp reg here for loading
in the address, so that these instructions can be optimized
properly. */
rtx temp_reg = ((reload_in_progress || reload_completed)
? reg : gen_reg_rtx (Pmode));
/* Must put the SYMBOL_REF inside an UNSPEC here so that cse
won't get confused into thinking that these two instructions
are loading in the true address of the symbol. If in the
future a PIC rtx exists, that should be used instead. */
if (Pmode == SImode)
{
emit_insn (gen_movsi_high_pic (temp_reg, orig));
emit_insn (gen_movsi_lo_sum_pic (temp_reg, temp_reg, orig));
}
else
{
emit_insn (gen_movdi_high_pic (temp_reg, orig));
emit_insn (gen_movdi_lo_sum_pic (temp_reg, temp_reg, orig));
}
address = temp_reg;
}
else
address = orig;
pic_ref = gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode,
pic_offset_table_rtx, address));
current_function_uses_pic_offset_table = 1;
RTX_UNCHANGING_P (pic_ref) = 1;
insn = emit_move_insn (reg, pic_ref);
/* Put a REG_EQUAL note on this insn, so that it can be optimized
by loop. */
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig,
REG_NOTES (insn));
return reg;
}
else if (GET_CODE (orig) == CONST)
{
rtx base, offset;
if (GET_CODE (XEXP (orig, 0)) == PLUS
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
return orig;
if (reg == 0)
{
if (reload_in_progress || reload_completed)
abort ();
else
reg = gen_reg_rtx (Pmode);
}
if (GET_CODE (XEXP (orig, 0)) == PLUS)
{
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
else
abort ();
if (GET_CODE (offset) == CONST_INT)
{
if (SMALL_INT (offset))
return plus_constant_for_output (base, INTVAL (offset));
else if (! reload_in_progress && ! reload_completed)
offset = force_reg (Pmode, offset);
else
/* If we reach here, then something is seriously wrong. */
abort ();
}
return gen_rtx_PLUS (Pmode, base, offset);
}
else if (GET_CODE (orig) == LABEL_REF)
/* ??? Why do we do this? */
/* Now movsi_pic_label_ref uses it, but we ought to be checking that
the register is live instead, in case it is eliminated. */
current_function_uses_pic_offset_table = 1;
return orig;
}
/* Return the RTX for insns to set the PIC register. */
static rtx
pic_setup_code ()
{
rtx seq;
start_sequence ();
emit_insn (gen_get_pc (pic_offset_table_rtx, global_offset_table,
get_pc_symbol));
seq = gen_sequence ();
end_sequence ();
return seq;
}
/* Emit special PIC prologues and epilogues. */
void
finalize_pic ()
{
/* Labels to get the PC in the prologue of this function. */
int orig_flag_pic = flag_pic;
rtx insn;
if (current_function_uses_pic_offset_table == 0)
return;
if (! flag_pic)
abort ();
/* If we havn't emitted the special get_pc helper function, do so now. */
if (get_pc_symbol_name[0] == 0)
{
int align;
ASM_GENERATE_INTERNAL_LABEL (get_pc_symbol_name, "LGETPC", 0);
text_section ();
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, "LGETPC", 0);
fputs ("\tretl\n\tadd %o7,%l7,%l7\n", asm_out_file);
}
/* Initialize every time through, since we can't easily
know this to be permanent. */
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
get_pc_symbol = gen_rtx_SYMBOL_REF (Pmode, get_pc_symbol_name);
flag_pic = 0;
emit_insn_after (pic_setup_code (), get_insns ());
/* Insert the code in each nonlocal goto receiver.
If you make changes here or to the nonlocal_goto_receiver
pattern, make sure the unspec_volatile numbers still
match. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE
&& XINT (PATTERN (insn), 1) == 5)
emit_insn_after (pic_setup_code (), insn);
flag_pic = orig_flag_pic;
/* Need to emit this whether or not we obey regdecls,
since setjmp/longjmp can cause life info to screw up.
??? In the case where we don't obey regdecls, this is not sufficient
since we may not fall out the bottom. */
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
}
/* Return 1 if RTX is a MEM which is known to be aligned to at
least an 8 byte boundary. */
int
mem_min_alignment (mem, desired)
rtx mem;
int desired;
{
rtx addr, base, offset;
/* If it's not a MEM we can't accept it. */
if (GET_CODE (mem) != MEM)
return 0;
addr = XEXP (mem, 0);
base = offset = NULL_RTX;
if (GET_CODE (addr) == PLUS)
{
if (GET_CODE (XEXP (addr, 0)) == REG)
{
base = XEXP (addr, 0);
/* What we are saying here is that if the base
REG is aligned properly, the compiler will make
sure any REG based index upon it will be so
as well. */
if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
offset = XEXP (addr, 1);
else
offset = const0_rtx;
}
}
else if (GET_CODE (addr) == REG)
{
base = addr;
offset = const0_rtx;
}
if (base != NULL_RTX)
{
int regno = REGNO (base);
if (regno != FRAME_POINTER_REGNUM
&& regno != STACK_POINTER_REGNUM)
{
/* Check if the compiler has recorded some information
about the alignment of the base REG. If reload has
completed, we already matched with proper alignments. */
if (((regno_pointer_align != NULL
&& REGNO_POINTER_ALIGN (regno) >= desired)
|| reload_completed)
&& ((INTVAL (offset) & (desired - 1)) == 0))
return 1;
}
else
{
if (((INTVAL (offset) - SPARC_STACK_BIAS) & (desired - 1)) == 0)
return 1;
}
}
else if (! TARGET_UNALIGNED_DOUBLES
|| CONSTANT_P (addr)
|| GET_CODE (addr) == LO_SUM)
{
/* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES
is true, in which case we can only assume that an access is aligned if
it is to a constant address, or the address involves a LO_SUM. */
return 1;
}
/* An obviously unaligned address. */
return 0;
}
/* Vectors to keep interesting information about registers where it can easily
be got. We use to use the actual mode value as the bit number, but there
are more than 32 modes now. Instead we use two tables: one indexed by
hard register number, and one indexed by mode. */
/* The purpose of sparc_mode_class is to shrink the range of modes so that
they all fit (as bit numbers) in a 32 bit word (again). Each real mode is
mapped into one sparc_mode_class mode. */
enum sparc_mode_class {
S_MODE, D_MODE, T_MODE, O_MODE,
SF_MODE, DF_MODE, TF_MODE, OF_MODE,
CC_MODE, CCFP_MODE
};
/* Modes for single-word and smaller quantities. */
#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
/* Modes for double-word and smaller quantities. */
#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
/* Modes for quad-word and smaller quantities. */
#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
/* Modes for single-float quantities. We must allow any single word or
smaller quantity. This is because the fix/float conversion instructions
take integer inputs/outputs from the float registers. */
#define SF_MODES (S_MODES)
/* Modes for double-float and smaller quantities. */
#define DF_MODES (S_MODES | D_MODES)
#define DF_MODES64 DF_MODES
/* Modes for double-float only quantities. */
#define DF_ONLY_MODES ((1 << (int) DF_MODE) | (1 << (int) D_MODE))
/* Modes for double-float and larger quantities. */
#define DF_UP_MODES (DF_ONLY_MODES | TF_ONLY_MODES)
/* Modes for quad-float only quantities. */
#define TF_ONLY_MODES (1 << (int) TF_MODE)
/* Modes for quad-float and smaller quantities. */
#define TF_MODES (DF_MODES | TF_ONLY_MODES)
#define TF_MODES64 (DF_MODES64 | TF_ONLY_MODES)
/* Modes for condition codes. */
#define CC_MODES (1 << (int) CC_MODE)
#define CCFP_MODES (1 << (int) CCFP_MODE)
/* Value is 1 if register/mode pair is acceptable on sparc.
The funny mixture of D and T modes is because integer operations
do not specially operate on tetra quantities, so non-quad-aligned
registers can hold quadword quantities (except %o4 and %i4 because
they cross fixed registers). */
/* This points to either the 32 bit or the 64 bit version. */
int *hard_regno_mode_classes;
static int hard_32bit_mode_classes[] = {
S_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
T_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
/* FP regs f32 to f63. Only the even numbered registers actually exist,
and none can hold SFmode/SImode values. */
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
/* %fcc[0123] */
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
/* %icc */
CC_MODES
};
static int hard_64bit_mode_classes[] = {
D_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES,
TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES,
TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES,
TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES,
/* FP regs f32 to f63. Only the even numbered registers actually exist,
and none can hold SFmode/SImode values. */
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0,
/* %fcc[0123] */
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
/* %icc */
CC_MODES
};
int sparc_mode_class [NUM_MACHINE_MODES];
enum reg_class sparc_regno_reg_class[FIRST_PSEUDO_REGISTER];
static void
sparc_init_modes ()
{
int i;
for (i = 0; i < NUM_MACHINE_MODES; i++)
{
switch (GET_MODE_CLASS (i))
{
case MODE_INT:
case MODE_PARTIAL_INT:
case MODE_COMPLEX_INT:
if (GET_MODE_SIZE (i) <= 4)
sparc_mode_class[i] = 1 << (int) S_MODE;
else if (GET_MODE_SIZE (i) == 8)
sparc_mode_class[i] = 1 << (int) D_MODE;
else if (GET_MODE_SIZE (i) == 16)
sparc_mode_class[i] = 1 << (int) T_MODE;
else if (GET_MODE_SIZE (i) == 32)
sparc_mode_class[i] = 1 << (int) O_MODE;
else
sparc_mode_class[i] = 0;
break;
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:
if (GET_MODE_SIZE (i) <= 4)
sparc_mode_class[i] = 1 << (int) SF_MODE;
else if (GET_MODE_SIZE (i) == 8)
sparc_mode_class[i] = 1 << (int) DF_MODE;
else if (GET_MODE_SIZE (i) == 16)
sparc_mode_class[i] = 1 << (int) TF_MODE;
else if (GET_MODE_SIZE (i) == 32)
sparc_mode_class[i] = 1 << (int) OF_MODE;
else
sparc_mode_class[i] = 0;
break;
case MODE_CC:
default:
/* mode_class hasn't been initialized yet for EXTRA_CC_MODES, so
we must explicitly check for them here. */
if (i == (int) CCFPmode || i == (int) CCFPEmode)
sparc_mode_class[i] = 1 << (int) CCFP_MODE;
else if (i == (int) CCmode || i == (int) CC_NOOVmode
|| i == (int) CCXmode || i == (int) CCX_NOOVmode)
sparc_mode_class[i] = 1 << (int) CC_MODE;
else
sparc_mode_class[i] = 0;
break;
}
}
if (TARGET_ARCH64)
hard_regno_mode_classes = hard_64bit_mode_classes;
else
hard_regno_mode_classes = hard_32bit_mode_classes;
/* Initialize the array used by REGNO_REG_CLASS. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
if (i < 16 && TARGET_V8PLUS)
sparc_regno_reg_class[i] = I64_REGS;
else if (i < 32)
sparc_regno_reg_class[i] = GENERAL_REGS;
else if (i < 64)
sparc_regno_reg_class[i] = FP_REGS;
else if (i < 96)
sparc_regno_reg_class[i] = EXTRA_FP_REGS;
else if (i < 100)
sparc_regno_reg_class[i] = FPCC_REGS;
else
sparc_regno_reg_class[i] = NO_REGS;
}
}
/* Save non call used registers from LOW to HIGH at BASE+OFFSET.
N_REGS is the number of 4-byte regs saved thus far. This applies even to
v9 int regs as it simplifies the code. */
static int
save_regs (file, low, high, base, offset, n_regs, real_offset)
FILE *file;
int low, high;
const char *base;
int offset;
int n_regs;
int real_offset;
{
int i;
if (TARGET_ARCH64 && high <= 32)
{
for (i = low; i < high; i++)
{
if (regs_ever_live[i] && ! call_used_regs[i])
{
fprintf (file, "\tstx\t%s, [%s+%d]\n",
reg_names[i], base, offset + 4 * n_regs);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", i, real_offset + 4 * n_regs);
n_regs += 2;
}
}
}
else
{
for (i = low; i < high; i += 2)
{
if (regs_ever_live[i] && ! call_used_regs[i])
{
if (regs_ever_live[i+1] && ! call_used_regs[i+1])
{
fprintf (file, "\tstd\t%s, [%s+%d]\n",
reg_names[i], base, offset + 4 * n_regs);
if (dwarf2out_do_frame ())
{
char *l = dwarf2out_cfi_label ();
dwarf2out_reg_save (l, i, real_offset + 4 * n_regs);
dwarf2out_reg_save (l, i+1, real_offset + 4 * n_regs + 4);
}
n_regs += 2;
}
else
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
reg_names[i], base, offset + 4 * n_regs);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", i, real_offset + 4 * n_regs);
n_regs += 2;
}
}
else
{
if (regs_ever_live[i+1] && ! call_used_regs[i+1])
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
reg_names[i+1], base, offset + 4 * n_regs + 4);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", i + 1, real_offset + 4 * n_regs + 4);
n_regs += 2;
}
}
}
}
return n_regs;
}
/* Restore non call used registers from LOW to HIGH at BASE+OFFSET.
N_REGS is the number of 4-byte regs saved thus far. This applies even to
v9 int regs as it simplifies the code. */
static int
restore_regs (file, low, high, base, offset, n_regs)
FILE *file;
int low, high;
const char *base;
int offset;
int n_regs;
{
int i;
if (TARGET_ARCH64 && high <= 32)
{
for (i = low; i < high; i++)
{
if (regs_ever_live[i] && ! call_used_regs[i])
fprintf (file, "\tldx\t[%s+%d], %s\n",
base, offset + 4 * n_regs, reg_names[i]),
n_regs += 2;
}
}
else
{
for (i = low; i < high; i += 2)
{
if (regs_ever_live[i] && ! call_used_regs[i])
if (regs_ever_live[i+1] && ! call_used_regs[i+1])
fprintf (file, "\tldd\t[%s+%d], %s\n",
base, offset + 4 * n_regs, reg_names[i]),
n_regs += 2;
else
fprintf (file, "\tld\t[%s+%d],%s\n",
base, offset + 4 * n_regs, reg_names[i]),
n_regs += 2;
else if (regs_ever_live[i+1] && ! call_used_regs[i+1])
fprintf (file, "\tld\t[%s+%d],%s\n",
base, offset + 4 * n_regs + 4, reg_names[i+1]),
n_regs += 2;
}
}
return n_regs;
}
/* Static variables we want to share between prologue and epilogue. */
/* Number of live general or floating point registers needed to be saved
(as 4-byte quantities). This is only done if TARGET_EPILOGUE. */
static int num_gfregs;
/* Compute the frame size required by the function. This function is called
during the reload pass and also by output_function_prologue(). */
int
compute_frame_size (size, leaf_function)
int size;
int leaf_function;
{
int n_regs = 0, i;
int outgoing_args_size = (current_function_outgoing_args_size
+ REG_PARM_STACK_SPACE (current_function_decl));
if (TARGET_EPILOGUE)
{
/* N_REGS is the number of 4-byte regs saved thus far. This applies
even to v9 int regs to be consistent with save_regs/restore_regs. */
if (TARGET_ARCH64)
{
for (i = 0; i < 8; i++)
if (regs_ever_live[i] && ! call_used_regs[i])
n_regs += 2;
}
else
{
for (i = 0; i < 8; i += 2)
if ((regs_ever_live[i] && ! call_used_regs[i])
|| (regs_ever_live[i+1] && ! call_used_regs[i+1]))
n_regs += 2;
}
for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
if ((regs_ever_live[i] && ! call_used_regs[i])
|| (regs_ever_live[i+1] && ! call_used_regs[i+1]))
n_regs += 2;
}
/* Set up values for use in `function_epilogue'. */
num_gfregs = n_regs;
if (leaf_function && n_regs == 0
&& size == 0 && current_function_outgoing_args_size == 0)
{
actual_fsize = apparent_fsize = 0;
}
else
{
/* We subtract STARTING_FRAME_OFFSET, remember it's negative.
The stack bias (if any) is taken out to undo its effects. */
apparent_fsize = (size - STARTING_FRAME_OFFSET + SPARC_STACK_BIAS + 7) & -8;
apparent_fsize += n_regs * 4;
actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8);
}
/* Make sure nothing can clobber our register windows.
If a SAVE must be done, or there is a stack-local variable,
the register window area must be allocated.
??? For v8 we apparently need an additional 8 bytes of reserved space. */
if (leaf_function == 0 || size > 0)
actual_fsize += (16 * UNITS_PER_WORD) + (TARGET_ARCH64 ? 0 : 8);
return SPARC_STACK_ALIGN (actual_fsize);
}
/* Build a (32 bit) big number in a register. */
/* ??? We may be able to use the set macro here too. */
static void
build_big_number (file, num, reg)
FILE *file;
int num;
const char *reg;
{
if (num >= 0 || ! TARGET_ARCH64)
{
fprintf (file, "\tsethi\t%%hi(%d), %s\n", num, reg);
if ((num & 0x3ff) != 0)
fprintf (file, "\tor\t%s, %%lo(%d), %s\n", reg, num, reg);
}
else /* num < 0 && TARGET_ARCH64 */
{
/* Sethi does not sign extend, so we must use a little trickery
to use it for negative numbers. Invert the constant before
loading it in, then use xor immediate to invert the loaded bits
(along with the upper 32 bits) to the desired constant. This
works because the sethi and immediate fields overlap. */
int asize = num;
int inv = ~asize;
int low = -0x400 + (asize & 0x3FF);
fprintf (file, "\tsethi\t%%hi(%d), %s\n\txor\t%s, %d, %s\n",
inv, reg, reg, low, reg);
}
}
/* Output code for the function prologue. */
void
output_function_prologue (file, size, leaf_function)
FILE *file;
int size;
int leaf_function;
{
/* Need to use actual_fsize, since we are also allocating
space for our callee (and our own register save area). */
actual_fsize = compute_frame_size (size, leaf_function);
if (leaf_function)
{
frame_base_name = "%sp";
frame_base_offset = actual_fsize + SPARC_STACK_BIAS;
}
else
{
frame_base_name = "%fp";
frame_base_offset = SPARC_STACK_BIAS;
}
/* This is only for the human reader. */
fprintf (file, "\t%s#PROLOGUE# 0\n", ASM_COMMENT_START);
if (actual_fsize == 0)
/* do nothing. */ ;
else if (! leaf_function && ! TARGET_BROKEN_SAVERESTORE)
{
if (actual_fsize <= 4096)
fprintf (file, "\tsave\t%%sp, -%d, %%sp\n", actual_fsize);
else if (actual_fsize <= 8192)
{
fprintf (file, "\tsave\t%%sp, -4096, %%sp\n");
fprintf (file, "\tadd\t%%sp, -%d, %%sp\n", actual_fsize - 4096);
}
else
{
build_big_number (file, -actual_fsize, "%g1");
fprintf (file, "\tsave\t%%sp, %%g1, %%sp\n");
}
}
else if (! leaf_function && TARGET_BROKEN_SAVERESTORE)
{
/* We assume the environment will properly handle or otherwise avoid
trouble associated with an interrupt occurring after the `save' or
trap occurring during it. */
fprintf (file, "\tsave\n");
if (actual_fsize <= 4096)
fprintf (file, "\tadd\t%%fp, -%d, %%sp\n", actual_fsize);
else if (actual_fsize <= 8192)
{
fprintf (file, "\tadd\t%%fp, -4096, %%sp\n");
fprintf (file, "\tadd\t%%fp, -%d, %%sp\n", actual_fsize - 4096);
}
else
{
build_big_number (file, -actual_fsize, "%g1");
fprintf (file, "\tadd\t%%fp, %%g1, %%sp\n");
}
}
else /* leaf function */
{
if (actual_fsize <= 4096)
fprintf (file, "\tadd\t%%sp, -%d, %%sp\n", actual_fsize);
else if (actual_fsize <= 8192)
{
fprintf (file, "\tadd\t%%sp, -4096, %%sp\n");
fprintf (file, "\tadd\t%%sp, -%d, %%sp\n", actual_fsize - 4096);
}
else
{
build_big_number (file, -actual_fsize, "%g1");
fprintf (file, "\tadd\t%%sp, %%g1, %%sp\n");
}
}
if (dwarf2out_do_frame () && actual_fsize)
{
char *label = dwarf2out_cfi_label ();
/* The canonical frame address refers to the top of the frame. */
dwarf2out_def_cfa (label, (leaf_function ? STACK_POINTER_REGNUM
: FRAME_POINTER_REGNUM),
frame_base_offset);
if (! leaf_function)
{
/* Note the register window save. This tells the unwinder that
it needs to restore the window registers from the previous
frame's window save area at 0(cfa). */
dwarf2out_window_save (label);
/* The return address (-8) is now in %i7. */
dwarf2out_return_reg (label, 31);
}
}
/* If doing anything with PIC, do it now. */
if (! flag_pic)
fprintf (file, "\t%s#PROLOGUE# 1\n", ASM_COMMENT_START);
/* Call saved registers are saved just above the outgoing argument area. */
if (num_gfregs)
{
int offset, real_offset, n_regs;
const char *base;
real_offset = -apparent_fsize;
offset = -apparent_fsize + frame_base_offset;
if (offset < -4096 || offset + num_gfregs * 4 > 4096)
{
/* ??? This might be optimized a little as %g1 might already have a
value close enough that a single add insn will do. */
/* ??? Although, all of this is probably only a temporary fix
because if %g1 can hold a function result, then
output_function_epilogue will lose (the result will get
clobbered). */
build_big_number (file, offset, "%g1");
fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
base = "%g1";
offset = 0;
}
else
{
base = frame_base_name;
}
n_regs = 0;
if (TARGET_EPILOGUE && ! leaf_function)
/* ??? Originally saved regs 0-15 here. */
n_regs = save_regs (file, 0, 8, base, offset, 0, real_offset);
else if (leaf_function)
/* ??? Originally saved regs 0-31 here. */
n_regs = save_regs (file, 0, 8, base, offset, 0, real_offset);
if (TARGET_EPILOGUE)
save_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs,
real_offset);
}
leaf_label = 0;
if (leaf_function && actual_fsize != 0)
{
/* warning ("leaf procedure with frame size %d", actual_fsize); */
if (! TARGET_EPILOGUE)
leaf_label = gen_label_rtx ();
}
}
/* Output code for the function epilogue. */
void
output_function_epilogue (file, size, leaf_function)
FILE *file;
int size ATTRIBUTE_UNUSED;
int leaf_function;
{
const char *ret;
if (leaf_label)
{
emit_label_after (leaf_label, get_last_insn ());
final_scan_insn (get_last_insn (), file, 0, 0, 1);
}
#ifdef FUNCTION_BLOCK_PROFILER_EXIT
else if (profile_block_flag == 2)
{
FUNCTION_BLOCK_PROFILER_EXIT(file);
}
#endif
else if (current_function_epilogue_delay_list == 0)
{
/* If code does not drop into the epilogue, we need
do nothing except output pending case vectors. */
rtx insn = get_last_insn ();
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn && GET_CODE (insn) == BARRIER)
goto output_vectors;
}
/* Restore any call saved registers. */
if (num_gfregs)
{
int offset, n_regs;
const char *base;
offset = -apparent_fsize + frame_base_offset;
if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/)
{
build_big_number (file, offset, "%g1");
fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
base = "%g1";
offset = 0;
}
else
{
base = frame_base_name;
}
n_regs = 0;
if (TARGET_EPILOGUE && ! leaf_function)
/* ??? Originally saved regs 0-15 here. */
n_regs = restore_regs (file, 0, 8, base, offset, 0);
else if (leaf_function)
/* ??? Originally saved regs 0-31 here. */
n_regs = restore_regs (file, 0, 8, base, offset, 0);
if (TARGET_EPILOGUE)
restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs);
}
/* Work out how to skip the caller's unimp instruction if required. */
if (leaf_function)
ret = (SKIP_CALLERS_UNIMP_P ? "jmp\t%o7+12" : "retl");
else
ret = (SKIP_CALLERS_UNIMP_P ? "jmp\t%i7+12" : "ret");
if (TARGET_EPILOGUE || leaf_label)
{
int old_target_epilogue = TARGET_EPILOGUE;
target_flags &= ~old_target_epilogue;
if (! leaf_function)
{
/* If we wound up with things in our delay slot, flush them here. */
if (current_function_epilogue_delay_list)
{
rtx insn = emit_jump_insn_after (gen_rtx_RETURN (VOIDmode),
get_last_insn ());
PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2,
PATTERN (XEXP (current_function_epilogue_delay_list, 0)),
PATTERN (insn)));
final_scan_insn (insn, file, 1, 0, 1);
}
else if (TARGET_V9 && ! SKIP_CALLERS_UNIMP_P)
fputs ("\treturn\t%i7+8\n\tnop\n", file);
else
fprintf (file, "\t%s\n\trestore\n", ret);
}
/* All of the following cases are for leaf functions. */
else if (current_function_epilogue_delay_list)
{
/* eligible_for_epilogue_delay_slot ensures that if this is a
leaf function, then we will only have insn in the delay slot
if the frame size is zero, thus no adjust for the stack is
needed here. */
if (actual_fsize != 0)
abort ();
fprintf (file, "\t%s\n", ret);
final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
file, 1, 0, 1);
}
/* Output 'nop' instead of 'sub %sp,-0,%sp' when no frame, so as to
avoid generating confusing assembly language output. */
else if (actual_fsize == 0)
fprintf (file, "\t%s\n\tnop\n", ret);
else if (actual_fsize <= 4096)
fprintf (file, "\t%s\n\tsub\t%%sp, -%d, %%sp\n", ret, actual_fsize);
else if (actual_fsize <= 8192)
fprintf (file, "\tsub\t%%sp, -4096, %%sp\n\t%s\n\tsub\t%%sp, -%d, %%sp\n",
ret, actual_fsize - 4096);
else if ((actual_fsize & 0x3ff) == 0)
fprintf (file, "\tsethi\t%%hi(%d), %%g1\n\t%s\n\tadd\t%%sp, %%g1, %%sp\n",
actual_fsize, ret);
else
fprintf (file, "\tsethi\t%%hi(%d), %%g1\n\tor\t%%g1, %%lo(%d), %%g1\n\t%s\n\tadd\t%%sp, %%g1, %%sp\n",
actual_fsize, actual_fsize, ret);
target_flags |= old_target_epilogue;
}
output_vectors:
sparc_output_deferred_case_vectors ();
}
/* Functions for handling argument passing.
For v8 the first six args are normally in registers and the rest are
pushed. Any arg that starts within the first 6 words is at least
partially passed in a register unless its data type forbids.
For v9, the argument registers are laid out as an array of 16 elements
and arguments are added sequentially. The first 6 int args and up to the
first 16 fp args (depending on size) are passed in regs.
Slot Stack Integral Float Float in structure Double Long Double
---- ----- -------- ----- ------------------ ------ -----------
15 [SP+248] %f31 %f30,%f31 %d30
14 [SP+240] %f29 %f28,%f29 %d28 %q28
13 [SP+232] %f27 %f26,%f27 %d26
12 [SP+224] %f25 %f24,%f25 %d24 %q24
11 [SP+216] %f23 %f22,%f23 %d22
10 [SP+208] %f21 %f20,%f21 %d20 %q20
9 [SP+200] %f19 %f18,%f19 %d18
8 [SP+192] %f17 %f16,%f17 %d16 %q16
7 [SP+184] %f15 %f14,%f15 %d14
6 [SP+176] %f13 %f12,%f13 %d12 %q12
5 [SP+168] %o5 %f11 %f10,%f11 %d10
4 [SP+160] %o4 %f9 %f8,%f9 %d8 %q8
3 [SP+152] %o3 %f7 %f6,%f7 %d6
2 [SP+144] %o2 %f5 %f4,%f5 %d4 %q4
1 [SP+136] %o1 %f3 %f2,%f3 %d2
0 [SP+128] %o0 %f1 %f0,%f1 %d0 %q0
Here SP = %sp if -mno-stack-bias or %sp+stack_bias otherwise.
Integral arguments are always passed as 64 bit quantities appropriately
extended.
Passing of floating point values is handled as follows.
If a prototype is in scope:
If the value is in a named argument (i.e. not a stdarg function or a
value not part of the `...') then the value is passed in the appropriate
fp reg.
If the value is part of the `...' and is passed in one of the first 6
slots then the value is passed in the appropriate int reg.
If the value is part of the `...' and is not passed in one of the first 6
slots then the value is passed in memory.
If a prototype is not in scope:
If the value is one of the first 6 arguments the value is passed in the
appropriate integer reg and the appropriate fp reg.
If the value is not one of the first 6 arguments the value is passed in
the appropriate fp reg and in memory.
*/
/* Maximum number of int regs for args. */
#define SPARC_INT_ARG_MAX 6
/* Maximum number of fp regs for args. */
#define SPARC_FP_ARG_MAX 16
#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
/* Handle the INIT_CUMULATIVE_ARGS macro.
Initialize a variable CUM of type CUMULATIVE_ARGS
for a call to a function whose data type is FNTYPE.
For a library call, FNTYPE is 0. */
void
init_cumulative_args (cum, fntype, libname, indirect)
CUMULATIVE_ARGS *cum;
tree fntype;
tree libname ATTRIBUTE_UNUSED;
int indirect ATTRIBUTE_UNUSED;
{
cum->words = 0;
cum->prototype_p = fntype && TYPE_ARG_TYPES (fntype);
cum->libcall_p = fntype == 0;
}
/* Compute the slot number to pass an argument in.
Returns the slot number or -1 if passing on the stack.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
MODE is the argument's machine mode.
TYPE is the data type of the argument (as a tree).
This is null for libcalls where that information may
not be available.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis).
INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.
*PREGNO records the register number to use if scalar type.
*PPADDING records the amount of padding needed in words. */
static int
function_arg_slotno (cum, mode, type, named, incoming_p, pregno, ppadding)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
int incoming_p;
int *pregno;
int *ppadding;
{
int regbase = (incoming_p
? SPARC_INCOMING_INT_ARG_FIRST
: SPARC_OUTGOING_INT_ARG_FIRST);
int slotno = cum->words;
int regno;
*ppadding = 0;
if (type != 0 && TREE_ADDRESSABLE (type))
return -1;
if (TARGET_ARCH32
&& type != 0 && mode == BLKmode
&& TYPE_ALIGN (type) % PARM_BOUNDARY != 0)
return -1;
switch (mode)
{
case VOIDmode :
/* MODE is VOIDmode when generating the actual call.
See emit_call_1. */
return -1;
case QImode : case CQImode :
case HImode : case CHImode :
case SImode : case CSImode :
case DImode : case CDImode :
if (slotno >= SPARC_INT_ARG_MAX)
return -1;
regno = regbase + slotno;
break;
case SFmode : case SCmode :
case DFmode : case DCmode :
case TFmode : case TCmode :
if (TARGET_ARCH32)
{
if (slotno >= SPARC_INT_ARG_MAX)
return -1;
regno = regbase + slotno;
}
else
{
if ((mode == TFmode || mode == TCmode)
&& (slotno & 1) != 0)
slotno++, *ppadding = 1;
if (TARGET_FPU && named)
{
if (slotno >= SPARC_FP_ARG_MAX)
return -1;
regno = SPARC_FP_ARG_FIRST + slotno * 2;
if (mode == SFmode)
regno++;
}
else
{
if (slotno >= SPARC_INT_ARG_MAX)
return -1;
regno = regbase + slotno;
}
}
break;
case BLKmode :
/* For sparc64, objects requiring 16 byte alignment get it. */
if (TARGET_ARCH64)
{
if (type && TYPE_ALIGN (type) == 128 && (slotno & 1) != 0)
slotno++, *ppadding = 1;
}
if (TARGET_ARCH32
|| (type && TREE_CODE (type) == UNION_TYPE))
{
if (slotno >= SPARC_INT_ARG_MAX)
return -1;
regno = regbase + slotno;
}
else
{
tree field;
int intregs_p = 0, fpregs_p = 0;
/* The ABI obviously doesn't specify how packed
structures are passed. These are defined to be passed
in int regs if possible, otherwise memory. */
int packed_p = 0;
/* First see what kinds of registers we need. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL)
{
if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& TARGET_FPU)
fpregs_p = 1;
else
intregs_p = 1;
if (DECL_PACKED (field))
packed_p = 1;
}
}
if (packed_p || !named)
fpregs_p = 0, intregs_p = 1;
/* If all arg slots are filled, then must pass on stack. */
if (fpregs_p && slotno >= SPARC_FP_ARG_MAX)
return -1;
/* If there are only int args and all int arg slots are filled,
then must pass on stack. */
if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX)
return -1;
/* Note that even if all int arg slots are filled, fp members may
still be passed in regs if such regs are available.
*PREGNO isn't set because there may be more than one, it's up
to the caller to compute them. */
return slotno;
}
break;
default :
abort ();
}
*pregno = regno;
return slotno;
}
/* Handle recursive register counting for structure field layout. */
struct function_arg_record_value_parms
{
rtx ret;
int slotno, named, regbase;
int nregs, intoffset;
};
static void function_arg_record_value_3
PROTO((int, struct function_arg_record_value_parms *));
static void function_arg_record_value_2
PROTO((tree, int, struct function_arg_record_value_parms *));
static rtx function_arg_record_value
PROTO((tree, enum machine_mode, int, int, int));
static void
function_arg_record_value_1 (type, startbitpos, parms)
tree type;
int startbitpos;
struct function_arg_record_value_parms *parms;
{
tree field;
/* The ABI obviously doesn't specify how packed structures are
passed. These are defined to be passed in int regs if possible,
otherwise memory. */
int packed_p = 0;
/* We need to compute how many registers are needed so we can
allocate the PARALLEL but before we can do that we need to know
whether there are any packed fields. If there are, int regs are
used regardless of whether there are fp values present. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
{
packed_p = 1;
break;
}
}
/* Compute how many registers we need. */
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL)
{
int bitpos = startbitpos;
if (DECL_FIELD_BITPOS (field))
bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
/* ??? FIXME: else assume zero offset. */
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
{
function_arg_record_value_1 (TREE_TYPE (field), bitpos, parms);
}
else if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& TARGET_FPU
&& ! packed_p
&& parms->named)
{
if (parms->intoffset != -1)
{
int intslots, this_slotno;
intslots = (bitpos - parms->intoffset + BITS_PER_WORD - 1)
/ BITS_PER_WORD;
this_slotno = parms->slotno + parms->intoffset
/ BITS_PER_WORD;
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
intslots = MAX (intslots, 0);
parms->nregs += intslots;
parms->intoffset = -1;
}
/* There's no need to check this_slotno < SPARC_FP_ARG MAX.
If it wasn't true we wouldn't be here. */
parms->nregs += 1;
}
else
{
if (parms->intoffset == -1)
parms->intoffset = bitpos;
}
}
}
}
/* Handle recursive structure field register assignment. */
static void
function_arg_record_value_3 (bitpos, parms)
int bitpos;
struct function_arg_record_value_parms *parms;
{
enum machine_mode mode;
int regno, this_slotno, intslots, intoffset;
rtx reg;
if (parms->intoffset == -1)
return;
intoffset = parms->intoffset;
parms->intoffset = -1;
intslots = (bitpos - intoffset + BITS_PER_WORD - 1) / BITS_PER_WORD;
this_slotno = parms->slotno + intoffset / BITS_PER_WORD;
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
if (intslots <= 0)
return;
/* If this is the trailing part of a word, only load that much into
the register. Otherwise load the whole register. Note that in
the latter case we may pick up unwanted bits. It's not a problem
at the moment but may wish to revisit. */
if (intoffset % BITS_PER_WORD != 0)
{
mode = mode_for_size (BITS_PER_WORD - intoffset%BITS_PER_WORD,
MODE_INT, 0);
}
else
mode = word_mode;
intoffset /= BITS_PER_UNIT;
do
{
regno = parms->regbase + this_slotno;
reg = gen_rtx_REG (mode, regno);
XVECEXP (parms->ret, 0, parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
this_slotno += 1;
intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
parms->nregs += 1;
intslots -= 1;
}
while (intslots > 0);
}
static void
function_arg_record_value_2 (type, startbitpos, parms)
tree type;
int startbitpos;
struct function_arg_record_value_parms *parms;
{
tree field;
int packed_p = 0;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
{
packed_p = 1;
break;
}
}
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
{
if (TREE_CODE (field) == FIELD_DECL)
{
int bitpos = startbitpos;
if (DECL_FIELD_BITPOS (field))
bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
/* ??? FIXME: else assume zero offset. */
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
{
function_arg_record_value_2 (TREE_TYPE (field), bitpos, parms);
}
else if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& TARGET_FPU
&& ! packed_p
&& parms->named)
{
int this_slotno = parms->slotno + bitpos / BITS_PER_WORD;
rtx reg;
function_arg_record_value_3 (bitpos, parms);
reg = gen_rtx_REG (DECL_MODE (field),
(SPARC_FP_ARG_FIRST + this_slotno * 2
+ (DECL_MODE (field) == SFmode
&& (bitpos & 32) != 0)));
XVECEXP (parms->ret, 0, parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT (bitpos / BITS_PER_UNIT));
parms->nregs += 1;
}
else
{
if (parms->intoffset == -1)
parms->intoffset = bitpos;
}
}
}
}
static rtx
function_arg_record_value (type, mode, slotno, named, regbase)
tree type;
enum machine_mode mode;
int slotno, named, regbase;
{
HOST_WIDE_INT typesize = int_size_in_bytes (type);
struct function_arg_record_value_parms parms;
int nregs;
parms.ret = NULL_RTX;
parms.slotno = slotno;
parms.named = named;
parms.regbase = regbase;
/* Compute how many registers we need. */
parms.nregs = 0;
parms.intoffset = 0;
function_arg_record_value_1 (type, 0, &parms);
if (parms.intoffset != -1)
{
int intslots, this_slotno;
intslots = (typesize*BITS_PER_UNIT - parms.intoffset + BITS_PER_WORD - 1)
/ BITS_PER_WORD;
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
intslots = MAX (intslots, 0);
parms.nregs += intslots;
}
nregs = parms.nregs;
/* Allocate the vector and handle some annoying special cases. */
if (nregs == 0)
{
/* ??? Empty structure has no value? Duh? */
if (typesize <= 0)
{
/* Though there's nothing really to store, return a word register
anyway so the rest of gcc doesn't go nuts. Returning a PARALLEL
leads to breakage due to the fact that there are zero bytes to
load. */
return gen_rtx_REG (mode, regbase);
}
else
{
/* ??? C++ has structures with no fields, and yet a size. Give up
for now and pass everything back in integer registers. */
nregs = (typesize + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
if (nregs + slotno > SPARC_INT_ARG_MAX)
nregs = SPARC_INT_ARG_MAX - slotno;
}
if (nregs == 0)
abort ();
parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (nregs));
/* Fill in the entries. */
parms.nregs = 0;
parms.intoffset = 0;
function_arg_record_value_2 (type, 0, &parms);
function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms);
if (parms.nregs != nregs)
abort ();
return parms.ret;
}
/* Handle the FUNCTION_ARG macro.
Determine where to put an argument to a function.
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
MODE is the argument's machine mode.
TYPE is the data type of the argument (as a tree).
This is null for libcalls where that information may
not be available.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis).
INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG. */
rtx
function_arg (cum, mode, type, named, incoming_p)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
int incoming_p;
{
int regbase = (incoming_p
? SPARC_INCOMING_INT_ARG_FIRST
: SPARC_OUTGOING_INT_ARG_FIRST);
int slotno, regno, padding;
rtx reg;
slotno = function_arg_slotno (cum, mode, type, named, incoming_p,
&regno, &padding);
if (slotno == -1)
return 0;
if (TARGET_ARCH32)
{
reg = gen_rtx_REG (mode, regno);
return reg;
}
/* v9 fp args in reg slots beyond the int reg slots get passed in regs
but also have the slot allocated for them.
If no prototype is in scope fp values in register slots get passed
in two places, either fp regs and int regs or fp regs and memory. */
if ((GET_MODE_CLASS (mode) == MODE_FLOAT
|| GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
&& SPARC_FP_REG_P (regno))
{
reg = gen_rtx_REG (mode, regno);
if (cum->prototype_p || cum->libcall_p)
{
/* "* 2" because fp reg numbers are recorded in 4 byte
quantities. */
#if 0
/* ??? This will cause the value to be passed in the fp reg and
in the stack. When a prototype exists we want to pass the
value in the reg but reserve space on the stack. That's an
optimization, and is deferred [for a bit]. */
if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2)
return gen_rtx_PARALLEL (mode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
NULL_RTX, const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
reg, const0_rtx)));
else
#else
/* ??? It seems that passing back a register even when past
the area declared by REG_PARM_STACK_SPACE will allocate
space appropriately, and will not copy the data onto the
stack, exactly as we desire.
This is due to locate_and_pad_parm being called in
expand_call whenever reg_parm_stack_space > 0, which
while benefical to our example here, would seem to be
in error from what had been intended. Ho hum... -- r~ */
#endif
return reg;
}
else
{
rtx v0, v1;
if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2)
{
int intreg;
/* On incoming, we don't need to know that the value
is passed in %f0 and %i0, and it confuses other parts
causing needless spillage even on the simplest cases. */
if (incoming_p)
return reg;
intreg = (SPARC_OUTGOING_INT_ARG_FIRST
+ (regno - SPARC_FP_ARG_FIRST) / 2);
v0 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
v1 = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode, intreg),
const0_rtx);
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
}
else
{
v0 = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
v1 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
}
}
}
else if (type && TREE_CODE (type) == RECORD_TYPE)
{
/* Structures up to 16 bytes in size are passed in arg slots on the
stack and are promoted to registers where possible. */
if (int_size_in_bytes (type) > 16)
abort (); /* shouldn't get here */
return function_arg_record_value (type, mode, slotno, named, regbase);
}
else if (type && TREE_CODE (type) == UNION_TYPE)
{
enum machine_mode mode;
int bytes = int_size_in_bytes (type);
if (bytes > 16)
abort ();
mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0);
reg = gen_rtx_REG (mode, regno);
}
else
{
/* Scalar or complex int. */
reg = gen_rtx_REG (mode, regno);
}
return reg;
}
/* Handle the FUNCTION_ARG_PARTIAL_NREGS macro.
For an arg passed partly in registers and partly in memory,
this is the number of registers used.
For args passed entirely in registers or entirely in memory, zero.
Any arg that starts in the first 6 regs but won't entirely fit in them
needs partial registers on v8. On v9, structures with integer
values in arg slots 5,6 will be passed in %o5 and SP+176, and complex fp
values that begin in the last fp reg [where "last fp reg" varies with the
mode] will be split between that reg and memory. */
int
function_arg_partial_nregs (cum, mode, type, named)
const CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
int slotno, regno, padding;
/* We pass 0 for incoming_p here, it doesn't matter. */
slotno = function_arg_slotno (cum, mode, type, named, 0, &regno, &padding);
if (slotno == -1)
return 0;
if (TARGET_ARCH32)
{
if ((slotno + (mode == BLKmode
? ROUND_ADVANCE (int_size_in_bytes (type))
: ROUND_ADVANCE (GET_MODE_SIZE (mode))))
> NPARM_REGS (SImode))
return NPARM_REGS (SImode) - slotno;
return 0;
}
else
{
if (type && AGGREGATE_TYPE_P (type))
{
int size = int_size_in_bytes (type);
int align = TYPE_ALIGN (type);
if (align == 16)
slotno += slotno & 1;
if (size > 8 && size <= 16
&& slotno == SPARC_INT_ARG_MAX - 1)
return 1;
}
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
|| (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
&& ! TARGET_FPU))
{
if (GET_MODE_ALIGNMENT (mode) == 128)
{
slotno += slotno & 1;
if (slotno == SPARC_INT_ARG_MAX - 2)
return 1;
}
else
{
if (slotno == SPARC_INT_ARG_MAX - 1)
return 1;
}
}
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
{
if (GET_MODE_ALIGNMENT (mode) == 128)
slotno += slotno & 1;
if ((slotno + GET_MODE_SIZE (mode) / UNITS_PER_WORD)
> SPARC_FP_ARG_MAX)
return 1;
}
return 0;
}
}
/* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
!v9: The SPARC ABI stipulates passing struct arguments (of any size) and
quad-precision floats by invisible reference.
v9: Aggregates greater than 16 bytes are passed by reference.
For Pascal, also pass arrays by reference. */
int
function_arg_pass_by_reference (cum, mode, type, named)
const CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
enum machine_mode mode;
tree type;
int named ATTRIBUTE_UNUSED;
{
if (TARGET_ARCH32)
{
return ((type && AGGREGATE_TYPE_P (type))
|| mode == TFmode || mode == TCmode);
}
else
{
return ((type && TREE_CODE (type) == ARRAY_TYPE)
/* Consider complex values as aggregates, so care for TCmode. */
|| GET_MODE_SIZE (mode) > 16
|| (type && AGGREGATE_TYPE_P (type)
&& int_size_in_bytes (type) > 16));
}
}
/* Handle the FUNCTION_ARG_ADVANCE macro.
Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
TYPE is null for libcalls where that information may not be available. */
void
function_arg_advance (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
int slotno, regno, padding;
/* We pass 0 for incoming_p here, it doesn't matter. */
slotno = function_arg_slotno (cum, mode, type, named, 0, &regno, &padding);
/* If register required leading padding, add it. */
if (slotno != -1)
cum->words += padding;
if (TARGET_ARCH32)
{
cum->words += (mode != BLKmode
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
: ROUND_ADVANCE (int_size_in_bytes (type)));
}
else
{
if (type && AGGREGATE_TYPE_P (type))
{
int size = int_size_in_bytes (type);
if (size <= 8)
++cum->words;
else if (size <= 16)
cum->words += 2;
else /* passed by reference */
++cum->words;
}
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT)
{
cum->words += 2;
}
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
{
cum->words += GET_MODE_SIZE (mode) / UNITS_PER_WORD;
}
else
{
cum->words += (mode != BLKmode
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
: ROUND_ADVANCE (int_size_in_bytes (type)));
}
}
}
/* Handle the FUNCTION_ARG_PADDING macro.
For the 64 bit ABI structs are always stored left shifted in their
argument slot. */
enum direction
function_arg_padding (mode, type)
enum machine_mode mode;
tree type;
{
if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type))
return upward;
/* This is the default definition. */
return (! BYTES_BIG_ENDIAN
? upward
: ((mode == BLKmode
? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
&& int_size_in_bytes (type) < (PARM_BOUNDARY / BITS_PER_UNIT))
: GET_MODE_BITSIZE (mode) < PARM_BOUNDARY)
? downward : upward));
}
/* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE macros.
For v9, function return values are subject to the same rules as arguments,
except that up to 32-bytes may be returned in registers. */
rtx
function_value (type, mode, incoming_p)
tree type;
enum machine_mode mode;
int incoming_p;
{
int regno;
int regbase = (incoming_p
? SPARC_OUTGOING_INT_ARG_FIRST
: SPARC_INCOMING_INT_ARG_FIRST);
if (TARGET_ARCH64 && type)
{
if (TREE_CODE (type) == RECORD_TYPE)
{
/* Structures up to 32 bytes in size are passed in registers,
promoted to fp registers where possible. */
if (int_size_in_bytes (type) > 32)
abort (); /* shouldn't get here */
return function_arg_record_value (type, mode, 0, 1, regbase);
}
else if (TREE_CODE (type) == UNION_TYPE)
{
int bytes = int_size_in_bytes (type);
if (bytes > 32)
abort ();
mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0);
}
}
if (TARGET_ARCH64
&& GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD
&& type && TREE_CODE (type) != UNION_TYPE)
mode = DImode;
if (incoming_p)
regno = BASE_RETURN_VALUE_REG (mode);
else
regno = BASE_OUTGOING_VALUE_REG (mode);
return gen_rtx_REG (mode, regno);
}
/* Do what is necessary for `va_start'. The argument is ignored.
We look at the current function to determine if stdarg or varargs
is used and return the address of the first unnamed parameter. */
rtx
sparc_builtin_saveregs (arglist)
tree arglist ATTRIBUTE_UNUSED;
{
int first_reg = current_function_args_info.words;
rtx address;
int regno;
for (regno = first_reg; regno < NPARM_REGS (word_mode); regno++)
emit_move_insn (gen_rtx_MEM (word_mode,
gen_rtx_PLUS (Pmode,
frame_pointer_rtx,
GEN_INT (STACK_POINTER_OFFSET
+ UNITS_PER_WORD * regno))),
gen_rtx_REG (word_mode,
BASE_INCOMING_ARG_REG (word_mode) + regno));
address = gen_rtx_PLUS (Pmode,
frame_pointer_rtx,
GEN_INT (STACK_POINTER_OFFSET
+ UNITS_PER_WORD * first_reg));
if (current_function_check_memory_usage
&& first_reg < NPARM_REGS (word_mode))
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
address, ptr_mode,
GEN_INT (UNITS_PER_WORD
* (NPARM_REGS (word_mode) - first_reg)),
TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW),
TYPE_MODE (integer_type_node));
return address;
}
/* Return the string to output a conditional branch to LABEL, which is
the operand number of the label. OP is the conditional expression.
XEXP (OP, 0) is assumed to be a condition code register (integer or
floating point) and its mode specifies what kind of comparison we made.
REVERSED is non-zero if we should reverse the sense of the comparison.
ANNUL is non-zero if we should generate an annulling branch.
NOOP is non-zero if we have to follow this branch by a noop.
INSN, if set, is the insn. */
char *
output_cbranch (op, label, reversed, annul, noop, insn)
rtx op;
int label;
int reversed, annul, noop;
rtx insn;
{
static char string[32];
enum rtx_code code = GET_CODE (op);
rtx cc_reg = XEXP (op, 0);
enum machine_mode mode = GET_MODE (cc_reg);
static char v8_labelno[] = "%lX";
static char v9_icc_labelno[] = "%%icc, %lX";
static char v9_xcc_labelno[] = "%%xcc, %lX";
static char v9_fcc_labelno[] = "%%fccX, %lY";
char *labelno;
int labeloff, spaces = 8;
/* ??? !v9: FP branches cannot be preceded by another floating point insn.
Because there is currently no concept of pre-delay slots, we can fix
this only by always emitting a nop before a floating point branch. */
if ((mode == CCFPmode || mode == CCFPEmode) && ! TARGET_V9)
strcpy (string, "nop\n\t");
else
string[0] = '\0';
/* If not floating-point or if EQ or NE, we can just reverse the code. */
if (reversed
&& ((mode != CCFPmode && mode != CCFPEmode) || code == EQ || code == NE))
code = reverse_condition (code), reversed = 0;
/* Start by writing the branch condition. */
switch (code)
{
case NE:
if (mode == CCFPmode || mode == CCFPEmode)
{
strcat (string, "fbne");
spaces -= 4;
}
else
{
strcpy (string, "bne");
spaces -= 3;
}
break;
case EQ:
if (mode == CCFPmode || mode == CCFPEmode)
{
strcat (string, "fbe");
spaces -= 3;
}
else
{
strcpy (string, "be");
spaces -= 2;
}
break;
case GE:
if (mode == CCFPmode || mode == CCFPEmode)
{
if (reversed)
strcat (string, "fbul");
else
strcat (string, "fbge");
spaces -= 4;
}
else if (mode == CC_NOOVmode)
{
strcpy (string, "bpos");
spaces -= 4;
}
else
{
strcpy (string, "bge");
spaces -= 3;
}
break;
case GT:
if (mode == CCFPmode || mode == CCFPEmode)
{
if (reversed)
{
strcat (string, "fbule");
spaces -= 5;
}
else
{
strcat (string, "fbg");
spaces -= 3;
}
}
else
{
strcpy (string, "bg");
spaces -= 2;
}
break;
case LE:
if (mode == CCFPmode || mode == CCFPEmode)
{
if (reversed)
strcat (string, "fbug");
else
strcat (string, "fble");
spaces -= 4;
}
else
{
strcpy (string, "ble");
spaces -= 3;
}
break;
case LT:
if (mode == CCFPmode || mode == CCFPEmode)
{
if (reversed)
{
strcat (string, "fbuge");
spaces -= 5;
}
else
{
strcat (string, "fbl");
spaces -= 3;
}
}
else if (mode == CC_NOOVmode)
{
strcpy (string, "bneg");
spaces -= 4;
}
else
{
strcpy (string, "bl");
spaces -= 2;
}
break;
case GEU:
strcpy (string, "bgeu");
spaces -= 4;
break;
case GTU:
strcpy (string, "bgu");
spaces -= 3;
break;
case LEU:
strcpy (string, "bleu");
spaces -= 4;
break;
case LTU:
strcpy (string, "blu");
spaces -= 3;
break;
default:
abort ();
}
/* Now add the annulling, the label, and a possible noop. */
if (annul)
{
strcat (string, ",a");
spaces -= 2;
}
if (! TARGET_V9)
{
labeloff = 2;
labelno = v8_labelno;
}
else
{
rtx note;
if (insn && (note = find_reg_note (insn, REG_BR_PRED, NULL_RTX)))
{
strcat (string,
INTVAL (XEXP (note, 0)) & ATTR_FLAG_likely ? ",pt" : ",pn");
spaces -= 3;
}
labeloff = 9;
if (mode == CCFPmode || mode == CCFPEmode)
{
labeloff = 10;
labelno = v9_fcc_labelno;
/* Set the char indicating the number of the fcc reg to use. */
labelno[5] = REGNO (cc_reg) - SPARC_FIRST_V9_FCC_REG + '0';
}
else if (mode == CCXmode || mode == CCX_NOOVmode)
labelno = v9_xcc_labelno;
else
labelno = v9_icc_labelno;
}
/* Set the char indicating the number of the operand containing the
label_ref. */
labelno[labeloff] = label + '0';
if (spaces > 0)
strcat (string, "\t");
else
strcat (string, " ");
strcat (string, labelno);
if (noop)
strcat (string, "\n\tnop");
return string;
}
/* Return the string to output a conditional branch to LABEL, testing
register REG. LABEL is the operand number of the label; REG is the
operand number of the reg. OP is the conditional expression. The mode
of REG says what kind of comparison we made.
REVERSED is non-zero if we should reverse the sense of the comparison.
ANNUL is non-zero if we should generate an annulling branch.
NOOP is non-zero if we have to follow this branch by a noop. */
char *
output_v9branch (op, reg, label, reversed, annul, noop, insn)
rtx op;
int reg, label;
int reversed, annul, noop;
rtx insn;
{
static char string[20];
enum rtx_code code = GET_CODE (op);
enum machine_mode mode = GET_MODE (XEXP (op, 0));
static char labelno[] = "%X, %lX";
rtx note;
int spaces = 8;
/* If not floating-point or if EQ or NE, we can just reverse the code. */
if (reversed)
code = reverse_condition (code), reversed = 0;
/* Only 64 bit versions of these instructions exist. */
if (mode != DImode)
abort ();
/* Start by writing the branch condition. */
switch (code)
{
case NE:
strcpy (string, "brnz");
spaces -= 4;
break;
case EQ:
strcpy (string, "brz");
spaces -= 3;
break;
case GE:
strcpy (string, "brgez");
spaces -= 5;
break;
case LT:
strcpy (string, "brlz");
spaces -= 4;
break;
case LE:
strcpy (string, "brlez");
spaces -= 5;
break;
case GT:
strcpy (string, "brgz");
spaces -= 4;
break;
default:
abort ();
}
/* Now add the annulling, reg, label, and nop. */
if (annul)
{
strcat (string, ",a");
spaces -= 2;
}
if (insn && (note = find_reg_note (insn, REG_BR_PRED, NULL_RTX)))
{
strcat (string,
INTVAL (XEXP (note, 0)) & ATTR_FLAG_likely ? ",pt" : ",pn");
spaces -= 3;
}
labelno[1] = reg + '0';
labelno[6] = label + '0';
if (spaces > 0)
strcat (string, "\t");
else
strcat (string, " ");
strcat (string, labelno);
if (noop)
strcat (string, "\n\tnop");
return string;
}
/* Renumber registers in delay slot. Replace registers instead of
renumbering because they may be shared.
This does not handle instructions other than move. */
static void
epilogue_renumber (where)
rtx *where;
{
rtx x = *where;
enum rtx_code code = GET_CODE (x);
switch (code)
{
case MEM:
*where = x = copy_rtx (x);
epilogue_renumber (&XEXP (x, 0));
return;
case REG:
{
int regno = REGNO (x);
if (regno > 8 && regno < 24)
abort ();
if (regno >= 24 && regno < 32)
*where = gen_rtx_REG (GET_MODE (x), regno - 16);
return;
}
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return;
case IOR:
case AND:
case XOR:
case PLUS:
case MINUS:
epilogue_renumber (&XEXP (x, 1));
case NEG:
case NOT:
epilogue_renumber (&XEXP (x, 0));
return;
default:
debug_rtx (*where);
abort ();
}
}
/* Output assembler code to return from a function. */
const char *
output_return (operands)
rtx *operands;
{
rtx delay = final_sequence ? XVECEXP (final_sequence, 0, 1) : 0;
if (leaf_label)
{
operands[0] = leaf_label;
return "b%* %l0%(";
}
else if (current_function_uses_only_leaf_regs)
{
/* No delay slot in a leaf function. */
if (delay)
abort ();
/* If we didn't allocate a frame pointer for the current function,
the stack pointer might have been adjusted. Output code to
restore it now. */
operands[0] = GEN_INT (actual_fsize);
/* Use sub of negated value in first two cases instead of add to
allow actual_fsize == 4096. */
if (actual_fsize <= 4096)
{
if (SKIP_CALLERS_UNIMP_P)
return "jmp\t%%o7+12\n\tsub\t%%sp, -%0, %%sp";
else
return "retl\n\tsub\t%%sp, -%0, %%sp";
}
else if (actual_fsize <= 8192)
{
operands[0] = GEN_INT (actual_fsize - 4096);
if (SKIP_CALLERS_UNIMP_P)
return "sub\t%%sp, -4096, %%sp\n\tjmp\t%%o7+12\n\tsub\t%%sp, -%0, %%sp";
else
return "sub\t%%sp, -4096, %%sp\n\tretl\n\tsub\t%%sp, -%0, %%sp";
}
else if (SKIP_CALLERS_UNIMP_P)
{
if ((actual_fsize & 0x3ff) != 0)
return "sethi\t%%hi(%a0), %%g1\n\tor\t%%g1, %%lo(%a0), %%g1\n\tjmp\t%%o7+12\n\tadd\t%%sp, %%g1, %%sp";
else
return "sethi\t%%hi(%a0), %%g1\n\tjmp\t%%o7+12\n\tadd\t%%sp, %%g1, %%sp";
}
else
{
if ((actual_fsize & 0x3ff) != 0)
return "sethi %%hi(%a0),%%g1\n\tor %%g1,%%lo(%a0),%%g1\n\tretl\n\tadd %%sp,%%g1,%%sp";
else
return "sethi %%hi(%a0),%%g1\n\tretl\n\tadd %%sp,%%g1,%%sp";
}
}
else if (TARGET_V9)
{
if (delay)
{
epilogue_renumber (&SET_DEST (PATTERN (delay)));
epilogue_renumber (&SET_SRC (PATTERN (delay)));
}
if (SKIP_CALLERS_UNIMP_P)
return "return\t%%i7+12%#";
else
return "return\t%%i7+8%#";
}
else
{
if (delay)
abort ();
if (SKIP_CALLERS_UNIMP_P)
return "jmp\t%%i7+12\n\trestore";
else
return "ret\n\trestore";
}
}
/* Leaf functions and non-leaf functions have different needs. */
static int
reg_leaf_alloc_order[] = REG_LEAF_ALLOC_ORDER;
static int
reg_nonleaf_alloc_order[] = REG_ALLOC_ORDER;
static int *reg_alloc_orders[] = {
reg_leaf_alloc_order,
reg_nonleaf_alloc_order};
void
order_regs_for_local_alloc ()
{
static int last_order_nonleaf = 1;
if (regs_ever_live[15] != last_order_nonleaf)
{
last_order_nonleaf = !last_order_nonleaf;
bcopy ((char *) reg_alloc_orders[last_order_nonleaf],
(char *) reg_alloc_order, FIRST_PSEUDO_REGISTER * sizeof (int));
}
}
/* Return 1 if REG and MEM are legitimate enough to allow the various
mem<-->reg splits to be run. */
int
sparc_splitdi_legitimate (reg, mem)
rtx reg;
rtx mem;
{
/* Punt if we are here by mistake. */
if (! reload_completed)
abort ();
/* We must have an offsettable memory reference. */
if (! offsettable_memref_p (mem))
return 0;
/* If we have legitimate args for ldd/std, we do not want
the split to happen. */
if ((REGNO (reg) % 2) == 0
&& mem_min_alignment (mem, 8))
return 0;
/* Success. */
return 1;
}
/* Return 1 if x and y are some kind of REG and they refer to
different hard registers. This test is guarenteed to be
run after reload. */
int
sparc_absnegfloat_split_legitimate (x, y)
rtx x, y;
{
if (GET_CODE (x) == SUBREG)
x = alter_subreg (x);
if (GET_CODE (x) != REG)
return 0;
if (GET_CODE (y) == SUBREG)
y = alter_subreg (y);
if (GET_CODE (y) != REG)
return 0;
if (REGNO (x) == REGNO (y))
return 0;
return 1;
}
/* Return 1 if REGNO (reg1) is even and REGNO (reg1) == REGNO (reg2) - 1.
This makes them candidates for using ldd and std insns.
Note reg1 and reg2 *must* be hard registers. */
int
registers_ok_for_ldd_peep (reg1, reg2)
rtx reg1, reg2;
{
/* We might have been passed a SUBREG. */
if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG)
return 0;
if (REGNO (reg1) % 2 != 0)
return 0;
/* Integer ldd is deprecated in SPARC V9 */
if (TARGET_V9 && REGNO (reg1) < 32)
return 0;
return (REGNO (reg1) == REGNO (reg2) - 1);
}
/* Return 1 if addr1 and addr2 are suitable for use in an ldd or
std insn.
This can only happen when addr1 and addr2 are consecutive memory
locations (addr1 + 4 == addr2). addr1 must also be aligned on a
64 bit boundary (addr1 % 8 == 0).
We know %sp and %fp are kept aligned on a 64 bit boundary. Other
registers are assumed to *never* be properly aligned and are
rejected.
Knowing %sp and %fp are kept aligned on a 64 bit boundary, we
need only check that the offset for addr1 % 8 == 0. */
int
addrs_ok_for_ldd_peep (addr1, addr2)
rtx addr1, addr2;
{
int reg1, offset1;
/* Extract a register number and offset (if used) from the first addr. */
if (GET_CODE (addr1) == PLUS)
{
/* If not a REG, return zero. */
if (GET_CODE (XEXP (addr1, 0)) != REG)
return 0;
else
{
reg1 = REGNO (XEXP (addr1, 0));
/* The offset must be constant! */
if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
return 0;
offset1 = INTVAL (XEXP (addr1, 1));
}
}
else if (GET_CODE (addr1) != REG)
return 0;
else
{
reg1 = REGNO (addr1);
/* This was a simple (mem (reg)) expression. Offset is 0. */
offset1 = 0;
}
/* Make sure the second address is a (mem (plus (reg) (const_int). */
if (GET_CODE (addr2) != PLUS)
return 0;
if (GET_CODE (XEXP (addr2, 0)) != REG
|| GET_CODE (XEXP (addr2, 1)) != CONST_INT)
return 0;
/* Only %fp and %sp are allowed. Additionally both addresses must
use the same register. */
if (reg1 != FRAME_POINTER_REGNUM && reg1 != STACK_POINTER_REGNUM)
return 0;
if (reg1 != REGNO (XEXP (addr2, 0)))
return 0;
/* The first offset must be evenly divisible by 8 to ensure the
address is 64 bit aligned. */
if (offset1 % 8 != 0)
return 0;
/* The offset for the second addr must be 4 more than the first addr. */
if (INTVAL (XEXP (addr2, 1)) != offset1 + 4)
return 0;
/* All the tests passed. addr1 and addr2 are valid for ldd and std
instructions. */
return 1;
}
/* Return 1 if reg is a pseudo, or is the first register in
a hard register pair. This makes it a candidate for use in
ldd and std insns. */
int
register_ok_for_ldd (reg)
rtx reg;
{
/* We might have been passed a SUBREG. */
if (GET_CODE (reg) != REG)
return 0;
if (REGNO (reg) < FIRST_PSEUDO_REGISTER)
return (REGNO (reg) % 2 == 0);
else
return 1;
}
/* Print operand X (an rtx) in assembler syntax to file FILE.
CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
For `%' followed by punctuation, CODE is the punctuation and X is null. */
void
print_operand (file, x, code)
FILE *file;
rtx x;
int code;
{
switch (code)
{
case '#':
/* Output a 'nop' if there's nothing for the delay slot. */
if (dbr_sequence_length () == 0)
fputs ("\n\t nop", file);
return;
case '*':
/* Output an annul flag if there's nothing for the delay slot and we
are optimizing. This is always used with '(' below. */
/* Sun OS 4.1.1 dbx can't handle an annulled unconditional branch;
this is a dbx bug. So, we only do this when optimizing. */
/* On UltraSPARC, a branch in a delay slot causes a pipeline flush.
Always emit a nop in case the next instruction is a branch. */
if (dbr_sequence_length () == 0
&& (optimize && (int)sparc_cpu < PROCESSOR_V9))
fputs (",a", file);
return;
case '(':
/* Output a 'nop' if there's nothing for the delay slot and we are
not optimizing. This is always used with '*' above. */
if (dbr_sequence_length () == 0
&& ! (optimize && (int)sparc_cpu < PROCESSOR_V9))
fputs ("\n\t nop", file);
return;
case '_':
/* Output the Embedded Medium/Anywhere code model base register. */
fputs (EMBMEDANY_BASE_REG, file);
return;
case '@':
/* Print out what we are using as the frame pointer. This might
be %fp, or might be %sp+offset. */
/* ??? What if offset is too big? Perhaps the caller knows it isn't? */
fprintf (file, "%s+%d", frame_base_name, frame_base_offset);
return;
case 'Y':
/* Adjust the operand to take into account a RESTORE operation. */
if (GET_CODE (x) == CONST_INT)
break;
else if (GET_CODE (x) != REG)
output_operand_lossage ("Invalid %%Y operand");
else if (REGNO (x) < 8)
fputs (reg_names[REGNO (x)], file);
else if (REGNO (x) >= 24 && REGNO (x) < 32)
fputs (reg_names[REGNO (x)-16], file);
else
output_operand_lossage ("Invalid %%Y operand");
return;
case 'L':
/* Print out the low order register name of a register pair. */
if (WORDS_BIG_ENDIAN)
fputs (reg_names[REGNO (x)+1], file);
else
fputs (reg_names[REGNO (x)], file);
return;
case 'H':
/* Print out the high order register name of a register pair. */
if (WORDS_BIG_ENDIAN)
fputs (reg_names[REGNO (x)], file);
else
fputs (reg_names[REGNO (x)+1], file);
return;
case 'R':
/* Print out the second register name of a register pair or quad.
I.e., R (%o0) => %o1. */
fputs (reg_names[REGNO (x)+1], file);
return;
case 'S':
/* Print out the third register name of a register quad.
I.e., S (%o0) => %o2. */
fputs (reg_names[REGNO (x)+2], file);
return;
case 'T':
/* Print out the fourth register name of a register quad.
I.e., T (%o0) => %o3. */
fputs (reg_names[REGNO (x)+3], file);
return;
case 'x':
/* Print a condition code register. */
if (REGNO (x) == SPARC_ICC_REG)
{
/* We don't handle CC[X]_NOOVmode because they're not supposed
to occur here. */
if (GET_MODE (x) == CCmode)
fputs ("%icc", file);
else if (GET_MODE (x) == CCXmode)
fputs ("%xcc", file);
else
abort ();
}
else
/* %fccN register */
fputs (reg_names[REGNO (x)], file);
return;
case 'm':
/* Print the operand's address only. */
output_address (XEXP (x, 0));
return;
case 'r':
/* In this case we need a register. Use %g0 if the
operand is const0_rtx. */
if (x == const0_rtx
|| (GET_MODE (x) != VOIDmode && x == CONST0_RTX (GET_MODE (x))))
{
fputs ("%g0", file);
return;
}
else
break;
case 'A':
switch (GET_CODE (x))
{
case IOR: fputs ("or", file); break;
case AND: fputs ("and", file); break;
case XOR: fputs ("xor", file); break;
default: output_operand_lossage ("Invalid %%A operand");
}
return;
case 'B':
switch (GET_CODE (x))
{
case IOR: fputs ("orn", file); break;
case AND: fputs ("andn", file); break;
case XOR: fputs ("xnor", file); break;
default: output_operand_lossage ("Invalid %%B operand");
}
return;
/* These are used by the conditional move instructions. */
case 'c' :
case 'C':
{
enum rtx_code rc = (code == 'c'
? reverse_condition (GET_CODE (x))
: GET_CODE (x));
switch (rc)
{
case NE: fputs ("ne", file); break;
case EQ: fputs ("e", file); break;
case GE: fputs ("ge", file); break;
case GT: fputs ("g", file); break;
case LE: fputs ("le", file); break;
case LT: fputs ("l", file); break;
case GEU: fputs ("geu", file); break;
case GTU: fputs ("gu", file); break;
case LEU: fputs ("leu", file); break;
case LTU: fputs ("lu", file); break;
default: output_operand_lossage (code == 'c'
? "Invalid %%c operand"
: "Invalid %%C operand");
}
return;
}
/* These are used by the movr instruction pattern. */
case 'd':
case 'D':
{
enum rtx_code rc = (code == 'd'
? reverse_condition (GET_CODE (x))
: GET_CODE (x));
switch (rc)
{
case NE: fputs ("ne", file); break;
case EQ: fputs ("e", file); break;
case GE: fputs ("gez", file); break;
case LT: fputs ("lz", file); break;
case LE: fputs ("lez", file); break;
case GT: fputs ("gz", file); break;
default: output_operand_lossage (code == 'd'
? "Invalid %%d operand"
: "Invalid %%D operand");
}
return;
}
case 'b':
{
/* Print a sign-extended character. */
int i = INTVAL (x) & 0xff;
if (i & 0x80)
i |= 0xffffff00;
fprintf (file, "%d", i);
return;
}
case 'f':
/* Operand must be a MEM; write its address. */
if (GET_CODE (x) != MEM)
output_operand_lossage ("Invalid %%f operand");
output_address (XEXP (x, 0));
return;
case 0:
/* Do nothing special. */
break;
default:
/* Undocumented flag. */
output_operand_lossage ("invalid operand output code");
}
if (GET_CODE (x) == REG)
fputs (reg_names[REGNO (x)], file);
else if (GET_CODE (x) == MEM)
{
fputc ('[', file);
/* Poor Sun assembler doesn't understand absolute addressing. */
if (CONSTANT_P (XEXP (x, 0))
&& ! TARGET_LIVE_G0)
fputs ("%g0+", file);
output_address (XEXP (x, 0));
fputc (']', file);
}
else if (GET_CODE (x) == HIGH)
{
fputs ("%hi(", file);
output_addr_const (file, XEXP (x, 0));
fputc (')', file);
}
else if (GET_CODE (x) == LO_SUM)
{
print_operand (file, XEXP (x, 0), 0);
if (TARGET_CM_MEDMID)
fputs ("+%l44(", file);
else
fputs ("+%lo(", file);
output_addr_const (file, XEXP (x, 1));
fputc (')', file);
}
else if (GET_CODE (x) == CONST_DOUBLE
&& (GET_MODE (x) == VOIDmode
|| GET_MODE_CLASS (GET_MODE (x)) == MODE_INT))
{
if (CONST_DOUBLE_HIGH (x) == 0)
fprintf (file, "%u", CONST_DOUBLE_LOW (x));
else if (CONST_DOUBLE_HIGH (x) == -1
&& CONST_DOUBLE_LOW (x) < 0)
fprintf (file, "%d", CONST_DOUBLE_LOW (x));
else
output_operand_lossage ("long long constant not a valid immediate operand");
}
else if (GET_CODE (x) == CONST_DOUBLE)
output_operand_lossage ("floating point constant not a valid immediate operand");
else { output_addr_const (file, x); }
}
/* This function outputs assembler code for VALUE to FILE, where VALUE is
a 64 bit (DImode) value. */
/* ??? If there is a 64 bit counterpart to .word that the assembler
understands, then using that would simply this code greatly. */
/* ??? We only output .xword's for symbols and only then in environments
where the assembler can handle them. */
void
output_double_int (file, value)
FILE *file;
rtx value;
{
if (GET_CODE (value) == CONST_INT)
{
/* ??? This has endianness issues. */
#if HOST_BITS_PER_WIDE_INT == 64
HOST_WIDE_INT xword = INTVAL (value);
HOST_WIDE_INT high, low;
high = (xword >> 32) & 0xffffffff;
low = xword & 0xffffffff;
ASM_OUTPUT_INT (file, GEN_INT (high));
ASM_OUTPUT_INT (file, GEN_INT (low));
#else
if (INTVAL (value) < 0)
ASM_OUTPUT_INT (file, constm1_rtx);
else
ASM_OUTPUT_INT (file, const0_rtx);
ASM_OUTPUT_INT (file, value);
#endif
}
else if (GET_CODE (value) == CONST_DOUBLE)
{
ASM_OUTPUT_INT (file, GEN_INT (CONST_DOUBLE_HIGH (value)));
ASM_OUTPUT_INT (file, GEN_INT (CONST_DOUBLE_LOW (value)));
}
else if (GET_CODE (value) == SYMBOL_REF
|| GET_CODE (value) == CONST
|| GET_CODE (value) == PLUS
|| (TARGET_ARCH64 &&
(GET_CODE (value) == LABEL_REF
|| GET_CODE (value) == CODE_LABEL
|| GET_CODE (value) == MINUS)))
{
if (! TARGET_V9)
{
ASM_OUTPUT_INT (file, const0_rtx);
ASM_OUTPUT_INT (file, value);
}
else
{
fprintf (file, "\t%s\t", ASM_LONGLONG);
output_addr_const (file, value);
fprintf (file, "\n");
}
}
else
abort ();
}
/* Return the value of a code used in the .proc pseudo-op that says
what kind of result this function returns. For non-C types, we pick
the closest C type. */
#ifndef CHAR_TYPE_SIZE
#define CHAR_TYPE_SIZE BITS_PER_UNIT
#endif
#ifndef SHORT_TYPE_SIZE
#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2)
#endif
#ifndef INT_TYPE_SIZE
#define INT_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef LONG_TYPE_SIZE
#define LONG_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef LONG_LONG_TYPE_SIZE
#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifndef FLOAT_TYPE_SIZE
#define FLOAT_TYPE_SIZE BITS_PER_WORD
#endif
#ifndef DOUBLE_TYPE_SIZE
#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
#ifndef LONG_DOUBLE_TYPE_SIZE
#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
#endif
unsigned long
sparc_type_code (type)
register tree type;
{
register unsigned long qualifiers = 0;
register unsigned shift;
/* Only the first 30 bits of the qualifier are valid. We must refrain from
setting more, since some assemblers will give an error for this. Also,
we must be careful to avoid shifts of 32 bits or more to avoid getting
unpredictable results. */
for (shift = 6; shift < 30; shift += 2, type = TREE_TYPE (type))
{
switch (TREE_CODE (type))
{
case ERROR_MARK:
return qualifiers;
case ARRAY_TYPE:
qualifiers |= (3 << shift);
break;
case FUNCTION_TYPE:
case METHOD_TYPE:
qualifiers |= (2 << shift);
break;
case POINTER_TYPE:
case REFERENCE_TYPE:
case OFFSET_TYPE:
qualifiers |= (1 << shift);
break;
case RECORD_TYPE:
return (qualifiers | 8);
case UNION_TYPE:
case QUAL_UNION_TYPE:
return (qualifiers | 9);
case ENUMERAL_TYPE:
return (qualifiers | 10);
case VOID_TYPE:
return (qualifiers | 16);
case INTEGER_TYPE:
/* If this is a range type, consider it to be the underlying
type. */
if (TREE_TYPE (type) != 0)
break;
/* Carefully distinguish all the standard types of C,
without messing up if the language is not C. We do this by
testing TYPE_PRECISION and TREE_UNSIGNED. The old code used to
look at both the names and the above fields, but that's redundant.
Any type whose size is between two C types will be considered
to be the wider of the two types. Also, we do not have a
special code to use for "long long", so anything wider than
long is treated the same. Note that we can't distinguish
between "int" and "long" in this code if they are the same
size, but that's fine, since neither can the assembler. */
if (TYPE_PRECISION (type) <= CHAR_TYPE_SIZE)
return (qualifiers | (TREE_UNSIGNED (type) ? 12 : 2));
else if (TYPE_PRECISION (type) <= SHORT_TYPE_SIZE)
return (qualifiers | (TREE_UNSIGNED (type) ? 13 : 3));
else if (TYPE_PRECISION (type) <= INT_TYPE_SIZE)
return (qualifiers | (TREE_UNSIGNED (type) ? 14 : 4));
else
return (qualifiers | (TREE_UNSIGNED (type) ? 15 : 5));
case REAL_TYPE:
/* If this is a range type, consider it to be the underlying
type. */
if (TREE_TYPE (type) != 0)
break;
/* Carefully distinguish all the standard types of C,
without messing up if the language is not C. */
if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE)
return (qualifiers | 6);
else
return (qualifiers | 7);
case COMPLEX_TYPE: /* GNU Fortran COMPLEX type. */
/* ??? We need to distinguish between double and float complex types,
but I don't know how yet because I can't reach this code from
existing front-ends. */
return (qualifiers | 7); /* Who knows? */
case CHAR_TYPE: /* GNU Pascal CHAR type. Not used in C. */
case BOOLEAN_TYPE: /* GNU Fortran BOOLEAN type. */
case FILE_TYPE: /* GNU Pascal FILE type. */
case SET_TYPE: /* GNU Pascal SET type. */
case LANG_TYPE: /* ? */
return qualifiers;
default:
abort (); /* Not a type! */
}
}
return qualifiers;
}
/* Nested function support. */
/* Emit RTL insns to initialize the variable parts of a trampoline.
FNADDR is an RTX for the address of the function's pure code.
CXT is an RTX for the static chain value for the function.
This takes 16 insns: 2 shifts & 2 ands (to split up addresses), 4 sethi
(to load in opcodes), 4 iors (to merge address and opcodes), and 4 writes
(to store insns). This is a bit excessive. Perhaps a different
mechanism would be better here.
Emit enough FLUSH insns to synchronize the data and instruction caches. */
void
sparc_initialize_trampoline (tramp, fnaddr, cxt)
rtx tramp, fnaddr, cxt;
{
/* SPARC 32 bit trampoline:
sethi %hi(fn), %g1
sethi %hi(static), %g2
jmp %g1+%lo(fn)
or %g2, %lo(static), %g2
SETHI i,r = 00rr rrr1 00ii iiii iiii iiii iiii iiii
JMPL r+i,d = 10dd ddd1 1100 0rrr rr1i iiii iiii iiii
*/
#ifdef TRANSFER_FROM_TRAMPOLINE
emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"),
0, VOIDmode, 1, tramp, Pmode);
#endif
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 0)),
expand_binop (SImode, ior_optab,
expand_shift (RSHIFT_EXPR, SImode, fnaddr,
size_int (10), 0, 1),
GEN_INT (0x03000000),
NULL_RTX, 1, OPTAB_DIRECT));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)),
expand_binop (SImode, ior_optab,
expand_shift (RSHIFT_EXPR, SImode, cxt,
size_int (10), 0, 1),
GEN_INT (0x05000000),
NULL_RTX, 1, OPTAB_DIRECT));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)),
expand_binop (SImode, ior_optab,
expand_and (fnaddr, GEN_INT (0x3ff), NULL_RTX),
GEN_INT (0x81c06000),
NULL_RTX, 1, OPTAB_DIRECT));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)),
expand_binop (SImode, ior_optab,
expand_and (cxt, GEN_INT (0x3ff), NULL_RTX),
GEN_INT (0x8410a000),
NULL_RTX, 1, OPTAB_DIRECT));
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode, tramp))));
/* On UltraSPARC a flush flushes an entire cache line. The trampoline is
aligned on a 16 byte boundary so one flush clears it all. */
if (sparc_cpu != PROCESSOR_ULTRASPARC)
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode,
plus_constant (tramp, 8)))));
}
/* The 64 bit version is simpler because it makes more sense to load the
values as "immediate" data out of the trampoline. It's also easier since
we can read the PC without clobbering a register. */
void
sparc64_initialize_trampoline (tramp, fnaddr, cxt)
rtx tramp, fnaddr, cxt;
{
#ifdef TRANSFER_FROM_TRAMPOLINE
emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"),
0, VOIDmode, 1, tramp, Pmode);
#endif
/*
rd %pc, %g1
ldx [%g1+24], %g5
jmp %g5
ldx [%g1+16], %g5
+16 bytes data
*/
emit_move_insn (gen_rtx_MEM (SImode, tramp),
GEN_INT (0x83414000));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)),
GEN_INT (0xca586018));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)),
GEN_INT (0x81c14000));
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)),
GEN_INT (0xca586010));
emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 16)), cxt);
emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 24)), fnaddr);
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (DImode, tramp))));
if (sparc_cpu != PROCESSOR_ULTRASPARC)
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (DImode, plus_constant (tramp, 8)))));
}
/* Subroutines to support a flat (single) register window calling
convention. */
/* Single-register window sparc stack frames look like:
Before call After call
+-----------------------+ +-----------------------+
high | | | |
mem | caller's temps. | | caller's temps. |
| | | |
+-----------------------+ +-----------------------+
| | | |
| arguments on stack. | | arguments on stack. |
| | | |
+-----------------------+FP+92->+-----------------------+
| 6 words to save | | 6 words to save |
| arguments passed | | arguments passed |
| in registers, even | | in registers, even |
| if not passed. | | if not passed. |
SP+68->+-----------------------+FP+68->+-----------------------+
| 1 word struct addr | | 1 word struct addr |
+-----------------------+FP+64->+-----------------------+
| | | |
| 16 word reg save area | | 16 word reg save area |
| | | |
SP->+-----------------------+ FP->+-----------------------+
| 4 word area for |
| fp/alu reg moves |
FP-16->+-----------------------+
| |
| local variables |
| |
+-----------------------+
| |
| fp register save |
| |
+-----------------------+
| |
| gp register save |
| |
+-----------------------+
| |
| alloca allocations |
| |
+-----------------------+
| |
| arguments on stack |
| |
SP+92->+-----------------------+
| 6 words to save |
| arguments passed |
| in registers, even |
low | if not passed. |
memory SP+68->+-----------------------+
| 1 word struct addr |
SP+64->+-----------------------+
| |
I 16 word reg save area |
| |
SP->+-----------------------+ */
/* Structure to be filled in by sparc_flat_compute_frame_size with register
save masks, and offsets for the current function. */
struct sparc_frame_info
{
unsigned long total_size; /* # bytes that the entire frame takes up. */
unsigned long var_size; /* # bytes that variables take up. */
unsigned long args_size; /* # bytes that outgoing arguments take up. */
unsigned long extra_size; /* # bytes of extra gunk. */
unsigned int gp_reg_size; /* # bytes needed to store gp regs. */
unsigned int fp_reg_size; /* # bytes needed to store fp regs. */
unsigned long gmask; /* Mask of saved gp registers. */
unsigned long fmask; /* Mask of saved fp registers. */
unsigned long reg_offset; /* Offset from new sp to store regs. */
int initialized; /* Nonzero if frame size already calculated. */
};
/* Current frame information calculated by sparc_flat_compute_frame_size. */
struct sparc_frame_info current_frame_info;
/* Zero structure to initialize current_frame_info. */
struct sparc_frame_info zero_frame_info;
/* Tell prologue and epilogue if register REGNO should be saved / restored. */
#define RETURN_ADDR_REGNUM 15
#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM))
#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM))
#define MUST_SAVE_REGISTER(regno) \
((regs_ever_live[regno] && !call_used_regs[regno]) \
|| (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) \
|| (regno == RETURN_ADDR_REGNUM && regs_ever_live[RETURN_ADDR_REGNUM]))
/* Return the bytes needed to compute the frame pointer from the current
stack pointer. */
unsigned long
sparc_flat_compute_frame_size (size)
int size; /* # of var. bytes allocated. */
{
int regno;
unsigned long total_size; /* # bytes that the entire frame takes up. */
unsigned long var_size; /* # bytes that variables take up. */
unsigned long args_size; /* # bytes that outgoing arguments take up. */
unsigned long extra_size; /* # extra bytes. */
unsigned int gp_reg_size; /* # bytes needed to store gp regs. */
unsigned int fp_reg_size; /* # bytes needed to store fp regs. */
unsigned long gmask; /* Mask of saved gp registers. */
unsigned long fmask; /* Mask of saved fp registers. */
unsigned long reg_offset; /* Offset to register save area. */
int need_aligned_p; /* 1 if need the save area 8 byte aligned. */
/* This is the size of the 16 word reg save area, 1 word struct addr
area, and 4 word fp/alu register copy area. */
extra_size = -STARTING_FRAME_OFFSET + FIRST_PARM_OFFSET(0);
var_size = size;
gp_reg_size = 0;
fp_reg_size = 0;
gmask = 0;
fmask = 0;
reg_offset = 0;
need_aligned_p = 0;
args_size = 0;
if (!leaf_function_p ())
{
/* Also include the size needed for the 6 parameter registers. */
args_size = current_function_outgoing_args_size + 24;
}
total_size = var_size + args_size;
/* Calculate space needed for gp registers. */
for (regno = 1; regno <= 31; regno++)
{
if (MUST_SAVE_REGISTER (regno))
{
/* If we need to save two regs in a row, ensure there's room to bump
up the address to align it to a doubleword boundary. */
if ((regno & 0x1) == 0 && MUST_SAVE_REGISTER (regno+1))
{
if (gp_reg_size % 8 != 0)
gp_reg_size += 4;
gp_reg_size += 2 * UNITS_PER_WORD;
gmask |= 3 << regno;
regno++;
need_aligned_p = 1;
}
else
{
gp_reg_size += UNITS_PER_WORD;
gmask |= 1 << regno;
}
}
}
/* Calculate space needed for fp registers. */
for (regno = 32; regno <= 63; regno++)
{
if (regs_ever_live[regno] && !call_used_regs[regno])
{
fp_reg_size += UNITS_PER_WORD;
fmask |= 1 << (regno - 32);
}
}
if (gmask || fmask)
{
int n;
reg_offset = FIRST_PARM_OFFSET(0) + args_size;
/* Ensure save area is 8 byte aligned if we need it. */
n = reg_offset % 8;
if (need_aligned_p && n != 0)
{
total_size += 8 - n;
reg_offset += 8 - n;
}
total_size += gp_reg_size + fp_reg_size;
}
/* If we must allocate a stack frame at all, we must also allocate
room for register window spillage, so as to be binary compatible
with libraries and operating systems that do not use -mflat. */
if (total_size > 0)
total_size += extra_size;
else
extra_size = 0;
total_size = SPARC_STACK_ALIGN (total_size);
/* Save other computed information. */
current_frame_info.total_size = total_size;
current_frame_info.var_size = var_size;
current_frame_info.args_size = args_size;
current_frame_info.extra_size = extra_size;
current_frame_info.gp_reg_size = gp_reg_size;
current_frame_info.fp_reg_size = fp_reg_size;
current_frame_info.gmask = gmask;
current_frame_info.fmask = fmask;
current_frame_info.reg_offset = reg_offset;
current_frame_info.initialized = reload_completed;
/* Ok, we're done. */
return total_size;
}
/* Save/restore registers in GMASK and FMASK at register BASE_REG plus offset
OFFSET.
BASE_REG must be 8 byte aligned. This allows us to test OFFSET for
appropriate alignment and use DOUBLEWORD_OP when we can. We assume
[BASE_REG+OFFSET] will always be a valid address.
WORD_OP is either "st" for save, "ld" for restore.
DOUBLEWORD_OP is either "std" for save, "ldd" for restore. */
void
sparc_flat_save_restore (file, base_reg, offset, gmask, fmask, word_op,
doubleword_op, base_offset)
FILE *file;
char *base_reg;
unsigned int offset;
unsigned long gmask;
unsigned long fmask;
char *word_op;
char *doubleword_op;
unsigned long base_offset;
{
int regno;
if (gmask == 0 && fmask == 0)
return;
/* Save registers starting from high to low. We've already saved the
previous frame pointer and previous return address for the debugger's
sake. The debugger allows us to not need a nop in the epilog if at least
one register is reloaded in addition to return address. */
if (gmask)
{
for (regno = 1; regno <= 31; regno++)
{
if ((gmask & (1L << regno)) != 0)
{
if ((regno & 0x1) == 0 && ((gmask & (1L << (regno+1))) != 0))
{
/* We can save two registers in a row. If we're not at a
double word boundary, move to one.
sparc_flat_compute_frame_size ensures there's room to do
this. */
if (offset % 8 != 0)
offset += UNITS_PER_WORD;
if (word_op[0] == 's')
{
fprintf (file, "\t%s\t%s, [%s+%d]\n",
doubleword_op, reg_names[regno],
base_reg, offset);
if (dwarf2out_do_frame ())
{
char *l = dwarf2out_cfi_label ();
dwarf2out_reg_save (l, regno, offset + base_offset);
dwarf2out_reg_save
(l, regno+1, offset+base_offset + UNITS_PER_WORD);
}
}
else
fprintf (file, "\t%s\t[%s+%d], %s\n",
doubleword_op, base_reg, offset,
reg_names[regno]);
offset += 2 * UNITS_PER_WORD;
regno++;
}
else
{
if (word_op[0] == 's')
{
fprintf (file, "\t%s\t%s, [%s+%d]\n",
word_op, reg_names[regno],
base_reg, offset);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", regno, offset + base_offset);
}
else
fprintf (file, "\t%s\t[%s+%d], %s\n",
word_op, base_reg, offset, reg_names[regno]);
offset += UNITS_PER_WORD;
}
}
}
}
if (fmask)
{
for (regno = 32; regno <= 63; regno++)
{
if ((fmask & (1L << (regno - 32))) != 0)
{
if (word_op[0] == 's')
{
fprintf (file, "\t%s\t%s, [%s+%d]\n",
word_op, reg_names[regno],
base_reg, offset);
if (dwarf2out_do_frame ())
dwarf2out_reg_save ("", regno, offset + base_offset);
}
else
fprintf (file, "\t%s\t[%s+%d], %s\n",
word_op, base_reg, offset, reg_names[regno]);
offset += UNITS_PER_WORD;
}
}
}
}
/* Set up the stack and frame (if desired) for the function. */
void
sparc_flat_output_function_prologue (file, size)
FILE *file;
int size;
{
char *sp_str = reg_names[STACK_POINTER_REGNUM];
unsigned long gmask = current_frame_info.gmask;
/* This is only for the human reader. */
fprintf (file, "\t%s#PROLOGUE# 0\n", ASM_COMMENT_START);
fprintf (file, "\t%s# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n",
ASM_COMMENT_START,
current_frame_info.var_size,
current_frame_info.gp_reg_size / 4,
current_frame_info.fp_reg_size / 4,
current_function_outgoing_args_size,
current_frame_info.extra_size);
size = SPARC_STACK_ALIGN (size);
size = (! current_frame_info.initialized
? sparc_flat_compute_frame_size (size)
: current_frame_info.total_size);
/* These cases shouldn't happen. Catch them now. */
if (size == 0 && (gmask || current_frame_info.fmask))
abort ();
/* Allocate our stack frame by decrementing %sp.
At present, the only algorithm gdb can use to determine if this is a
flat frame is if we always set %i7 if we set %sp. This can be optimized
in the future by putting in some sort of debugging information that says
this is a `flat' function. However, there is still the case of debugging
code without such debugging information (including cases where most fns
have such info, but there is one that doesn't). So, always do this now
so we don't get a lot of code out there that gdb can't handle.
If the frame pointer isn't needn't then that's ok - gdb won't be able to
distinguish us from a non-flat function but there won't (and shouldn't)
be any differences anyway. The return pc is saved (if necessary) right
after %i7 so gdb won't have to look too far to find it. */
if (size > 0)
{
unsigned int reg_offset = current_frame_info.reg_offset;
char *fp_str = reg_names[FRAME_POINTER_REGNUM];
const char *t1_str = "%g1";
/* Things get a little tricky if local variables take up more than ~4096
bytes and outgoing arguments take up more than ~4096 bytes. When that
happens, the register save area can't be accessed from either end of
the frame. Handle this by decrementing %sp to the start of the gp
register save area, save the regs, update %i7, and then set %sp to its
final value. Given that we only have one scratch register to play
with it is the cheapest solution, and it helps gdb out as it won't
slow down recognition of flat functions.
Don't change the order of insns emitted here without checking with
the gdb folk first. */
/* Is the entire register save area offsettable from %sp? */
if (reg_offset < 4096 - 64 * UNITS_PER_WORD)
{
if (size <= 4096)
{
fprintf (file, "\tadd\t%s, %d, %s\n",
sp_str, -size, sp_str);
if (gmask & FRAME_POINTER_MASK)
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
fp_str, sp_str, reg_offset);
fprintf (file, "\tsub\t%s, %d, %s\t%s# set up frame pointer\n",
sp_str, -size, fp_str, ASM_COMMENT_START);
reg_offset += 4;
}
}
else
{
fprintf (file, "\tset\t%d, %s\n\tsub\t%s, %s, %s\n",
size, t1_str, sp_str, t1_str, sp_str);
if (gmask & FRAME_POINTER_MASK)
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
fp_str, sp_str, reg_offset);
fprintf (file, "\tadd\t%s, %s, %s\t%s# set up frame pointer\n",
sp_str, t1_str, fp_str, ASM_COMMENT_START);
reg_offset += 4;
}
}
if (dwarf2out_do_frame ())
{
char *l = dwarf2out_cfi_label ();
if (gmask & FRAME_POINTER_MASK)
{
dwarf2out_reg_save (l, FRAME_POINTER_REGNUM,
reg_offset - 4 - size);
dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, 0);
}
else
dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, size);
}
if (gmask & RETURN_ADDR_MASK)
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
reg_names[RETURN_ADDR_REGNUM], sp_str, reg_offset);
if (dwarf2out_do_frame ())
dwarf2out_return_save ("", reg_offset - size);
reg_offset += 4;
}
sparc_flat_save_restore (file, sp_str, reg_offset,
gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
current_frame_info.fmask,
"st", "std", -size);
}
else
{
/* Subtract %sp in two steps, but make sure there is always a
64 byte register save area, and %sp is properly aligned. */
/* Amount to decrement %sp by, the first time. */
unsigned int size1 = ((size - reg_offset + 64) + 15) & -16;
/* Offset to register save area from %sp. */
unsigned int offset = size1 - (size - reg_offset);
if (size1 <= 4096)
{
fprintf (file, "\tadd\t%s, %d, %s\n",
sp_str, -size1, sp_str);
if (gmask & FRAME_POINTER_MASK)
{
fprintf (file, "\tst\t%s, [%s+%d]\n\tsub\t%s, %d, %s\t%s# set up frame pointer\n",
fp_str, sp_str, offset, sp_str, -size1, fp_str,
ASM_COMMENT_START);
offset += 4;
}
}
else
{
fprintf (file, "\tset\t%d, %s\n\tsub\t%s, %s, %s\n",
size1, t1_str, sp_str, t1_str, sp_str);
if (gmask & FRAME_POINTER_MASK)
{
fprintf (file, "\tst\t%s, [%s+%d]\n\tadd\t%s, %s, %s\t%s# set up frame pointer\n",
fp_str, sp_str, offset, sp_str, t1_str, fp_str,
ASM_COMMENT_START);
offset += 4;
}
}
if (dwarf2out_do_frame ())
{
char *l = dwarf2out_cfi_label ();
if (gmask & FRAME_POINTER_MASK)
{
dwarf2out_reg_save (l, FRAME_POINTER_REGNUM,
offset - 4 - size1);
dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, 0);
}
else
dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, size1);
}
if (gmask & RETURN_ADDR_MASK)
{
fprintf (file, "\tst\t%s, [%s+%d]\n",
reg_names[RETURN_ADDR_REGNUM], sp_str, offset);
if (dwarf2out_do_frame ())
/* offset - size1 == reg_offset - size
if reg_offset were updated above like offset. */
dwarf2out_return_save ("", offset - size1);
offset += 4;
}
sparc_flat_save_restore (file, sp_str, offset,
gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
current_frame_info.fmask,
"st", "std", -size1);
fprintf (file, "\tset\t%d, %s\n\tsub\t%s, %s, %s\n",
size - size1, t1_str, sp_str, t1_str, sp_str);
if (dwarf2out_do_frame ())
if (! (gmask & FRAME_POINTER_MASK))
dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, size);
}
}
fprintf (file, "\t%s#PROLOGUE# 1\n", ASM_COMMENT_START);
}
/* Do any necessary cleanup after a function to restore stack, frame,
and regs. */
void
sparc_flat_output_function_epilogue (file, size)
FILE *file;
int size;
{
rtx epilogue_delay = current_function_epilogue_delay_list;
int noepilogue = FALSE;
/* This is only for the human reader. */
fprintf (file, "\t%s#EPILOGUE#\n", ASM_COMMENT_START);
/* The epilogue does not depend on any registers, but the stack
registers, so we assume that if we have 1 pending nop, it can be
ignored, and 2 it must be filled (2 nops occur for integer
multiply and divide). */
size = SPARC_STACK_ALIGN (size);
size = (!current_frame_info.initialized
? sparc_flat_compute_frame_size (size)
: current_frame_info.total_size);
if (size == 0 && epilogue_delay == 0)
{
rtx insn = get_last_insn ();
/* If the last insn was a BARRIER, we don't have to write any code
because a jump (aka return) was put there. */
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn && GET_CODE (insn) == BARRIER)
noepilogue = TRUE;
}
if (!noepilogue)
{
unsigned int reg_offset = current_frame_info.reg_offset;
unsigned int size1;
char *sp_str = reg_names[STACK_POINTER_REGNUM];
char *fp_str = reg_names[FRAME_POINTER_REGNUM];
const char *t1_str = "%g1";
/* In the reload sequence, we don't need to fill the load delay
slots for most of the loads, also see if we can fill the final
delay slot if not otherwise filled by the reload sequence. */
if (size > 4095)
fprintf (file, "\tset\t%d, %s\n", size, t1_str);
if (frame_pointer_needed)
{
if (size > 4095)
fprintf (file,"\tsub\t%s, %s, %s\t\t%s# sp not trusted here\n",
fp_str, t1_str, sp_str, ASM_COMMENT_START);
else
fprintf (file,"\tsub\t%s, %d, %s\t\t%s# sp not trusted here\n",
fp_str, size, sp_str, ASM_COMMENT_START);
}
/* Is the entire register save area offsettable from %sp? */
if (reg_offset < 4096 - 64 * UNITS_PER_WORD)
{
size1 = 0;
}
else
{
/* Restore %sp in two steps, but make sure there is always a
64 byte register save area, and %sp is properly aligned. */
/* Amount to increment %sp by, the first time. */
size1 = ((reg_offset - 64 - 16) + 15) & -16;
/* Offset to register save area from %sp. */
reg_offset = size1 - reg_offset;
fprintf (file, "\tset\t%d, %s\n\tadd\t%s, %s, %s\n",
size1, t1_str, sp_str, t1_str, sp_str);
}
/* We must restore the frame pointer and return address reg first
because they are treated specially by the prologue output code. */
if (current_frame_info.gmask & FRAME_POINTER_MASK)
{
fprintf (file, "\tld\t[%s+%d], %s\n",
sp_str, reg_offset, fp_str);
reg_offset += 4;
}
if (current_frame_info.gmask & RETURN_ADDR_MASK)
{
fprintf (file, "\tld\t[%s+%d], %s\n",
sp_str, reg_offset, reg_names[RETURN_ADDR_REGNUM]);
reg_offset += 4;
}
/* Restore any remaining saved registers. */
sparc_flat_save_restore (file, sp_str, reg_offset,
current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
current_frame_info.fmask,
"ld", "ldd", 0);
/* If we had to increment %sp in two steps, record it so the second
restoration in the epilogue finishes up. */
if (size1 > 0)
{
size -= size1;
if (size > 4095)
fprintf (file, "\tset\t%d, %s\n",
size, t1_str);
}
if (current_function_returns_struct)
fprintf (file, "\tjmp\t%%o7+12\n");
else
fprintf (file, "\tretl\n");
/* If the only register saved is the return address, we need a
nop, unless we have an instruction to put into it. Otherwise
we don't since reloading multiple registers doesn't reference
the register being loaded. */
if (epilogue_delay)
{
if (size)
abort ();
final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1);
}
else if (size > 4095)
fprintf (file, "\tadd\t%s, %s, %s\n", sp_str, t1_str, sp_str);
else if (size > 0)
fprintf (file, "\tadd\t%s, %d, %s\n", sp_str, size, sp_str);
else
fprintf (file, "\tnop\n");
}
/* Reset state info for each function. */
current_frame_info = zero_frame_info;
sparc_output_deferred_case_vectors ();
}
/* Define the number of delay slots needed for the function epilogue.
On the sparc, we need a slot if either no stack has been allocated,
or the only register saved is the return register. */
int
sparc_flat_epilogue_delay_slots ()
{
if (!current_frame_info.initialized)
(void) sparc_flat_compute_frame_size (get_frame_size ());
if (current_frame_info.total_size == 0)
return 1;
return 0;
}
/* Return true is TRIAL is a valid insn for the epilogue delay slot.
Any single length instruction which doesn't reference the stack or frame
pointer is OK. */
int
sparc_flat_eligible_for_epilogue_delay (trial, slot)
rtx trial;
int slot ATTRIBUTE_UNUSED;
{
rtx pat = PATTERN (trial);
if (get_attr_length (trial) != 1)
return 0;
/* If %g0 is live, there are lots of things we can't handle.
Rather than trying to find them all now, let's punt and only
optimize things as necessary. */
if (TARGET_LIVE_G0)
return 0;
if (! reg_mentioned_p (stack_pointer_rtx, pat)
&& ! reg_mentioned_p (frame_pointer_rtx, pat))
return 1;
return 0;
}
/* Adjust the cost of a scheduling dependency. Return the new cost of
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
static int
supersparc_adjust_cost (insn, link, dep_insn, cost)
rtx insn;
rtx link;
rtx dep_insn;
int cost;
{
enum attr_type insn_type;
if (! recog_memoized (insn))
return 0;
insn_type = get_attr_type (insn);
if (REG_NOTE_KIND (link) == 0)
{
/* Data dependency; DEP_INSN writes a register that INSN reads some
cycles later. */
/* if a load, then the dependence must be on the memory address;
add an extra "cycle". Note that the cost could be two cycles
if the reg was written late in an instruction group; we ca not tell
here. */
if (insn_type == TYPE_LOAD || insn_type == TYPE_FPLOAD)
return cost + 3;
/* Get the delay only if the address of the store is the dependence. */
if (insn_type == TYPE_STORE || insn_type == TYPE_FPSTORE)
{
rtx pat = PATTERN(insn);
rtx dep_pat = PATTERN (dep_insn);
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
return cost; /* This should not happen! */
/* The dependency between the two instructions was on the data that
is being stored. Assume that this implies that the address of the
store is not dependent. */
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
return cost;
return cost + 3; /* An approximation. */
}
/* A shift instruction cannot receive its data from an instruction
in the same cycle; add a one cycle penalty. */
if (insn_type == TYPE_SHIFT)
return cost + 3; /* Split before cascade into shift. */
}
else
{
/* Anti- or output- dependency; DEP_INSN reads/writes a register that
INSN writes some cycles later. */
/* These are only significant for the fpu unit; writing a fp reg before
the fpu has finished with it stalls the processor. */
/* Reusing an integer register causes no problems. */
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
return 0;
}
return cost;
}
static int
hypersparc_adjust_cost (insn, link, dep_insn, cost)
rtx insn;
rtx link;
rtx dep_insn;
int cost;
{
enum attr_type insn_type, dep_type;
rtx pat = PATTERN(insn);
rtx dep_pat = PATTERN (dep_insn);
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
return cost;
insn_type = get_attr_type (insn);
dep_type = get_attr_type (dep_insn);
switch (REG_NOTE_KIND (link))
{
case 0:
/* Data dependency; DEP_INSN writes a register that INSN reads some
cycles later. */
switch (insn_type)
{
case TYPE_STORE:
case TYPE_FPSTORE:
/* Get the delay iff the address of the store is the dependence. */
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
return cost;
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
return cost;
return cost + 3;
case TYPE_LOAD:
case TYPE_SLOAD:
case TYPE_FPLOAD:
/* If a load, then the dependence must be on the memory address. If
the addresses aren't equal, then it might be a false dependency */
if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE)
{
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET
|| GET_CODE (SET_DEST (dep_pat)) != MEM
|| GET_CODE (SET_SRC (pat)) != MEM
|| ! rtx_equal_p (XEXP (SET_DEST (dep_pat), 0),
XEXP (SET_SRC (pat), 0)))
return cost + 2;
return cost + 8;
}
break;
case TYPE_BRANCH:
/* Compare to branch latency is 0. There is no benefit from
separating compare and branch. */
if (dep_type == TYPE_COMPARE)
return 0;
/* Floating point compare to branch latency is less than
compare to conditional move. */
if (dep_type == TYPE_FPCMP)
return cost - 1;
break;
default:
break;
}
break;
case REG_DEP_ANTI:
/* Anti-dependencies only penalize the fpu unit. */
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
return 0;
break;
default:
break;
}
return cost;
}
static int
ultrasparc_adjust_cost (insn, link, dep_insn, cost)
rtx insn;
rtx link;
rtx dep_insn;
int cost;
{
enum attr_type insn_type, dep_type;
rtx pat = PATTERN(insn);
rtx dep_pat = PATTERN (dep_insn);
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
return cost;
insn_type = get_attr_type (insn);
dep_type = get_attr_type (dep_insn);
/* Nothing issues in parallel with integer multiplies, so
mark as zero cost since the scheduler can not do anything
about it. */
if (insn_type == TYPE_IMUL)
return 0;
#define SLOW_FP(dep_type) \
(dep_type == TYPE_FPSQRT || dep_type == TYPE_FPDIVS || dep_type == TYPE_FPDIVD)
switch (REG_NOTE_KIND (link))
{
case 0:
/* Data dependency; DEP_INSN writes a register that INSN reads some
cycles later. */
if (dep_type == TYPE_CMOVE)
{
/* Instructions that read the result of conditional moves cannot
be in the same group or the following group. */
return cost + 1;
}
switch (insn_type)
{
/* UltraSPARC can dual issue a store and an instruction setting
the value stored, except for divide and square root. */
case TYPE_FPSTORE:
if (! SLOW_FP (dep_type))
return 0;
return cost;
case TYPE_STORE:
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
return cost;
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
/* The dependency between the two instructions is on the data
that is being stored. Assume that the address of the store
is not also dependent. */
return 0;
return cost;
case TYPE_LOAD:
case TYPE_SLOAD:
case TYPE_FPLOAD:
/* A load does not return data until at least 11 cycles after
a store to the same location. 3 cycles are accounted for
in the load latency; add the other 8 here. */
if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE)
{
/* If the addresses are not equal this may be a false
dependency because pointer aliasing could not be
determined. Add only 2 cycles in that case. 2 is
an arbitrary compromise between 8, which would cause
the scheduler to generate worse code elsewhere to
compensate for a dependency which might not really
exist, and 0. */
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET
|| GET_CODE (SET_SRC (pat)) != MEM
|| GET_CODE (SET_DEST (dep_pat)) != MEM
|| ! rtx_equal_p (XEXP (SET_SRC (pat), 0),
XEXP (SET_DEST (dep_pat), 0)))
return cost + 2;
return cost + 8;
}
return cost;
case TYPE_BRANCH:
/* Compare to branch latency is 0. There is no benefit from
separating compare and branch. */
if (dep_type == TYPE_COMPARE)
return 0;
/* Floating point compare to branch latency is less than
compare to conditional move. */
if (dep_type == TYPE_FPCMP)
return cost - 1;
return cost;
case TYPE_FPCMOVE:
/* FMOVR class instructions can not issue in the same cycle
or the cycle after an instruction which writes any
integer register. Model this as cost 2 for dependent
instructions. */
if ((dep_type == TYPE_IALU || dep_type == TYPE_UNARY
|| dep_type == TYPE_BINARY)
&& cost < 2)
return 2;
/* Otherwise check as for integer conditional moves. */
case TYPE_CMOVE:
/* Conditional moves involving integer registers wait until
3 cycles after loads return data. The interlock applies
to all loads, not just dependent loads, but that is hard
to model. */
if (dep_type == TYPE_LOAD || dep_type == TYPE_SLOAD)
return cost + 3;
return cost;
default:
break;
}
break;
case REG_DEP_ANTI:
/* Divide and square root lock destination registers for full latency. */
if (! SLOW_FP (dep_type))
return 0;
break;
case REG_DEP_OUTPUT:
/* IEU and FPU instruction that have the same destination
register cannot be grouped together. */
return cost + 1;
default:
break;
}
/* Other costs not accounted for:
- Single precision floating point loads lock the other half of
the even/odd register pair.
- Several hazards associated with ldd/std are ignored because these
instructions are rarely generated for V9.
- The floating point pipeline can not have both a single and double
precision operation active at the same time. Format conversions
and graphics instructions are given honorary double precision status.
- call and jmpl are always the first instruction in a group. */
return cost;
#undef SLOW_FP
}
int
sparc_adjust_cost(insn, link, dep, cost)
rtx insn;
rtx link;
rtx dep;
int cost;
{
switch (sparc_cpu)
{
case PROCESSOR_SUPERSPARC:
cost = supersparc_adjust_cost (insn, link, dep, cost);
break;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
cost = hypersparc_adjust_cost (insn, link, dep, cost);
break;
case PROCESSOR_ULTRASPARC:
cost = ultrasparc_adjust_cost (insn, link, dep, cost);
break;
default:
break;
}
return cost;
}
/* This describes the state of the UltraSPARC pipeline during
instruction scheduling. */
#define TMASK(__x) ((unsigned)1 << ((int)(__x)))
#define UMASK(__x) ((unsigned)1 << ((int)(__x)))
enum ultra_code { NONE=0, /* no insn at all */
IEU0, /* shifts and conditional moves */
IEU1, /* condition code setting insns, calls+jumps */
IEUN, /* all other single cycle ieu insns */
LSU, /* loads and stores */
CTI, /* branches */
FPM, /* FPU pipeline 1, multiplies and divides */
FPA, /* FPU pipeline 2, all other operations */
SINGLE, /* single issue instructions */
NUM_ULTRA_CODES };
static const char *ultra_code_names[NUM_ULTRA_CODES] = {
"NONE", "IEU0", "IEU1", "IEUN", "LSU", "CTI",
"FPM", "FPA", "SINGLE" };
struct ultrasparc_pipeline_state {
/* The insns in this group. */
rtx group[4];
/* The code for each insn. */
enum ultra_code codes[4];
/* Which insns in this group have been committed by the
scheduler. This is how we determine how many more
can issue this cycle. */
char commit[4];
/* How many insns in this group. */
char group_size;
/* Mask of free slots still in this group. */
char free_slot_mask;
/* The slotter uses the following to determine what other
insn types can still make their way into this group. */
char contents [NUM_ULTRA_CODES];
char num_ieu_insns;
};
#define ULTRA_NUM_HIST 8
static struct ultrasparc_pipeline_state ultra_pipe_hist[ULTRA_NUM_HIST];
static int ultra_cur_hist;
static int ultra_cycles_elapsed;
#define ultra_pipe (ultra_pipe_hist[ultra_cur_hist])
/* Given TYPE_MASK compute the ultra_code it has. */
static enum ultra_code
ultra_code_from_mask (type_mask)
int type_mask;
{
if (type_mask & (TMASK (TYPE_SHIFT) | TMASK (TYPE_CMOVE)))
return IEU0;
else if (type_mask & (TMASK (TYPE_COMPARE) |
TMASK (TYPE_CALL) |
TMASK (TYPE_UNCOND_BRANCH)))
return IEU1;
else if (type_mask & (TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) |
TMASK (TYPE_MOVE) | TMASK (TYPE_UNARY)))
return IEUN;
else if (type_mask & (TMASK (TYPE_LOAD) | TMASK (TYPE_SLOAD) |
TMASK (TYPE_STORE) | TMASK (TYPE_FPLOAD) |
TMASK (TYPE_FPSTORE)))
return LSU;
else if (type_mask & (TMASK (TYPE_FPMUL) | TMASK (TYPE_FPDIVS) |
TMASK (TYPE_FPDIVD) | TMASK (TYPE_FPSQRT)))
return FPM;
else if (type_mask & (TMASK (TYPE_FPMOVE) | TMASK (TYPE_FPCMOVE) |
TMASK (TYPE_FP) | TMASK (TYPE_FPCMP)))
return FPA;
else if (type_mask & TMASK (TYPE_BRANCH))
return CTI;
return SINGLE;
}
/* Check INSN (a conditional move) and make sure that it's
results are available at this cycle. Return 1 if the
results are in fact ready. */
static int
ultra_cmove_results_ready_p (insn)
rtx insn;
{
struct ultrasparc_pipeline_state *up;
int entry, slot;
/* If this got dispatched in the previous
group, the results are not ready. */
entry = (ultra_cur_hist - 1) % (ULTRA_NUM_HIST - 1);
up = &ultra_pipe_hist[entry];
slot = 4;
while (--slot >= 0)
if (up->group[slot] == insn)
return 0;
return 1;
}
/* Walk backwards in pipeline history looking for FPU
operations which use a mode different than FPMODE and
will create a stall if an insn using FPMODE were to be
dispatched this cycle. */
static int
ultra_fpmode_conflict_exists (fpmode)
enum machine_mode fpmode;
{
int hist_ent;
int hist_lim;
hist_ent = (ultra_cur_hist - 1) % (ULTRA_NUM_HIST - 1);
if (ultra_cycles_elapsed < 4)
hist_lim = ultra_cycles_elapsed;
else
hist_lim = 4;
while (hist_lim > 0)
{
struct ultrasparc_pipeline_state *up = &ultra_pipe_hist[hist_ent];
int slot = 4;
while (--slot >= 0)
{
rtx insn = up->group[slot];
enum machine_mode this_mode;
rtx pat;
if (! insn
|| GET_CODE (insn) != INSN
|| (pat = PATTERN (insn)) == 0
|| GET_CODE (pat) != SET)
continue;
this_mode = GET_MODE (SET_DEST (pat));
if ((this_mode != SFmode
&& this_mode != DFmode)
|| this_mode == fpmode)
continue;
/* If it is not FMOV, FABS, FNEG, FDIV, or FSQRT then
we will get a stall. Loads and stores are independant
of these rules. */
if (GET_CODE (SET_SRC (pat)) != ABS
&& GET_CODE (SET_SRC (pat)) != NEG
&& ((TMASK (get_attr_type (insn)) &
(TMASK (TYPE_FPDIVS) | TMASK (TYPE_FPDIVD) |
TMASK (TYPE_FPMOVE) | TMASK (TYPE_FPSQRT) |
TMASK (TYPE_LOAD) | TMASK (TYPE_STORE))) == 0))
return 1;
}
hist_lim--;
hist_ent = (hist_ent - 1) % (ULTRA_NUM_HIST - 1);
}
/* No conflicts, safe to dispatch. */
return 0;
}
/* Find an instruction in LIST which has one of the
type attributes enumerated in TYPE_MASK. START
says where to begin the search.
NOTE: This scheme depends upon the fact that we
have less than 32 distinct type attributes. */
static int ultra_types_avail;
static rtx *
ultra_find_type (type_mask, list, start)
int type_mask;
rtx *list;
int start;
{
int i;
/* Short circuit if no such insn exists in the ready
at the moment. */
if ((type_mask & ultra_types_avail) == 0)
return 0;
for (i = start; i >= 0; i--)
{
rtx insn = list[i];
if (recog_memoized (insn) >= 0
&& (TMASK(get_attr_type (insn)) & type_mask))
{
enum machine_mode fpmode = SFmode;
rtx pat = 0;
int slot;
int check_depend = 0;
int check_fpmode_conflict = 0;
if (GET_CODE (insn) == INSN
&& (pat = PATTERN(insn)) != 0
&& GET_CODE (pat) == SET
&& !(type_mask & (TMASK (TYPE_STORE) |
TMASK (TYPE_FPSTORE))))
{
check_depend = 1;
if (GET_MODE (SET_DEST (pat)) == SFmode
|| GET_MODE (SET_DEST (pat)) == DFmode)
{
fpmode = GET_MODE (SET_DEST (pat));
check_fpmode_conflict = 1;
}
}
slot = 4;
while(--slot >= 0)
{
rtx slot_insn = ultra_pipe.group[slot];
rtx slot_pat;
/* Already issued, bad dependency, or FPU
mode conflict. */
if (slot_insn != 0
&& (slot_pat = PATTERN (slot_insn)) != 0
&& ((insn == slot_insn)
|| (check_depend == 1
&& GET_CODE (slot_insn) == INSN
&& GET_CODE (slot_pat) == SET
&& ((GET_CODE (SET_DEST (slot_pat)) == REG
&& GET_CODE (SET_SRC (pat)) == REG
&& REGNO (SET_DEST (slot_pat)) ==
REGNO (SET_SRC (pat)))
|| (GET_CODE (SET_DEST (slot_pat)) == SUBREG
&& GET_CODE (SET_SRC (pat)) == SUBREG
&& REGNO (SUBREG_REG (SET_DEST (slot_pat))) ==
REGNO (SUBREG_REG (SET_SRC (pat)))
&& SUBREG_WORD (SET_DEST (slot_pat)) ==
SUBREG_WORD (SET_SRC (pat)))))
|| (check_fpmode_conflict == 1
&& GET_CODE (slot_insn) == INSN
&& GET_CODE (slot_pat) == SET
&& (GET_MODE (SET_DEST (slot_pat)) == SFmode
|| GET_MODE (SET_DEST (slot_pat)) == DFmode)
&& GET_MODE (SET_DEST (slot_pat)) != fpmode)))
goto next;
}
/* Check for peculiar result availability and dispatch
interference situations. */
if (pat != 0
&& ultra_cycles_elapsed > 0)
{
rtx link;
for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
{
rtx link_insn = XEXP (link, 0);
if (GET_CODE (link_insn) == INSN
&& recog_memoized (link_insn) >= 0
&& (TMASK (get_attr_type (link_insn)) &
(TMASK (TYPE_CMOVE) | TMASK (TYPE_FPCMOVE)))
&& ! ultra_cmove_results_ready_p (link_insn))
goto next;
}
if (check_fpmode_conflict
&& ultra_fpmode_conflict_exists (fpmode))
goto next;
}
return &list[i];
}
next:
;
}
return 0;
}
static void
ultra_build_types_avail (ready, n_ready)
rtx *ready;
int n_ready;
{
int i = n_ready - 1;
ultra_types_avail = 0;
while(i >= 0)
{
rtx insn = ready[i];
if (recog_memoized (insn) >= 0)
ultra_types_avail |= TMASK (get_attr_type (insn));
i -= 1;
}
}
/* Place insn pointed to my IP into the pipeline.
Make element THIS of READY be that insn if it
is not already. TYPE indicates the pipeline class
this insn falls into. */
static void
ultra_schedule_insn (ip, ready, this, type)
rtx *ip;
rtx *ready;
int this;
enum ultra_code type;
{
int pipe_slot;
char mask = ultra_pipe.free_slot_mask;
/* Obtain free slot. */
for (pipe_slot = 0; pipe_slot < 4; pipe_slot++)
if ((mask & (1 << pipe_slot)) != 0)
break;
if (pipe_slot == 4)
abort ();
/* In it goes, and it hasn't been committed yet. */
ultra_pipe.group[pipe_slot] = *ip;
ultra_pipe.codes[pipe_slot] = type;
ultra_pipe.contents[type] = 1;
if (UMASK (type) &
(UMASK (IEUN) | UMASK (IEU0) | UMASK (IEU1)))
ultra_pipe.num_ieu_insns += 1;
ultra_pipe.free_slot_mask = (mask & ~(1 << pipe_slot));
ultra_pipe.group_size += 1;
ultra_pipe.commit[pipe_slot] = 0;
/* Update ready list. */
if (ip != &ready[this])
{
rtx temp = *ip;
*ip = ready[this];
ready[this] = temp;
}
}
/* Advance to the next pipeline group. */
static void
ultra_flush_pipeline ()
{
ultra_cur_hist = (ultra_cur_hist + 1) % (ULTRA_NUM_HIST - 1);
ultra_cycles_elapsed += 1;
bzero ((char *) &ultra_pipe, sizeof ultra_pipe);
ultra_pipe.free_slot_mask = 0xf;
}
static int ultra_reorder_called_this_block;
/* Init our data structures for this current block. */
void
ultrasparc_sched_init (dump, sched_verbose)
FILE *dump ATTRIBUTE_UNUSED;
int sched_verbose ATTRIBUTE_UNUSED;
{
bzero ((char *) ultra_pipe_hist, sizeof ultra_pipe_hist);
ultra_cur_hist = 0;
ultra_cycles_elapsed = 0;
ultra_reorder_called_this_block = 0;
ultra_pipe.free_slot_mask = 0xf;
}
/* INSN has been scheduled, update pipeline commit state
and return how many instructions are still to be
scheduled in this group. */
int
ultrasparc_variable_issue (insn)
rtx insn;
{
struct ultrasparc_pipeline_state *up = &ultra_pipe;
int i, left_to_fire;
left_to_fire = 0;
for (i = 0; i < 4; i++)
{
if (up->group[i] == 0)
continue;
if (up->group[i] == insn)
{
up->commit[i] = 1;
}
else if (! up->commit[i])
left_to_fire++;
}
return left_to_fire;
}
/* In actual_hazard_this_instance, we may have yanked some
instructions from the ready list due to conflict cost
adjustments. If so, and such an insn was in our pipeline
group, remove it and update state. */
static void
ultra_rescan_pipeline_state (ready, n_ready)
rtx *ready;
int n_ready;
{
struct ultrasparc_pipeline_state *up = &ultra_pipe;
int i;
for (i = 0; i < 4; i++)
{
rtx insn = up->group[i];
int j;
if (! insn)
continue;
/* If it has been committed, then it was removed from
the ready list because it was actually scheduled,
and that is not the case we are searching for here. */
if (up->commit[i] != 0)
continue;
for (j = n_ready - 1; j >= 0; j--)
if (ready[j] == insn)
break;
/* If we didn't find it, toss it. */
if (j < 0)
{
enum ultra_code ucode = up->codes[i];
up->group[i] = 0;
up->codes[i] = NONE;
up->contents[ucode] = 0;
if (UMASK (ucode) &
(UMASK (IEUN) | UMASK (IEU0) | UMASK (IEU1)))
up->num_ieu_insns -= 1;
up->free_slot_mask |= (1 << i);
up->group_size -= 1;
up->commit[i] = 0;
}
}
}
void
ultrasparc_sched_reorder (dump, sched_verbose, ready, n_ready)
FILE *dump;
int sched_verbose;
rtx *ready;
int n_ready;
{
struct ultrasparc_pipeline_state *up = &ultra_pipe;
int i, this_insn;
/* We get called once unnecessarily per block of insns
scheduled. */
if (ultra_reorder_called_this_block == 0)
{
ultra_reorder_called_this_block = 1;
return;
}
if (sched_verbose)
{
int n;
fprintf (dump, "\n;;\tUltraSPARC Looking at [");
for (n = n_ready - 1; n >= 0; n--)
{
rtx insn = ready[n];
enum ultra_code ucode;
if (recog_memoized (insn) < 0)
continue;
ucode = ultra_code_from_mask (TMASK (get_attr_type (insn)));
if (n != 0)
fprintf (dump, "%s(%d) ",
ultra_code_names[ucode],
INSN_UID (insn));
else
fprintf (dump, "%s(%d)",
ultra_code_names[ucode],
INSN_UID (insn));
}
fprintf (dump, "]\n");
}
this_insn = n_ready - 1;
/* Skip over junk we don't understand. */
while ((this_insn >= 0)
&& recog_memoized (ready[this_insn]) < 0)
this_insn--;
ultra_build_types_avail (ready, this_insn + 1);
while (this_insn >= 0) {
int old_group_size = up->group_size;
if (up->group_size != 0)
{
int num_committed;
num_committed = (up->commit[0] + up->commit[1] +
up->commit[2] + up->commit[3]);
/* If nothing has been commited from our group, or all of
them have. Clear out the (current cycle's) pipeline
state and start afresh. */
if (num_committed == 0
|| num_committed == up->group_size)
{
ultra_flush_pipeline ();
up = &ultra_pipe;
old_group_size = 0;
}
else
{
/* OK, some ready list insns got requeued and thus removed
from the ready list. Account for this fact. */
ultra_rescan_pipeline_state (ready, n_ready);
/* Something "changed", make this look like a newly
formed group so the code at the end of the loop
knows that progress was in fact made. */
if (up->group_size != old_group_size)
old_group_size = 0;
}
}
if (up->group_size == 0)
{
/* If the pipeline is (still) empty and we have any single
group insns, get them out now as this is a good time. */
rtx *ip = ultra_find_type ((TMASK (TYPE_RETURN) | TMASK (TYPE_ADDRESS) |
TMASK (TYPE_IMUL) | TMASK (TYPE_CMOVE) |
TMASK (TYPE_MULTI) | TMASK (TYPE_MISC)),
ready, this_insn);
if (ip)
{
ultra_schedule_insn (ip, ready, this_insn, SINGLE);
break;
}
/* If we are not in the process of emptying out the pipe, try to
obtain an instruction which must be the first in it's group. */
ip = ultra_find_type ((TMASK (TYPE_CALL) |
TMASK (TYPE_CALL_NO_DELAY_SLOT) |
TMASK (TYPE_UNCOND_BRANCH)),
ready, this_insn);
if (ip)
{
ultra_schedule_insn (ip, ready, this_insn, IEU1);
this_insn--;
}
else if ((ip = ultra_find_type ((TMASK (TYPE_FPDIVS) |
TMASK (TYPE_FPDIVD) |
TMASK (TYPE_FPSQRT)),
ready, this_insn)) != 0)
{
ultra_schedule_insn (ip, ready, this_insn, FPM);
this_insn--;
}
}
/* Try to fill the integer pipeline. First, look for an IEU0 specific
operation. We can't do more IEU operations if the first 3 slots are
all full or we have dispatched two IEU insns already. */
if ((up->free_slot_mask & 0x7) != 0
&& up->num_ieu_insns < 2
&& up->contents[IEU0] == 0
&& up->contents[IEUN] == 0)
{
rtx *ip = ultra_find_type (TMASK(TYPE_SHIFT), ready, this_insn);
if (ip)
{
ultra_schedule_insn (ip, ready, this_insn, IEU0);
this_insn--;
}
}
/* If we can, try to find an IEU1 specific or an unnamed
IEU instruction. */
if ((up->free_slot_mask & 0x7) != 0
&& up->num_ieu_insns < 2)
{
rtx *ip = ultra_find_type ((TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) |
TMASK (TYPE_MOVE) | TMASK (TYPE_UNARY) |
(up->contents[IEU1] == 0 ? TMASK (TYPE_COMPARE) : 0)),
ready, this_insn);
if (ip)
{
rtx insn = *ip;
ultra_schedule_insn (ip, ready, this_insn,
(!up->contents[IEU1]
&& get_attr_type (insn) == TYPE_COMPARE)
? IEU1 : IEUN);
this_insn--;
}
}
/* If only one IEU insn has been found, try to find another unnamed
IEU operation or an IEU1 specific one. */
if ((up->free_slot_mask & 0x7) != 0
&& up->num_ieu_insns < 2)
{
rtx *ip;
int tmask = (TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) |
TMASK (TYPE_MOVE) | TMASK (TYPE_UNARY));
if (!up->contents[IEU1])
tmask |= TMASK (TYPE_COMPARE);
ip = ultra_find_type (tmask, ready, this_insn);
if (ip)
{
rtx insn = *ip;
ultra_schedule_insn (ip, ready, this_insn,
(!up->contents[IEU1]
&& get_attr_type (insn) == TYPE_COMPARE)
? IEU1 : IEUN);
this_insn--;
}
}
/* Try for a load or store, but such an insn can only be issued
if it is within' one of the first 3 slots. */
if ((up->free_slot_mask & 0x7) != 0
&& up->contents[LSU] == 0)
{
rtx *ip = ultra_find_type ((TMASK (TYPE_LOAD) | TMASK (TYPE_SLOAD) |
TMASK (TYPE_STORE) | TMASK (TYPE_FPLOAD) |
TMASK (TYPE_FPSTORE)), ready, this_insn);
if (ip)
{
ultra_schedule_insn (ip, ready, this_insn, LSU);
this_insn--;
}
}
/* Now find FPU operations, first FPM class. But not divisions or
square-roots because those will break the group up. Unlike all
the previous types, these can go in any slot. */
if (up->free_slot_mask != 0
&& up->contents[FPM] == 0)
{
rtx *ip = ultra_find_type (TMASK (TYPE_FPMUL), ready, this_insn);
if (ip)
{
ultra_schedule_insn (ip, ready, this_insn, FPM);
this_insn--;
}
}
/* Continue on with FPA class if we have not filled the group already. */
if (up->free_slot_mask != 0
&& up->contents[FPA] == 0)
{
rtx *ip = ultra_find_type ((TMASK (TYPE_FPMOVE) | TMASK (TYPE_FPCMOVE) |
TMASK (TYPE_FP) | TMASK (TYPE_FPCMP)),
ready, this_insn);
if (ip)
{
ultra_schedule_insn (ip, ready, this_insn, FPA);
this_insn--;
}
}
/* Finally, maybe stick a branch in here. */
if (up->free_slot_mask != 0
&& up->contents[CTI] == 0)
{
rtx *ip = ultra_find_type (TMASK (TYPE_BRANCH), ready, this_insn);
/* Try to slip in a branch only if it is one of the
next 2 in the ready list. */
if (ip && ((&ready[this_insn] - ip) < 2))
{
ultra_schedule_insn (ip, ready, this_insn, CTI);
this_insn--;
}
}
up->group_size = 0;
for (i = 0; i < 4; i++)
if ((up->free_slot_mask & (1 << i)) == 0)
up->group_size++;
/* See if we made any progress... */
if (old_group_size != up->group_size)
break;
/* Clean out the (current cycle's) pipeline state
and try once more. If we placed no instructions
into the pipeline at all, it means a real hard
conflict exists with some earlier issued instruction
so we must advance to the next cycle to clear it up. */
if (up->group_size == 0)
{
ultra_flush_pipeline ();
up = &ultra_pipe;
}
else
{
bzero ((char *) &ultra_pipe, sizeof ultra_pipe);
ultra_pipe.free_slot_mask = 0xf;
}
}
if (sched_verbose)
{
int n, gsize;
fprintf (dump, ";;\tUltraSPARC Launched [");
gsize = up->group_size;
for (n = 0; n < 4; n++)
{
rtx insn = up->group[n];
if (! insn)
continue;
gsize -= 1;
if (gsize != 0)
fprintf (dump, "%s(%d) ",
ultra_code_names[up->codes[n]],
INSN_UID (insn));
else
fprintf (dump, "%s(%d)",
ultra_code_names[up->codes[n]],
INSN_UID (insn));
}
fprintf (dump, "]\n");
}
}
int
sparc_issue_rate ()
{
switch (sparc_cpu)
{
default:
return 1;
case PROCESSOR_V9:
/* Assume V9 processors are capable of at least dual-issue. */
return 2;
case PROCESSOR_SUPERSPARC:
return 3;
case PROCESSOR_HYPERSPARC:
case PROCESSOR_SPARCLITE86X:
return 2;
case PROCESSOR_ULTRASPARC:
return 4;
}
}
static int
set_extends(x, insn)
rtx x, insn;
{
register rtx pat = PATTERN (insn);
switch (GET_CODE (SET_SRC (pat)))
{
/* Load and some shift instructions zero extend. */
case MEM:
case ZERO_EXTEND:
/* sethi clears the high bits */
case HIGH:
/* LO_SUM is used with sethi. sethi cleared the high
bits and the values used with lo_sum are positive */
case LO_SUM:
/* Store flag stores 0 or 1 */
case LT: case LTU:
case GT: case GTU:
case LE: case LEU:
case GE: case GEU:
case EQ:
case NE:
return 1;
case AND:
{
rtx op1 = XEXP (SET_SRC (pat), 1);
if (GET_CODE (op1) == CONST_INT)
return INTVAL (op1) >= 0;
if (GET_CODE (XEXP (SET_SRC (pat), 0)) == REG
&& sparc_check_64 (XEXP (SET_SRC (pat), 0), insn) == 1)
return 1;
if (GET_CODE (op1) == REG
&& sparc_check_64 ((op1), insn) == 1)
return 1;
}
case ASHIFT:
case LSHIFTRT:
return GET_MODE (SET_SRC (pat)) == SImode;
/* Positive integers leave the high bits zero. */
case CONST_DOUBLE:
return ! (CONST_DOUBLE_LOW (x) & 0x80000000);
case CONST_INT:
return ! (INTVAL (x) & 0x80000000);
case ASHIFTRT:
case SIGN_EXTEND:
return - (GET_MODE (SET_SRC (pat)) == SImode);
default:
return 0;
}
}
/* We _ought_ to have only one kind per function, but... */
static rtx sparc_addr_diff_list;
static rtx sparc_addr_list;
void
sparc_defer_case_vector (lab, vec, diff)
rtx lab, vec;
int diff;
{
vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec);
if (diff)
sparc_addr_diff_list
= gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_diff_list);
else
sparc_addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_list);
}
static void
sparc_output_addr_vec (vec)
rtx vec;
{
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
int idx, vlen = XVECLEN (body, 0);
#ifdef ASM_OUTPUT_ADDR_VEC_START
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
#endif
#ifdef ASM_OUTPUT_CASE_LABEL
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
NEXT_INSN (lab));
#else
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
#endif
for (idx = 0; idx < vlen; idx++)
{
ASM_OUTPUT_ADDR_VEC_ELT
(asm_out_file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0)));
}
#ifdef ASM_OUTPUT_ADDR_VEC_END
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
#endif
}
static void
sparc_output_addr_diff_vec (vec)
rtx vec;
{
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
rtx base = XEXP (XEXP (body, 0), 0);
int idx, vlen = XVECLEN (body, 1);
#ifdef ASM_OUTPUT_ADDR_VEC_START
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
#endif
#ifdef ASM_OUTPUT_CASE_LABEL
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
NEXT_INSN (lab));
#else
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
#endif
for (idx = 0; idx < vlen; idx++)
{
ASM_OUTPUT_ADDR_DIFF_ELT
(asm_out_file,
body,
CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)),
CODE_LABEL_NUMBER (base));
}
#ifdef ASM_OUTPUT_ADDR_VEC_END
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
#endif
}
static void
sparc_output_deferred_case_vectors ()
{
rtx t;
int align;
if (sparc_addr_list == NULL_RTX
&& sparc_addr_diff_list == NULL_RTX)
return;
/* Align to cache line in the function's code section. */
function_section (current_function_decl);
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
if (align > 0)
ASM_OUTPUT_ALIGN (asm_out_file, align);
for (t = sparc_addr_list; t ; t = XEXP (t, 1))
sparc_output_addr_vec (XEXP (t, 0));
for (t = sparc_addr_diff_list; t ; t = XEXP (t, 1))
sparc_output_addr_diff_vec (XEXP (t, 0));
sparc_addr_list = sparc_addr_diff_list = NULL_RTX;
}
/* Return 0 if the high 32 bits of X (the low word of X, if DImode) are
unknown. Return 1 if the high bits are zero, -1 if the register is
sign extended. */
int
sparc_check_64 (x, insn)
rtx x, insn;
{
/* If a register is set only once it is safe to ignore insns this
code does not know how to handle. The loop will either recognize
the single set and return the correct value or fail to recognize
it and return 0. */
int set_once = 0;
if (GET_CODE (x) == REG
&& flag_expensive_optimizations
&& REG_N_SETS (REGNO (x)) == 1)
set_once = 1;
if (insn == 0)
{
if (set_once)
insn = get_last_insn_anywhere ();
else
return 0;
}
while ((insn = PREV_INSN (insn)))
{
switch (GET_CODE (insn))
{
case JUMP_INSN:
case NOTE:
break;
case CODE_LABEL:
case CALL_INSN:
default:
if (! set_once)
return 0;
break;
case INSN:
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) != SET)
return 0;
if (rtx_equal_p (x, SET_DEST (pat)))
return set_extends (x, insn);
if (reg_overlap_mentioned_p (SET_DEST (pat), x))
return 0;
}
}
}
return 0;
}
char *
sparc_v8plus_shift (operands, insn, opcode)
rtx *operands;
rtx insn;
char *opcode;
{
static char asm_code[60];
if (GET_CODE (operands[3]) == SCRATCH)
operands[3] = operands[0];
if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("mov %1,%3", operands);
}
else
{
output_asm_insn ("sllx %H1,32,%3", operands);
if (sparc_check_64 (operands[1], insn) <= 0)
output_asm_insn ("srl %L1,0,%L1", operands);
output_asm_insn ("or %L1,%3,%3", operands);
}
strcpy(asm_code, opcode);
if (which_alternative != 2)
return strcat (asm_code, " %0,%2,%L0\n\tsrlx %L0,32,%H0");
else
return strcat (asm_code, " %3,%2,%3\n\tsrlx %3,32,%H0\n\tmov %3,%L0");
}
/* Return 1 if DEST and SRC reference only global and in registers. */
int
sparc_return_peephole_ok (dest, src)
rtx dest, src;
{
if (! TARGET_V9)
return 0;
if (current_function_uses_only_leaf_regs)
return 0;
if (GET_CODE (src) != CONST_INT
&& (GET_CODE (src) != REG || ! IN_OR_GLOBAL_P (src)))
return 0;
return IN_OR_GLOBAL_P (dest);
}
/* Output assembler code to FILE to increment profiler label # LABELNO
for profiling a function entry.
32 bit sparc uses %g2 as the STATIC_CHAIN_REGNUM which gets clobbered
during profiling so we need to save/restore it around the call to mcount.
We're guaranteed that a save has just been done, and we use the space
allocated for intreg/fpreg value passing. */
void
sparc_function_profiler (file, labelno)
FILE *file;
int labelno;
{
char buf[32];
ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
if (! TARGET_ARCH64)
fputs ("\tst\t%g2,[%fp-4]\n", file);
fputs ("\tsethi\t%hi(", file);
assemble_name (file, buf);
fputs ("),%o0\n", file);
fputs ("\tcall\t", file);
assemble_name (file, MCOUNT_FUNCTION);
putc ('\n', file);
fputs ("\t or\t%o0,%lo(", file);
assemble_name (file, buf);
fputs ("),%o0\n", file);
if (! TARGET_ARCH64)
fputs ("\tld\t[%fp-4],%g2\n", file);
}
/* The following macro shall output assembler code to FILE
to initialize basic-block profiling.
If profile_block_flag == 2
Output code to call the subroutine `__bb_init_trace_func'
and pass two parameters to it. The first parameter is
the address of a block allocated in the object module.
The second parameter is the number of the first basic block
of the function.
The name of the block is a local symbol made with this statement:
ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0);
Of course, since you are writing the definition of
`ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
can take a short cut in the definition of this macro and use the
name that you know will result.
The number of the first basic block of the function is
passed to the macro in BLOCK_OR_LABEL.
If described in a virtual assembler language the code to be
output looks like:
parameter1 <- LPBX0
parameter2 <- BLOCK_OR_LABEL
call __bb_init_trace_func
else if profile_block_flag != 0
Output code to call the subroutine `__bb_init_func'
and pass one single parameter to it, which is the same
as the first parameter to `__bb_init_trace_func'.
The first word of this parameter is a flag which will be nonzero if
the object module has already been initialized. So test this word
first, and do not call `__bb_init_func' if the flag is nonzero.
Note: When profile_block_flag == 2 the test need not be done
but `__bb_init_trace_func' *must* be called.
BLOCK_OR_LABEL may be used to generate a label number as a
branch destination in case `__bb_init_func' will not be called.
If described in a virtual assembler language the code to be
output looks like:
cmp (LPBX0),0
jne local_label
parameter1 <- LPBX0
call __bb_init_func
local_label:
*/
void
sparc_function_block_profiler(file, block_or_label)
FILE *file;
int block_or_label;
{
char LPBX[32];
ASM_GENERATE_INTERNAL_LABEL (LPBX, "LPBX", 0);
if (profile_block_flag == 2)
{
fputs ("\tsethi\t%hi(", file);
assemble_name (file, LPBX);
fputs ("),%o0\n", file);
fprintf (file, "\tsethi\t%%hi(%d),%%o1\n", block_or_label);
fputs ("\tor\t%o0,%lo(", file);
assemble_name (file, LPBX);
fputs ("),%o0\n", file);
fprintf (file, "\tcall\t%s__bb_init_trace_func\n", user_label_prefix);
fprintf (file, "\t or\t%%o1,%%lo(%d),%%o1\n", block_or_label);
}
else if (profile_block_flag != 0)
{
char LPBY[32];
ASM_GENERATE_INTERNAL_LABEL (LPBY, "LPBY", block_or_label);
fputs ("\tsethi\t%hi(", file);
assemble_name (file, LPBX);
fputs ("),%o0\n", file);
fputs ("\tld\t[%lo(", file);
assemble_name (file, LPBX);
fputs (")+%o0],%o1\n", file);
fputs ("\ttst\t%o1\n", file);
if (TARGET_V9)
{
fputs ("\tbne,pn\t%icc,", file);
assemble_name (file, LPBY);
putc ('\n', file);
}
else
{
fputs ("\tbne\t", file);
assemble_name (file, LPBY);
putc ('\n', file);
}
fputs ("\t or\t%o0,%lo(", file);
assemble_name (file, LPBX);
fputs ("),%o0\n", file);
fprintf (file, "\tcall\t%s__bb_init_func\n\t nop\n", user_label_prefix);
ASM_OUTPUT_INTERNAL_LABEL (file, "LPBY", block_or_label);
}
}
/* The following macro shall output assembler code to FILE
to increment a counter associated with basic block number BLOCKNO.
If profile_block_flag == 2
Output code to initialize the global structure `__bb' and
call the function `__bb_trace_func' which will increment the
counter.
`__bb' consists of two words. In the first word the number
of the basic block has to be stored. In the second word
the address of a block allocated in the object module
has to be stored.
The basic block number is given by BLOCKNO.
The address of the block is given by the label created with
ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0);
by FUNCTION_BLOCK_PROFILER.
Of course, since you are writing the definition of
`ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
can take a short cut in the definition of this macro and use the
name that you know will result.
If described in a virtual assembler language the code to be
output looks like:
move BLOCKNO -> (__bb)
move LPBX0 -> (__bb+4)
call __bb_trace_func
Note that function `__bb_trace_func' must not change the
machine state, especially the flag register. To grant
this, you must output code to save and restore registers
either in this macro or in the macros MACHINE_STATE_SAVE
and MACHINE_STATE_RESTORE. The last two macros will be
used in the function `__bb_trace_func', so you must make
sure that the function prologue does not change any
register prior to saving it with MACHINE_STATE_SAVE.
else if profile_block_flag != 0
Output code to increment the counter directly.
Basic blocks are numbered separately from zero within each
compiled object module. The count associated with block number
BLOCKNO is at index BLOCKNO in an array of words; the name of
this array is a local symbol made with this statement:
ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 2);
Of course, since you are writing the definition of
`ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
can take a short cut in the definition of this macro and use the
name that you know will result.
If described in a virtual assembler language, the code to be
output looks like:
inc (LPBX2+4*BLOCKNO)
*/
void
sparc_block_profiler(file, blockno)
FILE *file;
int blockno;
{
char LPBX[32];
if (profile_block_flag == 2)
{
ASM_GENERATE_INTERNAL_LABEL (LPBX, "LPBX", 0);
fprintf (file, "\tsethi\t%%hi(%s__bb),%%g1\n", user_label_prefix);
fprintf (file, "\tsethi\t%%hi(%d),%%g2\n", blockno);
fprintf (file, "\tor\t%%g1,%%lo(%s__bb),%%g1\n", user_label_prefix);
fprintf (file, "\tor\t%%g2,%%lo(%d),%%g2\n", blockno);
fputs ("\tst\t%g2,[%g1]\n", file);
fputs ("\tsethi\t%hi(", file);
assemble_name (file, LPBX);
fputs ("),%g2\n", file);
- fputs ("\tor\t%o2,%lo(", file);
+ fputs ("\tor\t%g2,%lo(", file);
assemble_name (file, LPBX);
fputs ("),%g2\n", file);
fputs ("\tst\t%g2,[%g1+4]\n", file);
fputs ("\tmov\t%o7,%g2\n", file);
fprintf (file, "\tcall\t%s__bb_trace_func\n\t nop\n", user_label_prefix);
fputs ("\tmov\t%g2,%o7\n", file);
}
else if (profile_block_flag != 0)
{
ASM_GENERATE_INTERNAL_LABEL (LPBX, "LPBX", 2);
fputs ("\tsethi\t%hi(", file);
assemble_name (file, LPBX);
fprintf (file, "+%d),%%g1\n", blockno*4);
fputs ("\tld\t[%g1+%lo(", file);
assemble_name (file, LPBX);
fprintf (file, "+%d)],%%g2\n", blockno*4);
fputs ("\tadd\t%g2,1,%g2\n", file);
fputs ("\tst\t%g2,[%g1+%lo(", file);
assemble_name (file, LPBX);
fprintf (file, "+%d)]\n", blockno*4);
}
}
/* The following macro shall output assembler code to FILE
to indicate a return from function during basic-block profiling.
If profile_block_flag == 2:
Output assembler code to call function `__bb_trace_ret'.
Note that function `__bb_trace_ret' must not change the
machine state, especially the flag register. To grant
this, you must output code to save and restore registers
either in this macro or in the macros MACHINE_STATE_SAVE_RET
and MACHINE_STATE_RESTORE_RET. The last two macros will be
used in the function `__bb_trace_ret', so you must make
sure that the function prologue does not change any
register prior to saving it with MACHINE_STATE_SAVE_RET.
else if profile_block_flag != 0:
The macro will not be used, so it need not distinguish
these cases.
*/
void
sparc_function_block_profiler_exit(file)
FILE *file;
{
if (profile_block_flag == 2)
fprintf (file, "\tcall\t%s__bb_trace_ret\n\t nop\n", user_label_prefix);
else
abort ();
}
Index: head/contrib/gcc/config/sparc/sparc.md
===================================================================
--- head/contrib/gcc/config/sparc/sparc.md (revision 52750)
+++ head/contrib/gcc/config/sparc/sparc.md (revision 52751)
@@ -1,8236 +1,8236 @@
;;- Machine description for SPARC chip for GNU C compiler
;; Copyright (C) 1987, 88, 89, 92-98, 1999 Free Software Foundation, Inc.
;; Contributed by Michael Tiemann (tiemann@cygnus.com)
;; 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
;; at Cygnus Support.
;; 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.
;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
;; Uses of UNSPEC and UNSPEC_VOLATILE in this file:
;;
;; UNSPEC: 0 movsi_{lo_sum,high}_pic
;; pic_lo_sum_di
;; pic_sethi_di
;; 1 update_return
;; 2 get_pc
;; 5 movsi_{,lo_sum_,high_}pic_label_ref
;; 6 seth44
;; 7 setm44
;; 8 setl44
;; 9 sethh
;; 10 setlm
;; 11 embmedany_sethi, embmedany_brsum
;; 12 movsf_const_high
;; 13 embmedany_textuhi
;; 14 embmedany_texthi
;; 15 embmedany_textulo
;; 16 embmedany_textlo
;; 17 movsf_const_lo
;; 18 sethm
;; 19 setlo
;;
;; UNSPEC_VOLATILE: 0 blockage
;; 1 flush_register_windows
;; 2 goto_handler_and_restore
;; 3 goto_handler_and_restore_v9*
;; 4 flush
;; 5 nonlocal_goto_receiver
;;
;; The upper 32 fp regs on the v9 can't hold SFmode values. To deal with this
;; a second register class, EXTRA_FP_REGS, exists for the v9 chip. The name
;; is a bit of a misnomer as it covers all 64 fp regs. The corresponding
;; constraint letter is 'e'. To avoid any confusion, 'e' is used instead of
;; 'f' for all DF/TFmode values, including those that are specific to the v8.
;;
;; -mlive-g0 is *not* supported for TARGET_ARCH64, so we don't bother to
;; test TARGET_LIVE_G0 if we have TARGET_ARCH64.
;; Attribute for cpu type.
;; These must match the values for enum processor_type in sparc.h.
(define_attr "cpu" "v7,cypress,v8,supersparc,sparclite,f930,f934,hypersparc,sparclite86x,sparclet,tsc701,v9,ultrasparc"
(const (symbol_ref "sparc_cpu_attr")))
;; Attribute for the instruction set.
;; At present we only need to distinguish v9/!v9, but for clarity we
;; test TARGET_V8 too.
(define_attr "isa" "v6,v8,v9,sparclet"
(const
(cond [(symbol_ref "TARGET_V9") (const_string "v9")
(symbol_ref "TARGET_V8") (const_string "v8")
(symbol_ref "TARGET_SPARCLET") (const_string "sparclet")]
(const_string "v6"))))
;; Architecture size.
(define_attr "arch" "arch32bit,arch64bit"
(const
(cond [(symbol_ref "TARGET_ARCH64") (const_string "arch64bit")]
(const_string "arch32bit"))))
;; Whether -mlive-g0 is in effect.
(define_attr "live_g0" "no,yes"
(const
(cond [(symbol_ref "TARGET_LIVE_G0") (const_string "yes")]
(const_string "no"))))
;; Insn type. Used to default other attribute values.
;; type "unary" insns have one input operand (1) and one output operand (0)
;; type "binary" insns have two input operands (1,2) and one output (0)
;; type "compare" insns have one or two input operands (0,1) and no output
;; type "call_no_delay_slot" is a call followed by an unimp instruction.
(define_attr "type"
"move,unary,binary,compare,load,sload,store,ialu,shift,uncond_branch,branch,call,call_no_delay_slot,return,address,imul,fpload,fpstore,fp,fpmove,fpcmove,fpcmp,fpmul,fpdivs,fpdivd,fpsqrt,cmove,multi,misc"
(const_string "binary"))
;; Set true if insn uses call-clobbered intermediate register.
(define_attr "use_clobbered" "false,true"
(if_then_else (and (eq_attr "type" "address")
(match_operand 0 "clobbered_register" ""))
(const_string "true")
(const_string "false")))
;; Length (in # of insns).
(define_attr "length" ""
(cond [(eq_attr "type" "load,sload,fpload")
(if_then_else (match_operand 1 "symbolic_memory_operand" "")
(const_int 2) (const_int 1))
(eq_attr "type" "store,fpstore")
(if_then_else (match_operand 0 "symbolic_memory_operand" "")
(const_int 2) (const_int 1))
(eq_attr "type" "address") (const_int 2)
(eq_attr "type" "binary")
(if_then_else (ior (match_operand 2 "arith_operand" "")
(match_operand 2 "arith_double_operand" ""))
(const_int 1) (const_int 3))
(eq_attr "type" "multi") (const_int 2)
(eq_attr "type" "move,unary")
(if_then_else (ior (match_operand 1 "arith_operand" "")
(match_operand 1 "arith_double_operand" ""))
(const_int 1) (const_int 2))]
(const_int 1)))
(define_asm_attributes
[(set_attr "length" "1")
(set_attr "type" "multi")])
;; Attributes for instruction and branch scheduling
(define_attr "in_call_delay" "false,true"
(cond [(eq_attr "type" "uncond_branch,branch,call,call_no_delay_slot,return,multi")
(const_string "false")
(eq_attr "type" "load,fpload,store,fpstore")
(if_then_else (eq_attr "length" "1")
(const_string "true")
(const_string "false"))
(eq_attr "type" "address")
(if_then_else (eq_attr "use_clobbered" "false")
(const_string "true")
(const_string "false"))]
(if_then_else (eq_attr "length" "1")
(const_string "true")
(const_string "false"))))
(define_delay (eq_attr "type" "call")
[(eq_attr "in_call_delay" "true") (nil) (nil)])
(define_attr "leaf_function" "false,true"
(const (symbol_ref "current_function_uses_only_leaf_regs")))
(define_attr "in_return_delay" "false,true"
(if_then_else (and (and (and (eq_attr "type" "move,load,sload,store,binary,ialu")
(eq_attr "length" "1"))
(eq_attr "leaf_function" "false"))
(match_insn "eligible_for_return_delay"))
(const_string "true")
(const_string "false")))
(define_delay (and (eq_attr "type" "return")
(eq_attr "isa" "v9"))
[(eq_attr "in_return_delay" "true") (nil) (nil)])
;; ??? Should implement the notion of predelay slots for floating point
;; branches. This would allow us to remove the nop always inserted before
;; a floating point branch.
;; ??? It is OK for fill_simple_delay_slots to put load/store instructions
;; in a delay slot, but it is not OK for fill_eager_delay_slots to do so.
;; This is because doing so will add several pipeline stalls to the path
;; that the load/store did not come from. Unfortunately, there is no way
;; to prevent fill_eager_delay_slots from using load/store without completely
;; disabling them. For the SPEC benchmark set, this is a serious lose,
;; because it prevents us from moving back the final store of inner loops.
(define_attr "in_branch_delay" "false,true"
(if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi")
(eq_attr "length" "1"))
(const_string "true")
(const_string "false")))
(define_attr "in_uncond_branch_delay" "false,true"
(if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi")
(eq_attr "length" "1"))
(const_string "true")
(const_string "false")))
(define_attr "in_annul_branch_delay" "false,true"
(if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi")
(eq_attr "length" "1"))
(const_string "true")
(const_string "false")))
(define_delay (eq_attr "type" "branch")
[(eq_attr "in_branch_delay" "true")
(nil) (eq_attr "in_annul_branch_delay" "true")])
(define_delay (eq_attr "type" "uncond_branch")
[(eq_attr "in_uncond_branch_delay" "true")
(nil) (nil)])
;; Function units of the SPARC
;; (define_function_unit {name} {num-units} {n-users} {test}
;; {ready-delay} {issue-delay} [{conflict-list}])
;; The integer ALU.
;; (Noted only for documentation; units that take one cycle do not need to
;; be specified.)
;; On the sparclite, integer multiply takes 1, 3, or 5 cycles depending on
;; the inputs.
;; (define_function_unit "alu" 1 0
;; (eq_attr "type" "unary,binary,move,address") 1 0)
;; ---- cypress CY7C602 scheduling:
;; Memory with load-delay of 1 (i.e., 2 cycle load).
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "cypress")
(eq_attr "type" "load,sload,fpload"))
2 2)
;; SPARC has two floating-point units: the FP ALU,
;; and the FP MUL/DIV/SQRT unit.
;; Instruction timings on the CY7C602 are as follows
;; FABSs 4
;; FADDs/d 5/5
;; FCMPs/d 4/4
;; FDIVs/d 23/37
;; FMOVs 4
;; FMULs/d 5/7
;; FNEGs 4
;; FSQRTs/d 34/63
;; FSUBs/d 5/5
;; FdTOi/s 5/5
;; FsTOi/d 5/5
;; FiTOs/d 9/5
;; The CY7C602 can only support 2 fp isnsn simultaneously.
;; More insns cause the chip to stall.
(define_function_unit "fp_alu" 1 0
(and (eq_attr "cpu" "cypress")
(eq_attr "type" "fp,fpmove"))
5 5)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "cypress")
(eq_attr "type" "fpmul"))
7 7)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "cypress")
(eq_attr "type" "fpdivs,fpdivd"))
37 37)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "cypress")
(eq_attr "type" "fpsqrt"))
63 63)
;; ----- The TMS390Z55 scheduling
;; The Supersparc can issue 1 - 3 insns per cycle: up to two integer,
;; one ld/st, one fp.
;; Memory delivers its result in one cycle to IU, zero cycles to FP
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "load,sload"))
1 1)
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "fpload"))
0 1)
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "store,fpstore"))
1 1)
(define_function_unit "shift" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "shift"))
1 1)
;; There are only two write ports to the integer register file
;; A store also uses a write port
(define_function_unit "iwport" 2 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "load,sload,store,shift,ialu"))
1 1)
;; Timings; throughput/latency
;; FADD 1/3 add/sub, format conv, compar, abs, neg
;; FMUL 1/3
;; FDIVs 4/6
;; FDIVd 7/9
;; FSQRTs 6/8
;; FSQRTd 10/12
;; IMUL 4/4
(define_function_unit "fp_alu" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "fp,fpmove,fpcmp"))
3 1)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "fpmul"))
3 1)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "fpdivs"))
6 4)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "fpdivd"))
9 7)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "fpsqrt"))
12 10)
(define_function_unit "fp_mds" 1 0
(and (eq_attr "cpu" "supersparc")
(eq_attr "type" "imul"))
4 4)
;; ----- hypersparc/sparclite86x scheduling
;; The Hypersparc can issue 1 - 2 insns per cycle. The dual issue cases are:
;; L-Ld/St I-Int F-Float B-Branch LI/LF/LB/II/IF/IB/FF/FB
;; II/FF case is only when loading a 32 bit hi/lo constant
;; Single issue insns include call, jmpl, u/smul, u/sdiv, lda, sta, fcmp
;; Memory delivers its result in one cycle to IU
(define_function_unit "memory" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "load,sload,fpload"))
1 1)
(define_function_unit "memory" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "store,fpstore"))
2 1)
(define_function_unit "fp_alu" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "fp,fpmove,fpcmp"))
1 1)
(define_function_unit "fp_mds" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "fpmul"))
1 1)
(define_function_unit "fp_mds" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "fpdivs"))
8 6)
(define_function_unit "fp_mds" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "fpdivd"))
12 10)
(define_function_unit "fp_mds" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "fpsqrt"))
17 15)
(define_function_unit "fp_mds" 1 0
(and (ior (eq_attr "cpu" "hypersparc") (eq_attr "cpu" "sparclite86x"))
(eq_attr "type" "imul"))
17 15)
;; ----- sparclet tsc701 scheduling
;; The tsc701 issues 1 insn per cycle.
;; Results may be written back out of order.
;; Loads take 2 extra cycles to complete and 4 can be buffered at a time.
(define_function_unit "tsc701_load" 4 1
(and (eq_attr "cpu" "tsc701")
(eq_attr "type" "load,sload"))
3 1)
;; Stores take 2(?) extra cycles to complete.
;; It is desirable to not have any memory operation in the following 2 cycles.
;; (??? or 2 memory ops in the case of std).
(define_function_unit "tsc701_store" 1 0
(and (eq_attr "cpu" "tsc701")
(eq_attr "type" "store"))
3 3
[(eq_attr "type" "load,sload,store")])
;; The multiply unit has a latency of 5.
(define_function_unit "tsc701_mul" 1 0
(and (eq_attr "cpu" "tsc701")
(eq_attr "type" "imul"))
5 5)
;; ----- The UltraSPARC-1 scheduling
;; UltraSPARC has two integer units. Shift instructions can only execute
;; on IE0. Condition code setting instructions, call, and jmpl (including
;; the ret and retl pseudo-instructions) can only execute on IE1.
;; Branch on register uses IE1, but branch on condition code does not.
;; Conditional moves take 2 cycles. No other instruction can issue in the
;; same cycle as a conditional move.
;; Multiply and divide take many cycles during which no other instructions
;; can issue.
;; Memory delivers its result in two cycles (except for signed loads,
;; which take one cycle more). One memory instruction can be issued per
;; cycle.
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "load,fpload"))
2 1)
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "sload"))
3 1)
(define_function_unit "memory" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "store,fpstore"))
1 1)
(define_function_unit "ieuN" 2 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "ialu,binary,move,unary,shift,compare,call,call_no_delay_slot,uncond_branch"))
1 1)
(define_function_unit "ieu0" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "shift"))
1 1)
(define_function_unit "ieu0" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "cmove"))
2 1)
(define_function_unit "ieu1" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "compare,call,call_no_delay_slot,uncond_branch"))
1 1)
(define_function_unit "cti" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "branch"))
1 1)
;; Timings; throughput/latency
;; FMOV 1/1 fmov, fabs, fneg
;; FMOVcc 1/2
;; FADD 1/4 add/sub, format conv, compar
;; FMUL 1/4
;; FDIVs 12/12
;; FDIVd 22/22
;; FSQRTs 12/12
;; FSQRTd 22/22
;; FCMP takes 1 cycle to branch, 2 cycles to conditional move.
;;
;; ??? This is really bogus because the timings really depend upon
;; who uses the result. We should record who the user is with
;; more descriptive 'type' attribute names and account for these
;; issues in ultrasparc_adjust_cost.
(define_function_unit "fadd" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpmove"))
1 1)
(define_function_unit "fadd" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpcmove"))
2 1)
(define_function_unit "fadd" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fp"))
4 1)
(define_function_unit "fadd" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpcmp"))
2 1)
(define_function_unit "fmul" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpmul"))
4 1)
(define_function_unit "fadd" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpcmove"))
2 1)
(define_function_unit "fmul" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpdivs"))
12 12)
(define_function_unit "fmul" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpdivd"))
22 22)
(define_function_unit "fmul" 1 0
(and (eq_attr "cpu" "ultrasparc")
(eq_attr "type" "fpsqrt"))
12 12)
;; Compare instructions.
;; This controls RTL generation and register allocation.
;; We generate RTL for comparisons and branches by having the cmpxx
;; patterns store away the operands. Then, the scc and bcc patterns
;; emit RTL for both the compare and the branch.
;;
;; We do this because we want to generate different code for an sne and
;; seq insn. In those cases, if the second operand of the compare is not
;; const0_rtx, we want to compute the xor of the two operands and test
;; it against zero.
;;
;; We start with the DEFINE_EXPANDs, then the DEFINE_INSNs to match
;; the patterns. Finally, we have the DEFINE_SPLITs for some of the scc
;; insns that actually require more than one machine instruction.
;; Put cmpsi first among compare insns so it matches two CONST_INT operands.
(define_expand "cmpsi"
[(set (reg:CC 100)
(compare:CC (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "arith_operand" "")))]
""
"
{
sparc_compare_op0 = operands[0];
sparc_compare_op1 = operands[1];
DONE;
}")
(define_expand "cmpdi"
[(set (reg:CCX 100)
(compare:CCX (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "arith_double_operand" "")))]
"TARGET_ARCH64"
"
{
sparc_compare_op0 = operands[0];
sparc_compare_op1 = operands[1];
DONE;
}")
(define_expand "cmpsf"
;; The 96 here isn't ever used by anyone.
[(set (reg:CCFP 96)
(compare:CCFP (match_operand:SF 0 "register_operand" "")
(match_operand:SF 1 "register_operand" "")))]
"TARGET_FPU"
"
{
sparc_compare_op0 = operands[0];
sparc_compare_op1 = operands[1];
DONE;
}")
(define_expand "cmpdf"
;; The 96 here isn't ever used by anyone.
[(set (reg:CCFP 96)
(compare:CCFP (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "register_operand" "")))]
"TARGET_FPU"
"
{
sparc_compare_op0 = operands[0];
sparc_compare_op1 = operands[1];
DONE;
}")
(define_expand "cmptf"
;; The 96 here isn't ever used by anyone.
[(set (reg:CCFP 96)
(compare:CCFP (match_operand:TF 0 "register_operand" "")
(match_operand:TF 1 "register_operand" "")))]
"TARGET_FPU"
"
{
sparc_compare_op0 = operands[0];
sparc_compare_op1 = operands[1];
DONE;
}")
;; Now the compare DEFINE_INSNs.
(define_insn "*cmpsi_insn"
[(set (reg:CC 100)
(compare:CC (match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "arith_operand" "rI")))]
""
"cmp\\t%0, %1"
[(set_attr "type" "compare")])
(define_insn "*cmpdi_sp64"
[(set (reg:CCX 100)
(compare:CCX (match_operand:DI 0 "register_operand" "r")
(match_operand:DI 1 "arith_double_operand" "rHI")))]
"TARGET_ARCH64"
"cmp\\t%0, %1"
[(set_attr "type" "compare")])
(define_insn "*cmpsf_fpe"
[(set (match_operand:CCFPE 0 "fcc_reg_operand" "=c")
(compare:CCFPE (match_operand:SF 1 "register_operand" "f")
(match_operand:SF 2 "register_operand" "f")))]
"TARGET_FPU"
"*
{
if (TARGET_V9)
return \"fcmpes\\t%0, %1, %2\";
return \"fcmpes\\t%1, %2\";
}"
[(set_attr "type" "fpcmp")])
(define_insn "*cmpdf_fpe"
[(set (match_operand:CCFPE 0 "fcc_reg_operand" "=c")
(compare:CCFPE (match_operand:DF 1 "register_operand" "e")
(match_operand:DF 2 "register_operand" "e")))]
"TARGET_FPU"
"*
{
if (TARGET_V9)
return \"fcmped\\t%0, %1, %2\";
return \"fcmped\\t%1, %2\";
}"
[(set_attr "type" "fpcmp")])
(define_insn "*cmptf_fpe"
[(set (match_operand:CCFPE 0 "fcc_reg_operand" "=c")
(compare:CCFPE (match_operand:TF 1 "register_operand" "e")
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"*
{
if (TARGET_V9)
return \"fcmpeq\\t%0, %1, %2\";
return \"fcmpeq\\t%1, %2\";
}"
[(set_attr "type" "fpcmp")])
(define_insn "*cmpsf_fp"
[(set (match_operand:CCFP 0 "fcc_reg_operand" "=c")
(compare:CCFP (match_operand:SF 1 "register_operand" "f")
(match_operand:SF 2 "register_operand" "f")))]
"TARGET_FPU"
"*
{
if (TARGET_V9)
return \"fcmps\\t%0, %1, %2\";
return \"fcmps\\t%1, %2\";
}"
[(set_attr "type" "fpcmp")])
(define_insn "*cmpdf_fp"
[(set (match_operand:CCFP 0 "fcc_reg_operand" "=c")
(compare:CCFP (match_operand:DF 1 "register_operand" "e")
(match_operand:DF 2 "register_operand" "e")))]
"TARGET_FPU"
"*
{
if (TARGET_V9)
return \"fcmpd\\t%0, %1, %2\";
return \"fcmpd\\t%1, %2\";
}"
[(set_attr "type" "fpcmp")])
(define_insn "*cmptf_fp"
[(set (match_operand:CCFP 0 "fcc_reg_operand" "=c")
(compare:CCFP (match_operand:TF 1 "register_operand" "e")
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"*
{
if (TARGET_V9)
return \"fcmpq\\t%0, %1, %2\";
return \"fcmpq\\t%1, %2\";
}"
[(set_attr "type" "fpcmp")])
;; Next come the scc insns. For seq, sne, sgeu, and sltu, we can do this
;; without jumps using the addx/subx instructions. For seq/sne on v9 we use
;; the same code as v8 (the addx/subx method has more applications). The
;; exception to this is "reg != 0" which can be done in one instruction on v9
;; (so we do it). For the rest, on v9 we use conditional moves; on v8, we do
;; branches.
;; Seq_special[_xxx] and sne_special[_xxx] clobber the CC reg, because they
;; generate addcc/subcc instructions.
(define_expand "seqsi_special"
[(set (match_dup 3)
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "register_operand" "")))
(parallel [(set (match_operand:SI 0 "register_operand" "")
(eq:SI (match_dup 3) (const_int 0)))
(clobber (reg:CC 100))])]
"! TARGET_LIVE_G0"
"{ operands[3] = gen_reg_rtx (SImode); }")
(define_expand "seqdi_special"
[(set (match_dup 3)
(xor:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "register_operand" "")))
(set (match_operand:DI 0 "register_operand" "")
(eq:DI (match_dup 3) (const_int 0)))]
"TARGET_ARCH64"
"{ operands[3] = gen_reg_rtx (DImode); }")
(define_expand "snesi_special"
[(set (match_dup 3)
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "register_operand" "")))
(parallel [(set (match_operand:SI 0 "register_operand" "")
(ne:SI (match_dup 3) (const_int 0)))
(clobber (reg:CC 100))])]
"! TARGET_LIVE_G0"
"{ operands[3] = gen_reg_rtx (SImode); }")
(define_expand "snedi_special"
[(set (match_dup 3)
(xor:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "register_operand" "")))
(set (match_operand:DI 0 "register_operand" "")
(ne:DI (match_dup 3) (const_int 0)))]
"TARGET_ARCH64"
"{ operands[3] = gen_reg_rtx (DImode); }")
(define_expand "seqdi_special_trunc"
[(set (match_dup 3)
(xor:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "register_operand" "")))
(set (match_operand:SI 0 "register_operand" "")
(eq:SI (match_dup 3) (const_int 0)))]
"TARGET_ARCH64"
"{ operands[3] = gen_reg_rtx (DImode); }")
(define_expand "snedi_special_trunc"
[(set (match_dup 3)
(xor:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "register_operand" "")))
(set (match_operand:SI 0 "register_operand" "")
(ne:SI (match_dup 3) (const_int 0)))]
"TARGET_ARCH64"
"{ operands[3] = gen_reg_rtx (DImode); }")
(define_expand "seqsi_special_extend"
[(set (match_dup 3)
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "register_operand" "")))
(parallel [(set (match_operand:DI 0 "register_operand" "")
(eq:DI (match_dup 3) (const_int 0)))
(clobber (reg:CC 100))])]
"TARGET_ARCH64"
"{ operands[3] = gen_reg_rtx (SImode); }")
(define_expand "snesi_special_extend"
[(set (match_dup 3)
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "register_operand" "")))
(parallel [(set (match_operand:DI 0 "register_operand" "")
(ne:DI (match_dup 3) (const_int 0)))
(clobber (reg:CC 100))])]
"TARGET_ARCH64"
"{ operands[3] = gen_reg_rtx (SImode); }")
;; ??? v9: Operand 0 needs a mode, so SImode was chosen.
;; However, the code handles both SImode and DImode.
(define_expand "seq"
[(set (match_operand:SI 0 "intreg_operand" "")
(eq:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (GET_MODE (sparc_compare_op0) == SImode)
{
rtx pat;
if (GET_MODE (operands[0]) == SImode)
pat = gen_seqsi_special (operands[0], sparc_compare_op0,
sparc_compare_op1);
else if (! TARGET_ARCH64)
FAIL;
else
pat = gen_seqsi_special_extend (operands[0], sparc_compare_op0,
sparc_compare_op1);
emit_insn (pat);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == DImode)
{
rtx pat;
if (! TARGET_ARCH64)
FAIL;
else if (GET_MODE (operands[0]) == SImode)
pat = gen_seqdi_special_trunc (operands[0], sparc_compare_op0,
sparc_compare_op1);
else
pat = gen_seqdi_special (operands[0], sparc_compare_op0,
sparc_compare_op1);
emit_insn (pat);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, EQ);
emit_insn (gen_sne (operands[0]));
DONE;
}
else if (TARGET_V9)
{
if (gen_v9_scc (EQ, operands))
DONE;
/* fall through */
}
FAIL;
}")
;; ??? v9: Operand 0 needs a mode, so SImode was chosen.
;; However, the code handles both SImode and DImode.
(define_expand "sne"
[(set (match_operand:SI 0 "intreg_operand" "")
(ne:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (GET_MODE (sparc_compare_op0) == SImode)
{
rtx pat;
if (GET_MODE (operands[0]) == SImode)
pat = gen_snesi_special (operands[0], sparc_compare_op0,
sparc_compare_op1);
else if (! TARGET_ARCH64)
FAIL;
else
pat = gen_snesi_special_extend (operands[0], sparc_compare_op0,
sparc_compare_op1);
emit_insn (pat);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == DImode)
{
rtx pat;
if (! TARGET_ARCH64)
FAIL;
else if (GET_MODE (operands[0]) == SImode)
pat = gen_snedi_special_trunc (operands[0], sparc_compare_op0,
sparc_compare_op1);
else
pat = gen_snedi_special (operands[0], sparc_compare_op0,
sparc_compare_op1);
emit_insn (pat);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, NE);
emit_insn (gen_sne (operands[0]));
DONE;
}
else if (TARGET_V9)
{
if (gen_v9_scc (NE, operands))
DONE;
/* fall through */
}
FAIL;
}")
(define_expand "sgt"
[(set (match_operand:SI 0 "intreg_operand" "")
(gt:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GT);
emit_insn (gen_sne (operands[0]));
DONE;
}
else if (TARGET_V9)
{
if (gen_v9_scc (GT, operands))
DONE;
/* fall through */
}
FAIL;
}")
(define_expand "slt"
[(set (match_operand:SI 0 "intreg_operand" "")
(lt:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LT);
emit_insn (gen_sne (operands[0]));
DONE;
}
else if (TARGET_V9)
{
if (gen_v9_scc (LT, operands))
DONE;
/* fall through */
}
FAIL;
}")
(define_expand "sge"
[(set (match_operand:SI 0 "intreg_operand" "")
(ge:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GE);
emit_insn (gen_sne (operands[0]));
DONE;
}
else if (TARGET_V9)
{
if (gen_v9_scc (GE, operands))
DONE;
/* fall through */
}
FAIL;
}")
(define_expand "sle"
[(set (match_operand:SI 0 "intreg_operand" "")
(le:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LE);
emit_insn (gen_sne (operands[0]));
DONE;
}
else if (TARGET_V9)
{
if (gen_v9_scc (LE, operands))
DONE;
/* fall through */
}
FAIL;
}")
(define_expand "sgtu"
[(set (match_operand:SI 0 "intreg_operand" "")
(gtu:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (! TARGET_V9)
{
rtx tem, pat;
/* We can do ltu easily, so if both operands are registers, swap them and
do a LTU. */
if ((GET_CODE (sparc_compare_op0) == REG
|| GET_CODE (sparc_compare_op0) == SUBREG)
&& (GET_CODE (sparc_compare_op1) == REG
|| GET_CODE (sparc_compare_op1) == SUBREG))
{
tem = sparc_compare_op0;
sparc_compare_op0 = sparc_compare_op1;
sparc_compare_op1 = tem;
pat = gen_sltu (operands[0]);
if (pat == NULL_RTX)
FAIL;
emit_insn (pat);
DONE;
}
}
else
{
if (gen_v9_scc (GTU, operands))
DONE;
}
FAIL;
}")
(define_expand "sltu"
[(set (match_operand:SI 0 "intreg_operand" "")
(ltu:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (TARGET_V9)
{
if (gen_v9_scc (LTU, operands))
DONE;
}
operands[1] = gen_compare_reg (LTU, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "sgeu"
[(set (match_operand:SI 0 "intreg_operand" "")
(geu:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (TARGET_V9)
{
if (gen_v9_scc (GEU, operands))
DONE;
}
operands[1] = gen_compare_reg (GEU, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "sleu"
[(set (match_operand:SI 0 "intreg_operand" "")
(leu:SI (match_dup 1) (const_int 0)))]
"! TARGET_LIVE_G0"
"
{
if (! TARGET_V9)
{
rtx tem, pat;
/* We can do geu easily, so if both operands are registers, swap them and
do a GEU. */
if ((GET_CODE (sparc_compare_op0) == REG
|| GET_CODE (sparc_compare_op0) == SUBREG)
&& (GET_CODE (sparc_compare_op1) == REG
|| GET_CODE (sparc_compare_op1) == SUBREG))
{
tem = sparc_compare_op0;
sparc_compare_op0 = sparc_compare_op1;
sparc_compare_op1 = tem;
pat = gen_sgeu (operands[0]);
if (pat == NULL_RTX)
FAIL;
emit_insn (pat);
DONE;
}
}
else
{
if (gen_v9_scc (LEU, operands))
DONE;
}
FAIL;
}")
;; Now the DEFINE_INSNs for the scc cases.
;; The SEQ and SNE patterns are special because they can be done
;; without any branching and do not involve a COMPARE. We want
;; them to always use the splitz below so the results can be
;; scheduled.
(define_insn "*snesi_zero"
[(set (match_operand:SI 0 "register_operand" "=r")
(ne:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0)))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(ne:SI (match_operand:SI 1 "register_operand" "")
(const_int 0)))
(clobber (reg:CC 100))]
""
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (ltu:SI (reg:CC 100) (const_int 0)))]
"")
(define_insn "*neg_snesi_zero"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (ne:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0))))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(neg:SI (ne:SI (match_operand:SI 1 "register_operand" "")
(const_int 0))))
(clobber (reg:CC 100))]
""
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (neg:SI (ltu:SI (reg:CC 100) (const_int 0))))]
"")
(define_insn "*snesi_zero_extend"
[(set (match_operand:DI 0 "register_operand" "=r")
(ne:DI (match_operand:SI 1 "register_operand" "r")
(const_int 0)))
(clobber (reg:CC 100))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "unary")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(ne:DI (match_operand:SI 1 "register_operand" "")
(const_int 0)))
(clobber (reg:CC 100))]
"TARGET_ARCH64"
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (minus:SI (const_int 0) (match_dup 1))
(const_int 0)))
(set (match_dup 0) (zero_extend:DI (plus:SI (plus:SI (const_int 0)
(const_int 0))
(ltu:SI (reg:CC_NOOV 100)
(const_int 0)))))]
"")
(define_insn "*snedi_zero"
[(set (match_operand:DI 0 "register_operand" "=&r")
(ne:DI (match_operand:DI 1 "register_operand" "r")
(const_int 0)))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "cmove")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(ne:DI (match_operand:DI 1 "register_operand" "")
(const_int 0)))]
"TARGET_ARCH64"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0) (if_then_else:DI (ne:DI (match_dup 1)
(const_int 0))
(const_int 1)
(match_dup 0)))]
"")
(define_insn "*neg_snedi_zero"
[(set (match_operand:DI 0 "register_operand" "=&r")
(neg:DI (ne:DI (match_operand:DI 1 "register_operand" "r")
(const_int 0))))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "cmove")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(neg:DI (ne:DI (match_operand:DI 1 "register_operand" "")
(const_int 0))))]
"TARGET_ARCH64"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0) (if_then_else:DI (ne:DI (match_dup 1)
(const_int 0))
(const_int -1)
(match_dup 0)))]
"")
(define_insn "*snedi_zero_trunc"
[(set (match_operand:SI 0 "register_operand" "=&r")
(ne:SI (match_operand:DI 1 "register_operand" "r")
(const_int 0)))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "cmove")
(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(ne:SI (match_operand:DI 1 "register_operand" "")
(const_int 0)))]
"TARGET_ARCH64"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0) (if_then_else:SI (ne:DI (match_dup 1)
(const_int 0))
(const_int 1)
(match_dup 0)))]
"")
(define_insn "*seqsi_zero"
[(set (match_operand:SI 0 "register_operand" "=r")
(eq:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0)))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(eq:SI (match_operand:SI 1 "register_operand" "")
(const_int 0)))
(clobber (reg:CC 100))]
""
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (geu:SI (reg:CC 100) (const_int 0)))]
"")
(define_insn "*neg_seqsi_zero"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (eq:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0))))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(neg:SI (eq:SI (match_operand:SI 1 "register_operand" "")
(const_int 0))))
(clobber (reg:CC 100))]
""
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (neg:SI (geu:SI (reg:CC 100) (const_int 0))))]
"")
(define_insn "*seqsi_zero_extend"
[(set (match_operand:DI 0 "register_operand" "=r")
(eq:DI (match_operand:SI 1 "register_operand" "r")
(const_int 0)))
(clobber (reg:CC 100))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "unary")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(eq:DI (match_operand:SI 1 "register_operand" "")
(const_int 0)))
(clobber (reg:CC 100))]
"TARGET_ARCH64"
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (minus:SI (const_int 0) (match_dup 1))
(const_int 0)))
(set (match_dup 0) (zero_extend:DI (minus:SI (minus:SI (const_int 0)
(const_int -1))
(ltu:SI (reg:CC_NOOV 100)
(const_int 0)))))]
"")
(define_insn "*seqdi_zero"
[(set (match_operand:DI 0 "register_operand" "=&r")
(eq:DI (match_operand:DI 1 "register_operand" "r")
(const_int 0)))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "cmove")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(eq:DI (match_operand:DI 1 "register_operand" "")
(const_int 0)))]
"TARGET_ARCH64"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0) (if_then_else:DI (eq:DI (match_dup 1)
(const_int 0))
(const_int 1)
(match_dup 0)))]
"")
(define_insn "*neg_seqdi_zero"
[(set (match_operand:DI 0 "register_operand" "=&r")
(neg:DI (eq:DI (match_operand:DI 1 "register_operand" "r")
(const_int 0))))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "cmove")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(neg:DI (eq:DI (match_operand:DI 1 "register_operand" "")
(const_int 0))))]
"TARGET_ARCH64"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0) (if_then_else:DI (eq:DI (match_dup 1)
(const_int 0))
(const_int -1)
(match_dup 0)))]
"")
(define_insn "*seqdi_zero_trunc"
[(set (match_operand:SI 0 "register_operand" "=&r")
(eq:SI (match_operand:DI 1 "register_operand" "r")
(const_int 0)))]
"TARGET_ARCH64"
"#"
[(set_attr "type" "cmove")
(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(eq:SI (match_operand:DI 1 "register_operand" "")
(const_int 0)))]
"TARGET_ARCH64"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0) (if_then_else:SI (eq:DI (match_dup 1)
(const_int 0))
(const_int 1)
(match_dup 0)))]
"")
;; We can also do (x + (i == 0)) and related, so put them in.
;; ??? The addx/subx insns use the 32 bit carry flag so there are no DImode
;; versions for v9.
(define_insn "*x_plus_i_ne_0"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (ne:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0))
(match_operand:SI 2 "register_operand" "r")))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (ne:SI (match_operand:SI 1 "register_operand" "")
(const_int 0))
(match_operand:SI 2 "register_operand" "")))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (plus:SI (ltu:SI (reg:CC 100) (const_int 0))
(match_dup 2)))]
"")
(define_insn "*x_minus_i_ne_0"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 2 "register_operand" "r")
(ne:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0))))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 2 "register_operand" "")
(ne:SI (match_operand:SI 1 "register_operand" "")
(const_int 0))))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (minus:SI (match_dup 2)
(ltu:SI (reg:CC 100) (const_int 0))))]
"")
(define_insn "*x_plus_i_eq_0"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (eq:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0))
(match_operand:SI 2 "register_operand" "r")))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (eq:SI (match_operand:SI 1 "register_operand" "")
(const_int 0))
(match_operand:SI 2 "register_operand" "")))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (plus:SI (geu:SI (reg:CC 100) (const_int 0))
(match_dup 2)))]
"")
(define_insn "*x_minus_i_eq_0"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 2 "register_operand" "r")
(eq:SI (match_operand:SI 1 "register_operand" "r")
(const_int 0))))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 2 "register_operand" "")
(eq:SI (match_operand:SI 1 "register_operand" "")
(const_int 0))))
(clobber (reg:CC 100))]
"! TARGET_LIVE_G0"
[(set (reg:CC_NOOV 100) (compare:CC_NOOV (neg:SI (match_dup 1))
(const_int 0)))
(set (match_dup 0) (minus:SI (match_dup 2)
(geu:SI (reg:CC 100) (const_int 0))))]
"")
;; We can also do GEU and LTU directly, but these operate after a compare.
;; ??? The addx/subx insns use the 32 bit carry flag so there are no DImode
;; versions for v9.
(define_insn "*sltu_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(ltu:SI (reg:CC 100) (const_int 0)))]
"! TARGET_LIVE_G0"
"addx\\t%%g0, 0, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*neg_sltu_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (ltu:SI (reg:CC 100) (const_int 0))))]
"! TARGET_LIVE_G0"
"subx\\t%%g0, 0, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
;; ??? Combine should canonicalize these next two to the same pattern.
(define_insn "*neg_sltu_minus_x"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (neg:SI (ltu:SI (reg:CC 100) (const_int 0)))
(match_operand:SI 1 "arith_operand" "rI")))]
"! TARGET_LIVE_G0"
"subx\\t%%g0, %1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*neg_sltu_plus_x"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (plus:SI (ltu:SI (reg:CC 100) (const_int 0))
(match_operand:SI 1 "arith_operand" "rI"))))]
"! TARGET_LIVE_G0"
"subx\\t%%g0, %1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*sgeu_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(geu:SI (reg:CC 100) (const_int 0)))]
"! TARGET_LIVE_G0"
"subx\\t%%g0, -1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*neg_sgeu_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (geu:SI (reg:CC 100) (const_int 0))))]
"! TARGET_LIVE_G0"
"addx\\t%%g0, -1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
;; We can also do (x + ((unsigned) i >= 0)) and related, so put them in.
;; ??? The addx/subx insns use the 32 bit carry flag so there are no DImode
;; versions for v9.
(define_insn "*sltu_plus_x"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (ltu:SI (reg:CC 100) (const_int 0))
(match_operand:SI 1 "arith_operand" "rI")))]
"! TARGET_LIVE_G0"
"addx\\t%%g0, %1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*sltu_plus_x_plus_y"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (ltu:SI (reg:CC 100) (const_int 0))
(plus:SI (match_operand:SI 1 "arith_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI"))))]
""
"addx\\t%1, %2, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*x_minus_sltu"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 1 "register_operand" "r")
(ltu:SI (reg:CC 100) (const_int 0))))]
""
"subx\\t%1, 0, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
;; ??? Combine should canonicalize these next two to the same pattern.
(define_insn "*x_minus_y_minus_sltu"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC 100) (const_int 0))))]
""
"subx\\t%r1, %2, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*x_minus_sltu_plus_y"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(plus:SI (ltu:SI (reg:CC 100) (const_int 0))
(match_operand:SI 2 "arith_operand" "rI"))))]
""
"subx\\t%r1, %2, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*sgeu_plus_x"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (geu:SI (reg:CC 100) (const_int 0))
(match_operand:SI 1 "register_operand" "r")))]
""
"subx\\t%1, -1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*x_minus_sgeu"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 1 "register_operand" "r")
(geu:SI (reg:CC 100) (const_int 0))))]
""
"addx\\t%1, -1, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_split
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 2 "noov_compare_op"
[(match_operand 1 "icc_or_fcc_reg_operand" "")
(const_int 0)]))]
;; 32 bit LTU/GEU are better implemented using addx/subx
"TARGET_V9 && REGNO (operands[1]) == SPARC_ICC_REG
&& (GET_MODE (operands[1]) == CCXmode
|| (GET_CODE (operands[2]) != LTU && GET_CODE (operands[2]) != GEU))"
[(set (match_dup 0) (const_int 0))
(set (match_dup 0)
(if_then_else:SI (match_op_dup:SI 2 [(match_dup 1) (const_int 0)])
(const_int 1)
(match_dup 0)))]
"")
;; These control RTL generation for conditional jump insns
;; The quad-word fp compare library routines all return nonzero to indicate
;; true, which is different from the equivalent libgcc routines, so we must
;; handle them specially here.
(define_expand "beq"
[(set (pc)
(if_then_else (eq (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode)
{
emit_v9_brxx_insn (EQ, sparc_compare_op0, operands[0]);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, EQ);
emit_jump_insn (gen_bne (operands[0]));
DONE;
}
operands[1] = gen_compare_reg (EQ, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bne"
[(set (pc)
(if_then_else (ne (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode)
{
emit_v9_brxx_insn (NE, sparc_compare_op0, operands[0]);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, NE);
emit_jump_insn (gen_bne (operands[0]));
DONE;
}
operands[1] = gen_compare_reg (NE, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bgt"
[(set (pc)
(if_then_else (gt (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode)
{
emit_v9_brxx_insn (GT, sparc_compare_op0, operands[0]);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GT);
emit_jump_insn (gen_bne (operands[0]));
DONE;
}
operands[1] = gen_compare_reg (GT, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bgtu"
[(set (pc)
(if_then_else (gtu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{ operands[1] = gen_compare_reg (GTU, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "blt"
[(set (pc)
(if_then_else (lt (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode)
{
emit_v9_brxx_insn (LT, sparc_compare_op0, operands[0]);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LT);
emit_jump_insn (gen_bne (operands[0]));
DONE;
}
operands[1] = gen_compare_reg (LT, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bltu"
[(set (pc)
(if_then_else (ltu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{ operands[1] = gen_compare_reg (LTU, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bge"
[(set (pc)
(if_then_else (ge (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode)
{
emit_v9_brxx_insn (GE, sparc_compare_op0, operands[0]);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, GE);
emit_jump_insn (gen_bne (operands[0]));
DONE;
}
operands[1] = gen_compare_reg (GE, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bgeu"
[(set (pc)
(if_then_else (geu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{ operands[1] = gen_compare_reg (GEU, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "ble"
[(set (pc)
(if_then_else (le (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{
if (TARGET_ARCH64 && sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode)
{
emit_v9_brxx_insn (LE, sparc_compare_op0, operands[0]);
DONE;
}
else if (GET_MODE (sparc_compare_op0) == TFmode && ! TARGET_HARD_QUAD)
{
emit_float_lib_cmp (sparc_compare_op0, sparc_compare_op1, LE);
emit_jump_insn (gen_bne (operands[0]));
DONE;
}
operands[1] = gen_compare_reg (LE, sparc_compare_op0, sparc_compare_op1);
}")
(define_expand "bleu"
[(set (pc)
(if_then_else (leu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"
{ operands[1] = gen_compare_reg (LEU, sparc_compare_op0, sparc_compare_op1);
}")
;; Now match both normal and inverted jump.
;; XXX fpcmp nop braindamage
(define_insn "*normal_branch"
[(set (pc)
(if_then_else (match_operator 0 "noov_compare_op"
[(reg 100) (const_int 0)])
(label_ref (match_operand 1 "" ""))
(pc)))]
""
"*
{
return output_cbranch (operands[0], 1, 0,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; XXX fpcmp nop braindamage
(define_insn "*inverted_branch"
[(set (pc)
(if_then_else (match_operator 0 "noov_compare_op"
[(reg 100) (const_int 0)])
(pc)
(label_ref (match_operand 1 "" ""))))]
""
"*
{
return output_cbranch (operands[0], 1, 1,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; XXX fpcmp nop braindamage
(define_insn "*normal_fp_branch"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(match_operand:CCFP 0 "fcc_reg_operand" "c")
(const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
""
"*
{
return output_cbranch (operands[1], 2, 0,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; XXX fpcmp nop braindamage
(define_insn "*inverted_fp_branch"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(match_operand:CCFP 0 "fcc_reg_operand" "c")
(const_int 0)])
(pc)
(label_ref (match_operand 2 "" ""))))]
""
"*
{
return output_cbranch (operands[1], 2, 1,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; XXX fpcmp nop braindamage
(define_insn "*normal_fpe_branch"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(match_operand:CCFPE 0 "fcc_reg_operand" "c")
(const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
""
"*
{
return output_cbranch (operands[1], 2, 0,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; XXX fpcmp nop braindamage
(define_insn "*inverted_fpe_branch"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(match_operand:CCFPE 0 "fcc_reg_operand" "c")
(const_int 0)])
(pc)
(label_ref (match_operand 2 "" ""))))]
""
"*
{
return output_cbranch (operands[1], 2, 1,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; Sparc V9-specific jump insns. None of these are guaranteed to be
;; in the architecture.
;; There are no 32 bit brreg insns.
;; XXX
(define_insn "*normal_int_branch_sp64"
[(set (pc)
(if_then_else (match_operator 0 "v9_regcmp_op"
[(match_operand:DI 1 "register_operand" "r")
(const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_ARCH64"
"*
{
return output_v9branch (operands[0], 1, 2, 0,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; XXX
(define_insn "*inverted_int_branch_sp64"
[(set (pc)
(if_then_else (match_operator 0 "v9_regcmp_op"
[(match_operand:DI 1 "register_operand" "r")
(const_int 0)])
(pc)
(label_ref (match_operand 2 "" ""))))]
"TARGET_ARCH64"
"*
{
return output_v9branch (operands[0], 1, 2, 1,
final_sequence && INSN_ANNULLED_BRANCH_P (insn),
! final_sequence, insn);
}"
[(set_attr "type" "branch")])
;; Load program counter insns.
(define_insn "get_pc"
[(clobber (reg:SI 15))
(set (match_operand 0 "register_operand" "=r")
(unspec [(match_operand 1 "" "") (match_operand 2 "" "")] 2))]
"flag_pic && REGNO (operands[0]) == 23"
"sethi\\t%%hi(%a1-4), %0\\n\\tcall\\t%a2\\n\\tadd\\t%0, %%lo(%a1+4), %0"
[(set_attr "length" "3")])
;; Currently unused...
;; (define_insn "get_pc_via_rdpc"
;; [(set (match_operand 0 "register_operand" "=r") (pc))]
;; "TARGET_V9"
;; "rd\\t%%pc, %0"
;; [(set_attr "type" "move")])
;; Move instructions
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
"
{
/* Working with CONST_INTs is easier, so convert
a double if needed. */
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]) & 0xff);
}
else if (GET_CODE (operands[1]) == CONST_INT)
{
/* And further, we know for all QI cases that only the
low byte is significant, which we can always process
in a single insn. So mask it now. */
operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);
}
/* Handle sets of MEM first. */
if (GET_CODE (operands[0]) == MEM)
{
/* This checks TARGET_LIVE_G0 for us. */
if (reg_or_0_operand (operands[1], QImode))
goto movqi_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (QImode, operands[1]);
}
}
/* Fixup PIC cases. */
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], QImode, 0);
if (symbolic_operand (operands[1], QImode))
{
operands[1] = legitimize_pic_address (operands[1],
QImode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
goto movqi_is_ok;
}
}
/* All QI constants require only one insn, so proceed. */
movqi_is_ok:
;
}")
(define_insn "*movqi_insn"
[(set (match_operand:QI 0 "general_operand" "=r,r,m")
(match_operand:QI 1 "input_operand" "rI,m,rJ"))]
"(register_operand (operands[0], QImode)
|| reg_or_0_operand (operands[1], QImode))"
"@
mov\\t%1, %0
ldub\\t%1, %0
stb\\t%r1, %0"
[(set_attr "type" "move,load,store")
(set_attr "length" "1")])
(define_expand "movhi"
[(set (match_operand:HI 0 "general_operand" "")
(match_operand:HI 1 "general_operand" ""))]
""
"
{
/* Working with CONST_INTs is easier, so convert
a double if needed. */
if (GET_CODE (operands[1]) == CONST_DOUBLE)
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
/* Handle sets of MEM first. */
if (GET_CODE (operands[0]) == MEM)
{
/* This checks TARGET_LIVE_G0 for us. */
if (reg_or_0_operand (operands[1], HImode))
goto movhi_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (HImode, operands[1]);
}
}
/* Fixup PIC cases. */
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], HImode, 0);
if (symbolic_operand (operands[1], HImode))
{
operands[1] = legitimize_pic_address (operands[1],
HImode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
goto movhi_is_ok;
}
}
/* This makes sure we will not get rematched due to splittage. */
if (! CONSTANT_P (operands[1]) || input_operand (operands[1], HImode))
;
else if (CONSTANT_P (operands[1])
&& GET_CODE (operands[1]) != HIGH
&& GET_CODE (operands[1]) != LO_SUM)
{
sparc_emit_set_const32 (operands[0], operands[1]);
DONE;
}
movhi_is_ok:
;
}")
(define_insn "*movhi_const64_special"
[(set (match_operand:HI 0 "register_operand" "=r")
(match_operand:HI 1 "const64_high_operand" ""))]
"TARGET_ARCH64"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*movhi_insn"
[(set (match_operand:HI 0 "general_operand" "=r,r,r,m")
(match_operand:HI 1 "input_operand" "rI,K,m,rJ"))]
"(register_operand (operands[0], HImode)
|| reg_or_0_operand (operands[1], HImode))"
"@
mov\\t%1, %0
sethi\\t%%hi(%a1), %0
lduh\\t%1, %0
sth\\t%r1, %0"
[(set_attr "type" "move,move,load,store")
(set_attr "length" "1")])
;; We always work with constants here.
(define_insn "*movhi_lo_sum"
[(set (match_operand:HI 0 "register_operand" "=r")
(ior:HI (match_operand:HI 1 "arith_operand" "%r")
(match_operand:HI 2 "arith_operand" "I")))]
""
"or\\t%1, %2, %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_expand "movsi"
[(set (match_operand:SI 0 "general_operand" "")
(match_operand:SI 1 "general_operand" ""))]
""
"
{
/* Working with CONST_INTs is easier, so convert
a double if needed. */
if (GET_CODE (operands[1]) == CONST_DOUBLE)
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
/* Handle sets of MEM first. */
if (GET_CODE (operands[0]) == MEM)
{
/* This checks TARGET_LIVE_G0 for us. */
if (reg_or_0_operand (operands[1], SImode))
goto movsi_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (SImode, operands[1]);
}
}
/* Fixup PIC cases. */
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], SImode, 0);
if (GET_CODE (operands[1]) == LABEL_REF)
{
/* shit */
emit_insn (gen_movsi_pic_label_ref (operands[0], operands[1]));
DONE;
}
if (symbolic_operand (operands[1], SImode))
{
operands[1] = legitimize_pic_address (operands[1],
SImode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
goto movsi_is_ok;
}
}
/* If we are trying to toss an integer constant into the
FPU registers, force it into memory. */
if (GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) >= SPARC_FIRST_FP_REG
&& REGNO (operands[0]) <= SPARC_LAST_V9_FP_REG
&& CONSTANT_P (operands[1]))
operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]),
operands[1]));
/* This makes sure we will not get rematched due to splittage. */
if (! CONSTANT_P (operands[1]) || input_operand (operands[1], SImode))
;
else if (CONSTANT_P (operands[1])
&& GET_CODE (operands[1]) != HIGH
&& GET_CODE (operands[1]) != LO_SUM)
{
sparc_emit_set_const32 (operands[0], operands[1]);
DONE;
}
movsi_is_ok:
;
}")
;; Special LIVE_G0 pattern to obtain zero in a register.
(define_insn "*movsi_zero_liveg0"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operand:SI 1 "zero_operand" "J"))]
"TARGET_LIVE_G0"
"and\\t%0, 0, %0"
[(set_attr "type" "binary")
(set_attr "length" "1")])
;; This is needed to show CSE exactly which bits are set
;; in a 64-bit register by sethi instructions.
(define_insn "*movsi_const64_special"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operand:SI 1 "const64_high_operand" ""))]
"TARGET_ARCH64"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*movsi_insn"
[(set (match_operand:SI 0 "general_operand" "=r,f,r,r,r,f,m,m,d")
(match_operand:SI 1 "input_operand" "rI,!f,K,J,m,!m,rJ,!f,J"))]
"(register_operand (operands[0], SImode)
|| reg_or_0_operand (operands[1], SImode))"
"@
mov\\t%1, %0
fmovs\\t%1, %0
sethi\\t%%hi(%a1), %0
clr\\t%0
ld\\t%1, %0
ld\\t%1, %0
st\\t%r1, %0
st\\t%1, %0
fzeros\\t%0"
[(set_attr "type" "move,fpmove,move,move,load,fpload,store,fpstore,fpmove")
(set_attr "length" "1")])
(define_insn "*movsi_lo_sum"
[(set (match_operand:SI 0 "register_operand" "=r")
(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "immediate_operand" "in")))]
""
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "*movsi_high"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (match_operand:SI 1 "immediate_operand" "in")))]
""
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
;; The next two patterns must wrap the SYMBOL_REF in an UNSPEC
;; so that CSE won't optimize the address computation away.
(define_insn "movsi_lo_sum_pic"
[(set (match_operand:SI 0 "register_operand" "=r")
(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
(unspec:SI [(match_operand:SI 2 "immediate_operand" "in")] 0)))]
"flag_pic"
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "movsi_high_pic"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (unspec:SI [(match_operand 1 "" "")] 0)))]
"flag_pic && check_pic (1)"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_expand "movsi_pic_label_ref"
[(set (match_dup 3) (high:SI
(unspec:SI [(match_operand:SI 1 "label_ref_operand" "")
(match_dup 2)] 5)))
(set (match_dup 4) (lo_sum:SI (match_dup 3)
(unspec:SI [(match_dup 1) (match_dup 2)] 5)))
(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_dup 5) (match_dup 4)))]
"flag_pic"
"
{
current_function_uses_pic_offset_table = 1;
operands[2] = gen_rtx_SYMBOL_REF (Pmode, \"_GLOBAL_OFFSET_TABLE_\");
operands[3] = gen_reg_rtx (SImode);
operands[4] = gen_reg_rtx (SImode);
operands[5] = pic_offset_table_rtx;
}")
(define_insn "*movsi_high_pic_label_ref"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI
(unspec:SI [(match_operand:SI 1 "label_ref_operand" "")
(match_operand:SI 2 "" "")] 5)))]
"flag_pic"
"sethi\\t%%hi(%a2-(%a1-.)), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*movsi_lo_sum_pic_label_ref"
[(set (match_operand:SI 0 "register_operand" "=r")
(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
(unspec:SI [(match_operand:SI 2 "label_ref_operand" "")
(match_operand:SI 3 "" "")] 5)))]
"flag_pic"
"or\\t%1, %%lo(%a3-(%a2-.)), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_expand "movdi"
[(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" "")
(match_operand:DI 1 "general_operand" ""))]
""
"
{
/* Where possible, convert CONST_DOUBLE into a CONST_INT. */
if (GET_CODE (operands[1]) == CONST_DOUBLE
#if HOST_BITS_PER_WIDE_INT == 32
&& ((CONST_DOUBLE_HIGH (operands[1]) == 0
&& (CONST_DOUBLE_LOW (operands[1]) & 0x80000000) == 0)
|| (CONST_DOUBLE_HIGH (operands[1]) == (HOST_WIDE_INT) 0xffffffff
&& (CONST_DOUBLE_LOW (operands[1]) & 0x80000000) != 0))
#endif
)
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
/* Handle MEM cases first. */
if (GET_CODE (operands[0]) == MEM)
{
/* If it's a REG, we can always do it.
The const zero case is more complex, on v9
we can always perform it. */
if (register_operand (operands[1], DImode)
|| (TARGET_ARCH64
&& (operands[1] == const0_rtx)))
goto movdi_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (DImode, operands[1]);
}
}
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], DImode, 0);
if (GET_CODE (operands[1]) == LABEL_REF)
{
if (! TARGET_ARCH64)
abort ();
emit_insn (gen_movdi_pic_label_ref (operands[0], operands[1]));
DONE;
}
if (symbolic_operand (operands[1], DImode))
{
operands[1] = legitimize_pic_address (operands[1],
DImode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
goto movdi_is_ok;
}
}
/* If we are trying to toss an integer constant into the
FPU registers, force it into memory. */
if (GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) >= SPARC_FIRST_FP_REG
&& REGNO (operands[0]) <= SPARC_LAST_V9_FP_REG
&& CONSTANT_P (operands[1]))
operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]),
operands[1]));
/* This makes sure we will not get rematched due to splittage. */
if (! CONSTANT_P (operands[1]) || input_operand (operands[1], DImode))
;
else if (TARGET_ARCH64
&& CONSTANT_P (operands[1])
&& GET_CODE (operands[1]) != HIGH
&& GET_CODE (operands[1]) != LO_SUM)
{
sparc_emit_set_const64 (operands[0], operands[1]);
DONE;
}
movdi_is_ok:
;
}")
;; Be careful, fmovd does not exist when !arch64.
;; We match MEM moves directly when we have correct even
;; numbered registers, but fall into splits otherwise.
;; The constraint ordering here is really important to
;; avoid insane problems in reload, especially for patterns
;; of the form:
;;
;; (set (mem:DI (plus:SI (reg:SI 30 %fp)
;; (const_int -5016)))
;; (reg:DI 2 %g2))
;;
(define_insn "*movdi_insn_sp32"
[(set (match_operand:DI 0 "general_operand" "=T,U,o,r,r,r,?T,?f,?f,?o,?f")
(match_operand:DI 1 "input_operand" "U,T,r,o,i,r,f,T,o,f,f"))]
"! TARGET_ARCH64 &&
(register_operand (operands[0], DImode)
|| register_operand (operands[1], DImode))"
"@
std\\t%1, %0
ldd\\t%1, %0
#
#
#
#
std\\t%1, %0
ldd\\t%1, %0
#
#
#"
[(set_attr "type" "store,load,*,*,*,*,fpstore,fpload,*,*,*")
(set_attr "length" "1,1,2,2,2,2,1,1,2,2,2")])
;; The following are generated by sparc_emit_set_const64
(define_insn "*movdi_sp64_dbl"
[(set (match_operand:DI 0 "register_operand" "=r")
(match_operand:DI 1 "const64_operand" ""))]
"(TARGET_ARCH64
&& HOST_BITS_PER_WIDE_INT != 64)"
"mov\\t%1, %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
;; This is needed to show CSE exactly which bits are set
;; in a 64-bit register by sethi instructions.
(define_insn "*movdi_const64_special"
[(set (match_operand:DI 0 "register_operand" "=r")
(match_operand:DI 1 "const64_high_operand" ""))]
"TARGET_ARCH64"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*movdi_insn_sp64"
[(set (match_operand:DI 0 "general_operand" "=r,r,r,r,m,?e,?e,?m,b")
(match_operand:DI 1 "input_operand" "rI,K,J,m,rJ,e,m,e,J"))]
"TARGET_ARCH64 &&
(register_operand (operands[0], DImode)
|| reg_or_0_operand (operands[1], DImode))"
"@
mov\\t%1, %0
sethi\\t%%hi(%a1), %0
clr\\t%0
ldx\\t%1, %0
stx\\t%r1, %0
fmovd\\t%1, %0
ldd\\t%1, %0
std\\t%1, %0
fzero\\t%0"
[(set_attr "type" "move,move,move,load,store,fpmove,fpload,fpstore,fpmove")
(set_attr "length" "1")])
(define_expand "movdi_pic_label_ref"
[(set (match_dup 3) (high:DI
(unspec:DI [(match_operand:DI 1 "label_ref_operand" "")
(match_dup 2)] 5)))
(set (match_dup 4) (lo_sum:DI (match_dup 3)
(unspec:DI [(match_dup 1) (match_dup 2)] 5)))
(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_dup 5) (match_dup 4)))]
"TARGET_ARCH64 && flag_pic"
"
{
current_function_uses_pic_offset_table = 1;
operands[2] = gen_rtx_SYMBOL_REF (Pmode, \"_GLOBAL_OFFSET_TABLE_\");
operands[3] = gen_reg_rtx (DImode);
operands[4] = gen_reg_rtx (DImode);
operands[5] = pic_offset_table_rtx;
}")
(define_insn "*movdi_high_pic_label_ref"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI
(unspec:DI [(match_operand:DI 1 "label_ref_operand" "")
(match_operand:DI 2 "" "")] 5)))]
"TARGET_ARCH64 && flag_pic"
"sethi\\t%%hi(%a2-(%a1-.)), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*movdi_lo_sum_pic_label_ref"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(unspec:DI [(match_operand:DI 2 "label_ref_operand" "")
(match_operand:DI 3 "" "")] 5)))]
"TARGET_ARCH64 && flag_pic"
"or\\t%1, %%lo(%a3-(%a2-.)), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
;; Sparc-v9 code model support insns. See sparc_emit_set_symbolic_const64
;; in sparc.c to see what is going on here... PIC stuff comes first.
(define_insn "movdi_lo_sum_pic"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(unspec:DI [(match_operand:DI 2 "immediate_operand" "in")] 0)))]
"TARGET_ARCH64 && flag_pic"
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "movdi_high_pic"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand 1 "" "")] 0)))]
"TARGET_ARCH64 && flag_pic && check_pic (1)"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*sethi_di_medlow_embmedany_pic"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (match_operand:DI 1 "sp64_medium_pic_operand" "")))]
"(TARGET_CM_MEDLOW || TARGET_CM_EMBMEDANY) && check_pic (1)"
"sethi\\t%%lo(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*sethi_di_medlow"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (match_operand:DI 1 "symbolic_operand" "")))]
"TARGET_CM_MEDLOW && check_pic (1)"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*losum_di_medlow"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "symbolic_operand" "")))]
"TARGET_CM_MEDLOW"
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "seth44"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")] 6)))]
"TARGET_CM_MEDMID"
"sethi\\t%%h44(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "setm44"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(unspec:DI [(match_operand:DI 2 "symbolic_operand" "")] 7)))]
"TARGET_CM_MEDMID"
"or\\t%1, %%m44(%a2), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "setl44"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "symbolic_operand" "")))]
"TARGET_CM_MEDMID"
"or\\t%1, %%l44(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "sethh"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")] 9)))]
"TARGET_CM_MEDANY"
"sethi\\t%%hh(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "setlm"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")] 10)))]
"TARGET_CM_MEDANY"
"sethi\\t%%lm(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "sethm"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(unspec:DI [(match_operand:DI 2 "symbolic_operand" "")] 18)))]
"TARGET_CM_MEDANY"
"or\\t%1, %%hm(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "setlo"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "symbolic_operand" "")))]
"TARGET_CM_MEDANY"
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "embmedany_sethi"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand:DI 1 "data_segment_operand" "")] 11)))]
"TARGET_CM_EMBMEDANY && check_pic (1)"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "embmedany_losum"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "data_segment_operand" "")))]
"TARGET_CM_EMBMEDANY"
"add\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "embmedany_brsum"
[(set (match_operand:DI 0 "register_operand" "=r")
(unspec:DI [(match_operand:DI 1 "register_operand" "r")] 11))]
"TARGET_CM_EMBMEDANY"
"add\\t%1, %_, %0"
[(set_attr "length" "1")])
(define_insn "embmedany_textuhi"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand:DI 1 "text_segment_operand" "")] 13)))]
"TARGET_CM_EMBMEDANY && check_pic (1)"
"sethi\\t%%uhi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "embmedany_texthi"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (unspec:DI [(match_operand:DI 1 "text_segment_operand" "")] 14)))]
"TARGET_CM_EMBMEDANY && check_pic (1)"
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "embmedany_textulo"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(unspec:DI [(match_operand:DI 2 "text_segment_operand" "")] 15)))]
"TARGET_CM_EMBMEDANY"
"or\\t%1, %%ulo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "embmedany_textlo"
[(set (match_operand:DI 0 "register_operand" "=r")
(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "text_segment_operand" "")))]
"TARGET_CM_EMBMEDANY"
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
;; Now some patterns to help reload out a bit.
(define_expand "reload_indi"
[(parallel [(match_operand:DI 0 "register_operand" "=r")
(match_operand:DI 1 "immediate_operand" "")
(match_operand:TI 2 "register_operand" "=&r")])]
"(TARGET_CM_MEDANY
|| TARGET_CM_EMBMEDANY)
&& ! flag_pic"
"
{
sparc_emit_set_symbolic_const64 (operands[0], operands[1],
gen_rtx_REG (DImode, REGNO (operands[2])));
DONE;
}")
(define_expand "reload_outdi"
[(parallel [(match_operand:DI 0 "register_operand" "=r")
(match_operand:DI 1 "immediate_operand" "")
(match_operand:TI 2 "register_operand" "=&r")])]
"(TARGET_CM_MEDANY
|| TARGET_CM_EMBMEDANY)
&& ! flag_pic"
"
{
sparc_emit_set_symbolic_const64 (operands[0], operands[1],
gen_rtx_REG (DImode, REGNO (operands[2])));
DONE;
}")
;; Split up putting CONSTs and REGs into DI regs when !arch64
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "const_int_operand" ""))]
"! TARGET_ARCH64 && reload_completed"
[(clobber (const_int 0))]
"
{
emit_insn (gen_movsi (gen_highpart (SImode, operands[0]),
(INTVAL (operands[1]) < 0) ?
constm1_rtx :
const0_rtx));
emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
operands[1]));
DONE;
}")
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "const_double_operand" ""))]
"! TARGET_ARCH64 && reload_completed"
[(clobber (const_int 0))]
"
{
emit_insn (gen_movsi (gen_highpart (SImode, operands[0]),
GEN_INT (CONST_DOUBLE_HIGH (operands[1]))));
/* Slick... but this trick loses if this subreg constant part
can be done in one insn. */
if (CONST_DOUBLE_LOW (operands[1]) == CONST_DOUBLE_HIGH (operands[1])
&& !(SPARC_SETHI_P (CONST_DOUBLE_HIGH (operands[1]))
|| SPARC_SIMM13_P (CONST_DOUBLE_HIGH (operands[1]))))
{
emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
gen_highpart (SImode, operands[0])));
}
else
{
emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
GEN_INT (CONST_DOUBLE_LOW (operands[1]))));
}
DONE;
}")
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "register_operand" ""))]
"! TARGET_ARCH64 && reload_completed"
[(clobber (const_int 0))]
"
{
rtx set_dest = operands[0];
rtx set_src = operands[1];
rtx dest1, dest2;
rtx src1, src2;
if (GET_CODE (set_dest) == SUBREG)
set_dest = alter_subreg (set_dest);
if (GET_CODE (set_src) == SUBREG)
set_src = alter_subreg (set_src);
dest1 = gen_highpart (SImode, set_dest);
dest2 = gen_lowpart (SImode, set_dest);
src1 = gen_highpart (SImode, set_src);
src2 = gen_lowpart (SImode, set_src);
/* Now emit using the real source and destination we found, swapping
the order if we detect overlap. */
if (reg_overlap_mentioned_p (dest1, src2))
{
emit_insn (gen_movsi (dest2, src2));
emit_insn (gen_movsi (dest1, src1));
}
else
{
emit_insn (gen_movsi (dest1, src1));
emit_insn (gen_movsi (dest2, src2));
}
DONE;
}")
;; Now handle the cases of memory moves from/to non-even
;; DI mode register pairs.
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operand:DI 1 "memory_operand" ""))]
"(! TARGET_ARCH64
&& reload_completed
&& sparc_splitdi_legitimate (operands[0], operands[1]))"
[(clobber (const_int 0))]
"
{
rtx word0 = change_address (operands[1], SImode, NULL_RTX);
rtx word1 = change_address (operands[1], SImode,
plus_constant_for_output (XEXP (word0, 0), 4));
rtx high_part = gen_highpart (SImode, operands[0]);
rtx low_part = gen_lowpart (SImode, operands[0]);
if (reg_overlap_mentioned_p (high_part, word1))
{
emit_insn (gen_movsi (low_part, word1));
emit_insn (gen_movsi (high_part, word0));
}
else
{
emit_insn (gen_movsi (high_part, word0));
emit_insn (gen_movsi (low_part, word1));
}
DONE;
}")
(define_split
[(set (match_operand:DI 0 "memory_operand" "")
(match_operand:DI 1 "register_operand" ""))]
"(! TARGET_ARCH64
&& reload_completed
&& sparc_splitdi_legitimate (operands[1], operands[0]))"
[(clobber (const_int 0))]
"
{
rtx word0 = change_address (operands[0], SImode, NULL_RTX);
rtx word1 = change_address (operands[0], SImode,
plus_constant_for_output (XEXP (word0, 0), 4));
rtx high_part = gen_highpart (SImode, operands[1]);
rtx low_part = gen_lowpart (SImode, operands[1]);
emit_insn (gen_movsi (word0, high_part));
emit_insn (gen_movsi (word1, low_part));
DONE;
}")
;; Floating point move insns
(define_insn "*clear_sf"
[(set (match_operand:SF 0 "general_operand" "=f")
(match_operand:SF 1 "" ""))]
"TARGET_VIS
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& GET_CODE (operands[0]) == REG
&& fp_zero_operand (operands[1])"
"fzeros\\t%0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_insn "*movsf_const_intreg"
[(set (match_operand:SF 0 "general_operand" "=f,r")
(match_operand:SF 1 "" "m,F"))]
"TARGET_FPU
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& GET_CODE (operands[0]) == REG"
"*
{
REAL_VALUE_TYPE r;
long i;
if (which_alternative == 0)
return \"ld\\t%1, %0\";
REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
REAL_VALUE_TO_TARGET_SINGLE (r, i);
if (SPARC_SIMM13_P (i) || SPARC_SETHI_P (i))
{
operands[1] = GEN_INT (i);
if (SPARC_SIMM13_P (INTVAL (operands[1])))
return \"mov\\t%1, %0\";
else if (SPARC_SETHI_P (INTVAL (operands[1])))
return \"sethi\\t%%hi(%a1), %0\";
else
abort ();
}
else
return \"#\";
}"
[(set_attr "type" "move")
- (set_attr "length" "1")])
+ (set_attr "length" "1,2")])
;; There isn't much I can do about this, if I change the
;; mode then flow info gets really confused because the
;; destination no longer looks the same. Ho hum...
(define_insn "*movsf_const_high"
[(set (match_operand:SF 0 "register_operand" "=r")
(unspec:SF [(match_operand 1 "const_int_operand" "")] 12))]
""
"sethi\\t%%hi(%a1), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*movsf_const_lo"
[(set (match_operand:SF 0 "register_operand" "=r")
(unspec:SF [(match_operand 1 "register_operand" "r")
(match_operand 2 "const_int_operand" "")] 17))]
""
"or\\t%1, %%lo(%a2), %0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_split
[(set (match_operand:SF 0 "register_operand" "")
(match_operand:SF 1 "const_double_operand" ""))]
"TARGET_FPU
&& (GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)"
[(set (match_dup 0) (unspec:SF [(match_dup 1)] 12))
(set (match_dup 0) (unspec:SF [(match_dup 0) (match_dup 1)] 17))]
"
{
REAL_VALUE_TYPE r;
long i;
REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
REAL_VALUE_TO_TARGET_SINGLE (r, i);
operands[1] = GEN_INT (i);
}")
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand" "")
(match_operand:SF 1 "general_operand" ""))]
""
"
{
/* Force SFmode constants into memory. */
if (GET_CODE (operands[0]) == REG
&& CONSTANT_P (operands[1]))
{
if (TARGET_VIS
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& fp_zero_operand (operands[1]))
goto movsf_is_ok;
/* emit_group_store will send such bogosity to us when it is
not storing directly into memory. So fix this up to avoid
crashes in output_constant_pool. */
if (operands [1] == const0_rtx)
operands[1] = CONST0_RTX (SFmode);
operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]),
operands[1]));
}
/* Handle sets of MEM first. */
if (GET_CODE (operands[0]) == MEM)
{
if (register_operand (operands[1], SFmode))
goto movsf_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (SFmode, operands[1]);
}
}
/* Fixup PIC cases. */
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], SFmode, 0);
if (symbolic_operand (operands[1], SFmode))
{
operands[1] = legitimize_pic_address (operands[1],
SFmode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
}
}
movsf_is_ok:
;
}")
(define_insn "*movsf_insn"
[(set (match_operand:SF 0 "general_operand" "=f,f,m,r,r,m")
(match_operand:SF 1 "input_operand" "f,m,f,r,m,r"))]
"TARGET_FPU
&& (register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode))"
"@
fmovs\\t%1, %0
ld\\t%1, %0
st\\t%1, %0
mov\\t%1, %0
ld\\t%1, %0
st\\t%1, %0"
[(set_attr "type" "fpmove,fpload,fpstore,move,load,store")
(set_attr "length" "1")])
;; Exactly the same as above, except that all `f' cases are deleted.
;; This is necessary to prevent reload from ever trying to use a `f' reg
;; when -mno-fpu.
(define_insn "*movsf_no_f_insn"
[(set (match_operand:SF 0 "general_operand" "=r,r,m")
(match_operand:SF 1 "input_operand" "r,m,r"))]
"! TARGET_FPU
&& (register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode))"
"@
mov\\t%1, %0
ld\\t%1, %0
st\\t%1, %0"
[(set_attr "type" "move,load,store")
(set_attr "length" "1")])
(define_insn "*clear_df"
[(set (match_operand:DF 0 "general_operand" "=e")
(match_operand:DF 1 "" ""))]
"TARGET_VIS
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& GET_CODE (operands[0]) == REG
&& fp_zero_operand (operands[1])"
"fzero\\t%0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_insn "*movdf_const_intreg_sp32"
[(set (match_operand:DF 0 "general_operand" "=e,e,r")
(match_operand:DF 1 "" "T,o,F"))]
"TARGET_FPU && ! TARGET_ARCH64
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& GET_CODE (operands[0]) == REG"
"*
{
if (which_alternative == 0)
return \"ldd\\t%1, %0\";
else
return \"#\";
}"
[(set_attr "type" "move")
(set_attr "length" "1,2,2")])
;; Now that we redo life analysis with a clean slate after
;; instruction splitting for sched2 this can work.
(define_insn "*movdf_const_intreg_sp64"
[(set (match_operand:DF 0 "general_operand" "=e,e,r")
(match_operand:DF 1 "" "m,o,F"))]
"TARGET_FPU
&& TARGET_ARCH64
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& GET_CODE (operands[0]) == REG"
"*
{
if (which_alternative == 0)
return \"ldd\\t%1, %0\";
else
return \"#\";
}"
[(set_attr "type" "move")
- (set_attr "length" "1")])
+ (set_attr "length" "1,2,2")])
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "const_double_operand" ""))]
"TARGET_FPU
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& (GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
&& reload_completed"
[(clobber (const_int 0))]
"
{
REAL_VALUE_TYPE r;
long l[2];
REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
REAL_VALUE_TO_TARGET_DOUBLE (r, l);
if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
operands[0] = gen_rtx_raw_REG (DImode, REGNO (operands[0]));
if (TARGET_ARCH64)
{
#if HOST_BITS_PER_WIDE_INT == 64
HOST_WIDE_INT val;
val = ((HOST_WIDE_INT)l[1] |
((HOST_WIDE_INT)l[0] << 32));
emit_insn (gen_movdi (operands[0], GEN_INT (val)));
#else
emit_insn (gen_movdi (operands[0],
gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx,
l[1], l[0])));
#endif
}
else
{
emit_insn (gen_movsi (gen_highpart (SImode, operands[0]),
GEN_INT (l[0])));
/* Slick... but this trick loses if this subreg constant part
can be done in one insn. */
if (l[1] == l[0]
&& !(SPARC_SETHI_P (l[0])
|| SPARC_SIMM13_P (l[0])))
{
emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
gen_highpart (SImode, operands[0])));
}
else
{
emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
GEN_INT (l[1])));
}
}
DONE;
}")
(define_expand "movdf"
[(set (match_operand:DF 0 "general_operand" "")
(match_operand:DF 1 "general_operand" ""))]
""
"
{
/* Force DFmode constants into memory. */
if (GET_CODE (operands[0]) == REG
&& CONSTANT_P (operands[1]))
{
if (TARGET_VIS
&& GET_CODE (operands[1]) == CONST_DOUBLE
&& fp_zero_operand (operands[1]))
goto movdf_is_ok;
/* emit_group_store will send such bogosity to us when it is
not storing directly into memory. So fix this up to avoid
crashes in output_constant_pool. */
if (operands [1] == const0_rtx)
operands[1] = CONST0_RTX (DFmode);
operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]),
operands[1]));
}
/* Handle MEM cases first. */
if (GET_CODE (operands[0]) == MEM)
{
if (register_operand (operands[1], DFmode))
goto movdf_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (DFmode, operands[1]);
}
}
/* Fixup PIC cases. */
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], DFmode, 0);
if (symbolic_operand (operands[1], DFmode))
{
operands[1] = legitimize_pic_address (operands[1],
DFmode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
}
}
movdf_is_ok:
;
}")
;; Be careful, fmovd does not exist when !v9.
(define_insn "*movdf_insn_sp32"
[(set (match_operand:DF 0 "general_operand" "=e,T,U,T,e,r,r,o,e,o")
(match_operand:DF 1 "input_operand" "T,e,T,U,e,r,o,r,o,e"))]
"TARGET_FPU
&& ! TARGET_V9
&& (register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode))"
"@
ldd\\t%1, %0
std\\t%1, %0
ldd\\t%1, %0
std\\t%1, %0
#
#
#
#
#
#"
[(set_attr "type" "fpload,fpstore,load,store,*,*,*,*,*,*")
(set_attr "length" "1,1,1,1,2,2,2,2,2,2")])
(define_insn "*movdf_no_e_insn_sp32"
[(set (match_operand:DF 0 "general_operand" "=U,T,r,r,o")
(match_operand:DF 1 "input_operand" "T,U,r,o,r"))]
"! TARGET_FPU
&& ! TARGET_ARCH64
&& (register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode))"
"@
ldd\\t%1, %0
std\\t%1, %0
#
#
#"
[(set_attr "type" "load,store,*,*,*")
(set_attr "length" "1,1,2,2,2")])
;; We have available v9 double floats but not 64-bit
;; integer registers.
(define_insn "*movdf_insn_v9only"
[(set (match_operand:DF 0 "general_operand" "=e,e,m,U,T,r,r,o")
(match_operand:DF 1 "input_operand" "e,m,e,T,U,r,o,r"))]
"TARGET_FPU
&& TARGET_V9
&& ! TARGET_ARCH64
&& (register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode))"
"@
fmovd\\t%1, %0
ldd\\t%1, %0
std\\t%1, %0
ldd\\t%1, %0
std\\t%1, %0
#
#
#"
[(set_attr "type" "fpmove,load,store,load,store,*,*,*")
(set_attr "length" "1,1,1,1,1,2,2,2")])
;; We have available both v9 double floats and 64-bit
;; integer registers.
(define_insn "*movdf_insn_sp64"
[(set (match_operand:DF 0 "general_operand" "=e,e,m,r,r,m")
(match_operand:DF 1 "input_operand" "e,m,e,r,m,r"))]
"TARGET_FPU
&& TARGET_V9
&& TARGET_ARCH64
&& (register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode))"
"@
fmovd\\t%1, %0
ldd\\t%1, %0
std\\t%1, %0
mov\\t%1, %0
ldx\\t%1, %0
stx\\t%1, %0"
[(set_attr "type" "fpmove,load,store,move,load,store")
(set_attr "length" "1")])
(define_insn "*movdf_no_e_insn_sp64"
[(set (match_operand:DF 0 "general_operand" "=r,r,m")
(match_operand:DF 1 "input_operand" "r,m,r"))]
"! TARGET_FPU
&& TARGET_ARCH64
&& (register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode))"
"@
mov\\t%1, %0
ldx\\t%1, %0
stx\\t%1, %0"
[(set_attr "type" "move,load,store")
(set_attr "length" "1")])
;; Ok, now the splits to handle all the multi insn and
;; mis-aligned memory address cases.
;; In these splits please take note that we must be
;; careful when V9 but not ARCH64 because the integer
;; register DFmode cases must be handled.
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "register_operand" ""))]
"(! TARGET_V9
|| (! TARGET_ARCH64
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))))
&& reload_completed"
[(clobber (const_int 0))]
"
{
rtx set_dest = operands[0];
rtx set_src = operands[1];
rtx dest1, dest2;
rtx src1, src2;
if (GET_CODE (set_dest) == SUBREG)
set_dest = alter_subreg (set_dest);
if (GET_CODE (set_src) == SUBREG)
set_src = alter_subreg (set_src);
dest1 = gen_highpart (SFmode, set_dest);
dest2 = gen_lowpart (SFmode, set_dest);
src1 = gen_highpart (SFmode, set_src);
src2 = gen_lowpart (SFmode, set_src);
/* Now emit using the real source and destination we found, swapping
the order if we detect overlap. */
if (reg_overlap_mentioned_p (dest1, src2))
{
emit_insn (gen_movsf (dest2, src2));
emit_insn (gen_movsf (dest1, src1));
}
else
{
emit_insn (gen_movsf (dest1, src1));
emit_insn (gen_movsf (dest2, src2));
}
DONE;
}")
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(match_operand:DF 1 "memory_operand" ""))]
"((! TARGET_V9
|| (! TARGET_ARCH64
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))))
&& (reload_completed
&& (((REGNO (operands[0])) % 2) != 0
|| ! mem_min_alignment (operands[1], 8))
&& offsettable_memref_p (operands[1])))"
[(clobber (const_int 0))]
"
{
rtx word0 = change_address (operands[1], SFmode, NULL_RTX);
rtx word1 = change_address (operands[1], SFmode,
plus_constant_for_output (XEXP (word0, 0), 4));
if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (reg_overlap_mentioned_p (gen_highpart (SFmode, operands[0]), word1))
{
emit_insn (gen_movsf (gen_lowpart (SFmode, operands[0]),
word1));
emit_insn (gen_movsf (gen_highpart (SFmode, operands[0]),
word0));
}
else
{
emit_insn (gen_movsf (gen_highpart (SFmode, operands[0]),
word0));
emit_insn (gen_movsf (gen_lowpart (SFmode, operands[0]),
word1));
}
DONE;
}")
(define_split
[(set (match_operand:DF 0 "memory_operand" "")
(match_operand:DF 1 "register_operand" ""))]
"((! TARGET_V9
|| (! TARGET_ARCH64
&& ((GET_CODE (operands[1]) == REG
&& REGNO (operands[1]) < 32)
|| (GET_CODE (operands[1]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[1])) == REG
&& REGNO (SUBREG_REG (operands[1])) < 32))))
&& (reload_completed
&& (((REGNO (operands[1])) % 2) != 0
|| ! mem_min_alignment (operands[0], 8))
&& offsettable_memref_p (operands[0])))"
[(clobber (const_int 0))]
"
{
rtx word0 = change_address (operands[0], SFmode, NULL_RTX);
rtx word1 = change_address (operands[0], SFmode,
plus_constant_for_output (XEXP (word0, 0), 4));
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
emit_insn (gen_movsf (word0,
gen_highpart (SFmode, operands[1])));
emit_insn (gen_movsf (word1,
gen_lowpart (SFmode, operands[1])));
DONE;
}")
(define_expand "movtf"
[(set (match_operand:TF 0 "general_operand" "")
(match_operand:TF 1 "general_operand" ""))]
""
"
{
/* Force TFmode constants into memory. */
if (GET_CODE (operands[0]) == REG
&& CONSTANT_P (operands[1]))
{
/* emit_group_store will send such bogosity to us when it is
not storing directly into memory. So fix this up to avoid
crashes in output_constant_pool. */
if (operands [1] == const0_rtx)
operands[1] = CONST0_RTX (TFmode);
operands[1] = validize_mem (force_const_mem (GET_MODE (operands[0]),
operands[1]));
}
/* Handle MEM cases first, note that only v9 guarentees
full 16-byte alignment for quads. */
if (GET_CODE (operands[0]) == MEM)
{
if (register_operand (operands[1], TFmode))
goto movtf_is_ok;
if (! reload_in_progress)
{
operands[0] = validize_mem (operands[0]);
operands[1] = force_reg (TFmode, operands[1]);
}
}
/* Fixup PIC cases. */
if (flag_pic)
{
if (CONSTANT_P (operands[1])
&& pic_address_needs_scratch (operands[1]))
operands[1] = legitimize_pic_address (operands[1], TFmode, 0);
if (symbolic_operand (operands[1], TFmode))
{
operands[1] = legitimize_pic_address (operands[1],
TFmode,
(reload_in_progress ?
operands[0] :
NULL_RTX));
}
}
movtf_is_ok:
;
}")
;; Be careful, fmovq and {st,ld}{x,q} do not exist when !arch64 so
;; we must split them all. :-(
(define_insn "*movtf_insn_sp32"
[(set (match_operand:TF 0 "general_operand" "=e,o,U,o,e,r,r,o")
(match_operand:TF 1 "input_operand" "o,e,o,U,e,r,o,r"))]
"TARGET_FPU
&& ! TARGET_ARCH64
&& (register_operand (operands[0], TFmode)
|| register_operand (operands[1], TFmode))"
"#"
[(set_attr "length" "4")])
;; Exactly the same as above, except that all `e' cases are deleted.
;; This is necessary to prevent reload from ever trying to use a `e' reg
;; when -mno-fpu.
(define_insn "*movtf_no_e_insn_sp32"
[(set (match_operand:TF 0 "general_operand" "=U,o,r,r,o")
(match_operand:TF 1 "input_operand" "o,U,r,o,r"))]
"! TARGET_FPU
&& ! TARGET_ARCH64
&& (register_operand (operands[0], TFmode)
|| register_operand (operands[1], TFmode))"
"#"
[(set_attr "length" "4")])
;; Now handle the float reg cases directly when arch64,
;; hard_quad, and proper reg number alignment are all true.
(define_insn "*movtf_insn_hq_sp64"
[(set (match_operand:TF 0 "general_operand" "=e,e,m,r,r,o")
(match_operand:TF 1 "input_operand" "e,m,e,r,o,r"))]
"TARGET_FPU
&& TARGET_ARCH64
&& TARGET_V9
&& TARGET_HARD_QUAD
&& (register_operand (operands[0], TFmode)
|| register_operand (operands[1], TFmode))"
"@
fmovq\\t%1, %0
ldq\\t%1, %0
stq\\t%1, %0
#
#
#"
[(set_attr "type" "fpmove,fpload,fpstore,*,*,*")
(set_attr "length" "1,1,1,2,2,2")])
;; Now we allow the integer register cases even when
;; only arch64 is true.
(define_insn "*movtf_insn_sp64"
[(set (match_operand:TF 0 "general_operand" "=e,o,r,o,e,r")
(match_operand:TF 1 "input_operand" "o,e,o,r,e,r"))]
"TARGET_FPU
&& TARGET_ARCH64
&& ! TARGET_HARD_QUAD
&& (register_operand (operands[0], TFmode)
|| register_operand (operands[1], TFmode))"
"#"
[(set_attr "length" "2")])
(define_insn "*movtf_no_e_insn_sp64"
[(set (match_operand:TF 0 "general_operand" "=r,o,r")
(match_operand:TF 1 "input_operand" "o,r,r"))]
"! TARGET_FPU
&& TARGET_ARCH64
&& (register_operand (operands[0], TFmode)
|| register_operand (operands[1], TFmode))"
"#"
[(set_attr "length" "2")])
;; Now all the splits to handle multi-insn TF mode moves.
(define_split
[(set (match_operand:TF 0 "register_operand" "")
(match_operand:TF 1 "register_operand" ""))]
"reload_completed
&& (! TARGET_ARCH64
|| (TARGET_FPU
&& ! TARGET_HARD_QUAD))"
[(clobber (const_int 0))]
"
{
rtx set_dest = operands[0];
rtx set_src = operands[1];
rtx dest1, dest2;
rtx src1, src2;
if (GET_CODE (set_dest) == SUBREG)
set_dest = alter_subreg (set_dest);
if (GET_CODE (set_src) == SUBREG)
set_src = alter_subreg (set_src);
/* Ugly, but gen_highpart will crap out here for 32-bit targets. */
dest1 = gen_rtx_SUBREG (DFmode, set_dest, WORDS_BIG_ENDIAN == 0);
dest2 = gen_rtx_SUBREG (DFmode, set_dest, WORDS_BIG_ENDIAN != 0);
src1 = gen_rtx_SUBREG (DFmode, set_src, WORDS_BIG_ENDIAN == 0);
src2 = gen_rtx_SUBREG (DFmode, set_src, WORDS_BIG_ENDIAN != 0);
/* Now emit using the real source and destination we found, swapping
the order if we detect overlap. */
if (reg_overlap_mentioned_p (dest1, src2))
{
emit_insn (gen_movdf (dest2, src2));
emit_insn (gen_movdf (dest1, src1));
}
else
{
emit_insn (gen_movdf (dest1, src1));
emit_insn (gen_movdf (dest2, src2));
}
DONE;
}")
(define_split
[(set (match_operand:TF 0 "register_operand" "")
(match_operand:TF 1 "memory_operand" ""))]
"(reload_completed
&& offsettable_memref_p (operands[1]))"
[(clobber (const_int 0))]
"
{
rtx word0 = change_address (operands[1], DFmode, NULL_RTX);
rtx word1 = change_address (operands[1], DFmode,
plus_constant_for_output (XEXP (word0, 0), 8));
rtx dest1, dest2;
/* Ugly, but gen_highpart will crap out here for 32-bit targets. */
dest1 = gen_rtx_SUBREG (DFmode, operands[0], WORDS_BIG_ENDIAN == 0);
dest2 = gen_rtx_SUBREG (DFmode, operands[0], WORDS_BIG_ENDIAN != 0);
/* Now output, ordering such that we don't clobber any registers
mentioned in the address. */
if (reg_overlap_mentioned_p (dest1, word1))
{
emit_insn (gen_movdf (dest2, word1));
emit_insn (gen_movdf (dest1, word0));
}
else
{
emit_insn (gen_movdf (dest1, word0));
emit_insn (gen_movdf (dest2, word1));
}
DONE;
}")
(define_split
[(set (match_operand:TF 0 "memory_operand" "")
(match_operand:TF 1 "register_operand" ""))]
"(reload_completed
&& offsettable_memref_p (operands[0]))"
[(clobber (const_int 0))]
"
{
rtx word0 = change_address (operands[0], DFmode, NULL_RTX);
rtx word1 = change_address (operands[0], DFmode,
plus_constant_for_output (XEXP (word0, 0), 8));
rtx src1, src2;
/* Ugly, but gen_highpart will crap out here for 32-bit targets. */
src1 = gen_rtx_SUBREG (DFmode, operands[1], WORDS_BIG_ENDIAN == 0);
src2 = gen_rtx_SUBREG (DFmode, operands[1], WORDS_BIG_ENDIAN != 0);
emit_insn (gen_movdf (word0, src1));
emit_insn (gen_movdf (word1, src2));
DONE;
}")
;; Sparc V9 conditional move instructions.
;; We can handle larger constants here for some flavors, but for now we keep
;; it simple and only allow those constants supported by all flavours.
;; Note that emit_conditional_move canonicalizes operands 2,3 so that operand
;; 3 contains the constant if one is present, but we handle either for
;; generality (sparc.c puts a constant in operand 2).
(define_expand "movqicc"
[(set (match_operand:QI 0 "register_operand" "")
(if_then_else:QI (match_operand 1 "comparison_operator" "")
(match_operand:QI 2 "arith10_operand" "")
(match_operand:QI 3 "arith10_operand" "")))]
"TARGET_V9"
"
{
enum rtx_code code = GET_CODE (operands[1]);
if (GET_MODE (sparc_compare_op0) == DImode
&& ! TARGET_ARCH64)
FAIL;
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode
&& v9_regcmp_p (code))
{
operands[1] = gen_rtx_fmt_ee (code, DImode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx);
}
}")
(define_expand "movhicc"
[(set (match_operand:HI 0 "register_operand" "")
(if_then_else:HI (match_operand 1 "comparison_operator" "")
(match_operand:HI 2 "arith10_operand" "")
(match_operand:HI 3 "arith10_operand" "")))]
"TARGET_V9"
"
{
enum rtx_code code = GET_CODE (operands[1]);
if (GET_MODE (sparc_compare_op0) == DImode
&& ! TARGET_ARCH64)
FAIL;
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode
&& v9_regcmp_p (code))
{
operands[1] = gen_rtx_fmt_ee (code, DImode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx);
}
}")
(define_expand "movsicc"
[(set (match_operand:SI 0 "register_operand" "")
(if_then_else:SI (match_operand 1 "comparison_operator" "")
(match_operand:SI 2 "arith10_operand" "")
(match_operand:SI 3 "arith10_operand" "")))]
"TARGET_V9"
"
{
enum rtx_code code = GET_CODE (operands[1]);
enum machine_mode op0_mode = GET_MODE (sparc_compare_op0);
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& (TARGET_ARCH64 && op0_mode == DImode && v9_regcmp_p (code)))
{
operands[1] = gen_rtx_fmt_ee (code, op0_mode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg),
cc_reg, const0_rtx);
}
}")
(define_expand "movdicc"
[(set (match_operand:DI 0 "register_operand" "")
(if_then_else:DI (match_operand 1 "comparison_operator" "")
(match_operand:DI 2 "arith10_double_operand" "")
(match_operand:DI 3 "arith10_double_operand" "")))]
"TARGET_ARCH64"
"
{
enum rtx_code code = GET_CODE (operands[1]);
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode
&& v9_regcmp_p (code))
{
operands[1] = gen_rtx_fmt_ee (code, DImode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg),
cc_reg, const0_rtx);
}
}")
(define_expand "movsfcc"
[(set (match_operand:SF 0 "register_operand" "")
(if_then_else:SF (match_operand 1 "comparison_operator" "")
(match_operand:SF 2 "register_operand" "")
(match_operand:SF 3 "register_operand" "")))]
"TARGET_V9 && TARGET_FPU"
"
{
enum rtx_code code = GET_CODE (operands[1]);
if (GET_MODE (sparc_compare_op0) == DImode
&& ! TARGET_ARCH64)
FAIL;
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode
&& v9_regcmp_p (code))
{
operands[1] = gen_rtx_fmt_ee (code, DImode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx);
}
}")
(define_expand "movdfcc"
[(set (match_operand:DF 0 "register_operand" "")
(if_then_else:DF (match_operand 1 "comparison_operator" "")
(match_operand:DF 2 "register_operand" "")
(match_operand:DF 3 "register_operand" "")))]
"TARGET_V9 && TARGET_FPU"
"
{
enum rtx_code code = GET_CODE (operands[1]);
if (GET_MODE (sparc_compare_op0) == DImode
&& ! TARGET_ARCH64)
FAIL;
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode
&& v9_regcmp_p (code))
{
operands[1] = gen_rtx_fmt_ee (code, DImode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx);
}
}")
(define_expand "movtfcc"
[(set (match_operand:TF 0 "register_operand" "")
(if_then_else:TF (match_operand 1 "comparison_operator" "")
(match_operand:TF 2 "register_operand" "")
(match_operand:TF 3 "register_operand" "")))]
"TARGET_V9 && TARGET_FPU"
"
{
enum rtx_code code = GET_CODE (operands[1]);
if (GET_MODE (sparc_compare_op0) == DImode
&& ! TARGET_ARCH64)
FAIL;
if (sparc_compare_op1 == const0_rtx
&& GET_CODE (sparc_compare_op0) == REG
&& GET_MODE (sparc_compare_op0) == DImode
&& v9_regcmp_p (code))
{
operands[1] = gen_rtx_fmt_ee (code, DImode,
sparc_compare_op0, sparc_compare_op1);
}
else
{
rtx cc_reg = gen_compare_reg (code,
sparc_compare_op0, sparc_compare_op1);
operands[1] = gen_rtx_fmt_ee (code, GET_MODE (cc_reg), cc_reg, const0_rtx);
}
}")
;; Conditional move define_insns.
(define_insn "*movqi_cc_sp64"
[(set (match_operand:QI 0 "register_operand" "=r,r")
(if_then_else:QI (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:QI 3 "arith11_operand" "rL,0")
(match_operand:QI 4 "arith11_operand" "0,rL")))]
"TARGET_V9"
"@
mov%C1\\t%x2, %3, %0
mov%c1\\t%x2, %4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movhi_cc_sp64"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(if_then_else:HI (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:HI 3 "arith11_operand" "rL,0")
(match_operand:HI 4 "arith11_operand" "0,rL")))]
"TARGET_V9"
"@
mov%C1\\t%x2, %3, %0
mov%c1\\t%x2, %4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movsi_cc_sp64"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(if_then_else:SI (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:SI 3 "arith11_operand" "rL,0")
(match_operand:SI 4 "arith11_operand" "0,rL")))]
"TARGET_V9"
"@
mov%C1\\t%x2, %3, %0
mov%c1\\t%x2, %4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
;; ??? The constraints of operands 3,4 need work.
(define_insn "*movdi_cc_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(if_then_else:DI (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:DI 3 "arith11_double_operand" "rLH,0")
(match_operand:DI 4 "arith11_double_operand" "0,rLH")))]
"TARGET_ARCH64"
"@
mov%C1\\t%x2, %3, %0
mov%c1\\t%x2, %4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movdi_cc_sp64_trunc"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(if_then_else:SI (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:SI 3 "arith11_double_operand" "rLH,0")
(match_operand:SI 4 "arith11_double_operand" "0,rLH")))]
"TARGET_ARCH64"
"@
mov%C1\\t%x2, %3, %0
mov%c1\\t%x2, %4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movsf_cc_sp64"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(if_then_else:SF (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:SF 3 "register_operand" "f,0")
(match_operand:SF 4 "register_operand" "0,f")))]
"TARGET_V9 && TARGET_FPU"
"@
fmovs%C1\\t%x2, %3, %0
fmovs%c1\\t%x2, %4, %0"
[(set_attr "type" "fpcmove")
(set_attr "length" "1")])
(define_insn "*movdf_cc_sp64"
[(set (match_operand:DF 0 "register_operand" "=e,e")
(if_then_else:DF (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:DF 3 "register_operand" "e,0")
(match_operand:DF 4 "register_operand" "0,e")))]
"TARGET_V9 && TARGET_FPU"
"@
fmovd%C1\\t%x2, %3, %0
fmovd%c1\\t%x2, %4, %0"
[(set_attr "type" "fpcmove")
(set_attr "length" "1")])
(define_insn "*movtf_cc_sp64"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(if_then_else:TF (match_operator 1 "comparison_operator"
[(match_operand 2 "icc_or_fcc_reg_operand" "X,X")
(const_int 0)])
(match_operand:TF 3 "register_operand" "e,0")
(match_operand:TF 4 "register_operand" "0,e")))]
"TARGET_V9 && TARGET_FPU && TARGET_HARD_QUAD"
"@
fmovq%C1\\t%x2, %3, %0
fmovq%c1\\t%x2, %4, %0"
[(set_attr "type" "fpcmove")
(set_attr "length" "1")])
(define_insn "*movqi_cc_reg_sp64"
[(set (match_operand:QI 0 "register_operand" "=r,r")
(if_then_else:QI (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:QI 3 "arith10_operand" "rM,0")
(match_operand:QI 4 "arith10_operand" "0,rM")))]
"TARGET_ARCH64"
"@
movr%D1\\t%2, %r3, %0
movr%d1\\t%2, %r4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movhi_cc_reg_sp64"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(if_then_else:HI (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:HI 3 "arith10_operand" "rM,0")
(match_operand:HI 4 "arith10_operand" "0,rM")))]
"TARGET_ARCH64"
"@
movr%D1\\t%2, %r3, %0
movr%d1\\t%2, %r4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movsi_cc_reg_sp64"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(if_then_else:SI (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:SI 3 "arith10_operand" "rM,0")
(match_operand:SI 4 "arith10_operand" "0,rM")))]
"TARGET_ARCH64"
"@
movr%D1\\t%2, %r3, %0
movr%d1\\t%2, %r4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
;; ??? The constraints of operands 3,4 need work.
(define_insn "*movdi_cc_reg_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(if_then_else:DI (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:DI 3 "arith10_double_operand" "rMH,0")
(match_operand:DI 4 "arith10_double_operand" "0,rMH")))]
"TARGET_ARCH64"
"@
movr%D1\\t%2, %r3, %0
movr%d1\\t%2, %r4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movdi_cc_reg_sp64_trunc"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(if_then_else:SI (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:SI 3 "arith10_double_operand" "rMH,0")
(match_operand:SI 4 "arith10_double_operand" "0,rMH")))]
"TARGET_ARCH64"
"@
movr%D1\\t%2, %r3, %0
movr%d1\\t%2, %r4, %0"
[(set_attr "type" "cmove")
(set_attr "length" "1")])
(define_insn "*movsf_cc_reg_sp64"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(if_then_else:SF (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:SF 3 "register_operand" "f,0")
(match_operand:SF 4 "register_operand" "0,f")))]
"TARGET_ARCH64 && TARGET_FPU"
"@
fmovrs%D1\\t%2, %3, %0
fmovrs%d1\\t%2, %4, %0"
[(set_attr "type" "fpcmove")
(set_attr "length" "1")])
(define_insn "*movdf_cc_reg_sp64"
[(set (match_operand:DF 0 "register_operand" "=e,e")
(if_then_else:DF (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:DF 3 "register_operand" "e,0")
(match_operand:DF 4 "register_operand" "0,e")))]
"TARGET_ARCH64 && TARGET_FPU"
"@
fmovrd%D1\\t%2, %3, %0
fmovrd%d1\\t%2, %4, %0"
[(set_attr "type" "fpcmove")
(set_attr "length" "1")])
(define_insn "*movtf_cc_reg_sp64"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(if_then_else:TF (match_operator 1 "v9_regcmp_op"
[(match_operand:DI 2 "register_operand" "r,r")
(const_int 0)])
(match_operand:TF 3 "register_operand" "e,0")
(match_operand:TF 4 "register_operand" "0,e")))]
"TARGET_ARCH64 && TARGET_FPU"
"@
fmovrq%D1\\t%2, %3, %0
fmovrq%d1\\t%2, %4, %0"
[(set_attr "type" "fpcmove")
(set_attr "length" "1")])
;;- zero extension instructions
;; These patterns originally accepted general_operands, however, slightly
;; better code is generated by only accepting register_operands, and then
;; letting combine generate the ldu[hb] insns.
(define_expand "zero_extendhisi2"
[(set (match_operand:SI 0 "register_operand" "")
(zero_extend:SI (match_operand:HI 1 "register_operand" "")))]
""
"
{
rtx temp = gen_reg_rtx (SImode);
rtx shift_16 = GEN_INT (16);
int op1_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1,
op1_subword),
shift_16));
emit_insn (gen_lshrsi3 (operand0, temp, shift_16));
DONE;
}")
(define_insn "*zero_extendhisi2_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
""
"lduh\\t%1, %0"
[(set_attr "type" "load")
(set_attr "length" "1")])
(define_expand "zero_extendqihi2"
[(set (match_operand:HI 0 "register_operand" "")
(zero_extend:HI (match_operand:QI 1 "register_operand" "")))]
""
"")
(define_insn "*zero_extendqihi2_insn"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(zero_extend:HI (match_operand:QI 1 "input_operand" "r,m")))]
"GET_CODE (operands[1]) != CONST_INT"
"@
and\\t%1, 0xff, %0
ldub\\t%1, %0"
[(set_attr "type" "unary,load")
(set_attr "length" "1")])
(define_expand "zero_extendqisi2"
[(set (match_operand:SI 0 "register_operand" "")
(zero_extend:SI (match_operand:QI 1 "register_operand" "")))]
""
"")
(define_insn "*zero_extendqisi2_insn"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extend:SI (match_operand:QI 1 "input_operand" "r,m")))]
"GET_CODE (operands[1]) != CONST_INT"
"@
and\\t%1, 0xff, %0
ldub\\t%1, %0"
[(set_attr "type" "unary,load")
(set_attr "length" "1")])
(define_expand "zero_extendqidi2"
[(set (match_operand:DI 0 "register_operand" "")
(zero_extend:DI (match_operand:QI 1 "register_operand" "")))]
"TARGET_ARCH64"
"")
(define_insn "*zero_extendqidi2_insn"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(zero_extend:DI (match_operand:QI 1 "input_operand" "r,m")))]
"TARGET_ARCH64 && GET_CODE (operands[1]) != CONST_INT"
"@
and\\t%1, 0xff, %0
ldub\\t%1, %0"
[(set_attr "type" "unary,load")
(set_attr "length" "1")])
(define_expand "zero_extendhidi2"
[(set (match_operand:DI 0 "register_operand" "")
(zero_extend:DI (match_operand:HI 1 "register_operand" "")))]
"TARGET_ARCH64"
"
{
rtx temp = gen_reg_rtx (DImode);
rtx shift_48 = GEN_INT (48);
int op1_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
emit_insn (gen_ashldi3 (temp, gen_rtx_SUBREG (DImode, operand1,
op1_subword),
shift_48));
emit_insn (gen_lshrdi3 (operand0, temp, shift_48));
DONE;
}")
(define_insn "*zero_extendhidi2_insn"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (match_operand:HI 1 "memory_operand" "m")))]
"TARGET_ARCH64"
"lduh\\t%1, %0"
[(set_attr "type" "load")
(set_attr "length" "1")])
;; ??? Write truncdisi pattern using sra?
(define_expand "zero_extendsidi2"
[(set (match_operand:DI 0 "register_operand" "")
(zero_extend:DI (match_operand:SI 1 "register_operand" "")))]
""
"")
(define_insn "*zero_extendsidi2_insn_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(zero_extend:DI (match_operand:SI 1 "input_operand" "r,m")))]
"TARGET_ARCH64 && GET_CODE (operands[1]) != CONST_INT"
"@
srl\\t%1, 0, %0
lduw\\t%1, %0"
[(set_attr "type" "shift,load")
(set_attr "length" "1")])
(define_insn "*zero_extendsidi2_insn_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (match_operand:SI 1 "register_operand" "r")))]
"! TARGET_ARCH64"
"#"
[(set_attr "type" "unary")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(zero_extend:DI (match_operand:SI 1 "register_operand" "")))]
"! TARGET_ARCH64 && reload_completed"
[(set (match_dup 2) (match_dup 3))
(set (match_dup 4) (match_dup 5))]
"
{
rtx dest1, dest2;
if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
dest1 = gen_highpart (SImode, operands[0]);
dest2 = gen_lowpart (SImode, operands[0]);
/* Swap the order in case of overlap. */
if (REGNO (dest1) == REGNO (operands[1]))
{
operands[2] = dest2;
operands[3] = operands[1];
operands[4] = dest1;
operands[5] = const0_rtx;
}
else
{
operands[2] = dest1;
operands[3] = const0_rtx;
operands[4] = dest2;
operands[5] = operands[1];
}
}")
;; Simplify comparisons of extended values.
(define_insn "*cmp_zero_extendqisi2"
[(set (reg:CC 100)
(compare:CC (zero_extend:SI (match_operand:QI 0 "register_operand" "r"))
(const_int 0)))]
"! TARGET_LIVE_G0"
"andcc\\t%0, 0xff, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_zero_extendqisi2_set"
[(set (reg:CC 100)
(compare:CC (zero_extend:SI (match_operand:QI 1 "register_operand" "r"))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(zero_extend:SI (match_dup 1)))]
""
"andcc\\t%1, 0xff, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_zero_extendqidi2"
[(set (reg:CCX 100)
(compare:CCX (zero_extend:DI (match_operand:QI 0 "register_operand" "r"))
(const_int 0)))]
"TARGET_ARCH64"
"andcc\\t%0, 0xff, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_zero_extendqidi2_set"
[(set (reg:CCX 100)
(compare:CCX (zero_extend:DI (match_operand:QI 1 "register_operand" "r"))
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (match_dup 1)))]
"TARGET_ARCH64"
"andcc\\t%1, 0xff, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;; Similarly, handle {SI,DI}->QI mode truncation followed by a compare.
(define_insn "*cmp_siqi_trunc"
[(set (reg:CC 100)
(compare:CC (subreg:QI (match_operand:SI 0 "register_operand" "r") 0)
(const_int 0)))]
"! TARGET_LIVE_G0"
"andcc\\t%0, 0xff, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_siqi_trunc_set"
[(set (reg:CC 100)
(compare:CC (subreg:QI (match_operand:SI 1 "register_operand" "r") 0)
(const_int 0)))
(set (match_operand:QI 0 "register_operand" "=r")
(subreg:QI (match_dup 1) 0))]
""
"andcc\\t%1, 0xff, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_diqi_trunc"
[(set (reg:CC 100)
(compare:CC (subreg:QI (match_operand:DI 0 "register_operand" "r") 0)
(const_int 0)))]
"TARGET_ARCH64"
"andcc\\t%0, 0xff, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_diqi_trunc_set"
[(set (reg:CC 100)
(compare:CC (subreg:QI (match_operand:DI 1 "register_operand" "r") 0)
(const_int 0)))
(set (match_operand:QI 0 "register_operand" "=r")
(subreg:QI (match_dup 1) 0))]
"TARGET_ARCH64"
"andcc\\t%1, 0xff, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;;- sign extension instructions
;; These patterns originally accepted general_operands, however, slightly
;; better code is generated by only accepting register_operands, and then
;; letting combine generate the lds[hb] insns.
(define_expand "extendhisi2"
[(set (match_operand:SI 0 "register_operand" "")
(sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
""
"
{
rtx temp = gen_reg_rtx (SImode);
rtx shift_16 = GEN_INT (16);
int op1_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1,
op1_subword),
shift_16));
emit_insn (gen_ashrsi3 (operand0, temp, shift_16));
DONE;
}")
(define_insn "*sign_extendhisi2_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
""
"ldsh\\t%1, %0"
[(set_attr "type" "sload")
(set_attr "length" "1")])
(define_expand "extendqihi2"
[(set (match_operand:HI 0 "register_operand" "")
(sign_extend:HI (match_operand:QI 1 "register_operand" "")))]
""
"
{
rtx temp = gen_reg_rtx (SImode);
rtx shift_24 = GEN_INT (24);
int op1_subword = 0;
int op0_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
if (GET_CODE (operand0) == SUBREG)
{
op0_subword = SUBREG_WORD (operand0);
operand0 = XEXP (operand0, 0);
}
emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1,
op1_subword),
shift_24));
if (GET_MODE (operand0) != SImode)
operand0 = gen_rtx_SUBREG (SImode, operand0, op0_subword);
emit_insn (gen_ashrsi3 (operand0, temp, shift_24));
DONE;
}")
(define_insn "*sign_extendqihi2_insn"
[(set (match_operand:HI 0 "register_operand" "=r")
(sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
""
"ldsb\\t%1, %0"
[(set_attr "type" "sload")
(set_attr "length" "1")])
(define_expand "extendqisi2"
[(set (match_operand:SI 0 "register_operand" "")
(sign_extend:SI (match_operand:QI 1 "register_operand" "")))]
""
"
{
rtx temp = gen_reg_rtx (SImode);
rtx shift_24 = GEN_INT (24);
int op1_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
emit_insn (gen_ashlsi3 (temp, gen_rtx_SUBREG (SImode, operand1,
op1_subword),
shift_24));
emit_insn (gen_ashrsi3 (operand0, temp, shift_24));
DONE;
}")
(define_insn "*sign_extendqisi2_insn"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
""
"ldsb\\t%1, %0"
[(set_attr "type" "sload")
(set_attr "length" "1")])
(define_expand "extendqidi2"
[(set (match_operand:DI 0 "register_operand" "")
(sign_extend:DI (match_operand:QI 1 "register_operand" "")))]
"TARGET_ARCH64"
"
{
rtx temp = gen_reg_rtx (DImode);
rtx shift_56 = GEN_INT (56);
int op1_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
emit_insn (gen_ashldi3 (temp, gen_rtx_SUBREG (DImode, operand1,
op1_subword),
shift_56));
emit_insn (gen_ashrdi3 (operand0, temp, shift_56));
DONE;
}")
(define_insn "*sign_extendqidi2_insn"
[(set (match_operand:DI 0 "register_operand" "=r")
(sign_extend:DI (match_operand:QI 1 "memory_operand" "m")))]
"TARGET_ARCH64"
"ldsb\\t%1, %0"
[(set_attr "type" "sload")
(set_attr "length" "1")])
(define_expand "extendhidi2"
[(set (match_operand:DI 0 "register_operand" "")
(sign_extend:DI (match_operand:HI 1 "register_operand" "")))]
"TARGET_ARCH64"
"
{
rtx temp = gen_reg_rtx (DImode);
rtx shift_48 = GEN_INT (48);
int op1_subword = 0;
if (GET_CODE (operand1) == SUBREG)
{
op1_subword = SUBREG_WORD (operand1);
operand1 = XEXP (operand1, 0);
}
emit_insn (gen_ashldi3 (temp, gen_rtx_SUBREG (DImode, operand1,
op1_subword),
shift_48));
emit_insn (gen_ashrdi3 (operand0, temp, shift_48));
DONE;
}")
(define_insn "*sign_extendhidi2_insn"
[(set (match_operand:DI 0 "register_operand" "=r")
(sign_extend:DI (match_operand:HI 1 "memory_operand" "m")))]
"TARGET_ARCH64"
"ldsh\\t%1, %0"
[(set_attr "type" "sload")
(set_attr "length" "1")])
(define_expand "extendsidi2"
[(set (match_operand:DI 0 "register_operand" "")
(sign_extend:DI (match_operand:SI 1 "register_operand" "")))]
"TARGET_ARCH64"
"")
(define_insn "*sign_extendsidi2_insn"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(sign_extend:DI (match_operand:SI 1 "input_operand" "r,m")))]
"TARGET_ARCH64"
"@
sra\\t%1, 0, %0
ldsw\\t%1, %0"
[(set_attr "type" "shift,sload")
(set_attr "length" "1")])
;; Special pattern for optimizing bit-field compares. This is needed
;; because combine uses this as a canonical form.
(define_insn "*cmp_zero_extract"
[(set (reg:CC 100)
(compare:CC
(zero_extract:SI (match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "small_int_or_double" "n")
(match_operand:SI 2 "small_int_or_double" "n"))
(const_int 0)))]
"! TARGET_LIVE_G0
&& ((GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) > 19)
|| (GET_CODE (operands[2]) == CONST_DOUBLE
&& CONST_DOUBLE_LOW (operands[2]) > 19))"
"*
{
int len = (GET_CODE (operands[1]) == CONST_INT
? INTVAL (operands[1])
: CONST_DOUBLE_LOW (operands[1]));
int pos = 32 -
(GET_CODE (operands[2]) == CONST_INT
? INTVAL (operands[2])
: CONST_DOUBLE_LOW (operands[2])) - len;
HOST_WIDE_INT mask = ((1 << len) - 1) << pos;
operands[1] = GEN_INT (mask);
return \"andcc\\t%0, %1, %%g0\";
}"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_zero_extract_sp64"
[(set (reg:CCX 100)
(compare:CCX
(zero_extract:DI (match_operand:DI 0 "register_operand" "r")
(match_operand:SI 1 "small_int_or_double" "n")
(match_operand:SI 2 "small_int_or_double" "n"))
(const_int 0)))]
"TARGET_ARCH64
&& ((GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) > 51)
|| (GET_CODE (operands[2]) == CONST_DOUBLE
&& CONST_DOUBLE_LOW (operands[2]) > 51))"
"*
{
int len = (GET_CODE (operands[1]) == CONST_INT
? INTVAL (operands[1])
: CONST_DOUBLE_LOW (operands[1]));
int pos = 64 -
(GET_CODE (operands[2]) == CONST_INT
? INTVAL (operands[2])
: CONST_DOUBLE_LOW (operands[2])) - len;
HOST_WIDE_INT mask = (((unsigned HOST_WIDE_INT) 1 << len) - 1) << pos;
operands[1] = GEN_INT (mask);
return \"andcc\\t%0, %1, %%g0\";
}"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;; Conversions between float, double and long double.
(define_insn "extendsfdf2"
[(set (match_operand:DF 0 "register_operand" "=e")
(float_extend:DF
(match_operand:SF 1 "register_operand" "f")))]
"TARGET_FPU"
"fstod\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "extendsftf2"
[(set (match_operand:TF 0 "register_operand" "=e")
(float_extend:TF
(match_operand:SF 1 "register_operand" "f")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fstoq\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "extenddftf2"
[(set (match_operand:TF 0 "register_operand" "=e")
(float_extend:TF
(match_operand:DF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fdtoq\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "truncdfsf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(float_truncate:SF
(match_operand:DF 1 "register_operand" "e")))]
"TARGET_FPU"
"fdtos\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "trunctfsf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(float_truncate:SF
(match_operand:TF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fqtos\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "trunctfdf2"
[(set (match_operand:DF 0 "register_operand" "=e")
(float_truncate:DF
(match_operand:TF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fqtod\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
;; Conversion between fixed point and floating point.
(define_insn "floatsisf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(float:SF (match_operand:SI 1 "register_operand" "f")))]
"TARGET_FPU"
"fitos\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "floatsidf2"
[(set (match_operand:DF 0 "register_operand" "=e")
(float:DF (match_operand:SI 1 "register_operand" "f")))]
"TARGET_FPU"
"fitod\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "floatsitf2"
[(set (match_operand:TF 0 "register_operand" "=e")
(float:TF (match_operand:SI 1 "register_operand" "f")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fitoq\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
;; Now the same for 64 bit sources.
(define_insn "floatdisf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(float:SF (match_operand:DI 1 "register_operand" "e")))]
"TARGET_V9 && TARGET_FPU"
"fxtos\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "floatdidf2"
[(set (match_operand:DF 0 "register_operand" "=e")
(float:DF (match_operand:DI 1 "register_operand" "e")))]
"TARGET_V9 && TARGET_FPU"
"fxtod\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "floatditf2"
[(set (match_operand:TF 0 "register_operand" "=e")
(float:TF (match_operand:DI 1 "register_operand" "e")))]
"TARGET_V9 && TARGET_FPU && TARGET_HARD_QUAD"
"fxtoq\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
;; Convert a float to an actual integer.
;; Truncation is performed as part of the conversion.
(define_insn "fix_truncsfsi2"
[(set (match_operand:SI 0 "register_operand" "=f")
(fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))]
"TARGET_FPU"
"fstoi\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "fix_truncdfsi2"
[(set (match_operand:SI 0 "register_operand" "=f")
(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "e"))))]
"TARGET_FPU"
"fdtoi\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "fix_trunctfsi2"
[(set (match_operand:SI 0 "register_operand" "=f")
(fix:SI (fix:TF (match_operand:TF 1 "register_operand" "e"))))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fqtoi\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
;; Now the same, for V9 targets
(define_insn "fix_truncsfdi2"
[(set (match_operand:DI 0 "register_operand" "=e")
(fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))]
"TARGET_V9 && TARGET_FPU"
"fstox\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "fix_truncdfdi2"
[(set (match_operand:DI 0 "register_operand" "=e")
(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "e"))))]
"TARGET_V9 && TARGET_FPU"
"fdtox\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "fix_trunctfdi2"
[(set (match_operand:DI 0 "register_operand" "=e")
(fix:DI (fix:TF (match_operand:TF 1 "register_operand" "e"))))]
"TARGET_V9 && TARGET_FPU && TARGET_HARD_QUAD"
"fqtox\\t%1, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
;;- arithmetic instructions
(define_expand "adddi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_add_operand" "rHI")))]
""
"
{
if (! TARGET_ARCH64)
{
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2,
gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_PLUS (DImode, operands[1],
operands[2])),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (CCmode, SPARC_ICC_REG)))));
DONE;
}
if (arith_double_4096_operand(operands[2], DImode))
{
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_MINUS (DImode, operands[1],
GEN_INT(-4096))));
DONE;
}
}")
(define_insn "adddi3_insn_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64 && reload_completed"
[(parallel [(set (reg:CC_NOOV 100)
(compare:CC_NOOV (plus:SI (match_dup 4)
(match_dup 5))
(const_int 0)))
(set (match_dup 3)
(plus:SI (match_dup 4) (match_dup 5)))])
(set (match_dup 6)
(plus:SI (plus:SI (match_dup 7)
(match_dup 8))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))]
"
{
operands[3] = gen_lowpart (SImode, operands[0]);
operands[4] = gen_lowpart (SImode, operands[1]);
operands[5] = gen_lowpart (SImode, operands[2]);
operands[6] = gen_highpart (SImode, operands[0]);
operands[7] = gen_highpart (SImode, operands[1]);
if (GET_CODE (operands[2]) == CONST_INT)
{
if (INTVAL (operands[2]) < 0)
operands[8] = constm1_rtx;
else
operands[8] = const0_rtx;
}
else
operands[8] = gen_highpart (SImode, operands[2]);
}")
(define_split
[(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_operand:DI 1 "arith_double_operand" "r")
(match_operand:DI 2 "arith_double_operand" "rHI")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64 && reload_completed"
[(parallel [(set (reg:CC_NOOV 100)
(compare:CC_NOOV (minus:SI (match_dup 4)
(match_dup 5))
(const_int 0)))
(set (match_dup 3)
(minus:SI (match_dup 4) (match_dup 5)))])
(set (match_dup 6)
(minus:SI (minus:SI (match_dup 7)
(match_dup 8))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))]
"
{
operands[3] = gen_lowpart (SImode, operands[0]);
operands[4] = gen_lowpart (SImode, operands[1]);
operands[5] = gen_lowpart (SImode, operands[2]);
operands[6] = gen_highpart (SImode, operands[0]);
operands[7] = gen_highpart (SImode, operands[1]);
if (GET_CODE (operands[2]) == CONST_INT)
{
if (INTVAL (operands[2]) < 0)
operands[8] = constm1_rtx;
else
operands[8] = const0_rtx;
}
else
operands[8] = gen_highpart (SImode, operands[2]);
}")
;; LTU here means "carry set"
(define_insn "addx"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (plus:SI (match_operand:SI 1 "arith_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))]
""
"addx\\t%1, %2, %0"
[(set_attr "type" "unary")
(set_attr "length" "1")])
(define_insn "*addx_extend_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0)))))]
"! TARGET_ARCH64"
"#"
[(set_attr "type" "unary")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "")
(match_operand:SI 2 "arith_operand" ""))
(ltu:SI (reg:CC_NOOV 100) (const_int 0)))))]
"! TARGET_ARCH64 && reload_completed"
[(set (match_dup 3) (plus:SI (plus:SI (match_dup 1) (match_dup 2))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))
(set (match_dup 4) (const_int 0))]
"operands[3] = gen_lowpart (SImode, operands[0]);
operands[4] = gen_highpart (SImode, operands[1]);")
(define_insn "*addx_extend_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (plus:SI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0)))))]
"TARGET_ARCH64"
"addx\\t%r1, %2, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "subx"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))]
""
"subx\\t%r1, %2, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*subx_extend_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0)))))]
"TARGET_ARCH64"
"subx\\t%r1, %2, %0"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "*subx_extend"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0)))))]
"! TARGET_ARCH64"
"#"
[(set_attr "type" "unary")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extend:DI (minus:SI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(ltu:SI (reg:CC_NOOV 100) (const_int 0)))))]
"! TARGET_ARCH64 && reload_completed"
[(set (match_dup 3) (minus:SI (minus:SI (match_dup 1) (match_dup 2))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))
(set (match_dup 4) (const_int 0))]
"operands[3] = gen_lowpart (SImode, operands[0]);
operands[4] = gen_highpart (SImode, operands[0]);")
(define_insn ""
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
(match_operand:DI 2 "register_operand" "r")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64"
"#"
[(set_attr "type" "multi")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(plus:DI (zero_extend:DI (match_operand:SI 1 "register_operand" ""))
(match_operand:DI 2 "register_operand" "")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64 && reload_completed"
[(parallel [(set (reg:CC_NOOV 100)
(compare:CC_NOOV (plus:SI (match_dup 3) (match_dup 1))
(const_int 0)))
(set (match_dup 5) (plus:SI (match_dup 3) (match_dup 1)))])
(set (match_dup 6)
(plus:SI (plus:SI (match_dup 4) (const_int 0))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))]
"operands[3] = gen_lowpart (SImode, operands[2]);
operands[4] = gen_highpart (SImode, operands[2]);
operands[5] = gen_lowpart (SImode, operands[0]);
operands[6] = gen_highpart (SImode, operands[0]);")
(define_insn "*adddi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")))]
"TARGET_ARCH64"
"add\\t%1, %2, %0"
[(set_attr "type" "binary")
(set_attr "length" "1")])
(define_expand "addsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(plus:SI (match_operand:SI 1 "arith_operand" "%r,d")
(match_operand:SI 2 "arith_add_operand" "rI,d")))]
""
"
{
if (arith_4096_operand(operands[2], DImode))
{
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_MINUS (SImode, operands[1],
GEN_INT(-4096))));
DONE;
}
}")
(define_insn "*addsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(plus:SI (match_operand:SI 1 "arith_operand" "%r,d")
(match_operand:SI 2 "arith_operand" "rI,d")))]
""
"@
add\\t%1, %2, %0
fpadd32s\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1")])
(define_insn "*cmp_cc_plus"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (plus:SI (match_operand:SI 0 "arith_operand" "%r")
(match_operand:SI 1 "arith_operand" "rI"))
(const_int 0)))]
"! TARGET_LIVE_G0"
"addcc\\t%0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_plus"
[(set (reg:CCX_NOOV 100)
(compare:CCX_NOOV (plus:DI (match_operand:DI 0 "arith_double_operand" "%r")
(match_operand:DI 1 "arith_double_operand" "rHI"))
(const_int 0)))]
"TARGET_ARCH64"
"addcc\\t%0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_plus_set"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (plus:SI (match_operand:SI 1 "arith_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI"))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (match_dup 1) (match_dup 2)))]
""
"addcc\\t%1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_plus_set"
[(set (reg:CCX_NOOV 100)
(compare:CCX_NOOV (plus:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI"))
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (match_dup 1) (match_dup 2)))]
"TARGET_ARCH64"
"addcc\\t%1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_expand "subdi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "arith_double_add_operand" "rHI")))]
""
"
{
if (! TARGET_ARCH64)
{
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2,
gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_MINUS (DImode, operands[1],
operands[2])),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (CCmode, SPARC_ICC_REG)))));
DONE;
}
if (arith_double_4096_operand(operands[2], DImode))
{
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_PLUS (DImode, operands[1],
GEN_INT(-4096))));
DONE;
}
}")
(define_insn "*subdi3_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "arith_double_operand" "rHI")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64"
"#"
[(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(minus:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "arith_double_operand" "")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64
&& reload_completed
&& (GET_CODE (operands[2]) == CONST_INT
|| GET_CODE (operands[2]) == CONST_DOUBLE)"
[(clobber (const_int 0))]
"
{
rtx highp, lowp;
highp = gen_highpart (SImode, operands[2]);
lowp = gen_lowpart (SImode, operands[2]);
if ((lowp == const0_rtx)
&& (operands[0] == operands[1]))
{
emit_insn (gen_rtx_SET (VOIDmode,
gen_highpart (SImode, operands[0]),
gen_rtx_MINUS (SImode,
gen_highpart (SImode, operands[1]),
highp)));
}
else
{
emit_insn (gen_cmp_minus_cc_set (gen_lowpart (SImode, operands[0]),
gen_lowpart (SImode, operands[1]),
lowp));
emit_insn (gen_subx (gen_highpart (SImode, operands[0]),
gen_highpart (SImode, operands[1]),
highp));
}
DONE;
}")
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(minus:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "register_operand" "")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64
&& reload_completed"
[(clobber (const_int 0))]
"
{
emit_insn (gen_cmp_minus_cc_set (gen_lowpart (SImode, operands[0]),
gen_lowpart (SImode, operands[1]),
gen_lowpart (SImode, operands[2])));
emit_insn (gen_subx (gen_highpart (SImode, operands[0]),
gen_highpart (SImode, operands[1]),
gen_highpart (SImode, operands[2])));
DONE;
}")
(define_insn ""
[(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_operand:DI 1 "register_operand" "r")
(zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))
(clobber (reg:CC 100))]
"! TARGET_ARCH64"
"#"
[(set_attr "type" "multi")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(minus:DI (match_operand:DI 1 "register_operand" "")
(zero_extend:DI (match_operand:SI 2 "register_operand" ""))))
(clobber (reg:CC 100))]
"! TARGET_ARCH64 && reload_completed"
[(parallel [(set (reg:CC_NOOV 100)
(compare:CC_NOOV (minus:SI (match_dup 3) (match_dup 2))
(const_int 0)))
(set (match_dup 5) (minus:SI (match_dup 3) (match_dup 2)))])
(set (match_dup 6)
(minus:SI (minus:SI (match_dup 4) (const_int 0))
(ltu:SI (reg:CC_NOOV 100) (const_int 0))))]
"operands[3] = gen_lowpart (SImode, operands[1]);
operands[4] = gen_highpart (SImode, operands[1]);
operands[5] = gen_lowpart (SImode, operands[0]);
operands[6] = gen_highpart (SImode, operands[0]);")
(define_insn "*subdi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "arith_double_operand" "rHI")))]
"TARGET_ARCH64"
"sub\\t%1, %2, %0"
[(set_attr "type" "binary")
(set_attr "length" "1")])
(define_expand "subsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(minus:SI (match_operand:SI 1 "register_operand" "r,d")
(match_operand:SI 2 "arith_add_operand" "rI,d")))]
""
"
{
if (arith_4096_operand(operands[2], DImode))
{
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_PLUS (SImode, operands[1],
GEN_INT(-4096))));
DONE;
}
}")
(define_insn "*subsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(minus:SI (match_operand:SI 1 "register_operand" "r,d")
(match_operand:SI 2 "arith_operand" "rI,d")))]
""
"@
sub\\t%1, %2, %0
fpsub32s\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1")])
(define_insn "*cmp_minus_cc"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (minus:SI (match_operand:SI 0 "reg_or_0_operand" "rJ")
(match_operand:SI 1 "arith_operand" "rI"))
(const_int 0)))]
"! TARGET_LIVE_G0"
"subcc\\t%r0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_minus_ccx"
[(set (reg:CCX_NOOV 100)
(compare:CCX_NOOV (minus:DI (match_operand:DI 0 "register_operand" "r")
(match_operand:DI 1 "arith_double_operand" "rHI"))
(const_int 0)))]
"TARGET_ARCH64"
"subcc\\t%0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "cmp_minus_cc_set"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
(match_operand:SI 2 "arith_operand" "rI"))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_dup 1) (match_dup 2)))]
""
"subcc\\t%r1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_minus_ccx_set"
[(set (reg:CCX_NOOV 100)
(compare:CCX_NOOV (minus:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "arith_double_operand" "rHI"))
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(minus:DI (match_dup 1) (match_dup 2)))]
"TARGET_ARCH64"
"subcc\\t%1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;; Integer Multiply/Divide.
;; The 32 bit multiply/divide instructions are deprecated on v9 and shouldn't
;; we used. We still use them in 32 bit v9 compilers.
;; The 64 bit v9 compiler will (/should) widen the args and use muldi3.
(define_insn "mulsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (match_operand:SI 1 "arith_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_HARD_MUL"
"smul\\t%1, %2, %0"
[(set_attr "type" "imul")
(set_attr "length" "1")])
(define_expand "muldi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(mult:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")))]
"TARGET_ARCH64 || TARGET_V8PLUS"
"
{
if (TARGET_V8PLUS)
{
emit_insn (gen_muldi3_v8plus (operands[0], operands[1], operands[2]));
DONE;
}
}")
(define_insn "*muldi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(mult:DI (match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")))]
"TARGET_ARCH64"
"mulx\\t%1, %2, %0"
[(set_attr "type" "imul")
(set_attr "length" "1")])
;; V8plus wide multiply.
;; XXX
(define_insn "muldi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=r,h")
(mult:DI (match_operand:DI 1 "arith_double_operand" "%r,0")
(match_operand:DI 2 "arith_double_operand" "rHI,rHI")))
(clobber (match_scratch:SI 3 "=&h,X"))
(clobber (match_scratch:SI 4 "=&h,X"))]
"TARGET_V8PLUS"
"*
{
if (sparc_check_64 (operands[1], insn) <= 0)
output_asm_insn (\"srl\\t%L1, 0, %L1\", operands);
if (which_alternative == 1)
output_asm_insn (\"sllx\\t%H1, 32, %H1\", operands);
if (sparc_check_64 (operands[2], insn) <= 0)
output_asm_insn (\"srl\\t%L2, 0, %L2\", operands);
if (which_alternative == 1)
return \"or\\t%L1, %H1, %H1\\n\\tsllx\\t%H2, 32, %L1\\n\\tor\\t%L2, %L1, %L1\\n\\tmulx\\t%H1, %L1, %L0\;srlx\\t%L0, 32, %H0\";
else
return \"sllx\\t%H1, 32, %3\\n\\tsllx\\t%H2, 32, %4\\n\\tor\\t%L1, %3, %3\\n\\tor\\t%L2, %4, %4\\n\\tmulx\\t%3, %4, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0\";
}"
[(set_attr "length" "9,8")])
;; It is not known whether this will match.
(define_insn "*cmp_mul_set"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (match_operand:SI 1 "arith_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI")))
(set (reg:CC_NOOV 100)
(compare:CC_NOOV (mult:SI (match_dup 1) (match_dup 2))
(const_int 0)))]
"TARGET_V8 || TARGET_SPARCLITE || TARGET_DEPRECATED_V8_INSNS"
"smulcc\\t%1, %2, %0"
[(set_attr "type" "imul")
(set_attr "length" "1")])
(define_expand "mulsidi3"
[(set (match_operand:DI 0 "register_operand" "")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" ""))
(sign_extend:DI (match_operand:SI 2 "arith_operand" ""))))]
"TARGET_HARD_MUL"
"
{
if (CONSTANT_P (operands[2]))
{
if (TARGET_V8PLUS)
{
emit_insn (gen_const_mulsidi3_v8plus (operands[0], operands[1],
operands[2]));
DONE;
}
emit_insn (gen_const_mulsidi3 (operands[0], operands[1], operands[2]));
DONE;
}
if (TARGET_V8PLUS)
{
emit_insn (gen_mulsidi3_v8plus (operands[0], operands[1], operands[2]));
DONE;
}
}")
;; V9 puts the 64 bit product in a 64 bit register. Only out or global
;; registers can hold 64 bit values in the V8plus environment.
;; XXX
(define_insn "mulsidi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=h,r")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(sign_extend:DI (match_operand:SI 2 "register_operand" "r,r"))))
(clobber (match_scratch:SI 3 "=X,&h"))]
"TARGET_V8PLUS"
"@
smul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0
smul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0"
[(set_attr "length" "2,3")])
;; XXX
(define_insn "const_mulsidi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=h,r")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(match_operand:SI 2 "small_int" "I,I")))
(clobber (match_scratch:SI 3 "=X,&h"))]
"TARGET_V8PLUS"
"@
smul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0
smul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0"
[(set_attr "length" "2,3")])
;; XXX
(define_insn "*mulsidi3_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r"))
(sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
"TARGET_HARD_MUL32"
"*
{
return TARGET_SPARCLET ? \"smuld\\t%1, %2, %L0\" : \"smul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "sparclet")
(const_int 1) (const_int 2)))])
;; Extra pattern, because sign_extend of a constant isn't valid.
;; XXX
(define_insn "const_mulsidi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r"))
(match_operand:SI 2 "small_int" "I")))]
"TARGET_HARD_MUL"
"*
{
return TARGET_SPARCLET ? \"smuld\\t%1, %2, %L0\" : \"smul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "sparclet")
(const_int 1) (const_int 2)))])
(define_expand "smulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "")
(truncate:SI
(lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" ""))
(sign_extend:DI (match_operand:SI 2 "arith_operand" "")))
(const_int 32))))]
"TARGET_HARD_MUL"
"
{
if (CONSTANT_P (operands[2]))
{
if (TARGET_V8PLUS)
{
emit_insn (gen_const_smulsi3_highpart_v8plus (operands[0],
operands[1],
operands[2],
GEN_INT (32)));
DONE;
}
emit_insn (gen_const_smulsi3_highpart (operands[0], operands[1], operands[2]));
DONE;
}
if (TARGET_V8PLUS)
{
emit_insn (gen_smulsi3_highpart_v8plus (operands[0], operands[1],
operands[2], GEN_INT (32)));
DONE;
}
}")
;; XXX
(define_insn "smulsi3_highpart_v8plus"
[(set (match_operand:SI 0 "register_operand" "=h,r")
(truncate:SI
(lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(sign_extend:DI (match_operand:SI 2 "register_operand" "r,r")))
(match_operand:SI 3 "const_int_operand" "i,i"))))
(clobber (match_scratch:SI 4 "=X,&h"))]
"TARGET_V8PLUS"
"@
smul %1,%2,%0\;srlx %0,%3,%0
smul %1,%2,%4\;srlx %4,%3,%0"
[(set_attr "length" "2")])
;; The combiner changes TRUNCATE in the previous pattern to SUBREG.
;; XXX
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=h,r")
(subreg:SI
(lshiftrt:DI
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(sign_extend:DI (match_operand:SI 2 "register_operand" "r,r")))
(match_operand:SI 3 "const_int_operand" "i,i"))
1))
(clobber (match_scratch:SI 4 "=X,&h"))]
"TARGET_V8PLUS"
"@
smul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0
smul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0"
[(set_attr "length" "2")])
;; XXX
(define_insn "const_smulsi3_highpart_v8plus"
[(set (match_operand:SI 0 "register_operand" "=h,r")
(truncate:SI
(lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(match_operand 2 "small_int" "i,i"))
(match_operand:SI 3 "const_int_operand" "i,i"))))
(clobber (match_scratch:SI 4 "=X,&h"))]
"TARGET_V8PLUS"
"@
smul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0
smul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0"
[(set_attr "length" "2")])
;; XXX
(define_insn "*smulsi3_highpart_sp32"
[(set (match_operand:SI 0 "register_operand" "=r")
(truncate:SI
(lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r"))
(sign_extend:DI (match_operand:SI 2 "register_operand" "r")))
(const_int 32))))]
"TARGET_HARD_MUL32
&& ! TARGET_LIVE_G0"
"smul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0"
[(set_attr "length" "2")])
;; XXX
(define_insn "const_smulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "=r")
(truncate:SI
(lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r"))
(match_operand:SI 2 "register_operand" "r"))
(const_int 32))))]
"TARGET_HARD_MUL32
&& ! TARGET_LIVE_G0"
"smul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0"
[(set_attr "length" "2")])
(define_expand "umulsidi3"
[(set (match_operand:DI 0 "register_operand" "")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" ""))
(zero_extend:DI (match_operand:SI 2 "uns_arith_operand" ""))))]
"TARGET_HARD_MUL"
"
{
if (CONSTANT_P (operands[2]))
{
if (TARGET_V8PLUS)
{
emit_insn (gen_const_umulsidi3_v8plus (operands[0], operands[1],
operands[2]));
DONE;
}
emit_insn (gen_const_umulsidi3 (operands[0], operands[1], operands[2]));
DONE;
}
if (TARGET_V8PLUS)
{
emit_insn (gen_umulsidi3_v8plus (operands[0], operands[1], operands[2]));
DONE;
}
}")
;; XXX
(define_insn "umulsidi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=h,r")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(zero_extend:DI (match_operand:SI 2 "register_operand" "r,r"))))
(clobber (match_scratch:SI 3 "=X,&h"))]
"TARGET_V8PLUS"
"@
umul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0
umul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0"
[(set_attr "length" "2,3")])
;; XXX
(define_insn "*umulsidi3_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
(zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
"TARGET_HARD_MUL32"
"*
{
return TARGET_SPARCLET ? \"umuld\\t%1, %2, %L0\" : \"umul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "sparclet")
(const_int 1) (const_int 2)))])
;; Extra pattern, because sign_extend of a constant isn't valid.
;; XXX
(define_insn "const_umulsidi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
(match_operand:SI 2 "uns_small_int" "")))]
"TARGET_HARD_MUL32"
"*
{
return TARGET_SPARCLET ? \"umuld\\t%1, %2, %L0\" : \"umul\\t%1, %2, %L0\\n\\trd\\t%%y, %H0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "sparclet")
(const_int 1) (const_int 2)))])
;; XXX
(define_insn "const_umulsidi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=h,r")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(match_operand:SI 2 "uns_small_int" "")))
(clobber (match_scratch:SI 3 "=X,h"))]
"TARGET_V8PLUS"
"@
umul\\t%1, %2, %L0\\n\\tsrlx\\t%L0, 32, %H0
umul\\t%1, %2, %3\\n\\tsrlx\\t%3, 32, %H0\\n\\tmov\\t%3, %L0"
[(set_attr "length" "2,3")])
(define_expand "umulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "")
(truncate:SI
(lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" ""))
(zero_extend:DI (match_operand:SI 2 "uns_arith_operand" "")))
(const_int 32))))]
"TARGET_HARD_MUL"
"
{
if (CONSTANT_P (operands[2]))
{
if (TARGET_V8PLUS)
{
emit_insn (gen_const_umulsi3_highpart_v8plus (operands[0],
operands[1],
operands[2],
GEN_INT (32)));
DONE;
}
emit_insn (gen_const_umulsi3_highpart (operands[0], operands[1], operands[2]));
DONE;
}
if (TARGET_V8PLUS)
{
emit_insn (gen_umulsi3_highpart_v8plus (operands[0], operands[1],
operands[2], GEN_INT (32)));
DONE;
}
}")
;; XXX
(define_insn "umulsi3_highpart_v8plus"
[(set (match_operand:SI 0 "register_operand" "=h,r")
(truncate:SI
(lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(zero_extend:DI (match_operand:SI 2 "register_operand" "r,r")))
(match_operand:SI 3 "const_int_operand" "i,i"))))
(clobber (match_scratch:SI 4 "=X,h"))]
"TARGET_V8PLUS"
"@
umul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0
umul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0"
[(set_attr "length" "2")])
;; XXX
(define_insn "const_umulsi3_highpart_v8plus"
[(set (match_operand:SI 0 "register_operand" "=h,r")
(truncate:SI
(lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
(match_operand:SI 2 "uns_small_int" ""))
(match_operand:SI 3 "const_int_operand" "i,i"))))
(clobber (match_scratch:SI 4 "=X,h"))]
"TARGET_V8PLUS"
"@
umul\\t%1, %2, %0\\n\\tsrlx\\t%0, %3, %0
umul\\t%1, %2, %4\\n\\tsrlx\\t%4, %3, %0"
[(set_attr "length" "2")])
;; XXX
(define_insn "*umulsi3_highpart_sp32"
[(set (match_operand:SI 0 "register_operand" "=r")
(truncate:SI
(lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
(zero_extend:DI (match_operand:SI 2 "register_operand" "r")))
(const_int 32))))]
"TARGET_HARD_MUL32
&& ! TARGET_LIVE_G0"
"umul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0"
[(set_attr "length" "2")])
;; XXX
(define_insn "const_umulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "=r")
(truncate:SI
(lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
(match_operand:SI 2 "uns_small_int" ""))
(const_int 32))))]
"TARGET_HARD_MUL32
&& ! TARGET_LIVE_G0"
"umul\\t%1, %2, %%g0\\n\\trd\\t%%y, %0"
[(set_attr "length" "2")])
;; The v8 architecture specifies that there must be 3 instructions between
;; a y register write and a use of it for correct results.
;; XXX SHEESH
(define_insn "divsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(div:SI (match_operand:SI 1 "register_operand" "r,r")
(match_operand:SI 2 "input_operand" "rI,m")))
(clobber (match_scratch:SI 3 "=&r,&r"))]
"(TARGET_V8
|| TARGET_DEPRECATED_V8_INSNS)
&& ! TARGET_LIVE_G0"
"*
{
if (which_alternative == 0)
if (TARGET_V9)
return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tsdiv\\t%1, %2, %0\";
else
return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tnop\\n\\tnop\\n\\tnop\\n\\tsdiv\\t%1, %2, %0\";
else
if (TARGET_V9)
return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tld\\t%2, %3\\n\\tsdiv\\t%1, %3, %0\";
else
return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tld\\t%2, %3\\n\\tnop\\n\\tnop\\n\\tsdiv\\t%1, %3, %0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "v9")
(const_int 4) (const_int 7)))])
(define_insn "divdi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(div:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "arith_double_operand" "rHI")))]
"TARGET_ARCH64"
"sdivx\\t%1, %2, %0")
;; It is not known whether this will match.
;; XXX I hope it doesn't fucking match...
(define_insn "*cmp_sdiv_cc_set"
[(set (match_operand:SI 0 "register_operand" "=r")
(div:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))
(set (reg:CC 100)
(compare:CC (div:SI (match_dup 1) (match_dup 2))
(const_int 0)))
(clobber (match_scratch:SI 3 "=&r"))]
"(TARGET_V8
|| TARGET_DEPRECATED_V8_INSNS)
&& ! TARGET_LIVE_G0"
"*
{
if (TARGET_V9)
return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tsdivcc\\t%1, %2, %0\";
else
return \"sra\\t%1, 31, %3\\n\\twr\\t%%g0, %3, %%y\\n\\tnop\\n\\tnop\\n\\tnop\\n\\tsdivcc\\t%1, %2, %0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "v9")
(const_int 3) (const_int 6)))])
;; XXX
(define_insn "udivsi3"
[(set (match_operand:SI 0 "register_operand" "=r,&r,&r")
(udiv:SI (match_operand:SI 1 "reg_or_nonsymb_mem_operand" "r,r,m")
(match_operand:SI 2 "input_operand" "rI,m,r")))]
"(TARGET_V8
|| TARGET_DEPRECATED_V8_INSNS)
&& ! TARGET_LIVE_G0"
"*
{
output_asm_insn (\"wr\\t%%g0, %%g0, %%y\", operands);
switch (which_alternative)
{
default:
if (TARGET_V9)
return \"udiv\\t%1, %2, %0\";
return \"nop\\n\\tnop\\n\\tnop\\n\\tudiv\\t%1, %2, %0\";
case 1:
return \"ld\\t%2, %0\\n\\tnop\\n\\tnop\\n\\tudiv\\t%1, %0, %0\";
case 2:
return \"ld\\t%1, %0\\n\\tnop\\n\\tnop\\n\\tudiv\\t%0, %2, %0\";
}
}"
[(set (attr "length")
(if_then_else (and (eq_attr "isa" "v9")
(eq_attr "alternative" "0"))
(const_int 2) (const_int 5)))])
(define_insn "udivdi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(udiv:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "arith_double_operand" "rHI")))]
"TARGET_ARCH64"
"udivx\\t%1, %2, %0")
;; It is not known whether this will match.
;; XXX I hope it doesn't fucking match...
(define_insn "*cmp_udiv_cc_set"
[(set (match_operand:SI 0 "register_operand" "=r")
(udiv:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))
(set (reg:CC 100)
(compare:CC (udiv:SI (match_dup 1) (match_dup 2))
(const_int 0)))]
"(TARGET_V8
|| TARGET_DEPRECATED_V8_INSNS)
&& ! TARGET_LIVE_G0"
"*
{
if (TARGET_V9)
return \"wr\\t%%g0, %%g0, %%y\\n\\tudivcc\\t%1, %2, %0\";
else
return \"wr\\t%%g0, %%g0, %%y\\n\\tnop\\n\\tnop\\n\\tnop\\n\\tudivcc\\t%1, %2, %0\";
}"
[(set (attr "length")
(if_then_else (eq_attr "isa" "v9")
(const_int 2) (const_int 5)))])
; sparclet multiply/accumulate insns
(define_insn "*smacsi"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI"))
(match_operand:SI 3 "register_operand" "0")))]
"TARGET_SPARCLET"
"smac\\t%1, %2, %0"
[(set_attr "type" "imul")
(set_attr "length" "1")])
(define_insn "*smacdi"
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (mult:DI (sign_extend:DI
(match_operand:SI 1 "register_operand" "%r"))
(sign_extend:DI
(match_operand:SI 2 "register_operand" "r")))
(match_operand:DI 3 "register_operand" "0")))]
"TARGET_SPARCLET"
"smacd\\t%1, %2, %L0"
[(set_attr "type" "imul")
(set_attr "length" "1")])
(define_insn "*umacdi"
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI (mult:DI (zero_extend:DI
(match_operand:SI 1 "register_operand" "%r"))
(zero_extend:DI
(match_operand:SI 2 "register_operand" "r")))
(match_operand:DI 3 "register_operand" "0")))]
"TARGET_SPARCLET"
"umacd\\t%1, %2, %L0"
[(set_attr "type" "imul")
(set_attr "length" "1")])
;;- Boolean instructions
;; We define DImode `and' so with DImode `not' we can get
;; DImode `andn'. Other combinations are possible.
(define_expand "anddi3"
[(set (match_operand:DI 0 "register_operand" "")
(and:DI (match_operand:DI 1 "arith_double_operand" "")
(match_operand:DI 2 "arith_double_operand" "")))]
""
"")
(define_insn "*anddi3_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(and:DI (match_operand:DI 1 "arith_double_operand" "%r,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b")))]
"! TARGET_ARCH64"
"@
#
fand\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "2,1")])
(define_insn "*anddi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(and:DI (match_operand:DI 1 "arith_double_operand" "%r,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b")))]
"TARGET_ARCH64"
"@
and\\t%1, %2, %0
fand\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_insn "andsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(and:SI (match_operand:SI 1 "arith_operand" "%r,d")
(match_operand:SI 2 "arith_operand" "rI,d")))]
""
"@
and\\t%1, %2, %0
fands\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(and:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "" "")))
(clobber (match_operand:SI 3 "register_operand" ""))]
"GET_CODE (operands[2]) == CONST_INT
&& !SMALL_INT32 (operands[2])
&& (INTVAL (operands[2]) & 0x3ff) == 0x3ff"
[(set (match_dup 3) (match_dup 4))
(set (match_dup 0) (and:SI (not:SI (match_dup 3)) (match_dup 1)))]
"
{
operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff);
}")
;; Split DImode logical operations requiring two instructions.
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(match_operator:DI 1 "cc_arithop" ; AND, IOR, XOR
[(match_operand:DI 2 "register_operand" "")
(match_operand:DI 3 "arith_double_operand" "")]))]
"! TARGET_ARCH64
&& reload_completed
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))"
[(set (match_dup 4) (match_op_dup:SI 1 [(match_dup 6) (match_dup 8)]))
(set (match_dup 5) (match_op_dup:SI 1 [(match_dup 7) (match_dup 9)]))]
"
{
if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
operands[4] = gen_highpart (SImode, operands[0]);
operands[5] = gen_lowpart (SImode, operands[0]);
operands[6] = gen_highpart (SImode, operands[2]);
operands[7] = gen_lowpart (SImode, operands[2]);
if (GET_CODE (operands[3]) == CONST_INT)
{
if (INTVAL (operands[3]) < 0)
operands[8] = constm1_rtx;
else
operands[8] = const0_rtx;
}
else
operands[8] = gen_highpart (SImode, operands[3]);
operands[9] = gen_lowpart (SImode, operands[3]);
}")
(define_insn "*and_not_di_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(and:DI (not:DI (match_operand:DI 1 "register_operand" "r,b"))
(match_operand:DI 2 "register_operand" "r,b")))]
"! TARGET_ARCH64"
"@
#
fandnot1\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "2,1")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(and:DI (not:DI (match_operand:DI 1 "register_operand" ""))
(match_operand:DI 2 "register_operand" "")))]
"! TARGET_ARCH64
&& reload_completed
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))"
[(set (match_dup 3) (and:SI (not:SI (match_dup 4)) (match_dup 5)))
(set (match_dup 6) (and:SI (not:SI (match_dup 7)) (match_dup 8)))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
operands[3] = gen_highpart (SImode, operands[0]);
operands[4] = gen_highpart (SImode, operands[1]);
operands[5] = gen_highpart (SImode, operands[2]);
operands[6] = gen_lowpart (SImode, operands[0]);
operands[7] = gen_lowpart (SImode, operands[1]);
operands[8] = gen_lowpart (SImode, operands[2]);")
(define_insn "*and_not_di_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(and:DI (not:DI (match_operand:DI 1 "register_operand" "r,b"))
(match_operand:DI 2 "register_operand" "r,b")))]
"TARGET_ARCH64"
"@
andn\\t%2, %1, %0
fandnot1\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_insn "*and_not_si"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(and:SI (not:SI (match_operand:SI 1 "register_operand" "r,d"))
(match_operand:SI 2 "register_operand" "r,d")))]
""
"@
andn\\t%2, %1, %0
fandnot1s\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_expand "iordi3"
[(set (match_operand:DI 0 "register_operand" "")
(ior:DI (match_operand:DI 1 "arith_double_operand" "")
(match_operand:DI 2 "arith_double_operand" "")))]
""
"")
(define_insn "*iordi3_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(ior:DI (match_operand:DI 1 "arith_double_operand" "%r,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b")))]
"! TARGET_ARCH64"
"@
#
for\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "2,1")])
(define_insn "*iordi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(ior:DI (match_operand:DI 1 "arith_double_operand" "%r,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b")))]
"TARGET_ARCH64"
"@
or\\t%1, %2, %0
for\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_insn "iorsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(ior:SI (match_operand:SI 1 "arith_operand" "%r,d")
(match_operand:SI 2 "arith_operand" "rI,d")))]
""
"@
or\\t%1, %2, %0
fors\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(ior:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "" "")))
(clobber (match_operand:SI 3 "register_operand" ""))]
"GET_CODE (operands[2]) == CONST_INT
&& !SMALL_INT32 (operands[2])
&& (INTVAL (operands[2]) & 0x3ff) == 0x3ff"
[(set (match_dup 3) (match_dup 4))
(set (match_dup 0) (ior:SI (not:SI (match_dup 3)) (match_dup 1)))]
"
{
operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff);
}")
(define_insn "*or_not_di_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(ior:DI (not:DI (match_operand:DI 1 "register_operand" "r,b"))
(match_operand:DI 2 "register_operand" "r,b")))]
"! TARGET_ARCH64"
"@
#
fornot1\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "2,1")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(ior:DI (not:DI (match_operand:DI 1 "register_operand" ""))
(match_operand:DI 2 "register_operand" "")))]
"! TARGET_ARCH64
&& reload_completed
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))"
[(set (match_dup 3) (ior:SI (not:SI (match_dup 4)) (match_dup 5)))
(set (match_dup 6) (ior:SI (not:SI (match_dup 7)) (match_dup 8)))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
operands[3] = gen_highpart (SImode, operands[0]);
operands[4] = gen_highpart (SImode, operands[1]);
operands[5] = gen_highpart (SImode, operands[2]);
operands[6] = gen_lowpart (SImode, operands[0]);
operands[7] = gen_lowpart (SImode, operands[1]);
operands[8] = gen_lowpart (SImode, operands[2]);")
(define_insn "*or_not_di_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(ior:DI (not:DI (match_operand:DI 1 "register_operand" "r,b"))
(match_operand:DI 2 "register_operand" "r,b")))]
"TARGET_ARCH64"
"@
orn\\t%2, %1, %0
fornot1\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_insn "*or_not_si"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(ior:SI (not:SI (match_operand:SI 1 "register_operand" "r,d"))
(match_operand:SI 2 "register_operand" "r,d")))]
""
"@
orn\\t%2, %1, %0
fornot1s\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_expand "xordi3"
[(set (match_operand:DI 0 "register_operand" "")
(xor:DI (match_operand:DI 1 "arith_double_operand" "")
(match_operand:DI 2 "arith_double_operand" "")))]
""
"")
(define_insn "*xordi3_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(xor:DI (match_operand:DI 1 "arith_double_operand" "%r,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b")))]
"! TARGET_ARCH64"
"@
#
fxor\\t%1, %2, %0"
[(set_attr "length" "2,1")
(set_attr "type" "ialu,fp")])
(define_insn "*xordi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(xor:DI (match_operand:DI 1 "arith_double_operand" "%rJ,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b")))]
"TARGET_ARCH64"
"@
xor\\t%r1, %2, %0
fxor\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_insn "*xordi3_sp64_dbl"
[(set (match_operand:DI 0 "register_operand" "=r")
(xor:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:DI 2 "const64_operand" "")))]
"(TARGET_ARCH64
&& HOST_BITS_PER_WIDE_INT != 64)"
"xor\\t%1, %2, %0"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_insn "xorsi3"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(xor:SI (match_operand:SI 1 "arith_operand" "%rJ,d")
(match_operand:SI 2 "arith_operand" "rI,d")))]
""
"@
xor\\t%r1, %2, %0
fxors\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "" "")))
(clobber (match_operand:SI 3 "register_operand" ""))]
"GET_CODE (operands[2]) == CONST_INT
&& !SMALL_INT32 (operands[2])
&& (INTVAL (operands[2]) & 0x3ff) == 0x3ff"
[(set (match_dup 3) (match_dup 4))
(set (match_dup 0) (not:SI (xor:SI (match_dup 3) (match_dup 1))))]
"
{
operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff);
}")
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(not:SI (xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "" ""))))
(clobber (match_operand:SI 3 "register_operand" ""))]
"GET_CODE (operands[2]) == CONST_INT
&& !SMALL_INT32 (operands[2])
&& (INTVAL (operands[2]) & 0x3ff) == 0x3ff"
[(set (match_dup 3) (match_dup 4))
(set (match_dup 0) (xor:SI (match_dup 3) (match_dup 1)))]
"
{
operands[4] = GEN_INT (~INTVAL (operands[2]) & 0xffffffff);
}")
;; xnor patterns. Note that (a ^ ~b) == (~a ^ b) == ~(a ^ b).
;; Combine now canonicalizes to the rightmost expression.
(define_insn "*xor_not_di_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(not:DI (xor:DI (match_operand:DI 1 "register_operand" "r,b")
(match_operand:DI 2 "register_operand" "r,b"))))]
"! TARGET_ARCH64"
"@
#
fxnor\\t%1, %2, %0"
[(set_attr "length" "2,1")
(set_attr "type" "ialu,fp")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(not:DI (xor:DI (match_operand:DI 1 "register_operand" "")
(match_operand:DI 2 "register_operand" ""))))]
"! TARGET_ARCH64
&& reload_completed
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))"
[(set (match_dup 3) (not:SI (xor:SI (match_dup 4) (match_dup 5))))
(set (match_dup 6) (not:SI (xor:SI (match_dup 7) (match_dup 8))))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
operands[3] = gen_highpart (SImode, operands[0]);
operands[4] = gen_highpart (SImode, operands[1]);
operands[5] = gen_highpart (SImode, operands[2]);
operands[6] = gen_lowpart (SImode, operands[0]);
operands[7] = gen_lowpart (SImode, operands[1]);
operands[8] = gen_lowpart (SImode, operands[2]);")
(define_insn "*xor_not_di_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(not:DI (xor:DI (match_operand:DI 1 "reg_or_0_operand" "rJ,b")
(match_operand:DI 2 "arith_double_operand" "rHI,b"))))]
"TARGET_ARCH64"
"@
xnor\\t%r1, %2, %0
fxnor\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
(define_insn "*xor_not_si"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(not:SI (xor:SI (match_operand:SI 1 "reg_or_0_operand" "rJ,d")
(match_operand:SI 2 "arith_operand" "rI,d"))))]
""
"@
xnor\\t%r1, %2, %0
fxnors\\t%1, %2, %0"
[(set_attr "type" "ialu,fp")
(set_attr "length" "1,1")])
;; These correspond to the above in the case where we also (or only)
;; want to set the condition code.
(define_insn "*cmp_cc_arith_op"
[(set (reg:CC 100)
(compare:CC
(match_operator:SI 2 "cc_arithop"
[(match_operand:SI 0 "arith_operand" "%r")
(match_operand:SI 1 "arith_operand" "rI")])
(const_int 0)))]
"! TARGET_LIVE_G0"
"%A2cc\\t%0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_arith_op"
[(set (reg:CCX 100)
(compare:CCX
(match_operator:DI 2 "cc_arithop"
[(match_operand:DI 0 "arith_double_operand" "%r")
(match_operand:DI 1 "arith_double_operand" "rHI")])
(const_int 0)))]
"TARGET_ARCH64"
"%A2cc\\t%0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_arith_op_set"
[(set (reg:CC 100)
(compare:CC
(match_operator:SI 3 "cc_arithop"
[(match_operand:SI 1 "arith_operand" "%r")
(match_operand:SI 2 "arith_operand" "rI")])
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(match_dup 3))]
""
"%A3cc\\t%1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_arith_op_set"
[(set (reg:CCX 100)
(compare:CCX
(match_operator:DI 3 "cc_arithop"
[(match_operand:DI 1 "arith_double_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")])
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(match_dup 3))]
"TARGET_ARCH64"
"%A3cc\\t%1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_xor_not"
[(set (reg:CC 100)
(compare:CC
(not:SI (xor:SI (match_operand:SI 0 "reg_or_0_operand" "%rJ")
(match_operand:SI 1 "arith_operand" "rI")))
(const_int 0)))]
"! TARGET_LIVE_G0"
"xnorcc\\t%r0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_xor_not"
[(set (reg:CCX 100)
(compare:CCX
(not:DI (xor:DI (match_operand:DI 0 "reg_or_0_operand" "%rJ")
(match_operand:DI 1 "arith_double_operand" "rHI")))
(const_int 0)))]
"TARGET_ARCH64"
"xnorcc\\t%r0, %1, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_xor_not_set"
[(set (reg:CC 100)
(compare:CC
(not:SI (xor:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ")
(match_operand:SI 2 "arith_operand" "rI")))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(not:SI (xor:SI (match_dup 1) (match_dup 2))))]
""
"xnorcc\\t%r1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_xor_not_set"
[(set (reg:CCX 100)
(compare:CCX
(not:DI (xor:DI (match_operand:DI 1 "reg_or_0_operand" "%rJ")
(match_operand:DI 2 "arith_double_operand" "rHI")))
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(not:DI (xor:DI (match_dup 1) (match_dup 2))))]
"TARGET_ARCH64"
"xnorcc\\t%r1, %2, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_arith_op_not"
[(set (reg:CC 100)
(compare:CC
(match_operator:SI 2 "cc_arithopn"
[(not:SI (match_operand:SI 0 "arith_operand" "rI"))
(match_operand:SI 1 "reg_or_0_operand" "rJ")])
(const_int 0)))]
"! TARGET_LIVE_G0"
"%B2cc\\t%r1, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_arith_op_not"
[(set (reg:CCX 100)
(compare:CCX
(match_operator:DI 2 "cc_arithopn"
[(not:DI (match_operand:DI 0 "arith_double_operand" "rHI"))
(match_operand:DI 1 "reg_or_0_operand" "rJ")])
(const_int 0)))]
"TARGET_ARCH64"
"%B2cc\\t%r1, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_arith_op_not_set"
[(set (reg:CC 100)
(compare:CC
(match_operator:SI 3 "cc_arithopn"
[(not:SI (match_operand:SI 1 "arith_operand" "rI"))
(match_operand:SI 2 "reg_or_0_operand" "rJ")])
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(match_dup 3))]
""
"%B3cc\\t%r2, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_arith_op_not_set"
[(set (reg:CCX 100)
(compare:CCX
(match_operator:DI 3 "cc_arithopn"
[(not:DI (match_operand:DI 1 "arith_double_operand" "rHI"))
(match_operand:DI 2 "reg_or_0_operand" "rJ")])
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(match_dup 3))]
"TARGET_ARCH64"
"%B3cc\\t%r2, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;; We cannot use the "neg" pseudo insn because the Sun assembler
;; does not know how to make it work for constants.
(define_expand "negdi2"
[(set (match_operand:DI 0 "register_operand" "=r")
(neg:DI (match_operand:DI 1 "register_operand" "r")))]
""
"
{
if (! TARGET_ARCH64)
{
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2,
gen_rtx_SET (VOIDmode, operand0,
gen_rtx_NEG (DImode, operand1)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (CCmode, SPARC_ICC_REG)))));
DONE;
}
}")
(define_insn "*negdi2_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
(neg:DI (match_operand:DI 1 "register_operand" "r")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64
&& ! TARGET_LIVE_G0"
"#"
[(set_attr "type" "unary")
(set_attr "length" "2")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(neg:DI (match_operand:DI 1 "register_operand" "")))
(clobber (reg:CC 100))]
"! TARGET_ARCH64
&& ! TARGET_LIVE_G0
&& reload_completed"
[(parallel [(set (reg:CC_NOOV 100)
(compare:CC_NOOV (minus:SI (const_int 0) (match_dup 5))
(const_int 0)))
(set (match_dup 4) (minus:SI (const_int 0) (match_dup 5)))])
(set (match_dup 2) (minus:SI (minus:SI (const_int 0) (match_dup 3))
(ltu:SI (reg:CC 100) (const_int 0))))]
"operands[2] = gen_highpart (SImode, operands[0]);
operands[3] = gen_highpart (SImode, operands[1]);
operands[4] = gen_lowpart (SImode, operands[0]);
operands[5] = gen_lowpart (SImode, operands[1]);")
(define_insn "*negdi2_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(neg:DI (match_operand:DI 1 "register_operand" "r")))]
"TARGET_ARCH64"
"sub\\t%%g0, %1, %0"
[(set_attr "type" "unary")
(set_attr "length" "1")])
(define_expand "negsi2"
[(set (match_operand:SI 0 "register_operand" "")
(neg:SI (match_operand:SI 1 "arith_operand" "")))]
""
"
{
if (TARGET_LIVE_G0)
{
rtx zero_reg = gen_reg_rtx (SImode);
emit_insn (gen_rtx_SET (VOIDmode, zero_reg, const0_rtx));
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_MINUS (SImode, zero_reg,
operands[1])));
DONE;
}
}")
(define_insn "*negsi2_not_liveg0"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (match_operand:SI 1 "arith_operand" "rI")))]
"! TARGET_LIVE_G0"
"sub\\t%%g0, %1, %0"
[(set_attr "type" "unary")
(set_attr "length" "1")])
(define_insn "*cmp_cc_neg"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (neg:SI (match_operand:SI 0 "arith_operand" "rI"))
(const_int 0)))]
"! TARGET_LIVE_G0"
"subcc\\t%%g0, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_neg"
[(set (reg:CCX_NOOV 100)
(compare:CCX_NOOV (neg:DI (match_operand:DI 0 "arith_double_operand" "rHI"))
(const_int 0)))]
"TARGET_ARCH64"
"subcc\\t%%g0, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_set_neg"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (neg:SI (match_operand:SI 1 "arith_operand" "rI"))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (match_dup 1)))]
"! TARGET_LIVE_G0"
"subcc\\t%%g0, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_set_neg"
[(set (reg:CCX_NOOV 100)
(compare:CCX_NOOV (neg:DI (match_operand:DI 1 "arith_double_operand" "rHI"))
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(neg:DI (match_dup 1)))]
"TARGET_ARCH64"
"subcc\\t%%g0, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;; We cannot use the "not" pseudo insn because the Sun assembler
;; does not know how to make it work for constants.
(define_expand "one_cmpldi2"
[(set (match_operand:DI 0 "register_operand" "")
(not:DI (match_operand:DI 1 "register_operand" "")))]
""
"")
(define_insn "*one_cmpldi2_sp32"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(not:DI (match_operand:DI 1 "register_operand" "r,b")))]
"! TARGET_ARCH64"
"@
#
fnot1\\t%1, %0"
[(set_attr "type" "unary,fp")
(set_attr "length" "2,1")])
(define_split
[(set (match_operand:DI 0 "register_operand" "")
(not:DI (match_operand:DI 1 "register_operand" "")))]
"! TARGET_ARCH64
&& reload_completed
&& ((GET_CODE (operands[0]) == REG
&& REGNO (operands[0]) < 32)
|| (GET_CODE (operands[0]) == SUBREG
&& GET_CODE (SUBREG_REG (operands[0])) == REG
&& REGNO (SUBREG_REG (operands[0])) < 32))"
[(set (match_dup 2) (not:SI (xor:SI (match_dup 3) (const_int 0))))
(set (match_dup 4) (not:SI (xor:SI (match_dup 5) (const_int 0))))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
operands[2] = gen_highpart (SImode, operands[0]);
operands[3] = gen_highpart (SImode, operands[1]);
operands[4] = gen_lowpart (SImode, operands[0]);
operands[5] = gen_lowpart (SImode, operands[1]);")
(define_insn "*one_cmpldi2_sp64"
[(set (match_operand:DI 0 "register_operand" "=r,b")
(not:DI (match_operand:DI 1 "arith_double_operand" "rHI,b")))]
"TARGET_ARCH64"
"@
xnor\\t%%g0, %1, %0
fnot1\\t%1, %0"
[(set_attr "type" "unary,fp")
(set_attr "length" "1")])
(define_expand "one_cmplsi2"
[(set (match_operand:SI 0 "register_operand" "")
(not:SI (match_operand:SI 1 "arith_operand" "")))]
""
"
{
if (TARGET_LIVE_G0
&& GET_CODE (operands[1]) == CONST_INT)
{
rtx zero_reg = gen_reg_rtx (SImode);
emit_insn (gen_rtx_SET (VOIDmode, zero_reg, const0_rtx));
emit_insn (gen_rtx_SET (VOIDmode,
operands[0],
gen_rtx_NOT (SImode,
gen_rtx_XOR (SImode,
zero_reg,
operands[1]))));
DONE;
}
}")
(define_insn "*one_cmplsi2_not_liveg0"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(not:SI (match_operand:SI 1 "arith_operand" "rI,d")))]
"! TARGET_LIVE_G0"
"@
xnor\\t%%g0, %1, %0
fnot1s\\t%1, %0"
[(set_attr "type" "unary,fp")
(set_attr "length" "1,1")])
(define_insn "*one_cmplsi2_liveg0"
[(set (match_operand:SI 0 "register_operand" "=r,d")
(not:SI (match_operand:SI 1 "arith_operand" "r,d")))]
"TARGET_LIVE_G0"
"@
xnor\\t%1, 0, %0
fnot1s\\t%1, %0"
[(set_attr "type" "unary,fp")
(set_attr "length" "1,1")])
(define_insn "*cmp_cc_not"
[(set (reg:CC 100)
(compare:CC (not:SI (match_operand:SI 0 "arith_operand" "rI"))
(const_int 0)))]
"! TARGET_LIVE_G0"
"xnorcc\\t%%g0, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_not"
[(set (reg:CCX 100)
(compare:CCX (not:DI (match_operand:DI 0 "arith_double_operand" "rHI"))
(const_int 0)))]
"TARGET_ARCH64"
"xnorcc\\t%%g0, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_set_not"
[(set (reg:CC 100)
(compare:CC (not:SI (match_operand:SI 1 "arith_operand" "rI"))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(not:SI (match_dup 1)))]
"! TARGET_LIVE_G0"
"xnorcc\\t%%g0, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_ccx_set_not"
[(set (reg:CCX 100)
(compare:CCX (not:DI (match_operand:DI 1 "arith_double_operand" "rHI"))
(const_int 0)))
(set (match_operand:DI 0 "register_operand" "=r")
(not:DI (match_dup 1)))]
"TARGET_ARCH64"
"xnorcc\\t%%g0, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
;; Floating point arithmetic instructions.
(define_insn "addtf3"
[(set (match_operand:TF 0 "register_operand" "=e")
(plus:TF (match_operand:TF 1 "register_operand" "e")
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"faddq\\t%1, %2, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "adddf3"
[(set (match_operand:DF 0 "register_operand" "=e")
(plus:DF (match_operand:DF 1 "register_operand" "e")
(match_operand:DF 2 "register_operand" "e")))]
"TARGET_FPU"
"faddd\\t%1, %2, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "addsf3"
[(set (match_operand:SF 0 "register_operand" "=f")
(plus:SF (match_operand:SF 1 "register_operand" "f")
(match_operand:SF 2 "register_operand" "f")))]
"TARGET_FPU"
"fadds\\t%1, %2, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "subtf3"
[(set (match_operand:TF 0 "register_operand" "=e")
(minus:TF (match_operand:TF 1 "register_operand" "e")
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fsubq\\t%1, %2, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "subdf3"
[(set (match_operand:DF 0 "register_operand" "=e")
(minus:DF (match_operand:DF 1 "register_operand" "e")
(match_operand:DF 2 "register_operand" "e")))]
"TARGET_FPU"
"fsubd\\t%1, %2, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "subsf3"
[(set (match_operand:SF 0 "register_operand" "=f")
(minus:SF (match_operand:SF 1 "register_operand" "f")
(match_operand:SF 2 "register_operand" "f")))]
"TARGET_FPU"
"fsubs\\t%1, %2, %0"
[(set_attr "type" "fp")
(set_attr "length" "1")])
(define_insn "multf3"
[(set (match_operand:TF 0 "register_operand" "=e")
(mult:TF (match_operand:TF 1 "register_operand" "e")
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fmulq\\t%1, %2, %0"
[(set_attr "type" "fpmul")
(set_attr "length" "1")])
(define_insn "muldf3"
[(set (match_operand:DF 0 "register_operand" "=e")
(mult:DF (match_operand:DF 1 "register_operand" "e")
(match_operand:DF 2 "register_operand" "e")))]
"TARGET_FPU"
"fmuld\\t%1, %2, %0"
[(set_attr "type" "fpmul")
(set_attr "length" "1")])
(define_insn "mulsf3"
[(set (match_operand:SF 0 "register_operand" "=f")
(mult:SF (match_operand:SF 1 "register_operand" "f")
(match_operand:SF 2 "register_operand" "f")))]
"TARGET_FPU"
"fmuls\\t%1, %2, %0"
[(set_attr "type" "fpmul")
(set_attr "length" "1")])
(define_insn "*muldf3_extend"
[(set (match_operand:DF 0 "register_operand" "=e")
(mult:DF (float_extend:DF (match_operand:SF 1 "register_operand" "f"))
(float_extend:DF (match_operand:SF 2 "register_operand" "f"))))]
"(TARGET_V8 || TARGET_V9) && TARGET_FPU"
"fsmuld\\t%1, %2, %0"
[(set_attr "type" "fpmul")
(set_attr "length" "1")])
(define_insn "*multf3_extend"
[(set (match_operand:TF 0 "register_operand" "=e")
(mult:TF (float_extend:TF (match_operand:DF 1 "register_operand" "e"))
(float_extend:TF (match_operand:DF 2 "register_operand" "e"))))]
"(TARGET_V8 || TARGET_V9) && TARGET_FPU && TARGET_HARD_QUAD"
"fdmulq\\t%1, %2, %0"
[(set_attr "type" "fpmul")
(set_attr "length" "1")])
;; don't have timing for quad-prec. divide.
(define_insn "divtf3"
[(set (match_operand:TF 0 "register_operand" "=e")
(div:TF (match_operand:TF 1 "register_operand" "e")
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fdivq\\t%1, %2, %0"
[(set_attr "type" "fpdivd")
(set_attr "length" "1")])
(define_insn "divdf3"
[(set (match_operand:DF 0 "register_operand" "=e")
(div:DF (match_operand:DF 1 "register_operand" "e")
(match_operand:DF 2 "register_operand" "e")))]
"TARGET_FPU"
"fdivd\\t%1, %2, %0"
[(set_attr "type" "fpdivd")
(set_attr "length" "1")])
(define_insn "divsf3"
[(set (match_operand:SF 0 "register_operand" "=f")
(div:SF (match_operand:SF 1 "register_operand" "f")
(match_operand:SF 2 "register_operand" "f")))]
"TARGET_FPU"
"fdivs\\t%1, %2, %0"
[(set_attr "type" "fpdivs")
(set_attr "length" "1")])
(define_expand "negtf2"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(neg:TF (match_operand:TF 1 "register_operand" "0,e")))]
"TARGET_FPU"
"")
(define_insn "*negtf2_notv9"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(neg:TF (match_operand:TF 1 "register_operand" "0,e")))]
; We don't use quad float insns here so we don't need TARGET_HARD_QUAD.
"TARGET_FPU
&& ! TARGET_V9"
"@
fnegs\\t%0, %0
#"
[(set_attr "type" "fpmove")
(set_attr "length" "1,2")])
(define_split
[(set (match_operand:TF 0 "register_operand" "")
(neg:TF (match_operand:TF 1 "register_operand" "")))]
"TARGET_FPU
&& ! TARGET_V9
&& reload_completed
&& sparc_absnegfloat_split_legitimate (operands[0], operands[1])"
[(set (match_dup 2) (neg:SF (match_dup 3)))
(set (match_dup 4) (match_dup 5))
(set (match_dup 6) (match_dup 7))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]));
operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]));
operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1);
operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1);
operands[6] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2);
operands[7] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);")
(define_insn "*negtf2_v9"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(neg:TF (match_operand:TF 1 "register_operand" "0,e")))]
; We don't use quad float insns here so we don't need TARGET_HARD_QUAD.
"TARGET_FPU && TARGET_V9"
"@
fnegd\\t%0, %0
#"
[(set_attr "type" "fpmove")
(set_attr "length" "1,2")])
(define_split
[(set (match_operand:TF 0 "register_operand" "")
(neg:TF (match_operand:TF 1 "register_operand" "")))]
"TARGET_FPU
&& TARGET_V9
&& reload_completed
&& sparc_absnegfloat_split_legitimate (operands[0], operands[1])"
[(set (match_dup 2) (neg:DF (match_dup 3)))
(set (match_dup 4) (match_dup 5))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
operands[2] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]));
operands[3] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]));
operands[4] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2);
operands[5] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);")
(define_expand "negdf2"
[(set (match_operand:DF 0 "register_operand" "")
(neg:DF (match_operand:DF 1 "register_operand" "")))]
"TARGET_FPU"
"")
(define_insn "*negdf2_notv9"
[(set (match_operand:DF 0 "register_operand" "=e,e")
(neg:DF (match_operand:DF 1 "register_operand" "0,e")))]
"TARGET_FPU && ! TARGET_V9"
"@
fnegs\\t%0, %0
#"
[(set_attr "type" "fpmove")
(set_attr "length" "1,2")])
(define_split
[(set (match_operand:DF 0 "register_operand" "")
(neg:DF (match_operand:DF 1 "register_operand" "")))]
"TARGET_FPU
&& ! TARGET_V9
&& reload_completed
&& sparc_absnegfloat_split_legitimate (operands[0], operands[1])"
[(set (match_dup 2) (neg:SF (match_dup 3)))
(set (match_dup 4) (match_dup 5))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]));
operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]));
operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1);
operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1);")
(define_insn "*negdf2_v9"
[(set (match_operand:DF 0 "register_operand" "=e")
(neg:DF (match_operand:DF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_V9"
"fnegd\\t%1, %0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_insn "negsf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(neg:SF (match_operand:SF 1 "register_operand" "f")))]
"TARGET_FPU"
"fnegs\\t%1, %0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_expand "abstf2"
[(set (match_operand:TF 0 "register_operand" "")
(abs:TF (match_operand:TF 1 "register_operand" "")))]
"TARGET_FPU"
"")
(define_insn "*abstf2_notv9"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(abs:TF (match_operand:TF 1 "register_operand" "0,e")))]
; We don't use quad float insns here so we don't need TARGET_HARD_QUAD.
"TARGET_FPU && ! TARGET_V9"
"@
fabss\\t%0, %0
#"
[(set_attr "type" "fpmove")
(set_attr "length" "1,2")])
(define_split
[(set (match_operand:TF 0 "register_operand" "=e,e")
(abs:TF (match_operand:TF 1 "register_operand" "0,e")))]
"TARGET_FPU
&& ! TARGET_V9
&& reload_completed
&& sparc_absnegfloat_split_legitimate (operands[0], operands[1])"
[(set (match_dup 2) (abs:SF (match_dup 3)))
(set (match_dup 4) (match_dup 5))
(set (match_dup 6) (match_dup 7))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]));
operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]));
operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1);
operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1);
operands[6] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2);
operands[7] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);")
(define_insn "*abstf2_hq_v9"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(abs:TF (match_operand:TF 1 "register_operand" "0,e")))]
"TARGET_FPU && TARGET_V9 && TARGET_HARD_QUAD"
"@
fabsd\\t%0, %0
fabsq\\t%1, %0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_insn "*abstf2_v9"
[(set (match_operand:TF 0 "register_operand" "=e,e")
(abs:TF (match_operand:TF 1 "register_operand" "0,e")))]
"TARGET_FPU && TARGET_V9 && !TARGET_HARD_QUAD"
"@
fabsd\\t%0, %0
#"
[(set_attr "type" "fpmove")
(set_attr "length" "1,2")])
(define_split
[(set (match_operand:TF 0 "register_operand" "=e,e")
(abs:TF (match_operand:TF 1 "register_operand" "0,e")))]
"TARGET_FPU
&& TARGET_V9
&& reload_completed
&& sparc_absnegfloat_split_legitimate (operands[0], operands[1])"
[(set (match_dup 2) (abs:DF (match_dup 3)))
(set (match_dup 4) (match_dup 5))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
operands[2] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]));
operands[3] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]));
operands[4] = gen_rtx_raw_REG (DFmode, REGNO (operands[0]) + 2);
operands[5] = gen_rtx_raw_REG (DFmode, REGNO (operands[1]) + 2);")
(define_expand "absdf2"
[(set (match_operand:DF 0 "register_operand" "")
(abs:DF (match_operand:DF 1 "register_operand" "")))]
"TARGET_FPU"
"")
(define_insn "*absdf2_notv9"
[(set (match_operand:DF 0 "register_operand" "=e,e")
(abs:DF (match_operand:DF 1 "register_operand" "0,e")))]
"TARGET_FPU && ! TARGET_V9"
"@
fabss\\t%0, %0
#"
[(set_attr "type" "fpmove")
(set_attr "length" "1,2")])
(define_split
[(set (match_operand:DF 0 "register_operand" "=e,e")
(abs:DF (match_operand:DF 1 "register_operand" "0,e")))]
"TARGET_FPU
&& ! TARGET_V9
&& reload_completed
&& sparc_absnegfloat_split_legitimate (operands[0], operands[1])"
[(set (match_dup 2) (abs:SF (match_dup 3)))
(set (match_dup 4) (match_dup 5))]
"if (GET_CODE (operands[0]) == SUBREG)
operands[0] = alter_subreg (operands[0]);
if (GET_CODE (operands[1]) == SUBREG)
operands[1] = alter_subreg (operands[1]);
operands[2] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]));
operands[3] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]));
operands[4] = gen_rtx_raw_REG (SFmode, REGNO (operands[0]) + 1);
operands[5] = gen_rtx_raw_REG (SFmode, REGNO (operands[1]) + 1);")
(define_insn "*absdf2_v9"
[(set (match_operand:DF 0 "register_operand" "=e")
(abs:DF (match_operand:DF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_V9"
"fabsd\\t%1, %0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_insn "abssf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(abs:SF (match_operand:SF 1 "register_operand" "f")))]
"TARGET_FPU"
"fabss\\t%1, %0"
[(set_attr "type" "fpmove")
(set_attr "length" "1")])
(define_insn "sqrttf2"
[(set (match_operand:TF 0 "register_operand" "=e")
(sqrt:TF (match_operand:TF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fsqrtq\\t%1, %0"
[(set_attr "type" "fpsqrt")
(set_attr "length" "1")])
(define_insn "sqrtdf2"
[(set (match_operand:DF 0 "register_operand" "=e")
(sqrt:DF (match_operand:DF 1 "register_operand" "e")))]
"TARGET_FPU"
"fsqrtd\\t%1, %0"
[(set_attr "type" "fpsqrt")
(set_attr "length" "1")])
(define_insn "sqrtsf2"
[(set (match_operand:SF 0 "register_operand" "=f")
(sqrt:SF (match_operand:SF 1 "register_operand" "f")))]
"TARGET_FPU"
"fsqrts\\t%1, %0"
[(set_attr "type" "fpsqrt")
(set_attr "length" "1")])
;;- arithmetic shift instructions
(define_insn "ashlsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 31)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
return \"sll\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; We special case multiplication by two, as add can be done
;; in both ALUs, while shift only in IEU0 on UltraSPARC.
(define_insn "*ashlsi3_const1"
[(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (match_operand:SI 1 "register_operand" "r")
(const_int 1)))]
""
"add\\t%1, %1, %0"
[(set_attr "type" "binary")
(set_attr "length" "1")])
(define_expand "ashldi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(ashift:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_ARCH64 || TARGET_V8PLUS"
"
{
if (! TARGET_ARCH64)
{
if (GET_CODE (operands[2]) == CONST_INT)
FAIL;
emit_insn (gen_ashldi3_v8plus (operands[0], operands[1], operands[2]));
DONE;
}
}")
;; We special case multiplication by two, as add can be done
;; in both ALUs, while shift only in IEU0 on UltraSPARC.
(define_insn "*ashldi3_const1"
[(set (match_operand:DI 0 "register_operand" "=r")
(ashift:DI (match_operand:DI 1 "register_operand" "r")
(const_int 1)))]
"TARGET_ARCH64"
"add\\t%1, %1, %0"
[(set_attr "type" "binary")
(set_attr "length" "1")])
(define_insn "*ashldi3_sp64"
[(set (match_operand:DI 0 "register_operand" "=r")
(ashift:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_ARCH64"
"*
{
if (GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 63)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
return \"sllx\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; XXX UGH!
(define_insn "ashldi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=&h,&h,r")
(ashift:DI (match_operand:DI 1 "arith_operand" "rI,0,rI")
(match_operand:SI 2 "arith_operand" "rI,rI,rI")))
(clobber (match_scratch:SI 3 "=X,X,&h"))]
"TARGET_V8PLUS"
"*return sparc_v8plus_shift (operands, insn, \"sllx\");"
[(set_attr "length" "5,5,6")])
;; Optimize (1LL<<x)-1
;; XXX this also needs to be fixed to handle equal subregs
;; XXX first before we could re-enable it.
(define_insn ""
[(set (match_operand:DI 0 "register_operand" "=h")
(plus:DI (ashift:DI (const_int 1)
(match_operand:SI 2 "arith_operand" "rI"))
(const_int -1)))]
"0 && TARGET_V8PLUS"
"*
{
if (GET_CODE (operands[2]) == REG && REGNO (operands[2]) == REGNO (operands[0]))
return \"mov 1,%L0\;sllx %L0,%2,%L0\;sub %L0,1,%L0\;srlx %L0,32,%H0\";
return \"mov 1,%H0\;sllx %H0,%2,%L0\;sub %L0,1,%L0\;srlx %L0,32,%H0\";
}"
[(set_attr "length" "4")])
(define_insn "*cmp_cc_ashift_1"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (ashift:SI (match_operand:SI 0 "register_operand" "r")
(const_int 1))
(const_int 0)))]
"! TARGET_LIVE_G0"
"addcc\\t%0, %0, %%g0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "*cmp_cc_set_ashift_1"
[(set (reg:CC_NOOV 100)
(compare:CC_NOOV (ashift:SI (match_operand:SI 1 "register_operand" "r")
(const_int 1))
(const_int 0)))
(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (match_dup 1) (const_int 1)))]
""
"addcc\\t%1, %1, %0"
[(set_attr "type" "compare")
(set_attr "length" "1")])
(define_insn "ashrsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 31)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
return \"sra\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
(define_insn "*ashrsi3_extend"
[(set (match_operand:DI 0 "register_operand" "=r")
(sign_extend:DI (ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "r"))))]
"TARGET_ARCH64"
"sra\\t%1, %2, %0"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; This handles the case as above, but with constant shift instead of
;; register. Combiner "simplifies" it for us a little bit though.
(define_insn "*ashrsi3_extend2"
[(set (match_operand:DI 0 "register_operand" "=r")
(ashiftrt:DI (ashift:DI (subreg:DI (match_operand:SI 1 "register_operand" "r") 0)
(const_int 32))
(match_operand:SI 2 "small_int_or_double" "n")))]
"TARGET_ARCH64
&& ((GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) >= 32 && INTVAL (operands[2]) < 64)
|| (GET_CODE (operands[2]) == CONST_DOUBLE
&& !CONST_DOUBLE_HIGH (operands[2])
&& CONST_DOUBLE_LOW (operands[2]) >= 32
&& CONST_DOUBLE_LOW (operands[2]) < 64))"
"*
{
operands[2] = GEN_INT (INTVAL (operands[2]) - 32);
return \"sra\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
(define_expand "ashrdi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(ashiftrt:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_ARCH64 || TARGET_V8PLUS"
"
{
if (! TARGET_ARCH64)
{
if (GET_CODE (operands[2]) == CONST_INT)
FAIL; /* prefer generic code in this case */
emit_insn (gen_ashrdi3_v8plus (operands[0], operands[1], operands[2]));
DONE;
}
}")
(define_insn ""
[(set (match_operand:DI 0 "register_operand" "=r")
(ashiftrt:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_ARCH64"
"*
{
if (GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 63)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
return \"srax\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; XXX
(define_insn "ashrdi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=&h,&h,r")
(ashiftrt:DI (match_operand:DI 1 "arith_operand" "rI,0,rI")
(match_operand:SI 2 "arith_operand" "rI,rI,rI")))
(clobber (match_scratch:SI 3 "=X,X,&h"))]
"TARGET_V8PLUS"
"*return sparc_v8plus_shift (operands, insn, \"srax\");"
[(set_attr "length" "5,5,6")])
(define_insn "lshrsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 31)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
return \"srl\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; This handles the case where
;; (zero_extend:DI (lshiftrt:SI (match_operand:SI) (match_operand:SI))),
;; but combiner "simplifies" it for us.
(define_insn "*lshrsi3_extend"
[(set (match_operand:DI 0 "register_operand" "=r")
(and:DI (subreg:DI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "r")) 0)
(match_operand 3 "" "")))]
"TARGET_ARCH64
&& ((GET_CODE (operands[3]) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (operands[3]) == 0
&& CONST_DOUBLE_LOW (operands[3]) == 0xffffffff)
#if HOST_BITS_PER_WIDE_INT >= 64
|| (GET_CODE (operands[3]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[3]) == 0xffffffff)
#endif
)"
"srl\\t%1, %2, %0"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; This handles the case where
;; (lshiftrt:DI (zero_extend:DI (match_operand:SI)) (const_int >=0 < 32))
;; but combiner "simplifies" it for us.
(define_insn "*lshrsi3_extend2"
[(set (match_operand:DI 0 "register_operand" "=r")
(zero_extract:DI (subreg:DI (match_operand:SI 1 "register_operand" "r") 0)
(match_operand 2 "small_int_or_double" "n")
(const_int 32)))]
"TARGET_ARCH64
&& ((GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) < 32)
|| (GET_CODE (operands[2]) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (operands[2]) == 0
&& (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (operands[2]) < 32))"
"*
{
operands[2] = GEN_INT (32 - INTVAL (operands[2]));
return \"srl\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
(define_expand "lshrdi3"
[(set (match_operand:DI 0 "register_operand" "=r")
(lshiftrt:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_ARCH64 || TARGET_V8PLUS"
"
{
if (! TARGET_ARCH64)
{
if (GET_CODE (operands[2]) == CONST_INT)
FAIL;
emit_insn (gen_lshrdi3_v8plus (operands[0], operands[1], operands[2]));
DONE;
}
}")
(define_insn ""
[(set (match_operand:DI 0 "register_operand" "=r")
(lshiftrt:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))]
"TARGET_ARCH64"
"*
{
if (GET_CODE (operands[2]) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > 63)
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
return \"srlx\\t%1, %2, %0\";
}"
[(set_attr "type" "shift")
(set_attr "length" "1")])
;; XXX
(define_insn "lshrdi3_v8plus"
[(set (match_operand:DI 0 "register_operand" "=&h,&h,r")
(lshiftrt:DI (match_operand:DI 1 "arith_operand" "rI,0,rI")
(match_operand:SI 2 "arith_operand" "rI,rI,rI")))
(clobber (match_scratch:SI 3 "=X,X,&h"))]
"TARGET_V8PLUS"
"*return sparc_v8plus_shift (operands, insn, \"srlx\");"
[(set_attr "length" "5,5,6")])
;; Unconditional and other jump instructions
;; On the Sparc, by setting the annul bit on an unconditional branch, the
;; following insn is never executed. This saves us a nop. Dbx does not
;; handle such branches though, so we only use them when optimizing.
(define_insn "jump"
[(set (pc) (label_ref (match_operand 0 "" "")))]
""
"*
{
/* TurboSparc is reported to have problems with
with
foo: b,a foo
i.e. an empty loop with the annul bit set. The workaround is to use
foo: b foo; nop
instead. */
if (! TARGET_V9 && flag_delayed_branch
&& (insn_addresses[INSN_UID (operands[0])]
== insn_addresses[INSN_UID (insn)]))
return \"b\\t%l0%#\";
else
return TARGET_V9 ? \"ba,pt%*\\t%%xcc, %l0%(\" : \"b%*\\t%l0%(\";
}"
[(set_attr "type" "uncond_branch")])
(define_expand "tablejump"
[(parallel [(set (pc) (match_operand 0 "register_operand" "r"))
(use (label_ref (match_operand 1 "" "")))])]
""
"
{
if (GET_MODE (operands[0]) != CASE_VECTOR_MODE)
abort ();
/* In pic mode, our address differences are against the base of the
table. Add that base value back in; CSE ought to be able to combine
the two address loads. */
if (flag_pic)
{
rtx tmp, tmp2;
tmp = gen_rtx_LABEL_REF (Pmode, operands[1]);
tmp2 = operands[0];
if (CASE_VECTOR_MODE != Pmode)
tmp2 = gen_rtx_SIGN_EXTEND (Pmode, tmp2);
tmp = gen_rtx_PLUS (Pmode, tmp2, tmp);
operands[0] = memory_address (Pmode, tmp);
}
}")
(define_insn "*tablejump_sp32"
[(set (pc) (match_operand:SI 0 "address_operand" "p"))
(use (label_ref (match_operand 1 "" "")))]
"! TARGET_PTR64"
"jmp\\t%a0%#"
[(set_attr "type" "uncond_branch")])
(define_insn "*tablejump_sp64"
[(set (pc) (match_operand:DI 0 "address_operand" "p"))
(use (label_ref (match_operand 1 "" "")))]
"TARGET_PTR64"
"jmp\\t%a0%#"
[(set_attr "type" "uncond_branch")])
;; This pattern recognizes the "instruction" that appears in
;; a function call that wants a structure value,
;; to inform the called function if compiled with Sun CC.
;(define_insn "*unimp_insn"
; [(match_operand:SI 0 "immediate_operand" "")]
; "GET_CODE (operands[0]) == CONST_INT && INTVAL (operands[0]) > 0"
; "unimp\\t%0"
; [(set_attr "type" "marker")])
;;- jump to subroutine
(define_expand "call"
;; Note that this expression is not used for generating RTL.
;; All the RTL is generated explicitly below.
[(call (match_operand 0 "call_operand" "")
(match_operand 3 "" "i"))]
;; operands[2] is next_arg_register
;; operands[3] is struct_value_size_rtx.
""
"
{
rtx fn_rtx, nregs_rtx;
if (GET_MODE (operands[0]) != FUNCTION_MODE)
abort ();
if (GET_CODE (XEXP (operands[0], 0)) == LABEL_REF)
{
/* This is really a PIC sequence. We want to represent
it as a funny jump so its delay slots can be filled.
??? But if this really *is* a CALL, will not it clobber the
call-clobbered registers? We lose this if it is a JUMP_INSN.
Why cannot we have delay slots filled if it were a CALL? */
if (! TARGET_ARCH64 && INTVAL (operands[3]) != 0)
emit_jump_insn
(gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (3,
gen_rtx_SET (VOIDmode, pc_rtx,
XEXP (operands[0], 0)),
GEN_INT (INTVAL (operands[3]) & 0xfff),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (Pmode, 15)))));
else
emit_jump_insn
(gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode, pc_rtx,
XEXP (operands[0], 0)),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (Pmode, 15)))));
goto finish_call;
}
fn_rtx = operands[0];
/* Count the number of parameter registers being used by this call.
if that argument is NULL, it means we are using them all, which
means 6 on the sparc. */
#if 0
if (operands[2])
nregs_rtx = GEN_INT (REGNO (operands[2]) - 8);
else
nregs_rtx = GEN_INT (6);
#else
nregs_rtx = const0_rtx;
#endif
if (! TARGET_ARCH64 && INTVAL (operands[3]) != 0)
emit_call_insn
(gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (3, gen_rtx_CALL (VOIDmode, fn_rtx, nregs_rtx),
GEN_INT (INTVAL (operands[3]) & 0xfff),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (Pmode, 15)))));
else
emit_call_insn
(gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, gen_rtx_CALL (VOIDmode, fn_rtx, nregs_rtx),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (Pmode, 15)))));
finish_call:
#if 0
/* If this call wants a structure value,
emit an unimp insn to let the called function know about this. */
if (! TARGET_ARCH64 && INTVAL (operands[3]) > 0)
{
rtx insn = emit_insn (operands[3]);
SCHED_GROUP_P (insn) = 1;
}
#endif
DONE;
}")
;; We can't use the same pattern for these two insns, because then registers
;; in the address may not be properly reloaded.
(define_insn "*call_address_sp32"
[(call (mem:SI (match_operand:SI 0 "address_operand" "p"))
(match_operand 1 "" ""))
(clobber (reg:SI 15))]
;;- Do not use operand 1 for most machines.
"! TARGET_PTR64"
"call\\t%a0, %1%#"
[(set_attr "type" "call")])
(define_insn "*call_symbolic_sp32"
[(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s"))
(match_operand 1 "" ""))
(clobber (reg:SI 15))]
;;- Do not use operand 1 for most machines.
"! TARGET_PTR64"
"call\\t%a0, %1%#"
[(set_attr "type" "call")])
(define_insn "*call_address_sp64"
[(call (mem:SI (match_operand:DI 0 "address_operand" "p"))
(match_operand 1 "" ""))
(clobber (reg:DI 15))]
;;- Do not use operand 1 for most machines.
"TARGET_PTR64"
"call\\t%a0, %1%#"
[(set_attr "type" "call")])
(define_insn "*call_symbolic_sp64"
[(call (mem:SI (match_operand:DI 0 "symbolic_operand" "s"))
(match_operand 1 "" ""))
(clobber (reg:DI 15))]
;;- Do not use operand 1 for most machines.
"TARGET_PTR64"
"call\\t%a0, %1%#"
[(set_attr "type" "call")])
;; This is a call that wants a structure value.
;; There is no such critter for v9 (??? we may need one anyway).
(define_insn "*call_address_struct_value_sp32"
[(call (mem:SI (match_operand:SI 0 "address_operand" "p"))
(match_operand 1 "" ""))
(match_operand 2 "immediate_operand" "")
(clobber (reg:SI 15))]
;;- Do not use operand 1 for most machines.
"! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 0"
"call\\t%a0, %1\\n\\tnop\\n\\tunimp\\t%2"
[(set_attr "type" "call_no_delay_slot")])
;; This is a call that wants a structure value.
;; There is no such critter for v9 (??? we may need one anyway).
(define_insn "*call_symbolic_struct_value_sp32"
[(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s"))
(match_operand 1 "" ""))
(match_operand 2 "immediate_operand" "")
(clobber (reg:SI 15))]
;;- Do not use operand 1 for most machines.
"! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 0"
"call\\t%a0, %1\\n\\tnop\\n\\tunimp\\t%2"
[(set_attr "type" "call_no_delay_slot")])
;; This is a call that may want a structure value. This is used for
;; untyped_calls.
(define_insn "*call_address_untyped_struct_value_sp32"
[(call (mem:SI (match_operand:SI 0 "address_operand" "p"))
(match_operand 1 "" ""))
(match_operand 2 "immediate_operand" "")
(clobber (reg:SI 15))]
;;- Do not use operand 1 for most machines.
"! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0"
"call\\t%a0, %1\\n\\tnop\\n\\tnop"
[(set_attr "type" "call_no_delay_slot")])
;; This is a call that wants a structure value.
(define_insn "*call_symbolic_untyped_struct_value_sp32"
[(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s"))
(match_operand 1 "" ""))
(match_operand 2 "immediate_operand" "")
(clobber (reg:SI 15))]
;;- Do not use operand 1 for most machines.
"! TARGET_ARCH64 && GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0"
"call\\t%a0, %1\\n\\tnop\\n\\tnop"
[(set_attr "type" "call_no_delay_slot")])
(define_expand "call_value"
;; Note that this expression is not used for generating RTL.
;; All the RTL is generated explicitly below.
[(set (match_operand 0 "register_operand" "=rf")
(call (match_operand:SI 1 "" "")
(match_operand 4 "" "")))]
;; operand 2 is stack_size_rtx
;; operand 3 is next_arg_register
""
"
{
rtx fn_rtx, nregs_rtx;
rtvec vec;
if (GET_MODE (operands[1]) != FUNCTION_MODE)
abort ();
fn_rtx = operands[1];
#if 0
if (operands[3])
nregs_rtx = GEN_INT (REGNO (operands[3]) - 8);
else
nregs_rtx = GEN_INT (6);
#else
nregs_rtx = const0_rtx;
#endif
vec = gen_rtvec (2,
gen_rtx_SET (VOIDmode, operands[0],
gen_rtx_CALL (VOIDmode, fn_rtx, nregs_rtx)),
gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 15)));
emit_call_insn (gen_rtx_PARALLEL (VOIDmode, vec));
DONE;
}")
(define_insn "*call_value_address_sp32"
[(set (match_operand 0 "" "=rf")
(call (mem:SI (match_operand:SI 1 "address_operand" "p"))
(match_operand 2 "" "")))
(clobber (reg:SI 15))]
;;- Do not use operand 2 for most machines.
"! TARGET_PTR64"
"call\\t%a1, %2%#"
[(set_attr "type" "call")])
(define_insn "*call_value_symbolic_sp32"
[(set (match_operand 0 "" "=rf")
(call (mem:SI (match_operand:SI 1 "symbolic_operand" "s"))
(match_operand 2 "" "")))
(clobber (reg:SI 15))]
;;- Do not use operand 2 for most machines.
"! TARGET_PTR64"
"call\\t%a1, %2%#"
[(set_attr "type" "call")])
(define_insn "*call_value_address_sp64"
[(set (match_operand 0 "" "")
(call (mem:SI (match_operand:DI 1 "address_operand" "p"))
(match_operand 2 "" "")))
(clobber (reg:DI 15))]
;;- Do not use operand 2 for most machines.
"TARGET_PTR64"
"call\\t%a1, %2%#"
[(set_attr "type" "call")])
(define_insn "*call_value_symbolic_sp64"
[(set (match_operand 0 "" "")
(call (mem:SI (match_operand:DI 1 "symbolic_operand" "s"))
(match_operand 2 "" "")))
(clobber (reg:DI 15))]
;;- Do not use operand 2 for most machines.
"TARGET_PTR64"
"call\\t%a1, %2%#"
[(set_attr "type" "call")])
(define_expand "untyped_call"
[(parallel [(call (match_operand 0 "" "")
(const_int 0))
(match_operand 1 "" "")
(match_operand 2 "" "")])]
""
"
{
int i;
/* Pass constm1 to indicate that it may expect a structure value, but
we don't know what size it is. */
emit_call_insn (gen_call (operands[0], const0_rtx, NULL, constm1_rtx));
for (i = 0; i < XVECLEN (operands[2], 0); i++)
{
rtx set = XVECEXP (operands[2], 0, i);
emit_move_insn (SET_DEST (set), SET_SRC (set));
}
/* The optimizer does not know that the call sets the function value
registers we stored in the result block. We avoid problems by
claiming that all hard registers are used and clobbered at this
point. */
emit_insn (gen_blockage ());
DONE;
}")
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory. This blocks insns from being moved across this point.
(define_insn "blockage"
[(unspec_volatile [(const_int 0)] 0)]
""
""
[(set_attr "length" "0")])
;; Prepare to return any type including a structure value.
(define_expand "untyped_return"
[(match_operand:BLK 0 "memory_operand" "")
(match_operand 1 "" "")]
""
"
{
rtx valreg1 = gen_rtx_REG (DImode, 24);
rtx valreg2 = gen_rtx_REG (TARGET_ARCH64 ? TFmode : DFmode, 32);
rtx result = operands[0];
if (! TARGET_ARCH64)
{
rtx rtnreg = gen_rtx_REG (SImode, (current_function_uses_only_leaf_regs
? 15 : 31));
rtx value = gen_reg_rtx (SImode);
/* Fetch the instruction where we will return to and see if it's an unimp
instruction (the most significant 10 bits will be zero). If so,
update the return address to skip the unimp instruction. */
emit_move_insn (value,
gen_rtx_MEM (SImode, plus_constant (rtnreg, 8)));
emit_insn (gen_lshrsi3 (value, value, GEN_INT (22)));
emit_insn (gen_update_return (rtnreg, value));
}
/* Reload the function value registers. */
emit_move_insn (valreg1, change_address (result, DImode, XEXP (result, 0)));
emit_move_insn (valreg2,
change_address (result, TARGET_ARCH64 ? TFmode : DFmode,
plus_constant (XEXP (result, 0), 8)));
/* Put USE insns before the return. */
emit_insn (gen_rtx_USE (VOIDmode, valreg1));
emit_insn (gen_rtx_USE (VOIDmode, valreg2));
/* Construct the return. */
expand_null_return ();
DONE;
}")
;; This is a bit of a hack. We're incrementing a fixed register (%i7),
;; and parts of the compiler don't want to believe that the add is needed.
(define_insn "update_return"
[(unspec:SI [(match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "register_operand" "r")] 1)]
"! TARGET_ARCH64"
"cmp %1,0\;be,a .+8\;add %0,4,%0"
[(set_attr "type" "multi")])
(define_insn "return"
[(return)
(use (reg:SI 31))]
"! TARGET_EPILOGUE"
"* return output_return (operands);"
[(set_attr "type" "return")])
(define_peephole
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operand:SI 1 "arith_operand" "rI"))
(parallel [(return)
(use (reg:SI 31))])]
"sparc_return_peephole_ok (operands[0], operands[1])"
"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0")
(define_insn "nop"
[(const_int 0)]
""
"nop"
[(set_attr "type" "ialu")
(set_attr "length" "1")])
(define_expand "indirect_jump"
[(set (pc) (match_operand 0 "address_operand" "p"))]
""
"")
(define_insn "*branch_sp32"
[(set (pc) (match_operand:SI 0 "address_operand" "p"))]
"! TARGET_PTR64"
"jmp\\t%a0%#"
[(set_attr "type" "uncond_branch")])
(define_insn "*branch_sp64"
[(set (pc) (match_operand:DI 0 "address_operand" "p"))]
"TARGET_PTR64"
"jmp\\t%a0%#"
[(set_attr "type" "uncond_branch")])
;; ??? Doesn't work with -mflat.
(define_expand "nonlocal_goto"
[(match_operand:SI 0 "general_operand" "")
(match_operand:SI 1 "general_operand" "")
(match_operand:SI 2 "general_operand" "")
(match_operand:SI 3 "" "")]
""
"
{
#if 0
rtx chain = operands[0];
#endif
rtx fp = operands[1];
rtx stack = operands[2];
rtx lab = operands[3];
rtx labreg;
/* Trap instruction to flush all the register windows. */
emit_insn (gen_flush_register_windows ());
/* Load the fp value for the containing fn into %fp. This is needed
because STACK refers to %fp. Note that virtual register instantiation
fails if the virtual %fp isn't set from a register. */
if (GET_CODE (fp) != REG)
fp = force_reg (Pmode, fp);
emit_move_insn (virtual_stack_vars_rtx, fp);
/* Find the containing function's current nonlocal goto handler,
which will do any cleanups and then jump to the label. */
labreg = gen_rtx_REG (Pmode, 8);
emit_move_insn (labreg, lab);
/* Restore %fp from stack pointer value for containing function.
The restore insn that follows will move this to %sp,
and reload the appropriate value into %fp. */
emit_move_insn (frame_pointer_rtx, stack);
/* USE of frame_pointer_rtx added for consistency; not clear if
really needed. */
/*emit_insn (gen_rtx_USE (VOIDmode, frame_pointer_rtx));*/
emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
#if 0
/* Return, restoring reg window and jumping to goto handler. */
if (TARGET_V9 && GET_CODE (chain) == CONST_INT
&& ! (INTVAL (chain) & ~(HOST_WIDE_INT)0xffffffff))
{
emit_insn (gen_goto_handler_and_restore_v9 (labreg, static_chain_rtx,
chain));
emit_barrier ();
DONE;
}
/* Put in the static chain register the nonlocal label address. */
emit_move_insn (static_chain_rtx, chain);
#endif
emit_insn (gen_rtx_USE (VOIDmode, static_chain_rtx));
emit_insn (gen_goto_handler_and_restore (labreg));
emit_barrier ();
DONE;
}")
;; Special trap insn to flush register windows.
(define_insn "flush_register_windows"
[(unspec_volatile [(const_int 0)] 1)]
""
"* return TARGET_V9 ? \"flushw\" : \"ta\\t3\";"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn "goto_handler_and_restore"
[(unspec_volatile [(match_operand 0 "register_operand" "=r")] 2)]
""
"jmp\\t%0+0\\n\\trestore"
[(set_attr "type" "misc")
(set_attr "length" "2")])
;;(define_insn "goto_handler_and_restore_v9"
;; [(unspec_volatile [(match_operand:SI 0 "register_operand" "=r,r")
;; (match_operand:SI 1 "register_operand" "=r,r")
;; (match_operand:SI 2 "const_int_operand" "I,n")] 3)]
;; "TARGET_V9 && ! TARGET_ARCH64"
;; "@
;; return\\t%0+0\\n\\tmov\\t%2, %Y1
;; sethi\\t%%hi(%2), %1\\n\\treturn\\t%0+0\\n\\tor\\t%Y1, %%lo(%2), %Y1"
;; [(set_attr "type" "misc")
;; (set_attr "length" "2,3")])
;;
;;(define_insn "*goto_handler_and_restore_v9_sp64"
;; [(unspec_volatile [(match_operand:DI 0 "register_operand" "=r,r")
;; (match_operand:DI 1 "register_operand" "=r,r")
;; (match_operand:SI 2 "const_int_operand" "I,n")] 3)]
;; "TARGET_V9 && TARGET_ARCH64"
;; "@
;; return\\t%0+0\\n\\tmov\\t%2, %Y1
;; sethi\\t%%hi(%2), %1\\n\\treturn\\t%0+0\\n\\tor\\t%Y1, %%lo(%2), %Y1"
;; [(set_attr "type" "misc")
;; (set_attr "length" "2,3")])
;; Pattern for use after a setjmp to store FP and the return register
;; into the stack area.
(define_expand "setjmp"
[(const_int 0)]
""
"
{
if (TARGET_ARCH64)
emit_insn (gen_setjmp_64 ());
else
emit_insn (gen_setjmp_32 ());
DONE;
}")
(define_expand "setjmp_32"
[(set (mem:SI (plus:SI (reg:SI 14) (const_int 56))) (match_dup 0))
(set (mem:SI (plus:SI (reg:SI 14) (const_int 60))) (reg:SI 31))]
""
"
{ operands[0] = frame_pointer_rtx; }")
(define_expand "setjmp_64"
[(set (mem:DI (plus:DI (reg:DI 14) (const_int 112))) (match_dup 0))
(set (mem:DI (plus:DI (reg:DI 14) (const_int 120))) (reg:DI 31))]
""
"
{ operands[0] = frame_pointer_rtx; }")
;; Special pattern for the FLUSH instruction.
(define_insn "flush"
[(unspec_volatile [(match_operand 0 "memory_operand" "m")] 4)]
""
"* return TARGET_V9 ? \"flush\\t%f0\" : \"iflush\\t%f0\";"
[(set_attr "type" "misc")
(set_attr "length" "1")])
;; find first set.
;; The scan instruction searches from the most significant bit while ffs
;; searches from the least significant bit. The bit index and treatment of
;; zero also differ. It takes at least 7 instructions to get the proper
;; result. Here is an obvious 8 instruction sequence.
;; XXX
(define_insn "ffssi2"
[(set (match_operand:SI 0 "register_operand" "=&r")
(ffs:SI (match_operand:SI 1 "register_operand" "r")))
(clobber (match_scratch:SI 2 "=&r"))]
"TARGET_SPARCLITE || TARGET_SPARCLET"
"*
{
if (TARGET_LIVE_G0)
output_asm_insn (\"and %%g0,0,%%g0\", operands);
return \"sub %%g0,%1,%0\;and %0,%1,%0\;scan %0,0,%0\;mov 32,%2\;sub %2,%0,%0\;sra %0,31,%2\;and %2,31,%2\;add %2,%0,%0\";
}"
[(set_attr "type" "multi")
(set_attr "length" "8")])
;; ??? This should be a define expand, so that the extra instruction have
;; a chance of being optimized away.
;; Disabled because none of the UltraSparcs implement popc. The HAL R1
;; does, but no one uses that and we don't have a switch for it.
;
;(define_insn "ffsdi2"
; [(set (match_operand:DI 0 "register_operand" "=&r")
; (ffs:DI (match_operand:DI 1 "register_operand" "r")))
; (clobber (match_scratch:DI 2 "=&r"))]
; "TARGET_ARCH64"
; "neg %1,%2\;xnor %1,%2,%2\;popc %2,%0\;movzr %1,0,%0"
; [(set_attr "type" "multi")
; (set_attr "length" "4")])
;; Peepholes go at the end.
;; Optimize consecutive loads or stores into ldd and std when possible.
;; The conditions in which we do this are very restricted and are
;; explained in the code for {registers,memory}_ok_for_ldd functions.
(define_peephole
[(set (match_operand:SI 0 "memory_operand" "")
(const_int 0))
(set (match_operand:SI 1 "memory_operand" "")
(const_int 0))]
"TARGET_V9
&& ! MEM_VOLATILE_P (operands[0])
&& ! MEM_VOLATILE_P (operands[1])
&& addrs_ok_for_ldd_peep (XEXP (operands[0], 0), XEXP (operands[1], 0))"
"stx\\t%%g0, %0")
(define_peephole
[(set (match_operand:SI 0 "memory_operand" "")
(const_int 0))
(set (match_operand:SI 1 "memory_operand" "")
(const_int 0))]
"TARGET_V9
&& ! MEM_VOLATILE_P (operands[0])
&& ! MEM_VOLATILE_P (operands[1])
&& addrs_ok_for_ldd_peep (XEXP (operands[1], 0), XEXP (operands[0], 0))"
"stx\\t%%g0, %1")
(define_peephole
[(set (match_operand:SI 0 "register_operand" "=rf")
(match_operand:SI 1 "memory_operand" ""))
(set (match_operand:SI 2 "register_operand" "=rf")
(match_operand:SI 3 "memory_operand" ""))]
"registers_ok_for_ldd_peep (operands[0], operands[2])
&& ! MEM_VOLATILE_P (operands[1])
&& ! MEM_VOLATILE_P (operands[3])
&& addrs_ok_for_ldd_peep (XEXP (operands[1], 0), XEXP (operands[3], 0))"
"ldd\\t%1, %0")
(define_peephole
[(set (match_operand:SI 0 "memory_operand" "")
(match_operand:SI 1 "register_operand" "rf"))
(set (match_operand:SI 2 "memory_operand" "")
(match_operand:SI 3 "register_operand" "rf"))]
"registers_ok_for_ldd_peep (operands[1], operands[3])
&& ! MEM_VOLATILE_P (operands[0])
&& ! MEM_VOLATILE_P (operands[2])
&& addrs_ok_for_ldd_peep (XEXP (operands[0], 0), XEXP (operands[2], 0))"
"std\\t%1, %0")
(define_peephole
[(set (match_operand:SF 0 "register_operand" "=fr")
(match_operand:SF 1 "memory_operand" ""))
(set (match_operand:SF 2 "register_operand" "=fr")
(match_operand:SF 3 "memory_operand" ""))]
"registers_ok_for_ldd_peep (operands[0], operands[2])
&& ! MEM_VOLATILE_P (operands[1])
&& ! MEM_VOLATILE_P (operands[3])
&& addrs_ok_for_ldd_peep (XEXP (operands[1], 0), XEXP (operands[3], 0))"
"ldd\\t%1, %0")
(define_peephole
[(set (match_operand:SF 0 "memory_operand" "")
(match_operand:SF 1 "register_operand" "fr"))
(set (match_operand:SF 2 "memory_operand" "")
(match_operand:SF 3 "register_operand" "fr"))]
"registers_ok_for_ldd_peep (operands[1], operands[3])
&& ! MEM_VOLATILE_P (operands[0])
&& ! MEM_VOLATILE_P (operands[2])
&& addrs_ok_for_ldd_peep (XEXP (operands[0], 0), XEXP (operands[2], 0))"
"std\\t%1, %0")
(define_peephole
[(set (match_operand:SI 0 "register_operand" "=rf")
(match_operand:SI 1 "memory_operand" ""))
(set (match_operand:SI 2 "register_operand" "=rf")
(match_operand:SI 3 "memory_operand" ""))]
"registers_ok_for_ldd_peep (operands[2], operands[0])
&& ! MEM_VOLATILE_P (operands[3])
&& ! MEM_VOLATILE_P (operands[1])
&& addrs_ok_for_ldd_peep (XEXP (operands[3], 0), XEXP (operands[1], 0))"
"ldd\\t%3, %2")
(define_peephole
[(set (match_operand:SI 0 "memory_operand" "")
(match_operand:SI 1 "register_operand" "rf"))
(set (match_operand:SI 2 "memory_operand" "")
(match_operand:SI 3 "register_operand" "rf"))]
"registers_ok_for_ldd_peep (operands[3], operands[1])
&& ! MEM_VOLATILE_P (operands[2])
&& ! MEM_VOLATILE_P (operands[0])
&& addrs_ok_for_ldd_peep (XEXP (operands[2], 0), XEXP (operands[0], 0))"
"std\\t%3, %2")
(define_peephole
[(set (match_operand:SF 0 "register_operand" "=fr")
(match_operand:SF 1 "memory_operand" ""))
(set (match_operand:SF 2 "register_operand" "=fr")
(match_operand:SF 3 "memory_operand" ""))]
"registers_ok_for_ldd_peep (operands[2], operands[0])
&& ! MEM_VOLATILE_P (operands[3])
&& ! MEM_VOLATILE_P (operands[1])
&& addrs_ok_for_ldd_peep (XEXP (operands[3], 0), XEXP (operands[1], 0))"
"ldd\\t%3, %2")
(define_peephole
[(set (match_operand:SF 0 "memory_operand" "")
(match_operand:SF 1 "register_operand" "fr"))
(set (match_operand:SF 2 "memory_operand" "")
(match_operand:SF 3 "register_operand" "fr"))]
"registers_ok_for_ldd_peep (operands[3], operands[1])
&& ! MEM_VOLATILE_P (operands[2])
&& ! MEM_VOLATILE_P (operands[0])
&& addrs_ok_for_ldd_peep (XEXP (operands[2], 0), XEXP (operands[0], 0))"
"std\\t%3, %2")
;; Optimize the case of following a reg-reg move with a test
;; of reg just moved. Don't allow floating point regs for operand 0 or 1.
;; This can result from a float to fix conversion.
(define_peephole
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operand:SI 1 "register_operand" "r"))
(set (reg:CC 100)
(compare:CC (match_operand:SI 2 "register_operand" "r")
(const_int 0)))]
"(rtx_equal_p (operands[2], operands[0])
|| rtx_equal_p (operands[2], operands[1]))
&& ! FP_REG_P (operands[0])
&& ! FP_REG_P (operands[1])"
"orcc\\t%1, 0, %0")
(define_peephole
[(set (match_operand:DI 0 "register_operand" "=r")
(match_operand:DI 1 "register_operand" "r"))
(set (reg:CCX 100)
(compare:CCX (match_operand:DI 2 "register_operand" "r")
(const_int 0)))]
"TARGET_ARCH64
&& (rtx_equal_p (operands[2], operands[0])
|| rtx_equal_p (operands[2], operands[1]))
&& ! FP_REG_P (operands[0])
&& ! FP_REG_P (operands[1])"
"orcc\\t%1, 0, %0")
;; Return peepholes. First the "normal" ones.
;; These are necessary to catch insns ending up in the epilogue delay list.
(define_insn "*return_qi"
[(set (match_operand:QI 0 "restore_operand" "")
(match_operand:QI 1 "arith_operand" "rI"))
(return)]
"! TARGET_EPILOGUE && ! TARGET_LIVE_G0"
"*
{
if (! TARGET_ARCH64 && current_function_returns_struct)
return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\";
else if (TARGET_V9 && (GET_CODE (operands[1]) == CONST_INT
|| IN_OR_GLOBAL_P (operands[1])))
return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\";
else
return \"ret\\n\\trestore %%g0, %1, %Y0\";
}"
[(set_attr "type" "multi")])
(define_insn "*return_hi"
[(set (match_operand:HI 0 "restore_operand" "")
(match_operand:HI 1 "arith_operand" "rI"))
(return)]
"! TARGET_EPILOGUE && ! TARGET_LIVE_G0"
"*
{
if (! TARGET_ARCH64 && current_function_returns_struct)
return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\";
else if (TARGET_V9 && (GET_CODE (operands[1]) == CONST_INT
|| IN_OR_GLOBAL_P (operands[1])))
return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\";
else
return \"ret\;restore %%g0, %1, %Y0\";
}"
[(set_attr "type" "multi")])
(define_insn "*return_si"
[(set (match_operand:SI 0 "restore_operand" "")
(match_operand:SI 1 "arith_operand" "rI"))
(return)]
"! TARGET_EPILOGUE && ! TARGET_LIVE_G0"
"*
{
if (! TARGET_ARCH64 && current_function_returns_struct)
return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\";
else if (TARGET_V9 && (GET_CODE (operands[1]) == CONST_INT
|| IN_OR_GLOBAL_P (operands[1])))
return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\";
else
return \"ret\;restore %%g0, %1, %Y0\";
}"
[(set_attr "type" "multi")])
;; The following pattern is only generated by delayed-branch scheduling,
;; when the insn winds up in the epilogue. This can happen not only when
;; ! TARGET_FPU because we move complex types around by parts using
;; SF mode SUBREGs.
(define_insn "*return_sf_no_fpu"
[(set (match_operand:SF 0 "restore_operand" "r")
(match_operand:SF 1 "register_operand" "r"))
(return)]
"! TARGET_EPILOGUE && ! TARGET_LIVE_G0"
"*
{
if (! TARGET_ARCH64 && current_function_returns_struct)
return \"jmp\\t%%i7+12\\n\\trestore %%g0, %1, %Y0\";
else if (TARGET_V9 && IN_OR_GLOBAL_P (operands[1]))
return \"return\\t%%i7+8\\n\\tmov\\t%Y1, %Y0\";
else
return \"ret\;restore %%g0, %1, %Y0\";
}"
[(set_attr "type" "multi")])
(define_insn "*return_addsi"
[(set (match_operand:SI 0 "restore_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "arith_operand" "rI")))
(return)]
"! TARGET_EPILOGUE && ! TARGET_LIVE_G0"
"*
{
if (! TARGET_ARCH64 && current_function_returns_struct)
return \"jmp\\t%%i7+12\\n\\trestore %r1, %2, %Y0\";
/* If operands are global or in registers, can use return */
else if (TARGET_V9 && IN_OR_GLOBAL_P (operands[1])
&& (GET_CODE (operands[2]) == CONST_INT
|| IN_OR_GLOBAL_P (operands[2])))
return \"return\\t%%i7+8\\n\\tadd\\t%Y1, %Y2, %Y0\";
else
return \"ret\;restore %r1, %2, %Y0\";
}"
[(set_attr "type" "multi")])
(define_insn "*return_di"
[(set (match_operand:DI 0 "restore_operand" "")
(match_operand:DI 1 "arith_double_operand" "rHI"))
(return)]
"TARGET_ARCH64 && ! TARGET_EPILOGUE"
"ret\;restore %%g0, %1, %Y0"
[(set_attr "type" "multi")])
(define_insn "*return_adddi"
[(set (match_operand:DI 0 "restore_operand" "")
(plus:DI (match_operand:DI 1 "arith_operand" "%r")
(match_operand:DI 2 "arith_double_operand" "rHI")))
(return)]
"TARGET_ARCH64 && ! TARGET_EPILOGUE"
"ret\;restore %r1, %2, %Y0"
[(set_attr "type" "multi")])
;; The following pattern is only generated by delayed-branch scheduling,
;; when the insn winds up in the epilogue.
(define_insn "*return_sf"
[(set (reg:SF 32)
(match_operand:SF 0 "register_operand" "f"))
(return)]
"! TARGET_EPILOGUE"
"ret\;fmovs\\t%0, %%f0"
[(set_attr "type" "multi")])
;; Now peepholes to do a call followed by a jump.
(define_peephole
[(parallel [(set (match_operand 0 "" "")
(call (mem:SI (match_operand:SI 1 "call_operand_address" "ps"))
(match_operand 2 "" "")))
(clobber (reg:SI 15))])
(set (pc) (label_ref (match_operand 3 "" "")))]
"short_branch (INSN_UID (insn), INSN_UID (operands[3]))
&& in_same_eh_region (insn, operands[3])
&& in_same_eh_region (insn, ins1)"
"call\\t%a1, %2\\n\\tadd\\t%%o7, (%l3-.-4), %%o7")
(define_peephole
[(parallel [(call (mem:SI (match_operand:SI 0 "call_operand_address" "ps"))
(match_operand 1 "" ""))
(clobber (reg:SI 15))])
(set (pc) (label_ref (match_operand 2 "" "")))]
"short_branch (INSN_UID (insn), INSN_UID (operands[2]))
&& in_same_eh_region (insn, operands[2])
&& in_same_eh_region (insn, ins1)"
"call\\t%a0, %1\\n\\tadd\\t%%o7, (%l2-.-4), %%o7")
(define_peephole
[(parallel [(set (match_operand 0 "" "")
(call (mem:SI (match_operand:DI 1 "call_operand_address" "ps"))
(match_operand 2 "" "")))
(clobber (reg:DI 15))])
(set (pc) (label_ref (match_operand 3 "" "")))]
"TARGET_ARCH64
&& short_branch (INSN_UID (insn), INSN_UID (operands[3]))
&& in_same_eh_region (insn, operands[3])
&& in_same_eh_region (insn, ins1)"
"call\\t%a1, %2\\n\\tadd\\t%%o7, (%l3-.-4), %%o7")
(define_peephole
[(parallel [(call (mem:SI (match_operand:DI 0 "call_operand_address" "ps"))
(match_operand 1 "" ""))
(clobber (reg:DI 15))])
(set (pc) (label_ref (match_operand 2 "" "")))]
"TARGET_ARCH64
&& short_branch (INSN_UID (insn), INSN_UID (operands[2]))
&& in_same_eh_region (insn, operands[2])
&& in_same_eh_region (insn, ins1)"
"call\\t%a0, %1\\n\\tadd\\t%%o7, (%l2-.-4), %%o7")
;; After a nonlocal goto, we need to restore the PIC register, but only
;; if we need it. So do nothing much here, but we'll check for this in
;; finalize_pic.
;; Make sure this unspec_volatile number agrees with finalize_pic.
(define_insn "nonlocal_goto_receiver"
[(unspec_volatile [(const_int 0)] 5)]
"flag_pic"
""
[(set_attr "length" "0")])
(define_insn "trap"
[(trap_if (const_int 1) (const_int 5))]
""
"ta\\t5"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_expand "conditional_trap"
[(trap_if (match_operator 0 "noov_compare_op"
[(match_dup 2) (match_dup 3)])
(match_operand:SI 1 "arith_operand" ""))]
""
"operands[2] = gen_compare_reg (GET_CODE (operands[0]),
sparc_compare_op0, sparc_compare_op1);
operands[3] = const0_rtx;")
(define_insn ""
[(trap_if (match_operator 0 "noov_compare_op" [(reg:CC 100) (const_int 0)])
(match_operand:SI 1 "arith_operand" "rM"))]
""
"t%C0\\t%1"
[(set_attr "type" "misc")
(set_attr "length" "1")])
(define_insn ""
[(trap_if (match_operator 0 "noov_compare_op" [(reg:CCX 100) (const_int 0)])
(match_operand:SI 1 "arith_operand" "rM"))]
"TARGET_V9"
"t%C0\\t%%xcc, %1"
[(set_attr "type" "misc")
(set_attr "length" "1")])
Index: head/contrib/gcc/configure.in
===================================================================
--- head/contrib/gcc/configure.in (revision 52750)
+++ head/contrib/gcc/configure.in (revision 52751)
@@ -1,4506 +1,4515 @@
# configure.in for GNU CC
# Process this file with autoconf to generate a configuration script.
# Copyright (C) 1997, 1998, 1999 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.
# Initialization and defaults
AC_PREREQ(2.12.1)
AC_INIT(tree.c)
AC_CONFIG_HEADER(auto-host.h:config.in)
remove=rm
hard_link=ln
symbolic_link='ln -s'
copy=cp
# Check for bogus environment variables.
# Test if LIBRARY_PATH contains the notation for the current directory
# since this would lead to problems installing/building glibc.
# LIBRARY_PATH contains the current directory if one of the following
# is true:
# - one of the terminals (":" and ";") is the first or last sign
# - two terminals occur directly after each other
# - the path contains an element with a dot in it
AC_MSG_CHECKING(LIBRARY_PATH variable)
changequote(,)dnl
case ${LIBRARY_PATH} in
[:\;]* | *[:\;] | *[:\;][:\;]* | *[:\;]. | .[:\;]*| . | *[:\;].[:\;]* )
library_path_setting="contains current directory"
;;
*)
library_path_setting="ok"
;;
esac
changequote([,])dnl
AC_MSG_RESULT($library_path_setting)
if test "$library_path_setting" != "ok"; then
AC_MSG_ERROR([
*** LIBRARY_PATH shouldn't contain the current directory when
*** building gcc. Please change the environment variable
*** and run configure again.])
fi
# Test if GCC_EXEC_PREFIX contains the notation for the current directory
# since this would lead to problems installing/building glibc.
# GCC_EXEC_PREFIX contains the current directory if one of the following
# is true:
# - one of the terminals (":" and ";") is the first or last sign
# - two terminals occur directly after each other
# - the path contains an element with a dot in it
AC_MSG_CHECKING(GCC_EXEC_PREFIX variable)
changequote(,)dnl
case ${GCC_EXEC_PREFIX} in
[:\;]* | *[:\;] | *[:\;][:\;]* | *[:\;]. | .[:\;]*| . | *[:\;].[:\;]* )
gcc_exec_prefix_setting="contains current directory"
;;
*)
gcc_exec_prefix_setting="ok"
;;
esac
changequote([,])dnl
AC_MSG_RESULT($gcc_exec_prefix_setting)
if test "$gcc_exec_prefix_setting" != "ok"; then
AC_MSG_ERROR([
*** GCC_EXEC_PREFIX shouldn't contain the current directory when
*** building gcc. Please change the environment variable
*** and run configure again.])
fi
# Check for additional parameters
# With GNU ld
AC_ARG_WITH(gnu-ld,
[ --with-gnu-ld arrange to work with GNU ld.],
gnu_ld_flag="$with_gnu_ld",
gnu_ld_flag=no)
# With pre-defined ld
AC_ARG_WITH(ld,
[ --with-ld arrange to use the specified ld (full pathname).],
DEFAULT_LINKER="$with_ld")
if test x"${DEFAULT_LINKER+set}" = x"set"; then
if test ! -x "$DEFAULT_LINKER"; then
AC_MSG_WARN([cannot execute: $DEFAULT_LINKER: check --with-ld or env. var. DEFAULT_LINKER])
elif $DEFAULT_LINKER -v < /dev/null 2>&1 | grep GNU > /dev/null; then
gnu_ld_flag=yes
fi
AC_DEFINE_UNQUOTED(DEFAULT_LINKER,"$DEFAULT_LINKER")
fi
# With GNU as
AC_ARG_WITH(gnu-as,
[ --with-gnu-as arrange to work with GNU as.],
gas_flag="$with_gnu_as",
gas_flag=no)
AC_ARG_WITH(as,
[ --with-as arrange to use the specified as (full pathname).],
DEFAULT_ASSEMBLER="$with_as")
if test x"${DEFAULT_ASSEMBLER+set}" = x"set"; then
if test ! -x "$DEFAULT_ASSEMBLER"; then
AC_MSG_WARN([cannot execute: $DEFAULT_ASSEMBLER: check --with-as or env. var. DEFAULT_ASSEMBLER])
elif $DEFAULT_ASSEMBLER -v < /dev/null 2>&1 | grep GNU > /dev/null; then
gas_flag=yes
fi
AC_DEFINE_UNQUOTED(DEFAULT_ASSEMBLER,"$DEFAULT_ASSEMBLER")
fi
# With stabs
AC_ARG_WITH(stabs,
[ --with-stabs arrange to use stabs instead of host debug format.],
stabs="$with_stabs",
stabs=no)
# With ELF
AC_ARG_WITH(elf,
[ --with-elf arrange to use ELF instead of host debug format.],
elf="$with_elf",
elf=no)
# Specify the local prefix
local_prefix=
AC_ARG_WITH(local-prefix,
[ --with-local-prefix=DIR specifies directory to put local include.],
[case "${withval}" in
yes) AC_MSG_ERROR(bad value ${withval} given for local include directory prefix) ;;
no) ;;
*) local_prefix=$with_local_prefix ;;
esac])
# Default local prefix if it is empty
if test x$local_prefix = x; then
local_prefix=/usr/local
fi
# Don't set gcc_gxx_include_dir to gxx_include_dir since that's only
# passed in by the toplevel make and thus we'd get different behavior
# depending on where we built the sources.
gcc_gxx_include_dir=
# Specify the g++ header file directory
AC_ARG_WITH(gxx-include-dir,
[ --with-gxx-include-dir=DIR
specifies directory to put g++ header files.],
[case "${withval}" in
yes) AC_MSG_ERROR(bad value ${withval} given for g++ include directory) ;;
no) ;;
*) gcc_gxx_include_dir=$with_gxx_include_dir ;;
esac])
if test x${gcc_gxx_include_dir} = x; then
if test x${enable_version_specific_runtime_libs} = xyes; then
gcc_gxx_include_dir='${libsubdir}/include/g++'
else
topsrcdir=${srcdir}/.. . ${srcdir}/../config.if
changequote(<<, >>)dnl
gcc_gxx_include_dir="\$(libsubdir)/\$(unlibsubdir)/..\`echo \$(exec_prefix) | sed -e 's|^\$(prefix)||' -e 's|/[^/]*|/..|g'\`/include/g++"-${libstdcxx_interface}
changequote([, ])dnl
fi
fi
# Enable expensive internal checks
AC_ARG_ENABLE(checking,
[ --enable-checking enable expensive run-time checks.],
[case "${enableval}" in
yes) AC_DEFINE(ENABLE_CHECKING) ;;
no) ;;
*) AC_MSG_ERROR(bad value ${enableval} given for checking option) ;;
esac])
AC_ARG_ENABLE(cpp,
[ --disable-cpp don't provide a user-visible C preprocessor.],
[], [enable_cpp=yes])
AC_ARG_WITH(cpp_install_dir,
[ --with-cpp-install-dir=DIR
install the user visible C preprocessor in DIR
(relative to PREFIX) as well as PREFIX/bin.],
[if test x$withval = xyes; then
AC_MSG_ERROR([option --with-cpp-install-dir requires an argument])
elif test x$withval != xno; then
cpp_install_dir=$withval
fi])
# Use cpplib+cppmain for the preprocessor, but don't link it with the compiler.
cpp_main=cccp
AC_ARG_ENABLE(cpplib,
[ --enable-cpplib use cpplib for the C preprocessor.],
if test x$enable_cpplib != xno; then
cpp_main=cppmain
fi)
# Link cpplib into the compiler proper, for C/C++/ObjC.
AC_ARG_ENABLE(c-cpplib,
[ --enable-c-cpplib link cpplib directly into C and C++ compilers
(EXPERIMENTAL) (implies --enable-cpplib).],
if test x$enable_c_cpplib != xno; then
extra_c_objs="${extra_c_objs} libcpp.a"
extra_cxx_objs="${extra_cxx_objs} ../libcpp.a"
extra_c_flags="${extra_c_flags} -DUSE_CPPLIB=1"
cpp_main=cppmain
fi)
# Enable Multibyte Characters for C/C++
AC_ARG_ENABLE(c-mbchar,
[ --enable-c-mbchar enable multibyte characters for C and C++.],
if test x$enable_c_mbchar != xno; then
extra_c_flags=-DMULTIBYTE_CHARS=1
fi)
# Disable fast fixincludes
AC_ARG_ENABLE(fast-fixincludes,
[ --disable-fast-fixincludes
Disable the new fast fixincludes.
Run the old fixincludes script unconditionally],
if test x$enable_fast_fixincludes = xno ; then
cp $srcdir/fixincludes ./fixinc.sh
fi)
# Enable Haifa scheduler.
AC_ARG_ENABLE(haifa,
[ --enable-haifa use the experimental scheduler.
--disable-haifa don't use the experimental scheduler for the
targets which normally enable it.])
# Enable threads
# Pass with no value to take the default
# Pass with a value to specify a thread package
AC_ARG_ENABLE(threads,
[ --enable-threads enable thread usage for target GCC.
--enable-threads=LIB use LIB thread package for target GCC.],
if test x$enable_threads = xno; then
enable_threads=''
fi,
enable_threads='')
enable_threads_flag=$enable_threads
# Check if a valid thread package
case x${enable_threads_flag} in
x | xno)
# No threads
target_thread_file='single'
;;
xyes)
# default
target_thread_file=''
;;
xdecosf1 | xirix | xmach | xos2 | xposix | xpthreads | xsingle | \
xsolaris | xwin32 | xdce | xvxworks)
target_thread_file=$enable_threads_flag
;;
*)
echo "$enable_threads is an unknown thread package" 1>&2
exit 1
;;
esac
AC_ARG_ENABLE(objc-gc,
[ --enable-objc-gc enable the use of Boehm's garbage collector with
the GNU Objective-C runtime.],
if [[[ x$enable_objc_gc = xno ]]]; then
objc_boehm_gc=''
else
objc_boehm_gc=1
fi,
objc_boehm_gc='')
AC_ARG_ENABLE(java-gc,
changequote(<<,>>)dnl
<< --enable-java-gc=TYPE choose garbage collector [boehm]>>,
changequote([,])
JAVAGC=$enableval,
JAVAGC=boehm)
AC_ARG_WITH(dwarf2,
[ --enable-dwarf2 enable DWARF2 debugging as default.],
dwarf2="$with_dwarf2",
dwarf2=no)
# Determine the host, build, and target systems
AC_CANONICAL_SYSTEM
# Find the native compiler
AC_PROG_CC
# If the native compiler is GCC, we can enable warnings even in stage1.
# That's useful for people building cross-compilers, or just running a
# quick `make'.
if test "x$GCC" = "xyes"; then
stage1_warn_cflags='$(WARN_CFLAGS)'
else
stage1_warn_cflags=""
fi
AC_SUBST(stage1_warn_cflags)
AC_PROG_MAKE_SET
AC_MSG_CHECKING([whether a default assembler was specified])
if test x"${DEFAULT_ASSEMBLER+set}" = x"set"; then
if test x"$gas_flag" = x"no"; then
AC_MSG_RESULT([yes ($DEFAULT_ASSEMBLER)])
else
AC_MSG_RESULT([yes ($DEFAULT_ASSEMBLER - GNU as)])
fi
else
AC_MSG_RESULT(no)
fi
AC_MSG_CHECKING([whether a default linker was specified])
if test x"${DEFAULT_LINKER+set}" = x"set"; then
if test x"$gnu_ld_flag" = x"no"; then
AC_MSG_RESULT([yes ($DEFAULT_LINKER)])
else
AC_MSG_RESULT([yes ($DEFAULT_LINKER - GNU ld)])
fi
else
AC_MSG_RESULT(no)
fi
# Find some useful tools
AC_PROG_AWK
AC_PROG_LEX
GCC_PROG_LN
GCC_PROG_LN_S
GCC_C_VOLATILE
AC_PROG_RANLIB
AC_PROG_YACC
EGCS_PROG_INSTALL
AC_HEADER_STDC
AC_HEADER_TIME
GCC_HEADER_STRING
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(limits.h stddef.h string.h strings.h stdlib.h time.h fcntl.h unistd.h stab.h sys/file.h sys/time.h sys/resource.h sys/param.h sys/times.h sys/stat.h direct.h)
# Check for thread headers.
AC_CHECK_HEADER(thread.h, [have_thread_h=yes], [have_thread_h=])
AC_CHECK_HEADER(pthread.h, [have_pthread_h=yes], [have_pthread_h=])
# See if GNAT has been installed
AC_CHECK_PROG(gnat, gnatbind, yes, no)
# See if the system preprocessor understands the ANSI C preprocessor
# stringification operator.
AC_MSG_CHECKING(whether cpp understands the stringify operator)
AC_CACHE_VAL(gcc_cv_c_have_stringify,
[AC_TRY_COMPILE(,
[#define S(x) #x
char *test = S(foo);],
gcc_cv_c_have_stringify=yes, gcc_cv_c_have_stringify=no)])
AC_MSG_RESULT($gcc_cv_c_have_stringify)
if test $gcc_cv_c_have_stringify = yes; then
AC_DEFINE(HAVE_CPP_STRINGIFY)
fi
# Use <inttypes.h> only if it exists,
# doesn't clash with <sys/types.h>, and declares intmax_t.
AC_MSG_CHECKING(for inttypes.h)
AC_CACHE_VAL(gcc_cv_header_inttypes_h,
[AC_TRY_COMPILE(
[#include <sys/types.h>
#include <inttypes.h>],
[intmax_t i = -1;],
[gcc_cv_header_inttypes_h=yes],
gcc_cv_header_inttypes_h=no)])
AC_MSG_RESULT($gcc_cv_header_inttypes_h)
if test $gcc_cv_header_inttypes_h = yes; then
AC_DEFINE(HAVE_INTTYPES_H)
fi
AC_CHECK_FUNCS(strtoul bsearch strerror putenv popen bcopy bzero bcmp \
index rindex strchr strrchr kill getrlimit setrlimit atoll atoq \
sysconf isascii gettimeofday strsignal putc_unlocked fputc_unlocked \
fputs_unlocked)
# Make sure wchar_t is available
#AC_CHECK_TYPE(wchar_t, unsigned int)
GCC_FUNC_VFPRINTF_DOPRNT
GCC_FUNC_PRINTF_PTR
case "${host}" in
*-*-uwin*)
# Under some versions of uwin, vfork is notoriously buggy and the test
# can hang configure; on other versions, vfork exists just as a stub.
# FIXME: This should be removed once vfork in uwin's runtime is fixed.
ac_cv_func_vfork_works=no
;;
esac
AC_FUNC_VFORK
GCC_NEED_DECLARATIONS(malloc realloc calloc free bcopy bzero bcmp \
index rindex getenv atol sbrk abort atof strerror getcwd getwd \
strsignal putc_unlocked fputs_unlocked strstr)
GCC_NEED_DECLARATIONS(getrlimit setrlimit, [
#include <sys/types.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
])
AC_DECL_SYS_SIGLIST
# mkdir takes a single argument on some systems.
GCC_FUNC_MKDIR_TAKES_ONE_ARG
# File extensions
manext='.1'
objext='.o'
AC_SUBST(manext)
AC_SUBST(objext)
build_xm_file=
build_xm_defines=
build_install_headers_dir=install-headers-tar
build_exeext=
host_xm_file=
host_xm_defines=
host_xmake_file=
host_truncate_target=
host_exeext=
# Decode the host machine, then the target machine.
# For the host machine, we save the xm_file variable as host_xm_file;
# then we decode the target machine and forget everything else
# that came from the host machine.
for machine in $build $host $target; do
out_file=
xmake_file=
tmake_file=
extra_headers=
extra_passes=
extra_parts=
extra_programs=
extra_objs=
extra_host_objs=
extra_gcc_objs=
xm_defines=
float_format=
# Set this to force installation and use of collect2.
use_collect2=
# Set this to override the default target model.
target_cpu_default=
# Set this to control how the header file directory is installed.
install_headers_dir=install-headers-tar
# Set this to a non-empty list of args to pass to cpp if the target
# wants its .md file passed through cpp.
md_cppflags=
# Set this if directory names should be truncated to 14 characters.
truncate_target=
# Set this if gdb needs a dir command with `dirname $out_file`
gdb_needs_out_file_path=
# Set this if the build machine requires executables to have a
# file name suffix.
exeext=
# Set this to control which thread package will be used.
thread_file=
# Reinitialize these from the flag values every loop pass, since some
# configure entries modify them.
gas="$gas_flag"
gnu_ld="$gnu_ld_flag"
enable_threads=$enable_threads_flag
# Set default cpu_type, tm_file and xm_file so it can be updated in
# each machine entry.
cpu_type=`echo $machine | sed 's/-.*$//'`
case $machine in
alpha*-*-*)
cpu_type=alpha
;;
arm*-*-*)
cpu_type=arm
;;
c*-convex-*)
cpu_type=convex
;;
changequote(,)dnl
i[34567]86-*-*)
changequote([,])dnl
cpu_type=i386
;;
hppa*-*-*)
cpu_type=pa
;;
m68000-*-*)
cpu_type=m68k
;;
mips*-*-*)
cpu_type=mips
;;
powerpc*-*-*)
cpu_type=rs6000
;;
pyramid-*-*)
cpu_type=pyr
;;
sparc*-*-*)
cpu_type=sparc
;;
esac
tm_file=${cpu_type}/${cpu_type}.h
xm_file=${cpu_type}/xm-${cpu_type}.h
# Common parts for linux-gnu and openbsd systems
case $machine in
*-*-linux-gnu*)
xm_defines="HAVE_ATEXIT POSIX BSTRING"
;;
*-*-openbsd*)
tm_file=${cpu_type}/openbsd.h
tmake_file="t-libc-ok t-openbsd"
# avoid surprises, always provide an xm-openbsd file
xm_file=${cpu_type}/xm-openbsd.h
# don't depend on processor x-fragments as well
xmake_file=none
if test x$enable_threads = xyes; then
thread_file='posix'
tmake_file="${tmake_file} t-openbsd-thread"
fi
;;
esac
case $machine in
# Support site-specific machine types.
*local*)
cpu_type=`echo $machine | sed -e 's/-.*//'`
rest=`echo $machine | sed -e "s/$cpu_type-//"`
xm_file=${cpu_type}/xm-$rest.h
tm_file=${cpu_type}/$rest.h
if test -f $srcdir/config/${cpu_type}/x-$rest; \
then xmake_file=${cpu_type}/x-$rest; \
else true; \
fi
if test -f $srcdir/config/${cpu_type}/t-$rest; \
then tmake_file=${cpu_type}/t-$rest; \
else true; \
fi
;;
1750a-*-*)
;;
a29k-*-bsd* | a29k-*-sym1*)
tm_file="${tm_file} a29k/unix.h"
xm_defines=USG
xmake_file=a29k/x-unix
use_collect2=yes
;;
a29k-*-udi | a29k-*-coff)
tm_file="${tm_file} dbxcoff.h a29k/udi.h"
tmake_file=a29k/t-a29kbare
;;
a29k-wrs-vxworks*)
tm_file="${tm_file} dbxcoff.h a29k/udi.h a29k/vx29k.h"
tmake_file=a29k/t-vx29k
extra_parts="crtbegin.o crtend.o"
thread_file='vxworks'
;;
a29k-*-*) # Default a29k environment.
use_collect2=yes
;;
alpha-*-interix)
tm_file="${tm_file} alpha/alpha32.h interix.h alpha/alpha-interix.h"
# GAS + IEEE_CONFORMANT+IEEE (no inexact);
#target_cpu_default="MASK_GAS|MASK_IEEE_CONFORMANT|MASK_IEEE"
# GAS + IEEE_CONFORMANT
target_cpu_default="MASK_GAS|MASK_IEEE_CONFORMANT"
xm_file="alpha/xm-alpha-interix.h xm-interix.h"
xmake_file="x-interix alpha/t-pe"
tmake_file="alpha/t-interix alpha/t-ieee"
if test x$enable_threads = xyes ; then
thread_file='posix'
fi
if test x$stabs = xyes ; then
tm_file="${tm_file} dbxcoff.h"
fi
#prefix='$$INTERIX_ROOT'/usr/contrib
#local_prefix='$$INTERIX_ROOT'/usr/contrib
;;
alpha*-*-linux-gnuecoff*)
tm_file="${tm_file} alpha/linux-ecoff.h alpha/linux.h"
target_cpu_default="MASK_GAS"
tmake_file="alpha/t-ieee"
gas=no
xmake_file=none
gas=yes gnu_ld=yes
;;
alpha*-*-linux-gnulibc1*)
tm_file="${tm_file} alpha/elf.h alpha/linux.h alpha/linux-elf.h"
target_cpu_default="MASK_GAS"
tmake_file="t-linux t-linux-gnulibc1 alpha/t-linux alpha/t-crtbe alpha/t-ieee"
extra_parts="crtbegin.o crtend.o"
xmake_file=none
gas=yes gnu_ld=yes
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
alpha*-*-linux-gnu*)
tm_file="${tm_file} alpha/elf.h alpha/linux.h alpha/linux-elf.h"
target_cpu_default="MASK_GAS"
tmake_file="t-linux alpha/t-linux alpha/t-crtbe alpha/t-ieee"
extra_parts="crtbegin.o crtend.o"
xmake_file=none
gas=yes gnu_ld=yes
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
alpha*-*-netbsd*)
tm_file="${tm_file} alpha/elf.h alpha/netbsd.h alpha/netbsd-elf.h"
target_cpu_default="MASK_GAS"
tmake_file="alpha/t-crtbe alpha/t-ieee"
extra_parts="crtbegin.o crtend.o"
xmake_file=none
gas=yes gnu_ld=yes
;;
alpha*-*-openbsd*)
# default x-alpha is only appropriate for dec-osf.
target_cpu_default="MASK_GAS"
tmake_file="alpha/t-ieee"
;;
alpha*-dec-osf*)
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
if test x$gas != xyes
then
extra_passes="mips-tfile mips-tdump"
fi
use_collect2=yes
tmake_file="alpha/t-ieee"
case $machine in
*-*-osf1*)
tm_file="${tm_file} alpha/osf.h alpha/osf12.h alpha/osf2or3.h"
;;
changequote(,)dnl
*-*-osf[23]*)
changequote([,])dnl
tm_file="${tm_file} alpha/osf.h alpha/osf2or3.h"
;;
*-*-osf4*)
tm_file="${tm_file} alpha/osf.h"
# Some versions of OSF4 (specifically X4.0-9 296.7) have
# a broken tar, so we use cpio instead.
install_headers_dir=install-headers-cpio
;;
esac
case $machine in
changequote(,)dnl
*-*-osf4.0[b-z] | *-*-osf4.[1-9]*)
changequote([,])dnl
target_cpu_default=MASK_SUPPORT_ARCH
;;
esac
;;
alpha*-*-vxworks*)
tm_file="${tm_file} dbx.h alpha/vxworks.h"
tmake_file="alpha/t-ieee"
if [ x$gas != xyes ]
then
extra_passes="mips-tfile mips-tdump"
fi
use_collect2=yes
thread_file='vxworks'
;;
alpha*-*-winnt*)
tm_file="${tm_file} alpha/alpha32.h alpha/win-nt.h winnt/win-nt.h"
xm_file="${xm_file} config/winnt/xm-winnt.h alpha/xm-winnt.h"
tmake_file="t-libc-ok alpha/t-ieee"
xmake_file=winnt/x-winnt
extra_host_objs=oldnames.o
extra_gcc_objs="spawnv.o oldnames.o"
if test x$gnu_ld != xyes
then
extra_programs=ld.exe
fi
if test x$enable_threads = xyes; then
thread_file='win32'
fi
;;
alpha*-dec-vms*)
tm_file=alpha/vms.h
xm_file="${xm_file} alpha/xm-vms.h"
tmake_file="alpha/t-vms alpha/t-ieee"
;;
arc-*-elf*)
extra_parts="crtinit.o crtfini.o"
;;
arm-*-coff* | armel-*-coff*)
tm_file=arm/coff.h
tmake_file=arm/t-bare
;;
arm-*-vxworks*)
tm_file=arm/vxarm.h
tmake_file=arm/t-bare
thread_file='vxworks'
;;
changequote(,)dnl
arm-*-riscix1.[01]*) # Acorn RISC machine (early versions)
changequote([,])dnl
tm_file=arm/riscix1-1.h
use_collect2=yes
;;
arm-*-riscix*) # Acorn RISC machine
if test x$gas = xyes
then
tm_file=arm/rix-gas.h
else
tm_file=arm/riscix.h
fi
xmake_file=arm/x-riscix
tmake_file=arm/t-riscix
use_collect2=yes
;;
arm-semi-aout | armel-semi-aout)
tm_file=arm/semi.h
tmake_file=arm/t-semi
;;
arm-semi-aof | armel-semi-aof)
tm_file=arm/semiaof.h
tmake_file=arm/t-semiaof
;;
arm*-*-netbsd*)
tm_file=arm/netbsd.h
xm_file="arm/xm-netbsd.h ${xm_file}"
tmake_file="t-netbsd arm/t-netbsd"
use_collect2=yes
;;
arm*-*-linux-gnuaout*) # ARM GNU/Linux with a.out
cpu_type=arm
xmake_file=x-linux
tm_file=arm/linux-aout.h
tmake_file=arm/t-linux
gnu_ld=yes
;;
arm*-*-linux-gnu*) # ARM GNU/Linux with ELF
xm_file=arm/xm-linux.h
xmake_file=x-linux
case $machine in
armv2*-*-*)
tm_file=arm/linux-elf26.h
;;
*)
tm_file=arm/linux-elf.h
;;
esac
tmake_file="t-linux arm/t-linux"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
gnu_ld=yes
case x${enable_threads} in
x | xyes | xpthreads | xposix)
thread_file='posix'
;;
esac
;;
arm*-*-aout)
tm_file=arm/aout.h
tmake_file=arm/t-bare
;;
arm*-*-ecos-elf)
tm_file=arm/ecos-elf.h
tmake_file=arm/t-elf
;;
arm*-*-elf)
tm_file=arm/unknown-elf.h
tmake_file=arm/t-arm-elf
;;
arm*-*-oabi)
tm_file=arm/unknown-elf-oabi.h
tmake_file=arm/t-arm-elf
;;
c1-convex-*) # Convex C1
target_cpu_default=1
use_collect2=yes
;;
c2-convex-*) # Convex C2
target_cpu_default=2
use_collect2=yes
;;
c32-convex-*)
target_cpu_default=4
use_collect2=yes
;;
c34-convex-*)
target_cpu_default=8
use_collect2=yes
;;
c38-convex-*)
target_cpu_default=16
use_collect2=yes
;;
c4x-*)
cpu_type=c4x
tmake_file=c4x/t-c4x
;;
clipper-intergraph-clix*)
tm_file="${tm_file} svr3.h clipper/clix.h"
xm_file=clipper/xm-clix.h
xmake_file=clipper/x-clix
extra_headers=va-clipper.h
extra_parts="crtbegin.o crtend.o"
install_headers_dir=install-headers-cpio
;;
dsp16xx-*)
;;
elxsi-elxsi-*)
use_collect2=yes
;;
# This hasn't been upgraded to GCC 2.
# fx80-alliant-*) # Alliant FX/80
# ;;
h8300-*-*)
float_format=i32
;;
hppa*-*-openbsd*)
target_cpu_default="MASK_PA_11"
tmake_file=pa/t-openbsd
;;
hppa1.1-*-pro*)
tm_file="pa/pa-pro.h ${tm_file} pa/pa-pro-end.h libgloss.h"
xm_file=pa/xm-papro.h
tmake_file=pa/t-pro
;;
hppa1.1-*-osf*)
target_cpu_default="MASK_PA_11"
tm_file="${tm_file} pa/pa-osf.h"
use_collect2=yes
;;
hppa1.1-*-rtems*)
tm_file="pa/pa-pro.h ${tm_file} pa/pa-pro-end.h libgloss.h pa/rtems.h"
xm_file=pa/xm-papro.h
tmake_file=pa/t-pro
;;
hppa1.0-*-osf*)
tm_file="${tm_file} pa/pa-osf.h"
use_collect2=yes
;;
hppa1.1-*-bsd*)
target_cpu_default="MASK_PA_11"
use_collect2=yes
;;
hppa1.0-*-bsd*)
use_collect2=yes
;;
hppa1.0-*-hpux7*)
tm_file="pa/pa-oldas.h ${tm_file} pa/pa-hpux7.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
changequote(,)dnl
hppa1.0-*-hpux8.0[0-2]*)
changequote([,])dnl
tm_file="${tm_file} pa/pa-hpux.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
else
tm_file="pa/pa-oldas.h ${tm_file}"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
changequote(,)dnl
hppa1.1-*-hpux8.0[0-2]*)
changequote([,])dnl
target_cpu_default="MASK_PA_11"
tm_file="${tm_file} pa/pa-hpux.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
else
tm_file="pa/pa-oldas.h ${tm_file}"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.1-*-hpux8*)
target_cpu_default="MASK_PA_11"
tm_file="${tm_file} pa/pa-hpux.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.0-*-hpux8*)
tm_file="${tm_file} pa/pa-hpux.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.1-*-hpux10* | hppa2*-*-hpux10*)
target_cpu_default="MASK_PA_11"
tm_file="${tm_file} pa/pa-hpux.h pa/pa-hpux10.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
tmake_file=pa/t-pa
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
if test x$enable_threads = x; then
enable_threads=$have_pthread_h
fi
if test x$enable_threads = xyes; then
thread_file='dce'
tmake_file="${tmake_file} pa/t-dce-thr"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.0-*-hpux10*)
tm_file="${tm_file} pa/pa-hpux.h pa/pa-hpux10.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
tmake_file=pa/t-pa
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
if test x$enable_threads = x; then
enable_threads=$have_pthread_h
fi
if test x$enable_threads = xyes; then
thread_file='dce'
tmake_file="${tmake_file} pa/t-dce-thr"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.1-*-hpux* | hppa2*-*-hpux*)
target_cpu_default="MASK_PA_11"
tm_file="${tm_file} pa/pa-hpux.h pa/pa-hpux9.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.0-*-hpux*)
tm_file="${tm_file} pa/pa-hpux.h pa/pa-hpux9.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.1-*-hiux* | hppa2*-*-hiux*)
target_cpu_default="MASK_PA_11"
tm_file="${tm_file} pa/pa-hpux.h pa/pa-hiux.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa1.0-*-hiux*)
tm_file="${tm_file} pa/pa-hpux.h pa/pa-hiux.h"
xm_file=pa/xm-pahpux.h
xmake_file=pa/x-pa-hpux
if test x$gas = xyes
then
tm_file="${tm_file} pa/pa-gas.h"
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
;;
hppa*-*-lites*)
target_cpu_default="MASK_PA_11"
use_collect2=yes
;;
i370-*-mvs*)
;;
changequote(,)dnl
i[34567]86-ibm-aix*) # IBM PS/2 running AIX
changequote([,])dnl
if test x$gas = xyes
then
tm_file=i386/aix386.h
extra_parts="crtbegin.o crtend.o"
tmake_file=i386/t-crtstuff
else
tm_file=i386/aix386ng.h
use_collect2=yes
fi
xm_file="xm-alloca.h i386/xm-aix.h ${xm_file}"
xm_defines=USG
xmake_file=i386/x-aix
;;
changequote(,)dnl
i[34567]86-ncr-sysv4*) # NCR 3000 - ix86 running system V.4
changequote([,])dnl
xm_file="xm-siglist.h xm-alloca.h ${xm_file}"
xm_defines="USG POSIX SMALL_ARG_MAX"
xmake_file=i386/x-ncr3000
if test x$stabs = xyes -a x$gas = xyes
then
tm_file=i386/sysv4gdb.h
else
tm_file=i386/sysv4.h
fi
extra_parts="crtbegin.o crtend.o"
tmake_file=i386/t-crtpic
;;
changequote(,)dnl
i[34567]86-next-*)
changequote([,])dnl
tm_file=i386/next.h
xm_file=i386/xm-next.h
tmake_file=i386/t-next
xmake_file=i386/x-next
extra_objs=nextstep.o
extra_parts="crtbegin.o crtend.o"
if test x$enable_threads = xyes; then
thread_file='mach'
fi
;;
changequote(,)dnl
i[34567]86-sequent-bsd*) # 80386 from Sequent
changequote([,])dnl
use_collect2=yes
if test x$gas = xyes
then
tm_file=i386/seq-gas.h
else
tm_file=i386/sequent.h
fi
;;
changequote(,)dnl
i[34567]86-sequent-ptx1*)
changequote([,])dnl
xm_defines="USG SVR3"
xmake_file=i386/x-sysv3
tm_file=i386/seq-sysv3.h
tmake_file=i386/t-crtstuff
extra_parts="crtbegin.o crtend.o"
install_headers_dir=install-headers-cpio
;;
changequote(,)dnl
i[34567]86-sequent-ptx2* | i[34567]86-sequent-sysv3*)
changequote([,])dnl
xm_defines="USG SVR3"
xmake_file=i386/x-sysv3
tm_file=i386/seq2-sysv3.h
tmake_file=i386/t-crtstuff
extra_parts="crtbegin.o crtend.o"
install_headers_dir=install-headers-cpio
;;
changequote(,)dnl
i[34567]86-sequent-ptx4* | i[34567]86-sequent-sysv4*)
changequote([,])dnl
xm_file="xm-siglist.h xm-alloca.h ${xm_file}"
xm_defines="USG POSIX SMALL_ARG_MAX"
xmake_file=x-svr4
tm_file=i386/ptx4-i.h
tmake_file=t-svr4
extra_parts="crtbegin.o crtend.o"
install_headers_dir=install-headers-cpio
;;
i386-sun-sunos*) # Sun i386 roadrunner
xm_defines=USG
tm_file=i386/sun.h
use_collect2=yes
;;
changequote(,)dnl
i[34567]86-wrs-vxworks*)
changequote([,])dnl
tm_file=i386/vxi386.h
tmake_file=i386/t-i386bare
thread_file='vxworks'
;;
changequote(,)dnl
i[34567]86-*-aout*)
changequote([,])dnl
tm_file=i386/i386-aout.h
tmake_file=i386/t-i386bare
;;
changequote(,)dnl
i[34567]86-*-bsdi* | i[34567]86-*-bsd386*)
changequote([,])dnl
tm_file=i386/bsd386.h
# tmake_file=t-libc-ok
;;
changequote(,)dnl
i[34567]86-*-bsd*)
changequote([,])dnl
tm_file=i386/386bsd.h
# tmake_file=t-libc-ok
# Next line turned off because both 386BSD and BSD/386 use GNU ld.
# use_collect2=yes
;;
changequote(,)dnl
i[34567]86-*-freebsdelf*)
changequote([,])dnl
tm_file="i386/i386.h i386/att.h svr4.h i386/freebsd-elf.h i386/perform.h"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
tmake_file=t-freebsd
gas=yes
gnu_ld=yes
stabs=yes
;;
changequote(,)dnl
i[34567]86-*-freebsd*)
changequote([,])dnl
tm_file=i386/freebsd.h
tmake_file=t-freebsd
;;
changequote(,)dnl
i[34567]86-*-netbsd*)
changequote([,])dnl
tm_file=i386/netbsd.h
tmake_file=t-netbsd
use_collect2=yes
;;
changequote(,)dnl
i[34567]86-*-openbsd*)
changequote([,])dnl
# we need collect2 until our bug is fixed...
use_collect2=yes
;;
changequote(,)dnl
i[34567]86-*-coff*)
changequote([,])dnl
tm_file=i386/i386-coff.h
tmake_file=i386/t-i386bare
;;
changequote(,)dnl
i[34567]86-*-isc*) # 80386 running ISC system
changequote([,])dnl
xm_file="${xm_file} i386/xm-isc.h"
xm_defines="USG SVR3"
case $machine in
changequote(,)dnl
i[34567]86-*-isc[34]*)
changequote([,])dnl
xmake_file=i386/x-isc3
;;
*)
xmake_file=i386/x-isc
;;
esac
if test x$gas = xyes -a x$stabs = xyes
then
tm_file=i386/iscdbx.h
tmake_file=i386/t-svr3dbx
extra_parts="svr3.ifile svr3z.ifile"
else
tm_file=i386/isccoff.h
tmake_file=i386/t-crtstuff
extra_parts="crtbegin.o crtend.o"
fi
install_headers_dir=install-headers-cpio
;;
changequote(,)dnl
i[34567]86-*-linux-gnuoldld*) # Intel 80386's running GNU/Linux
changequote([,])dnl # with a.out format using
# pre BFD linkers
xmake_file=x-linux-aout
tmake_file="t-linux-aout i386/t-crtstuff"
tm_file=i386/linux-oldld.h
gnu_ld=yes
float_format=i386
;;
changequote(,)dnl
i[34567]86-*-linux-gnuaout*) # Intel 80386's running GNU/Linux
changequote([,])dnl # with a.out format
xmake_file=x-linux-aout
tmake_file="t-linux-aout i386/t-crtstuff"
tm_file=i386/linux-aout.h
gnu_ld=yes
float_format=i386
;;
changequote(,)dnl
i[34567]86-*-linux-gnulibc1) # Intel 80386's running GNU/Linux
changequote([,])dnl # with ELF format using the
# GNU/Linux C library 5
xmake_file=x-linux
tm_file=i386/linux.h
tmake_file="t-linux t-linux-gnulibc1 i386/t-crtstuff"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
gnu_ld=yes
float_format=i386
if test x$enable_threads = xyes; then
thread_file='single'
fi
;;
changequote(,)dnl
i[34567]86-*-linux-gnu*) # Intel 80386's running GNU/Linux
changequote([,])dnl # with ELF format using glibc 2
# aka GNU/Linux C library 6
xmake_file=x-linux
tm_file=i386/linux.h
tmake_file="t-linux i386/t-crtstuff"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
gnu_ld=yes
float_format=i386
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
changequote(,)dnl
i[34567]86-*-gnu*)
float_format=i386
changequote([,])dnl
;;
changequote(,)dnl
i[34567]86-go32-msdos | i[34567]86-*-go32*)
changequote([,])dnl
echo "GO32/DJGPP V1.X is no longer supported. Use *-pc-msdosdjgpp for DJGPP V2.X instead."
exit 1
;;
changequote(,)dnl
i[34567]86-pc-msdosdjgpp*)
changequote([,])dnl
xm_file=i386/xm-djgpp.h
tm_file=i386/djgpp.h
tmake_file=i386/t-djgpp
xmake_file=i386/x-djgpp
gnu_ld=yes
gas=yes
exeext=.exe
case $host in *pc-msdosdjgpp*)
target_alias=djgpp
;;
esac
;;
changequote(,)dnl
i[34567]86-moss-msdos* | i[34567]86-*-moss*)
changequote([,])dnl
tm_file=i386/moss.h
tmake_file=t-libc-ok
gnu_ld=yes
gas=yes
;;
changequote(,)dnl
i[34567]86-*-lynxos*)
changequote([,])dnl
if test x$gas = xyes
then
tm_file=i386/lynx.h
else
tm_file=i386/lynx-ng.h
fi
xm_file=i386/xm-lynx.h
tmake_file=i386/t-i386bare
xmake_file=x-lynx
;;
changequote(,)dnl
i[34567]86-*-mach*)
changequote([,])dnl
tm_file=i386/mach.h
# tmake_file=t-libc-ok
use_collect2=yes
;;
changequote(,)dnl
i[34567]86-*-osfrose*) # 386 using OSF/rose
changequote([,])dnl
if test x$elf = xyes
then
tm_file=i386/osfelf.h
use_collect2=
else
tm_file=i386/osfrose.h
use_collect2=yes
fi
xm_file="i386/xm-osf.h ${xm_file}"
xmake_file=i386/x-osfrose
tmake_file=i386/t-osf
extra_objs=halfpic.o
;;
changequote(,)dnl
i[34567]86-go32-rtems*)
changequote([,])dnl
cpu_type=i386
xm_file=i386/xm-go32.h
tm_file=i386/go32-rtems.h
tmake_file="i386/t-go32 t-rtems"
;;
changequote(,)dnl
i[34567]86-*-rtemself*)
changequote([,])dnl
cpu_type=i386
tm_file=i386/rtemself.h
tmake_file="i386/t-i386bare t-rtems"
;;
changequote(,)dnl
i[34567]86-*-rtems*)
changequote([,])dnl
cpu_type=i386
tm_file=i386/rtems.h
tmake_file="i386/t-i386bare t-rtems"
;;
changequote(,)dnl
i[34567]86-*-sco3.2v5*) # 80386 running SCO Open Server 5
changequote([,])dnl
xm_file="xm-siglist.h xm-alloca.h ${xm_file} i386/xm-sco5.h"
xm_defines="USG SVR3"
xmake_file=i386/x-sco5
install_headers_dir=install-headers-cpio
tm_file=i386/sco5.h
if test x$gas = xyes
then
tm_file="i386/sco5gas.h ${tm_file}"
tmake_file=i386/t-sco5gas
else
tmake_file=i386/t-sco5
fi
extra_parts="crti.o crtbegin.o crtend.o crtbeginS.o crtendS.o"
;;
changequote(,)dnl
i[34567]86-*-sco3.2v4*) # 80386 running SCO 3.2v4 system
changequote([,])dnl
xm_file="${xm_file} i386/xm-sco.h"
xm_defines="USG SVR3 BROKEN_LDEXP SMALL_ARG_MAX NO_SYS_SIGLIST"
xmake_file=i386/x-sco4
install_headers_dir=install-headers-cpio
if test x$stabs = xyes
then
tm_file=i386/sco4dbx.h
tmake_file=i386/t-svr3dbx
extra_parts="svr3.ifile svr3z.rfile"
else
tm_file=i386/sco4.h
tmake_file=i386/t-crtstuff
extra_parts="crtbegin.o crtend.o"
fi
truncate_target=yes
;;
changequote(,)dnl
i[34567]86-*-sco*) # 80386 running SCO system
changequote([,])dnl
xm_file=i386/xm-sco.h
xmake_file=i386/x-sco
install_headers_dir=install-headers-cpio
if test x$stabs = xyes
then
tm_file=i386/scodbx.h
tmake_file=i386/t-svr3dbx
extra_parts="svr3.ifile svr3z.rfile"
else
tm_file=i386/sco.h
extra_parts="crtbegin.o crtend.o"
tmake_file=i386/t-crtstuff
fi
truncate_target=yes
;;
changequote(,)dnl
i[34567]86-*-solaris2*)
changequote([,])dnl
xm_file="xm-siglist.h xm-alloca.h ${xm_file}"
xm_defines="USG POSIX SMALL_ARG_MAX"
- if test x$stabs = xyes
- then
- tm_file=i386/sol2dbg.h
+ if test x$gas = xyes; then
+ # Only needed if gas does not support -s
+ tm_file=i386/sol2gas.h
else
tm_file=i386/sol2.h
fi
tmake_file=i386/t-sol2
extra_parts="crt1.o crti.o crtn.o gcrt1.o gmon.o crtbegin.o crtend.o"
xmake_file=x-svr4
if test x$enable_threads = xyes; then
thread_file='solaris'
fi
;;
changequote(,)dnl
i[34567]86-*-sysv5*) # Intel x86 on System V Release 5
changequote([,])dnl
xm_file="xm-alloca.h xm-siglist.h ${xm_file}"
xm_defines="USG POSIX"
tm_file=i386/sysv5.h
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
tmake_file=i386/t-crtpic
xmake_file=x-svr4
extra_parts="crtbegin.o crtend.o"
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
changequote(,)dnl
i[34567]86-*-sysv4*) # Intel 80386's running system V.4
changequote([,])dnl
xm_file="xm-siglist.h xm-alloca.h ${xm_file}"
xm_defines="USG POSIX SMALL_ARG_MAX"
tm_file=i386/sysv4.h
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
tmake_file=i386/t-crtpic
xmake_file=x-svr4
extra_parts="crtbegin.o crtend.o"
;;
changequote(,)dnl
i[34567]86-*-udk*) # Intel x86 on SCO UW/OSR5 Dev Kit
changequote([,])dnl
xm_file="xm-alloca.h xm-siglist.h ${xm_file}"
xm_defines="USG POSIX"
tm_file=i386/udk.h
tmake_file="i386/t-crtpic i386/t-udk"
xmake_file=x-svr4
extra_parts="crtbegin.o crtend.o"
install_headers_dir=install-headers-cpio
;;
changequote(,)dnl
i[34567]86-*-osf1*) # Intel 80386's running OSF/1 1.3+
changequote([,])dnl
cpu_type=i386
xm_file="${xm_file} xm-svr4.h i386/xm-sysv4.h i386/xm-osf1elf.h"
xm_defines="USE_C_ALLOCA SMALL_ARG_MAX"
if test x$stabs = xyes
then
tm_file=i386/osf1elfgdb.h
else
tm_file=i386/osf1elf.h
fi
tmake_file=i386/t-osf1elf
xmake_file=i386/x-osf1elf
extra_parts="crti.o crtn.o crtbegin.o crtend.o"
;;
changequote(,)dnl
i[34567]86-*-sysv*) # Intel 80386's running system V
changequote([,])dnl
xm_defines="USG SVR3"
xmake_file=i386/x-sysv3
if test x$gas = xyes
then
if test x$stabs = xyes
then
tm_file=i386/svr3dbx.h
tmake_file=i386/t-svr3dbx
extra_parts="svr3.ifile svr3z.rfile"
else
tm_file=i386/svr3gas.h
extra_parts="crtbegin.o crtend.o"
tmake_file=i386/t-crtstuff
fi
else
tm_file=i386/sysv3.h
extra_parts="crtbegin.o crtend.o"
tmake_file=i386/t-crtstuff
fi
;;
i386-*-vsta) # Intel 80386's running VSTa kernel
xm_file="${xm_file} i386/xm-vsta.h"
tm_file=i386/vsta.h
tmake_file=i386/t-vsta
xmake_file=i386/x-vsta
;;
changequote(,)dnl
i[34567]86-*-win32)
changequote([,])dnl
xm_file="${xm_file} i386/xm-cygwin.h"
tmake_file=i386/t-cygwin
tm_file=i386/win32.h
xmake_file=i386/x-cygwin
extra_objs=winnt.o
if test x$enable_threads = xyes; then
thread_file='win32'
fi
exeext=.exe
;;
changequote(,)dnl
i[34567]86-*-pe | i[34567]86-*-cygwin*)
changequote([,])dnl
xm_file="${xm_file} i386/xm-cygwin.h"
tmake_file=i386/t-cygwin
tm_file=i386/cygwin.h
xmake_file=i386/x-cygwin
extra_objs=winnt.o
if test x$enable_threads = xyes; then
thread_file='win32'
fi
exeext=.exe
;;
changequote(,)dnl
i[34567]86-*-mingw32*)
changequote([,])dnl
tm_file=i386/mingw32.h
xm_file="${xm_file} i386/xm-mingw32.h"
tmake_file="i386/t-cygwin i386/t-mingw32"
extra_objs=winnt.o
xmake_file=i386/x-cygwin
if test x$enable_threads = xyes; then
thread_file='win32'
fi
exeext=.exe
case $machine in
*mingw32msv*)
;;
*minwg32crt* | *mingw32*)
tm_file="${tm_file} i386/crtdll.h"
;;
esac
;;
changequote(,)dnl
i[34567]86-*-uwin*)
changequote([,])dnl
tm_file=i386/uwin.h
xm_file="${xm_file} i386/xm-uwin.h"
xm_defines="USG NO_STAB_H NO_SYS_SIGLIST"
tmake_file="i386/t-cygwin i386/t-uwin"
extra_objs=winnt.o
xmake_file=i386/x-cygwin
if test x$enable_threads = xyes; then
thread_file='win32'
fi
exeext=.exe
;;
changequote(,)dnl
i[34567]86-*-interix*)
changequote([,])dnl
tm_file="i386/i386-interix.h interix.h"
xm_file="i386/xm-i386-interix.h xm-interix.h"
xm_defines="USG NO_SYS_SIGLIST"
tmake_file="i386/t-interix"
extra_objs=interix.o
xmake_file=x-interix
if test x$enable_threads = xyes ; then
thread_file='posix'
fi
if test x$stabs = xyes ; then
tm_file="${tm_file} dbxcoff.h"
fi
;;
changequote(,)dnl
i[34567]86-*-winnt3*)
changequote([,])dnl
tm_file=i386/win-nt.h
out_file=i386/i386.c
xm_file="xm-winnt.h ${xm_file}"
xmake_file=winnt/x-winnt
tmake_file=i386/t-winnt
extra_host_objs="winnt.o oldnames.o"
extra_gcc_objs="spawnv.o oldnames.o"
if test x$gnu_ld != xyes
then
extra_programs=ld.exe
fi
if test x$enable_threads = xyes; then
thread_file='win32'
fi
;;
changequote(,)dnl
i[34567]86-dg-dgux*)
changequote([,])dnl
xm_file="xm-alloca.h xm-siglist.h ${xm_file}"
xm_defines="USG POSIX"
out_file=i386/dgux.c
tm_file=i386/dgux.h
tmake_file=i386/t-dgux
xmake_file=i386/x-dgux
install_headers_dir=install-headers-cpio
;;
i860-alliant-*) # Alliant FX/2800
tm_file="${tm_file} svr4.h i860/sysv4.h i860/fx2800.h"
xm_file="${xm_file}"
xmake_file=i860/x-fx2800
tmake_file=i860/t-fx2800
extra_parts="crtbegin.o crtend.o"
;;
i860-*-bsd*)
tm_file="${tm_file} i860/bsd.h"
if test x$gas = xyes
then
tm_file="${tm_file} i860/bsd-gas.h"
fi
use_collect2=yes
;;
i860-*-mach*)
tm_file="${tm_file} i860/mach.h"
tmake_file=t-libc-ok
;;
i860-*-osf*) # Intel Paragon XP/S, OSF/1AD
tm_file="${tm_file} svr3.h i860/paragon.h"
xm_defines="USG SVR3"
tmake_file=t-osf
;;
i860-*-sysv3*)
tm_file="${tm_file} svr3.h i860/sysv3.h"
xm_defines="USG SVR3"
xmake_file=i860/x-sysv3
extra_parts="crtbegin.o crtend.o"
;;
i860-*-sysv4*)
tm_file="${tm_file} svr4.h i860/sysv4.h"
xm_defines="USG SVR3"
xmake_file=i860/x-sysv4
tmake_file=t-svr4
extra_parts="crtbegin.o crtend.o"
;;
i960-wrs-vxworks5 | i960-wrs-vxworks5.0*)
tm_file="${tm_file} i960/vx960.h"
tmake_file=i960/t-vxworks960
use_collect2=yes
thread_file='vxworks'
;;
i960-wrs-vxworks5* | i960-wrs-vxworks)
tm_file="${tm_file} dbxcoff.h i960/i960-coff.h i960/vx960-coff.h"
tmake_file=i960/t-vxworks960
use_collect2=yes
thread_file='vxworks'
;;
i960-wrs-vxworks*)
tm_file="${tm_file} i960/vx960.h"
tmake_file=i960/t-vxworks960
use_collect2=yes
thread_file='vxworks'
;;
i960-*-coff*)
tm_file="${tm_file} dbxcoff.h i960/i960-coff.h libgloss.h"
tmake_file=i960/t-960bare
use_collect2=yes
;;
i960-*-rtems)
tmake_file="i960/t-960bare t-rtems"
tm_file="${tm_file} dbxcoff.h i960/rtems.h"
use_collect2=yes
;;
i960-*-*) # Default i960 environment.
use_collect2=yes
;;
m32r-*-elf*)
extra_parts="crtinit.o crtfini.o"
;;
m68000-convergent-sysv*)
tm_file=m68k/ctix.h
xm_file="m68k/xm-3b1.h ${xm_file}"
xm_defines=USG
use_collect2=yes
extra_headers=math-68881.h
;;
m68000-hp-bsd*) # HP 9000/200 running BSD
tm_file=m68k/hp2bsd.h
xmake_file=m68k/x-hp2bsd
use_collect2=yes
extra_headers=math-68881.h
;;
m68000-hp-hpux*) # HP 9000 series 300
xm_file="xm_alloca.h ${xm_file}"
xm_defines="USG NO_SYS_SIGLIST"
if test x$gas = xyes
then
xmake_file=m68k/x-hp320g
tm_file=m68k/hp310g.h
else
xmake_file=m68k/x-hp320
tm_file=m68k/hp310.h
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
extra_headers=math-68881.h
;;
m68000-sun-sunos3*)
tm_file=m68k/sun2.h
use_collect2=yes
extra_headers=math-68881.h
;;
m68000-sun-sunos4*)
tm_file=m68k/sun2o4.h
use_collect2=yes
extra_headers=math-68881.h
;;
m68000-att-sysv*)
xm_file="m68k/xm-3b1.h ${xm_file}"
xm_defines=USG
if test x$gas = xyes
then
tm_file=m68k/3b1g.h
else
tm_file=m68k/3b1.h
fi
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-apple-aux*) # Apple Macintosh running A/UX
xm_defines="USG AUX"
tmake_file=m68k/t-aux
install_headers_dir=install-headers-cpio
extra_headers=math-68881.h
extra_parts="crt1.o mcrt1.o maccrt1.o crt2.o crtn.o"
tm_file=
if test "$gnu_ld" = yes
then
tm_file="${tm_file} m68k/auxgld.h"
else
tm_file="${tm_file} m68k/auxld.h"
fi
if test "$gas" = yes
then
tm_file="${tm_file} m68k/auxgas.h"
else
tm_file="${tm_file} m68k/auxas.h"
fi
tm_file="${tm_file} m68k/a-ux.h"
float_format=m68k
;;
m68k-apollo-*)
tm_file=m68k/apollo68.h
xmake_file=m68k/x-apollo68
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-altos-sysv*) # Altos 3068
if test x$gas = xyes
then
tm_file=m68k/altos3068.h
xm_defines=USG
else
echo "The Altos is supported only with the GNU assembler" 1>&2
exit 1
fi
extra_headers=math-68881.h
;;
m68k-bull-sysv*) # Bull DPX/2
if test x$gas = xyes
then
if test x$stabs = xyes
then
tm_file=m68k/dpx2cdbx.h
else
tm_file=m68k/dpx2g.h
fi
else
tm_file=m68k/dpx2.h
fi
xm_file="xm-alloca.h ${xm_file}"
xm_defines=USG
xmake_file=m68k/x-dpx2
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-atari-sysv4*) # Atari variant of V.4.
tm_file=m68k/atari.h
xm_file="xm-alloca.h ${xm_file}"
xm_defines="USG FULL_PROTOTYPES"
tmake_file=t-svr4
extra_parts="crtbegin.o crtend.o"
extra_headers=math-68881.h
float_format=m68k
;;
m68k-motorola-sysv*)
tm_file=m68k/mot3300.h
xm_file="xm-alloca.h m68k/xm-mot3300.h ${xm_file}"
xm_defines=NO_SYS_SIGLIST
if test x$gas = xyes
then
xmake_file=m68k/x-mot3300-gas
if test x$gnu_ld = xyes
then
tmake_file=m68k/t-mot3300-gald
else
tmake_file=m68k/t-mot3300-gas
use_collect2=yes
fi
else
xmake_file=m68k/x-mot3300
if test x$gnu_ld = xyes
then
tmake_file=m68k/t-mot3300-gld
else
tmake_file=m68k/t-mot3300
use_collect2=yes
fi
fi
gdb_needs_out_file_path=yes
extra_parts="crt0.o mcrt0.o"
extra_headers=math-68881.h
float_format=m68k
;;
m68k-ncr-sysv*) # NCR Tower 32 SVR3
tm_file=m68k/tower-as.h
xm_defines="USG SVR3"
xmake_file=m68k/x-tower
extra_parts="crtbegin.o crtend.o"
extra_headers=math-68881.h
;;
m68k-plexus-sysv*)
tm_file=m68k/plexus.h
xm_file="xm-alloca.h m68k/xm-plexus.h ${xm_file}"
xm_defines=USG
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-tti-*)
tm_file=m68k/pbb.h
xm_file="xm-alloca.h ${xm_file}"
xm_defines=USG
extra_headers=math-68881.h
;;
m68k-crds-unos*)
xm_file="xm-alloca.h m68k/xm-crds.h ${xm_file}"
xm_defines="USG unos"
xmake_file=m68k/x-crds
tm_file=m68k/crds.h
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-cbm-sysv4*) # Commodore variant of V.4.
tm_file=m68k/amix.h
xm_file="xm-alloca.h ${xm_file}"
xm_defines="USG FULL_PROTOTYPES"
xmake_file=m68k/x-amix
tmake_file=t-svr4
extra_parts="crtbegin.o crtend.o"
extra_headers=math-68881.h
float_format=m68k
;;
m68k-ccur-rtu)
tm_file=m68k/ccur-GAS.h
xmake_file=m68k/x-ccur
extra_headers=math-68881.h
use_collect2=yes
float_format=m68k
;;
m68k-hp-bsd4.4*) # HP 9000/3xx running 4.4bsd
tm_file=m68k/hp3bsd44.h
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-hp-bsd*) # HP 9000/3xx running Berkeley Unix
tm_file=m68k/hp3bsd.h
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-isi-bsd*)
if test x$with_fp = xno
then
tm_file=m68k/isi-nfp.h
else
tm_file=m68k/isi.h
float_format=m68k
fi
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-hp-hpux7*) # HP 9000 series 300 running HPUX version 7.
xm_file="xm_alloca.h ${xm_file}"
xm_defines="USG NO_SYS_SIGLIST"
if test x$gas = xyes
then
xmake_file=m68k/x-hp320g
tm_file=m68k/hp320g.h
else
xmake_file=m68k/x-hp320
tm_file=m68k/hpux7.h
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-hp-hpux*) # HP 9000 series 300
xm_file="xm_alloca.h ${xm_file}"
xm_defines="USG NO_SYS_SIGLIST"
if test x$gas = xyes
then
xmake_file=m68k/x-hp320g
tm_file=m68k/hp320g.h
else
xmake_file=m68k/x-hp320
tm_file=m68k/hp320.h
fi
install_headers_dir=install-headers-cpio
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-sun-mach*)
tm_file=m68k/sun3mach.h
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-sony-newsos3*)
if test x$gas = xyes
then
tm_file=m68k/news3gas.h
else
tm_file=m68k/news3.h
fi
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-sony-bsd* | m68k-sony-newsos*)
if test x$gas = xyes
then
tm_file=m68k/newsgas.h
else
tm_file=m68k/news.h
fi
use_collect2=yes
extra_headers=math-68881.h
float_format=m68k
;;
m68k-next-nextstep2*)
tm_file=m68k/next21.h
xm_file="m68k/xm-next.h ${xm_file}"
tmake_file=m68k/t-next
xmake_file=m68k/x-next
extra_objs=nextstep.o
extra_headers=math-68881.h
use_collect2=yes
float_format=m68k
;;
m68k-next-nextstep3*)
tm_file=m68k/next.h
xm_file="m68k/xm-next.h ${xm_file}"
tmake_file=m68k/t-next
xmake_file=m68k/x-next
extra_objs=nextstep.o
extra_parts="crtbegin.o crtend.o"
extra_headers=math-68881.h
float_format=m68k
if test x$enable_threads = xyes; then
thread_file='mach'
fi
;;
m68k-sun-sunos3*)
if test x$with_fp = xno
then
tm_file=m68k/sun3n3.h
else
tm_file=m68k/sun3o3.h
float_format=m68k
fi
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-sun-sunos*) # For SunOS 4 (the default).
if test x$with_fp = xno
then
tm_file=m68k/sun3n.h
else
tm_file=m68k/sun3.h
float_format=m68k
fi
use_collect2=yes
extra_headers=math-68881.h
;;
m68k-wrs-vxworks*)
tm_file=m68k/vxm68k.h
tmake_file=m68k/t-vxworks68
extra_headers=math-68881.h
thread_file='vxworks'
float_format=m68k
;;
m68k-*-aout*)
tmake_file=m68k/t-m68kbare
tm_file="m68k/m68k-aout.h libgloss.h"
extra_headers=math-68881.h
float_format=m68k
;;
m68k-*-coff*)
tmake_file=m68k/t-m68kbare
tm_file="m68k/m68k-coff.h dbx.h libgloss.h"
extra_headers=math-68881.h
float_format=m68k
;;
m68020-*-elf* | m68k-*-elf*)
tm_file="m68k/m68020-elf.h libgloss.h"
xm_file=m68k/xm-m68kv.h
tmake_file=m68k/t-m68kelf
header_files=math-68881.h
;;
m68k-*-lynxos*)
if test x$gas = xyes
then
tm_file=m68k/lynx.h
else
tm_file=m68k/lynx-ng.h
fi
xm_file=m68k/xm-lynx.h
xmake_file=x-lynx
tmake_file=m68k/t-lynx
extra_headers=math-68881.h
float_format=m68k
;;
m68k*-*-netbsd*)
tm_file=m68k/netbsd.h
tmake_file=t-netbsd
float_format=m68k
use_collect2=yes
;;
m68k*-*-openbsd*)
float_format=m68k
# we need collect2 until our bug is fixed...
use_collect2=yes
;;
m68k-*-sysv3*) # Motorola m68k's running system V.3
xm_file="xm-alloca.h ${xm_file}"
xm_defines=USG
xmake_file=m68k/x-m68kv
extra_parts="crtbegin.o crtend.o"
extra_headers=math-68881.h
float_format=m68k
;;
m68k-*-sysv4*) # Motorola m68k's running system V.4
tm_file=m68k/m68kv4.h
xm_file="xm-alloca.h ${xm_file}"
xm_defines=USG
tmake_file=t-svr4
extra_parts="crtbegin.o crtend.o"
extra_headers=math-68881.h
float_format=m68k
;;
m68k-*-linux-gnuaout*) # Motorola m68k's running GNU/Linux
# with a.out format
xmake_file=x-linux
tm_file=m68k/linux-aout.h
tmake_file="t-linux-aout m68k/t-linux-aout"
extra_headers=math-68881.h
float_format=m68k
gnu_ld=yes
;;
m68k-*-linux-gnulibc1) # Motorola m68k's running GNU/Linux
# with ELF format using the
# GNU/Linux C library 5
xmake_file=x-linux
tm_file=m68k/linux.h
tmake_file="t-linux t-linux-gnulibc1 m68k/t-linux"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
extra_headers=math-68881.h
float_format=m68k
gnu_ld=yes
;;
m68k-*-linux-gnu*) # Motorola m68k's running GNU/Linux
# with ELF format using glibc 2
# aka the GNU/Linux C library 6.
xmake_file=x-linux
tm_file=m68k/linux.h
tmake_file="t-linux m68k/t-linux"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
extra_headers=math-68881.h
float_format=m68k
gnu_ld=yes
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
m68k-*-psos*)
tmake_file=m68k/t-m68kbare
tm_file=m68k/m68k-psos.h
extra_headers=math-68881.h
float_format=m68k
;;
m68k-*-rtems*)
tmake_file="m68k/t-m68kbare t-rtems"
tm_file=m68k/rtems.h
extra_headers=math-68881.h
float_format=m68k
;;
m88k-dg-dgux*)
case $machine in
m88k-dg-dguxbcs*)
tm_file=m88k/dguxbcs.h
tmake_file=m88k/t-dguxbcs
;;
*)
tm_file=m88k/dgux.h
tmake_file=m88k/t-dgux
;;
esac
extra_parts="crtbegin.o bcscrtbegin.o crtend.o m88kdgux.ld"
xmake_file=m88k/x-dgux
if test x$gas = xyes
then
tmake_file=m88k/t-dgux-gas
fi
;;
m88k-dolphin-sysv3*)
tm_file=m88k/dolph.h
extra_parts="crtbegin.o crtend.o"
xm_file="m88k/xm-sysv3.h ${xm_file}"
xmake_file=m88k/x-dolph
if test x$gas = xyes
then
tmake_file=m88k/t-m88k-gas
fi
;;
m88k-tektronix-sysv3)
tm_file=m88k/tekXD88.h
extra_parts="crtbegin.o crtend.o"
xm_file="m88k/xm-sysv3.h ${xm_file}"
xmake_file=m88k/x-tekXD88
if test x$gas = xyes
then
tmake_file=m88k/t-m88k-gas
fi
;;
m88k-*-aout*)
tm_file=m88k/m88k-aout.h
;;
m88k-*-coff*)
tm_file=m88k/m88k-coff.h
tmake_file=m88k/t-bug
;;
m88k-*-luna*)
tm_file=m88k/luna.h
extra_parts="crtbegin.o crtend.o"
if test x$gas = xyes
then
tmake_file=m88k/t-luna-gas
else
tmake_file=m88k/t-luna
fi
;;
m88k-*-openbsd*)
tmake_file="${tmake_file} m88k/t-luna-gas"
;;
m88k-*-sysv3*)
tm_file=m88k/sysv3.h
extra_parts="crtbegin.o crtend.o"
xm_file="m88k/xm-sysv3.h ${xm_file}"
xmake_file=m88k/x-sysv3
if test x$gas = xyes
then
tmake_file=m88k/t-m88k-gas
fi
;;
m88k-*-sysv4*)
tm_file=m88k/sysv4.h
extra_parts="crtbegin.o crtend.o"
xmake_file=m88k/x-sysv4
tmake_file=m88k/t-sysv4
;;
mips-sgi-irix6*) # SGI System V.4., IRIX 6
tm_file=mips/iris6.h
xm_file=mips/xm-iris6.h
xmake_file=mips/x-iris6
tmake_file=mips/t-iris6
# if test x$enable_threads = xyes; then
# thread_file='irix'
# fi
;;
mips-wrs-vxworks)
tm_file="mips/elf.h libgloss.h mips/vxworks.h"
tmake_file=mips/t-ecoff
gas=yes
gnu_ld=yes
extra_parts="crtbegin.o crtend.o"
thread_file='vxworks'
;;
mips-sgi-irix5cross64) # Irix5 host, Irix 6 target, cross64
tm_file="mips/iris6.h mips/cross64.h"
xm_defines=USG
xm_file="mips/xm-iris5.h"
xmake_file=mips/x-iris
tmake_file=mips/t-cross64
# See comment in mips/iris[56].h files.
use_collect2=yes
# if test x$enable_threads = xyes; then
# thread_file='irix'
# fi
;;
mips-sni-sysv4)
if test x$gas = xyes
then
if test x$stabs = xyes
then
tm_file=mips/iris5gdb.h
else
tm_file="mips/sni-svr4.h mips/sni-gas.h"
fi
else
tm_file=mips/sni-svr4.h
fi
xm_defines=USG
xmake_file=mips/x-sni-svr4
tmake_file=mips/t-mips-gas
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-sgi-irix5*) # SGI System V.4., IRIX 5
if test x$gas = xyes
then
tm_file="mips/iris5.h mips/iris5gas.h"
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
else
tm_file=mips/iris5.h
fi
xm_defines=USG
xm_file="mips/xm-iris5.h"
xmake_file=mips/x-iris
# mips-tfile doesn't work yet
tmake_file=mips/t-mips-gas
# See comment in mips/iris5.h file.
use_collect2=yes
# if test x$enable_threads = xyes; then
# thread_file='irix'
# fi
;;
mips-sgi-irix4loser*) # Mostly like a MIPS.
tm_file="mips/iris4loser.h mips/iris3.h ${tm_file} mips/iris4.h"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_defines=USG
xmake_file=mips/x-iris
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
# if test x$enable_threads = xyes; then
# thread_file='irix'
# fi
;;
mips-sgi-irix4*) # Mostly like a MIPS.
tm_file="mips/iris3.h ${tm_file} mips/iris4.h"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_defines=USG
xmake_file=mips/x-iris
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
# if test x$enable_threads = xyes; then
# thread_file='irix'
# fi
;;
mips-sgi-*) # Mostly like a MIPS.
tm_file="mips/iris3.h ${tm_file}"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_defines=USG
xmake_file=mips/x-iris3
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-dec-osfrose*) # Decstation running OSF/1 reference port with OSF/rose.
tm_file="mips/osfrose.h ${tm_file}"
xmake_file=mips/x-osfrose
tmake_file=mips/t-osfrose
extra_objs=halfpic.o
use_collect2=yes
;;
mips-dec-osf*) # Decstation running OSF/1 as shipped by DIGITAL
tm_file=mips/dec-osf1.h
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xmake_file=mips/x-dec-osf1
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
tmake_file=mips/t-ultrix
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-dec-bsd*) # Decstation running 4.4 BSD
tm_file=mips/dec-bsd.h
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
tmake_file=mips/t-ultrix
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mipsel-*-netbsd* | mips-dec-netbsd*) # Decstation running NetBSD
tm_file=mips/netbsd.h
# On NetBSD, the headers are already okay, except for math.h.
tmake_file=t-netbsd
;;
mips*-*-linux*) # Linux MIPS, either endian.
xmake_file=x-linux
xm_file="xm-siglist.h ${xm_file}"
case $machine in
mipsel-*) tm_file="mips/elfl.h mips/linux.h" ;;
*) tm_file="mips/elf.h mips/linux.h" ;;
esac
extra_parts="crtbegin.o crtend.o"
gnu_ld=yes
gas=yes
;;
mips*el-*-openbsd*) # mips little endian
target_cpu_default="MASK_GAS|MASK_ABICALLS"
tm_file=mips/openbsd.h
;;
mips*-*-openbsd*) # mips big endian
target_cpu_default="MASK_GAS|MASK_ABICALLS"
tm_file=mips/openbsd-be.h
;;
mips-sony-bsd* | mips-sony-newsos*) # Sony NEWS 3600 or risc/news.
tm_file="mips/news4.h ${tm_file}"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
xmake_file=mips/x-sony
;;
mips-sony-sysv*) # Sony NEWS 3800 with NEWSOS5.0.
# That is based on svr4.
# t-svr4 is not right because this system doesn't use ELF.
tm_file="mips/news5.h ${tm_file}"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_file="xm-siglist.h ${xm_file}"
xm_defines=USG
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-tandem-sysv4*) # Tandem S2 running NonStop UX
tm_file="mips/svr4-5.h mips/svr4-t.h"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_file="xm-siglist.h ${xm_file}"
xm_defines=USG
xmake_file=mips/x-sysv
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
extra_parts="crtbegin.o crtend.o"
else
tmake_file=mips/t-mips
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-*-ultrix* | mips-dec-mach3) # Decstation.
tm_file="mips/ultrix.h ${tm_file}"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xmake_file=mips/x-ultrix
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
tmake_file=mips/t-ultrix
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
changequote(,)dnl
mips-*-riscos[56789]bsd*)
changequote([,])dnl
tm_file=mips/bsd-5.h # MIPS BSD 4.3, RISC-OS 5.0
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
if test x$gas = xyes
then
tmake_file=mips/t-bsd-gas
else
tmake_file=mips/t-bsd
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
changequote(,)dnl
mips-*-bsd* | mips-*-riscosbsd* | mips-*-riscos[1234]bsd*)
changequote([,])dnl
tm_file="mips/bsd-4.h ${tm_file}" # MIPS BSD 4.3, RISC-OS 4.0
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
if test x$gas = xyes
then
tmake_file=mips/t-bsd-gas
else
tmake_file=mips/t-bsd
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
changequote(,)dnl
mips-*-riscos[56789]sysv4*)
changequote([,])dnl
tm_file=mips/svr4-5.h # MIPS System V.4., RISC-OS 5.0
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_file="xm-siglist.h ${xm_file}"
xmake_file=mips/x-sysv
if test x$gas = xyes
then
tmake_file=mips/t-svr4-gas
else
tmake_file=mips/t-svr4
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
changequote(,)dnl
mips-*-sysv4* | mips-*-riscos[1234]sysv4* | mips-*-riscossysv4*)
changequote([,])dnl
tm_file="mips/svr4-4.h ${tm_file}"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_defines=USG
xmake_file=mips/x-sysv
if test x$gas = xyes
then
tmake_file=mips/t-svr4-gas
else
tmake_file=mips/t-svr4
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
changequote(,)dnl
mips-*-riscos[56789]sysv*)
changequote([,])dnl
tm_file=mips/svr3-5.h # MIPS System V.3, RISC-OS 5.0
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_defines=USG
xmake_file=mips/x-sysv
if test x$gas = xyes
then
tmake_file=mips/t-svr3-gas
else
tmake_file=mips/t-svr3
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-*-sysv* | mips-*-riscos*sysv*)
tm_file="mips/svr3-4.h ${tm_file}"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
xm_defines=USG
xmake_file=mips/x-sysv
if test x$gas = xyes
then
tmake_file=mips/t-svr3-gas
else
tmake_file=mips/t-svr3
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
changequote(,)dnl
mips-*-riscos[56789]*) # Default MIPS RISC-OS 5.0.
changequote([,])dnl
tm_file=mips/mips-5.h
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mips-*-gnu*)
;;
mipsel-*-ecoff*)
tm_file=mips/ecoffl.h
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
tmake_file=mips/t-ecoff
;;
mips-*-ecoff*)
tm_file="gofast.h mips/ecoff.h"
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
tmake_file=mips/t-ecoff
;;
mipsel-*-elf*)
tm_file="mips/elfl.h libgloss.h"
tmake_file=mips/t-elf
;;
mips-*-elf*)
tm_file="mips/elf.h"
tmake_file=mips/t-elf
;;
mips64el-*-elf*)
tm_file="mips/elfl64.h"
tmake_file=mips/t-elf
;;
mips64orionel-*-elf*)
tm_file="mips/elforion.h mips/elfl64.h libgloss.h"
tmake_file=mips/t-elf
;;
mips64-*-elf*)
tm_file="mips/elf64.h"
tmake_file=mips/t-elf
;;
mips64orion-*-elf*)
tm_file="mips/elforion.h mips/elf64.h libgloss.h"
tmake_file=mips/t-elf
;;
mips64orion-*-rtems*)
tm_file="mips/elforion.h mips/elf64.h mips/rtems64.h"
tmake_file="mips/t-ecoff t-rtems"
;;
mipstx39el-*-elf*)
tm_file="mips/r3900.h mips/elfl.h mips/abi64.h"
tmake_file=mips/t-r3900
;;
mipstx39-*-elf*)
tm_file="mips/r3900.h mips/elf.h mips/abi64.h"
tmake_file=mips/t-r3900
;;
mips-*-*) # Default MIPS RISC-OS 4.0.
if test x$stabs = xyes; then
tm_file="${tm_file} dbx.h"
fi
if test x$gas = xyes
then
tmake_file=mips/t-mips-gas
else
extra_passes="mips-tfile mips-tdump"
fi
if test x$gnu_ld != xyes
then
use_collect2=yes
fi
;;
mn10200-*-*)
cpu_type=mn10200
tm_file="mn10200/mn10200.h"
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
use_collect2=no
;;
mn10300-*-*)
cpu_type=mn10300
tm_file="mn10300/mn10300.h"
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
use_collect2=no
;;
ns32k-encore-bsd*)
tm_file=ns32k/encore.h
use_collect2=yes
;;
ns32k-sequent-bsd*)
tm_file=ns32k/sequent.h
use_collect2=yes
;;
ns32k-tek6100-bsd*)
tm_file=ns32k/tek6100.h
use_collect2=yes
;;
ns32k-tek6200-bsd*)
tm_file=ns32k/tek6200.h
use_collect2=yes
;;
# This has not been updated to GCC 2.
# ns32k-ns-genix*)
# xm_defines=USG
# xmake_file=ns32k/x-genix
# tm_file=ns32k/genix.h
# use_collect2=yes
# ;;
ns32k-merlin-*)
tm_file=ns32k/merlin.h
use_collect2=yes
;;
ns32k-pc532-mach*)
tm_file=ns32k/pc532-mach.h
use_collect2=yes
;;
ns32k-pc532-minix*)
tm_file=ns32k/pc532-min.h
xm_file="ns32k/xm-pc532-min.h ${xm-file}"
xm_defines=USG
use_collect2=yes
;;
ns32k-*-netbsd*)
tm_file=ns32k/netbsd.h
xm_file="ns32k/xm-netbsd.h ${xm_file}"
# On NetBSD, the headers are already okay, except for math.h.
tmake_file=t-netbsd
use_collect2=yes
;;
pdp11-*-bsd)
tm_file="${tm_file} pdp11/2bsd.h"
;;
pdp11-*-*)
;;
ns32k-*-openbsd*)
# Nothing special
;;
pyramid-*-*)
cpu_type=pyr
xmake_file=pyr/x-pyr
use_collect2=yes
;;
romp-*-aos*)
use_collect2=yes
;;
romp-*-mach*)
xmake_file=romp/x-mach
use_collect2=yes
;;
romp-*-openbsd*)
# Nothing special
;;
powerpc-*-openbsd*)
tmake_file="${tmake_file} rs6000/t-rs6000 rs6000/t-openbsd"
;;
powerpc-*-beos*)
cpu_type=rs6000
tm_file=rs6000/beos.h
xm_file=rs6000/xm-beos.h
tmake_file=rs6000/t-beos
xmake_file=rs6000/x-beos
;;
powerpc-*-sysv* | powerpc-*-elf*)
tm_file=rs6000/sysv4.h
xm_file="xm-siglist.h rs6000/xm-sysv4.h"
xm_defines="USG POSIX"
extra_headers=ppc-asm.h
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcos rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc rs6000/t-ppccomm"
fi
xmake_file=rs6000/x-sysv4
;;
powerpc-*-eabiaix*)
tm_file=rs6000/eabiaix.h
tmake_file="rs6000/t-ppcgas rs6000/t-ppccomm"
extra_headers=ppc-asm.h
;;
powerpc-*-eabisim*)
tm_file=rs6000/eabisim.h
tmake_file="rs6000/t-ppcgas rs6000/t-ppccomm"
extra_headers=ppc-asm.h
;;
powerpc-*-eabi*)
tm_file=rs6000/eabi.h
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcgas rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc rs6000/t-ppccomm"
fi
extra_headers=ppc-asm.h
;;
powerpc-*-rtems*)
tm_file=rs6000/rtems.h
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcgas t-rtems rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc t-rtems rs6000/t-ppccomm"
fi
extra_headers=ppc-asm.h
;;
powerpc-*-linux-gnulibc1)
tm_file=rs6000/linux.h
xm_file=rs6000/xm-sysv4.h
out_file=rs6000/rs6000.c
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcos t-linux t-linux-gnulibc1 rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc t-linux t-linux-gnulibc1 rs6000/t-ppccomm"
fi
xmake_file=x-linux
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
extra_headers=ppc-asm.h
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
powerpc-*-linux-gnu*)
tm_file=rs6000/linux.h
xm_file="xm-siglist.h rs6000/xm-sysv4.h"
xm_defines="USG ${xm_defines}"
out_file=rs6000/rs6000.c
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcos t-linux rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc t-linux rs6000/t-ppccomm"
fi
xmake_file=x-linux
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
extra_headers=ppc-asm.h
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
powerpc-wrs-vxworks*)
cpu_type=rs6000
xm_file="xm-siglist.h rs6000/xm-sysv4.h"
xm_defines="USG POSIX"
tm_file=rs6000/vxppc.h
tmake_file="rs6000/t-ppcgas rs6000/t-ppccomm"
extra_headers=ppc-asm.h
thread_file='vxworks'
;;
powerpcle-*-sysv* | powerpcle-*-elf*)
tm_file=rs6000/sysv4le.h
xm_file="xm-siglist.h rs6000/xm-sysv4.h"
xm_defines="USG POSIX"
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcos rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc rs6000/t-ppccomm"
fi
xmake_file=rs6000/x-sysv4
extra_headers=ppc-asm.h
;;
powerpcle-*-eabisim*)
tm_file=rs6000/eabilesim.h
tmake_file="rs6000/t-ppcgas rs6000/t-ppccomm"
extra_headers=ppc-asm.h
;;
powerpcle-*-eabi*)
tm_file=rs6000/eabile.h
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcgas rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc rs6000/t-ppccomm"
fi
extra_headers=ppc-asm.h
;;
powerpcle-*-winnt* )
tm_file=rs6000/win-nt.h
tmake_file=rs6000/t-winnt
# extra_objs=pe.o
if test x$enable_threads = xyes; then
thread_file='win32'
fi
extra_headers=ppc-asm.h
;;
powerpcle-*-pe | powerpcle-*-cygwin*)
tm_file=rs6000/cygwin.h
xm_file="rs6000/xm-cygwin.h ${xm_file}"
tmake_file=rs6000/t-winnt
xmake_file=rs6000/x-cygwin
# extra_objs=pe.o
if test x$enable_threads = xyes; then
thread_file='win32'
fi
exeext=.exe
extra_headers=ppc-asm.h
;;
powerpcle-*-solaris2*)
tm_file=rs6000/sol2.h
xm_file="xm-siglist.h rs6000/xm-sysv4.h"
xm_defines="USG POSIX"
if test x$gas = xyes
then
tmake_file="rs6000/t-ppcos rs6000/t-ppccomm"
else
tmake_file="rs6000/t-ppc rs6000/t-ppccomm"
fi
xmake_file=rs6000/x-sysv4
extra_headers=ppc-asm.h
;;
changequote(,)dnl
rs6000-ibm-aix3.[01]*)
changequote([,])dnl
tm_file=rs6000/aix31.h
xmake_file=rs6000/x-aix31
float_format=none
use_collect2=yes
;;
changequote(,)dnl
rs6000-ibm-aix3.2.[456789]* | powerpc-ibm-aix3.2.[456789]*)
changequote([,])dnl
tm_file=rs6000/aix3newas.h
if test x$host != x$target
then
tmake_file=rs6000/t-xnewas
else
tmake_file=rs6000/t-newas
fi
float_format=none
use_collect2=yes
;;
changequote(,)dnl
rs6000-ibm-aix4.[12]* | powerpc-ibm-aix4.[12]*)
changequote([,])dnl
tm_file=rs6000/aix41.h
if test x$host != x$target
then
tmake_file=rs6000/t-xnewas
else
tmake_file=rs6000/t-newas
fi
if test "$gnu_ld" = yes
then
xmake_file=rs6000/x-aix41-gld
else
xmake_file=rs6000/x-aix41
fi
float_format=none
use_collect2=yes
;;
changequote(,)dnl
rs6000-ibm-aix4.[3456789]* | powerpc-ibm-aix4.[3456789]*)
changequote([,])dnl
tm_file=rs6000/aix43.h
if test x$host != x$target
then
tmake_file=rs6000/t-xaix43
else
tmake_file=rs6000/t-aix43
fi
xmake_file=rs6000/x-aix43
float_format=none
use_collect2=yes
;;
changequote(,)dnl
rs6000-ibm-aix[56789].* | powerpc-ibm-aix[56789].*)
changequote([,])dnl
tm_file=rs6000/aix43.h
if test x$host != x$target
then
tmake_file=rs6000/t-xaix43
else
tmake_file=rs6000/t-aix43
fi
xmake_file=rs6000/x-aix43
float_format=none
use_collect2=yes
;;
rs6000-ibm-aix*)
float_format=none
use_collect2=yes
;;
rs6000-bull-bosx)
float_format=none
use_collect2=yes
;;
rs6000-*-mach*)
tm_file=rs6000/mach.h
xm_file="${xm_file} rs6000/xm-mach.h"
xmake_file=rs6000/x-mach
use_collect2=yes
;;
rs6000-*-lynxos*)
tm_file=rs6000/lynx.h
xm_file=rs6000/xm-lynx.h
tmake_file=rs6000/t-rs6000
xmake_file=rs6000/x-lynx
use_collect2=yes
;;
sh-*-elf*)
tm_file=sh/elf.h
float_format=sh
;;
sh-*-rtemself*)
tmake_file="sh/t-sh t-rtems"
tm_file=sh/rtemself.h
float_format=sh
;;
sh-*-rtems*)
tmake_file="sh/t-sh t-rtems"
tm_file=sh/rtems.h
float_format=sh
;;
sh-*-*)
float_format=sh
;;
sparc-tti-*)
tm_file=sparc/pbd.h
xm_file="xm-alloca.h ${xm_file}"
xm_defines=USG
;;
sparc-wrs-vxworks* | sparclite-wrs-vxworks*)
tm_file=sparc/vxsparc.h
tmake_file=sparc/t-vxsparc
use_collect2=yes
thread_file='vxworks'
;;
sparc-*-aout*)
tmake_file=sparc/t-sparcbare
tm_file="sparc/aout.h libgloss.h"
;;
sparc-*-netbsd*)
tm_file=sparc/netbsd.h
tmake_file=t-netbsd
use_collect2=yes
;;
sparc-*-openbsd*)
# we need collect2 until our bug is fixed...
use_collect2=yes
;;
sparc-*-bsd*)
tm_file=sparc/bsd.h
;;
sparc-*-elf*)
tm_file=sparc/elf.h
tmake_file=sparc/t-elf
extra_parts="crti.o crtn.o crtbegin.o crtend.o"
#float_format=i128
float_format=i64
;;
sparc-*-linux-gnuaout*) # Sparc's running GNU/Linux, a.out
xm_file="${xm_file} sparc/xm-linux.h"
tm_file=sparc/linux-aout.h
xmake_file=x-linux
gnu_ld=yes
;;
sparc-*-linux-gnulibc1*) # Sparc's running GNU/Linux, libc5
xm_file="${xm_file} sparc/xm-linux.h"
xmake_file=x-linux
tm_file=sparc/linux.h
tmake_file="t-linux t-linux-gnulibc1"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
gnu_ld=yes
;;
sparc-*-linux-gnu*) # Sparc's running GNU/Linux, libc6
xm_file="${xm_file} sparc/xm-linux.h"
xmake_file=x-linux
tm_file=sparc/linux.h
tmake_file="t-linux"
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
gnu_ld=yes
if test x$enable_threads = xyes; then
thread_file='posix'
fi
;;
sparc-*-lynxos*)
if test x$gas = xyes
then
tm_file=sparc/lynx.h
else
tm_file=sparc/lynx-ng.h
fi
xm_file=sparc/xm-lynx.h
tmake_file=sparc/t-sunos41
xmake_file=x-lynx
;;
sparc-*-rtems*)
tmake_file="sparc/t-sparcbare t-rtems"
tm_file=sparc/rtems.h
;;
sparcv9-*-solaris2*)
tm_file=sparc/sol2-sld-64.h
xm_file="sparc/xm-sysv4-64.h sparc/xm-sol2.h"
xm_defines="USG POSIX"
tmake_file="sparc/t-sol2 sparc/t-sol2-64"
xmake_file=sparc/x-sysv4
extra_parts="crt1.o crti.o crtn.o gcrt1.o crtbegin.o crtend.o"
float_format=none
if test x${enable_threads} = x ; then
enable_threads=$have_pthread_h
if test x${enable_threads} = x ; then
enable_threads=$have_thread_h
fi
fi
if test x${enable_threads} = xyes ; then
if test x${have_pthread_h} = xyes ; then
thread_file='posix'
else
thread_file='solaris'
fi
fi
;;
sparc-hal-solaris2*)
- xm_file=sparc/xm-sol2.h
+ xm_file="xm-siglist.h sparc/xm-sysv4.h sparc/xm-sol2.h"
+ xm_defines="USG POSIX"
tm_file="sparc/sol2.h sparc/hal.h"
tmake_file="sparc/t-halos sparc/t-sol2"
xmake_file=sparc/x-sysv4
extra_parts="crt1.o crti.o crtn.o gmon.o crtbegin.o crtend.o"
- broken_install=yes
+ case $machine in
+ *-*-solaris2.[0-4])
+ float_format=i128
+ ;;
+ *)
+ float_format=none
+ ;;
+ esac
+ thread_file='solaris'
;;
sparc-*-solaris2*)
if test x$gnu_ld = xyes
then
tm_file=sparc/sol2.h
else
tm_file=sparc/sol2-sld.h
fi
xm_file="xm-siglist.h sparc/xm-sysv4.h sparc/xm-sol2.h"
xm_defines="USG POSIX"
tmake_file=sparc/t-sol2
xmake_file=sparc/x-sysv4
extra_parts="crt1.o crti.o crtn.o gcrt1.o gmon.o crtbegin.o crtend.o"
case $machine in
changequote(,)dnl
*-*-solaris2.[0-4])
changequote([,])dnl
float_format=i128
;;
*)
float_format=none
;;
esac
if test x${enable_threads} = x; then
enable_threads=$have_pthread_h
if test x${enable_threads} = x; then
enable_threads=$have_thread_h
fi
fi
if test x${enable_threads} = xyes; then
if test x${have_pthread_h} = xyes; then
thread_file='posix'
else
thread_file='solaris'
fi
fi
;;
sparc-*-sunos4.0*)
tm_file=sparc/sunos4.h
tmake_file=sparc/t-sunos40
use_collect2=yes
;;
sparc-*-sunos4*)
tm_file=sparc/sunos4.h
tmake_file=sparc/t-sunos41
use_collect2=yes
if test x$gas = xyes; then
tm_file="${tm_file} sparc/sun4gas.h"
fi
;;
sparc-*-sunos3*)
tm_file=sparc/sun4o3.h
use_collect2=yes
;;
sparc-*-sysv4*)
tm_file=sparc/sysv4.h
xm_file="xm-siglist.h sparc/xm-sysv4.h"
xm_defines="USG POSIX"
tmake_file=t-svr4
xmake_file=sparc/x-sysv4
extra_parts="crtbegin.o crtend.o"
;;
sparc-*-vxsim*)
xm_file="xm-siglist.h sparc/xm-sysv4.h sparc/xm-sol2.h"
xm_defines="USG POSIX"
tm_file=sparc/vxsim.h
tmake_file=sparc/t-vxsparc
xmake_file=sparc/x-sysv4
;;
sparclet-*-aout*)
tm_file="sparc/splet.h libgloss.h"
tmake_file=sparc/t-splet
;;
sparclite-*-coff*)
tm_file="sparc/litecoff.h libgloss.h"
tmake_file=sparc/t-sparclite
;;
sparclite-*-aout*)
tm_file="sparc/lite.h aoutos.h libgloss.h"
tmake_file=sparc/t-sparclite
;;
sparc64-*-aout*)
tmake_file=sparc/t-sp64
tm_file=sparc/sp64-aout.h
;;
sparc64-*-elf*)
tmake_file=sparc/t-sp64
tm_file=sparc/sp64-elf.h
extra_parts="crtbegin.o crtend.o"
;;
sparc64-*-linux*) # 64-bit Sparc's running GNU/Linux
tmake_file="t-linux sparc/t-linux64"
xm_file="sparc/xm-sp64.h sparc/xm-linux.h"
tm_file=sparc/linux64.h
xmake_file=x-linux
extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
gnu_ld=yes
;;
# This hasn't been upgraded to GCC 2.
# tahoe-harris-*) # Harris tahoe, using COFF.
# tm_file=tahoe/harris.h
# ;;
# tahoe-*-bsd*) # tahoe running BSD
# ;;
thumb-*-coff* | thumbel-*-coff*)
tm_file=arm/tcoff.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb
;;
thumb-wrs-vxworks)
tm_file=arm/tcoff.h
out_file=arm/thumb.c
xm_file=arm/xm-thumb.h
md_file=arm/thumb.md
tmake_file=arm/t-thumb
thread_file='vxworks'
;;
# This hasn't been upgraded to GCC 2.
# tron-*-*)
# cpu_type=gmicro
# use_collect2=yes
# ;;
v850-*-*)
cpu_type=v850
tm_file="v850/v850.h"
xm_file="v850/xm-v850.h"
tmake_file=v850/t-v850
if test x$stabs = xyes
then
tm_file="${tm_file} dbx.h"
fi
use_collect2=no
;;
vax-*-bsd*) # vaxen running BSD
use_collect2=yes
float_format=vax
;;
vax-*-sysv*) # vaxen running system V
tm_file="${tm_file} vax/vaxv.h"
xm_defines=USG
float_format=vax
;;
vax-*-netbsd*)
tm_file="${tm_file} netbsd.h vax/netbsd.h"
tmake_file=t-netbsd
float_format=vax
use_collect2=yes
;;
vax-*-openbsd*)
tmake_file="${tmake_file} vax/t-openbsd"
;;
vax-*-ultrix*) # vaxen running ultrix
tm_file="${tm_file} vax/ultrix.h"
use_collect2=yes
float_format=vax
;;
vax-*-vms*) # vaxen running VMS
xm_file=vax/xm-vms.h
tm_file=vax/vms.h
float_format=vax
;;
vax-*-*) # vax default entry
float_format=vax
;;
we32k-att-sysv*)
xm_file="${xm_file} xm-svr3"
use_collect2=yes
;;
*)
echo "Configuration $machine not supported" 1>&2
exit 1
;;
esac
case $machine in
*-*-linux-gnu*)
;; # Existing GNU/Linux systems do not use the GNU setup.
*-*-gnu*)
# On the GNU system, the setup is just about the same on
# each different CPU. The specific machines that GNU
# supports are matched above and just set $cpu_type.
xm_file="xm-gnu.h ${xm_file}"
tm_file=${cpu_type}/gnu.h
extra_parts="crtbegin.o crtend.o crtbeginS.o crtendS.o"
# GNU always uses ELF.
elf=yes
# GNU tools are the only tools.
gnu_ld=yes
gas=yes
xmake_file=x-linux # These details are the same as Linux.
tmake_file=t-gnu # These are not.
;;
*-*-sysv4*)
xmake_try_sysv=x-sysv
install_headers_dir=install-headers-cpio
;;
*-*-sysv*)
install_headers_dir=install-headers-cpio
;;
esac
# Distinguish i[34567]86
# Also, do not run mips-tfile on MIPS if using gas.
# Process --with-cpu= for PowerPC/rs6000
target_cpu_default2=
case $machine in
i486-*-*)
target_cpu_default2=1
;;
i586-*-*)
case $target_alias in
k6-*)
target_cpu_default2=4
;;
*)
target_cpu_default2=2
;;
esac
;;
i686-*-* | i786-*-*)
target_cpu_default2=3
;;
alpha*-*-*)
case $machine in
alphaev6*)
target_cpu_default2="MASK_CPU_EV6|MASK_BWX|MASK_MAX|MASK_FIX"
;;
alphapca56*)
target_cpu_default2="MASK_CPU_EV5|MASK_BWX|MASK_MAX"
;;
alphaev56*)
target_cpu_default2="MASK_CPU_EV5|MASK_BWX"
;;
alphaev5*)
target_cpu_default2="MASK_CPU_EV5"
;;
esac
if test x$gas = xyes
then
if test "$target_cpu_default2" = ""
then
target_cpu_default2="MASK_GAS"
else
target_cpu_default2="${target_cpu_default2}|MASK_GAS"
fi
fi
;;
arm*-*-*)
case "x$with_cpu" in
x)
# The most generic
target_cpu_default2="TARGET_CPU_generic"
;;
# Distinguish cores, and major variants
# arm7m doesn't exist, but D & I don't affect code
xarm[23678] | xarm250 | xarm[67][01]0 \
| xarm7m | xarm7dm | xarm7dmi | xarm7tdmi \
| xarm7100 | xarm7500 | xarm7500fe | xarm810 \
| xstrongarm | xstrongarm110 | xstrongarm1100)
target_cpu_default2="TARGET_CPU_$with_cpu"
;;
xyes | xno)
echo "--with-cpu must be passed a value" 1>&2
exit 1
;;
*)
if test x$pass2done = xyes
then
echo "Unknown cpu used with --with-cpu=$with_cpu" 1>&2
exit 1
fi
;;
esac
;;
mips*-*-ecoff* | mips*-*-elf*)
if test x$gas = xyes
then
if test x$gnu_ld = xyes
then
target_cpu_default2=20
else
target_cpu_default2=16
fi
fi
;;
mips*-*-*)
if test x$gas = xyes
then
target_cpu_default2=16
fi
;;
powerpc*-*-* | rs6000-*-*)
case "x$with_cpu" in
x)
;;
xcommon | xpower | xpower2 | xpowerpc | xrios \
| xrios1 | xrios2 | xrsc | xrsc1 \
| x601 | x602 | x603 | x603e | x604 | x604e | x620 \
| xec603e | x740 | x750 | x401 \
| x403 | x505 | x801 | x821 | x823 | x860)
target_cpu_default2="\"$with_cpu\""
;;
xyes | xno)
echo "--with-cpu must be passed a value" 1>&2
exit 1
;;
*)
if test x$pass2done = xyes
then
echo "Unknown cpu used with --with-cpu=$with_cpu" 1>&2
exit 1
fi
;;
esac
;;
sparc*-*-*)
case ".$with_cpu" in
.)
target_cpu_default2=TARGET_CPU_"`echo $machine | sed 's/-.*$//'`"
;;
.supersparc | .hypersparc | .ultrasparc | .v7 | .v8 | .v9)
target_cpu_default2="TARGET_CPU_$with_cpu"
;;
*)
if test x$pass2done = xyes
then
echo "Unknown cpu used with --with-cpu=$with_cpu" 1>&2
exit 1
fi
;;
esac
;;
esac
if test "$target_cpu_default2" != ""
then
if test "$target_cpu_default" != ""
then
target_cpu_default="(${target_cpu_default}|${target_cpu_default2})"
else
target_cpu_default=$target_cpu_default2
fi
fi
# No need for collect2 if we have the GNU linker.
# Actually, there is now; GNU ld doesn't handle the EH info or
# collecting for shared libraries.
#case x$gnu_ld in
#xyes)
# use_collect2=
# ;;
#esac
# Save data on machine being used to compile GCC in build_xm_file.
# Save data on host machine in vars host_xm_file and host_xmake_file.
if test x$pass1done = x
then
if test x"$xm_file" = x
then build_xm_file=$cpu_type/xm-$cpu_type.h
else build_xm_file=$xm_file
fi
build_xm_defines=$xm_defines
build_install_headers_dir=$install_headers_dir
build_exeext=$exeext
pass1done=yes
else
if test x$pass2done = x
then
if test x"$xm_file" = x
then host_xm_file=$cpu_type/xm-$cpu_type.h
else host_xm_file=$xm_file
fi
host_xm_defines=$xm_defines
if test x"$xmake_file" = x
then xmake_file=$cpu_type/x-$cpu_type
fi
host_xmake_file="$xmake_file"
host_truncate_target=$truncate_target
host_extra_gcc_objs=$extra_gcc_objs
host_extra_objs=$extra_host_objs
host_exeext=$exeext
pass2done=yes
fi
fi
done
extra_objs="${host_extra_objs} ${extra_objs}"
# Default the target-machine variables that were not explicitly set.
if test x"$tm_file" = x
then tm_file=$cpu_type/$cpu_type.h; fi
if test x$extra_headers = x
then extra_headers=; fi
if test x"$xm_file" = x
then xm_file=$cpu_type/xm-$cpu_type.h; fi
if test x$md_file = x
then md_file=$cpu_type/$cpu_type.md; fi
if test x$out_file = x
then out_file=$cpu_type/$cpu_type.c; fi
if test x"$tmake_file" = x
then tmake_file=$cpu_type/t-$cpu_type
fi
if test x"$dwarf2" = xyes
then tm_file="tm-dwarf2.h $tm_file"
fi
if test x$float_format = x
then float_format=i64
fi
if test $float_format = none
then float_h_file=Makefile.in
else float_h_file=float-$float_format.h
fi
if test x$enable_haifa = x
then
case $target in
alpha*-* | hppa*-* | powerpc*-* | rs6000-* | *sparc*-* | m32r*-*)
enable_haifa=yes;;
esac
fi
# Handle cpp installation.
if test x$enable_cpp != xno
then
tmake_file="$tmake_file t-install-cpp"
fi
# Say what files are being used for the output code and MD file.
echo "Using \`$srcdir/config/$out_file' to output insns."
echo "Using \`$srcdir/config/$md_file' as machine description file."
count=a
for f in $tm_file; do
count=${count}x
done
if test $count = ax; then
echo "Using \`$srcdir/config/$tm_file' as target machine macro file."
else
echo "Using the following target machine macro files:"
for f in $tm_file; do
echo " $srcdir/config/$f"
done
fi
count=a
for f in $host_xm_file; do
count=${count}x
done
if test $count = ax; then
echo "Using \`$srcdir/config/$host_xm_file' as host machine macro file."
else
echo "Using the following host machine macro files:"
for f in $host_xm_file; do
echo " $srcdir/config/$f"
done
fi
if test "$host_xm_file" != "$build_xm_file"; then
count=a
for f in $build_xm_file; do
count=${count}x
done
if test $count = ax; then
echo "Using \`$srcdir/config/$build_xm_file' as build machine macro file."
else
echo "Using the following build machine macro files:"
for f in $build_xm_file; do
echo " $srcdir/config/$f"
done
fi
fi
if test x$thread_file = x; then
if test x$target_thread_file != x; then
thread_file=$target_thread_file
else
thread_file='single'
fi
fi
# Set up the header files.
# $links is the list of header files to create.
# $vars is the list of shell variables with file names to include.
# auto-host.h is the file containing items generated by autoconf and is
# the first file included by config.h.
null_defines=
host_xm_file="auto-host.h gansidecl.h ${host_xm_file} hwint.h"
# If host=build, it is correct to have hconfig include auto-host.h
# as well. If host!=build, we are in error and need to do more
# work to find out the build config parameters.
if test x$host = x$build
then
build_xm_file="auto-host.h gansidecl.h ${build_xm_file} hwint.h"
else
# We create a subdir, then run autoconf in the subdir.
# To prevent recursion we set host and build for the new
# invocation of configure to the build for this invocation
# of configure.
tempdir=build.$$
rm -rf $tempdir
mkdir $tempdir
cd $tempdir
case ${srcdir} in
/*) realsrcdir=${srcdir};;
*) realsrcdir=../${srcdir};;
esac
CC=${CC_FOR_BUILD} ${realsrcdir}/configure \
--target=$target --host=$build --build=$build
# We just finished tests for the build machine, so rename
# the file auto-build.h in the gcc directory.
mv auto-host.h ../auto-build.h
cd ..
rm -rf $tempdir
build_xm_file="auto-build.h gansidecl.h ${build_xm_file} hwint.h"
fi
xm_file="gansidecl.h ${xm_file}"
tm_file="gansidecl.h ${tm_file}"
vars="host_xm_file tm_file xm_file build_xm_file"
links="config.h tm.h tconfig.h hconfig.h"
defines="host_xm_defines null_defines xm_defines build_xm_defines"
rm -f config.bak
if test -f config.status; then mv -f config.status config.bak; fi
# Make the links.
while test -n "$vars"
do
set $vars; var=$1; shift; vars=$*
set $links; link=$1; shift; links=$*
set $defines; define=$1; shift; defines=$*
rm -f $link
# Define TARGET_CPU_DEFAULT if the system wants one.
# This substitutes for lots of *.h files.
if test "$target_cpu_default" != "" -a $link = tm.h
then
echo "#define TARGET_CPU_DEFAULT ($target_cpu_default)" >>$link
fi
for file in `eval echo '$'$var`; do
case $file in
auto-config.h)
;;
*)
echo '#ifdef IN_GCC' >>$link
;;
esac
echo "#include \"$file\"" >>$link
case $file in
auto-config.h)
;;
*)
echo '#endif' >>$link
;;
esac
done
for def in `eval echo '$'$define`; do
echo "#ifndef $def" >>$link
echo "#define $def" >>$link
echo "#endif" >>$link
done
done
# Truncate the target if necessary
if test x$host_truncate_target != x; then
target=`echo $target | sed -e 's/\(..............\).*/\1/'`
fi
# Get the version trigger filename from the toplevel
if test "${with_gcc_version_trigger+set}" = set; then
gcc_version_trigger=$with_gcc_version_trigger
else
gcc_version_trigger=${srcdir}/version.c
fi
changequote(,)dnl
gcc_version=`sed -e 's/.*\"\([^ \"]*\)[ \"].*/\1/' < ${gcc_version_trigger}`
changequote([,])dnl
# Internationalization
PACKAGE=gcc
VERSION="$gcc_version"
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
AC_SUBST(PACKAGE)
AC_SUBST(VERSION)
ALL_LINGUAS="en_UK"
# NLS support is still experimental, so disable it by default for now.
AC_ARG_ENABLE(nls,
[ --enable-nls use Native Language Support (disabled by default)],
, enable_nls=no)
AM_GNU_GETTEXT
XGETTEXT="AWK='$AWK' \$(SHELL) \$(top_srcdir)/exgettext $XGETTEXT"
# Get an absolute path to the GCC top-level source directory
holddir=`pwd`
cd $srcdir
topdir=`pwd`
cd $holddir
# Conditionalize the makefile for this host machine.
# Make-host contains the concatenation of all host makefile fragments
# [there can be more than one]. This file is built by configure.frag.
host_overrides=Make-host
dep_host_xmake_file=
for f in .. ${host_xmake_file}
do
if test -f ${srcdir}/config/$f
then
dep_host_xmake_file="${dep_host_xmake_file} ${srcdir}/config/$f"
fi
done
# Conditionalize the makefile for this target machine.
# Make-target contains the concatenation of all host makefile fragments
# [there can be more than one]. This file is built by configure.frag.
target_overrides=Make-target
dep_tmake_file=
for f in .. ${tmake_file}
do
if test -f ${srcdir}/config/$f
then
dep_tmake_file="${dep_tmake_file} ${srcdir}/config/$f"
fi
done
# If the host doesn't support symlinks, modify CC in
# FLAGS_TO_PASS so CC="stage1/xgcc -Bstage1/" works.
# Otherwise, we can use "CC=$(CC)".
rm -f symtest.tem
if $symbolic_link $srcdir/gcc.c symtest.tem 2>/dev/null
then
cc_set_by_configure="\$(CC)"
quoted_cc_set_by_configure="\$(CC)"
stage_prefix_set_by_configure="\$(STAGE_PREFIX)"
else
rm -f symtest.tem
if cp -p $srcdir/gcc.c symtest.tem 2>/dev/null
then
symbolic_link="cp -p"
else
symbolic_link="cp"
fi
cc_set_by_configure="\`case '\$(CC)' in stage*) echo '\$(CC)' | sed -e 's|stage|../stage|g';; *) echo '\$(CC)';; esac\`"
quoted_cc_set_by_configure="\\\`case '\\\$(CC)' in stage*) echo '\\\$(CC)' | sed -e 's|stage|../stage|g';; *) echo '\\\$(CC)';; esac\\\`"
stage_prefix_set_by_configure="\`case '\$(STAGE_PREFIX)' in stage*) echo '\$(STAGE_PREFIX)' | sed -e 's|stage|../stage|g';; *) echo '\$(STAGE_PREFIX)';; esac\`"
fi
rm -f symtest.tem
out_object_file=`basename $out_file .c`.o
tm_file_list=
for f in $tm_file; do
case $f in
gansidecl.h )
tm_file_list="${tm_file_list} $f" ;;
*) tm_file_list="${tm_file_list} \$(srcdir)/config/$f" ;;
esac
done
host_xm_file_list=
for f in $host_xm_file; do
case $f in
auto-host.h | gansidecl.h | hwint.h )
host_xm_file_list="${host_xm_file_list} $f" ;;
*) host_xm_file_list="${host_xm_file_list} \$(srcdir)/config/$f" ;;
esac
done
build_xm_file_list=
for f in $build_xm_file; do
case $f in
auto-build.h | auto-host.h | gansidecl.h | hwint.h )
build_xm_file_list="${build_xm_file_list} $f" ;;
*) build_xm_file_list="${build_xm_file_list} \$(srcdir)/config/$f" ;;
esac
done
# Define macro CROSS_COMPILE in compilation
# if this is a cross-compiler.
# Also use all.cross instead of all.internal
# and add cross-make to Makefile.
cross_overrides="/dev/null"
if test x$host != x$target
then
cross_defines="CROSS=-DCROSS_COMPILE"
cross_overrides="${topdir}/cross-make"
fi
# When building gcc with a cross-compiler, we need to fix a few things.
# This must come after cross-make as we want all.build to override
# all.cross.
build_overrides="/dev/null"
if test x$build != x$host
then
build_overrides="${topdir}/build-make"
fi
# Expand extra_headers to include complete path.
# This substitutes for lots of t-* files.
extra_headers_list=
if test "x$extra_headers" = x
then true
else
# Prepend ${srcdir}/ginclude/ to every entry in extra_headers.
for file in $extra_headers;
do
extra_headers_list="${extra_headers_list} \$(srcdir)/ginclude/${file}"
done
fi
if test x$use_collect2 = xno; then
use_collect2=
fi
# Add a definition of USE_COLLECT2 if system wants one.
# Also tell toplev.c what to do.
# This substitutes for lots of t-* files.
if test x$use_collect2 = x
then
will_use_collect2=
maybe_use_collect2=
else
will_use_collect2="collect2"
maybe_use_collect2="-DUSE_COLLECT2"
fi
# NEED TO CONVERT
# Set MD_DEPS if the real md file is in md.pre-cpp.
# Set MD_CPP to the cpp to pass the md file through. Md files use ';'
# for line oriented comments, so we must always use a GNU cpp. If
# building gcc with a cross compiler, use the cross compiler just
# built. Otherwise, we can use the cpp just built.
md_file_sub=
if test "x$md_cppflags" = x
then
md_file_sub=$srcdir/config/$md_file
else
md_file=md
fi
# If we have gas in the build tree, make a link to it.
if test -f ../gas/Makefile; then
rm -f as; $symbolic_link ../gas/as-new$host_exeext as$host_exeext 2>/dev/null
fi
# If we have nm in the build tree, make a link to it.
if test -f ../binutils/Makefile; then
rm -f nm; $symbolic_link ../binutils/nm-new$host_exeext nm$host_exeext 2>/dev/null
fi
# If we have ld in the build tree, make a link to it.
if test -f ../ld/Makefile; then
# if test x$use_collect2 = x; then
# rm -f ld; $symbolic_link ../ld/ld-new$host_exeext ld$host_exeext 2>/dev/null
# else
rm -f collect-ld; $symbolic_link ../ld/ld-new$host_exeext collect-ld$host_exeext 2>/dev/null
# fi
fi
# Figure out what assembler alignment features are present.
AC_MSG_CHECKING(assembler alignment features)
gcc_cv_as=
gcc_cv_as_alignment_features=
gcc_cv_as_gas_srcdir=`echo $srcdir | sed -e 's,/gcc$,,'`/gas
if test -x "$DEFAULT_ASSEMBLER"; then
gcc_cv_as="$DEFAULT_ASSEMBLER"
elif test -x "$AS"; then
gcc_cv_as="$AS"
elif test -x as$host_exeext; then
# Build using assembler in the current directory.
gcc_cv_as=./as$host_exeext
elif test -f $gcc_cv_as_gas_srcdir/configure.in -a -f ../gas/Makefile; then
# Single tree build which includes gas.
for f in $gcc_cv_as_gas_srcdir/configure $gcc_cv_as_gas_srcdir/configure.in $gcc_cv_as_gas_srcdir/Makefile.in
do
changequote(,)dnl
gcc_cv_gas_version=`grep '^VERSION=[0-9]*\.[0-9]*' $f`
changequote([,])dnl
if test x$gcc_cv_gas_version != x; then
break
fi
done
changequote(,)dnl
gcc_cv_gas_major_version=`expr "$gcc_cv_gas_version" : "VERSION=\([0-9]*\)"`
gcc_cv_gas_minor_version=`expr "$gcc_cv_gas_version" : "VERSION=[0-9]*\.\([0-9]*\)"`
changequote([,])dnl
if test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x; then
# Gas version 2.6 and later support for .balign and .p2align.
# bytes to skip when using .p2align.
if test "$gcc_cv_gas_major_version" -eq 2 -a "$gcc_cv_gas_minor_version" -ge 6 -o "$gcc_cv_gas_major_version" -gt 2; then
gcc_cv_as_alignment_features=".balign and .p2align"
AC_DEFINE(HAVE_GAS_BALIGN_AND_P2ALIGN)
fi
# Gas version 2.8 and later support specifying the maximum
# bytes to skip when using .p2align.
if test "$gcc_cv_gas_major_version" -eq 2 -a "$gcc_cv_gas_minor_version" -ge 8 -o "$gcc_cv_gas_major_version" -gt 2; then
gcc_cv_as_alignment_features=".p2align including maximum skip"
AC_DEFINE(HAVE_GAS_MAX_SKIP_P2ALIGN)
fi
fi
elif test x$host = x$target; then
# Native build.
# Search the same directories that the installed compiler will
# search. Else we may find the wrong assembler and lose. If we
# do not find a suitable assembler binary, then try the user's
# path.
#
# Also note we have to check MD_EXEC_PREFIX before checking the
# user's path. Unfortunately, there is no good way to get at the
# value of MD_EXEC_PREFIX here. So we do a brute force search
# through all the known MD_EXEC_PREFIX values. Ugh. This needs
# to be fixed as part of the make/configure rewrite too.
if test "x$exec_prefix" = xNONE; then
if test "x$prefix" = xNONE; then
test_prefix=/usr/local
else
test_prefix=$prefix
fi
else
test_prefix=$exec_prefix
fi
# If the loop below does not find an assembler, then use whatever
# one we can find in the users's path.
# user's path.
as=as$host_exeext
test_dirs="$test_prefix/lib/gcc-lib/$target/$gcc_version \
$test_prefix/lib/gcc-lib/$target \
/usr/lib/gcc/$target/$gcc_version \
/usr/lib/gcc/$target \
$test_prefix/$target/bin/$target/$gcc_version \
$test_prefix/$target/bin \
/usr/libexec \
/usr/ccs/gcc \
/usr/ccs/bin \
/udk/usr/ccs/bin \
/bsd43/usr/lib/cmplrs/cc \
/usr/cross64/usr/bin \
/usr/lib/cmplrs/cc \
/sysv/usr/lib/cmplrs/cc \
/svr4/usr/lib/cmplrs/cc \
/usr/bin"
for dir in $test_dirs; do
if test -f $dir/as$host_exeext; then
gcc_cv_as=$dir/as$host_exeext
break;
fi
done
fi
if test x$gcc_cv_as != x; then
# Check if we have .balign and .p2align
echo ".balign 4" > conftest.s
echo ".p2align 2" >> conftest.s
if $gcc_cv_as -o conftest.o conftest.s > /dev/null 2>&1; then
gcc_cv_as_alignment_features=".balign and .p2align"
AC_DEFINE(HAVE_GAS_BALIGN_AND_P2ALIGN)
fi
rm -f conftest.s conftest.o
# Check if specifying the maximum bytes to skip when
# using .p2align is supported.
echo ".p2align 4,,7" > conftest.s
if $gcc_cv_as -o conftest.o conftest.s > /dev/null 2>&1; then
gcc_cv_as_alignment_features=".p2align including maximum skip"
AC_DEFINE(HAVE_GAS_MAX_SKIP_P2ALIGN)
fi
rm -f conftest.s conftest.o
fi
AC_MSG_RESULT($gcc_cv_as_alignment_features)
AC_MSG_CHECKING(assembler subsection support)
gcc_cv_as_subsections=
if test x$gcc_cv_as != x; then
# Check if we have .subsection
echo ".subsection 1" > conftest.s
if $gcc_cv_as -o conftest.o conftest.s > /dev/null 2>&1; then
gcc_cv_as_subsections=".subsection"
if test -x nm$host_exeext; then
gcc_cv_nm=./nm$host_exeext
elif test x$host = x$target; then
# Native build.
gcc_cv_nm=nm$host_exeext
fi
if test x$gcc_cv_nm != x; then
cat > conftest.s <<EOF
conftest_label1: .word 0
.subsection -1
conftest_label2: .word 0
.previous
EOF
if $gcc_cv_as -o conftest.o conftest.s > /dev/null 2>&1; then
$gcc_cv_nm conftest.o | grep conftest_label1 > conftest.nm1
$gcc_cv_nm conftest.o | grep conftest_label2 | sed -e 's/label2/label1/' > conftest.nm2
if cmp conftest.nm1 conftest.nm2 > /dev/null 2>&1; then
:
else
gcc_cv_as_subsections="working .subsection -1"
AC_DEFINE(HAVE_GAS_SUBSECTION_ORDERING)
fi
fi
fi
fi
rm -f conftest.s conftest.o conftest.nm1 conftest.nm2
fi
AC_MSG_RESULT($gcc_cv_as_subsections)
AC_MSG_CHECKING(assembler instructions)
gcc_cv_as_instructions=
if test x$gcc_cv_as != x; then
set "filds fists" "filds mem; fists mem"
while test $# -gt 0
do
echo "$2" > conftest.s
if $gcc_cv_as -o conftest.o conftest.s > /dev/null 2>&1; then
gcc_cv_as_instructions=${gcc_cv_as_instructions}$1" "
AC_DEFINE_UNQUOTED(HAVE_GAS_`echo "$1" | tr '[a-z ]' '[A-Z_]'`)
fi
shift 2
done
rm -f conftest.s conftest.o
fi
AC_MSG_RESULT($gcc_cv_as_instructions)
# Figure out what language subdirectories are present.
# Look if the user specified --enable-languages="..."; if not, use
# the environment variable $LANGUAGES if defined. $LANGUAGES might
# go away some day.
if test x"${enable_languages+set}" != xset; then
if test x"${LANGUAGES+set}" = xset; then
enable_languages="`echo ${LANGUAGES} | tr ' ' ','`"
else
enable_languages=all
fi
fi
subdirs=
for lang in ${srcdir}/*/config-lang.in ..
do
case $lang in
..) ;;
# The odd quoting in the next line works around
# an apparent bug in bash 1.12 on linux.
changequote(,)dnl
${srcdir}/[*]/config-lang.in) ;;
*)
lang_alias=`sed -n -e 's,^language=['"'"'"'"]\(.*\)["'"'"'"'].*$,\1,p' -e 's,^language=\([^ ]*\).*$,\1,p' $lang`
if test "x$lang_alias" = x
then
echo "$lang doesn't set \$language." 1>&2
exit 1
fi
if test x"${enable_languages}" = xall; then
add_this_lang=yes
else
case "${enable_languages}" in
${lang_alias} | "${lang_alias},"* | *",${lang_alias},"* | *",${lang_alias}" )
add_this_lang=yes
;;
* )
add_this_lang=no
;;
esac
fi
if test x"${add_this_lang}" = xyes; then
case $lang in
${srcdir}/ada/config-lang.in)
if test x$gnat = xyes ; then
subdirs="$subdirs `echo $lang | sed -e 's,^.*/\([^/]*\)/config-lang.in$,\1,'`"
fi
;;
*)
subdirs="$subdirs `echo $lang | sed -e 's,^.*/\([^/]*\)/config-lang.in$,\1,'`"
;;
esac
fi
;;
changequote([,])dnl
esac
done
# Make gthr-default.h if we have a thread file.
gthread_flags=
if test $thread_file != single; then
rm -f gthr-default.h
echo "#include \"gthr-${thread_file}.h\"" > gthr-default.h
gthread_flags=-DHAVE_GTHR_DEFAULT
fi
AC_SUBST(gthread_flags)
# Make empty files to contain the specs and options for each language.
# Then add #include lines to for a compiler that has specs and/or options.
lang_specs_files=
lang_options_files=
lang_tree_files=
rm -f specs.h options.h gencheck.h
touch specs.h options.h gencheck.h
for subdir in . $subdirs
do
if test -f $srcdir/$subdir/lang-specs.h; then
echo "#include \"$subdir/lang-specs.h\"" >>specs.h
lang_specs_files="$lang_specs_files $srcdir/$subdir/lang-specs.h"
fi
if test -f $srcdir/$subdir/lang-options.h; then
echo "#include \"$subdir/lang-options.h\"" >>options.h
lang_options_files="$lang_options_files $srcdir/$subdir/lang-options.h"
fi
if test -f $srcdir/$subdir/$subdir-tree.def; then
echo "#include \"$subdir/$subdir-tree.def\"" >>gencheck.h
lang_tree_files="$lang_tree_files $srcdir/$subdir/$subdir-tree.def"
fi
done
# These (without "all_") are set in each config-lang.in.
# `language' must be a single word so is spelled singularly.
all_languages=
all_boot_languages=
all_compilers=
all_stagestuff=
all_diff_excludes=
all_outputs='Makefile intl/Makefile po/Makefile.in fixinc/Makefile'
# List of language makefile fragments.
all_lang_makefiles=
all_headers=
all_lib2funcs=
# Add the language fragments.
# Languages are added via two mechanisms. Some information must be
# recorded in makefile variables, these are defined in config-lang.in.
# We accumulate them and plug them into the main Makefile.
# The other mechanism is a set of hooks for each of the main targets
# like `clean', `install', etc.
language_fragments="Make-lang"
language_hooks="Make-hooks"
oldstyle_subdirs=
for s in .. $subdirs
do
if test $s != ".."
then
language=
boot_language=
compilers=
stagestuff=
diff_excludes=
headers=
outputs=
lib2funcs=
. ${srcdir}/$s/config-lang.in
if test "x$language" = x
then
echo "${srcdir}/$s/config-lang.in doesn't set \$language." 1>&2
exit 1
fi
all_lang_makefiles="$all_lang_makefiles ${srcdir}/$s/Make-lang.in ${srcdir}/$s/Makefile.in"
all_languages="$all_languages $language"
if test "x$boot_language" = xyes
then
all_boot_languages="$all_boot_languages $language"
fi
all_compilers="$all_compilers $compilers"
all_stagestuff="$all_stagestuff $stagestuff"
all_diff_excludes="$all_diff_excludes $diff_excludes"
all_headers="$all_headers $headers"
all_outputs="$all_outputs $outputs"
if test x$outputs = x
then
oldstyle_subdirs="$oldstyle_subdirs $s"
fi
all_lib2funcs="$all_lib2funcs $lib2funcs"
fi
done
# Since we can't use `::' targets, we link each language in
# with a set of hooks, reached indirectly via lang.${target}.
rm -f Make-hooks
touch Make-hooks
target_list="all.build all.cross start.encap rest.encap \
info dvi \
install-normal install-common install-info install-man \
uninstall distdir \
mostlyclean clean distclean extraclean maintainer-clean \
stage1 stage2 stage3 stage4"
for t in $target_list
do
x=
for lang in .. $all_languages
do
if test $lang != ".."; then
x="$x $lang.$t"
fi
done
echo "lang.$t: $x" >> Make-hooks
done
# If we're not building in srcdir, create .gdbinit.
if test ! -f Makefile.in; then
echo "dir ." > .gdbinit
echo "dir ${srcdir}" >> .gdbinit
if test x$gdb_needs_out_file_path = xyes
then
echo "dir ${srcdir}/config/"`dirname ${out_file}` >> .gdbinit
fi
if test "x$subdirs" != x; then
for s in $subdirs
do
echo "dir ${srcdir}/$s" >> .gdbinit
done
fi
echo "source ${srcdir}/.gdbinit" >> .gdbinit
fi
# Define variables host_canonical and build_canonical
# because some Cygnus local changes in the Makefile depend on them.
build_canonical=${build}
host_canonical=${host}
target_subdir=
if test "${host}" != "${target}" ; then
target_subdir=${target}/
fi
AC_SUBST(build_canonical)
AC_SUBST(host_canonical)
AC_SUBST(target_subdir)
# If this is using newlib, then define inhibit_libc in
# LIBGCC2_CFLAGS. This will cause __eprintf to be left out of
# libgcc.a, but that's OK because newib should have its own version of
# assert.h.
inhibit_libc=
if test x$with_newlib = xyes; then
inhibit_libc=-Dinhibit_libc
fi
AC_SUBST(inhibit_libc)
# Override SCHED_OBJ and SCHED_CFLAGS to enable the Haifa scheduler.
sched_prefix=
sched_cflags=
if test x$enable_haifa = xyes; then
echo "Using the Haifa scheduler."
sched_prefix=haifa-
sched_cflags=-DHAIFA
fi
AC_SUBST(sched_prefix)
AC_SUBST(sched_cflags)
if test x$enable_haifa != x; then
# Explicitly remove files that need to be recompiled for the Haifa scheduler.
for x in genattrtab.o toplev.o *sched.o; do
if test -f $x; then
echo "Removing $x"
rm -f $x
fi
done
fi
# If $(exec_prefix) exists and is not the same as $(prefix), then compute an
# absolute path for gcc_tooldir based on inserting the number of up-directory
# movements required to get from $(exec_prefix) to $(prefix) into the basic
# $(libsubdir)/@(unlibsubdir) based path.
# Don't set gcc_tooldir to tooldir since that's only passed in by the toplevel
# make and thus we'd get different behavior depending on where we built the
# sources.
if test x$exec_prefix = xNONE -o x$exec_prefix = x$prefix; then
gcc_tooldir='$(libsubdir)/$(unlibsubdir)/../$(target_alias)'
else
changequote(<<, >>)dnl
# An explanation of the sed strings:
# -e 's|^\$(prefix)||' matches and eliminates 'prefix' from 'exec_prefix'
# -e 's|/$||' match a trailing forward slash and eliminates it
# -e 's|^[^/]|/|' forces the string to start with a forward slash (*)
# -e 's|/[^/]*|../|g' replaces each occurance of /<directory> with ../
#
# (*) Note this pattern overwrites the first character of the string
# with a forward slash if one is not already present. This is not a
# problem because the exact names of the sub-directories concerned is
# unimportant, just the number of them matters.
#
# The practical upshot of these patterns is like this:
#
# prefix exec_prefix result
# ------ ----------- ------
# /foo /foo/bar ../
# /foo/ /foo/bar ../
# /foo /foo/bar/ ../
# /foo/ /foo/bar/ ../
# /foo /foo/bar/ugg ../../
#
dollar='$$'
gcc_tooldir="\$(libsubdir)/\$(unlibsubdir)/\`echo \$(exec_prefix) | sed -e 's|^\$(prefix)||' -e 's|/\$(dollar)||' -e 's|^[^/]|/|' -e 's|/[^/]*|../|g'\`\$(target_alias)"
changequote([, ])dnl
fi
AC_SUBST(gcc_tooldir)
AC_SUBST(dollar)
# Nothing to do for FLOAT_H, float_format already handled.
objdir=`pwd`
AC_SUBST(objdir)
# Process the language and host/target makefile fragments.
${CONFIG_SHELL-/bin/sh} $srcdir/configure.frag $srcdir "$subdirs" "$dep_host_xmake_file" "$dep_tmake_file"
# Substitute configuration variables
AC_SUBST(subdirs)
AC_SUBST(all_boot_languages)
AC_SUBST(all_compilers)
AC_SUBST(all_diff_excludes)
AC_SUBST(all_headers)
AC_SUBST(all_lang_makefiles)
AC_SUBST(all_languages)
AC_SUBST(all_lib2funcs)
AC_SUBST(all_stagestuff)
AC_SUBST(build_exeext)
AC_SUBST(build_install_headers_dir)
AC_SUBST(build_xm_file_list)
AC_SUBST(cc_set_by_configure)
AC_SUBST(quoted_cc_set_by_configure)
AC_SUBST(cpp_install_dir)
AC_SUBST(cpp_main)
AC_SUBST(dep_host_xmake_file)
AC_SUBST(dep_tmake_file)
AC_SUBST(extra_c_flags)
AC_SUBST(extra_c_objs)
AC_SUBST(extra_cpp_objs)
AC_SUBST(extra_cxx_objs)
AC_SUBST(extra_headers_list)
AC_SUBST(extra_objs)
AC_SUBST(extra_parts)
AC_SUBST(extra_passes)
AC_SUBST(extra_programs)
AC_SUBST(fixinc_defs)
AC_SUBST(float_h_file)
AC_SUBST(gcc_gxx_include_dir)
AC_SUBST(gcc_version)
AC_SUBST(gcc_version_trigger)
AC_SUBST(host_exeext)
AC_SUBST(host_extra_gcc_objs)
AC_SUBST(host_xm_file_list)
AC_SUBST(install)
AC_SUBST(JAVAGC)
AC_SUBST(lang_options_files)
AC_SUBST(lang_specs_files)
AC_SUBST(lang_tree_files)
AC_SUBST(local_prefix)
AC_SUBST(maybe_use_collect2)
AC_SUBST(md_file)
AC_SUBST(objc_boehm_gc)
AC_SUBST(out_file)
AC_SUBST(out_object_file)
AC_SUBST(stage_prefix_set_by_configure)
AC_SUBST(symbolic_link)
AC_SUBST(thread_file)
AC_SUBST(tm_file_list)
AC_SUBST(will_use_collect2)
AC_SUBST_FILE(target_overrides)
AC_SUBST_FILE(host_overrides)
AC_SUBST(cross_defines)
AC_SUBST_FILE(cross_overrides)
AC_SUBST_FILE(build_overrides)
AC_SUBST_FILE(language_fragments)
AC_SUBST_FILE(language_hooks)
# Echo that links are built
if test x$host = x$target
then
str1="native "
else
str1="cross-"
str2=" from $host"
fi
if test x$host != x$build
then
str3=" on a $build system"
fi
if test "x$str2" != x || test "x$str3" != x
then
str4=
fi
echo "Links are now set up to build a ${str1}compiler for ${target}$str4" 1>&2
if test "x$str2" != x || test "x$str3" != x
then
echo " ${str2}${str3}." 1>&2
fi
# Truncate the target if necessary
if test x$host_truncate_target != x; then
target=`echo $target | sed -e 's/\(..............\).*/\1/'`
fi
# Configure the subdirectories
# AC_CONFIG_SUBDIRS($subdirs)
# Create the Makefile
# and configure language subdirectories
AC_OUTPUT($all_outputs,
[
. $srcdir/configure.lang
case x$CONFIG_HEADERS in
xauto-host.h:config.in)
echo > cstamp-h ;;
esac
# If the host supports symlinks, point stage[1234] at ../stage[1234] so
# bootstrapping and the installation procedure can still use
# CC="stage1/xgcc -Bstage1/". If the host doesn't support symlinks,
# FLAGS_TO_PASS has been modified to solve the problem there.
# This is virtually a duplicate of what happens in configure.lang; we do
# an extra check to make sure this only happens if ln -s can be used.
if test "$symbolic_link" = "ln -s"; then
for d in .. ${subdirs} ; do
if test $d != ..; then
STARTDIR=`pwd`
cd $d
for t in stage1 stage2 stage3 stage4 include
do
rm -f $t
$symbolic_link ../$t $t 2>/dev/null
done
cd $STARTDIR
fi
done
else true ; fi
# Avoid having to add intl to our include paths.
if test -f intl/libintl.h; then
echo creating libintl.h
echo '#include "intl/libintl.h"' >libintl.h
fi
],
[
host='${host}'
build='${build}'
target='${target}'
target_alias='${target_alias}'
srcdir='${srcdir}'
subdirs='${subdirs}'
oldstyle_subdirs='${oldstyle_subdirs}'
symbolic_link='${symbolic_link}'
program_transform_set='${program_transform_set}'
program_transform_name='${program_transform_name}'
dep_host_xmake_file='${dep_host_xmake_file}'
host_xmake_file='${host_xmake_file}'
dep_tmake_file='${dep_tmake_file}'
tmake_file='${tmake_file}'
thread_file='${thread_file}'
gcc_version='${gcc_version}'
gcc_version_trigger='${gcc_version_trigger}'
local_prefix='${local_prefix}'
build_install_headers_dir='${build_install_headers_dir}'
build_exeext='${build_exeext}'
host_exeext='${host_exeext}'
out_file='${out_file}'
gdb_needs_out_file_path='${gdb_needs_out_file_path}'
SET_MAKE='${SET_MAKE}'
target_list='${target_list}'
target_overrides='${target_overrides}'
host_overrides='${host_overrides}'
cross_defines='${cross_defines}'
cross_overrides='${cross_overrides}'
build_overrides='${build_overrides}'
cpp_install_dir='${cpp_install_dir}'
])
Index: head/contrib/gcc/cp/ChangeLog
===================================================================
--- head/contrib/gcc/cp/ChangeLog (revision 52750)
+++ head/contrib/gcc/cp/ChangeLog (revision 52751)
@@ -1,16063 +1,16084 @@
+Sun Oct 24 23:54:10 PDT 1999 Jeff Law (law@cygnus.com)
+
+ * gcc-2.95.2 Released.
+
+1999-09-06 Mark Mitchell <mark@codesourcery.com>
+
+ * pt.c (tsubst): Back out 1999-08-06 patch. Use fold and
+ decl_constant_value to simplify array bounds.
+
+1999-08-19 Jason Merrill <jason@yorick.cygnus.com>
+
+ * cp-tree.h: Declare flag_use_repository.
+ * pt.c (do_decl_instantiation): Don't complain about duplicate
+ instantiation with -frepo.
+ (do_type_instantiation): Likewise.
+
+1999-08-14 Jason Merrill <jason@yorick.cygnus.com>
+
+ * decl2.c (lookup_arg_dependent): Note that we've already checked
+ the current namespace.
+
Mon Aug 16 01:29:24 PDT 1999 Jeff Law (law@cygnus.com)
* gcc-2.95.1 Released.
1999-08-12 Mark Mitchell <mark@codesourcery.com>
* decl2.c (lang_decode_option): Deprecate signatures.
1999-08-11 Martin v. Loewis <martin@mira.isdn.cs.tu-berlin.de>
* lex.c (do_identifier): If we find a hidden type after a global
was selected already, continue using the global.
1999-08-10 Martin v. Loewis <martin@mira.isdn.cs.tu-berlin.de>
* decl2.c (set_decl_namespace): Do not complain about non-matching
decls if processing a template.
1999-08-09 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (function_try_block): Call end_protect_partials
before expand_start_all_catch.
1999-08-06 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (maybe_get_template_decl_from_type_decl): Make sure that
we're looking at a class.
* decl.c (lookup_name_real): Set the complain flag if we're
looking for a namespace member.
* decl.c (pushdecl): Only give an error for shadowing a parm
from *this* function.
* decl2.c (build_expr_from_tree, case METHOD_CALL_EXPR): Only
build_expr_from_tree on the args of a TEMPLATE_ID_EXPR.
* class.c (mark_overriders): Fix order of args to overrides.
(warn_hidden): Likewise. Fix for having virtual and non-virtual
functions with the same name.
* cp-tree.h (DECL_VIRTUAL_CONTEXT): New macro.
* typeck.c (expand_ptrmemfunc_cst): Calculate delta correctly for
virtual functions and MI. Simplify.
* typeck.c (c_expand_return): Downgrade pedwarn about returning NULL
from op new to warning.
* decl2.c (reparse_absdcl_as_casts): Don't warn about old-style
casts in system headers or extern "C" blocks.
* pt.c (do_decl_instantiation): Downgrade duplicate instantiation
errors to pedwarn.
* error.c (dump_type_real): Handle TREE_LIST again.
* typeck.c (comp_target_parms): Don't complain about
converting from () to (...) if !flag_strict_prototype.
* class.c (instantiate_type): Downgrade errors for object-dependent
memfn refs to pedwarn.
1999-08-06 Alexandre Oliva <oliva@dcc.unicamp.br>
* pt.c (tsubst): Use build_index_type to build in-template array
index type. Fixes g++.oliva/dwarf1.C.
* decl.c (grokdeclarator): Likewise, just for consistency, as it
doesn't seem to trigger the bug without it.
Thu Aug 5 02:40:42 1999 Jeffrey A Law (law@cygnus.com)
* typeck2.c: Update URLs and mail addresses.
1999-08-03 Mumit Khan <khan@xraylith.wisc.edu>
* decl.c (start_decl): Set attributes before duplicate_decls call.
Wed Jul 28 21:39:31 PDT 1999 Jeff Law (law@cygnus.com)
* gcc-2.95 Released.
Sun Jul 25 15:24:21 1999 Jeffrey A Law (law@cygnus.com)
* g++FAQ.texi: Deleted per Joe Buck's request.
* Makefile.in: Corresponding changes.
Sat Jul 17 23:50:47 1999 Jeffrey A Law (law@cygnus.com)
* Makefile.in (INTERFACE): Bump to 2.
1999-07-17 Alexandre Oliva <oliva@dcc.unicamp.br>
* typeck2.c (my_friendly_abort): Updated URL with bug reporting
instructions to gcc.gnu.org. Removed e-mail address.
1999-07-15 Mark Mitchell <mark@codesourcery.com>
* pt.c (check_default_tmpl_args): Handle friends defined in the
class just like member functions defined in the class.
Thu Jul 15 01:26:49 1999 H.J. Lu <hjl@gnu.org>
* decl.c (duplicate_decls): Relax restriction for exception
checks on duplicate symbols.
1999-07-07 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Update the names of all variants when
de-anonymizing.
Wed Jul 7 01:26:47 1999 Alexandre Oliva <oliva@dcc.unicamp.br>
* decl2.c (mark_vtable_entries): Fix check for rtti offset.
1999-06-26 Richard Henderson <rth@cygnus.com>
* decl.c (cp_finish_decl): Fix typo in cp_warning_at call.
1999-06-21 Mark Mitchell <mark@codesourcery.com>
* init.c (expand_aggr_vbase_init): Rename to
construct_virtual_bases. Conditionalize construction here,
rather than ...
(emit_base_init): Here.
1999-06-19 Mark Mitchell <mark@codesourcery.com>
* semantics.c (finish_asm_statement): Apply decay conversions to
input operands.
* decl.c (expand_static_init): When building an anonymous function
for use with atexit, compute its body before and after entering
the function.
1999-06-18 Mark Mitchell <mark@codesourcery.com>
* init.c (expand_aggr_vbase_init): Add flag parameter.
(build_partial_cleanup_for): Remove, inlining into ..
(expand_cleanup_for_base): ... here. Take flag parameter.
(emit_base_init): Pass the in_chrg parameter to
emit_aggr_vbase_init.
(emit_aggr_vbase_init): Pass it to expand_cleanup_for_base.
1999-06-16 Mark Mitchell <mark@codesourcery.com>
* decl2.c (import_export_decl): Use same_type_p, rather than
relying on pointer-equality for types.
* method.c (do_build_copy_constructor): Simplify.
* call.c (build_method_call): Remove bogus code for two-argument
delete.
* init.c (build_new_1): Expand on comment, and remove dead code.
* init.c (expand_cleanup_for_base): New function, split out
from ...
(emit_base_init): Here.
(expand_aggr_vbase_init): Use it.
1999-06-15 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (class_cache_firstobj): Declare.
(maybe_push_cache_obstack): Rename to push_cache_obstack.
* class.c (permanent_obstack): Remove declaration.
(class_cache_firstobj): Make it global.
(add_method): Don't use permanent_obstack directly.
(pushclass): Only free the class_cache_obstack if we know how far
back to free it.
(maybe_push_cache_obstack): Rename to push_cache_obstack.
* decl.c: Remove dead comment.
(saved_scope): Add class_cache_firstobj.
(push_to_top_level): Save it.
(pop_from_top_level): Restore it.
(push_class_level_binding): Use push_cache_obstack, not
maybe_push_cache_obstack.
* search.c (push_class_decls): Likewise.
1999-06-14 Nathan Sidwell <nathan@acm.org>
* call.c (build_new_op): Remove REF_BIND from all operands.
1999-06-07 Mark Mitchell <mark@codesourcery.com>
* search.c (convert_pointer_to_single_level): Reimplement without
using get_binfo.
1999-06-06 Mark Mitchell <mark@codesourcery.com>
* method.c (is_back_referenceable_type): Back-reference bools when
not squangling.
1999-06-04 Jason Merrill <jason@yorick.cygnus.com>
* semantics.c (finish_if_stmt_cond): Copy cond to permanent_obstack.
(finish_while_stmt_cond, finish_do_stmt, finish_for_cond): Likewise.
1999-05-30 Mark Mitchell <mark@codesourcery.com>
* lex.c (make_lang_type): Create TYPE_BINFO for
TEMPLATE_TYPE_PARMs just like for non-template types.
1999-05-28 Nathan Sidwell <nathan@acm.org>
* decl.c (complete_array_type): Allocate off same obstack. Fix
DO_DEFAULT comment to match reality.
1999-05-22 Mark Mitchell <mark@codesourcery.com>
* tree.c (mapcar): Handle NON_LVALUE_EXPR.
1999-05-21 Mark Mitchell <mark@codesourcery.com>
* typeck.c (build_ptrmemfunc): Handle PTRMEM_CSTs carefully to
reveal optimization opportunities.
1999-05-20 Mark Mitchell <mark@codesourcery.com>
* decl.c (grokdeclarator): Don't treat [] as indicating a
zero-sized array in a typedef.
* call.c (build_object_call): Don't look at DECL_NAME for a type.
(pt.c): Or CP_TYPE_QUALS for an ERROR_MARK.
(typeck.c): Or TYPE_MAIN_VARIANT for a type.
1999-05-20 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (lvalue_p_1): A NOP_EXPR can be an lvalue.
(build_cplus_new): Make sure that what we return is of the right type.
1999-05-20 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (make_ptrmem_cst): New function.
* expr.c (cplus_expand_constant): Split out from ...
(cplus_expand_expr): Here. Use cplus_expand_constant.
(init_cplus_expand): Set lang_expand_constant.
* pt.c (convert_nontype_argument): Use make_ptrmem_cst.
* tree.c (make_ptrmem_cst): Define.
* typeck.c (unary_complex_lvalue): Use make_ptrmem_cst.
* typeck2.c (initializer_constant_valid_p): Use make_ptrmem_cst.
1999-05-19 Mark Mitchell <mark@codesourcery.com>
* decl2.c (start_static_storage_duration_function): Fix comment.
(finish_file): Create static storage duration functions lazily.
Wed May 19 02:50:53 1999 Arvind Sankar <arvinds@mit.edu>
* gxxint.texi: Fix typo.
1999-05-18 Jason Merrill <jason@yorick.cygnus.com>
* call.c (joust): Compare the types of the conv ops, not the
target types of the conversions.
Tue May 18 00:21:34 1999 Zack Weinberg <zack@rabi.phys.columbia.edu>
* lang-specs.h: Define __GNUC__ and __GNUC_MINOR__ only if -no-gcc
was not given.
1999-05-17 Mark Mitchell <mark@codesourcery.com>
* cp-tree.def (TEMPLATE_ID_EXPR): Update documentation.
* decl.c (grokfndecl): Don't allow inline declarations of friend
template specializations, or friend template specializations with
default arguments.
* pt.c (tsubst): Handle substitution into array types that does
not yield a fixed upper bound, even when not processing a
template.
(tsubst_copy): Deal with the fact that the second operand to a
TEMPLATE_ID_EXPR may be NULL_TREE, a TREE_LIST, or a TREE_VEC.
* search.c (marked_pushdecls_p): Don't descend into
TEMPLATE_TYPE_PARMs and the like.
(unmarked_pushdecls_p): Likewise.
* call.c (build_over_call): Don't throw away
initializations/copies of empty classes; use MODIFY_EXPR and
INIT_EXPR as for non-empty classes.
* class.c (finish_struct_1): Put the padding byte for an empty
class on the TYPE_NONCOPIED_PARTS list for the class.
1999-05-16 Mark Mitchell <mark@codesourcery.com>
* decl2.c (build_expr_from_tree): Handle COMPONENT_REFs that
indicate a reference to a field that is a qualified name.
1999-05-16 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_objects): Don't use .?tors.* if we don't have
ASM_OUTPUT_CONSTRUCTOR.
* friend.c (do_friend): Add attrlist arg. Remove support for
getting a non-decl as 'decl'.
* decl.c (grokfndecl): Remove attrlist arg. Don't set attrs or
rtl.
(grokdeclarator): Adjust.
* cp-tree.h: Adjust.
1999-05-16 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (permanent_p): New function.
* init.c (build_new_1): Use mapcar, not copy_node, to copy a
possibly complex tree node.
* tree.c (mapcar): Adjust comments, and follow coding standards in
conditional.
(permanent_p): New function.
1999-05-13 Per Bothner <bothner@cygnus.com>
* class.c (push_lang_context): Turn off DECL_IGNORED_P for
primitive Java types, if we actually see `extern "Java"'.
1999-05-10 18:21 -0400 Zack Weinberg <zack@rabi.phys.columbia.edu>
* lang-specs.h: Pass -$ to the preprocessor.
1999-05-10 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_offset_ref): Wrap baselinks in OFFSET_REF, too.
Don't bother wrapping an OFFSET_TYPE around unknown_type_node.
(resolve_offset_ref): Don't handle a raw baselink.
* cvt.c (build_expr_type_conversion): Likewise.
* typeck.c (decay_conversion, build_c_cast, convert_for_assignment,
convert_for_initialization): Likewise.
* class.c (instantiate_type): Handle seeing a baselink under an
OFFSET_REF.
* error.c (dump_expr): Likewise.
* pt.c (for_each_template_parm): Likewise.
(resolve_overloaded_unification): Likewise.
* tree.c (is_overloaded_fn, really_overloaded_fn): Likewise.
* typeck.c (expr_sizeof): Also complain about other permutations
of overloaded functions.
1999-05-07 Jason Merrill <jason@yorick.cygnus.com>
* init.c (resolve_offset_ref): Don't return a raw method.
Use BASELINK_P.
* typeck.c (decay_conversion): Don't handle a raw method.
Resolve all OFFSET_REFs.
(get_member_function_from_ptrfunc): 0 is a valid vtable index.
(build_binary_op_nodefault): Handle resolving overloaded fns. Use
same_type_p for pmf bits. Don't use build_binary_op to compare
raw pointers to methods.
(convert_for_assignment): Check for OFFSET_REF, not OFFSET_TYPE,
to decide when to call resolve_offset_ref.
(build_c_cast, convert_for_initialization): Likewise.
* cvt.c (build_expr_type_conversion): Likewise.
1999-05-06 Nathan Sidwell <nathan@acm.org>
* call.c (build_new_method_call): Use TYPE_MAIN_VARIANT of class.
1999-05-05 Mark Mitchell <mark@codesourcery.com>
* decl2.c (start_objects): Don't let static constructors and
destructors get inlined.
* parse.y (nested_name_specifier): Make sure ordinary types are
complete, just like template types.
* parse.c: Regenerated.
* pt.c (check_explicit_specialization): Improve error messages.
1999-05-04 Martin von Löwis <loewis@informatik.hu-berlin.de>
* typeck.c (string_conv_p): Use same_type_p to check whether we
try to convert between char and wchar_t.
1999-05-03 Mark Mitchell <mark@codesourcery.com>
* search.c (lookup_field_r): Set the TREE_TYPE of an ambiguous
lookup to error_mark_node here.
(lookup_member): Revise documentation. Add comments. Don't set
the TREE_TYPE to error_mark_node here, and don't build up an extra
TREE_LIST for ambiguous lookups.
(setup_class_bindings): Adjust accordingly.
(push_class_decls): Revise out-of-date comments.
* typeck.c (build_const_cast): Tighten checks for legality.
1999-05-02 Martin von Löwis <loewis@informatik.hu-berlin.de>
* init.c (build_member_call): Lookup names coming from
namespace-scoped LOOKUP_EXPR.
1999-05-03 Jim Blandy <jimb@zwingli.cygnus.com>
* gxxint.texi: Add documentation for 'I'.
1999-05-02 Martin von Löwis <loewis@informatik.hu-berlin.de>
* tinfo.cc (operator==): Qualify type_info with std::.
1999-05-02 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (lang_decl_flags): Remove comdat. Updated dummy.
(DECL_COMDAT): Remove definition.
1999-05-01 Mark Mitchell <mark@codesourcery.com>
* decl.c (wrapup_globals_for_namespace): Fix thinko in previous
change.
1999-04-30 Mark Mitchell <mark@codesourcery.com>
* class.c (build_vtable): Use build_lang_decl when building
vtables, not just build_decl.
(prepare_fresh_vtable): Likewise.
* decl.c (wrapup_globals_for_namespace): Mark vtables as
DECL_EXTERNAL when calling wrapup_global_declarations.
* decl2.c (priority_info_s): Add initializations_p and
destructions_p members.
(finish_vtable_vardecl): Use TREE_SYMBOL_REFERENCED, not TREE_USED,
when deciding what vtables to write out.
(ssdf_decls): New variable.
(ssdf_decls_used): Likewise.
(start_static_storage_duration_function): Deal with being called
multiple times. Avoid inlining this function.
(generate_inits_for_priority): Deal with reuse of priority map.
(get_priority_info): Clear initializations_p and destructions_p.
(do_static_initialization): Tweak comment.
(do_static_destruction): Likewise. Fix condition on sentries for
destruction.
(generate_ctor_or_dtor_function): Call all of the static storage
duration functions.
(generate_ctor_or_dtor_function_for_priority): Check
initializations_p and destructions_p to see what priorities need
initialization functions.
(finish_file): Rework to generate multiple static storage duration
functions, rather than just one.
* typeck.c (build_const_cast): Tweak last change to handle
templates correctly.
* typeck.c (build_const_cast): Disallow use of const_cast to
anything but a pointer or reference type.
1999-04-30 Nathan Sidwell <nathan@acm.org>
* decl.c (cp_finish_decl): Don't permit arrays of abstract or
signature type.
1999-04-29 Mark Mitchell <mark@codesourcery.com>
* decl2.c (do_static_destruction): Remove obsolete FIXME comment.
(finish_file): Indent comments properly.
1999-04-29 Richard Henderson <rth@cygnus.com>
* decl2.c (do_static_initialization): Call do_pending_stack_adjust.
(do_static_destruction): Likewise.
1999-04-29 Nathan Sidwell <nathan@acm.org>
* cp-tree.h (TYPE_NOTHROW_P): New macro.
* decl2.c (delete_sanity): Warn on deleting void *.
* init.c (build_new_1): Use TYPE_NOTHROW_P.
* typeck.c (c_expand_return): cp_pedwarn on returning NULL from
throwing operator new.
1999-04-28 Nathan Sidwell <nathan@acm.org>
* cp-tree.h (build_component_addr): Remove prototype.
* typeck.c (build_component_addr): Make static. Remove MSG
argument.
(build_component_addr): Remove MSG parameter, clean up
comment.
(build_x_function_call): Use cp_error.
(build_unary_op): Adjust call of build_component_addr.
1999-04-28 Mark Mitchell <mark@codesourcery.com>
* pt.c (tsubst_friend_class): Check for NULL.
Wed Apr 28 11:42:22 1999 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* search.c (binfo_for_vtable): Initialize bfvi.var.
1999-04-27 Nathan Sidwell <nathan@acm.org>
* rtti.c (build_x_typeid): Check rtti is enabled.
1999-04-26 Mark Mitchell <mark@codesourcery.com>
* search.c (is_subobject_of_p): Make sure we're looking at the
right baseclasses.
1999-04-26 Marc Espie <espie@cvs.openbsd.org>
* Make-lang.in (cplib2.ready): Don't depend on phony targets.
1999-04-23 Mark Mitchell <mark@codesourcery.com>
* decl2.c (finish_file): Tweak handling of extern inlines so that
they are not unnecessarily put out.
* search.c (is_subobject_of_p): Handle TEMPLATE_TYPE_PARMs and
such as base classes.
1999-04-22 Brendan Kehoe <brendan@cygnus.com>
* tree.c (build_exception_variant): Fix typo: use the chain of U,
not trying V, while cycling through U.
1999-04-22 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (lang_decl_flags): Remove returns_first_arg and
preserves_first_arg. Enlarge dummy accordingly.
(DECL_TINFO_FN_P): New macro.
(SET_DECL_TINFO_FN_P): Likeiwse.
(DECL_RETURNS_FIRST_ARG): Remove.
(DECL_PRESERVES_THIS): Likewise.
(DECL_INIT_PRIORITY): New macro.
(finish_struct_1): Change prototype.
(cat_namespace_levels): Remove prototype.
(vtable_decl_p): New prototype.
(vtype_decl_p): Likewise.
(sigtable_decl_p): Likewise.
(walk_globals_pred): New typedef.
(walk_globals_fn): Likewise.
(walk_globals): New prototype.
(walk_namespaces_fn): New typedef.
(walk_namespaces): New prototype.
(wrapup_globals_for_namespace): Likewise.
(walk_vtables): Remove prototype.
(walk_sigtables): Likewise.
(instantiate_pending_templates): New prototype.
* class.c (finish_struct_1): Don't return a value.
* decl.h (pending_statics): Remove declaration.
* decl.c (walk_namespaces_r): New function.
(walk_globals_r): Likewise.
(vtable_decl_p): Likewise.
(vtype_decl_p): Likewise.
(sigtable_decl_p): Likewise.
(walk_namespaces): Likewise.
(walk_globals_data): New type.
(walk_globals): New function.
(wrapup_globals_for_namespace): Likewise.
(expand_static_init): Remove assertion. Remove redundancy in
conditional. Don't put static data members in static_aggregates
Tidy.
(finish_function): Remove redundancy in conditional. Don't set
DECL_RETURNS_FIRST_ARG.
(cat_namespace_levels): Remove.
* decl2.c: Include splay-tree.h and varray.h.
(priority_info_s): New structure.
(finish_vtable_vardecl): Change prototype. Adjust for new calling
conventions.
(prune_vtable_vardecl): Likewise.
(finish_sigtable_vardecl): Likewise.
(setup_initp): Remove.
(do_dtors): Remove.
(do_ctors): Remove.
(start_static_storage_duration_function): New function.
(generate_inits_for_priority): Likewise.
(finish_static_storage_duration_function): Likewise.
(get_priority_info): Likewise.
(do_static_initialization): Likewise.
(do_static_destruction): Likewise.
(do_static_initialization_and_destruction): Likewise.
(generate_ctor_or_dtor_function): Likewise.
(generate_ctor_and_dtor_functions_for_priority): Likewise.
(pending_statics): Make it a varray.
(pending_statics_used): New variable.
(saved_inlines): Make it a varray.
(saved_inlines_used): New variable.
(finish_static_data_member): Change method of updating
pending_statics.
(mark_inline_for_output): Remove #if 0'd code. Change method of
updating saved_inlines.
(walk_vtables): Remove.
(walk_sigtables): Likewise.
(import_export_decl): Use DECL_TINFO_FN_P.
(pending_templates): Remove declaration.
(maybe_templates): Likewise.
(static_aggregates_initp): Likewise.
(setup_initp): Likewise.
(finish_objects): Simplify.
(INITIALIZE_P_IDENTIFIER): New macro.
(PRIORITY_IDENTIFIER): New macro.
(SSDF_IDENTIFIER): New macro.
(initialize_p_decl): New variable.
(priority_decl): Likewise.
(ssdf_decl): Likewise.
(priority_info_map): Likewise.
(finish_file): Recode output of static intializers and other
file-scope finalization tasks.
* error.c (OB_END_TEMPLATE_ID): New macro.
(dump_type_real): Use it.
(dump_decl): Likewise.
(dump_function_name): Likewise.
* lex.c (set_typedecl_interface_info): Adjust for new walk_globals
interface.
(check_newline): Use walk_globals, not walk_vtables.
* pt.c (pending_tempalte_expansions): Remove.
(set_vardecl_interface_info): Likewise.
(pending_templates): Make static.
(maybe_templates): Likewise.
(instantiate_class_template): Adjust call to finish_struct_1.
(instantiate_pending_templates): New function.
* rtti.c (get_tinfo_fn): Use SET_DECL_TINFO_FN_P.
* tree.c (static_aggregates_initp): Remove.
(cp_valid_lang_attribute): Don't use it; use DECL_INIT_PRIORITY
instead.
* Makefile.in (decl2.o): Depend on varray.h and splay-tree.h.
* gxx.gperf (RETURN): Rename to RETURN_KEYWORD to avoid clashes
with the RTL code RETURN.
* hash.h: Regenerated.
* lex.c (reinit_parse_for_block): Use RETURN_KEYWORD.
* parse.y: Replace RETURN with RETURN_KEYWORD throughout.
* parse.c: Regenerated.
* pt.c: Include varray.h. Include rtl.h since varray.h requires
it.
(inline_parm_levels): New variable.
(inline_parm_levels_used): Likewise.
(maybe_begin_member_template_processing): Update them.
(maybe_end_member_template_processing): Use them, rather than
guessing how many levels to pop.
* decl.c (make_typename_type): Tighten error-checking.
1999-04-20 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (build_binary_op): Remove unneeded parameter.
* class.c (build_vrable_entry_ref): Adjust call to
build_binary_op.
* decl.c (expand_static_init): Likewise.
(grokdeclarator): Likewise.
(finish_function): Likewise.
* decl2.c (delete_sanity): Likewise.
(do_dtors): Likewise.
(do_ctors): Likewise.
* error.c (dump_type_suffix): Likewise.
* expr.c (cplus_expand_expr): Likewise.
* init.c (resolve_offset_ref): Likewise.
(build_new): Likewise.
(build_new_1): Likewise.
(build_vec_delete_1): Likewise.
(expand_vec_init_catch_clause): Likewise.
(build_delete): Likewise.
* pt.c (tsubst): Likewise.
* rtti.c (synthesize_tinfo_fn): Likewise.
* search.c (expand_upcast_fixups): Likewise.
(expand_direct_vtbls_init): Likewise.
* typeck.c (get_member_function_from_ptrfunc): Likewise.
(build_binary_op_nodefault): Likewise.
(point_int_sum): Likewise.
(pointer_diff): Likewise.
(build_unary_op): Likewise.
(build_modify_expr): Likewise.
(get_delta_difference): Likewise.
(build_ptrmemfunc): Likewise.
(expand_ptrmemfunc_cst): Likewise.
1999-04-20 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): Always call cplus_decl_attributes.
* decl2.c (grokfield): Pass attrlist to grokdeclarator.
1999-04-19 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (finish_static_data_member_decl): New function.
* decl2.c (finish_static_data_member_decl): Split out from ...
(grokfield): Here.
* pt.c (instantiate_class_template): Use it here instead of
trying to fake it.
(tsubst_decl): Don't set DECL_ASSEMBLER_NAME;
finish_static_data_member_decl will do that. Explicit set
DECL_EXTERNAL to match non-template processing.
1999-04-18 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (finish_class_definition): Add parameter.
* parse.y (structsp): Use it. Don't call pop_scope here.
* parse.c: Regenerated.
* semantics.c (finish_class_definition): Pop it here.
1999-04-17 Mark Mitchell <mark@codesourcery.com>
* decl.c (xref_tag): Revise handling of nested template
declarations.
* pt.c (check_explicit_specialization): Tweak handling of friend
templates in template classes.
(tsubst_friend_class): Handle friend declarations for nested
member template classes.
1999-04-16 Mark Mitchell <mark@codesourcery.com>
* class.c (finish_struct): Remove unused variable.
(pushclass): Likewise.
(invalidate_class_lookup_cache): Likewise.
* cp-tree.def (TYPENAME_TYPE): Improve documentation.
* decl.c (build_typename_type): Make sure TYPENAME_TYPE_FULLNAME
doesn't get obliterated.
(make_typename_type): Handle template classes correctly.
* cp-tree.h (TREE_NONLOCAL_FLAG): Remove.
(storetags): Declare.
* class.c (finish_struct): Don't use TREE_NONLOCAL_FLAG.
(pushclass): Likewise. Use storetags to install tag declarations,
not pushtag.
(invalidate_class_lookup_cache): Don't use TREE_NONLOCAL_FLAG.
* decl.c (storetags): Make it global.
(push_class_binding): Set INHERITED_VALUE_BINDING_P for an
implicit typename declaration.
(pushtag): Tidy. Don't use TREE_NONLOCAL_FLAG.
* method.c (hack_identifier): Likewise.
* search.c (lookup_member): Likewise.
* decl.c (warn_about_implicit_typename_lookup): New function.
(lookup_name_real): Use it. Rework handling of implicit typename
extension.
1999-04-15 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (lookup_nested_field): Remove.
* class.c (push_nested_class): Handle UNION_TYPEs.
(pop_nested_class): Likewise.
* decl.c (lookup_name_real): Don't call lookup_nested_field.
(start_decl): Use push_nested_class, not just pushclass.
(cp_finish_decl): Use pop_nested_class, not just popclass.
* search.c (lookup_nested_field): Remove.
* cp-tree.h (lang_type): Add documentation.
* decl2.c (handle_class_head): Create template declarations here,
as appropriate.
* parse.y (class_head): Return whether or not we entered a new
scope, as well as the type named.
(named_class_head): Likewise.
(named_complex_class_head_sans_basetype): Likewise.
(structsp): Adjust accordingly. Pop scope when required.
* parse.c: Regenerated.
* pt.c (check_default_tmpl_args): Robustify.
(redeclare_class_template): Likewise.
(instantiate_class_template): An instantiation of an
anonymous union is itself an anonymous union.
* semantics.c (begin_class_definition): Don't create template
declarations here.
1999-04-15 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (after_type_declarator_intern): New nonterminal.
(after_type_declarator): Use it.
(direct_after_type_declarator): Likewise. Move above
nonnested_type to fix reduce/reduce conflict resolution.
(declmods): Reducing from just 'attributes' has EMPTY precedence.
* Makefile.in (CONFLICTS): Update.
* decl.c (define_label): Downgrade error for jumping over a
non-POD decl to pedwarn.
1999-04-14 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (popclass): Change declaration.
(pop_nested_class): Likewise.
(poplevel_class): Remove declaration.
* call.c (convert_default_argument): Pass no arguments to
popclass.
* class.c (finish_struct_1): Likewise.
(finish_struct): Likewise.
(popclass): Remove argument. Simplify code accordingly.
(pop_nested_class): Likewise.
* decl.c (poplevel_class): Declare it here, and make it static.
(poplevel): Handle class scopes.
(poplevel_class): Don't take an rgument. Simplify.
(pop_everything): Pass no arguments to pop_nested_class.
(cp_finish_decl): Pass no arguments to popclass.
(grokdeclarator): Pass no arguments to pop_nested_class.
(finish_function): Likewise.
* decl2.c (grokfield): Likewise.
(pop_scope): Pass no arguments to popclass.
* lex.c (do_pending_defargs): Pass no arguments to pop_nested_class.
* pt.c (instantiate_class_template): Move call to pushclass, and
document. Pass no arguments to popclass.
(regenerate_decl_from_template): Likewise.
1999-04-14 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_unary_op): Handle taking the address of a unique
bound non-static member function.
1999-04-13 Martin von Loewis <loewis@informatik.hu-berlin.de>
* lang-options.h (-Wdeprecated): New flag.
* decl2.c (warn_deprecated): New flag.
(lang_decode_option): Deprecated this-is-variable,
external-templates, alt-external-templates.
Support -Wdeprecated.
* errfn.c (cp_deprecated): New function.
1999-04-13 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (setup_initp): Compare DECL_ASSEMBLER_NAME instead
of the decls themselves.
* pt.c (tsubst_function_type): Copy attributes over.
* tree.c (cp_valid_lang_attribute): New fn. Handle init_priority
and com_interface.
* cp-tree.h: Add prototype.
* decl.c (init_decl_processing): Set valid_lang_attribute.
1999-04-13 Mark Mitchell <mark@codesourcery.com>
* class.c (finish_struct_1): Look at the const-ness of the field's
type, not the TREE_READONLY-ness of the declaration.
* method.c (synthesize_method): Likewise.
* pt.c (tsubst_decl): Call c_apply_type_quals_to_decl when
creating new declarations.
1999-04-13 Mike Stump <mrs@wrs.com>
* decl2.c (import_export_decl): Because vtables always reference
virtual functions, even if they are inlined, don't allow
-fno-implement-inlines to not emit them, instead, emit them with
the vtable.
* decl.c (start_function): Likewise.
1999-04-12 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (struct lang_type): Add com_interface.
(CLASSTYPE_COM_INTERFACE): New macro.
* class.c (set_rtti_entry): COM interface classes have no RTTI
entries in their vtables; adjust.
(add_virtual_function, finish_base_struct, skip_rtti_stuff,
modify_one_vtable, fixup_vtable_deltas1, override_one_vtable,
finish_struct_1): Likewise.
* decl2.c (mark_vtable_entries): Likewise.
* rtti.c (build_headof, get_tinfo_fn_dynamic): Likewise.
* search.c (get_abstract_virtuals_1, get_abstract_virtuals,
expand_upcast_fixups): Likewise.
* tree.c (debug_binfo): Likewise.
* cp-tree.h (COMPARE_NO_ATTRIBUTES): New macro.
* typeck.c (comptypes): If we get it, ignore attributes.
* class.c (instantiate_type): Use BASELINK_P. Change complain
parameter to flags; 2 means ignore attributes.
* call.c (build_op_delete_call): Pass it.
* decl.c (xref_tag): Only complain once about using a typedef-name
with 'struct'. Downgrade to pedwarn.
* decl.c (grokdeclarator): Allow [] syntax for zero-length array.
* parse.y (absdcl_intern): New nonterminal.
(absdcl, direct_abstract_declarator): Use it.
* pt.c (lookup_template_class): Look through implict typename.
1999-04-11 Mark Mitchell <mark@codesourcery.com>
* friend.c (add_friend): Deal gracefully with error_mark_node.
* method.c (build_overload_value): Handle pointers-to-members as
template parameters.
* decl.c (push_binding): Fix typo in comment.
1999-04-10 Mark Mitchell <mark@codesourcery.com>
* error.c (dump_type_real): If a typename is a template-id, put
out the template arguments.
(dump_expr): Handle TEMPLATE_ID_EXPR.
* pt.c (lookup_template_class): Now that full arguments are
available everywhere, remove code that tried to guess them.
1999-04-09 Mark Mitchell <mark@codesourcery.com>
* decl.c (make_typename_type): Complain if we don't find a type
when trying to make a typename type for a non-template type.
1999-04-09 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): Pass attributes to grokdeclarator.
(grokdeclarator): Handle attributes on constructor-syntax
initializers.
1999-04-08 Mark Mitchell <mark@codesourcery.com>
* error.c (dump_expr): Don't crash on INDIRECT_REFs whose operands
don't have types.
* search.c (template_self_reference_p): Tweak.
1999-04-07 Mark Mitchell <mark@codesourcery.com>
* init.c (build_offset_ref): Don't build yet another weird data
structure to describe overloaded functions.
1999-04-06 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (BASELINK_P): New macro.
(SET_BASELINK_P): Likewise.
* init.c (build_member_call): Remove needless assignment in if
statement.
* search.c (lookup_field_r): Fix handling when we are looking
specifically for a type; these are not hidden by functions and
variables.
(lookup_member): Use SET_BASELINK_P.
* tree.c (is_overloaded_fn): Use BASELINK_P.
(really_overloaed_fn): Likewise.
(get_first_fn): Likewise.
1999-04-05 Mark Mitchell <mark@codesourcery.com>
* decl.c (lookup_name_current_level): Tweak, and improve
documentation.
* class.c (maybe_fixup_vptrs): Remove declaration.
(build_class_init_list): Likewise.
* decl.c (pushdecl_class_level): Call check_template_shadow here
...
(push_class_level_binding): ... not here.
* search.c (dfs_push_type_decls): Only avoid
template-self-reference TYPE_DECLs if they are from base classes.
1999-04-04 Mark Mitchell <mark@codesourcery.com>
* pt.c (check_template_shadow): Don't treat OVERLOADs as _DECL
nodes. Tidy.
1999-04-03 Jason Merrill <jason@yorick.cygnus.com>
* class.c (maybe_fixup_vptrs, build_class_init_list): Lose.
(finish_struct_1): Don't call build_class_init_list.
1999-04-02 Mark Mitchell <mark@codesourcery.com>
* tinfo.h (__class_type_info): Fix illegal declaration.
* cp-tree.def (TEMPLATE_ID_EXPR): Update comment.
* cp-tree.h (INHERITED_VALUE_BINDING_P): New macro.
(IDENTIFIER_CLASS_VALUE): Improve documentation.
(is_properly_derived_from): Declare.
(invalidate_class_lookup_cache): Likewise.
(maybe_maybe_note_name_used_in_class): Likewise.
(note_name_declared_in_class): Likewise.
(push_using_decl): Remove duplicate declaration.
(id_in_current_class): Remove declaration.
(push_class_binding): Change prototype.
(clear_identitifer_class_values): Declare.
* call.c (is_properly_derived_from): Make it global.
(build_new_function_call): Be careful about updating candidates.
(build_new_method_call): Handle COMPONENT_REFs. Don't crash when
asked to make illegal calls.
* class.c: Include splay-tree.h.
(class_stack_node): Add names_used slot.
(check_member_decl_is_same_in_complete_scope): Remove.
(add_method): Fix comment. Push the declaration into class
scope.
(finish_struct_1): When popping the class, pop the bindings too.
Remove check for data member/function member conflict.
(finish_struct): Remove calls to
check_member_decl_is_same_in_complete_scope. Change calls to
popclass.
(pushclass): Clear names_used in the class stack entry.
Use invalidate_class_lookup_cache to remove cached entries, rather
than magic values with popclass. Clear IDENTIFIER_CLASS_VALUE
before entering a new class. Remove dead code. Don't mess with
current_function_decl when pushing declarations.
(invalidate_class_lookup_cache): New function, split out from ...
(popclass): Here. Clean up names_used on our way out.
(instantiate_type): Adjust.
(build_self_reference): Don't push the declaration here.
(maybe_note_name_used_in_class): New function.
(note_name_declared_in_class): Likewise.
* decl.c (add_binding): Change prototype.
(find_class_binding_level): New function.
(innermost_nonclass_level): Likewise.
(current_binding_level): Update documentation.
(inner_binding_level): Remove. Replace with current_binding_level
throughout.
(push_binding_level): Remove special handling of
class_binding_level.
(pop_binding_level): Likewise. Use find_class_binding_level.
(suspend_binding_level): Likewise.
(global_bindings_p): Use innermost_nonclass_level.
(toplevel_bindings_p): Likewise.
(namespace_bindings_p): Likewise.
(pseudo_global_level_p): Likewise.
(push_binding): Clear INHERITED_VALUE_BINDING_P.
(add_binding): Check for illegal multiple declarations. Return a
value indicating whether or not the new binding was legal.
(push_local_binding): Skip over class binding levels. Check
return value from add_binding.
(push_class_binding): Set INHERITED_VALUE_BINDING_P. Call
note_name_declared_in_class.
(pushlevel_class): Remove "fake out the rest of the compiler"
code.
(poplevel_class): Reset IDENTIFIER_CLASS_VALUEs.
(clear_identifier_class_values): New function.
(pop_from_top_level): Use it.
(pop_everything): Tweak.
(maybe_process_template_type_declaration): Don't push the
declaration for the template here.
(pushtag): Don't push tag declarations into class scope here.
(pushdecl): Apply DeMorgan's law for readability.
(pushdecl_class_level): Remove special-case code for
TYPE_BEING_DEFINED. Handle OVERLOADs and anonymous unions.
(push_class_level_bindng): Deal with inherited bindings.
(lookup_name_real): Remove special-case code for
TYPE_BEING_DEFINED, and some implicit typename magic.
(grokdeclarator): Handle COMPONENT_REF for a template function.
(build_enumerator): Don't call pushdecl_class_level here.
(id_in_current_class): Remove.
* decl2.c (grokfield): Don't call pushdecl_class_level or
check_template_shadow.
* errfn.c (cp_file_of): Don't declare.
(cp_line_of): Likewise.
* error.c (dump_decl): Handle an OVERLOAD.
(cp_file_of): Likewise.
(cp_line_of): Likewise.
* init.c (build_member_call): Handle a COMPONENT_REF.
* lex.c (do_identifier): Call maybe_note_name_used_in_class, not
pushdecl_class_level.
* method.c (hack_identifier): Build COMPONENT_REFs for references
to member templates as well as member functions. Remove dead
code.
* parse.y (left_curly): Remove.
(nonnested_type): Call maybe_note_name_used_in_class, not
pushdecl_class_level.
* parse.c: Regenerated.
(nested_name_specifier_1): Likewise.
* pt.c (check_explicit_specialization): Adjust, for robustness.
(check_template_shadow): Handle OVERLOADs.
(build_template_decl): Set DECL_CONSTRUCTOR_P on the
TEMPLATE_DECL, if appropriate.
* search.c (envelope_add_decl): Remove.
(dfs_pushdecls): Likewise.
(dfs_compress_decls): Likewise.
(dfs_push_decls): New function.
(dfs_push_type_decls): Likewise.
(setup_class_bindings): Likewise.
(template_self_reference_p): Likewise.
(lookup_field_r): Use it.
(looup_member): Remove old comment. Deal with ambiguity.
(push_class_decls): Use dfs_push_decls and dfs_push_type_decls,
and remove envelope processing.
* semantics.c (begin_class_definition): Let pushclass push
declarations for base classes.
(finish_member_declaration): Push declarations into class scope.
* typeck.c (build_component_ref): Just put an OVERLOAD into the
COMPONENT_REF, not a TREE_LIST of an OVERLOAD.
(build_x_function_call): Deal with OVERLOAD. Handle template-ids.
* Makefile.in (class.o): Depend on splay-tree.h.
Wed Mar 31 11:30:43 1999 Nathan Sidwell <nathan@acm.org>
* cvt.c (convert_pointer_to_real): Use same_type_p.
* typeck.c (comp_target_types): Use same_type_p.
1999-03-31 Jason Merrill <jason@yorick.cygnus.com>
* semantics.c (begin_inline_definitions,
finish_inline_definitions): Rename from finish_default_args and
begin_inline_definitions, respectively, to something that isn't a
total lie. :)
* parse.y (structsp): Adjust.
* tree.c (hash_tree_cons): Remove obsolete via_* parms.
(list_hash_lookup): Likewise.
(hash_tree_chain): Adjust.
* pt.c (tsubst): Adjust.
(tsubst_arg_types): Use plain hash_tree_cons.
* cp-tree.h (hash_tree_cons_simple): Lose.
* parse.y (declmods, nonempty_cv_qualifiers): Use hash_tree_cons.
Wed Mar 31 10:48:29 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (hash.h): Generate using gperf language 'C', not
'KR-C', so gperf uses the `const' keyword on strings.
* gxx.gperf (resword): Const-ify a char*.
1999-03-30 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (IDENTIFIER_AS_DESC, IDENTIFIER_AS_LIST,
CLASSTYPE_BASELINK_VEC, CLASSTYPE_N_SUPERCLASSES,
CLASSTYPE_N_BASECLASSES, CLASSTYPE_MAX_DEPTH,
CLASSTYPE_BASE_INIT_LIST, CLASSTYPE_AS_LIST, CLASSTYPE_ID_AS_LIST,
CLASSTYPE_BINFO_AS_LIST): Remove cruft.
* class.c, lex.c, parse.y, ptree.c, search.c, semantics.c,
tree.c: Adjust.
1999-03-29 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (lang_decode_option): Remove -Wsign-promo from -Wall.
1999-03-28 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (fn_type_unification): Ignore 'this' parm from conversion ops.
1999-03-27 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (add_friend): Declare.
(add_friends): Likewise.
* friend.c (add_friend): Make it global. Don't add to
DECL_BEFRIENDING_CLASSES if the befriending class is a template.
(add_friends): Make it global.
(make_friend_class): Don't add to DECL_BEFRIENDING_CLASSES if the
befriending class is a template.
* parse.y (component_decl_1): Fix typo in comment.
* parse.c: Regenerated.
* pt.c (instantiate_class_template): Use add_friend and
add_friends rather that duplicating some of their functionality
here.
1999-03-27 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_field_call): Unify 'this' and non-'this' cases.
* typeck.c (build_indirect_ref): Check for 'this' sooner.
Fri Mar 26 10:20:34 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* call.c (op_error): Const-ify a char*.
(add_candidate, source_type, add_warning): Add static prototype.
(print_z_candidates): Const-ify a char*.
* class.c (resolve_address_of_overloaded_function,
fixed_type_or_null, build_vtable_entry_ref): Add static prototype.
(get_vtable_name, finish_struct_1): Const-ify a char*.
* cvt.c (convert_to_reference): Likewise.
* decl.c (redeclaration_error_message, record_builtin_type,
record_unknown_type, member_function_or_else, bad_specifiers):
Likewise.
(find_binding, select_decl, unqualified_namespace_lookup,
lookup_flags, qualify_lookup, record_builtin_java_type, tag_name):
Add static prototype.
(warn_extern_redeclared_static, duplicate_decls, pushdecl,
implicitly_declare, record_builtin_java_type, define_function,
grok_op_properties, tag_name): Const-ify a char*.
* cp-tree.h (FORMAT_VBASE_NAME): Allow parameter `BUF' to be const.
(define_function, finish_builtin_type): Const-ify a char*.
(cp_error, cp_error_at, cp_warning, cp_warning_at, cp_pedwarn,
cp_pedwarn_at, cp_compiler_error, cp_sprintf): Add prototype args.
(file_name_nondirectory): Const-ify a char*.
(init_filename_times): Don't prototype.
(compiler_error): Prototype.
(yyerror, init_repo): Const-ify a char*.
(build_srcloc): Don't prototype.
(build_x_indirect_ref, build_indirect_ref, build_component_addr):
Const-ify a char*.
(warn_for_assignment): Don't prototype.
(convert_for_initialization, readonly_error, check_for_new_type,
GNU_xref_begin, GNU_xref_file, GNU_xref_ref, GNU_xref_call):
Const-ify a char*.
* decl2.c (acceptable_java_type, output_vtable_inherit,
setup_initp, start_objects, finish_objects, do_dtors, do_ctors,
merge_functions, decl_namespace, validate_nonmember_using_decl,
do_nonmember_using_decl): Add static prototype.
(lang_f_options): Const-ify a char*.
(finish_builtin_type): Likewise.
(add_function, arg_assoc_namespace, arg_assoc_class): Add static
prototype.
* errfn.c: Include cp-tree.h.
(cp_thing): Add static prototype.
(compiler_error): Don't protoptype.
(cp_compiler_error): Cast `compiler_error' to `errorfn' before
passing it to `cp_thing'.
* error.c (interesting_scope_p): Add static prototype.
* except.c (build_eh_type_type, build_eh_type_type_ref): Const-ify
a char*.
* init.c (compiler_error): Don't prototype.
(member_init_ok_or_else): Const-ify a char*.
(build_java_class_ref): Add static prototype.
* lex.c (compiler_error): Don't prototype.
(get_time_identifier, interface_strcmp, extend_token_buffer,
handle_cp_pragma): Const-ify a char*.
(is_global, init_filename_times): Add static prototype.
(file_name_nondirectory, cplus_tree_code_name): Const-ify a char*.
(compiler_error): Change from fixed args to variable args.
(yyerror): Const-ify a char*.
* parse.y (cond_stmt_keyword): Const-ify a char*.
(parse_decl): Add static prototype.
* pt.c (template_args_equal, print_template_context): Likewise.
(print_candidates, check_default_tmpl_args): Const-ify a char*.
(instantiate_class_template): Likewise.
* repo.c (get_base_filename, open_repo_file, init_repo): Likewise.
* rtti.c (call_void_fn, expand_generic_desc, expand_si_desc,
expand_class_desc, expand_ptr_desc, expand_attr_desc): Likewise.
* search.c (lookup_field_info, lookup_member): Likewise.
(lookup_member): Cast the first argument of `bzero' to a PTR.
* sig.c (compiler_error): Don't prototype.
(build_signature_pointer_or_reference_nam): Const-ify a char*.
(get_sigtable_name, build_member_function_pointer): Likewise.
* tree.c (compiler_error): Don't prototype.
(no_linkage_helper, build_srcloc): Add static prototype.
(build_vbase_pointer_fields): Const-ify a char*.
(__eprintf): Don't unnecessarily handle `const' when !__STDC__.
* typeck.c (compiler_error): Don't prototype.
(convert_for_assignment): Const-ify a char*.
(comp_cv_target_types): Add static prototype.
(build_x_indirect_ref, build_indirect_ref, convert_arguments,
build_component_addr, build_unary_op, convert_for_initialization):
Const-ify a char*.
* typeck2.c (ack): Add static prototype and change from fixed args
to variable args.
(readonly_error, check_for_new_type): Const-ify a char*.
* xref.c (_XREF_FILE, find_file, filename, fctname, declname,
fixname, open_xref_file, classname, GNU_xref_begin): Likewise.
(GNU_xref_file): Likewise. Also use `xmalloc' instead of `malloc'.
(GNU_xref_end_scope, GNU_xref_ref, GNU_xref_decl, GNU_xref_call,
gen_assign, GNU_xref_member): Const-ify a char*.
1999-03-25 Martin von Löwis <loewis@informatik.hu-berlin.de>
* gxxint.texi: Remove old discussion on copying virtual bases.
1999-03-25 Zack Weinberg <zack@rabi.columbia.edu>
* Make-lang.in: Remove all references to g++.o/g++.c.
Link g++ from gcc.o.
1999-03-25 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (comdat_linkage): Treat vtables like functions.
1999-03-25 Mark Mitchell <mark@codesourcery.com>
* pt.c (tsubst_decl): tsubst into DECL_BEFRIENDING_CLASSES.
1999-03-25 Nathan Sidwell <nathan@acm.org>
* decl.c (init_decl_processing): Add `signed' type as a synonym
for `int'.
1999-03-25 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (common_type): Handle cv-qual unification for pointers
to members.
* decl.c (unqualified_namespace_lookup): Return error_mark_node
on error.
(lookup_name_real): Set LOOKUP_COMPLAIN when *not* parsing.
* lex.c (do_identifier): If we got error_mark_node, call
lookup_name again.
1999-03-24 Martin von Löwis <loewis@informatik.hu-berlin.de>
* class.c (finish_struct_1): Always reset TYPE_FIELDS for empty
classes.
1999-03-24 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): Do nested field lookup regardless of
TYPE_BEING_DEFINED.
1999-03-24 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (lang_type): Remove has_assignment and
has_real_assignment. Add befriending_classes.
(TYPE_HAS_ASSIGNMENT): Remove.
(TYPE_HAS_REAL_ASSIGNMENT): Likewise.
(CLASSTYPE_BEFRIENDING_CLASSES): New macro.
(lang_decl): Document.
(DECL_BEFRIENDING_CLASSES): New macro.
(FRIEND_NAME): Move declaration to more obvious location.
(FRIEND_DECLS): Likewise.
* class.c (finish_struct_1): Don't use TYPE_HAS_REAL_ASSIGNMENT.
* decl.c (duplicate_decls): Copy DECL_BEFRIENDING_CLASSES.
(fixup_anonymous_union): Don't use TYPE_HAS_ASSIGNMENT.
(grok_op_properties): Likewise.
* friend.c (is_friend): Use FRIEND_NAME and FRIEND_DECLS.
(add_friend): Likewise. Don't do weird things with assignment
operators. Update DECL_BEFRIENDING_CLASSES.
(add_friends): Don't do weird things with assignment operators.
(make_friend_class): Likewise. Update
CLASSTYPE_BEFRIENDING_CLASSES.
* pt.c (instantiate_class_template): Don't set
TYPE_HAS_ASSIGNMENT.
(tsubst_copy): Substitute the TREE_TYPE for more unary
expressions.
* ptree.c (print_lang_type): Don't look at TYPE_HAS_ASSIGNMENT.
* search.c (protected_accessible_p): New function.
(friend_accessible_p): Likewise.
(accessible_p): Use them.
1999-03-23 Mark Mitchell <mark@codesourcery.com>
* pt.c (convert_nontype_argument): Don't create things that aren't
PTRMEM_CSTs when applying a qualification conversion to a
PTRMEM_CST.
1999-03-23 Mark Mitchell <mark@codesourcery.com>
* Makefile.in (OBJS): Don't mention hash.o.
(OBJDEPS): Likewise.
1999-03-23 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Set at_eof to 2 after expanding ctors.
* decl.c (expand_static_init): Make sure we don't add any after
then.
* decl.c (cp_finish_decl): Move intelligence about handling
DECL_COMDAT for variables from here...
* decl2.c (comdat_linkage): ...to here.
(maybe_make_one_only): Tweak.
(import_export_decl): Call comdat_linkage for variables, too.
(finish_file): Handle template statics properly.
1999-03-22 Mark Mitchell <mark@codesourcery.com>
* cp-tree.h (TYPE_PTRMEMFUNC_P): Use TYPE_PTRMEMFUNC_FLAG.
Document internals of pointer-to-member-functions.
(DELTA2_FROM_PTRMEMFUNC): Make it call delta2_from_ptrmemfunc.
(PFN_FROM_PTRMEMFUNC): Likewise.
(build_type_conversion): Remove unused parameter.
(build_ptrmemfunc1): Declare.
(expand_ptrmemfunc_cst): New function.
(delta2_from_ptrmemfunc): Likewise.
(pfn_from_ptrmemfunc): Likewise.
* cvt.c (cp_convert_to_pointer): Remove unused parameter to
build_type_conversion. Use TYPE_PTRMEM_P for readability.
(convert_to_reference): Remove unused parameter to
build_type_conversion.
(ocp_convert): Likewise.
(build_user_type_conversion): Likewise.
* error.c (dump_expr): Handle NULL pointer-to-member functions.
* expr.c (cplus_expand_expr): Handle PTRMEM_CSTs for functions.
* method.c (build_overload_value): Don't go splitting CONSTRUCTORs
open when handling pointer-to-member functions.
* pt.c (convert_nontype_argument): Clean up error messages. Be
more stringent with pointers-to-members.
* typeck.c (build_ptrmemfunc1): Don't declare. Make it global.
(build_unary_op): Tidy ever-so-slightly.
(build_conditional_expr): Remove extra parameter to
build_type_conversion.
(build_ptrmemfunc): Build PTRMEM_CSTs if we know what function
we're using.
(expand_ptrmemfunc_cst): Define.
(delta2_from_ptrmemfunc): Likewise.
(pfn_from_ptrmemfunc): Likewise.
1999-03-19 Mark Mitchell <mark@codesourcery.com>
* init.c (build_member_call): Handle template-id expressions
correctly.
* typeck.c (build_x_function_call): Likewise.
1999-03-19 Chip Salzenberg <chip@perlsupport.com>
* friend.c (make_friend_class): Avoid core dump when
not-yet-defined friend type lacks TYPE_LANG_SPECIFIC().
1999-03-18 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_function): Suppress normal linkage heuristics
for #pragma interface under MULTIPLE_SYMBOL_SPACES.
1999-03-19 Alexandre Oliva <oliva@dcc.unicamp.br>
* Make-lang.in: ($(INTL_TARGETS)): Depend on cp/parse.c.
($(srcdir)/cp/parse.c): Moved from ../Makefile.in.
1999-03-17 Martin von Löwis <loewis@informatik.hu-berlin.de>
* parse.y (named_complex_class_head_sans_basetype):
Do not push a scope for error_mark_node.
(maybe_base_class_list): Likewise.
* decl.c (start_decl): Check for error_mark_node as a type.
Detected by g++.brendan/array-refs.C.
(start_decl_1): Likewise. Detected by g++.bugs/900322_01.C.
(maybe_build_cleanup_1): Likewise. Detected by
g++.jason/incomplete1.C.
* tree.c (build_dummy_object): Use void_zero_node instead of the
error_mark_node.
(is_dummy_object): Check for such a node.
Detected by g++.bob/inherit1.C
1999-03-16 Jason Merrill <jason@yorick.cygnus.com>
* method.c (old_backref_index): Split out...
(flush_repeats): From here. Rename back from try_old_backref.
(build_mangled_name): Put back some old-style repeat handling.
Mon Mar 15 21:57:16 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* lex.c: Don't include setjmp.h.
(parse_float): New static function.
(pf_args): New struct.
(real_yylex): Use them in call to `do_float_handler'.
1999-03-15 Mark Mitchell <mark@markmitchell.com>
* decl.c (xref_basetypes): Set CLASSTYPE_VBASECLASSES here.
* tree.c (layout_basetypes): Not here.
* search.c (dfs_search): Remove; no longer used.
1999-03-12 Mark Mitchell <mark@markmitchell.com>
* decl2.c (validate_nonmember_using_decl): Issue sensible
error-messages on bogus qualifiers.
1999-03-14 Jason Merrill <jason@yorick.cygnus.com>
* call.c (add_function_candidate): Fix uninitialized variable.
* Makefile.in (search.o): Add dependency on varray.h.
1999-03-13 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (duplicate_decls): Use same_type_p.
* method.c (try_old_backref): Renamed from flush_repeats. Use
same_type_p. Don't try to handle repeats. Return success.
(is_back_referenceable_type): Return 0 if TYPE_FOR_JAVA. Support
calls from old-style code, too.
(check_ktype): Use same_type_p.
(check_btype): Use same_type_p. Don't pull out TYPE_MAIN_VARIANT.
(build_qualified_name): Simplify logic.
(process_overload_item): Strip typedefs and quals at the top.
(build_mangled_name_for_type_with_Gcode): Remove call to
type_canonical_variant.
(build_mangled_name): Likewise. Remove support for old-style
repeats, which have been disabled since 2.7.2. Don't mess with
TREE_USED.
(build_decl_overload_real): Don't mess with TREE_USED.
1999-03-13 Nathan Sidwell <nathan@acm.org>
* error.c (cp_printers): Add 'F' escape character.
(dump_type_real): Remove TREE_LIST (fnargs) printing.
Functionality moved to dump_parameters.
(dump_type_suffix): Use dump_parameters and dump_exception_spec.
(dump_function_decl): Extend meaning of V parameter. Use
dump_parameters and dump_exception_spec.
(dump_parameters): New static function.
(dump_exception_spec): New static function.
(fndecl_as_string): Change argument semantics. Use
dump_function_decl directly.
* sig.c (build_signature_table_constructor): Use cp_error.
1999-03-13 Martin von Löwis <loewis@informatik.hu-berlin.de>
* semantics.c (finish_switch_cond): Handle error cases gracefully.
Detected by g++.law/enum5.C.
* typeck.c (build_modify_expr): Check for errors after resolving
offsets. Detected by g++.brendan/static1.C.
* decl.c (complete_array_type): Ignore initial_value if it is an
error. Detected by g++.benjamin/17930.C.
* typeck2.c (process_init_constructor): Return error if one argument
is in error. Detected by g++.benjamin/13478.C.
1999-03-12 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl.c (select_decl): Allow class templates when we need types.
* decl2.c (ambiguous_decl): Likewise.
1999-03-12 Mark Mitchell <mark@markmitchell.com>
* lex.c (do_identifier): Correct call to enforce_access.
* search.c (accessible_p): Tweak comment.
1999-03-10 Mark Mitchell <mark@markmitchell.com>
* semantics.c (begin_class_definition): Call build_self_reference.
(finish_member_declaration): Set DECL_CONTEXT for TYPE_DECLs.
* search.c (assert_canonical_unmarked): Fix typo in prototype.
* search.c (dfs_canonical_queue): New function.
(dfs_assert_unmarked_p): Likewise.
(assert_canonical_unmarked): Likewise.
(access_in_type): Use it.
(accessible_p): Likewise. Walk the whole tree when umarking.
* sig.c (build_signature_table_constructor): Use accessible_p
instead of compute_access.
1999-03-09 Jason Merrill <jason@yorick.cygnus.com>
* call.c (add_builtin_candidates): Handle overloaded conversion ops.
1999-03-09 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (flag_access_control): Declare.
(TREE_VIA_PPUBLIC): Document.
(DECL_NONSTATIC_MEMBER_P): New macro.
(enforce_access): Return an indication of whether or not access
was permitted.
(build_self_reference): Change prototype.
(compute_access): Replace with ...
(accessible_p): New function.
(dfs_walk): Change prototype.
(dfs_unmark): Likewise.
(markedp): Likewise.
* call.c (enforce_access): Use accessible_p.
* class.c (build_self_reference): Insert the declaration into the
list of members for this type, and make it public.
* decl.c (xref_basetypes): Avoid ill-timed recursion.
* init.c (build_offset_ref): Use lookup_member, not three separate
name-lookups. Call enforce_access rather than checking for
illegal accesses here.
(resolve_offset_ref): Likewise.
* lex.c (do_identifier): Likewise.
* method.c (hack_identifier): Likewise.
* parse.y (self_reference): Remove.
(opt_component_decl_list): Don't use it.
* parse.c: Regenerated.
* pt.c (print_candidates): Generalize to handle lists of
overloaded functions.
(instantiate_class_template): Don't rely on TREE_VIA_PRIVATE; it's
not set.
(get_template_base): Use new calling convention for dfs_walk.
* search.c: Include varray.h. Add prototypes.
(dfs_walk): Accept a data pointer to pass to the work functions.
All callers changed. All work functions changed.
(breadth_first_search): Rename to bfs_walk, and make consistent
with dfs_walk.
(dfs_walk_real): New function.
(canonical_binfo): New function.
(context_for_name_lookup): Likewise.
(shared_marked_p): Likewise.
(shared_unmarked_p): Likewise.
(lokup_field_queue_p): Likewise.
(lookup_field_r): Generalize to handle both functions and fields.
(lookup_field): Just call lookup_member.
(lookup_fnfields): Likewise.
(lookup_member): Move body of lookup_field here and generalize.
(dfs_accessible_queue_p): Likewise.
(dfs_accessible_p): Likewise.
(dfs_access_in_type): Likewise.
(access_in_type): Likewise.
(compute_access): Remove, and replace with ...
(accessible_p): New function.
(vbase_types): Remove.
(vbase_decl_ptr_intermediate): Likewise.
(vbase_decl_ptr): Likewise.
(vbase_init_result): Likewise.
(closed_envelopes): Likewise.
(bvtable): Likewise.
1999-03-09 Jason Merrill <jason@yorick.cygnus.com>
* call.c (add_function_candidate): Check for proper number of args
before checking the validity of those args.
1999-03-06 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (struct lang_type): Add anon_union field.
(ANON_UNION_TYPE_P): Use it instead of examining type.
(SET_ANON_UNION_TYPE_P): New macro.
* decl.c (check_tag_decl): Use it.
* search.c (compute_access): Handle non-type contexts earlier, and
handle NULL_TREE.
* tree.c (build_exception_variant): Use copy_to_permanent.
* decl2.c (setup_initp): Give statics with no priority the default
priority here.
(do_dtors, do_ctors, finish_file): Remove special handling of
non-prioritized statics.
1999-03-05 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (ANON_UNION_TYPE_P): Robustify.
* decl.c (make_typename_type): Don't issue an error if an
immediate lookup fails; it migt be resolved later.
* friend.c (is_friend): Add comment.
* search.c (breadth_first_search): Add POSTFN and DATA
parameters. Tidy. All callers changed.
(lookup_field_queue_p): New function.
(lookup_field_r): Likewise.
(lookup_field_post): Likewise.
(lookup_field): Use them, via breadth_first_search, instead of
duplicating logic.
(compute_access): Robustify.
(lookup_fnfield_info): New structure.
1999-03-05 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst, case ARRAY_REF): Use tsubst_expr again.
1999-03-03 Jason Merrill <jason@yorick.cygnus.com>
* class.c, decl2.c, method.c, pt.c: Add 'static' to make SunOS 4
cc happy.
* decl2.c (import_export_class): Also return if
CLASSTYPE_INTERFACE_ONLY is set.
1999-03-03 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl.c (push_overloaded_decl): Only overwrite the old binding if
there was one.
* decl2.c (do_local_using_decl): Fix loop termination.
1999-03-02 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (determine_specialization): Don't declare.
* pt.c (determine_specialization): Make it static. Eliminate
complain parameter. Note that decl is always non-NULL now, and
simplify accordingly.
* decl.c (maybe_push_to_top_level): Always call
push_cp_function_context.
(pop_from_top_level): Always call pop_cp_function_context.
1999-02-26 Nathan Sidwell <nathan@acm.org>
* typeck.c (complete_type_or_else): Add VALUE arg, for helpful
diagnostics.
* cp-tree.h (complete_type_or_else): Added VALUE parameter.
* init.c (build_new_1): Extra arg to complete_type_or_else.
(build_delete): Likewise.
* typeck.c (require_complete_type): Likewise.
(pointer_int_sum): Likewise.
(pointer_diff): Likewise.
(build_component_ref): Likewise.
* typeck2.c (incomplete_type_error): Always use cp_error.
Show declaration of undefined type, if appropriate.
Deal with UNKNOWN_TYPE nodes.
* typeck.c (require_complete_type): Use TYPE_SIZE as
size_zero_node to mean incomplete type.
(require_complete_type_in_void): New function.
(build_compound_expr): Call complete_type_in_void for LHS.
(build_c_cast): Call complete_type_in_void for void cast.
* cvt.c (ocp_convert): Call complete_type_in_void for void cast.
* decl.c (cplus_expand_expr_stmt): Void expression checks moved to
require_complete_type_in_void. Call it.
* cp-tree.h (require_complete_type_in_void): Prototype new function.
* typeck.c (convert_arguments): Use alternative format for
function decls. Don't require_complete_type here. Simplify
diagnostic printing.
(convert_for_initialization): Don't require_complete_type on RHS yet.
* call.c (convert_arg_to_ellipsis): Call require_complete_type.
* call.c (build_over_call): Cope with qualified void return type.
* semantics.c (finish_call_expr): Likewise.
* typeck.c (build_function_call_real): Likewise.
(c_expand_return): Likewise.
* decl2.c (reparse_absdcl_as_expr): Cope with qualified void type.
* call.c (print_z_candidates): Use alternate print format, to be
consistent with (pt.c) print_candidates.
* method.c (hack_identifier): List candidate members.
* search.c (lookup_field): Build ambiguous list, and show it, if
ambiguous.
1999-02-26 Mark Mitchell <mark@markmitchell.com>
* typeck.c (decay_conversion): Don't confuse constant array
variables with their initializers.
* decl.c (duplicate_decls): Copy DECL_TEMPLATE_INSTANTIATED when
merging decls.
* pt.c (regenerate_decl_from_template): Tweak for clarity.
(instantiate_decl): Mark a decl instantiated before regenerating
it to avoid recursion.
* tree.c (mapcar): Don't call decl_constant_value unless we know
something is TREE_READONLY_DECL_P.
* class.c (check_for_override): Don't stop checking when we find
the first overridden function. Delete #if 0'd code.
* search.c (get_matching_virtual): Likewise.
1999-02-25 Richard Henderson <rth@cygnus.com>
* lang-specs.h: Define __FAST_MATH__ when appropriate.
1999-02-24 Mike Stump <mrs@wrs.com>
* typeck.c (convert_for_assignment): Allow boolean integral constant
expressions to convert to null pointer.
1999-02-24 Martin von Loewis <loewis@informatik.hu-berlin.de>
* decl.c (lookup_namespace_name): Resolve namespace aliases.
* class.c (push_nested_class): Allow namespaces.
* decl2.c (set_decl_namespace): Add friendp parameter.
* decl.c (grokfndecl): Pass it.
(grokvardecl): Likewise.
* cp-tree.h: Change declaration.
1999-02-24 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Allow an array of explicit size zero.
1999-02-23 Jason Merrill <jason@yorick.cygnus.com>
* errfn.c: Change varargs code to look like toplev.c.
* method.c (process_modifiers): Don't prepend 'U' for char or
wchar_t.
1999-02-20 Craig Burley <craig@jcb-sc.com>
* Make-lang.in (cplib2.ready): Don't consider updating
cplib2 stuff if the current directory isn't writable, as
it won't work (such as during a `make install').
Sun Feb 21 20:38:00 1999 H.J. Lu (hjl@gnu.org)
* decl2.c (start_objects): Make file scope constructors and
destructors local to the file if ASM_OUTPUT_CONSTRUCTOR and
ASM_OUTPUT_DESTRUCTOR are defined.
1999-02-19 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (CLASSTYPE_METHOD_VEC): Adjust comment.
(fn_type_unification): Adjust prototype.
(lookup_fnfields_1): Declare.
* call.c (add_template_candidate_real): Adjust call to
fn_type_unification.
* class.c (add_method): Don't allow duplicate declarations of
constructors or destructors.
(resolve_address_of_overloaded_function): Remove unused variable.
Adjust call to fn_type_unification.
* decl.c (grokfndecl): Be more robust in the face of illegal
specializations.
* decl2.c (check_classfn): Remove hokey handling of member
templates.
* pt.c (determine_specialization): Improve comments. Adjust to
handle template argument deduction as per the standard.
(check_explicit_specialization): Fix comment spacing. Handle
type-conversion operators correctly. Improve error-recovery.
(fn_type_unification): Remove EXTRA_FN_ARG parameter.
(get_bindings_real): Simplify handling of static members.
* search.c (lookup_fnfields_1): Make it have external linkage.
* typeck.c (compparms): Fix comment.
(build_unary_op): Don't try to figure out which template
specialization is being referred to when when the address-of
operator is used with a template function.
Thu Feb 18 23:40:01 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cp-tree.h (lvalue_or_else): Qualify a char* with the `const'
keyword to match an analogous change at the top level.
* tree.c (lvalue_or_else): Likewise.
1999-02-17 Mark Mitchell <mark@markmitchell.com>
* decl.c (xref_basetypes): Comment.
* pt.c (instantiate_class_template): Use xref_basetypes.
1999-02-16 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (tsubst): Change prototype.
(tsubst_expr): Likewise.
(tsubst_copy): Likewise.
(type_unification): Remove prototype.
* call.c (convert_default_arg): Adjust call to tsubst_expr.
* class.c (resolve_address_of_overloaded_function): Just use
fn_type_unification.
* decl.c (grokdeclarator): Adjust call to tsubst.
* method.c (build_template_parm_names): Likewise.
* pt.c (GTB_VIA_VIRTUAL): New macro.
(GTB_IGNORE_TYPE): Likewise.
(resolve_overloaded_unification): Add `complain' parameter.
(try_one_overload): Likewise.
(tsubst_template_arg_vector): Likewise.
(tsubst_template_parms): Likewise.
(tsubst_aggr_type): Likewise.
(tsubst_arg_types): Likewise.
(tsubst_call_declarator_parms): Likewise.
(unify): Remove explicit_mask.
(type_unification_real): Likewise.
(get_template_base_recursive): Likewise.
(coerce_template_template_parms): Provide prototype.
(tsubst_function_type): Likewise.
(try_class_unification): New function.
All callers changed to use new complain parameter.
(get_template_base): Use try_class_unification.
(unify): Adjust handling of classes derived from template types.
(fn_type_unification): Substitute explicit arguments before
unification.
1999-02-16 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* decl.c (pushdecl): Remove dead code.
1999-02-16 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_objects): Fix code I missed in previous change.
1999-02-13 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): Return NULL_TREE instead of error_mark_node.
(grokdeclarator): Don't expect error_mark_node from grokfndecl.
* pt.c (maybe_process_partial_specialization): Complain about
'template <>' on non-specialization.
1999-02-10 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Catch wierd declarators.
* decl2.c (finish_file): Don't abort because of namespace parsing
failure.
(check_decl_namespace): Remove.
1999-02-09 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (get_template_base): Don't declare.
(dfs_walk): Declare.
(dfs_unmark): Likewise.
(markedp): Likewise.
* pt.c (unify): Remove duplicate declaration. Pass tparms and
targs to get_template_base.
(get_template_base_recursive): Move here from search.c. Check to
see that the base found can be instantiated to form the desired
type.
(get_template_base): Likewise.
(get_class_bindings): Simplify.
* search.c (get_template_base_recursive): Move to pt.c.
(get_template_base): Likewise.
(markedp): Make it global.
(dfs_walk): Likewise.
(dfs_unmark): Likewise.
1999-02-07 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (maybe_process_partial_specialization): Complain about
specialization in wrong namespace.
* tree.c (decl_namespace_context): New fn.
1999-02-06 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* decl2.c (arg_assoc_type): Handle TEMPLATE_TEMPLATE_PARM.
* pt.c (coerce_template_template_parms): Handle nested
template template parameters.
Sat Feb 6 18:08:40 1999 Jeffrey A Law (law@cygnus.com)
* typeck2.c: Update email addresses.
1999-02-04 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* pt.c (unify): Call coerce_template_parms with the COMPLAIN flag
turned off.
1999-02-04 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (retrofit_lang_decl): Split out...
(build_lang_decl): From here.
* decl.c (pushdecl): Call it for functions generated by the middle
end that don't have DECL_LANG_SPECIFIC.
* cp-tree.h: Declare it.
* decl2.c: Remove flag_init_priority. Always enable initp stuff.
(start_objects, finish_objects): Only use special
init_priority code if the user specified a priority.
(do_ctors, do_dtors): Use DEFAULT_INIT_PRIORITY for the non-initp
objects.
Wed Feb 3 22:50:17 1999 Marc Espie <Marc.Espie@liafa.jussieu.fr>
* Make-lang.in (GXX_OBJS): Remove choose-temp.o, pexecute.o and
mkstemp.o. Get them from libiberty now.
(DEMANGLER_PROG): Simlarly, remove getopt.o getopt1.o.
Tue Feb 2 22:38:48 1999 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* decl2.c (lang_decode_option): Use read_integral_parameter.
1999-02-01 Mark Mitchell <mark@markmitchell.com>
* pt.c (tsubst, case TYPENAME_TYPE): Check TYPE_BEING_DEFINED
before calling complete_type_or_else.
Mon Feb 1 09:49:52 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* input.c (inline): Don't define, its handled by system.h.
Sun Jan 31 20:34:29 1999 Zack Weinberg <zack@rabi.columbia.edu>
* decl2.c: Don't define flag_no_ident here. Don't process
-f(no-)ident here.
* cp-tree.h: Don't declare flag_no_ident here.
* lang-specs.h: Map -Qn to -fno-ident.
1999-01-28 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (struct tree_binding): Replace scope field with a union.
(BINDING_SCOPE): Adjust.
* decl.c (BINDING_LEVEL): Adjust.
1999-01-26 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Set up the DECL_INITIAL of
member constants.
* init.c (expand_member_init): Pull out TYPE_MAIN_VARIANT in
a ctor initializer.
* tree.c (equal_functions): Fix name in prototype.
* decl.c (push_local_binding): Add FLAGS argument.
(pushdecl, push_overloaded_decl): Pass it.
* decl2.c (do_local_using_decl): Likewise.
* cp-tree.h: Adjust prototype.
* decl.c (poplevel): Fix logic.
* decl.c (push_local_binding): Also wrap used decls in a TREE_LIST.
(poplevel): Handle that. Fix logic for removing TREE_LISTs.
(cat_namespace_levels): Don't loop forever.
1999-01-25 Richard Henderson <rth@cygnus.com>
* typeck.c (build_reinterpret_cast): Fix typo in duplicated test.
1999-01-25 Jason Merrill <jason@yorick.cygnus.com>
* class.c (resolve_address_of_overloaded_function): Mark the
chosen function used.
* call.c (build_call): Make sure that a function coming in has
been marked used already.
* decl.c (expand_static_init): Call mark_used instead of
assemble_external.
* except.c (call_eh_info, do_pop_exception, expand_end_eh_spec,
alloc_eh_object, expand_throw): Likewise.
* init.c (build_builtin_delete_call): Likewise.
* rtti.c (call_void_fn, get_tinfo_fn, build_dynamic_cast_1,
expand_si_desc, expand_class_desc, expand_ptr_desc, expand_attr_desc,
expand_generic_desc): Likewise.
1999-01-25 Martin von Löwis <loewis@informatik.hu-berlin.de>
* tree.c (equal_functions): New function.
(ovl_member): Call it.
1999-01-24 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert_to_pointer): Fix conversion of 0 to pmf.
1999-01-25 Martin von Loewis <loewis@informatik.hu-berlin.de>
* decl.c (decls_match): Return 1 if old and new are identical.
(push_overloaded_decl): Set OVL_USED when PUSH_USING.
1999-01-24 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_function): Make member functions one_only on windows.
* decl2.c (import_export_decl): Likewise.
* decl.c (grokdeclarator): Don't complain about implicit int in
a system header. Change same-name field check to not complain in
a system header instead of within extern "C".
1999-01-21 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (PUSH_GLOBAL): New macro.
(PUSH_LOCAL): Likewise.
(PUSH_USING): Likewise.
(namespace_bindings_p): Declare.
(push_overloaded_decl): Likewise.
* decl.c (push_overloaded_decl): Don't make it static. Check for
illegal declarations after using declarations here.
(namespace_bindings_p): Likewise.
(duplicate_decls): Don't consider declarations from different
namespaces to be the same.
(pushdecl): Use symbolic PUSH_ constants in calls to
push_overloaded_decl.
(push_overloaded_decl_1): Likewise.
* decl2.c (validate_nonmember_using_decl): Tweak `std' handling.
(do_nonmember_using_decl): Check for illegal using declarations
after ordinary declarations here.
(do_local_using_decl): Call pushdecl to insert declarations.
1999-01-21 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Fix lang_c -> lang_name_c typo.
1999-01-21 Mark Mitchell <mark@markmitchell.com>
* tree.c (build_cplus_array_type_1): Don't call build_array_type
for types involving template parameters.
* cp-tree.h (PARM_DECL_EXPR): Delete.
(convert_default_arg): Change prototype.
(check_default_argument): Declare.
(search_tree): Likewise.
* call.c (convert_default_arg): Take the function to which the
default argument belongs as a parameter, and do any necessary
instantiation here, instead of ...
(build_over_call): Here.
* decl.c (local_variable_p): New function.
(check_default_argument): Likewise, split out and tidied from ...
(grokparms): Here.
* error.c (dump_expr): Don't set PARM_DECL_EXPR.
* pt.c (tsubst_call_declarator_parms): New function.
(for_each_template_parm): Handle ARRAY_REFs. Do the obvious thing
with CALL_EXPRs, rather than trying to be clever.
(tsubst): Use tsubst_call_declarator_parms.
* tree.c (search_tree): Don't make it static.
* typeck.c (convert_arguments): Use new interface to
convert_default_arg.
1999-01-20 Mark Mitchell <mark@markmitchell.com>
* error.c (dump_function_decl): Don't print the argument types for
a function when the verbosity level is negative.
* call.c (build_over_call): Check format attributes at call-time.
* pt.c (tsubst_copy): Fix comment.
(unify): Don't allow unification with variable-sized arrays.
* semantics.c (finish_stmt_expr): When processing a template make
the BIND_EXPR long-lived.
1999-01-19 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_vtable_vardecl): Make vtables comdat here.
(import_export_vtable): Not here.
1999-01-18 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_component_ref): Wrap an OVERLOAD around a unique
non-static member function.
1999-01-18 Nathan Sidwell <nathan@acm.org>
* class.c (instantiate_type): Only diagnose illegal address of member
function if complaining.
* decl.c (lookup_name_real): Remove duplicate code.
1999-01-18 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (copy_template_template_parm): Use permanent_obstack.
1999-01-18 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* pt.c (unify): Remove restrictions on deduction of argument
of template template parameters.
1999-01-18 Nathan Sidwell <nathan@acm.org>
* rtti.c (build_dynamic_cast_1): Resolve OFFSET_REF exprs.
* class.c (resolve_address_of_overloaded_function): Show list of
all candidates, when none of them match.
1999-01-18 Chip Salzenberg <chip@perlsupport.com>
* typeck.c (comp_ptr_ttypes_reinterpret): Per ANSI, tighten up
definition of 'casting away const' in reinterpret_cast<>.
1999-01-18 Graham <grahams@rcp.co.uk>
* cvt.c: Add include for decl.h, remove extern for
static_aggregates which is now provided by decl.h.
* Makefile.in (cvt.o): Add dependency for decl.h and missing
dependencies for convert.h and flags.h.
1999-01-18 Nathan Sidwell <nathan@acm.org>
* decl2.c (do_dtors): Set current location to that of the
decl, for sensible diagnostics and debugging.
(check_classfn): Issue `incomplete type' error, if
class is not defined.
1999-01-16 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h: Add prototype for bound_pmf_p.
1999-01-16 Jason Merrill <jason@yorick.cygnus.com>
Manfred Hollstein <manfred@s-direktnet.de>
* decl.c (grokdeclarator): Don't make 'main(){}' an error with only
-Wreturn-type.
1999-01-16 Nathan Sidwell <nathan@acm.org>
* cp-tree.h (struct lang_type): Added has_mutable flag.
(CLASSTYPE_HAS_MUTABLE): New macro to access it.
(TYPE_HAS_MUTABLE_P): New macro to read it.
(cp_has_mutable_p): Prototype for new function.
* class.c (finish_struct_1): Set has_mutable from members.
* decl.c (cp_finish_decl): Clear decl's TREE_READONLY flag, if
it contains a mutable.
* typeck.c (cp_has_mutable_p): New function.
1999-01-15 Mark Mitchell <mark@markmitchell.com>
* pt.c (process_template_parm): Ignore top-level qualifiers on
non-type parameters.
* decl.c (start_function): Use current_function_parms in the call
to require_complete_type_for_parms, not the probably empty
DECL_ARGUMENTS.
1999-01-14 Jason Merrill <jason@yorick.cygnus.com>
* semantics.c (finish_asm_stmt): Don't warn about redundant volatile.
* decl2.c (import_export_class): MULTIPLE_SYMBOL_SPACES only means
that we don't suppress the other copies.
* lex.c (handle_cp_pragma): Likewise.
1999-01-13 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Undo 1998-12-14 change.
* tree.c (build_cplus_array_type_1): Likewise.
* pt.c (instantiate_class_template): Remove misleading comment.
(tsubst_aggr_type): Substitute if there are template parameters,
regardless of whether or not they use template arguments.
(unify): Likewise, but for unification.
1999-01-12 Richard Henderson <rth@cygnus.com>
* cp-tree.h (flag_permissive): Declare extern.
1999-01-06 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (IDENTIFIER_TYPENAME_P): Use OPERATOR_TYPENAME_FORMAT
here.
(lang_type): Add is_partial_instantiation. Decrease width of
dummy.
(PARTIAL_INSTANTIATION_P): New macro.
(OPERATOR_TYPENAME_P): Remove.
* decl.c (unary_op_p): Use IDENTIFIER_TYPENAME_P, not
OPERATOR_TYPENAME_P.
(grok_op_properties): Likewise.
* friend.c (do_friend): Handle friends that are member functions
correctly.
* lex.c (init_parse): Use OPERATOR_TYPENAME_FORMAT.
* pt.c (instantiate_class_template): Rework for clarity. Avoid
leaving TYPE_BEING_DEFINED set in obscure cases. Don't do
any more partial instantiation than is absolutely necessary for
implicit typename. Set PARTIAL_INSTANTIATION_P.
(tsubst_decl): Use IDENTIFIER_TYPENAME_P.
* semantics.c (begin_class_definition): Handle partial
specializations of a type that was previously partially
instantiated.
Wed Jan 6 03:18:53 1999 Mark Elbrecht <snowball3@usa.net.
* g++spec.c (LIBSTDCXX): Provide default definition.
(lang_specific_driver): Use LIBSTDCXX instead of "-lstdc++".
Tue Jan 5 22:11:25 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Make-lang.in (g++.o): Depend on prefix.h.
1999-01-04 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (bound_pmf_p): New fn.
* typeck.c (build_c_cast): Use it.
* decl.c (grok_op_properties): Use same_type_p.
Tue Dec 22 15:09:25 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (cvt.o): Depend on toplev.h.
* cp-tree.h (check_template_shadow, pod_type_p): Add prototypes.
* cvt.c: Include toplev.h.
* except.c (get_eh_caught, get_eh_handlers): Hide prototypes and
definitions.
* init.c (expand_vec_init): Initialize variable `itype'.
* lex.c (yyerror): Cast the argument passed to a ctype function to
an unsigned char.
* method.c (build_mangled_C9x_name): Wrap prototype and definition
in "HOST_BITS_PER_WIDE_INT >= 64".
* typeck.c (build_binary_op): Mark parameter `convert_p' with
ATTRIBUTE_UNUSED.
1998-12-22 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (TYPE_RAISES_EXCEPTIONS): Improve documentation.
* tree.c (build_exception_variant): Don't crash on empty throw
specs.
1998-12-18 DJ Delorie <dj@cygnus.com>
* cvt.c (convert_to_reference): Check for both error_mark_node
and NULL_NODE after call to convert_for_initialization.
1998-12-17 Jason Merrill <jason@yorick.cygnus.com>
* error.c (interesting_scope_p): New fn.
(dump_simple_decl): Use it.
(dump_expr, case CONSTRUCTOR): Force a & for a PMF.
(dump_expr, case OFFSET_REF): Print ->* if appropriate.
1998-12-16 Mark Mitchell <mark@markmitchell.com>
* class.c (resolve_address_of_overloaded_function): Do conversion
to correct type here, rather than ...
(instantiate_type): Here.
* cp-tree.h (DECL_TEMPLATE_PARM_P): New macro.
(DECL_TEMPLATE_TEMPLATE_PARM_P): Use it.
(decl_template_parm_p): Remove.
* decl.c (pushdecl): Don't set DECL_CONTEXT for a template
parameter.
* lex.c (do_identifier): Use DECL_TEMPLATE_PARM_P.
* pt.c (push_inline_template_parms_recursive): Set it.
(decl_template_parm_p): Remove.
(check_template_shadow): Use DECL_TEMPLATE_PARM_P.
(process_template_parm): Set it.
Wed Dec 16 16:33:58 1998 Dave Brolley <brolley@cygnus.com>
* lang-specs.h (default_compilers): Pass -MD, -MMD and -MG to cc1plus
if configured with cpplib.
1998-12-15 Mark Mitchell <mark@markmitchell.com>
* decl.c (poplevel): Make sure ns_binding is initialized.
* decl.c (finish_function): Undo inadvertent change in previous
patch.
1998-12-14 Mark Mitchell <mark@markmitchell.com>
* class.c (pushclass): Tweak handling of class-level bindings.
(resolve_address_of_overloaded_function): Update pointer-to-member
handling.
(instantiate_type): Likewise.
* cvt.c (cp_convert_to_pointer): Likewise.
* decl.c (pop_binding): Take the DECL to pop, not just the name.
Deal with `struct stat' hack.
(binding_level): Add to documentation.
(push_binding): Clear BINDING_TYPE.
(add_binding): New function.
(push_local_binding): Use it.
(push_class_binding): Likewise.
(poplevel): Adjust calls to pop_binding.
(poplevel_class): Likewise.
(pushdecl): Adjust handling of TYPE_DECLs; add bindings for hidden
declarations to current binding level.
(push_class_level_binding): Likewise.
(push_overloaded_decl): Adjust handling of OVERLOADs in local
bindings.
(lookup_namespace_name): Don't crash when confronted with a
TEMPLATE_DECL.
(lookup_name_real): Do `struct stat' hack in local binding
contexts.
(build_ptrmemfunc_type): Adjust documentation.
(grokdeclarator): Don't avoid building real array types when
processing templates unless really necessary.
(finish_method): Adjust calls to pop_binding.
* decl2.c (reparse_absdcl_as_expr): Recursively call ourselves,
not reparse_decl_as_expr.
(build_expr_from_tree): Deal with a template-id as the function to
call in a METHOD_CALL_EXPR.
* pt.c (convert_nontype_argument): Tweak pointer-to-member handling.
(maybe_adjust_types_For_deduction): Don't do peculiar things with
METHOD_TYPEs here.
(resolve_overloaded_unification): Handle COMPONENT_REFs. Build
pointer-to-member types where necessary.
* tree.c (build_cplus_array_type_1): Don't avoid building real
array types when processing templates unless really necessary.
(build_exception_variant): Compare the exception lists correctly.
1998-12-13 Mark Mitchell <mark@markmitchell.com>
* cp-tree.def (CPLUS_BINDING): Update documentation.
* cp-tree.h (LOCAL_BINDING_P): New macro.
(lang_identifier): Rename local_value to bindings.
(tree_binding): Make `scope' of type `void*', not `tree'.
(BINDING_SCOPE): Update documentation.
(IDENTIFIER_LOCAL_VALUE): Remove.
(IDENTIFIER_CLASS_VALUE): Document.
(IDENTIFIER_BINDING): New macro.
(IDENTIFIER_VALUE): Likewise.
(TIME_IDENTIFIER_TIME): Likewise.
(TIME_IDENTIFIER_FILEINFO): Likewise.
(IMPLICIT_TYPENAME_P): Likewise.
(set_identifier_local_value): Remove.
(push_local_binding): New function.
(push_class_binding): Likewise.
* class.c (pushclass): Update comments; use push_class_binding.
* decl.c (set_identifier_local_value_with_scope): Remove.
(set_identifier_local_value): Likewise.
(push_binding): New function.
(pop_binding): Likewise.
(binding_level): Update documentation. Remove shadowed.
(BINDING_LEVEL): New macro.
(free_binding_nodes): New variable.
(poplevel): Adjust for new name-lookup scheme. Don't mess up
BLOCK_VARs when doing for-scope extension. Remove effectively
dead code.
(pushlevel_class): Tweak formatting.
(poplevel_class): Adjust for new name-lookup scheme.
(print_binding_level): Likewise.
(store_bindings): Likewise.
(pushdecl): Likewise.
(pushdecl_class_level): Likewise.
(push_class_level_binding): Likewise.
(push_overloaded_decl): Update comments. Adjust for new
name-lookup scheme.
(lookup_name_real): Likewise.
(lookup_name_current_level): Likewise.
(cp_finish_decl): Likewise.
(require_complete_types_for_parms): Likewise. Remove misleading
#if 0'd code.
(grok_parms): Likewise. Don't call
require_complete_types_for_parms here.
(grok_ctor_properties): Don't treat templates as copy
constructors.
(grop_op_properties): Or as assignment operators.
(start_function): Document. Adjust for new name-lookup scheme.
(finish_function): Likewise.
* decl2.c (do_local_using_decl): Use push_local_binding.
* lex.c (begin_definition_of_inclass_inline): New function, split
out from ...
(do_pending_inlines): Here, and ...
(process_next_inline): Here.
(get_time_identifier): Use TIME_IDENTIFIER_* macros.
(init_filename_times): Likewise.
(extract_interface_info): Likewise.
(ste_typedecl_interface_info): Likewise.
(check_newline): Likewise.
(dump_time_statistics): Likewise.
(handle_cp_pragma): Likewise.
(do_identifier): Adjust for new name-lookup scheme.
* parse.y (function_try_block): Return ctor_initializer_opt value.
(fndef): Use it.
(fn.defpen): Pass appropriate values to start_function.
(pending_inline): Use functor_try_block value, and pass
appropriate values to finish_function.
* pt.c (is_member_template): Update documentation; remove handling
of FUNCTION_DECLs. As per name, this function should deal only in
TEMPLATE_DECLs.
(decl_template_parm_p): Change name of olddecl parameter to decl.
(check_template_shadow): Adjust for new name-lookup scheme.
(lookup_template_class): Likewise.
(tsubst_decl): Tweak so as not to confuse member templates with
copy constructors and assignment operators.
(unify): Handle UNION_TYPEs.
* ptree.c (print_lang_identifier): Adjust for new name-lookup scheme.
(lang_print_xnode): Adjust for new name-lookup scheme.
* typeck.c (mark_addressable): Likewise.
(c_expand_return): Likewise.
1998-12-08 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Allow field with same name as class
in extern "C".
* decl.c (lookup_name_real): Don't limit field lookup to types.
* class.c (check_member_decl_is_same_in_complete_scope): No error
if icv and x are the same.
* lex.c (do_identifier): Tweak error message.
1998-12-10 Mark Mitchell <mark@markmitchell.com>
* decl.c (start_enum): Use push_obstacks, not
end_temporary_allocation.
(finish_enum): Call pop_obstacks.
1998-12-10 Mark Mitchell <mark@markmitchell.com>
* class.c (instantiate_type): Return error_mark_node rather than
junk.
1998-12-09 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (most_specialized_instantiation): New function.
(print_candidates): Likewise.
* class.c (validate_lhs): Remove.
(resolve_address_of_overloaded_function): New function, split out
and then substantially reworked, from ...
(instantiate_type): Use it. Simplify.
* cvt.c (convert_to_reference): Complain when caller has indicated
that's the right thing to do. Don't crash if instantiate_type
fails.
* pt.c: Substitute `parameters' for `paramters' throughout.
(print_candidates): Don't make it static.
(most_specialized_instantiation): Split out from ...
(most_specialized): Here.
Wed Dec 9 15:33:01 1998 Dave Brolley <brolley@cygnus.com>
* lex.c (lang_init_options): Initialize cpplib.
* decl2.c (parse_options,cpp_initialized): Removed.
(lang_decode_option): Move initialization of cpplib to
lang_init_options.
1998-12-09 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Update the name of the TEMPLATE_DECL, as
well as the TYPE_DECL, when a typedef name is assigned to a
previously anonymous type.
1998-12-08 Andrew MacLeod <amacleod@cygnus.com>
* cp/except.c (call_eh_info): Use __start_cp_handler instead of
__cp_eh_info for getting the eh info pointer. Add table_index to
field list.
(push_eh_cleanup): Don't increment 'handlers' data field.
(process_start_catch_block): Don't set the 'caught' field.
* cp/exception.cc (CP_EH_INFO): New macro for getting the
exception info pointer within library routines.
(__cp_eh_info): Use CP_EH_INFO.
(__start_cp_handler): Get exception info pointer, set caught field,
and increment the handlers field. Avoids this being done by handlers.
(__uncatch_exception, __check_eh_spec): Use CP_EH_INFO macro.
(uncaught_exception): Use CP_EH_INFO macro.
Tue Dec 8 10:48:21 1998 Jeffrey A Law (law@cygnus.com)
* Make-lang.in (cxxmain.o): Depend on $(DEMANGLE_H), not demangle.h
Mon Dec 7 17:56:06 1998 Mike Stump <mrs@wrs.com>
* lex.c (check_newline): Add support for \ as `natural'
characters in file names in #line to be consistent with #include
handling. We support escape processing in the # 1 "..." version of
the command. See also support in cp/lex.c.
1998-12-07 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cp/decl2.c: s/data/opts/ when initializing cpp_reader
structure.
1998-12-07 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (build_typename_type): Set DECL_ARTIFICIAL.
* error.c (dump_simple_decl): Also print namespace context.
(dump_function_decl): Likewise.
* decl2.c (ambiguous_decl): Don't print old value if it's
error_mark_node.
* decl.c (lookup_name_real): Fix handling of local types shadowed
by a non-type decl. Remove obsolete code.
* cp-tree.h (DECL_FUNCTION_SCOPE_P): New macro.
* lang-options.h: Add -fpermissive.
* decl2.c: Likewise.
* cp-tree.h: Add flag_permissive.
* decl.c (init_decl_processing): If neither -fpermissive or -pedantic
were specified, set flag_pedantic_errors.
* call.c (build_over_call): Turn dropped qualifier messages
back into pedwarns.
* cvt.c (convert_to_reference): Likewise.
* typeck.c (convert_for_assignment): Likewise.
1998-12-05 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (coerce_new_type): Use same_type_p.
(coerce_delete_type): Likewise.
* call.c (check_dtor_name): Return 1, not error_mark_node.
1998-12-04 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (handle_cp_pragma): Disable #pragma interface/implementation
if MULTIPLE_SYMBOL_SPACES.
* pt.c (check_template_shadow): New fn.
* decl2.c (grokfield): Use it.
* decl.c (pushdecl): Likewise.
(pushdecl_class_level): Likewise.
(start_method): Likewise.
(xref_tag): Don't try to use 't' if we're defining.
* call.c (check_dtor_name): Just return an error_mark_node.
* pt.c (lookup_template_class): Complain about using non-template here.
* parse.y (apparent_template_type): Not here.
* pt.c (check_explicit_specialization): Complain about specialization
with C linkage.
* lang-options.h: Add -f{no-,}implicit-inline-templates.
* pt.c (convert_nontype_argument): Don't assume that any integer
argument is intended to be a constant-expression.
1998-12-03 Mark Mitchell <mark@markmitchell.com>
* class.c (handle_using_decl): Fix comment. Don't lookup
constructors in base classes.
(validate_lhs): Fix typo in comment.
* search.c (lookup_field_1): Don't return a USING_DECL.
* cp-tree.h (DECL_ACCESS): Improve documentation.
* decl.c (expand_static_init): Don't set the initialization-done
flag until the initialization is done.
1998-12-02 Mark Mitchell <mark@markmitchell.com>
* decl2.c (validate_nonmember_using_decl): Complain about using
declarations for class members.
1998-11-29 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (process_init_constructor): Use same_type_p.
* decl.c (check_tag_decl): Don't warn about null decl inside a
class.
* pt.c (unify, case OFFSET_TYPE): Pass down 'strict' rather than
UNIFY_ALLOW_NONE.
(convert_nontype_argument): Use TYPE_PTRMEMFUNC_FN_TYPE.
(resolve_overloaded_unification): Strip baselinks.
Fri Nov 27 13:07:23 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* g++spec.c: Don't prototype xmalloc.
1998-11-25 Jason Merrill <jason@yorick.cygnus.com>
* except.c (expand_throw): Use TYPE_PTR_P to check for pointers.
* decl.c (check_tag_decl): Do complain about null friend decl at
file scope.
1998-11-25 Andreas Schwab <schwab@issan.cs.uni-dortmund.de>
* lex.c (make_lang_type): Clear the whole struct lang_type, not
only the first multiple of sizeof (int).
1998-11-24 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): An explicit specialization of a static data
member is only a definition if it has an initializer.
* except.c (expand_throw): Use cp_finish_decl for the throw temp.
* cvt.c (build_up_reference): Pass DIRECT_BIND down into
cp_finish_decl.
* init.c (expand_default_init): Check for DIRECT_BIND instead of
DECL_ARTIFICIAL.
* call.c (build_over_call): Use build_decl.
* except.c (expand_throw): Just use convert, not
build_reinterpret_cast.
* lex.c (handle_generic_pragma): Use token_buffer.
* decl.c (check_tag_decl): Don't complain about null friend decl.
1998-11-24 Dave Pitts <dpitts@cozx.com>
* Make-lang.in (DEMANGLER_PROG): Move the output arguments to the
first position.
* lex.c (check_newline): Use ISALPHA.
(readescape): Use ISGRAPH.
(yyerror): Use ISGRAPH.
1998-11-24 Nathan Sidwell <nathan@acm.org>
* search.c (get_abstract_virtuals): Do not use initial
CLASSTYPE_ABSTRACT_VIRTUALS.
* typeck2.c (abstract_virtuals_error): Show location of abstract
declaration.
* call.c (build_new_method_call): Use
CLASSTYPE_ABSTRACT_VIRTUAL, rather than recalculate.
* class.c (finish_struct_bits): Don't bother working out whether
get_abstract_virtuals will do anything, just do it.
1998-11-24 Graham <grahams@rcp.co.uk>
* typeck.c (build_component_ref): Remove unused statement.
1998-11-24 Jason Merrill <jason@yorick.cygnus.com>
* class.c (add_method): Catch invalid overloads.
* class.c (add_method): Build up OVERLOADs properly for conversion ops.
* search.c (lookup_conversions): Handle getting real OVERLOADs.
(add_conversions): Likewise. Revert last change.
* call.c (add_conv_candidate): Pass totype to add_candidate instead
of fn. Don't add a new candidate if the last one was for the same
type.
(print_z_candidates): Handle getting a type as a function.
(joust): If we got two conversion candidates to the same type,
just pick one.
(build_object_call): Lose 'templates'.
(build_user_type_conversion_1): Handle getting real OVERLOADs.
1998-11-23 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (process_init_constructor): If there are elements
that don't have initializers and they need to have constructors
run, supply them with initializers.
* class.c (finish_struct_1): A class with a 0-width bitfield is
still empty.
1998-11-23 Mark Mitchell <mark@markmitchell.com>
* pt.c (instantiate_class_template): Don't try to figure out what
specialization to use for a partial instantiation. Correct
typos in a couple of comments. Avoid calling uses_template_parms
multiple times.
1998-11-23 Benjamin Kosnik <bkoz@cygnus.com>
* method.c (process_overload_item): Add call to
build_mangled_C9x_name for intTI_type_nodes.
(build_mangled_C9x_name): Add prototype, define.
* decl.c (init_decl_processing): Add names for
TImode_type_node.
1998-11-23 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (named_class_head): Update CLASSTYPE_DECLARED_CLASS.
* class.c (finish_struct_1): Set things up for 0-width bitfields
like we do for others.
* decl.c (check_tag_decl): New fn.
(shadow_tag): Split out from here.
* decl2.c (grok_x_components): Call it.
1998-11-22 Jason Merrill <jason@yorick.cygnus.com>
* decl.c: Lose warn_about_return_type.
(grokdeclarator): Always complain about implicit int, except for
`main () { ... }'.
* decl.c (tag_name): New fn.
(xref_tag): Complain about using typedef-name after class-key.
* init.c (expand_vec_init): Also keep going if from_array.
* tree.c (is_overloaded_fn): Also handle the output of
build_offset_ref.
* decl.c (grokdeclarator): Use constructor_name when comparing
field name against enclosing class.
* class.c (finish_struct_anon): Likewise.
1998-11-22 Mark Mitchell <mark@markmitchell.com>
* decl.c (poplevel): Remove code to handle KEEP == 2.
(finish_function): Don't confuse BLOCK-order when
processing a destructor.
1998-11-21 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (require_complete_types_for_parms): Call layout_decl
after we've completed the type.
1998-11-21 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl2.c (validate_nonmember_using_decl): Allow using templates
from the global namespace.
1998-11-21 Jason Merrill <jason@yorick.cygnus.com>
Handle specifying template args to member function templates.
* tree.c (build_overload): Always create an OVERLOAD for a template.
* search.c (add_conversions): Handle finding an OVERLOAD.
* decl2.c (check_classfn): Likewise.
* lex.c (identifier_type): See through a baselink.
* parse.y (do_id): Don't call do_identifier if we got a baselink.
* class.c (instantiate_type, case TREE_LIST): Recurse.
* decl.c (grokdeclarator): Allow a boolean constant for array
bounds, odd as that sounds.
* pt.c (unify): Be more strict about non-type parms, except for
array bounds.
(UNIFY_ALLOW_INTEGER): New macro.
1998-11-19 Manfred Hollstein <manfred@s-direktnet.de>
* Make-lang.in (mandir): Replace all uses of $(mandir) by $(man1dir).
1998-11-19 Jason Merrill <jason@yorick.cygnus.com>
* semantics.c (begin_class_definition): Call
maybe_process_partial_specialization before push_template_decl.
Don't call push_template_decl for a specialization.
* search.c (lookup_field): Do return a member template class.
* decl2.c (handle_class_head): Handle member template classes.
* decl.c (grokdeclarator): A parm type need not be complete.
* pt.c (convert_nontype_argument): Fix thinko.
1998-11-18 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (PTRMEM_CST_CLASS): Fix typo.
(global_delete_fndecl): New variable.
* decl.c (global_delete_fndecl): Define it.
(init_decl_processing): Set it.
* init.c (build_builtin_delete_call): Use it.
* tree.c (mapcar): Recursively call mapcar for the type of EXPR
nodes.
1998-11-18 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (cplus_expand_expr_stmt): Always complain about unresolved
type.
* tree.c (lvalue_p_1): An INDIRECT_REF to a function is an lvalue.
* call.c (build_object_call): Also support references to functions.
* typeck.c (convert_for_initialization): Don't decay a function
if the target is a reference to function.
* search.c (add_conversions): Get all the overloads from a class.
* decl.c (grok_ctor_properties): Complain about any constructor
that will take a single arg of the class type by value.
* typeck2.c (build_functional_cast): Can't create objects of
abstract classes this way.
* cvt.c (ocp_convert): Likewise.
* decl.c (grokfndecl): Member functions of local classes are not
public.
1998-11-18 Mark Mitchell <mark@markmitchell.com>
* Make-lang.in (cc1plus): Add dependency on hash.o.
1998-11-18 Jason Merrill <jason@yorick.cygnus.com>
* search.c (get_abstract_virtuals): Complain about virtuals with
no final overrider.
* typeck2.c (abstract_virtuals_error): Remove handling for virtuals
with no final overrider.
* class.c (override_one_vtable): Don't set DECL_ABSTRACT_VIRTUAL_P
on virtuals with no final overrider.
* lex.c (reinit_parse_for_block): Add a space after the initial ':'.
* class.c (finish_struct_1): Don't remove zero-width bit-fields until
after layout_type.
* friend.c (do_friend): Don't set_mangled_name_for_decl.
* class.c (finish_struct_anon): Complain about non-fields.
* decl2.c (build_anon_union_vars): Likewise.
* decl.c (grokdeclarator): Normal data members can't have the same
name as the class, either.
* class.c (finish_struct_anon): Neither can members of an
anonymous union.
1998-11-17 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (TYPE_ALIAS_SET): Document language-dependent uses.
(TYPE_BINFO): Likewise.
(IS_AGGR_TYPE): Tweak.
(SET_IS_AGGR_TYPE): New macro.
(CLASS_TYPE_P): Tweak.
(lang_type): Group mark bitfields together. Remove linenum.
(CLASSTYPE_SOURCE_LINE): Remove macro.
(CLASSTYPE_MARKED_N): New macro.
(SET_CLASSTYPE_MARKED_N): Likewise.
(CLEAR_CLASSTYPE_MARKED_N): Likewise.
(CLASS_TYPE_MARKED_*): Use them.
(SET_CLASSTYPE_MARKED_*): Likewise.
(CLEAR_CLASSTYPE_MARKED_*): Likewise.
(TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO): Likewise.
(TYPE_TEMPLATE_INFO): Handle TEMPLATE_TEMPLATE_PARMs as well.
(TYPENAME_TYPE_FULLNAME): Use TYPE_BINFO rather than CLASSTYPE_SIZE.
* class.c (class_cache_obstack): New variable.
(class_cache_firstobj): Likewise.
(finish_struct): Don't set CLASSTYPE_SOURCE_LINE.
(pushclass): Free the cache, when appropriate.
(popclass): Tidy.
(maybe_push_cache_obstack): Use class_cache_obstack.
* decl.c (include hash.h).
(typename_hash): New function.
(typename_compare): Likewise.
(build_typename_type): Check the hash table to avoid creating
duplicates.
(build_ptrmemfunc_type): Use SET_IS_AGGR_TYPE.
(grokdeclarator): Use CLASS_TYPE_P.
(xref_basetypes): Likewise.
(start_function): Likewise. Don't put current_class_ref on the
permanent obstack.
* error.c (dump_type_real): Use TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO
and TYPE_TI_ARGS.
* lex.c (note_got_semicolon): Use CLASS_TYPE_P.
(make_lang_type): Don't create TYPE_LANG_SPECIFIC and associated
fields for types other than class types. Do clear TYPE_ALIAS_SET
for types other than class types, though.
* method.c (build_overload_identifier): Use CLASS_TYPE_P and
TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO.
* pt.c (process_template_parm): Don't set
CLASSTYPE_GOT_SEMICOLON.
(lookup_template_class): Use TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO.
Coerce arguments on the momentary obstack.
(for_each_template_parm): Use TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO.
(instantiate_class_template): Calculate template arguments on the
momentary obstack. Tidy.
(tsubst_template_arg_vector): Use make_temp_vec.
(tsubst_aggr_type): Put template arguments on the momentary
obstack.
(tsubst_decl): Likewise.
(tsubst): Copy the array bounds index to the permanent obstack
before building index types. Use new macros.
(unify): Use new macros.
(do_type_instantiation): Likewise.
* search.c (lookup_fnfields_1): Use new macros.
(dfs_pushdecls): Build envelopes on the cache obstack.
(dfs_compress_decls): Use new macros.
(push_class_decls): Build on the cache obstack.
* semantics.c (finish_typeof): Don't set CLASSTYPE_GOT_SEMICOLON.
* sign.c (build_signature_pointer_or_reference_type): Use
SET_IS_AGGR_TYPE.
* tree.c (make_binfo): Check CLASS_TYPE_P.
(copy_template_template_parm): Adjust.
(make_temp_vec): Use push_expression_obstack.
* typeck.c (complete_type): Use new macros.
(comptypes): Likewise.
1998-11-17 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Add diagnostics for invalid array, reference
and pointer to member types.
1998-11-16 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (my_friendly_abort): Don't fatal twice in a row.
* typeck.c (c_expand_start_case): Use build_expr_type_conversion.
Simplify.
* parse.y (structsp): Fix cut-and-paste error.
* init.c (build_new): Complain about non-integral size.
* parse.y (unary_expr): Complain about defining types in sizeof.
* typeck.c (expr_sizeof): Complain about sizeof an overloaded fn.
* rtti.c (build_x_typeid): Complain about typeid without
including <typeinfo>.
(get_typeid): Likewise. Complain about typeid of incomplete type.
(get_tinfo_fn_dynamic): Likewise.
(get_typeid_1): Not static anymore.
* except.c (build_eh_type_type): Use get_typeid_1.
* rtti.c (build_dynamic_cast_1): Give errors for dynamic_cast to
ambiguous or private bases. Fix warning for reference cast.
1998-11-16 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (DECL_TEMPLATE_INSTANTIATED): New macro.
* decl.c (duplicate_decls): Remove special-case code to deal with
template friends, and just do the obvious thing.
* pt.c (register_specialization): Tweak for clarity, and also to
clear DECL_INITIAL for an instantiation before it is merged with a
specialization.
(check_explicit_specialization): Fix indentation.
(tsubst_friend_function): Handle both definitions in friend
declaration and outside friend declarations.
(tsubst_decl): Don't clear DECL_INITIAL for an instantiation.
(regenerate_decl_from_template): Tweak accordingly.
(instantiate_decl): Likewise.
1998-11-16 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (cplus_expand_expr_stmt): Promote warning about naked
member function reference to error.
* cvt.c (ocp_convert): Complain about converting an overloaded
function to void.
* init.c (build_offset_ref): Just return a lone static member
function.
* decl.c (cp_finish_decl): Only complain about real CONSTRUCTORs,
not internal ones.
* typeck.c (build_binary_op_nodefault): Improve error handling.
* decl.c (grokfndecl): Complain about making 'main' a template.
* typeck.c (string_conv_p): Don't convert from wchar_t[] to char*.
* call.c (build_method_call): Handle a BIT_NOT_EXPR around a
TYPE_DECL in a template.
1998-11-15 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (my_friendly_abort): Add URL in the other case, too.
* decl.c (struct cp_function): Add named_label_uses.
(push_cp_function_context): Save it.
(pop_cp_function_context): Restore it.
(define_label): Also complain about jumping into the scope of
non-POD objects that don't have constructors.
* tree.c (pod_type_p): New fn.
* pt.c (instantiate_class_template): Clear TYPE_BEING_DEFINED sooner.
* rtti.c (synthesize_tinfo_fn): Call import_export_decl here.
(get_tinfo_fn): Not here.
* repo.c (repo_get_id): Abort if we get called for an incomplete
type.
1998-11-13 Mark Mitchell <mark@markmitchell.com>
* except.c (expand_throw): Make sure first argument to
__cp_push_exception is of type `void*' to avoid spurious error
messages.
1998-11-11 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (try_one_overload): Take orig_targs again. Only check for
mismatches against them; we don't care what a previous call found.
(resolve_overloaded_unification): Adjust.
* search.c (lookup_field): Don't return anything for a non-type
field from a dependent type.
* decl.c (grokdeclarator): Resolve SCOPE_REFs of the current class
in an array declarator.
(start_decl): Push into the class before looking for the field.
1998-11-08 Mark Mitchell <mark@markmitchell.com>
* method.c (build_overload_value): Handle REFERENCE_TYPE.
1998-11-08 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl.c (grokdeclarator): Allow namespace-scoped members if they
are friends.
1998-11-08 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_decl): Don't mess with the global value of an
un-mangled DECL_ASSEMBLER_NAME.
1998-11-03 Christopher Faylor <cgf@cygnus.com>
* decl.c (init_decl_processing): Remove CYGWIN conditional
since CYGWIN is now able to deal with trapping signals.
Sat Nov 7 15:48:02 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cp-tree.h: Don't include gansidecl.h.
* exception.cc: Include gansidecl.h (since we don't include config.h)
* g++spec.c: Don't include gansidecl.h.
1998-11-06 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (lang_decl_flags): Add defined_in_class. Decrease
size of dummy.
(DECL_DEFINED_IN_CLASS_P): New macro.
(TEMPLATE_PARMS_FOR_INLINE): Document.
(check_static_variable_definition): New function.
* decl.c (cp_finish_decl): Set DECL_DEFINED_IN_CLASS_P, if
appropriate.
(check_static_variable_definition): Split out from ...
(grokdeclarator): Here.
* pt.c (check_default_tmpl_args): New function, split out from ...
(push_template_decl_real): Here.
(instantiate_template): Fix comment.
1998-11-04 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (CP_TYPE_CONST_P): Make {0,1}-valued.
(CP_TYPE_VOLATILE_P): Likewise.
(CP_TYPE_RESTRICT_P): Likewise.
1998-11-03 Mark Mitchell <mark@markmitchell.com>
* pt.c (tsubst): Use build_index_type, not build_index_2_type.
1998-11-02 Jason Merrill <jason@yorick.cygnus.com>
* class.c (instantiate_type): Be more helpful.
* decl2.c (import_export_decl): Call import_export_class.
* cp-tree.h (EMPTY_CONSTRUCTOR_P): Check !TREE_HAS_CONSTRUCTOR.
* decl2.c (build_expr_from_tree): Propagate TREE_HAS_CONSTRUCTOR.
* pt.c (tsubst_copy): Likewise.
1998-11-02 Mark Mitchell <mark@markmitchell.com>
* init.c (expand_vec_init): Fix off-by-one error.
1998-11-02 Alexandre Oliva <oliva@dcc.unicamp.br>
* parse.y (apparent_template_type): New type.
(named_complex_class_head_sans_basetype): Use it.
* Makefile.in (CONFLICTS): One new conflict.
* parse.c: Regenerated.
1998-11-01 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (COMPARE_STRICT): New macro.
(COMPARE_BASE): Likewise.
(COMPARE_RELAXED): Likewise.
(COMPARE_REDECLARATION): Likewise.
(same_type_p): Likewise.
(same_or_base_type_p): Likewise.
* call.c (standard_conversion): Use them, in place of comptypes
with numeric arguments.
(reference_binding): Likewise.
(convert_like): Likewise.
(build_over_call): Likewise.
(is_subseq): Likewise.
(is_properly_derived_from): Likewise.
(compare_ics): Likewise.
(joust): Likewise.
* class.c (delete_duplicate_fields_1): Likewise.
(resolves_to_fixed_type_p): Likewise.
(instantiate_type): Likewise. Remove #if 0'd code.
* decl.c (decls_match): Likewise. Use COMPARE_REDECLARATION here.
(pushdecl): Likewise.
(lookup_name_real): Likewise.
(grokdeclarator): Likewise. Check for illegal array declarations.
(grokparms): Likewise.
(grok_op_properties): Likewise.
* decl2.c (check_classfn): Likewise.
* friend.c (is_friend): Likewise.
(make_friend_class): Likewise.
* init.c (expand_aggr_init): Likewise.
(expand_vec_init): Likewise.
* pt.c (is_member_template_class): Remove declaration.
(is_specialization_of): Use COMPARE_* and new macros.
(comp_template_parms): Likewise.
(convert_nontype_argument): Likewise.
(coerce_template_template_parms): Likewise.
(template_args_equal): Likewise.
(lookup_template_class): Likewise.
(type_unification_real): Likewise.
(unify): Likewise.
(get_bindings_real): Likewise.
* search.c (covariant_return_p): Likewise.
(get_matching_virtual): Likewise.
* sig.c (match_method_types): Likewise.
* tree.c (vec_binfo_member): Likewise.
(cp_tree_equal): Likewise.
* typeck.c (common_type): Likewise.
(comp_array_types): Likewise. Get issues involving unknown array
bounds right.
(comptypes): Update comments. Use new flags.
(comp_target_types): Use new macros.
(compparms): Likewise.
(comp_target_parms): Likewise.
(string_conv_p): Likewise.
(build_component_ref): Likewise.
(build_indirect_ref): Likewise.
(build_conditional_expr): Likewise.
(build_static_cast): Likewise.
(build_reinterpret_cast): Likewise.
(build_const_cast): Likewise.
(build_modify_expr): Likewise.
(convert_for_assignment): Likewise.
(comp_ptr_ttypes_real): Likewise.
(ptr_reasonably_similar): Likewise.
(comp_ptr_ttypes_const): Likewise.
1998-10-31 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (build_dynamic_cast_1): Fix cut-and-paste error.
1998-10-30 Mark Mitchell <mark@markmitchell.com>
* decl2.c (delete_sanity): Pass integer_zero_node, not
integer_two_node, to build_vec_delete.
* init.c (build_array_eh_cleanup): Remove.
(expand_vec_init_try_block): New function.
(expand_vec_init_catch_clause): Likewise.
(build_vec_delete_1): Don't deal with case that auto_delete_vec
might be integer_two_node anymore.
(expand_vec_init): Rework for initialization-correctness and
exception-correctness.
* typeck2.c (process_init_constructor): Make mutual exclusivity
of cases more obvious.
1998-10-29 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): OK, only warn if not lexing.
Simplify suggested fix.
* cp-tree.h (IDENTIFIER_MARKED): New macro.
* search.c (lookup_conversions): Use breadth_first_search.
(add_conversions): Avoid adding two conversions to the same type.
(breadth_first_search): Work with base binfos, rather
than binfos and base indices.
(get_virtual_destructor): Adjust.
(tree_has_any_destructor_p): Adjust.
(get_matching_virtual): Adjust.
* pt.c (push_template_decl_real): Generalize check for incorrect
number of template parms.
(is_member_template_class): #if 0.
1998-10-29 Richard Henderson <rth@cygnus.com>
* Makefile.in (cc1plus): Put CXX_OBJS, and thence @extra_cxx_objs@,
last.
1998-10-28 Zack Weinberg <zack@rabi.phys.columbia.edu>
* lex.c: Call check_newline from lang_init always. After
calling cpp_start_read, set yy_cur and yy_lim to read from the
cpplib token buffer.
1998-10-28 Jason Merrill <jason@yorick.cygnus.com>
* class.c (instantiate_type): Don't consider templates for a normal
match.
* class.c (finish_struct_1): Don't complain about non-copy
assignment ops in union members.
* class.c (build_vtable): Don't pass at_eof to import_export_vtable.
(prepare_fresh_vtable): Likewise.
(finish_struct_1): Don't call import_export_class.
* decl2.c (finish_vtable_vardecl): Do import/export stuff.
(finish_prevtable_vardecl): Lose.
(finish_file): Don't call it.
* pt.c (instantiate_class_template): Likewise.
* cp-tree.h: Remove it.
* init.c (build_delete): Reset TYPE_HAS_DESTRUCTOR here.
* decl.c (finish_function): Not here.
(start_function): Do set DECL_INITIAL.
* pt.c (push_template_decl_real): Complain about default template
args for enclosing classes.
* call.c (add_function_candidate): Treat conversion functions
as coming from the argument's class.
* cp-tree.h (DECL_CONV_FN_P): New fn.
(DECL_DESTRUCTOR_P): Also check DECL_LANGUAGE.
* class.c (add_method): Use DECL_CONV_FN_P.
* decl2.c (check_classfn): Likewise.
* error.c (dump_function_name): Likewise.
(dump_function_decl): Likewise.
* pt.c (fn_type_unification): Likewise.
* search.c (add_conversions): Likewise.
1998-10-27 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (do_identifier): Also generate LOOKUP_EXPR for RESULT_DECL.
* method.c (hack_identifier): Also check for using RESULT_DECL
from outer context.
1998-10-27 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Use type_quals, rather than constp,
consistently.
1998-10-27 Jason Merrill <jason@yorick.cygnus.com>
* call.c (standard_conversion): instantiate_type here.
(reference_binding): And here.
(implicit_conversion): Not here.
(build_op_delete_call): No need to cons up an OVERLOAD.
* cvt.c (cp_convert_to_pointer): instantiate_type here.
(convert_to_reference): And here.
* decl.c (grok_reference_init): Not here.
(grokparms): Or here.
* typeck2.c (digest_init): Or here.
* typeck.c (decay_conversion): Take the address of overloaded
functions, too.
(require_instantiated_type): Lose.
(convert_arguments): Don't handle unknown types here.
(build_c_cast): Likewise.
(build_binary_op): Gut.
(build_conditional_expr): Don't require_instantiated_type.
(build_modify_expr): Likewise.
(build_static_cast): Don't instantiate_type.
(build_reinterpret_cast): Likewise.
(build_const_cast): Likewise.
(convert_for_initialization): Likewise.
(build_ptrmemfunc): Use type_unknown_p.
(convert_for_assignment): Also do default_conversion on overloaded
functions. Hand them off to ocp_convert.
1998-10-26 Mark Mitchell <mark@markmitchell.com>
* error.c (dump_decl): Deal with TEMPLATE_DECLs that are
VAR_DECLs. Handle vtables whose DECL_CONTEXT is not a type.
* class.c (finish_struct_1): Use build_cplus_array_type to build
array types.
* decl.c (init_decl_processing): Likewise.
* except.c (expand_end_eh_spec): Likewise.
* search.c (expand_upcast_fixups): Simplify very slightly.
1998-10-26 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Complain about a variable using
constructor syntax coming back null from start_decl.
* friend.c (make_friend_class): Complain about trying to make
a non-class type a friend.
* decl.c (grokfndecl): Set DECL_INITIAL for a defn here.
(start_function): Not here.
1998-10-26 Brendan Kehoe <brendan@cygnus.com>
* decl.c (grokdeclarator): Disallow `explicit' in a friend declaration.
1998-10-26 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (process_init_constructor): Only skip anonymous fields
if they are bitfields.
* cp-tree.def (TYPEOF_TYPE): New code.
* error.c (dump_type_real): Handle it.
* pt.c (tsubst): Likewise.
* tree.c (search_tree): Likewise.
* semantics.c (finish_typeof): New fn.
* parse.y (typespec): Use it.
* cp-tree.h: Declare it.
1998-10-26 Manfred Hollstein <manfred@s-direktnet.de>
* cp-tree.h (FORMAT_VBASE_NAME): Make definition unconditional.
1998-10-26 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (convert_arguments): Don't handle pmf references
specially.
* init.c (build_member_call): Don't try to convert to the base type
if it's ambiguous or pedantic.
* typeck2.c (check_for_new_type): Only depend on pedantic for
C-style casts.
1998-10-25 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Set DECL_NONCONVERTING_P for all
non-converting constructors.
1998-10-24 Martin von Löwis <loewis@informatik.hu-berlin.de>
* gxxint.texi: Correct documentation for n, N, Q, and B.
1998-10-23 Martin von Löwis <loewis@informatik.hu-berlin.de>
* parse.y (condition): Convert VAR_DECL from reference to indirect
reference.
1998-10-23 Andrew MacLeod <amacleod@cygnus.com>
* exception.cc (__cp_pop_exception): Free the original exception
value, not the potentially coerced one.
1998-10-23 Mark Mitchell <mark@markmitchell.com>
* Makefile.in (hash.h): Run gperf when necessary.
* cp-tree.h (CP_TYPE_READONLY): Remove.
(CP_TYPE_VOLATILE): Likewise.
(CP_TYPE_QUALS): New macro.
(CP_TYPE_CONST_P): Likewise.
(CP_TYPE_VOLATILE_P): Likewise.
(CP_TYPE_RESTRICT_P): Likewise.
(CP_TYPE_CONST_NON_VOLATILE_P): Likewise.
(cp_build_type_variant): Rename to ...
(cp_build_qualified_type): New function.
(c_apply_type_quals_to_decl): Declare.
(SIGNATURE_POINTER_NAME_FORMAT): Modify to allow `restrict'.
(SIGNATURE_REFERENCE_NAME_FORMAT): Likewise.
(cp_type_qual_from_rid): New function.
(compparms): Remove unused parameter. All callers changed.
(cp_type_quals): New function.
(at_least_as_qualified_p): Likewise.
(more_qualified_p): Likewise.
* call.c (standard_conversion): Replace calls to
cp_build_type_variant with cp_build_qualified_type. Use
CP_TYPE_QUALS to get qualifiers and at_least_as_qualified_p to
compare them. Use CP_TYPE_* macros to check qualifiers.
(reference_binding): Likewise.
(implicit_conversion): Likewise.
(add_builtin_candidates): Likewise.
(build_over_call): Likewise.
* class.c (overrides): Compare all qualifiers, not just `const',
on method declarations.
* cvt.c (convert_to_reference): More CP_TYPE_QUALS conversion, etc.
(convert_pointer_to_real): Likewise.
(type_promotes_to): Likewise.
* decl.c (check_for_uninitialized_const_var): New function.
(init_decl_processing): More CP_TYPE_QUALS conversion, etc.
(cp_finish_decl): Use check_for_uninitialized_const_var.
(grokdeclarator): More CP_TYPE_QUALS conversion, etc. Update to
handle `restrict'.
(grok_ctor_properties): Likewise.
(grok_op_properties): Likewise.
(start_function): Likewise.
(rever_static_member_fn): Likewise.
* decl2.c (grok_method_quals): Likewise.
(grokfield): Likewise.
* error.c (dump_readonly_or_volatile): Rename to ...
(dump_qualifiers): New function. Handle `restrict'.
(dump_type_real): Use it.
(dump_aggr_type): Likewise.
(dump_type_prefix): Likewise.
(dump_type_suffix): Likewise.
(dump_function_decl): Likewise.
(cv_as_string): Likewise.
* gxx.gperf: Add __restrict and __restrict__.
* gxxint.texi: Document `u' as used for `__restrict', and a few
other previously undocumented codes.
* hash.h: Regenerated.
* init.c (expand_aggr_init): More CP_TYPE_QUALS conversion, etc.
(build_member_call): Likewise.
(build_new_1): Likewise.
* lex.c (init_parse): Add entry for RID_RESTRICT.
(cons_up_default_function): More CP_TYPE_QUALS conversion, etc.
(cp_type_qual_from_rid): Define.
* lex.h (enum rid): Add RID_RESTRICT.
* method.c (process_modifiers): Deal with `restrict'.
* parse.y (primary): More CP_TYPE_QUALS conversion, etc.
* parse.c: Regenerated.
* pt.c (convert_nontype_argument): More CP_TYPE_QUALS conversion, etc.
(tsubst_aggr_type): Likewise.
(tsubst): Likewise.
(check_cv_quals_for_unify): Likewise.
(unify): Likewise.
* rtti.c (init_rtti_processing): Likewise.
(build_headof): Likewise.
(get_tinfo_var): Likewise.
(buidl_dynamic_cast_1): Likewise. Fix `volatile' handling.
(expand_class_desc): Likewise.
(expand_attr_desc): Likewise.
(synthesize_tinfo_fn): Likewise.
* search.c (covariant_return_p): Likewise. Fix `volatile' handling.
(get_matching_virtual): Likewise.
(expand_upcast_fixups): Likewise.
* sig.c (build_signature_pointer_or_reference_name): Take
type_quals, not constp and volatilep.
(build_signature_pointer_or_reference_type): Likewise.
(match_method_types): More CP_TYPE_QUALS conversion, etc.
(build_signature_pointer_constructor): Likewise.
(build_signature_method_call): Likewise.
* tree.c (build_cplus_array_type): Likewise.
(cp_build_type_variant): Rename to ...
(cp_build_qualified_type): New function. Deal with `__restrict'.
(canonical_type_variant): More CP_TYPE_QUALS conversion, etc.
(build_exception_variant): Likewise.
(mapcar): Likewise.
* typeck.c (qualif_type): Likewise.
(common_type): Likewise.
(comptypes): Likewise.
(comp_cv_target_types): Likewise.
(at_least_as_qualified_p): Define.
(more_qualified_p): Likewise.
(comp_cv_qualification): More CP_TYPE_QUALS conversion, etc.
(compparms): Likewise.
(inline_conversion): Likewise.
(string_conv_p): Likewise.
(build_component_ref): Likewise.
(build_indirect_ref): Likewise.
(build_array_ref): Likewise.
(build_unary_op): Likewise.
(build_conditional_expr): Likewise.
(build_static_cast): Likewise.
(build_c_cast): Likewise.
(build_modify_expr): Likewise.
(convert_For_assignment): Likewise.
(comp_ptr_ttypes_real): Likewise.
(cp_type_quals): New function.
1998-10-23 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (CP_TYPE_READONLY): New macro to handle arrays.
(CP_TYPE_VOLATILE): Likewise.
* decl.c (grokdeclarator): Use them.
* tree.c (canonical_type_variant): Likewise.
1998-10-22 Martin von Löwis <loewis@informatik.hu-berlin.de>
* parse.y (named_class_head): Push into class while parsing the
base class list.
* decl2.c (push_scope, pop_scope): New functions.
* cp-tree.h: Declare them.
* init.c (build_new_1): Delay cleanup until end of full expression.
1998-10-21 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_component_ref): Use of a type here is an error.
1998-10-19 Jason Merrill <jason@yorick.cygnus.com>
Revamp references to member functions.
* method.c (hack_identifier): Call build_component_ref for a
reference to a member function.
* typeck.c (build_component_ref): Only return a single function
if it's static. Otherwise, return a COMPONENT_REF.
(build_x_function_call): Handle a COMPONENT_REF.
(build_unary_op): Handle all unknown-type things.
* decl2.c (arg_assoc): Handle COMPONENT_REF.
* class.c (instantiate_type): Complain if the function we get is a
nonstatic member function. Remove code for finding "compatible"
functions.
* pt.c (tsubst_copy): Handle NOP_EXPR.
* tree.c (build_dummy_object): New fn.
(maybe_dummy_object): New fn.
(is_dummy_object): New fn.
* cp-tree.h: Declare them.
* cvt.c (cp_convert_to_pointer): Use maybe_dummy_object.
* error.c (dump_expr, case OFFSET_REF): Use is_dummy_object.
* init.c (build_member_call): Use maybe_dummy_object and
is_dummy_object.
(build_offset_ref): Use maybe_dummy_object.
(resolve_offset_ref): Use is_dummy_object.
* typeck.c (build_x_function_call): Call build_dummy_object.
(unary_complex_lvalue): Call is_dummy_object.
* typeck.c (build_component_addr): Make sure field is a field.
* call.c (build_new_op): Delete obsolete code.
* pt.c (tsubst, TEMPLATE*PARM*): Abort if we don't have any args.
1998-10-18 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl2.c (validate_nonmember_using_decl): Fix using-directives of
std if std is ignored.
1998-10-18 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokvardecl): Fix thinko.
* decl.c (grokdeclarator): Embedded attrs bind to the right,
not the left.
* parse.y (fn.def2): Fix 'attrs' format.
1998-10-18 Alastair J. Houghton <ajh8@doc.ic.ac.uk>
* Makefile.in (CONFLICTS): Update.
* parse.y (expr_or_declarator_intern): New rule.
(expr_or_declarator, direct_notype_declarator, primary,
functional_cast): Use it.
(notype_declarator_intern): New rule.
(notype_declarator, complex_notype_declarator): Use it.
1998-10-17 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): Set DECL_CONTEXT to namespace if appropriate.
(grokvardecl): Likewise.
Sat Oct 17 23:27:20 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* class.c (make_method_vec): Cast 1st argument of `bzero' to (PTR).
(add_method): Likewise for arguments 1 & 2 of `bcopy'.
* decl.c (signal_catch): Mark with ATTRIBUTE_NORETURN.
* pt.c (process_partial_specialization): Cast 1st argument of
`bzero' to (PTR).
* tree.c (build_base_fields): Cast `base_align' to (int) when
comparing against one.
1998-10-16 Mark Mitchell <mark@markmitchell.com>
* decl.c (lookup_name_real): Handle template parameters for member
templates where said parameters have the same name as the
surrounding class.
* decl.c (expand_static_init): Build cleanups before entering the
anonymous function used to do them to avoid access-checking
confusion.
* decl.c (grokfndecl): Add back call to cplus_decl_attributes
accidentally removed by previous change, and make DECL_RTL here.
* class.c (add_method): Don't make DECL_RTL here.
* pt.c (for_each_template_parm): Don't examine uninstantiated
default arguments.
1998-10-16 Dave Brolley <brolley@cygnus.com>
* lex.c (real_yylex): Fix unaligned access of wchar_t.
1998-10-16 Mark Mitchell <mark@markmitchell.com>
* class.c (add_method): Fix documentation to reflect previous
changes. Check for duplicate method declarations here.
* decl.c (decls_match): Handle FUNCTION_DECL vs TEMPLATE_DECL
correctly; such things never match.
(grokfndecl): Don't look for duplicate methods here.
* decl2.c (check_classfn): Don't assume names are mangled.
Don't add bogus member function declarations to a class before the
class type is complete.
(grokfield): Reformat error message.
* method.c (set_mangled_name_for_decl): Don't mangle names while
processing_template_decl.
1998-10-16 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_indirect_ref): Complain about a pointer to data
member, too.
* typeck2.c (build_m_component_ref): Don't indirect a pointer to
data member.
* init.c (resolve_offset_ref): Don't undo the above.
* cp-tree.h (DECL_C_BIT_FIELD, SET_DECL_C_BIT_FIELD): New macros.
(struct lang_decl_flags): Add `bitfield'.
* class.c (finish_struct_1): Use DECL_C_BIT_FIELD instead of
DECL_BIT_FIELD.
* decl2.c (grokbitfield, grok_alignof): Likewise.
* init.c (build_offset_ref): Likewise.
* typeck.c (build_component_addr, expr_sizeof): Likewise.
* cvt.c (build_up_reference): Don't crash if taking the address
returns error_mark_node.
* decl.c (grokfndecl): Also check ctype when checking for ::main().
1998-10-15 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): ::main and __builtin_* get C linkage.
Do mangling here.
(grokdeclarator): Instead of here.
* friend.c (do_friend): Lose special handling of ::main and
__builtin_*.
* cp-tree.h (DECL_MAIN_P): Check for C linkage.
* spew.c (yylex): Clear looking_for_typename if we got
'enum { ... };'.
1998-10-15 Mark Mitchell <mark@markmitchell.com>
* class.c (maybe_warn_about_overly_private_class): Improve error
messages for class with only private constructors.
* cp-tree.def (TYPENAME_TYPE): Add to documentation.
* cp-tree.h (TYPENAME_TYPE_FULLNAME): Document.
(build_typename_type): New function.
* decl.c (build_typename_type): Broken out from ...
(make_typename_type): Use it.
* search.c (lookup_field): Likewise.
1998-10-14 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* pt.c (convert_nontype_argument): Check against type_referred_to.
* decl.c (grokvardecl): Check for declarator name before building
DECL_ASSEMBLER_NAME.
1998-10-14 Mark Mitchell <mark@markmitchell.com>
* pt.c (lookup_template_class): Add comment.
(instantiate_class_template): Don't mark the _TYPE node for
member class templates as an instantiation.
1998-10-14 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): Fix my thinko.
1998-10-13 Jason Merrill <jason@yorick.cygnus.com>
* tinfo2.cc (fast_compare): Remove.
(before): Just use strcmp.
* tinfo.cc (operator==): Just use strcmp.
1998-10-13 Klaus-Georg Adams <Klaus-Georg.Adams@chemie.uni-karlsruhe.de>
* decl.c (grokfndecl): Don't check for linkage in `extern "C"'
declarations.
1998-10-13 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (specializations_of_same_template_p): Remove.
* search.c (get_template_base): Don't use it.
(get_template_base_recursive): Likewise.
* pt.c (specializations_of_same_template_p): Remove.
(unify): Don't use it.
(lookup_template_class): Find the correct parent when setting
CLASSTYPE_TI_TEMPLATE.
1998-10-12 Jason Merrill <jason@yorick.cygnus.com>
* tinfo.cc (operator==): Always compare names.
1998-10-12 Herman ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
* decl.c (start_function): Fix cut-and-paste error.
1998-10-12 Jason Merrill <jason@yorick.cygnus.com>
* inc/typeinfo: Add #pragma interface.
(operator!=): Just call operator==.
* tinfo.cc: Add #pragma implementation.
(operator==): Move from inc/typeinfo and tinfo2.cc.
Check __COMMON_UNRELIABLE instead of _WIN32.
* typeck2.c (my_friendly_abort): Add URL.
1998-10-12 Alastair J. Houghton <ajh8@doc.ic.ac.uk>
* decl.c (start_method): Added extra parameter for attributes.
* cp-tree.h (start_method): Update prototype.
* parse.y (fn.def2): Update start_method parameter list.
1998-10-11 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (specializations_of_same_template_p): Declare.
* pt.c (specializations_of_same_template_p): New function.
(unify): Use it.
* search.c (get_template_base): Use it.
(get_template_base_recursive): Likewise.
1998-10-10 Manfred Hollstein <manfred@s-direktnet.de>
* decl2.c (start_objects): Add new variable `joiner' and
initialize it properly.
1998-10-09 Mark Mitchell <mark@markmitchell.com>
* search.c (expand_upcast_fixups): Tweak to match 1998-10-07
change to vtable types.
* cvt.c (ocp_convert): Avoid infinite recursion caused by
1998-10-03 change.
1998-10-08 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (resolve_overloaded_unification): New fn.
(try_one_overload): Likewise.
(unify): Don't fail on unknown type.
(type_unification_real): Likewise. Use resolve_overloaded_unification
to handle an overloaded argument.
(template_args_equal): Split out...
(comp_template_args): From here.
(determine_specialization): Also allow a template with more
parms than were explicitly specified.
* cp-tree.h: Add template_args_equal.
* call.c (resolve_args): Remove TEMPLATE_ID_EXPR code.
Thu Oct 8 15:58:30 1998 Anthony Green <green@cygnus.com>
* semantics.c (finish_asm_stmt): Revert my 1998-09-28
change.
Thu Oct 8 06:00:19 1998 Jeffrey A Law (law@cygnus.com)
* typeck.c (unsigned_type): Only return TItype nodes when
HOST_BITS_PER_WIDE_INT is >= 64 bits.
(signed_type): Likewise.
* decl.c (intTI_type_node, unsigned_intTI_type_node): Only declare
when HOST_BITS_PER_WIDE_INT is >= 64 bits.
(init_decl_processing): Only create TItype nodes when
HOST_BITS_PER_WIDE_INT is >= 64 bits.
* cp-tree.h (intTI_type_node, unsigned_intTI_type_node): Only declare
when HOST_BITS_PER_WIDE_INT is >= 64 bits.
Wed Oct 7 12:32:44 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (hash.h): Add -L KR-C -F ', 0, 0' flags to gperf.
(gxx.gperf): Update comments describing invocation flags.
(hash.h): Regenerate using gperf 2.7.1 (19981006 egcs).
1998-10-07 Mark Mitchell <mark@markmitchell.com>
* class.c (finish_struct_1): Add commentary on previous change.
* cp-tree.h (vtbl_ptr_type_node): New variable.
* class.c (build_vtbl_ref): Don't indirect through the vptr; it's
already of the right type.
(finish_struct_1): Make the vptr be of type vtbl_ptr_type_node.
Simplify code to grow vtable.
* decl.c (vtbl_ptr_type_node): Define.
(init_decl_processing): Initialize it.
1998-10-06 Mark Mitchell <mark@markmitchell.com>
* cp-tree.def (PTRMEM_CST): New tree node.
* cp-tree.h (ptrmem_cst): New type.
(lang_type): Remove local_typedecls.
(dummy): Increase to 12 bits from 11.
(CLASSTYPE_LOCAL_TYPEDECLS): Remove.
(PTRMEM_CST_CLASS): New macro.
(PTRMEM_CST_MEMBER): Likewise.
(current_access_specifier): New variable.
(current_class_type): Remove duplicate declaration.
(finish_struct): Change prototype.
(unreverse_member_declarations): New function.
(pushdecl_class_level): Change prototype.
(grok_enum_decls): Remove.
(fixup_anonymous_union): New function.
(grok_x_components): Change prototype.
(tsubst_chain): Remove.
(finish_member_template_decl): Likewise.
(check_explicit_specialization): Fix indentation.
(finish_class_definition): Change prototype.
(finish_member_class_template): Likewise.
(finish_member_declaration): New function.
(check_multiple_declarators): Likewise.
* class.c (class_stack_node_t): New type.
(current_class_base): Remove.
(current_class_stack): Change type.
(current_access_specifier): New variable.
(grow_method): Remove.
(check_member_decl_is_same_in_complete_scope): Break out from
finish_struct.
(make_method_vec): New function.
(free_method_vec): Likewise.
(add_implicitly_declared_members): Break out from finish_struct_1.
(free_method_vecs): New variable.
(add_method): Rework for direct use from parser.
(handle_using_decl): Watch for NULL_TREE while iterating through
CLASSTYPE_METHOD_VEC.
(finish_struct_methods): Don't build CLASSTYPE_METHOD_VEC here;
just do some error-checking.
(warn_hidden): Change iteration through CLASSTYPE_METHOD_VEC.
(finish_struct_1): Simplify. Use add_implicitly_declared_members.
(finish_struct): Change prototype. Simplify; fields and methods
are already set up at this point.
(init_class_processing): Set up current_class_stack.
(pushclass): Save current_access_specifier.
(popclass): Restore it.
(currently_open_class): Simplify.
(build_self_reference): Remove use of CLASSTYPE_LOCAL_TYPEDECLS.
* decl.c (saved_scope): Add access_specifier.
(maybe_push_to_top_level): Save it.
(pop_from_top_level): Restore it.
(maybe_process_template_type_declaration): Use
finish_member_declaration.
(pushtag): Likewise.
(pushdecl_class_level): Don't return a value.
(fixup_anonymous_union): Break out from grok_x_components.
(shadow_tag): Use it.
(xref_tag): Complain about using an elaborated type specifier to
reference a template type parameter or typedef name.
(xref_basetypes): Don't set CLASSTYPE_LOCAL_TYPEDECLS.
(current_local_enum): Remove.
(build_enumerator): Call finish_member_declaration.
(grok_enum_decls): Remove.
* decl2.c (grok_x_components): Simplify.
(check_classfn): Change iteration through CLASSTYPE_METHOD_VEC.
(grokfield): Don't set CLASSTYPE_LOCAL_TYPEDECLS.
(merge_functions): Add to comment.
(arg_assoc_type): Prototype.
(arg_assoc): Pass as many arguments as there are parameters.
* error.c (dump_expr): Handle PTRMEM_CST. Improve handling of
OFFSET_REF.
* expr.c (cpls_expand_expr): Remove dead code. Handle
PTRMEM_CST.
* friend.c (do_friend): Lookup friends when in nested classes.
Change comments.
* init.c (build_offset_ref): Do lookup even for classes that are
only partially defined.
(decl_constant_value): Remove dead code.
* method.c (build_overload_value): Remove hack where by TYPE was
not a TYPE. Handle PTRMEM_CST.
(build_template_parm_names): Don't pass a PARM_DECL where a TYPE
should go.
* parse.y (components, notype_components, component_decl,
component_decl_1, component_declarator, component_declarator0):
Now all are itype rather than ttype. Rework to add members to
classes on the fly.
(typesqpecqual_reserved): Use check_multiple_declarators.
(structsp): Update class to finish_class_definition.
(do_xref_defn): Unsplit into named_class_head.
(access_specifier): Set current_access_specifier.
* pt.c (set_current_access_from_decl): New function.
(finish_member_template_decl): Don't take the parameters.
(comp_template_args): Make more robust.
(lookup_template_class): Don't use current_local_enum.
(for_each_template_parm): Handle PTRMEM_CST.
(instantiate_class_template): Use set_current_access_from_decl,
finish_member_declaration and unreverse_member_declarations. Set
lineno/input_filename before generating implicit member functions.
(type_unification_real): Don't assume back-unification happens
only for the last argument.
(regenerate_decl_from_template): Call pushclass a bit earlier.
(tsubst_chain): Remove.
(tsubst_enum): Use set_current_access_from_decl.
(set_mangled_name_for_template_decl): Fix indentation.
* search.c (lookup_fnfields_1): Change iteration through
CLASSTYPE_METHOD_VEC.
(dfs_pushdecls): Likewise.
(dfs_compress_decls): Likewise.
(add_conversions): Likewise.
* semantics.c (finish_class_definition): Don't take components.
Change call to finish_struct.
(finish_member_declaration): New function.
(finish_member_class_template): Don't take template parameters.
Change call to grok_x_components. Call finish_member_template_decl.
(check_multiple_declarators): New function.
* sig.c (append_signature_fields): Work from the TYPE_METHODS, not
a passed in fieldlist.
* tree.c (search_tree): Handle PTRMEM_CST.
(mapcar): Likewise.
* typeck.c (unary_complex_lvalue): Build PTRMEM_CSTs, not
INTEGER_CSTs, for pointer-to-data members.
* call.c (resolve_args): Resolve template specializations, if
possible.
Tue Oct 6 07:57:26 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (spew.o): Depend on toplev.h.
* call.c (compare_ics): Initialize variables `deref_from_type2',
`deref_to_type1' and `deref_to_type2'.
* except.c (get_eh_type): Hide prototype and definition.
(process_start_catch_block_old): Remove unused static prototype.
* pt.c (tsubst_decl): Initialize variable `argvec'.
* spew.c: Include toplev.h.
1998-10-05 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Do save and restore file position.
1998-10-05 Martin von Löwis <loewis@informatik.hu-berlin.de>
* method.c (build_decl_overload_real): Clear
numeric_output_need_bar after __.
1998-10-05 Nathan Sidwell <nathan@acm.org>
* call.c (build_new_method_call): Issue 'incomplete type' error,
if class is not defined.
1998-10-05 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* call.c (build_object_call): Move declaration of variable
`fn' into the scope where it is used. Don't access variable
`fn' when it is uninitialized, instead use `fns'.
1998-10-04 Theodore Papadopoulo <Theodore.Papadopoulo@sophia.inria.fr>
* errfn.c (cp_thing): Print buf as a string not as a printf format
to avoid problems with the operator%. Consequently, `%%' sequences
in format are copied as `%' in buf.
1998-10-04 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (pop_tinst_level): Call extract_interface_info.
(instantiate_decl): Don't save and restore file position.
* decl.c (cp_finish_decl): Make statics in extern inlines and
templates common, if possible and the target doesn't support weak
symbols.
* decl.c (grokdeclarator): Remove redundant calls to
build_type_variant and some duplicated code.
* sig.c (build_signature_reference_type): Only take the type parm.
(build_signature_pointer_type): Likewise.
* tree.c (build_cplus_method_type): Adjust.
* cp-tree.h: Update.
1998-10-04 Mark Mitchell <mark@markmitchell.com>
* call.c (build_over_call): Make pedwarns about dropped qualifiers
into full-fledged errors.
* cvt.c (convert_to_reference): Likewise.
* typeck.c (convert_for_assignment): Likewise.
* search.c (expand_upcast_vtables): In addition to unsetting
TREE_READONLY, remove top-level const type qualifier.
1998-10-03 Mark Mitchell <mark@markmitchell.com>
* class.c (current_class_ptr, current_class_ref): Clarify
documentation.
* cvt.c (ocp_convert): Don't expect fold to remove all trivial
NOP type conversions.
* decl.c (decls_match): Use comptypes directly; ignore
qualifiers on the DECL.
(duplicate_decls): Remove qualifier checks on DECL.
(grokdeclarator): Make the type built up include top-level
qualifiers.
* decl2.c (do_dtors): Fix spelling error.
* error.c (dump_simple_decl): Don't look at qualifiers on the decl
when printing type information.
* init.c (build_new_1): Add documentation. Deal with the fact
that type of allocated memory now contains qualifiers.
* lex.c (is_global): Improve error-recovery.
* sig.c (build_member_function_pointer): Don't cast away const
on fields of sigtable_entry_type.
* tree.c (lvalue_type): Don't look at top-level qualifiers on
expressions.
* typeck.c (decay_conversion): Likewise.
(build_component_ref): Make sure the type of the COMPONENT_REF
contains top-level qualifiers, as appropriate. Improve
error-handling.
(build_indirect_ref): Simplify. Don't strip top-level qualifiers.
(build_array_ref): Likewise.
(build_unary_op): Improve error-recovery.
(unary_complex_lvalue): Make taking the address a bound member
function an error, not a sorry.
(build_conditional_expr): Look at the type qualifiers, not the
qualifiers on the expression itself.
1998-10-03 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (merge_functions): Remove duplicates.
* decl2.c: Add -f{no-,}implicit-inline-templates.
(import_export_decl): Check it.
* decl.c (lookup_name_real): Template parms also take precedence
over implicit typename. Only warn if yylex.
* typeck.c (build_conditional_expr): Only fold if ifexp is an
INTEGER_CST.
* decl2.c (finish_vtable_vardecl): Check DECL_INTERFACE_KNOWN
instead of linkage.
1998-10-01 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (FORMAT_VBASE_NAME): New macro.
* class.c (build_vbase_pointer): Use it.
* rtti.c (expand_class_desc): Likewise.
* tree.c (build_vbase_pointer_fields): Likewise.
Thu Oct 1 10:43:45 1998 Nick Clifton <nickc@cygnus.com>
* decl.c (start_decl): Add invocation of
SET_DEFAULT_DECL_ATTRIBUTES, if defined.
(start_function): Add invocation of
SET_DEFAULT_DECL_ATTRIBUTES, if defined.
* lex.c: Replace occurrences of HANDLE_SYSV_PRAGMA with
HANDLE_GENERIC_PRAGMAS.
1998-09-28 Anthony Green <green@cygnus.com>
* semantics.c (finish_asm_stmt): Always permit volatile asms.
1998-09-28 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Tighten checks for invalid
destructors. Improve error-messages and error-recovery.
* decl2.c (check_classfn): Don't assume that mangled destructor
names contain type information.
1998-09-25 Jason Merrill <jason@yorick.cygnus.com>
* search.c (get_base_distance): Remove assert.
* decl2.c (build_anon_union_vars): Don't process a field with no
name.
(finish_anon_union): Also complain about local anon unions with no
members.
1998-09-25 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl.c (lookup_namespace_name): If the name is a namespace,
return it immediately.
Fri Sep 25 11:45:38 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cp-tree.h (define_case_label): Remove unused parameter.
(check_java_method): Likewise.
(grokclassfn): Likewise.
(expand_aggr_init): Likewise.
(build_x_delete): Likewise.
(maybe_end_member_template_processing): Likewise.
(unshare_base_binfos): Add prototype.
(string_conv_p): Likewise.
(my_friendly_abort): Mark with ATTRIBUTE_NORETURN.
* cvt.c (build_up_reference): Remove unused parameter
`checkconst', all callers changed.
(build_type_conversion): Mark parameter `code' with
ATTRIBUTE_UNUSED.
(build_expr_type_conversion): Initialize variable `conv'.
* decl.c (push_namespace): Initialize variable `d'.
(define_case_label): Remove unused parameter `decl', all callers
changed.
* decl2.c (lang_decode_option): If !USE_CPPLIB, mark parameter
`argc' with ATTRIBUTE_UNUSED.
(grokclassfn): Remove unused parameter `cname', all callers
changed.
(check_java_method): Likewise for parameter `ctype'.
(copy_assignment_arg_p): Mark parameter `virtualp' with
ATTRIBUTE_UNUSED.
(finish_prevtable_vardecl): Likewise for parameter `prev'.
* expr.c (extract_init): Likewise for parameters `decl' and `init'.
* init.c (expand_aggr_init_1): Remove unused parameter
`alias_this', all callers changed.
(expand_aggr_init): Likewise.
(expand_default_init): Likewise.
(build_new_1): Initialize variable `susp'.
(build_x_delete): Remove unused parameter `type', all callers
changed.
* lex.c (set_typedecl_interface_info): Mark parameter `prev' with
ATTRIBUTE_UNUSED.
(readescape): Use (unsigned) value in shift.
(real_yylex): Likewise. Likewise. Also cast `sizeof' to int when
comparing to a signed quantity.
* pt.c (maybe_end_member_template_processing): Remove unused
parameter `decl', all callers changed.
(check_explicit_specialization): Add braces around empty body in
an else-statement.
(current_template_args): Initialize variable `args'.
(lookup_template_class): Likewise for variable `prev_local_enum'.
(tsubst_decl): Likewise for variable `r'.
(set_mangled_name_for_template_decl): Initialize variable
`context'.
* spew.c (scan_tokens): Change type of parameter `n' to unsigned.
Likewise for variable `i'.
(yylex): Initialize variable `trrr'.
* typeck.c (compparms): Mark variable `strict' with
ATTRIBUTE_UNUSED.
* xref.c (simplify_type): Cast argument of ctype function to
`unsigned char'.
1998-09-24 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (language_lvalue_valid): Remove.
* decl.c (grokdeclarator): Don't disallow references to functions.
* tree.c (lvalue_p_1): New function, combining duplicated
code from ...
(lvalue_p): Use it.
(real_lvalue_p): Likewise.
* typeck.c (language_lvalue_valid): Remove.
(build_modify_expr): Treat FUNCTION_TYPEs as readonly, even though
they don't have TREE_READONLY set.
* typeck2.c (readonly_error): Add case for FUNCTION_DECLs.
1998-09-24 Benjamin Kosnik <bkoz@loony.cygnus.com>
* spew.c (yylex): Give diagnostic.
* hash.h (is_reserved_word): Add export.
* gxx.gperf: Likewise.
* lex.h (rid): Add RID_EXPORT.
* lex.c (init_parse): Likewise.
Tue Sep 22 21:01:19 1998 Gerald Pfeifer <pfeifer@dbai.tuwien.ac.at>
* friend.c (do_friend): Make warning a full sentence.
1998-09-22 Mark Mitchell <mark@markmitchell.com>
* parse.y (component_decl_list): Improve error-recovery.
1998-09-22 Benjamin Kosnik <bkoz@loony.cygnus.com>
* decl.c (make_typename_type): Move error to point where name
variable can be used by dump_type.
1998-09-22 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokfndecl): Improve error-recovery.
* decl2.c (grokfield): Likewise.
* pt.c (finish_member_template_decl): Likewise.
1998-09-20 Martin von Löwis <loewis@informatik.hu-berlin.de>
* method.c (hack_identifier): Finding multiple members is always
an error.
1998-09-21 Per Bothner <bothner@cygnus.com>
* Make-lang.in (c++-filt): Link libiberty.a after cxxmain.o.
Mon Sep 21 01:53:05 1998 Felix Lee <flee@cygnus.com>
* lex.c (init_lex): Use getenv ("LANG"), not GET_ENVIRONMENT ().
1998-09-20 Mark Mitchell <mark@markmitchell.com>
* class.c (maybe_warn_about_overly_private_class): Reformat.
1998-09-17 Andrew MacLeod <amacleod@cygnus.com>
* exception.cc (__cplus_type_matcher): Realign some code.
1998-09-16 Mark Mitchell <mark@markmitchell.com>
* Make-lang.in (tinfo.o): Use CXXFLAGS when compiling.
(tinfo2.o): Likewise.
(exception.o): Likewise.
(new.o): Likewise.
(opnew.o): Likewise.
(opnewnt.o): Likewise.
(opvnew.o): Likewise.
(opvnewnt.o): Likewise.
(opdel.o): Likewise.
(opdelnt.o): Likewise.
(opvdel.o): Likewise.
(opvdelnt.o): Likewise.
1998-09-16 Richard Henderson <rth@cygnus.com>
* decl.c (init_decl_processing): Kill __builtin_fp and __builtin_sp.
1998-09-15 Alexandre Oliva <oliva@dcc.unicamp.br>
* call.c (build_field_call): Handle static data members too.
* typeck.c (comptypes): When comparing pointer types, check
whether referred types match even in strictest modes.
1998-09-15 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h: Revert previous change.
(finish_struct_methods): Remove declaration.
* class.c: Revert previous change.
(maybe_warn_about_overly_private_class): New function.
(finish_struct_methods): Declare here, and make static. Remove
unnecessary parameters. Tidy slightly. Use
maybe_warn_about_overly_private_class.
(finish_struct_1): Adjust. Remove check for private constructors,
now done elsewhere.
(finish_struct): Adjust.
1998-09-15 Andrew MacLeod <amacleod@cygnus.com>
* except.c (expand_start_catch_block): No need to check for new
exception model.
(process_start_catch_block_old): Deleted.
(process_start_catch_block): Add call to start_decl_1().
(expand_end_catch_block): Add call to end_catch_handler().
* exception.cc (__cplus_type_matcher): Only check the exception
language if there is an exception table.
1998-09-15 Andrew MacLeod <amacleod@cygnus.com>
* search.c (expand_indirect_vtbls_init): Mark temporary stack slots
as used to prevent conflicts with virtual function tables.
1998-09-14 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (lang_type): Add has_non_private_static_mem_fn.
(CLASSTYPE_HAS_NON_PRIVATE_STATIC_MEM_FN): New macro, to access it.
* class.c (maybe_class_too_private_p): New function.
(finish_struct_methods): Use it.
(finish_struct_1): Likewise.
(finish_struct): Set CLASSTYPE_HAS_NON_PRIVATE_STATIC_MEM_FN if
appropriate.
* pt.c (check_specialization_scope): Fix spelling error.
(check_explicit_specialization): Remove code to handle explicit
specializations in class scope; they are now correctly diagnosed
as errors.
1998-09-10 Mark Mitchell <mark@markmitchell.com>
* decl.c (pushdecl): Don't copy types if the
DECL_ABSTRACT_ORIGIN of the new decl matches the TYPE_NAME of the
type.
1998-09-09 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* class.c (get_enclosing_class): New function.
(is_base_of_enclosing_class): Likewise.
* cp-tree.h (get_enclosing_class): Declare.
(is_base_of_enclosing_class): Likewise.
* pt.c (coerce_template_parms): Use them.
1998-09-09 Jason Merrill <jason@yorick.cygnus.com>
* g++spec.c (lang_specific_driver): Check whether MATH_LIBRARY is
null to decide whether to use it.
* error.c (dump_type_real): Handle NAMESPACE_DECL.
* parse.y (base_class.1): Avoid crash on error.
1998-09-08 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl.c (make_typename_type): If context is a namespace, the code
is in error.
1998-09-08 Mumit Khan <khan@xraylith.wisc.edu>
* parse.y (nomods_initdcl0): Set up the parser stack correctly.
1998-09-08 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (anonymous_namespace_name): Declare.
* decl.c: Define it.
(push_namespace): Use anonymous_namespace_name, rather than local
static anon_name.
* error.c (dump_decl): If a namespace is named
anonymous_namespace_name, call it {anonymous}.
* decl.c (grokparms): Distinguish between references and pointers
in error message.
1998-09-08 Richard Henderson <rth@cygnus.com>
Mark Mitchell <mark@markmitchell.com>
* pt.c (process_partial_specialization): Consistently allocate
and zero tpd.parms based on ntparms. Use tpd2.parms, not
tpd.parms, where appropriate.
Sun Sep 6 00:00:51 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (INCLUDES): Update after recent toplevel gcc
reorganizations.
1998-09-05 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (TI_PENDING_SPECIALIZATION_FLAG): Remove.
* class.c (finish_struct): Remove hackery to deal with explicit
specializations in class scope.
* decl.c (grokfndecl): Improve error-recovery.
* decl2.c (grokfield): Likewise.
* pt.c (check_specialization_scope): New function.
(begin_specialization): Call it.
(process_partial_specialization): New function, split out from
push_template_decl. Check partial specializations more
stringently.
(push_template_decl): Call it.
(check_explicit_specialization): Don't attempt to handle explicit
specializations in class scope.
(template_parm_data): Document. Add current_arg and
arg_uses_template_parms.
(mark_template_parm): Set it.
(tsubst_arg_types): Remove unused variable.
* semantics.c (begin_class_definition): Tweak.
1998-09-04 Mark Mitchell <mark@markmitchell.com>
* inc/typeinfo (type_info::type_info(const char*)): Make
`explicit'.
* cp-tree.h (hash_tree_cons_simple): New macro.
* pt.c (tsubst_arg_types): New function. Use hash_tree_cons.
(coerce_template_parms): Use make_temp_vec, instead of
make_tree_vec. Document this behavior.
(lookup_template_class): Likewise.
(tsubst, cases METHOD_TYPE, FUNCTION_TYPE): Use tsubst_arg_types.
Remove dead code (and add assertion to check its deadness). Fix
bug w.r.t. exception specifications.
1998-09-03 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_vtable): Always make artificials comdat.
(import_export_decl): Likewise.
* pt.c (mark_decl_instantiated): Likewise.
1998-09-03 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (finish_globally_qualified_member_call_expr):
Rename to ...
(finish_qualified_call_expr).
* semantics.c: Likewise.
* parse.y (primary): Use it.
* method.c (hack_identifier): Remove redundant code.
* init.c (resolve_offset_ref): Call convert_from_reference to
handle members of reference type. Improve error recovery.
1998-09-03 Benjamin Kosnik <bkoz@cygnus.com>
* cp-tree.h: Declare warn_nontemplate_friend.
* decl2.c (lang_decode_option): Set.
* lang-options.h: Add -Wnon-template-friend.
* friend.c (do_friend): Use to toggle non-template function warning.
1998-09-03 Mark Mitchell <mark@markmitchell.com>
* decl.c (finish_enum): Don't resolve CONST_DECLs to their
corresponding INTEGER_CSTs when processing_template_decl.
* pt.c (tsubst_enum): Tweak accordingly.
1998-09-03 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* decl.c (pushdecl_class_level): Add warning here.
(pushdecl): Tweak.
1998-09-02 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (convert_pointer_to_real): Tidy.
* search.c (get_base_distance_recursive): Simplify.
(get_base_distance): Likewise.
* pt.c (unify): Only special-case INTEGER_TYPE if it uses template
parms.
Wed Sep 02 09:25:29 1998 Nick Clifton <nickc@cygnus.com>
* lex.c (check_newline): Call HANDLE_PRAGMA before
HANDLE_SYSV_PRAGMA if both are defined. Generate warning messages
if unknown pragmas are encountered.
(handle_sysv_pragma): Interpret return code from
handle_pragma_token (). Return success/failure indication rather
than next unprocessed character.
(pragma_getc): New function: retrieves characters from the
input stream. Defined when HANDLE_PRAGMA is defined.
(pragma_ungetc): New function: replaces characters back into the
input stream. Defined when HANDLE_PRAGMA is defined.
1998-09-01 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (output_vtable_inherit): Use %cDIGIT in the operands.
* class.c (build_vtable_entry_ref): Likewise.
1998-09-01 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION): New macro.
* decl2.c (import_export_decl): Likewise.
* pt.c (instantiate_decl): Use it.
1998-09-01 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): Also do implicit typename thing for
artificial TYPE_DECLs.
* search.c (lookup_field): Likewise.
(lookup_fnfields, lookup_field): Adjust for implicit typename kludge.
* semantics.c (begin_constructor_declarator): Use enter_scope_of.
(enter_scope_of): Extract type from implicit typename.
(begin_class_definition): Likewise.
* lex.c (identifier_type): Handle implicit typename when checking
for SELFNAME.
* cp-tree.h: Declare flag_strict_prototype.
* lex.c (do_scoped_id, do_identifier): Don't implicitly_declare if
-fstrict-prototype.
* decl.c (init_decl_processing): If -f{no,-}strict-prototype wasn't
specified, set it to the value of pedantic.
1998-09-01 Mark Mitchell <mark@markmitchell.com>
* decl2.c (arg_assoc): Handle template-id expressions as arguments.
1998-08-31 Mark Mitchell <mark@markmitchell.com>
* decl.c (finish_enum): Handle member enums of classes declared in
template functions.
* decl2.c (grok_x_components): Strip attributes before calling
groktypename.
1998-08-31 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h, decl2.c: Remove support for -fall-virtual,
-fenum-int-equivalence and -fno-nonnull-objects.
* class.c (check_for_override): Remove support for -fall-virtual.
(finish_struct_1): Likewise.
* call.c (build_new_op): Remove support for -fenum-int-equivalence.
* typeck.c (build_binary_op_nodefault): Likewise.
* cvt.c (ocp_convert): Likewise.
* call.c (build_vfield_ref): Remove support for -fno-nonnull-objects.
* class.c (build_vbase_path): Likewise.
Sun Aug 30 22:16:31 1998 H.J. Lu (hjl@gnu.org)
* Makefile.in (INTERFACE): New, set to 1.
1998-08-30 Mark Mitchell <mark@markmitchell.com>
* error.c (dump_decl): Use CP_DECL_CONTEXT, not DECL_CONTEXT, when
comparing with global_namespace.
(dump_aggr_type): Likewise.
* decl.c (grokfndecl): Issue error on declaration of friend
templates with explicit template arguments.
* pt.c (convert_template_argument): New function, split out
from...
(coerce_template_parms): Here.
(tsubst): Attempt better error-recovery.
1998-08-28 Benjamin Kosnik <bkoz@loony.cygnus.com>
* pt.c (decl_template_parm_p): Add checks for
TEMPLATE_TEMPLATE_PARM.
1998-08-28 Mark Mitchell <mark@markmitchell.com>
* lex.c (do_identifier): Fix thinko in previous change.
1998-08-28 Jason Merrill <jason@yorick.cygnus.com>
* search.c (dfs_search, binfo_for_vtable, dfs_bfv_helper): New fns.
* decl2.c (output_vtable_inherit): Call binfo_for_vtable.
1998-08-28 Richard Henderson <rth@cygnus.com>
Add support for discarding unused virtual functions.
* lang-options.h: Add -fvtable-gc.
* cp-tree.h: Add flag_vtable_gc.
* decl2.c (output_vtable_inherit): New fn.
(finish_vtable_vardecl): Call it.
* class.c (build_vtable_entry_ref): New fn.
(build_vtbl_ref): Call it.
1998-08-28 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (build_enumerator): Take the enumeration type as a
parameter.
* decl.c (finish_enum): Don't set the TREE_TYPE for the
enumeration constant values if we're processing_template_decls.
Don't set the type for the CONST_DECLs either; that's done in
build_enumerator.
(build_enumerator): Take the enumeration type as a
parameter.
* lex.c (do_identifier): Don't resolve enumeration constants while
processing template declarations, even if they happen to be
TEMPLATE_PARM_INDEXs.
* parse.y (current_enum_type): New variable.
(primary): Don't allow statement-expression in local classes just
as we don't in global classes.
(structsp): Use current_enum_type.
(enum_list): Likewise.
* pt.c (tsubst_enum): Don't check for NOP_EXPRs introduced by
finish_enum; they no longer occur.
* cp-tree.h (finish_base_specifier): New function.
* parse.y (base_class): Use it.
* semantics.c (finish_base_specifier): Define it.
* parse.y (structsp): Warn on use of typename outside of template
declarations.
1998-08-27 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (handle_cp_pragma): Remove #pragma vtable.
* lang-options.h: Remove +e options.
* decl2.c (lang_decode_option): Likewise.
(import_export_vtable): Don't check write_virtuals.
(finish_vtable_vardecl, finish_file): Likewise.
* search.c (dfs_debug_mark): Likewise.
* semantics.c (begin_class_definition): Likewise.
* class.c (build_vtable, finish_vtbls, finish_struct_1): Likewise.
* call.c (build_over_call): Check flag_elide_constructors.
* decl2.c: flag_elide_constructors defaults to 1.
* typeck.c (convert_arguments): Remove return_loc parm.
(build_function_call_real): Adjust.
* search.c: Tear out all mi_matrix and memoize code.
(lookup_field, lookup_fnfields): Use scratch_tree_cons.
* lang-options.h: Remove documentation for -fhandle-exceptions,
-fmemoize-lookups and -fsave-memoized.
* cp-tree.h: Lose mi_matrix and memoize support.
* decl2.c: Ignore -fmemoize-lookups and -fsave-memoized.
* class.c: Lose struct class_level.
(pushclass, popclass): Lose memoize support.
* init.c (build_offset_ref): Likewise.
Never change BINFO_INHERITANCE_CHAIN.
* init.c (emit_base_init): Change modification of
BINFO_INHERITANCE_CHAIN to an assert.
* search.c (get_base_distance_recursive): Likewise.
(get_base_distance): Likewise.
(lookup_member): Likewise.
(convert_pointer_to_single_level): Likewise.
(lookup_field): Likewise. Lose setting TREE_VIA_* on TREE_LISTs.
(lookup_fnfields): Likewise.
* tree.c (propagate_binfo_offsets): Don't call unshare_base_binfos.
(unshare_base_binfos): Don't call propagate_binfo_offsets.
(layout_basetypes): Call propagate_binfo_offsets instead of
unshare_base_binfos.
* decl.c (xref_basetypes): Call unshare_base_binfos.
* pt.c (instantiate_class_template): Likewise.
* tree.c (reverse_path): Remove 'copy' parm; always make a
temporary copy.
* class.c (build_vbase_path): Just call it.
* search.c (compute_access): Likewise. Don't re-reverse.
1998-08-27 Mark Mitchell <mark@markmitchell.com>
* class.c (build_vbase_path): Use reverse_path.
(finish_base_struct): Move warnings for inaccessible bases to
layout_basetypes.
(modify_one_vtable): Remove check of TREE_USED (binfo).
(fixup_vtable_deltas1): Likewise.
* cp-tree.h (BINFO_INHERITANCE_CHAIN): Document here.
(xref_tag): Remove binfos parameter.
(make_binfo): Remove chain parameter.
(reverse_path): Add copy parameter.
* decl.c (init_decl_processing): Change calls to xref_tag.
(xref_tag): Remove binfos parameter.
(xref_basetypes): Change calls to make_binfo.
* decl2.c (grok_x_components): Change calls to xref_tag.
(handle_class_head): Likewise.
* friend.c (do_friend): Likewise.
* lex.c (make_lang_type): Change calls to make_binfo.
* parse.y (structsp): Change calls to xref_tag.
(named_complex_class_head_sans_basetype): Likewise.
(named_class_head): Likewise.
* rtti.c (init_rtti_processing): Likewise.
* search.c (compute_access): Change calls to reverse_path.
(dfs_get_vbase_types): Change calls to make_binfo.
(get_vbase_types): Remove dead code.
* tree.c (unshare_base_binfos): Change calls to make_binfo.
(layout_basetypes): Warn here about inaccessible bases.
(make_binfo): Remove chain parameter.
(reverse_path): Add copy parameter.
1998-08-27 Jason Merrill <jason@yorick.cygnus.com>
* class.c: #if 0 complete_type_p.
* init.c (build_java_class_ref, build_new_1): Remove unused locals.
* method.c (process_overload_item): Likewise.
* typeck.c (comp_target_types): Likewise.
Stop sharing binfos for indirect virtual bases.
* tree.c (propagate_binfo_offsets): Unshare vbases, too.
(layout_basetypes): Likewise.
(unshare_base_binfos): Copy vbases, too.
* cp-tree.h (BINFO_VIA_PUBLIC, BINFO_BASEINIT_MARKED,
BINFO_VBASE_INIT_MARKED): Remove obsolete macros.
(BINFO_PUSHDECLS_MARKED, SET_BINFO_PUSHDECLS_MARKED,
CLEAR_BINFO_PUSHDECLS_MARKED): New macros.
* search.c (lookup_field, lookup_fnfields, lookup_member): Remove
reference to BINFO_VIA_PUBLIC.
(marked_pushdecls_p, unmarked_pushdecls_p): New fns.
(push_class_decls): Use them.
(dfs_pushdecls): Use SET_BINFO_PUSHDECLS_MARKED.
(dfs_compress_decls): Use CLEAR_BINFO_PUSHDECLS_MARKED.
1998-08-27 Mark Mitchell <mark@markmitchell.com>
* decl.c (build_enumerator): Set DECL_CONTEXT for the
CONST_DECLs.
1998-08-26 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (finish_enum): Change prototype.
* decl.c (finish_enum): Use TYPE_VALUES, rather than taking a
VALUES parameter. Don't try to compute mins/maxs if
processing_template_decl.
* parse.y (structsp): Use new calling sequence for finish_enum.
* pt.c (tsubst_enum): Likewise. Take the new type as input.
(lookup_template_class): Remove unused variables. Tweak.
Register enums on instantiation list before substituting
enumeration constants.
(tsubst_decl): Remove unused variables.
(regenerate_decl_from_template): Likewise.
* decl.c (duplicate_decls): Don't obliterate the
DECL_TEMPLATE_INFO for a template if we're not replacing it with
anything.
* lex.c (do_identifier): Fix typo in comment.
Wed Aug 26 10:54:51 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* errfn.c: Remove stdarg.h/varargs.h.
* tree.c: Likewise.
1998-08-25 Brendan Kehoe <brendan@cygnus.com>
* pt.c (tsubst_copy): Only do typename overloading on an
IDENTIFIER_NODE that happens to look like a typename if it actually
has a type for us to use.
1998-08-25 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (comp_cv_target_types): Split out...
(comp_target_types): From here. Don't allow cv-qual changes under
a pointer if nptrs == 0. Fix OFFSET_TYPE handling.
(build_ptrmemfunc): Pass 1 to nptrs.
* cvt.c (perform_qualification_conversions): Use comp_ptr_ttypes.
1998-08-25 Mark Mitchell <mark@markmitchell.com>
* search.c (dependent_base_p): Don't compare a binfo to
current_class_type; use the TREE_TYPE of the binfo instead.
* cp-tree.h (CLASS_TYPE_P): Revise definition.
1998-08-25 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (duplicate_decls): Don't complain about different
exceptions from an internal decl even if pedantic.
* typeck.c (convert_for_assignment): Converting from pm of vbase
to derived is an error, not a sorry.
* call.c (build_over_call): Use convert_pointer_to_real for 'this'.
* class.c (fixed_type_or_null): Rename from
resolves_to_fixed_type_p. Return the dynamic type of the
expression, if fixed, or null.
(resolves_to_fixed_type_p): Use it. Return 0 if the dynamic type
does not match the static type.
(build_vbase_path): Rename 'alias_this' to 'nonnull'. Use
resolves_to_fixed_type_p again.
1998-08-24 Mark Mitchell <mark@markmitchell.com>
* pt.c (tsubst_decl): Move special case code for dealing with
tricky friend templates here from ...
(regenerate_decl_from_template): Here.
1998-08-24 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): Remove redundant linkage check.
1998-08-24 Gavin Romig-Koch <gavin@cygnus.com>
* typeck.c (c_expand_return): Handle the case that valtype
is wider than the functions return type.
1998-08-24 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (CLASS_TYPE_P): New macro.
* decl.c (grokdeclarator): Use it instead of IS_AGGR_TYPE.
* pt.c (process_template_parm): Undo previous change.
1998-08-24 Benjamin Kosnik <bkoz@cygnus.com>
* cp-tree.h: Declare.
* pt.c (decl_template_parm_p): New function.
* decl.c (pushdecl): Check decls for redeclaring template parms.
(xref_tag): Make redeclaration an error, print decl.
* decl2.c (grokfield): Check field_decls for redeclaration as well.
1998-08-24 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (primary): Fix up the type of string constants.
1998-08-24 Mark Mitchell <mark@markmitchell.com>
* typeck.c (convert_for_initialization): Move check for odd uses
of NULL to avoid duplicate warnings.
1998-08-24 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (lvalue_type): Fix for arrays.
* typeck.c (string_conv_p): New fn.
(convert_for_assignment): Use it.
(build_unary_op): Use lvalue_type.
* call.c (standard_conversion, convert_like): Use string_conv_p.
(add_function_candidate): Use lvalue_type.
* cvt.c (convert_to_reference): Likewise.
* decl2.c (lang_decode_option): Ignore -traditional.
* decl.c (init_decl_processing): flag_writable_strings inhibits
flag_const_strings.
1998-08-24 Andrew MacLeod <amacleod@cygnus.com>
* lang-options.h (lang_options): Add fconst-strings to the list
of valid options.
* decl2.c (lang_f_options, lang_decode_option): Likewise.
1998-08-24 Nathan Sidwell <nathan@acm.org>
* lex.c (real_yylex): Don't warn about long long constants if
we're allowing long long.
1998-08-24 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl.c (pushdecl): Use IDENTIFIER_NAMESPACE_VALUE instead of
accessing bindings directly.
* search.c (my_tree_cons): Reimplement.
* lang-specs.h: Remove __HONOR_STD.
* inc/exception, inc/new, inc/new.h, inc/typeinfo: Likewise.
1998-08-23 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Complain about in-class initialization
of aggregates and/or references.
* pt.c (process_template_parm): Clear IS_AGGR_TYPE for
TEMPLATE_TYPE_PARMs.
* decl2.c (grok_array_decl): Add comment.
(mark_used): Don't instantiate an explicit instantiation.
* friend.c (make_friend_class): Remove bogus comment. Fix check
for partial specializations.
* pt.c (check_explicit_specialization): Don't
SET_DECL_EXPLICIT_INSTANTIATION here.
(mark_decl_instantiated): Or here.
(do_decl_instantiation): Do it here, instead. Add checks for
duplicate explicit instantiations, etc. Tidy.
(do_type_instantiation): Likewise.
(instantiate_decl): Improve comments. Complain about explicit
instantiations where no definition is available.
* cp-tree.h (ansi_null_node): Remove.
* call.c (build_over_call): Warn about converting NULL to an
arithmetic type.
* cvt.c (build_expr_type_conversion): Likewise. Use
null_ptr_cst_p instead of expanding it inline.
* decl.c (ansi_null_node): Remove.
(init_decl_processing): Make null_node always have integral type.
* except.c (build_throw): Warn about converting NULL to an
arithmetic type.
* lex.c (init_parse): Remove handling of ansi_null_node.
* pt.c (type_unification_real): Don't convert NULL to void* type.
* typeck.c (build_binary_op_nodefault): Fix NULL warnings.
(convert_for_assignment): Warn about converting NULL to an
arithmetic type.
(convert_for_initialization): Likewise.
1998-08-20 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (search_tree, no_linkage_helper, no_linkage_check): New fn.
* pt.c (coerce_template_parms): Use no_linkage_check.
* decl.c (grokvardecl): Likewise.
(grokfndecl): Likewise. Members of anonymous types have no linkage.
* method.c (process_overload_item): Remove useless code.
1998-08-20 Per Bothner <bothner@cygnus.com>
Handle new'ing of Java classes.
* init.c (build_class_classref): New function.
(build_new_1): If type is TYPE_FOR_JAVA: Call _Jv_AllocObject;
constructor does not return this; don't need to exception-protect.
* pt.c (lookup_template_class): Copy TYPE_FOR_JAVA flag.
* decl2.c (acceptable_java_type): Handle template-derived types.
1998-08-20 Per Bothner <bothner@cygnus.com>
* decl2.c (import_export_vtable): Suppress vtables for Java classes.
1998-08-20 Mark Mitchell <mark@markmitchell.com>
* decl.c (duplicate_decls): Always merge the old and new patterns
for templates, regardless of whether or not the new one has
DECL_INITIAL. Don't throw away specializations. Merge
DECL_SAVED_TREE.
* pt.c (tsubst_decl): Use the right pattern when calculating the
complete args for a new template instance.
(do_decl_instantiation): Fix typo in comment.
(regenerate_decl_from_template): Deal with tricky friend template
case.
(instantiate_decl): Likewise.
Thu Aug 20 09:09:45 1998 Jeffrey A Law (law@cygnus.com)
* init.c (build_builtin_delete_call): Add missing assemble_external
call.
1998-08-20 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (notype_unqualified_id): Also accept ~A<int>.
1998-08-19 Mark Mitchell <mark@markmitchell.com>
* typeck.c (build_binary_op_nodefault): Warn on use of NULL in
arithmetic.
* except.c (build_throw): Warn when NULL is thrown, even with
-ansi. Use ansi_null_node, rather than integer_zero_node, in the
thrown expression.
* cp-tree.h (ansi_null_node): New variable.
* decl.c (ansi_null_node): New variable.
(init_decl_processing): Initialize its type.
* lex.c (init_parse): Initialize its value. Use ansi_null_node
for null_node in non-ANSI mode.
* typeck.c (build_binary_op_nodefault): Use ansi_null_node in
place of null_node to avoid spurious errors.
1998-08-17 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (enter_scope_of): New function.
* parse.y (complex_direct_notype_declarator): Use it.
* semantics.c (enter_scope_of): New function.
1998-08-17 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokparms): No, here.
* decl.c (grokdeclarator): Catch parm with pointer to array of
unknown bound here...
* method.c (process_overload_item): ...not here.
* gxxint.texi: Remove obsolete documentation of overloading code.
* decl.c (finish_enum): Also set TYPE_SIZE_UNIT.
* class.c (finish_struct_bits): Likewise.
* tree.c (lvalue_type): Fix for arrays.
* typeck.c (build_unary_op): Use lvalue_type.
* call.c (add_function_candidate): Likewise.
* cvt.c (convert_to_reference): Likewise.
* decl2.c (lang_decode_option): Ignore -traditional.
* init.c (build_offset_ref): Don't mess with error_mark_node.
* lex.c (do_scoped_id): Use cp_error.
* rtti.c (get_tinfo_fn): Don't mess with the context for now.
1998-08-17 Benjamin Kosnik <bkoz@loony.cygnus.com>
* decl.c (grokdeclarator): Allow anonymous types to be cv-qualified.
Mon Aug 17 10:40:18 1998 Jeffrey A Law (law@cygnus.com)
* cp-tree.h (set_identifier_local_value): Provide prototype.
* decl2.c (do_namespace_alias): Remove unused variables `binding'
and `old'.
Fri Aug 14 16:42:27 1998 Nick Clifton <nickc@cygnus.com>
* Makefile.in: Rename BBISON to BISON so that it can be properly
inherited from the parent makefile.
1998-08-13 Jason Merrill <jason@yorick.cygnus.com>
* lang-options.h: Add -finit-priority.
* decl2.c: Likewise. Check flag_init_priority instead of
USE_INIT_PRIORITY.
* decl2.c (setup_initp): New fn.
(start_objects, finish_objects, do_ctors): Handle init_priority.
(do_dtors, finish_file): Likewise.
1998-08-13 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_copy): Hush warning.
* rtti.c (get_tinfo_fn): Also set DECL_IGNORED_P.
1998-08-12 Mark Mitchell <mark@markmitchell.com>
* pt.c (print_template_context): Don't abort when instantiating a
synthesized method.
* decl.c (grokdeclarator): Issue errors on namespace qualified
declarators in parameter lists or in class scope.
1998-08-09 Mark Mitchell <mark@markmitchell.com>
* pt.c (check_explicit_specialization): Don't abort on bogus
explicit instantiations.
1998-08-07 Mark Mitchell <mark@markmitchell.com>
* typeck.c (require_complete_type): Use complete_type_or_else.
(complete_type_or_else): Always return NULL_TREE on failure, as
documented.
* pt.c (tsubst_aggr_type): Prototype.
(tsubst_decl): New function, split out from tsubst. Set
input_filename and lineno as appropriate.
(pop_tinst_level): Restore the file and line number saved in
push_tinst_level.
(instantiate_class_template): Set input_filename and lineno as
appropriate.
(tsubst): Move _DECL processing to tsubst_decl. Make sure the
context for a TYPENAME_TYPE is complete.
* decl2.c (grokbitfield): Issue errors on bitfields declared with
function type.
(do_dtors): As in do_ctors, pretend to be a member of the same
class as a static data member while generating a call to its
destructor.
* cvt.c (cp_convert_to_pointer): Handle NULL pointer
conversions, even in complex virtual base class hierarchies.
1998-08-06 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (ENUM_TEMPLATE_INFO): New macro.
(TYPE_TEMPLATE_INFO): Likewise.
(SET_TYPE_TEMPLATE_INFO): Likewise.
(ENUM_TI_TEMPLATE): Likewise.
(ENUM_TI_ARGS): Likewise.
(lookup_nested_type_by_name): Remove.
* decl.c (maybe_process_template_type_declaration): Handle enums.
(start_enum): Don't check for primary-template enum declarations
here.
(finish_enum): Clean up, document. Make sure template enum
constants get the correct type.
(build_enumerator): Copy initializers for template enumerations,
too.
(grok_enum_decls): Document.
* lex.c (do_identifier): Document use of LOOKUP_EXPR a bit
better. Build LOOKUP_EXPRs for local variables, even if they are
TREE_PERMANENT.
* pt.c (tsubst_enum): Remove field_chain parameter.
(template_class_depth): Include the depth of surrounding function
contexts.
(push_template_decl): Check for primary-template enum declarations
here. Deal with enumeration templates.
(lookup_template_class): Likewise.
(for_each_template_parm): Likewise.
(instantiate_class_template): Don't call tsubst_enum directly,
call tsubst instead, to instantiate enums. Deal with all
field_chain issues here, not in tsubst_enum.
(lookup_nested_type_by_name): Remove.
(tsubst_aggr_type): Revise handling of enumeration types.
(tsubst): Likewise.
(tsubst_copy): Likewise.
(tsubst_expr): Call tsubst, not tsubst_enum for TAG_DEFNs.
1998-08-04 Mark Mitchell <mark@markmitchell.com>
* decl.c (pushtag): Don't mangle the name of a TYPE_DECL if it
uses template parameters.
* method.c (build_template_parm_names): Use the full set of
template arguments for tsubst'ing.
(build_overload_identifier): Pass the full set of template
arguments to build_template_parm_names, not just the
innermost_args.
* pt.c (TMPL_ARGS_DEPTH): Define using
TMPL_ARGS_HAVE_MULTIPLE_LEVELS, for clarity.
(NUM_TMPL_ARGS): New macro.
(add_outermost_template_args): Deal with the case where the outer
args will be completely discarded.
(coerce_template_parms): Use the full set of template arguments
for tsubst'ing. Simplify. Add some asserts. Improve
error messages.
(lookup_template_class): Pass the full set of template arguments
to coerce_template_parms.
(tsubst): Add assertion.
(do_type_instantiation): Don't instantiate member template
classes.
* init.c (build_offset_ref): Deal with a TEMPLATE_ID_EXPR whose
name is a LOOKUP_EXPR, rather than an IDENTIFIER_NODE.
1998-08-03 Jason Merrill <jason@yorick.cygnus.com>
* method.c (set_mangled_name_for_decl): Change return type to void.
* decl.c (lookup_name_real): A namespace-level decl takes priority
over implicit typename. Avoid doing the same lookup twice.
* search.c (dependent_base_p): New fn.
(dfs_pushdecls, dfs_compress_decls): Use it.
* typeck.c (get_member_function_from_ptrfunc): Don't try to handle
virtual functions if the type doesn't have any.
1998-08-03 Mark Mitchell <mark@markmitchell.com>
* decl2.c (grokfield): Don't mangle the name of a TYPE_DECL if it
uses template parameters.
1998-08-02 Mark Mitchell <mark@markmitchell.com>
* cp-tree.def (LOOKUP_EXPR): Document. Remove second argument.
* cp-tree.h (DECL_TI_TEMPLATE): Improve documentation.
* lex.c (do_identifier): Don't use a second argument, or a type,
when building LOOKUP_EXPRs.
(do_identifier): Likewise.
(do_scoped_id): Likewise.
* method.c (hack_identifier): Improve error message.
* pt.c (lookup_template_function): Don't needlessly call
copy_to_permanent or build_min.
(tsubst_copy): Remove #if 0'd code. tsubst into LOOKUP_EXPRs if
necessary.
(do_decl_instantiation): Improve error message.
* tree.c (mapcar, case LOOKUP_EXPR): Don't be sorry; make a copy.
(build_min): Copy the type to the permanent obstack, too.
1998-08-01 Jason Merrill <jason@yorick.cygnus.com>
* init.c (init_init_processing): Remove BI* handling.
(build_builtin_call): Remove.
(build_builtin_delete_call): New fn.
(build_delete): Use it.
1998-07-31 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (PROCESSING_REAL_TEMPLATE_DECL_P): New macro.
(maybe_check_template_type): New function.
* decl.c (maybe_process_template_type_declaration): New function,
split out from pushtag Call maybe_check_template_type.
(pushtag): Use it. Use PROCESSING_REAL_TEMPLATE_DECL_P.
(xref_tag): Use PROCESSING_REAL_TEMPLATE_DECL_P.
* friend.c (do_friend): Use PROCESSING_REAL_TEMPLATE_DECL_P.
* pt.c (template_class_depth_real): Generalization of ...
(template_class_depth): Use it.
(register_specialization): Use duplicate_decls for duplicate
declarations of specializations.
(maybe_check_template_type): New function.
(push_template_decl_real): Fix comment.
(convert_nontype_argument): Likewise.
(lookup_template_class): Likewise. Avoid an infinite loop on
erroneous code.
(tsubst_friend_function): Fix comment.
(tsubst, case FUNCTION_DECL): Deal with a DECL_TI_TEMPLATE that is
an IDENTIFIER_NODE.
* semantics.c (begin_function_definition): Use
reset_specialization to note that template headers don't apply
directly to declarations after the opening curly for a function.
1998-07-29 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (push_overloaded_decl): Use current_namespace instead of
DECL_CONTEXT (decl) to determine where we go.
* decl.c (lookup_name_real): Fix typo.
1998-07-28 Mark Mitchell <mark@markmitchell.com>
* friend.c (is_friend): Be lenient with member functions to deal
with nested friends.
1998-07-28 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): Convert integer_zero_node to
ssizetype before passing it to set_rtti_entry.
* typeck2.c (initializer_constant_valid_p): Allow conversion of 0
of any size to a pointer.
1998-07-27 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (TI_USES_TEMPLATE_PARMS): Remove.
(build_template_decl_overload): Remove.
(set_mangled_name_for_decl): New function.
(innermost_args): Remove is_spec parameter.
(most_specialized, most_specialized_class): Remove declarations.
(lookup_template_class): Add entering_scope parameter.
(maybe_process_partial_specialization): New function.
(finish_template_decl): Likewise.
(finish_template_type): Likewise.
* class.c (finish_struct): Clean up processing of member template
specializations.
* decl.c (pushtag): Fix formatting.
(lookup_tag): Improve handling of pseudo-global levels.
(make_typename_type): Adjust call to lookup_template_class.
(shadow_tag): Use maybe_process_partial_specialization.
(xref_tag): Improve handling of member friends.
(start_function): Call push_nested_class before
push_template_decl. Don't call push_template_decl for
specializations.
* decl2.c (grok_x_components): Don't call xref_tag for
template instantiations. Handle UNION_TYPEs like RECORD_TYPEs.
(grokclassfn): Use set_mangled_name_for_decl.
(arg_assoc_class): Adjust call to innermost_args.
(mark_used): Don't call instantiate_decl for a TEMPLATE_DECL.
* error.c (dump_function_name): Improve printing of template
function names.
* friend.c (is_friend): Don't compare types of decls to determine
friendship, unless flag_guiding_decls.
(make_friend_class): Partial specializations cannot be friends.
(do_friend): Use set_mangled_name_for_decl. Call
push_template_decl_real instead of push_template_decl.
* method.c (build_decl_overload_real): Remove prototype. Give it
external linkage.
(build_overload_identififer): Adjust call to innermost_args.
(build_template_decl_overload): Remove.
(set_mangled_name_for_decl): New function.
* parse.y (.finish_template_type): New non-terminal.
(template_def): Use finish_template_decl. Use template_extdef
instead of extdef.
(template_extdef, template_datadef): New non-terminals, containing
only those rules for things which can be templates.
(datadef): Tidy.
(template_type, self_template_type): Use .finish_template_type.
(named_class_head): Use maybe_process_partial_specialization.
* pt.c (mangle_class_name_for_template): Remove context parameter.
(get_class_bindings): Remove outer_args parameter.
(complete_template_args): Remove.
(add_outermost_template_args): New function.
(register_specialization): Return the specialization.
(unregister_specialization): New function.
(tsubst_template_parms): Likewise.
(most_specialized, most_specialized_class): Prototype here as
static.
(original_template): Rename to most_general_template.
(tsubst_template_parms): New function.
(set_mangled_name_for_template_decl): Likewise.
(TMPL_ARGS_DEPTH): New macro.
(TMPL_ARGS_HAVE_MULTIPLE_LEVELS): Adjust.
(TMPL_ARGS_LEVEL): New macro.
(SET_TMPL_ARGS_LEVEL): Likewise.
(TMPL_ARG): Likewise.
(SET_TMPL_ARG): Likewise.
(TMPL_ARGS_DEPTH): Likewise.
(finish_member_template_decl): Use finish_template_decl.
(maybe_process_partial_specialization): New function, split out
from tsubst.
(inline_needs_template_parms): Use TMPL_PARMS_DEPTH.
(maybe_begin_member_template_processing): Use new macros.
(is_member_template): Likewise.
(is_member_template_class): Likewise.
(add_to_template_args): Likewise. Deal with multiple levels of
args.
(maybe_process_partial_specialization): New function.
(retrieve_specialization): Add consistency check.
(determine_specialization): Return full argument list.
(check_explicit_specialization): Tweak friend handling. Use full
argument lists. Simplify.
(current_template_args): Use new macros.
(push_template_decl_real): Change ill-named mainargs to specargs.
Check that a partial specialization actually specializes at least
one parameter. Improve friend handling. Modify for full
template arguments.
(classtype_mangled_name): Don't mangle the names of
specializations.
(lookup_template_class): Add entering_scope parameter. Use it to
avoid finding a template type when an instantiation is required.
Simplify. Use full template arguments.
(tsubst_friend_function): Use unregister_specialization. Use new
macros. Use full template arguments.
(tsubst_friend_class): Substitute, using tsubst_template_parms,
into the template parameters before passing them to
redeclare_class_template.
(instantiate_class_template): Simplify. Use full template
arguments. Adjust calls to get_class_bindings. Use
SET_IDENTIFIER_TYPE_VALUE where needed. Improve friend handling.
(innermost_args): Use new macros.
(tsubst_aggr_type): New function, split out from tsubst.
(tsubst): Use tsubst_aggr_type, tsubst_template_parms, new calling
conventions for lookup_template_class. Refine handling of partial
instantiations. Remove calls to complete_template_args.
Simplify. Add consistency checks. Use set_mangled_name_for_decl
and set_mangled_name_for_template_decl.
(tsubst_copy): Use tsubst_aggr_type.
(instantiate_template): Use full template arguments.
(more_specialized): Improve formatting.
(more_specialized_class): Adjust calls to get_class_bindings.
(get_bindings_real): Don't call complete_template_args.
(most_specialized): Don't overwrite input; create a new list.
(most_specialized_class): Use most_general_template.
(regenerate_decl_from_template): Use unregister_specialization.
Use full template arguments.
(instantiate_decl): Use full template arguments.
(set_mangled_name_for_template_decl): New function.
* semantics.c (begin_class_definition): Use
maybe_process_partial_specialization.
(finish_member_class_template): New function.
(finish_template_decl): Likewise.
(finish_template_type): Likewise.
(typeck.c): Don't crash after issuing a compiler_error.
* Makefile.in (CONFLICTS): Adjust; we removed a s/r conflict.
1998-07-27 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (build_functional_cast): Handle default-initialization.
* call.c (build_over_call): Pass 1 to popclass.
* parse.y (direct_notype_declarator): Add precedence declaration
to notype_unqualified_id case.
* Makefile.in (EXPECT): Adjust.
* tree.c (ovl_member): Fix for single function in OVL.
1998-07-27 Dave Brolley <brolley@cygnus.com>
* c-lex.c (yylex): Fix boundary conditions in character literal and
string literal loops.
1998-07-24 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): OK, do return the from_obj value
unless got_object depends on template parms.
* parse.y (nested_name_specifier_1): Pull out the TYPE_MAIN_VARIANT.
* pt.c (coerce_template_parms): Also complain about local enums.
* cp-tree.h: Add prototype for set_identifier_local_value.
* decl.c (set_identifier_local_value_with_scope): Make static,
prototype.
* search.c (covariant_return_p): Likewise.
* except.c (build_terminate_handler, alloc_eh_object): Likewise.
* call.c (build_method_call): Only pull out the type of a destructor
if it's a template type parm.
* decl.c (lookup_name_real): Never return the from_obj value.
1998-07-23 Jason Merrill <jason@yorick.cygnus.com>
* except.c (process_start_catch_block_old): Call start_decl_1 for
catch parm.
* decl.c (start_decl_1): Avoid duplicate error.
* init.c (expand_default_init): Only perform the initialization if
it will do something.
1998-07-23 H.J. Lu (hjl@gnu.org)
* parse.y (base_class): Check for invalid base class.
1998-07-23 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_template): Fold in...
(import_export_class): ...to here. Handle dllimport/export.
* class.c (build_vtable): Pass at_eof to import_export_vtable.
(prepare_fresh_vtable): Likewise.
* decl2.c (import_export_class): Split out...
(finish_prevtable_vardecl): From here.
* class.c (finish_struct_1): Call import_export_class if at_eof.
* decl.c (start_function): #if 0 mysterious code I wrote and have
forgotten why.
* rtti.c (get_tinfo_fn): If this is for a class type, set
DECL_CONTEXT.
1998-07-22 Jason Merrill <jason@yorick.cygnus.com>
* inc/exception: Change terminate and unexpected to ().
* parse.y (named_class_head_sans_basetype_defn): A
named_class_head_sans_basetype followed by '{' or ':' is a defn.
1998-07-21 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (canonical_type_variant): New fn to handle arrays.
* cp-tree.h (CANONICAL_TYPE_VARIANT): Remove.
* pt.c (unify, default case): Also fold arg. Fix array bounds case.
* method.c (process_overload_item): Use build_overload_value for
arrays.
1998-07-20 Dave Brolley <brolley@cygnus.com>
* lex.c (mbchar.h): #include it.
(GET_ENVIRONMENT): New macro.
(init_parse): Set character set based on LANG environment variable.
(real_yylex): Handle multibyte characters in character literals.
(real_yylex): Handle multibyte characters in string literals.
1998-07-19 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (do_identifier): Look for class value even if we don't
have a global value. Do implicit declaration if parsing is 2.
* semantics.c (finish_call_expr): Pass 2 if we're doing Koenig
lookup.
1998-07-19 Mark Mitchell <mark@markmitchell.com>
* decl.c (pushtag): Revert previous change.
* pt.c (lookup_template_class): Don't put out debugging
information for types that use template parameters.
* decl.c (pushtag): Don't put out debugging information for
compiler-generated typedefs.
* error.c (dump_type_real): Don't crash when presented with
intQI_type_node or the like.
* semantics.c (finish_translation_unit): Fix spelling error in
comment.
1998-07-17 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): Pull out single function here.
(select_decl): Not here.
(unqualified_namespace_lookup): Use CP_DECL_CONTEXT.
* decl.c (qualify_lookup): Tweak again.
* pt.c (lookup_template_class): Don't mess with the context of the
instantiation.
* decl2.c (current_decl_namespace): Remove special handling for
templates.
* pt.c (tsubst, case FUNCTION_DECL): Fix getting complete args for
a member template specialization.
* tree.c (ovl_member): Use decls_match to compare functions.
* decl.c (decls_match): Check the context of a function.
* parse.y (primary): Use notype_unqualified_id instead of IDENTIFIER
in Koenig lookup support rules.
* semantics.c (finish_call_expr): Handle the new cases.
* typeck.c (build_x_function_call): Handle overloaded methods.
* decl.c (grokvardecl): Don't call build_static_name for extern "C".
1998-07-16 Mark Mitchell <mark@markmitchell.com>
* semantics.c (finish_object_call_expr): Revert previous change.
* call.c (build_new_method_call): Likewise. Instead, convert
TYPE_DECLs to IDENTIFIERs here, in the presence of templates.
1998-07-16 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (qualify_lookup): Handle templates.
* decl2.c (do_using_directive): Don't pass ancestor.
* decl.c (push_using_directive): Calculate ancestor.
* decl2.c (do_nonmember_using_decl): Allow for type shadowing.
* decl.c (pushdecl): Move type shadowing handling from here...
(duplicate_decls): ...to here.
* decl.c (set_identifier_local_value_with_scope): New fn.
(pushdecl): Use it.
(set_identifier_local_value, lookup_type_current_level): New fns.
* decl2.c (do_local_using_decl): Handle types and binding level
stuff properly.
* init.c (build_offset_ref): Don't call mark_used on an OVERLOAD.
* decl.c (select_decl): Extract a lone function from an OVERLOAD.
(lookup_namespace_name): Likewise.
* typeck.c (build_unary_op): Not here anymore.
* decl2.c (do_class_using_decl): Make sure we get an identifier.
* class.c (handle_using_decl): Ignore TYPE_DECLs.
* decl.c (qualify_lookup): New fn.
(lookup_name_real): Use it.
1998-07-16 Martin v. Loewis <loewis@informatik.hu-berlin.de>
* decl2.c (add_using_namespace): When directly using a namespace
that was indirect before, promote it.
* cp-tree.h (LOOKUP_PREFER_TYPES, LOOKUP_PREFER_NAMESPACES,
LOOKUP_PREFER_BOTH, LOOKUP_NAMESPACES_ONLY, LOOKUP_TYPES_ONLY,
LOOKUP_QUALIFIERS_ONLY, LOOKUP_TEMPLATES_EXPECTED): New macros.
* decl.c (select_decl): Replace two flag parameters by one.
(unqualified_namespace_lookup): Likewise, pass flag.
(lookup_flags): New function.
(lookup_name_real): Compute flags, pass them.
(lookup_namespace_name): Call with zero-flag.
* decl2.c (ambiguous_decl): Add flag parameter, complain only
according to flags.
(lookup_using_namespace, qualified_lookup_using_namespace):
Add flag parameter, pass them through.
* lex.c (do_scoped_id): Call with zero-flag.
1998-07-16 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (convert_for_assignment): Use comptypes.
1998-07-16 Mark Mitchell <mark@markmitchell.com>
* semantics.c (finish_object_call_expr): Move test for the
function called being a TYPE_DECL to ...
* call.c (build_new_method_call): Here.
1998-07-15 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (arg_assoc_class): Also look at template arguments, if any.
(arg_assoc): Handle error_mark_node and multiple levels of TREE_LIST.
* lex.c (looking_for_typename): Don't initialize.
* decl2.c (ambiguous_decl): Clarify error message.
* decl.c (push_using_directive): Iterate over namespaces used
indirectly.
1998-07-15 Martin v. Löwis <loewis@informatik.hu-berlin.de>
* decl2.c (add_using_namespace): Iterate over namespaces used
indirectly.
* decl.c (lookup_name_real): Accept namespace aliases as locals.
(cat_namespace_levels): Ignore aliases.
(duplicate_decls): Ignore duplicate aliases.
* decl2.c (do_namespace_alias): Process block level namespace
aliases. Store alias with pushdecl. Remove odr errors.
* parse.y (namespace_alias): New non-terminal.
(extdef): Use it.
1998-07-15 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (arg_assoc_type): Handle METHOD_TYPE like FUNCTION_TYPE.
Handle TEMPLATE_TYPE_PARM.
(arg_assoc): Rewrite.
* pt.c (complete_template_args): Don't look at the context unless
we have to.
* method.c (build_decl_overload_real): Fix namespace handling.
* typeck.c (build_unary_op): Extract a lone function from an
OVERLOAD.
* call.c (build_scoped_method_call): Handle getting a namespace
for basetype in a destructor call.
(check_dtor_name): Handle enums.
* parse.y (using_directive): New nonterminal.
(extdef, simple_stmt): Use it.
1998-07-14 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl2.c (add_function): Move error message ...
(arg_assoc_namespace): ... from here.
1998-07-14 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (namespace_qualifier): Fix multiple level handling.
* decl2.c (namespace_ancestor): Use CP_DECL_CONTEXT.
(arg_assoc): Don't skip the first argument of a function.
Tue Jul 14 20:09:22 1998 Jeffrey A Law (law@cygnus.com)
* search.c (my_tree_cons): Clean up.
1998-07-14 Jason Merrill <jason@yorick.cygnus.com>
* call.c (joust): Don't warn about "confusing" conversions to the
same type.
1998-07-14 Martin von Löwis <loewis@informatik.hu-berlin.de>
* class.c (push_nested_class): Complain about namespaces.
* decl.c (start_decl): Enter the object's namespace.
(cp_finish_decl): Leave it.
(grokdeclarator): Likewise.
* decl2.c (check_decl_namespace): New function.
(finish_file): Call it.
* parse.y (complex_direct_notype_declarator): Set complexity
of namespace-qualified ids to -1, enter the namespace.
* method.c (build_template_decl_overload): Expect _DECL as first
parameter. Put context temporarily into current_namespace.
* pt.c (check_explicit_specialization): Change caller.
(tsubst): Likewise.
* init.c (build_offset_ref): Call mark_used and
convert_from_reference for namespace members.
Mon Jul 13 23:25:28 1998 Martin von Löwis <loewis@informatik.hu-berlin.de>
* search.c (my_tree_cons): The bitfield is at index 2.
Mon Jul 13 17:21:01 1998 Nick Clifton <nickc@cygnus.com>
* lang-options.h: Format changed to work with new --help support
in gcc/toplev.c
1998-07-12 Martin von Löwis <loewis@informatik.hu-berlin.de>
* decl2.c (build_expr_from_tree): Change calls of do_identifier.
Do Koenig lookup in CALL_EXPR.
(arg_assoc): Handle error_mark.
* lex.c (is_global): New function.
(do_identifier): Expect arguments for Koenig lookup.
* parse.y (primary): Add rules for calls of unqualified function calls.
(do_id): Change call of do_identifier.
* pt.c (finish_stmt_expr): Likewise.
* semantics.c (finish_id_expr): Likewise.
(finish_call_expr): Add integer parameter to indicate
argument-dependent lookup.
* decl.c (struct binding_level): New field using_directives.
(push_using_decl): Not sorry anymore.
(push_using_directive): New function.
(lookup_tag): Use CP_DECL_CONTEXT to iterate.
(unqualified_namespace_lookup): New function, code from ...
(lookup_name_real): ... here.
* decl2.c (lookup_using_namespace): Pass using list instead of
initial scope.
(validate_nonmember_using_decl): New function.
(do_nonmember_using_decl): New function.
(do_toplevel_using_decl): Use them.
(do_local_using_decl): New function.
(do_using_directive): Support block-level directives.
* parse.y (simple_stmt): Support using declarations and
directives.
(namespace_qualifier, namespace_using_decl): New non-terminals.
* xref.c (classname): New function.
(GNU_xref_hier): Change class and base parameters to tree.
* decl.c (xref_baseypes): Change caller.
* friend.c (make_friend_class): Likewise.
1998-07-12 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* typeck.c (comptypes, case TEMPLATE_TEMPLATE_PARM): Add parameter
comparison.
* pt.c (for_each_template_parm, case TEMPLATE_DECL): If it is a
template template parameter, record its use.
(for_each_template_parm, case TEMPLATE_TEMPLATE_PARM): Traverse
its template arguments if exists.
* pt.c (coerce_template_template_parms): New function equivalent
to coerce_template_parms when IS_TMPL_PARM is true.
(coerce_template_parms): Use it. Remove the IS_TMPL_PARM parameter,
all callers changed.
(coerce_template_parms): Access ARGLIST properly when creating a
new vector. Only accept implicit TYPE_DECL as valid argument for
a template template parameter when it is a base class of
current_class_type. Don't display error message when COMPLAIN is
false.
1998-07-12 Klaus Kaempf (kkaempf@progis.de)
* repo.c (get_base_filename): Use file_name_nondirectory.
(open_repo_file): Likewise.
* cp-tree.h (file_name_nondirectory): Add prototype.
1998-07-12 Jason Merrill <jason@yorick.cygnus.com>
* friend.c (do_friend): Pull the identifier out of declarator.
Use cp_error and friends.
* decl2.c (qualified_lookup_using_namespace): Fix call to
purpose_member.
* decl.c (lookup_name_real): Don't call complete_type on a namespace.
(grokvardecl): Use DECL_CLASS_SCOPE_P.
* cvt.c (convert_pointer_to_real): Check for error_mark_node sooner.
* class.c (warn_hidden): Fix for OVERLOAD.
From grahams@rcp.co.uk:
* cp-tree.h (DEFARG_NODE_CHECK): New macro.
(DEFARG_LENGTH, DEFARG_POINTER): Use it.
Sun Jul 12 01:20:57 1998 Jeffrey A Law (law@cygnus.com)
* g++.1 (-traditional): Remove duplicated documentation.
1998-07-11 Mark Mitchell <mark@markmitchell.com>
* method.c (flush_repeats): Add nrepeats parameter.
(issue_nrepeats): Likewise.
(is_back_referenceable_type): New function. Don't back-reference
TEMPLATE_TYPE_PARMs as well as simple types like integers.
(build_mangled_name_for_type): Likewise.
(build_mangled_name_for_type_with_Gcode): Likewise.
(lasttype): Remove.
(nrepeats): Likewise.
(Nrepeats): Likewise.
(start_squangling): Don't clear the variables removed above.
(end_squangling): Likewise.
(flush_repeats): Tidy. Use nrepeats parameter rather than
Nrepeats global.
(issue_nrepeats): Likewise, but with nrepeats global. Use
is_backreferenceable_type.
(build_overload_nested_name): Tidy. Add comment. Use
build_mangled_name_for_type.
(build_underscore_int): Comment.
(build_overload_scope_ref): Use build_mangled_name_for_type.
(build_overload_int): Likewise.
(build_template_template_parm_names): Tidy.
(build_template_parm_names): Use build_mangled_name_for_type.
(build_overload_identifier): Add comments.
(build_mangled_name_for_type_with_Gcode): Split out from
build_mangled_name.
(build_mangled_name_for_type): Use it.
(build_mangled_name): Rework to use build_mangled_name_for_type
and to not use global nrepeats/Nrepeats. Tidy.
(process_modifiers): Tidy.
(check_btype): Use is_backreferenceable_type. Add comment.
Rename `node' to `type'.
(process_overload_item): Set numeric_output_need_bar here.
Use build_mangled_name_for_type. Tidy.
(build_decl_overload_real): Tidy. Don't use Nrepeats. Use
build_mangled_name_for_type.
* pt.c (push_template_decl_real): Don't look at DECL_TEMPLATE_INFO
for TYPE_DECLs.
1998-07-08 Vladimir N. Makarov <vmakarov@cygnus.com>
* cp-tree.h (warn_long_long): Define.
* decl.c (grokdeclarator): Add flag `warn_long_long' as guard for
warning "ANSI C++ does not support `long long'".
* decl2.c (warn_long_long): Define.
(lang_decode_option): Parse -Wlong-long, -Wno-long-long options.
1998-07-07 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (xref_tag): Handle attributes between 'class' and name.
* parse.y (aggr): Likewise.
* semantics.c (finish_class_definition): Likewise.
* Makefile.in (EXPECTED): Adjust.
* cp-tree.h: Declare flag_optional_diags and warn_multichar.
* decl2.c: Define them.
(lang_decode_option): Handle them.
* lang-options.h: Add -foptional-diags.
* class.c (finish_struct): Don't complain about multiple meanings of
name if -fno-optional-diags.
* decl.c (pushdecl_class_level): Likewise.
* lex.c (real_yylex): Check warn_multichar.
1998-07-06 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_tag): Use CP_DECL_CONTEXT.
* tree.c (make_binfo): Fix length.
1998-06-30 Benjamin Kosnik <bkoz@bliss.nabi.net>
* decl2.c (lang_decode_option): Remove warn_template_debugging.
* lang-options.h: Likewise.
Mon Jun 29 20:17:40 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* except.c (build_eh_type_type_ref): Remove unused variable `susp'.
(process_start_catch_block): Likewise for variables
`false_label_rtx', `call_rtx' and `return_value_rtx'.
1998-06-29 Brendan Kehoe <brendan@cygnus.com>
* tree.c (build_srcloc): Make sure we allocate this node on the
permanent obstack.
Sat Jun 27 23:34:18 1998 Fred Fish <fnf@ninemoons.com>
* g++spec.c (NEED_MATH_LIBRARY): Define to 1 if not already defined.
(lang_specific_driver): Initialize need_math with NEED_MATH_LIBRARY.
(lang_specific_driver): Only add -lm automatically if need_math is
nonzero.
Sat Jun 27 12:22:56 1998 Jeffrey A Law (law@cygnus.com)
* Make-lang.in (g++): Depend on mkstemp.o. Link in mkstemp.o
Sat Jun 27 07:36:09 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (EXPR_H): New dependency variable.
(decl2.o): Depend on $(EXPR_H).
(typeck.o): Likewise.
(init.o): Likewise.
(expr.o): Likewise.
1998-06-25 Benjamin Kosnik <bkoz@lisa.cygnus.com>
* decl.c (start_enum): Put local enums on permanent_obstack.
1998-06-25 Mark Mitchell <mark@markmitchell.com>
* cp-tree.h (c_get_alias_set): Declare.
* decl.c (init_decl_processing): Set lang_get_alias_set.
1998-06-25 Andrew MacLeod <amacleod@cygnus.com>
* cp-tree.h (mark_all_runtime_matches): Add function prototype.
* except.c (mark_all_runtime_matches): Set TREE_SYMBOL_REFERENCED
flag for all function decls which are in the exception table.
* exception.cc (__cplus_type_matcher): Check for CATCH_ALL_TYPE match.
* decl2.c (finish_file): Call mark_all_runtime_matches to make sure
code is emitted for any referenced rtti function.
1998-06-25 Dave Brolley <brolley@cygnus.com>
* lang-specs.h: Use new | syntax to eliminate
string concatenation.
1998-06-25 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (CP_DECL_CONTEXT): New macro.
* decl2.c (is_namespace_ancestor, lookup_using_namespace): Use it.
* method.c (build_overload_nested_name): Likewise.
* sig.c (build_signature_pointer_or_reference_type): Don't set
DECL_CONTEXT.
1998-06-24 Martin v. Löwis <loewis@informatik.hu-berlin.de>
Set DECL_CONTEXT for globals to NULL_TREE instead of global_namespace.
* cp-tree.h (FROB_CONTEXT): New macro.
(DECL_MAIN_P): ::main should have a DECL_CONTEXT of NULL_TREE.
* decl.c (namespace_binding): Replace NULL_TREE with
global_namespace.
(set_namespace_binding, pop_namespace, lookup_name_real): Likewise.
* decl2.c (is_namespace_ancestor, lookup_using_namespace):
Likewise.
* decl.c (pushtag): Use FROB_CONTEXT.
(pushdecl, make_typename_type, define_function, grokdeclarator):
Likewise.
* decl2.c (set_decl_namespace, do_namespace_alias): Likewise.
* pt.c (push_template_decl_real, lookup_template_class, tsubst):
Likewise.
* decl2.c (decl_namespace): Return global_namespace if no context.
* method.c (build_overload_nested_name): Expect null as context.
* pt.c (mangle_class_name_for_template): Do nothing for null
contexts.
(lookup_template_class): Allow for null id_context.
1998-06-25 Richard Henderson <rth@cygnus.com>
* method.c (emit_thunk): Set current_function_is_thunk for the
ASM_OUTPUT_MI_THUNK case as well.
1998-06-23 Andrew MacLeod <amacleod@cygnus.com>
* exception.cc (__cplus_type_matcher): Get a match_info pointer
instead of an exception table entry as a parameter.
1998-06-23 Andrew MacLeod <amacleod@cygnus.com>
* parse.y (function_try_block): Don't call start_catch_handler.
* except.c (call_eh_info): Remove coerced field from declaration.
(build_eh_type_type_ref): New function to create an address of a
rtti function for the new style exception tables.
(expand_start_catch_block): Split function, this contains the
common part.
(process_start_catch_block_old): New function to perform the rest
of expand_start_catch_block under old style exceptions.
(process_start_catch_block_old): New function to perform the rest
of expand_start_catch_block under new style exceptions.
(expand_end_catch_block): Only pop the false label off the stack under
the old style of exceptions.
* semantics.c (finish_try_block): Don't call start_catch_handler.
* exception.cc (struct cp_eh_info): Add original_value field.
(__cplus_type_matcher): Perform type matching on the original exception
value, and if we have a match, set the current value.
(__cp_push_exception): Set the original exception value.
1998-06-23 Jason Merrill <jason@yorick.cygnus.com>
* call.c (joust): Fix confusing conversion warning.
* call.c (build_op_delete_call): Add placement parm. Check
LOOKUP_SPECULATIVELY.
* cp-tree.h, decl2.c, init.c: Adjust.
* decl.c (finish_function): Use it.
* pt.c (tsubst): Diagnose creating void fields or variables.
Mon Jun 22 08:50:26 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* call.c (build_scoped_method_call): Remove unused variable `tmp'.
* cp-tree.h (check_dtor_name): Add prototype.
* init.c (expand_member_init): Remove unused variables
`ptr_type_node', `parm' and `rval'.
* ptree.c (print_lang_type): Use HOST_WIDE_INT_PRINT_DEC specifier
in call to fprintf.
(lang_print_xnode): Likewise.
* typeck2.c (enum_name_string): Cast argument to sprintf to long
and use %ld specifier.
* xref.c (GNU_xref_end_scope): Use HOST_WIDE_INT_PRINT_DEC
specifier in call to fprintf.
(GNU_xref_member): Cast argument to sprintf to int.
Fri Jun 19 23:22:42 1998 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* typeck2.c (pop_init_level): Warn about implicit zero initialization
of struct members.
Thu Jun 18 09:32:32 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* cp-tree.h: Prototype function `check_java_method'.
1998-06-17 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct): Make conflicting use of id a pedwarn.
* decl.c (pushdecl_class_level): Likewise.
1998-06-17 Mark Mitchell <mark@markmitchell.com>
* pt.c (convert_nontype_argument): Issue an error when presented
with an integer (real) constant that cannot be simplified to an
INT_CST (REAL_CST).
* cp-tree.h (c_get_alias_set): Remove declaration added in
1998-06-13 change that should never have been checked in.
1998-06-17 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_binary_op_nodefault): Change % in format strings
to %%.
* decl.c (grokvardecl): Don't build_static_name for decls that
aren't at namespace scope.
* init.c (perform_member_init): Catch default-initialization of
references.
1998-06-17 Mark Mitchell <mark@markmitchell.com>
* errfn.c (cp_thing): Handle the `%%' formatting sequence.
1998-06-17 Jason Merrill <jason@yorick.cygnus.com>
* method.c (hack_identifier): Complain about getting a namespace
or class template.
* typeck.c (decay_conversion): Remove check for namespaces.
* typeck2.c (incomplete_type_error): Likewise.
* parse.y (template_arg): Add PTYPENAME expansion.
1998-06-16 Andrew MacLeod <amacleod@cygnus.com>
* decl.c (grokvardecl): Don't build external assembler names for
TYPENAMEs in other namespaces as there is no declarator.
* error.c (cp_file_of, cp_line_of): Don't extract file or line number
info from DECL_CONTEXT if it is NULL.
1998-06-16 Jason Merrill <jason@yorick.cygnus.com>
* call.c (check_dtor_name): Split out.
(build_scoped_method_call): Use it.
(build_method_call): Use it.
* init.c (build_offset_ref): Use it.
* typeck.c (build_static_cast): Fix handling of pointers to members.
* decl.c (finish_function): Just return nothing from a constructor.
* typeck.c (c_expand_return): Complain about returning a void
expression from a destructor.
1998-06-13 Mark Mitchell <mark@markmitchell.com>
* class.c (alter_access): Accept a BINFO explaining how to get
from the entity whose accessed is being altered to the type doing
the altering.
(handle_using_decl): New function containing code split out from ...
(finish_struct_1): Here.
* cp-tree.h (complete_type_or_else): Declare.
* init.c (build_new_1, build_delete): Use it.
* typeck.c (require_complete_type): Use complete_type, rather than
expanding it inline.
(complete_type_or_else): New function.
(build_component_ref): Use it.
(pointer_int_sum): Make sure the type pointed to is complete.
(pointer_diff): Likewise.
* pt.c (for_each_template_parm): Traverse the TYPE_CONTEXT for
types.
* search.c (get_matching_virtual): Note that member templates
cannot override virtual functions.
1998-06-12 Brendan Kehoe <brendan@cygnus.com>
* pt.c (check_explicit_specialization): If DECLARATOR turned into
an error_mark_node from lookup_template_function, return the same.
(determine_specialization): Also make sure TEMPLATE_ID isn't an
error_mark_node, before we try to read its operands.
* decl.c (grokdeclarator): If we got an error_mark_node from
check_explicit_specialization, just return it right back.
1998-06-12 Mark Mitchell <mark@markmitchell.com>
* class.c (instantiate_type): Don't treat template-ids that don't
specify any template arguments as equivalent to ordinary
identifiers. Use OFFSET_REF instead of SCOPE_REF to refer to
pointer-to-members for member templates. Tidy slightly.
* cp-tree.def (TEMPLATE_ID_EXPR): Revise documentation.
* init.c (build_offset_ref): Handle template-ids like ordinary
identifiers, for the most part, but store a TEMPLATE_ID_EXPR in the
offset part of the OFFSET_REF.
* typeck.c (build_unary_op): Change check for unknown types to
look for OFFSET_REFs, not SCOPE_REFs.
1998-06-11 Mark Mitchell <mark@markmitchell.com>
* pt.c (is_member_template_class): New function.
(push_template_decl_real): Use it.
1998-06-11 Benjamin Kosnik <bkoz@elmo.cygnus.com>
* friend.c (do_friend): Add support for nested classes using
member functions of the enclosing class as friends.
1998-06-10 Mark Mitchell <mark@markmitchell.com>
* call.c (convert_default_arg): Make global, not static.
(convert_arg_for_ellipsis): Split out from ...
(build_over_call): Here.
* cp-tree.h (convert_default_arg); Declare.
(convert_arg_to_ellipsis): Likewise.
(do_member_init): Remove.
* init.c (do_member_init): Remove; this code is dead.
(expand_member_init): Remove much of this code; it is dead.
* typeck.c (convert_arguments): Use convert_default_arg and
convert_arg_for_ellipsis, rather than duplicating here.
* call.c (convert_like): Don't fail silently if
build_user_type_conversion fails. Always return error_mark_node
for failure.
1998-06-10 Jason Merrill <jason@yorick.cygnus.com>
* search.c (covariant_return_p): Complain about ambiguous base.
* typeck.c (build_component_ref): Diagnose ref to nested type.
1998-06-10 Brendan Kehoe <brendan@cygnus.com>
* decl.c (grokparms): Check that INIT isn't an error_mark_node
before giving error about invalid type for default arg.
1998-06-10 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_method_call): Fix thinko.
1998-06-10 Dave Brolley <brolley@cygnus.com>
* decl2.c (lang_decode_option): New argc/argv interface.
* cp-tree.h (lang_decode_option): New argc/argv interface.
* lang-specs.h (default_compilers): Only call cpp if -E, -M or -MM is
specified for cpplib-enabled compilers.
* lex.c (lang_init): Don't check_newline for cpplib.
(init_parse): Don't initialize cpplib here.
1998-06-10 Brendan Kehoe <brendan@cygnus.com>
* typeck.c (build_component_ref): Make sure FIELD has a lang_specific
piece before checking DECL_MUTABLE_P.
1998-06-10 John Carr <jfc@mit.edu>
* tree.c (debug_binfo): Make printf format match arguments.
* error.c (OB_PUTI): Make printf format match arguments.
1998-06-10 Jason Merrill <jason@yorick.cygnus.com>
* init.c (perform_member_init): Handle default-initialization.
* except.c (build_throw): Handle throwing NULL.
* typeck.c (build_x_function_call): Use resolve_offset_ref.
* search.c (compute_access): Only strip an anonymous union
for a FIELD_DECL.
* call.c (add_builtin_candidates): Tweak.
* cvt.c (build_expr_type_conversion): Restore code for conversion
from class types.
* decl2.c (delete_sanity): Use it. Clean up.
* typeck.c (comp_ptr_ttypes_real): Fix cv-qual comparisons.
1998-06-10 Branko Cibej <branko.cibej@hermes.si>
* typeck.c (c_expand_return): Don't warn about void expressions on
return statements in functions returning void.
1998-06-09 Mark Mitchell <mark@markmitchell.com>
* pt.c (fn_type_unification): Revise documentation. Tidy.
(type_unification): Likewise.
1998-06-09 Andrew MacLeod <amacleod@cygnus.com>
* semantics.c (finish_try_block): Rename expand_start_catch, and delete
expand_end_catch.
* parse.y (function_try_block): Rename expand_start_catch, and delete
expand_end_catch.
* except.c (expand_end_eh_spec): Rename expand_start_catch, and delete
expand_end_catch.
1998-06-09 Jason Merrill <jason@yorick.cygnus.com>
* search.c (lookup_member): New fn.
* class.c (finish_struct_1): Use it.
* decl.c (lookup_name_real): Use it.
Mon Jun 8 20:45:52 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (decl2.o): Depend on dwarf2out.h and dwarfout.h.
* cp-tree.h: Add prototype for `maybe_print_template_context' and
`maybe_make_one_only'.
* decl.c (auto_function): Remove unused variable `decl'.
* decl2.c: Include dwarf2out.h and dwarfout.h.
* lex.c: Remove redundant declarations of `set_float_handler' and
`asm_out_file'.
1998-06-08 Andrew MacLeod <amacleod@cygnus.com>
* except.c (init_exception_processing): Remove NEW_EH_MODEL compile
time flag. Call __cp_eh_info instead of __cp_exception_info.
* exception.cc (struct cp_eh_info): Remove NEW_EH_MODEL flag.
(__cp_exception_info): Return offset into cp_eh_info structure to
match what use to be the start of this structure.
(__cp_eh_info): New function to return a pointer to cp_eh_info struct.
(__cplus_type_matcher, __cp_push_exception): Remove NEW_EH_MODEL
compile time flag.
(__uncatch_exception, __check_eh_spec, std::uncaught_exception): Call
__cp_eh_info instead of __cp_exception_info.
1998-06-08 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (cp_finish_decl): Disable inlining of extern inlines
with static variables.
1998-06-08 Mark Mitchell <mark@markmitchell.com>
* init.c (build_offset_ref): Correct previous change to use build,
not build_min.
1998-06-07 Mark Mitchell <mark@markmitchell.com>
* class.c (instantiate_type): Handle pointer-to-members where the
member is a template.
* init.c (build_offset_ref): Likewise.
* typeck.c (build_unary_op): Likewise.
1998-06-07 Richard Henderson <rth@cygnus.com>
* lex.c (lang_init_options): New function.
(lang_init): Remove flag_exceptions == 2 hack.
1998-06-05 Jason Merrill <jason@yorick.cygnus.com>
* search.c (envelope_add_decl): Tweak for implicit typename.
* call.c (joust): Also warn about confusing conversion op/constructor
overload resolution.
* spew.c (yylex): Also return the TYPE_DECL if got_object.
Don't clear got_object after '~'.
* call.c (build_scoped_method_call): Tweak destructor handling.
(build_method_call): Likewise.
* pt.c (tsubst_copy, case METHOD_CALL_EXPR): Don't mess with
TYPE_MAIN_VARIANT for destructors.
* semantics.c (finish_object_call_expr): Complain about calling a
TYPE_DECL.
1998-06-05 Per Bothner <bothner@cygnus.com>
* g++spec.c (lang_specific_pre_link, lang_specific_extra_ofiles):
Define - update needed by gcc.c change.
1998-06-05 Jason Merrill <jason@yorick.cygnus.com>
* error.c (cp_printers): Use 'o' instead of '_' for the null entry.
1998-06-05 Martin v. Loewis <loewis@informatik.hu-berlin.de>
* cp-tree.h (DECL_NAMESPACE_ALIAS, ORIGINAL_NAMESPACE): Declare.
* decl.c (lookup_name_real): Add namespaces_only parameter.
If set, return only NAMESPACE_DECLs.
(select_decl): Likewise.
(identifier_type_value): Give additional parameter.
(lookup_name_nonclass): Likewise.
(lookup_name): Likewise.
(find_binding): Skip namespace aliases.
(binding_for_name): Likewise.
(push_namespace): Check for namespace aliases.
(lookup_name_namespace_only): New function.
(begin_only_namespace_names, end_only_namespace_names): New functions.
* decl2.c (set_decl_namespace): Skip namespace aliases.
(do_using_directive): Likewise.
(do_namespace_alias): Produce namespace aliases, fix alias
redeclaration.
* error.c (dump_decl): Support SCOPE_REF.
* parse.y (extdef): Wrap lookup with namespace_only for namespace
aliases and using declarations.
1998-06-04 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (really_overloaded_fn): Only see through one TREE_LIST.
* error.c (dump_expr): Clean up NEW_EXPR case.
1998-06-04 Martin von Löwis <loewis@informatik.hu-berlin.de>
Suggested by Brendan Kehoe
* decl2.c (do_toplevel_using_decl): When decl is a TYPE_DECL,
treat it as using ::decl.
* decl2.c (arg_assoc_type): Process unknown_type_node and OFFSET_TYPE.
* tree.c (mapcar): Support NEW_EXPR.
* error.c (dump_expr): Support NEW_EXPR.
1998-06-03 Jason Merrill <jason@yorick.cygnus.com>
* method.c (make_thunk): Use overload machinery to make name.
* search.c (covariant_return_p): New fn.
(get_matching_virtual): Use it.
* init.c (build_new_1): Fix check for void.
1998-06-01 Per Bothner <bothner@cygnus.com>
* cp-tree.h (TYPE_FOR_JAVA): New macro.
* decl.c, cp-tree.h (java_byte_type_node, java_short_type_node,
java_int_type_node, java_long_type_node, java_float_type_node,
java_double_type_node, java_char_type_node, java_boolean_type_node):
New "primitive" types, with predefined names __java_byte etc.
(record_builtin_java_type): New function.
(init_decl_processing): Make Java types with record_builtin_java_type.
(pushtag, grokdeclarator): Set TYPE_FOR_JAVA if in extern "JAVA".
(xref_baseypes): If base class was TYPE_FOR_JAVA, so is this class.
(grokfndecl): Call check_java_method for Java classes.
* method.c (is_java_type): Removed. Replaced with TYPE_FOR_JAVA.
(process_overload_item): Match types against specific
java_XX_type_node types, rather than using is_java_type.
* class.c (finish_struct_1): Don't add default copy constructor
or operator= if TYPE_FOR_JAVA.
(pop_lang_conext): Restore strict_prototyp proper if Java.
* decl2.c (acceptable_java_type, check_java_method): New functions.
* pt.c (instantiate_class_template): Copy TYPE_FOR_JAVA from pattern.
(tsubst): Move common statement after if statement.
* typeck.c (comptypes): If strict, TYPE_FOR_JAVA must match.
1998-06-01 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (for_each_template_parm): Use first_rtl_op.
* tree.c (build_cplus_array_type_1): Also check index_type for
template parms.
1998-05-31 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Always copy BINFO_BASETYPES.
1998-05-29 scott snyder <snyder@d0sgif.fnal.gov>
* tree.c (layout_basetypes): If we change TYPE_SIZE, change
TYPE_SIZE_UNIT too.
1998-05-29 Mark Mitchell <mark@markmitchell.com>
* decl.c (grokdeclarator): Don't complain about in-class
initialization of static consts if we don't really know the type
of the variable.
1998-05-29 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (DECL_DESTRUCTOR_P): New macro.
* method.c (build_destructor_name): New fn.
* decl2.c (maybe_retrofit_in_chrg): Split out...
(grokclassfn): From here. Reorganize.
* decl.c (grok_ctor_properties): Make sure ctors for types with
vbases have the in_chrg parm.
* pt.c (instantiate_class_template): Update
TYPE_USES_VIRTUAL_BASECLASSES from tsubsted bases. Don't call
grok_*_properties.
(tsubst): Call grok_ctor_properties and maybe_retrofit_in_chrg.
1998-05-28 Mark Mitchell <mark@markmitchell.com>
* pt.c (instantiate_decl): Make test for whether or not static
variables should be instantiated early match its comment.
1998-05-28 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): Always pedwarn about vacuously redeclaring
a member.
(start_function): Call check_default_args.
* decl2.c (grokfield): Don't call check_default_args.
(check_default_args): Use cp_error_at.
* lex.c (do_pending_defargs): Call check_default_args.
1998-05-27 Brendan Kehoe <brendan@cygnus.com>
* call.c (build_method_call): Make sure get_type_value returns
something before we try to use its TYPE_MAIN_VARIANT.
(build_scoped_method_call): Likewise.
1998-05-27 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (digest_init): Complain about getting a TREE_LIST to
initialize an array.
* search.c (expand_upcast_fixups): Don't set DECL_CONTEXT and
DECL_VIRTUAL_P.
* friend.c (do_friend): Clarify template warning.
1998-05-27 Mark Mitchell <mark@markmitchell.com>
* decl.c (shadow_label): Don't treat decls as identifiers.
(maybe_push_to_top_level): Clear shadowed_labels.
* pt.c (instantiate_decl): Reset lineno and filename after calling
regenerate_decl_from_template.
* decl.c (grokdeclarator): Don't try to use TYPE_OBSTACK on an
error_mark_node.
1998-05-27 Kevin Buhr <buhr@stat.wisc.edu>
* parse.y (base_class): Use is_aggr_type, not IS_AGGR_TYPE.
1998-05-26 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* pt.c (process_template_parm): Accept TYPENAME_TYPE nodes.
(convert_nontype_argument): Handle cases when nontype template
parameters become classes after substitution.
1998-05-26 Mark Mitchell <mark@markmitchell.com>
* friend.c (is_friend): Use comptypes, rather than == to compare
types. Modify for new representation of template friends.
(make_friend_class): Likewise.
* pt.c (tsubst_friend_class): Undo 1998-05-21 change. Tweak.
(instantiate_class_template): Deal with template friends.
* decl.c (store_parm_decls): Remove redundant call to
expand_main_function.
1998-05-26 Benjamin Kosnik <bkoz@loony.cygnus.com>
* decl.c (start_decl): Check for DECL_LANG_SPECIFIC before
DECL_USE_TEMPLATE.
1998-05-26 Per Bothner <bothner@cygnus.com>
* language_as_string: Handle lang_java.
1998-05-26 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (pushdecl): Don't copy the type_decl.
1998-05-26 Martin v. Löwis <loewis@informatik.hu-berlin.de>
* class.c (pushclass): Always store TYPE_MAIN_VARIANT in
current_class_type.
* decl.c (grokdeclarator): Put typedefs on the type's obstack.
* parse.y (complex_direct_notype_declarator): Use $1 to access
scope of notype_qualified_id.
1998-05-26 Dave Brolley <brolley@cygnus.com>
* lex.c (parse_options,yy_cur,yy_lim): Add for cpplib.
(init_parse): Initialize cpplib interface.
* Makefile.in (CXX_OBJS): Make sure dependencies never end with an
empty continuation.
1998-05-26 Mark Mitchell <mark@markmitchell.com>
* decl.c (pushtag): Avoid crashing on erroneous input.
1998-05-25 Martin v. Löwis <loewis@informatik.hu-berlin.de>
* decl.c (push_namespace): Only produce one unique name for
anonymous namespaces.
(get_unique_name): Remove.
1998-05-25 Mark Mitchell <mark@markmitchell.com>
* call.c (tourney): Don't do any extra comparisons.
* decl2.c (build_anon_union_vars): Don't crash on empty sub-unions.
* cp-tree.h (processing_template_parmlist): Declare.
* decl.c (pushtag): Don't call push_template_decl when we
shouldn't.
* pt.c (processing_template_parmlist): New variable.
(TMPL_ARGS_HAVE_MULTIPLE_LEVELS): New macro.
(complete_template_args): Use it.
(add_to_template_args): Likewise.
(innermost_args): Likewise.
(tsubst): Likewise.
(begin_template_parm_list): Use processing_template_parmlist.
(end_template_parm_list): Likewise.
* cp-tree.h (ANON_UNION_TYPE_P): New macro.
* decl.c (grokdeclarator): Use it.
* decl2.c (grok_x_components): Likewise.
* init.c (initializing_context): Likewise.
* method.c (do_build_copy_constructor): Likewise.
(do_build_assign_ref): Likewise.
* search.c (compute_access): Likewise.
* typeck.c (build_component_ref): Likewise.
* decl.c (grokdeclarator): Don't give a cv-qualified version of an
unnamed type a typedef name "for linkage purposes".
* pt.c (lookup_template_class): Don't look at
IDENTIFIER_CLASS_VALUE when there's no current_class_type.
* method.c (build_overload_int): Handle error cases gracefully.
* pt.c (instantiate_decl): Handle static member variables
correctly.
* pt.c (tsubst): Use the tsubst'd type when producing new
TEMPLATE_PARM_INDEX nodes.
1998-05-24 Mark Mitchell <mark@markmitchell.com>
* tree.c (cp_tree_equal): Handle pointers to member functions.
* call.c (maybe_handle_implicit_object): Handle QUAL_CONVs. Make
sure the type of the REF_BIND is a reference type.
(maybe_handle_ref_bind, compare_ics): Rename reference_type to
target_type for clarity.
* parse.y (xcond): Move call to condition_conversion ...
* semantics.c (finish_for_cond): Here.
* parse.c: Regenerated.
1998-05-24 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (push_namespace): Namespaces have type void.
* typeck2.c (incomplete_type_error): Complain about namespace
used as expression.
* typeck.c (decay_conversion): Likewise.
1998-05-24 Martin von Löwis <loewis@informatik.hu-berlin.de>
* error.c (dump_expr): Support namespaces.
1998-05-23 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.def: Add SRCLOC.
* cp-tree.h: Add struct tree_srcloc and accessor macros.
* tree.c (build_srcloc, build_srcloc_here): New fns.
* pt.c (add_pending_template): Use build_srcloc_here.
(push_tinst_level): Update last_template_error_tick before erroring.
(instantiate_decl): Restore lineno and input_filename before
calling add_pending_template.
* decl2.c (finish_file): Set up lineno and input_filename for
pending templates.
1998-05-22 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lang_print_error_function): New fn.
(init_decl_processing): Set print_error_function to use it.
* errfn.c (cp_thing): Don't call maybe_print_template_context here.
* call.c (maybe_handle_ref_bind): Propagate ICS_USER_FLAG and
ICS_BAD_FLAG.
* cvt.c (ocp_convert): Don't set LOOKUP_NO_CONVERSION for
copy-initialization.
* class.c (build_vtable_entry): Use int_fits_type_p.
(build_vtable): Pass a signed offset to build_vtable_entry.
(prepare_fresh_vtable, modify_one_vtable, fixup_vtable_deltas1,
set_rtti_entry): Likewise.
1998-05-22 Per Bothner <bothner@cygnus.com>
* cp-tree.h: Add comments documenting which LANG_FLAGS are used.
(C_TYPE_VARIABLE_SIZE, C_DECL_VARIABLE_SIZE): Removed, not used.
1998-05-22 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (print_template_context): Use fprintf instead of cp_error.
* pt.c (determine_specialization): Just return an error_mark_node.
Also print the decl we want in error messages. If we complain,
return error_mark_node.
(tsubst_friend_function): Set lineno and input_filename so
error messages will be useful.
(instantiate_template): Just return an error_mark_node.
(check_explicit_specialization): Don't mess with a returned
error_mark_node.
* pt.c (print_template_context): Add new argument.
(maybe_print_template_context): New fn.
(push_tinst_level): Increment tinst_level_tick.
(pop_tinst_level): Likewise.
* errfn.c (cp_thing): Call maybe_print_template_context. Use
xrealloc instead of xmalloc.
* typeck.c (build_unary_op, CONVERT_EXPR): Propagate TREE_CONSTANT.
1998-05-21 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_friend_class): Don't call redeclare_class_template
if the template we looked up is the same as the one we already
have.
Thu May 21 11:54:44 1998 Dave Brolley <brolley@cygnus.com>
* lex.c: (handle_sysv_pragma): FILE* parameter not used.
(cpp_reader,parse_in): Add for cpplib.
(check_newline): Call handle_sysv_pragma with new interface.
(check_newline): Call GET_DIRECTIVE_LINE, not get_directive_line.
* input.c: (yy_cur,yy_lim,yy_get_token,GETC): Add for cpplib.
(sub_getch): Call GETC for cpplib.
* cp-tree.h: (get_directive_line): Different prototype for cpplib.
(GET_DIRECTIVE_LINE): Macro wrapper for get_directive_line.
* Makefile.in (CXX_OBJS): Add @extra_cxx_objs@ for cpplib.
1998-05-21 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (maybe_make_one_only): New fn.
(import_export_vtable): Use it.
(import_export_decl): Likewise.
* pt.c (mark_decl_instantiated): Likewise.
1998-05-21 Mark Mitchell <mmitchell@usa.net>
* decl2.c (find_representative_member): Rename to ...
(build_anon_union_vars): New function.
(finish_anon_union): Fix stupidity of previous change.
1998-05-20 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): Handle definition of specialization in
friend declaration.
* error.c (dump_decl): Fix LOOKUP_EXPR handling.
1998-05-20 Mark Mitchell <mmitchell@usa.net>
* class.c (delete_duplicate_fields_1): Use DECL_DECLARES_TYPE_P
to look for type declarations.
(finish_struct): Deal with templates on the CLASSTYPE_TAGS list.
* cp-tree.h (DECL_DECLARES_TYPE_P): New macro.
(finish_member_class_template): Declare.
* decl.c (pushtag): Put member class templates on the
CLASSTYPE_TAGS list, just as for ordinary member classes.
(pushdecl_class_level): Use DECL_DECLARES_TYPE_P.
(lookup_tag): Look for IDENTIFIER_CLASS_VALUEs, just as with
IDENTIFIER_NAMESPACE_VALUEs.
* parse.y (component_decl): Move code to ...
* semantics.c (finish_member_class_template): New function.
Don't put member class templates on the list of components for a
class.
* parse.c: Regenerated.
* pt.c (classtype_mangled_name): Don't try DECL_CONTEXT on types.
In fact, don't use DECL_CONTEXT at all here.
1998-05-20 Martin von Loewis <loewis@informatik.hu-berlin.de>
* decl.c (record_unknown_type): New function.
(init_decl_processing): Call it for the unknown and global type
nodes.
1998-05-20 Mark Mitchell <mmitchell@usa.net>
* decl2.c (find_representative_member): New function.
(finish_anon_union): Use it.
* cp-tree.h (MAIN_NAME_P): New macro.
(DECL_MAIN_P): Likwise.
* decl.c (pushdecl): Avoid crashing on redefinitions of `main'.
(grokfndecl): Use the new macros.
(grokdeclarator): Likewise.
(start_function): Likewise.
(store_parm_decls): Likewise.
(finsh_function): Likewise.
* friend.c (do_friend): Likewise.
* typeck.c (build_function_call_real): Likewise.
(build_unary_op): Likewise.
Wed May 20 02:16:01 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (start_objects, finish_objects, do_dtors,
do_ctors): Split out from...
(finish_file): ...here.
Tue May 19 20:36:23 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (is_overloaded_fn): Don't abort on placeholders from
push_class_decls.
Tue May 19 15:16:22 1998 Brendan Kehoe <brendan@cygnus.com>
* class.c (is_empty_class): Return 0 if TYPE is an error_mark_node.
* error.c (dump_expr): Handle an ARROW_EXPR.
Tue May 19 15:13:39 1998 Mark Mitchell <mmitchell@usa.net>
* decl.c (saveable_obstack): Declare.
(pushdecl): Copy TYPE_DECLs to the same obstack as the type they
declare, if necessary.
Tue May 19 14:50:27 1998 Mark Mitchell <mmitchell@usa.net>
* call.c (compare_qual): Remove.
(is_subseq): Tweak.
(is_properly_derived_from): New function.
(maybe_handle_ref_bind): Likewise.
(maybe_handle_implicit_object): Likewise.
(compare_ics): Modify substantially to bring into conformance with
the standard.
* cp-tree.h (TYPE_PTRMEMFUNC_OBJECT_TYPE): New macro.
(comp_cv_qualification): Declare.
(comp_cv_qual_signature): Likewise.
* typeck.c (comp_cv_qualification): Likewise.
(comp_cv_qual_signature): Likewise.
Tue May 19 10:05:02 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (parse.o): Depend on toplev.h.
* class.c (typecode_p): Remove prototype and definition.
* cp-tree.h (currently_open_class, is_empty_class, member_p):
Add prototype.
* decl.c (push_overloaded_decl_top_level): Remove prototype and
definition.
* errfn.c (cp_error): Cast function pointer `error' to (errorfn *)
in call to `cp_thing'.
(cp_warning): Likewise for function pointer `warning'.
* except.c (do_function_call): Remove prototype and definition.
(call_eh_info): Wrap variable `t1' in macro NEW_EH_MODEL.
* method.c (is_java_type): Add prototype and make it static.
* parse.y: Include toplev.h.
* pt.c (type_unification): Remove unused variable `arg'.
(instantiate_decl): Likewise for `save_ti'.
* tree.c (propagate_binfo_offsets): Likewise for `base_binfos'.
Tue May 19 02:43:25 1998 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_member_call): Handle template_ids.
* parse.y (primary): Add global_scope template_id.
Mon May 18 23:22:52 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (get_sentry): Use end_temporary_allocation.
Don't declare permanent_obstack.
Mon May 18 12:28:44 1998 Mark Mitchell <mmitchell@usa.net>
* parse.y (.finish_new_placement): New non-terminal.
(unary_expr, new_type_id): Use it.
* parse.c: Regenerated.
Mon May 18 12:20:27 1998 Brendan Kehoe <brendan@cygnus.com>
* pt.c (redeclare_class_template): Say where the original definition
of the template-parameter's default argument appeared.
Mon May 18 03:00:57 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Tweak empty class handling.
* decl.c (make_typename_type): Use currently_open_class.
* class.c (instantiate_type): Don't abort on TREE_NONLOCAL_FLAG.
Mon May 18 01:43:01 1998 Martin v. Loewis <loewis@informatik.hu-berlin.de>
* decl.c (lookup_name_real): Don't look at IDENTIFIER_LOCAL_VALUE
for a type unless it is one.
* class.c (finish_struct_1): Use OVL_CURRENT in error message.
Mon May 18 01:24:08 1998 Jeffrey A Law (law@cygnus.com)
* Makefile.in (program_transform_name, objdir): Define.
* Makefile.in (BISON): Use bison from the build tree if it exists.
(FLEX): Likewise.
Sun May 17 14:52:08 1998 Martin v. Loewis <loewis@informatik.hu-berlin.de>
* typeck.c (type_unknown_p): Return true for TREE_LIST also.
* call.c (build_method_call): Use TYPE_MAIN_VARIANT on typedefs.
Sun May 17 14:51:41 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_scoped_method_call): Likewise.
Sun May 17 13:53:48 1998 Mark Mitchell <mmitchell@usa.net>
* init.c (build_new_1): Call suspend_momentary around the creation
of values that must be saved for exception handling.
* parse.y (.build_new_placement): New non-terminal.
(unary_expr, new_placement): Use it.
* parse.c: Regenerated.
Sun May 17 12:32:08 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (duplicate_decls): Use CANONICAL_TYPE_VARIANT to compare
old and new types.
* pt.c (tsubst): Make sure that BINFO_TYPE of new binfos is the
canonical type.
* call.c (build_over_call): Don't use IS_SIGNATURE on a namespace.
Fri May 15 20:28:00 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): Revert problem change.
* Makefile.in (CONFLICTS): Fix.
Fri May 15 15:34:02 1998 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* decl.c (duplicate_decls): Clean up, add DECL_DATA_AREA bits.
Fri May 15 00:46:05 1998 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): Use BINFO_SIZE.
* decl.c (start_decl): Use 'tem'.
Thu May 14 16:30:47 1998 Andrew MacLeod <amacleod@cygnus.com>
* exception.cc: Include eh-common.h.
(struct cp_eh_info): Add eh_info struct with NEW_EH_MODEL.
(__cplus_type_matcher): First stab at new C++ runtime type matcher.
(__cp_push_exception): Initialize eh_info struct as well.
* except.c: Remove local structs and include eh-common.h.
(init_exception_processing): Set language and version codes.
(call_eh_info): Add presence of eh_info to runtime description of
struct cp_eh_info.
(expand_end_eh_spec): Call start_catch_block() and end_catch_block().
* semantics.c (finish_try_block): Call start_catch_block() and
end_catch_block().
* parse.y (function_try_block): Call start_catch_block() and
end_catch_block().
Thu May 14 12:27:34 1998 Brendan Kehoe <brendan@cygnus.com>
* typeck.c (original_type): New function.
(common_type): Use it to get the DECL_ORIGINAL_TYPE for T1 and T2,
to see if they're actually the same.
* cp-tree.h (original_type): Declare.
Wed May 13 12:54:30 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (lex.o): Depend on output.h.
* call.c (add_function_candidate): Remove unused variable `cand'.
(add_conv_candidate): Likewise.
(build_builtin_candidate): Likewise.
* cp-tree.h: Add prototype for `types_overlap_p'.
* decl.c (signal_catch): Mark parameter `sig' with ATTRIBUTE_UNUSED.
* decl2.c (merge_functions): Remove unused variables `tmp' and
`tempn'.
* error.c (expr_as_string): Mark parameter `v' with ATTRIBUTE_UNUSED.
(code_as_string): Likewise.
(language_as_string): Likewise.
(parm_as_string): Likewise.
(op_as_string): Likewise.
(assop_as_string): Likewise.
(cv_as_string): Likewise.
* lex.c: Include output.h.
* pt.c (type_unification): Cast first argument of `bzero' to a char*.
* search.c (dfs_no_overlap_yet): Mark parameter `t' with
ATTRIBUTE_UNUSED.
* tinfo.cc (__class_type_info::dcast): Change the type of variable
`i' from int to size_t.
* typeck.c (language_lvalue_valid): Mark parameter `exp' with
ATTRIBUTE_UNUSED.
Tue May 12 21:37:49 1998 Jason Merrill <jason@yorick.cygnus.com>
* error.c (dump_simple_decl): Use DECL_CLASS_SCOPE_P and/or
DECL_NAMESPACE_SCOPE_P.
(lang_decl_name): Likewise.
* pt.c (tsubst_friend_function, tsubst): Likewise.
* decl.c (pushdecl, redeclaration_error_message, start_decl,
cp_finish_decl, start_function): Likewise.
* class.c (finish_struct_1): Likewise.
* call.c (build_over_call): Likewise.
(compare_ics): Use DERIVED_FROM_P.
Tue May 12 07:24:18 1998 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (CANONICAL_TYPE_VARIANT): New macro.
* method.c (build_mangled_name): Use it.
(build_decl_overload_real): Likewise.
* error.c (dump_simple_decl): New function, broken out from ...
(dump_decl): Use it.
Mon May 11 11:38:07 1998 Mark Mitchell <mmitchell@usa.net>
* ptree.c (lang_print_xnode): Add missing `break'.
* pt.c (tsubst): Remove duplicate check for IDENTIFIER_NODE.
* call.c (add_template_candidate): Adjust for changes to
fn_type_unification.
(add_template_candidate_real): Likewise.
(add_template_conv_candidate): Likewise.
(build_user_type_conversion_1): Likewise.
(build_new_function_call): Likewise.
(build_object_call): Likewise.
(build_new_op): Likewise.
(build_new_method_call): Likewise.
* class.c (instantiate_type): Likewise.
* cp-tree.h (unification_kind_t): New type.
(fn_type_unification): Adjust prototype.
(type_unificaiton): Likewise.
* pt.c (UNIFY_ALLOW_NONE): New macro.
(UNIFY_ALLOW_MORE_CV_QUAL): Likewise.
(UNIFY_ALLOW_LESS_CV_QUAL): Likewise.
(UNIFY_ALLOW_DERIVED): Likewise.
(unify): Change prototype.
(maybe_adjust_types_for_deduction): New function.
(check_cv_quals_for_unify): Likewise.
(determine_specialization): Adjust.
(fn_type_unification): Likewise.
(type_unification): Likewise.
(type_unification_real): Likewise. Use
maybe_adjust_types_for_deduction. Fix mishandling of
back-unification of template functions passed as arguments. Pass
appropriate combination of UNIFY_ALLOW_* to unify.
(unify): Remove unused NTPARMS parameter. Use
check_cv_quals_for_unify. Remove bogus code that allowed
too-generous unification in order to adhere more closely to standard.
(get_bindings_real): Adjust.
(get_class_bindings): Likewise.
* method.c (build_overload_identifier): Only use the innermost
template arguments when mangling.
* pt.c (tsubst_template_argument_vector): New function.
(complete_template_args): Deal with the situation where the
extra_args contain more than one level of arguments.
(lookup_template_class): Deal with member template classes, which
may have more than one level of arguments.
(tsubst): Don't tsbust into the TREE_TYPE of an IDENTIFIER_NODE.
Improve handling of member template classes. Use
DECL_PRIMARY_TEMPLATE instead of inline expansion. Use
tsubst_template_argument_vector where appropriate.
(regenerate_decl_from_template): Break out from ...
(instantiate_decl): Here.
* lex.c (yyprint): Remove TYPENAME_ELLIPSIS.
* parse.h: Regenerated.
* parse.c: Really regenerated.
* cp-tree.h (finish_unary_op_expr): New function.
(finish_id_expr): Likewise.
(begin_new_placement): Likewise.
(finish_new_placement): Likewise.
(finish_declarator): Likewise.
(finish_translation_unit): Likewise.
(finish_parmlist): Likewise.
(begin_class_definition): Likewise.
(finish_class_definition): Likewise.
(finish_default_args): Likewise.
(finish_inline_definitions): Likewise.
* parse.y (GCC_ASM_KEYWORD): Remove.
(TYPENAME_ELLIPSIS): Likewise.
* parse.c: Regenerated.
Use new functions in semantics.c in the actions for many rules.
* gxx.gperf (GCC_ASM_KEYWORD): Just use ASM_KEYWORD.
* hash.h: Regenerated.
* semantics.c (finish_expr_stmt): Allow NULL expr.
(finish_unary_op_expr): New function, containing
code previously in parse.y.
(finish_id_expr): Likewise.
(begin_new_placement): Likewise.
(finish_new_placement): Likewise.
(finish_declarator): Likewise.
(finish_translation_unit): Likewise.
(finish_parmlist): Likewise.
(begin_class_definition): Likewise.
(finish_class_definition): Likewise.
(finish_default_args): Likewise.
(finish_inline_definitions): Likewise.
Sun May 10 23:43:13 1998 Mark Mitchell <mmitchell@usa.net>
* typeck.c (build_c_cast): Don't decay arrays and functions to
pointer type when converting to a class type.
Sun May 10 22:53:56 1998 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (DECL_NAMESPACE_SCOPE_P): New macro.
(DECL_CLASS_SCOPE_P): Likewise.
Sun May 10 22:48:22 1998 H.J. Lu (hjl@gnu.org)
* class.c (finish_struct_1): Use OVL_CURRENT on TREE_VEC_ELT.
* decl2.c (constructor_name_full): Likewise.
Sun May 10 22:48:12 1998 Mike Stump <mrs@wrs.com>
* tree.c (mapcar): Add OVERLOAD support.
* init.c (resolve_offset_ref): We must use basetype_path before we
destroy it with a call to convert_pointer_to.
Sat May 9 14:44:37 1998 Jason Merrill <jason@yorick.cygnus.com>
* class.c (currently_open_class): New fn.
* decl.c (lookup_name_real): Use it.
* search.c (lookup_field): Likewise.
Fri May 8 23:32:42 1998 Martin von Loewis <loewis@informatik.hu-berlin.de>
* cp-tree.def (OVERLOAD): New node.
* cp-tree.h (BINDING_TYPE, SET_IDENTIFIER_GLOBAL_VALUE,
SET_IDENTIFIER_NAMESPACE_VALUE): Define.
(NAMESPACE_BINDING): Remove.
(IDENTIFIER_GLOBAL_VALUE, IDENTIFIER_NAMESPACE_VALUE): Use
namespace_binding.
(OVL_FUNCTION, OVL_CHAIN, OVL_CURRENT, OVL_NEXT, OVL_USED):
Define.
(tree_overload): New struct.
(IDENTIFIER_TYPE_VALUE): Use identifier_type_value.
(REAL_IDENTIFIER_TYPE_VALUE): Define.
(IDENTIFIER_HAS_TYPE_VALUE): Use IDENTIFIER_TYPE_VALUE.
(lang_decl_flags): Remove in_namespace.
(lang_decl): Remove chain.
(DECL_CHAIN, DECL_NAMESPACE): Remove.
(flag_honor_std): Declare extern.
(identifier_type_value, pushdecl_namespace_level, push_using_decl,
namespace_binding, set_namespace_binding,
lookup_function_nonclass, cat_namespace_levels,
set_decl_namespace, lookup_arg_dependent, binding_init, ovl_cons,
scratch_ovl_cons, ovl_member, build_overload): Declare.
(decl_list_length, get_namespace_id, current_namespace_id,
overloaded_globals_p): Remove.
(lookup_using_namespace, qualified_lookup_using_namespace): Change
return type.
(push_scratch_obstack): New macro.
* call.c (add_function_candidate): Special-case type of OVERLOAD node.
(build_user_conversions_1): Iterate using OVL_NEXT for ctors,
convs, fns.
(build_new_function_call): Iterate using OVL_CHAIN.
Print DECL_NAME in when reporting ambiguities.
(build_object_call): Iterate using OVL_NEXT for fns, convs.
(build_new_op): Call lookup_function_nonclass.
Iterate using OVL_NEXT.
(build_op_delete_call): Change detection of members.
Do not wrap TREE_LIST around fields and single global functions.
(build_over_call): Don't push a class level if the context is a
namespace.
(build_new_method_call): Iterate using OVL_NEXT.
* class.c (add_method): Chain overloaded members using
build_overload. Remove copying of method.
(grow_method): When iterating through the obstack, expect OVERLOAD
nodes. Chain overload members.
(finish_struct_methods): Chain overload members. Unpack OVERLOAD
nodes in call to get_baselinks.
(duplicate_tag_error): Expect OVERLOAD nodes when unchaining.
(finish_struct_1): Iterate over ctor using OVL_NEXT. Handle
fdecls that are OVERLOAD nodes.
(validate_lhs): New function.
(instantiate_type): Do not copy OVERLOAD nodes. Remove dead
code. Use DECL_NAME in error messages. Split code between global
and member function processing.
* decl.c (global_type_node): New static variable.
(in_std): New global.
(struct binding_level): New field usings.
(resume_binding_level): Assert that we are not in a class.
(toplevel_bindings_p): Just check for namespace_p or
pseudo_global.
(resume_level): Remove.
(find_binding): New function.
(binding_for_name): Call it.
(namespace_binding, set_namespace_binding): New functions.
(push_namespace): Associate binding level with new namespace,
resume_binding_level for existing namespace. Remove old code.
Fake std by counting.
(store_bindings): Use REAL_IDENTIFIER_TYPE_VALUE.
(maybe_push_to_top_level): Save current namespace.
(pop_from_top_level): Restore saved namespace.
(pop_namespace): Call suspend_binding_level. Remove old code.
(cat_namespace_levels): New function.
(set_identifier_type_value_with_scope): For namespace bindings,
set BINDING_TYPE, and use global_type_node.
Use REAL_IDENTIFIER_TYPE_VALUE otherwise.
(identifier_type_value): New function.
(pushtag): If no context, use current_namespace.
(duplicate_decls): Don't process DECL_CHAIN.
(pushdecl): Set DECL_CONTEXT to current_namespace, if it is not
already set. Never reset it to NULL_TREE. Lookup global variables
in their namespace. Push overloaded templates if they are on
namespace level.
(pushdecl_namespace_level): New function.
(pushdecl_top_level): Implement using pushdecl_namespace_level.
(pushdecl_using_decl): New function.
(overloaded_globals_p): Remove.
(push_overloaded_decl): Create OVERLOAD nodes, and iterate through
them. Use namespace_binding and set_namespace_value.
(redeclaration_error_message): Complain if the declarations come
from different namespaces.
(lookup_tag): On namespace level, look in the BINDING_TYPE.
(lookup_namespace_name): Pass tree_bindings from stack. Remove
old code.
(select_decl): New function.
(lookup_name_real): Call it for qualified and unqualified lookup.
Pass tree_bindings from the stack.
If prefer_type is 1, also accept namespaces.
(lookup_function_nonclass): New function.
(init_decl_processing): Set the binding level of the global
namespace to global_binding_level.
Build a proper type list for __builtin_apply.
Initialize std_node to "fake std" if flag_honor_std is set.
Initialize global_type_node.
Allocated bad_alloc in namespace std if flag_honor_std.
(define_function): Set the DECL_CONTEXT to the current_namespace.
(start_decl): A namespace is not considered as a context here. If
the DECL_CONTEXT is a namespace, push the decl.
(cp_finish_decl): Check for namespaces used as initializers.
(grokfndecl): Add namespace parameter. Remove processing of
DECL_CHAIN.
(grokvardecl): Add namespace parameter.
(grokdeclarator): Process SCOPEs that are namespaces. For
mangling, temporarily set the DECL_CONTEXT on anonymous structs.
(start_function): Check for contexts that are namespaces.
Set context for declarations that have not been pushed.
(store_parm_decls): Check for ::main only.
(finish_function): Likewise.
(start_method): Check for contexts that are namespaces.
(start_method): Remove DECL_CHAIN processing.
* decl2.c (flag_honor_std): Declare.
(lang_decode_option): Set it if -fhonor-std or -fnew-abi is given.
(decl_namespace_list): New static global.
(grok_x_components): Ignore namespaces as type contexts.
(check_classfn): Expect OVERLOAD nodes.
(grokfield): Remove DECL_CHAIN processing.
(finish_file): Call cat_namespace_levels.
(merge_functions): New function.
(ambiguous_decl): Rewrite.
(lookup_using_namespace): Produce tree_bindings.
(qualified_lookup_using_namespace): Likewise.
(set_decl_namespace, decl_namespace, current_decl_namespace,
push_decl_namespace, pop_decl_namespace): New functions.
(arg_lookup): New struct.
(add_function, arg_assoc_namespace, arg_assoc_class,
arg_assoc_type, arg_assoc_args, arg_assoc, lookup_arg_dependent):
New functions.
(get_namespace_id, current_namespace_id): Remove.
(do_toplevel_using_decl): Rewrite.
(do_class_using_decl): Complain about namespace qualifiers.
(do_using_directive): Sorry if not on namespace level. Complain
about unknown namespaces.
* error.c (dump_aggr_type): Check for namespace contexts.
* except.c (init_exception_processing): Push terminate into std.
* friend.c (is_friend): A namespace is not a context, here.
* init.c (expand_member_init): Remove DECL_CHAIN processing.
(build_offset_ref): Process OVERLOAD nodes.
* lang-specs.h (__HONOR_STD): Define if -fnew-abi or -fhonor-std.
* lex.c (identifier_type): Loop using OVL_CHAIN.
(see_typename): Set looking_for_typename to 2.
(real_yylex): Likewise.
(do_identifier): Expect OVERLOAD nodes instead of TREE_LISTs.
(do_scoped_id): Expect OVERLOAD nodes.
Change calling convention for qualified_lookup_using_namespace.
(build_lang_decl): Don't set in_namespace anymore.
* method.c (typevec_size): New global.
(build_overload_nested_name): Return if global_namespace.
Otherwise, always expect a declaration context.
(build_qualified_name): Likewise.
Make sure we don't write beyond typevec_size.
(build_decl_overload_real): Likewise.
Allocate one extra slot for the namespace.
(hack_identifier): Mark code dead.
Process OVERLOAD and NAMESPACE_DECL nodes.
* parse.y (program): Pop namespaces until in global namespace.
(extdef): In a using-declaration, don't discard the identifier if
there is no declaration.
(left_curly): Ignore type contexts which are namespaces.
(typename_sub2): Use IDENTIFIER_TYPE_VALUE to retrieve the type
used as scope.
* pt.c (template_class_depth): Expect types to be namespaces.
(determine_specialization): Simplify by expecting OVERLOAD nodes.
(push_template_decl): Push into namespace level.
Reset ctx if it is a namespace.
Set DECL_CONTEXT to current_namespace if not set already.
Ignore real contexts that are namespaces.
(mangle_class_name_for_template): Skip global_namespace.
Mangle other namespaces as declarations.
(lookup_template_function): Set type of OVERLOAD nodes to unknown.
(lookup_template_class): Push into namespace of context.
If the context is a namespace, set it to global_namespace.
Use id_context for mangling.
(for_each_template_parm): Handle OVERLOAD and NAMESPACE_DECL nodes.
(tsubst_friend_function): Ignore namespace contexts.
Push into namespace level.
(tsubst): Handle NAMESPACE_DECL nodes.
Remove DECL_CHAIN processing.
(type_unification_real): Recognize OVERLOAD instead of TREE_LIST nodes.
* ptree.c (print_lang_identifier): Print bindings.
(lang_print_xnode): Print OVERLOAD nodes.
* rtti.c (init_rtti_processing): Push type_info into std.
* search.c (lookup_fnfields_here): Expect OVERLOAD nodes.
(lookup_fnfields_1, get_virtuals_named_this, get_matching_virtual,
dfs_debug_mark, dfs_pushdecls, dfs_compress_decls, add_conversions,
lookup_fnfields_here): Likewise.
Process all nodes, instead of going through TREE_CHAIN.
* sig.c (build_signature_pointer_or_reference_type): Set context
to global_namespace.
(build_signature_table_constructor): Expect OVERLOAD nodes.
* spew.c (yylex): Save old setting of looking_for_typename.
* tree.c (decl_list_length): Remove.
(binding_init): New function.
(count_functions): Rewrite.
(is_overloaded_fn): Expect OVERLOAD nodes.
(really_overloaded_fn, get_first_fn, lvalue_type): Likewise.
(ovl_cons, scratch_ovl_cons, build_overload, build_overload_after,
ovl_member): New functions.
* typeck.c (require_complete_type): Expect OVERLOAD nodes.
(type_unknown_p): Likewise.
(require_instantiated_type): Likewise.
(build_component_ref): Declare code dead.
(build_x_function_call): Create and expect OVERLOAD nodes.
(build_function_call_real): Check for ::main only.
(build_unary_op): Likewise. Expect OVERLOAD nodes.
(convert_for_assignment): Check for TREE_LIST before accessing
TREE_VALUE.
* decl.c (duplicate_decls): Check for namespace bindings instead
of global bindings.
(pushdecl, push_overloaded_decl, lookup_tag, lookup_name_real,
lookup_name_current_level, start_decl, xref_tag,
finish_enum): Likewise.
* init.c (build_offset_ref): Likewise.
* search.c (lookup_field): Likewise.
(lookup_fnfields): Likewise.
(dfs_debug_mark): Likewise.
* decl.c (poplevel): Use SET_IDENTIFIER_TYPE_VALUE.
(poplevel_class, pop_from_top_level): Likewise.
* decl2.c (finish_method): Likewise.
* class.c (build_vtable): Use SET_IDENTIFIER_GLOBAL_VALUE.
* decl.c (record_builtin_type): Likewise.
(init_decl_processing, grokfndecl): Likewise.
* lex.c (get_time_identifier, do_identifier, do_scoped_id): Likewise.
(make_lang_type): Likewise.
* parse.y (make_thunk): Likewise.
* pt.c (tsubst): Likewise.
* tree.c (debug_binfo): Likewise.
* exception.cc, new.cc, new1.cc, new2.cc, tinfo.cc, tinfo.h,
tinfo2.cc, inc/new.h: Add std qualifications.
* inc/new: Wrap with namespace std if __HONOR_STD.
* inc/typeinfo: Likewise.
Fri May 8 00:43:50 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_user_type_conversion_1): Handle second_conv
properly for templates.
Thu May 7 17:09:25 1998 Andrew MacLeod <amacleod@cygnus.com>
* method.c (build_decl_overload_real): Set TREE_USED flag to
zero for build_type_variants nodes as well.
Wed May 6 19:27:09 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Don't tsubst the type of an IDENTIFIER_NODE.
Wed May 6 16:49:48 1998 Jim Wilson <wilson@cygnus.com>
* Makefile.in (call.o, class.o, decl.o, decl2.o, errfn.o, error.o,
except.o, expr.o, friend.o, init.o, lex.o, method.o, pt.o, repo.o,
rtti.o, search.o, semantics.o, sig.o, tree.o, typeck.o, typeck2.o,
xref.o): Add toplev.h dependencies.
Wed May 6 16:44:58 1998 Jeffrey A Law (law@cygnus.com)
* errfn.c (cp_error, cp_warning): Remove declarations for
error and warning respectively.
Wed May 6 14:28:18 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* error.c: Convert to using ctype macros defined in system.h.
* method.c: Likewise.
* xref.c: Likewise.
* lex.c: Likewise. Also remove redundant system header stuff.
Wed May 6 06:36:41 1998 Robert Lipe <robertl@dgii.com>
* call.c, class.c, decl.c, decl2.c, errfn.c, error.c, except.c,
expr.c, friend.c, init.c, lex.c, method.c, pt.c, repo.c, rtti.c,
search.c, semantics.c, sig.c, tree.c, typeck.c, typeck2.c,
xref.c: Add include of toplev.h.
Wed May 6 02:33:39 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (perm_manip): Also regenerate the RTL of an extern.
(copy_to_permanent): Use end_temporary_allocation.
Tue May 5 23:54:04 1998 Jason Merrill <jason@yorick.cygnus.com>
* init.c (expand_vec_init): The initialization of each array
element is a full-expression.
Tue May 5 18:24:13 1998 Andrew MacLeod <amacleod@cygnus.com>
* method.c (build_mangled_name): Add a call to build_type_variant
to get the right type.
Tue May 5 01:25:03 1998 Jason Merrill <jason@yorick.cygnus.com>
* Makefile.in: Add .SUFFIXES.
* cp-tree.def: Remove NAMESPACE_DECL.
Sun May 3 01:32:14 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Do evaluate arg even if it has empty
class type.
* decl.c (start_function): Don't push a member function.
Thu Apr 30 18:59:23 1998 Jim Wilson <wilson@cygnus.com>
* Makefile.in (g++FAQ.info): Put -o option before input file.
Thu Apr 30 13:05:33 1998 Andrew MacLeod <amacleod@cygnus.com>
* gxxint.texi: Add info for squangling codes K and B.
Tue Apr 28 13:22:01 1998 Mark Mitchell <mmitchell@usa.net>
* semantics.c (begin_stmt_expr): Avoid duplicating the effect of
the expression in templates.
(finish_stmt_expr): Likewise.
1998-04-28 Brendan Kehoe <brendan@cygnus.com>
* decl2.c (ambiguous_decl): Fix NAME parm to be a tree, not int.
Mon Apr 27 13:58:10 1998 Mark Mitchell <mmitchell@usa.net>
* decl.c (maybe_push_to_top_level): Always clear
current_template_parms and processing_template_decl.
(pushtag): Remove check of current_class_type and some comments,
since maybe_push_to_top_level no longer creates confusion.
Sun Apr 26 12:10:18 1998 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (CLASSTYPE_IS_TEMPLATE): New macro.
(DECL_CLASS_TEMPLATE_P): Likewise.
(DECL_PRIMARY_TEMPLATE): Likewise.
(PRIMARY_TEMPLATE_P): Use it.
(push_template_decl_real): New function.
(redeclare_class_template): Take new template parameters as
input.
(is_specialization_of): New function.
(comp_template_args): Declare.
* decl.c (pushtag): Handle friend template classes.
(xref_tag): Likewise. Use new calling convention for
redeclare_class_template.
* decl2.c (grok_x_components): Handle friend templates.
* friend.c (is_friend): Use is_specialization_of where
appropriate. Deal with friend class templates.
(make_friend_class): Let a class template be friends with itself.
* pt.c (comp_template_args): Remove declaration.
(tsubst_friend_class): New function.
(push_template_decl_real): New function.
(push_template_decl): Use it.
(redeclare_class_template): Adjust for new calling convention.
(comp_template_args): Give it external linkage.
(instantiate_class_type): Use tsubst_friend_class to deal
with friend templates.
* typeck.c (comptypes): Use comp_template_args, rather than
expanding it inline.
* parse.y (component_decl): Handle a nested template type
like other component type declarations.
* pt.c (check_explicit_specialization): Handle overloaded
constructors correctly.
* pt.c (mabybe_get_template_decl_from_type_decl): New function.
(lookup_template_class): Use it.
Thu Apr 23 21:19:06 1998 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.def: Add WRAPPER. USER_CONV now only has two ops.
* cp-tree.h: Add WRAPPER support.
* call.c (add_candidate): Split out from add_*_candidate fns.
(build_over_call): Take the candidate instead of function and args.
Enforce access control here. Emit overload warnings here.
(add_warning): New fn.
(joust): Add WARN parm. If not set, call add_warning instead of
printing a warning. Re-enable some warnings.
(tourney): Pass it.
(convert_like): Adjust.
(build_new_op): Adjust.
(build_new_function_call): Adjust.
(build_user_type_conversion_1): Adjust.
(USER_CONV_FN): Adjust.
* tree.c (build_expr_wrapper, build_expr_ptr_wrapper,
build_int_wrapper): New fns.
Thu Apr 23 18:27:53 1998 Mark P. Mitchell <mmitchell@usa.net>
* pt.c (unify): Fix typo in previous change.
Thu Apr 23 09:32:58 1998 Jason Merrill <jason@yorick.cygnus.com>
* error.c (dump_type_real): Declare canonical_name.
* typeck.c (comp_target_types): Fix PMFs.
Wed Apr 22 13:24:48 1998 Mark Mitchell <mmitchell@usa.net>
* class.c (finish_struct): Set TREE_PRIVATE and TREE_PROTECTED for
the DECL_RESULTs of a member TEMPLATE_DECL, not just the
TEMPLATE_DECL.
* pt.c (tsubst): Decrease the template-level of
TEMPLATE_TEMPLATE_PARMS. Likewise for the DECL_INITIAL of a
TEMPLATE_PARM_INDEX.
(template_decl_level): New function.
(unify): Make sure to record unifications for template
parameters, even when the parameters exactly match the arguments.
Combine duplicated code for TEMPLATE_TEMPLATE_PARMs and
TEMPLATE_TYPE_PARMS. Don't try to unify template parameters that
aren't from the level we're currently working on.
Tue Apr 21 22:00:04 1998 Mark Mitchell <mmitchell@usa.net>
* errfn.c (cp_thing): Use xrealloc, not xmalloc, to copy memory.
* decl2.c (check_member_template): Set DECL_IGNORED for member
class templates, too.
* decl2.c (grokfield): Remangle the name of a member TYPE_DECL.
Tue Apr 21 18:59:11 1998 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* decl.c (duplicate_decls): Only check DECL_FRIEND_P if function.
Tue Apr 21 14:22:00 1998 Jeffrey A Law (law@cygnus.com)
* cp-tree.h (intTI_type_node, unsigned_intTI_type_node): Declare.
* decl.c (intTI_type_node, unsigned_intTI_type_node): Define.
(init_decl_processing): Handle TI types.
* typeck.c (unsigned_type, signed_type): Handle TI types.
Sat Apr 18 15:25:21 1998 Jim Wilson <wilson@cygnus.com>
* g++spec.c (lang_specific_driver): New argument in_added_libraries.
New local added_libraries. Increment count when add library to
arglist.
Fri Apr 17 21:25:00 1998 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (type_as_string_real): New function.
* pt.c (mangle_class_name_for_template): Use it.
* error.c (dump_aggr_type): Change prototype.
(dump_type_prefix): Likewise.
(dump_type_suffix): Likewise.
(dump_type_real): Convert from dump_type. If desired, the
"canonica" name of a typedef, i.e., the name of the underlying
type, can be printed.
(dump_type): Call dump_type_real.
Fri Apr 17 14:30:45 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (lang_decode_option): -fnew-abi implies -fvtable-thunks.
* typeck.c (comp_target_types): Tweak pedantic case.
(comp_target_parms): Tweak pedantic case. Clean up somewhat.
Return -1 or 1 instead of 1 or 2.
(compparms): Remove STRICT handling.
(convert_for_assignment): Fix handling of pmfs.
Fri Apr 17 14:04:16 1998 Mark Mitchell <mmitchell@usa.net>
* typeck.c (comp_target_types): Handle references like pointers.
(comp_target_parms): Note that return code from comp_target_types
can be negative to indicate failure.
Fri Apr 17 09:10:52 1998 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* Make-lang.in (c++.all.build): Don't depend on $(DEMANGLER_PROG),
which requires a working target compiler to build.
Fri Apr 17 08:57:35 1998 Jeffrey A Law (law@cygnus.com)
* tree.c (avoid_overlap): Add prototype.
* spew.c (num_tokens): Add prototype.
(nth_noken, add_token, consume_token, debug_yychar): Likewise.
* search.c (dfs_check_overlap): Add prototype.
(dfs_no_overlap_yet): Likewise.
* pt.c (original_template): Add prototype.
(inline_needs_template_parms): Likewise.
(push_inline_template_parms_recursive): Likewise.
(retrieve_specialization, register_specialization): Likewise.
(print_candidates, reduce_template_parm_level): Likewise.
(build_template_decl, mark_template_parm): Likewise.
(tsubst_friend_function, get_bindings_real): Likewise.
* method.c (start_squangling): Add prototype.
(end_squangling, check_ktype, issue_ktype): Likewise.
(build_overloaded_scope_ref, check_btype): Likewise.
(build_mangled_template_parm_index): Likewise.
* lex.c (init_cpp_parse): Add prototype.
(handle_cp_pragma, handle_sysv_pragma): Likewise.
(reduce_cmp, token_cmp): Likewise.
* except.c (call_eh_info): Add prototype.
(push_eh_info, get_eh_info, get_eh_value, get_eh_type): Likewise.
(get_eh_caught, get_eh_handlers, do_pop_exception): Likewise.
* decl2.c (is_namespace_ancestor): Add prototype.
(namespace_ancestor, add_using_namespace): Likewise.
(ambiguous_decl): Likewise.
* decl.c (indent): Add prototype.
* call.c (add_template_candidate_real): Add prototype.
Fri Apr 17 01:57:12 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (build_expr_from_tree): Just return a PMF.
Fri Apr 17 00:45:12 1998 Mark Mitchell <mmitchell@usa.net>
* typeck2.c (process_init_constructor): Don't strip cv-qualifiers
when doing initializations.
* pt.c (unify): Use comptypes to compare type args.
Fri Apr 17 00:24:22 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (duplicate_decls): Fix check for when it's safe to free
the new decl.
* pt.c (mangle_class_name_for_template): Don't pass a typedef type
to type_as_string.
Thu Apr 16 17:47:30 1998 Jeffrey A Law (law@cygnus.com)
* pt.c (build_template_parm_index): Add prototype.
* search.c (my_tree_cons): Don't clear words outside the
newly allocated node.
Wed Apr 15 15:34:44 1998 Dave Brolley <brolley@cygnus.com>
* lex.c (init_parse): Now returns char* containing the filename.
Wed Apr 15 13:20:06 1998 John Carr <jfc@mit.edu>
Jeff Law <law@cygnus.com>
* errfn.c: Rework to avoid problems when HOST_WIDE_INT is longer
than a pointer.
Sun Apr 12 22:31:19 1998 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
* cvt.c (cp_convert_to_pointer): Use TYPE_PRECISION.
Fri Apr 10 12:16:49 1998 Benjamin Kosnik <bkoz@loony.cygnus.com>
* decl.c (duplicate_decls): Don't warn for redundant decls if
friend: let add_friend take care of it.
Thu Apr 9 02:40:48 1998 Jason Merrill <jason@yorick.cygnus.com>
* sig.c (build_signature_pointer_constructor): Don't set
TREE_HAS_CONSTRUCTOR for a signature pointer.
* cvt.c (ocp_convert): Don't force a temporary for internal structs.
* init.c (resolve_offset_ref): Warn about implicit & on pmfs
here, too.
* typeck.c (build_unary_op): Only allow taking the address of a
real constructor.
* typeck2.c (digest_init): Simplify.
(store_init_value): Don't pedwarn about using { } for pmfs.
Thu Apr 9 22:16:57 1998 Per Bothner <bothner@cygnus.com>
* cp-tree.h (start_decl): Update prototype.
* decl.c (start_decl): Like the C version, new parameters
for the attributes. Call cplus_decl_attributes here,
(pushdecl): Like C version, do build_type_copy if TYPE_DECL,
(grokdeclarator): Pass NULL for new start_decl arguments.
* pt.c (tsubst_expr): Likewise.
* parse.y: Merge cplus_decl_attribute calls into start_decl calls.
* typeck.c (common_type): Check TYPE_MAIN_VARIANT.
* lex.c (build_lang_decl): Add lang_name_java.
* class.c (push_lang_context): Add lang_name_java.
* method.c (build_mangled_name): Check for is_java_type.
Thu Apr 9 22:16:57 1998 Benjamin Kosnik <bkoz@loony.cygnus.com>
* decl.c (grokdeclarator): Check TYPE_MAIN_VARIANT.
* call.c (build_scoped_method_call): Check for TREE_CODE for
VOID_TYPE instead of type == void_type_node.
(build_method_call): Likewise.
* decl.c (lookup_name_real): Likewise.
(grokdeclarator): Likewise.
(start_decl): Likewise.
(grokparms): Likewise.
(start_function): Likewise.
(finish_function): Likewise.
(start_method): Likewise.
Thu Apr 9 00:18:44 1998 Dave Brolley (brolley@cygnus.com)
* lex.c (finput): New variable.
(init_cpp_parse): Renamed from init_parse.
(init_parse): Handle !USE_CPPLIB. Call init_cpp_parse when finished.
(finish_parse): New function.
* cp-tree.h (init_lex, init_parse): Remove declarations.
Mon Apr 6 02:25:05 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_call): Still evaluate the actual argument.
* class.c (is_empty_class): Update for -fnew-abi.
* decl2.c: -fnew-abi implies -fsquangle.
* method.c (do_build_assign_ref): Don't do anything to copy
an empty class.
(do_build_copy_constructor): Likewise.
* call.c (build_over_call): Likewise.
Sat Apr 4 18:43:58 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (avoid_overlap): Return a value.
Sat Apr 4 12:52:35 1998 Jeffrey A Law (law@cygnus.com)
* method.c (check_btype): Add missing argument to xrealloc.
(check_ktype): Likewise.
Fri Apr 3 02:22:59 1998 Jason Merrill <jason@yorick.cygnus.com>
Implement empty base optimization.
* class.c (finish_struct_1): Add vbase fields earlier. Set
CLASSTYPE_SIZE of an empty base to 0. Types with bases can be empty.
* search.c (dfs_check_overlap, dfs_no_overlap_yet): New fns.
(types_overlap_p): New fn.
* tree.c (avoid_overlap): New fn.
(build_base_fields): Use it to avoid overlapping empty bases.
* cp-tree.h, decl2.c, lang-options.h: Add -fnew-abi.
* decl.c (cplus_expand_expr_stmt): Strip unused INDIRECT_REFs.
Re-implement allocation of base class subobjects.
* tree.c (unshare_base_binfos): New fn.
(layout_basetypes): Use it. Now handles offsets of both virtual and
non-virtual bases, after layout_type.
(layout_vbasetypes): Remove.
(build_base_fields): Generate FIELD_DECLs for each non-virtual base.
(build_vbase_pointer_fields): Split out from old layout_basetypes.
* class.c (finish_base_struct): Lose offset handling code.
Move nonvdtor warning here. Don't mess with t_binfo anymore.
(finish_struct_1): Don't mess with t_binfo anymore. Use fns above.
* cp-tree.h: Adjust.
Thu Apr 2 14:25:13 1998 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h: Lose CLASSTYPE_VBASE_SIZE, some unused stuff.
* decl.c, decl2.c, pt.c, ptree.c, lex.c: Likewise.
* class.c (duplicate_tag_error): Likewise.
(finish_struct_1): Set CLASSTYPE_SIZE, CLASSTYPE_MODE, CLASSTYPE_ALIGN.
* tree.c (layout_vbasetypes): Update from layout_record, remove
var_size support, use CLASSTYPE_SIZE instead of CLASSTYPE_VBASE_SIZE.
(layout_basetypes): Likewise.
Wed Apr 1 18:22:25 1998 Jeffrey A Law (law@cygnus.com)
* class.c, Make sure system.h is included just after config.h.
Delete lingering stdio and errno references too.
* decl.c, errfn.c, parse.y, ptree.c search.c, xref.c: Likewise.
Wed Apr 1 15:38:36 1998 Jason Merrill <jason@yorick.cygnus.com>
* friend.c (is_friend): Fix access control for local classes.
* class.c (is_empty_class): New fn.
* call.c (build_call): Don't pass empty class objects to a function.
Wed Apr 1 14:58:35 1998 Mark Mitchell <mmitchell@usa.net>
* call.c (build_over_call): Do name resolution for default
arguments of function templates in the scope of the templates.
Tue Mar 31 13:43:57 1998 Jeffrey A Law (law@cygnus.com)
* call.c: Include system.h. Remove includes, declarations and
defines provided by system.h.
* class.c, cvt.c, decl.c, decl2.c, errfn.c error.c: Likewise.
* except.c, expr.c friend.c, g++spec.c, init.c, input.c: Likewise.
* lex.c, parse.y, pt.c, ptree.c repo.c rtti.c, search.c: Likewise.
* semantics.c, sig.c, spew.c, tree.c, typeck.c: Likewise.
* typeck2.c, xref.c: Likewise.
* Makefile.in: Dependencies updated as appropriate.
* Make-lang.in: Likewise.
Mon Mar 30 12:15:00 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (fn_type_unification): Allow incomplete unification without
an immediate error message.
Mon Mar 30 08:55:42 1998 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (member_p): New fn.
* decl2.c (finish_file): Only set DECL_STATIC_FUNCTION_P for
initializing class members.
* cp-tree.def (TEMPLATE_PARM_INDEX): Class 'x'.
* ptree.c (lang_print_xnode): Handle TEMPLATE_PARM_INDEX.
* call.c (build_method_call): Handle non-scoped destructors, too.
* pt.c (tsubst_copy): Likewise.
* pt.c (print_template_context): Split out...
(push_tinst_level): ...from here.
* friend.c (is_friend): Don't pass a type to decl_function_context.
* typeck.c (convert_for_initialization): Always hand off
conversions to class type.
Sun Mar 29 20:01:59 1998 Jason Merrill <jason@yorick.cygnus.com>
* friend.c (is_friend): Local classes have the same access as the
enclosing function.
Sun Mar 29 00:47:32 1998 Jeffrey A Law (law@cygnus.com)
* typeck.c (expand_target_expr): Delete dead function.
* search.c: Put various prototypes inside #ifdef MI_MATRIX.
* repo.c (save_string): Delete dead function.
* method.c (thunk_printable_name): Delete dead function.
* lex.c (yynextch): Delete dead function.
* expr.c (tree_extract_aggr_init): #if 0 out.
* except.c (do_unwind): Delete dead function.
(easy_expand_asm): Likewise.
* cvt.c (build_conversion_type_1): Delete dead function.
* cp-tree.h (push_expression_obstack): Declare.
* call.c (source_type): #if 0 out.
* class.c (alter_access): Remove unused label. Add braces
around empty else clause.
* lex.c (yyprint): Fix argument to printf.
Sat Mar 28 17:43:52 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (tsubst): Clear TREE_USED for new FUNCTION_DECLs.
* pt.c (instantiate_class_template): Make sure template
arguments are permanent.
* init.c (resolve_offset_ref): Don't go looking around in
template types.
* semantics.c: Add routines to handle expressions, and some
declaration processing.
* parse.y: Use them.
(current_class_depth): Move declaration to cp-tree.h.
* parse.c: Regenerated.
* cp-tree.h: Use them.
(current_class_depth): Declare.
* pt.c (tsubst_copy): Use begin_stmt_expr and finish_stmt_expr.
Fri Mar 27 20:23:18 1998 Mark Mitchell <mmitchell@usa.net>
* error.c (dump_decl): Be a bit more explicit with template
type arguments, when verbose.
Fri Mar 27 18:16:40 1998 Jason Merrill <jason@yorick.cygnus.com>
* inc/exception: Reorder closing braces.
Fri Mar 27 13:22:18 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (redeclare_class_template): New function.
* cp_tree.h (redeclare_class_template): Declare it.
* decl.c (xref_tag): Use it.
Thu Mar 26 11:16:30 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Check IS_AGGR_TYPE, not
TYPE_LANG_SPECIFIC.
* typeck.c (convert_arguments): Likewise.
* decl.c (grokdeclarator): Remove const and volatile from type after
setting constp and volatilep.
* class.c (finish_struct_1): Don't warn about bool bitfield larger
than one bit.
Thu Mar 26 10:25:52 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (convert_nontype_argument): STRIP_NOPS where appropriate.
Thu Mar 26 10:24:05 1998 Mark Mitchell <mmitchell@usa.net>
* call.c (build_object_call): Complain about ambiguous operator(),
rather that crashing.
(build_new_op): Likewise.
(build_op_delete_call): Likewise.
Thu Mar 26 10:23:24 1998 Mark Mitchell <mmitchell@usa.net>
* cvt.c (perform_qualification_conversions): Use comp_target_types
instead of comp_ptr_ttypes.
Wed Mar 25 16:10:50 1998 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (enforce_access): Declare.
* call.c (enforce_access): Make it extern, not static.
* class.c (alter_access): Use enforce_access; modify code for ISO
compliance, rather than ARM rules.
Wed Mar 25 12:10:45 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* cp-tree.h: Fix typo.
Wed Mar 25 02:01:02 1998 Jason Merrill <jason@yorick.cygnus.com>
* expr.c (cplus_expand_expr): Only do PCC_STATIC_STRUCT_RETURN thing
if (aggregate_value_p (type)).
* decl2.c (constructor_name_full): Handle TYPENAME_TYPE.
Tue Mar 24 16:12:01 1998 Mark Mitchell <mmitchell@usa.net>
* tree.c (mapcar): When dealing with a DECL, use it's constant
value, if any.
* pt.c (lookup_template_class): Don't mangle the names of template
classes whose arguments are unknown.
* pt.c (tsubst_expr): Handle GOTO_STMT correctly.
Tue Mar 24 12:21:55 1998 Benjamin Kosnik <bkoz@lisa.cygnus.com>
* decl.c (init_decl_processing): Set TYPE_PRECISON for bools to 1.
Tue Mar 24 12:21:48 1998 Jim Wilson <wilson@cygnus.com>
* decl.c (init_decl_processing): Initialize TYPE_MAX_VALUE for
boolean_type_node to 1.
Tue Mar 24 10:23:47 1998 Mark Mitchell <mmitchell@usa.net>
* error.c (dump_expr): Remove unused variable `l'.
* pt.c (for_each_template_parm): New function, created by
converting uses_template_parms.
(tree_fn_t): New typedef.
(uses_template_parms): Use it.
(mark_template_parm): New function.
(push_template_decl): Check that the argument list of a partial
specialization uses all the template parameters.
* Make-lang.in (c++filt): Don't delete cxxmain.c after we're done
with it; we might want it for debugging.
* cp-tree.h (type_unification): Change interface.
* class.c (finish_struct_1): Skip nested template types, just like
ordinary nested types.
(instantiate_type): Use new interface to type_unification.
* lex.c (init_lex): Add __sz as opname for sizeof.
* method.c (build_overload_scope_ref): New function.
(build_overload_int): Handle complex expressions. Set
numeric_output_need_bar if necessary.
(build_overload_value): Handle non-PARM_DECL nodes; this
routine is now used by build_overload_int. Remove some
assignments to numeric_output_need_bar. Use
build_overload_scope_ref.
(build_qualified_name): Note that some template mangled names end
with digits, and set numeric_output_need_bar appropriately. Use
build_underscore_int.
* pt.c (unify): Change interface.
(type_unification_real): Likewise.
(determine_specialization): Use new interfaces.
(tsubst): Deal gracefully with situations in which the argument
vector is not fully filled.
(fn_type_unification): Use new interfaces.
(type_unification): Likewise. Remove NOP_EXPR hack.
(type_unification_real): Likewise.
(unify): Likewise. Deal with unification of complex expressions.
Mon Mar 23 12:24:37 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (complete_template_args): Initialize skip properly.
* decl.c (make_typename_type): Revert.
(make_implicit_typename): Remove.
(lookup_name_real): Don't call it. Call lookup_field if we see a
TYPE_DECL from a template base.
* search.c (lookup_field): Do implicit typename stuff.
Sun Mar 22 00:50:42 1998 Nick Clifton <nickc@cygnus.com>
Geoff Noer <noer@cygnus.com>
* Makefile.in: Various fixes for building cygwin32 native toolchains.
* Make-lang.in: Likewise.
Fri Mar 20 18:07:39 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* pt.c (tsubst, TEMPLATE_TEMPLATE_PARM): Simplify.
Fri Mar 20 10:42:07 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (make_implicit_typename): Rewrite removed code.
(make_typename_type): Call it if the type we look up comes from
a base that uses template parms.
* pt.c (complete_template_args): Rewrite.
(tsubst, FUNCTION_DECL): Use it.
Fri Mar 20 08:12:43 1998 H.J. Lu (hjl@gnu.org)
* semantics.c (finish_asm_stmt): Fix combine strings. Call
c_expand_asm_operands () if output_operands, input_operands or
clobbers is not NULL_TREE.
Fri Mar 20 00:10:19 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* pt.c (complete_template_args): New function.
(get_bindings): Deal with specializations of function templates
with return type containing parameters from outer class
templates.
(tsubst, TEMPLATE_TEMPLATE_PARM): When reducing parameter level,
substitute arguments and compose a new type.
Thu Mar 19 19:01:48 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (tsubst): Clear DECL_PENDING_INLINE_INFO for new
FUNCTION_DECLs.
Thu Mar 19 11:51:58 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (make_implicit_typename): Lose useless code.
* call.c (standard_conversion): Handle A* -> const A* properly.
* pt.c (get_bindings_real): Rename from get_bindings. Add
check_rettype parm.
(get_bindings): Pass 1.
(get_bindings_overload): Pass 0.
Wed Mar 19 09:08:12 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (check_explicit_specialization): When reverting a static
member function, also remove the `this' parameter from
last_function_parms.
Thu Mar 19 02:27:48 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_copy, CONST_DECL): Don't bother tsubsting
a function context.
* decl.c (store_bindings): Use free_binding_vecs.
(pop_from_top_level): Likewise.
Wed Mar 18 12:41:43 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (make_implicit_typename): Only change the type of a
TYPENAME_TYPE.
Wed Mar 18 10:09:51 1998 Mark Mitchell <mmitchell@usa.net>
* semantics.c: New file, containing routines to perform the
semantic phase of parsing.
* parse.y: Use it.
* pt.c (tsubst_expr): Likewise.
* cp-tree.h: Declare the various functions in semantics.c.
Provide macros to access _STMT tree nodes.
* cp-tree.def: Add ASM_STMT tree node.
* Makefile.in, Make-lang.in: Add dependencies on and for
semantics.c.
Wed Mar 18 00:24:10 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (push_template_decl): Only check primary templates.
* pt.c (check_explicit_specialization): Complain about default args
in explicit specialization.
* parse.y (nomods_initdcl0): Also call cp_finish_decl for a
constructor_declarator.
Tue Mar 17 14:44:54 1998 Mark Mitchell <mmitchell@usa.net>
* typeck2.c (build_x_arrow): Don't crash when an aggregate type
has no overloaded operator ->.
* call.c (build_field_call): Don't crash when presented with a
field that is actually a nested type.
* decl.c (pushtag): Deal with friend class injection in local
classes.
* call.c (build_object_call): Don't crash if OBJ is a
pointer-to-member-function.
Tue Mar 17 11:40:26 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (push_template_decl): Complain about template with C linkage,
anonymous template class.
Mon Mar 16 12:10:39 1998 Jason Merrill <jason@yorick.cygnus.com>
* class.c (pushclass): Only use the mi_matrix stuff #ifdef MI_MATRIX.
* search.c: Likewise.
* lex.c (do_pending_defargs): Only call
maybe_{begin,end}_member_template_processing for FUNCTION_DECLs.
* parse.y (initdcl0_innards): Move maybeasm back into initdcl0 et al.
Mon Mar 16 10:47:22 1998 Mark Mitchell <mmitchell@usa.net>
* parse.y: Deal with CONSTRUCTORS in new_initializers.
Mon Mar 16 10:54:21 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (tsubst_copy): Deal with BIND_EXPR in a way that more
closely mimics the behavior in parse.y.
(tsubst_expr): Return the resulting BLOCK when making a tsubst'ing
into a compound statement.
Sun Mar 15 02:07:26 1998 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (TEMPLATE_PARMS_FOR_INLINE): New macro.
* pt.c (inline_needs_template_parms): New fn.
(original_template): New fn.
(push_inline_template_parms_recursive): New fn.
(maybe_begin_member_template_processing): Use them.
(maybe_end_member_template_processing): Likewise.
(is_member_or_friend_template): Rename to is_member_template.
Member functions of local classes are never member templates.
Sun Mar 15 01:14:22 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* lex.c (do_identifier): Handle TEMPLATE_DECL that was
added in the class scope to catch redefinition error.
* pt.c (reduce_template_parm_level): Also copy
the DECL_TEMPLATE_PARMS field.
Sun Mar 15 10:54:08 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (tsubst): Clear TYPE_REFERENCE_TO when creating a
reduced-level template type parameter.
Sun Mar 15 12:26:02 1998 Manfred Hollstein <manfred@s-direktnet.de>
* cp-tree.h (struct lang_decl_flags): Add needs_final_overrider.
(DECL_NEEDS_FINAL_OVERRIDER_P): New macro.
* class.c (override_one_vtable): Set DECL_NEEDS_FINAL_OVERRIDER_P.
* decl.c (duplicate_decls): Propagate it.
* typeck2.c (abstract_virtuals_error): Use two loops to emit
abstract virtual functions and virtual functions which need a
final overrider separately.
Thu Mar 12 09:39:40 1998 Manfred Hollstein <manfred@s-direktnet.de>
* lang-specs.h: Properly put brackets around array elements in
initializer.
* typeck.c (build_binary_op_nodefault): Correctly place parens around
&& and || in expression.
Thu Mar 12 09:26:04 1998 Manfred Hollstein <manfred@s-direktnet.de>
* call.c (default_parm_conversions): Remove prototype definition.
(build_method_call): Remove unused variable result.
* cvt.c (ocp_convert): Remove unused variable conversion.
* decl2.c (ambiguous_decl): Add explicit parameter definition for name.
* except.c (do_unwind): #if 0 definition of unused variables fcall
and next_pc.
* expr.c (extract_scalar_init): #if 0 prototype and function
definition.
* init.c (expand_aggr_init_1): Remove unused variable init_type.
(build_new_1): Remove unused variable t.
* pt.c (instantiate_class_template): Remove unused variable newtag;
cast called function return value to void.
(do_decl_instantiation): Remove unused variables name and fn.
* tree.c (get_type_decl): Add default return to shut up compiler from
complaining control reaches end of non-void function.
* typeck.c (build_x_conditional_expr): Remove unused variable rval.
Thu Mar 12 09:12:15 1998 Manfred Hollstein <manfred@s-direktnet.de>
* call.c (default_parm_conversions): Remove prototype definition.
(build_method_call): Remove unused variable result.
(build_over_call): Add default case in enumeration switch.
Thu Mar 12 08:39:13 1998 Manfred Hollstein <manfred@s-direktnet.de>
* decl2.c (lang_decode_option): Change j's type to size_t.
* tree.c (layout_vbasetypes): record_align and desired_align are of
type unsigned int; const_size and nonvirtual_const_size likewise.
Wed Mar 11 07:25:20 1998 Mark Mitchell <mmitchell@usa.net>
* parse.y (new_initializer): Make sure all initializers are
lists.
Tue Mar 10 07:32:36 1998 Mark Mitchell <mmitchell@usa.net>
* decl2.c (import_export_decl): Mark tinfo functions for
cv-qualified versions of class types as DECL_NOT_REALLY_EXTERN.
Fri Mar 6 23:27:35 1998 Jeffrey A Law (law@cygnus.com)
* method.c: Fix typo.
Fri Mar 6 10:06:59 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* method.c: Include "system.h" to get stdlib.h, stdio.h,
ctype.h, string.h, etc.
(issue_nrepeats): Add default case in enumeration switch.
(check_btype): Likewise.
(process_overload_item): Likewise.
* Makefile.in (method.o): Depend on system.h.
Wed Mar 4 22:26:53 1998 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* lex.c (do_scoped_id): Fix parenthesizing.
Wed Mar 4 12:11:53 1998 Michael Tiemann <tiemann@axon.cygnus.com>
* rtti.c (get_tinfo_fn_dynamic): If this function is called an
FLAG_RTTI is unset, initialize type info machinery and continue
with FLAG_RTTI enabled.
(get_typeid): Likewise.
Wed Mar 4 11:47:55 1998 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (unary_complex_lvalue): &D::i has type B::* if i comes
from B.
Wed Mar 4 11:28:08 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (finish_member_template_decl): Deal more gracefully with
invalid declarations.
Tue Mar 3 01:38:17 1998 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c, decl.c, decl2.c, init.c, rtti.c, typeck.c, typeck2.c,
cp-tree.h: Clean up more old overloading code, old RTTI code, and
some formatting quirks.
* call.c, class.c, cp-tree.h, cvt.c, decl.c, init.c, lex.c,
method.c, pt.c, ptree.c, typeck.c: Remove support for
-fno-ansi-overloading and overloading METHOD_CALL_EXPR.
* class.h: Remove.
* Makefile.in: Adjust.
* pt.c (unify): Don't allow reduced cv-quals when strict.
* call.c, class.c, pt.c, cp-tree.h: Remove nsubsts parm from
*type_unification* and unify.
Mon Mar 2 12:11:06 1998 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (explicit_template_type): Remove TEMPLATE keyword.
(nested_name_specifier): And add it before this use.
(typename_sub0): And this use. Also add use without the keyword.
(typename_sub1): Likewise.
* pt.c (instantiate_class_template): Don't actually instantiate
anything if our type uses template parms.
Mon Mar 2 11:04:59 1998 Jim Wilson <wilson@cygnus.com>
* decl.c (start_function): Don't call temporary_allocation for a
nested function.
Sun Mar 1 21:06:37 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Don't mess with friends if
our type uses template parms.
Sat Feb 28 12:06:44 1998 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (nested_name_specifier): Use explicit_template_type.
(typename_sub): Allow a template_type, an explicit_template_type,
or an implicit template type at the end.
* lex.c (yyprint): Handle a PTYPENAME being a TEMPLATE_DECL.
* decl.c (make_typename_type): Handle template-id where the name
is a TEMPLATE_DECL.
* call.c (build_scoped_method_call): Handle member template
destructor call.
* pt.c (tsubst_copy, METHOD_CALL_EXPR): Don't assume a member
destructor is represented by the type.
* cp-tree.h (TYPENAME_TYPE_FULLNAME): New macro.
* parse.y (nested_name_specifier): Add 'template' case.
(explicit_template_type): New rule.
(typename_sub): Use it.
* decl.c (make_typename_type): Handle getting a template-id for NAME.
* pt.c (tsubst): Likewise.
Fri Feb 27 11:17:50 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (add_to_template_args): Fix thinko.
(instantiate_class_template): Call it later.
* pt.c (get_class_bindings): Add outer_args parm.
(most_specialized_class): Likewise.
(instantiate_class_template): Pass it.
(more_specialized_class): Likewise.
(lookup_template_class): Get context from template if none
was specified.
(finish_member_template_decl): Don't do anything with a
partial specialization.
* decl2.c (check_member_template): Use IS_AGGR_TYPE instead of
AGGREGATE_TYPE_P.
* class.c (finish_struct): Member class templates have already been
checked for name clashes.
* decl.c (pushdecl_with_scope): Handle pushing at class level.
Fri Feb 27 02:25:16 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst, TEMPLATE_DECL): Support member class templates.
(tsubst, *_PARM): Support multiple levels of template classes.
(instantiate_class_template): Look up the pattern from the
original template.
(lookup_template_class): Handle getting a template for d1.
(push_template_decl): Correct setting of 'primary'.
(reduce_template_parm_level): Add 'levels' parm.
(finish_member_template_decl): Support member class templates.
(template_class_depth): Handle multiple levels.
* parse.y (component_decl_1, fn.def2): Remove member template case.
(component_decl): Add member template cases.
* decl2.c (check_member_template): We now handle member template
classes.
* decl.c (pushtag): Handle member templates.
* method.c (do_inline_function_hair): Don't touch
IDENTIFIER_GLOBAL_VALUE.
* init.c (build_offset_ref): If name isn't an identifier, just
return it.
* spew.c (yylex): Handle PTYPENAME like TYPENAME.
* typeck.c (get_delta_difference): Do adjust for conversions to
and from virtual base.
Wed Feb 25 09:51:29 1998 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (get_delta_difference): Give hard error for conversion
from virtual base.
* cp-tree.h: Tweak formatting.
Wed Feb 25 00:35:33 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (push_namespace): Handle redeclaration error.
* cp-tree.h (IDENTIFIER_NAMESPACE_VALUE): New macro.
(IDENTIFIER_NAMESPACE_BINDINGS): New macro.
(NAMESPACE_BINDING): New macro.
(IDENTIFIER_GLOBAL_VALUE): Use NAMESPACE_BINDING.
* *.c: Use them.
* pt.c (push_template_decl): Use innermost_args.
* decl.c (get_unique_name): Tweak from earlier in the name.
Tue Feb 24 22:15:04 1998 Martin von Loewis <loewis@informatik.hu-berlin.de>
* cp-tree.def: Add CPLUS_BINDING node.
* cp-tree.h (tree_binding): New struct.
(BINDING_SCOPE, BINDING_VALUE): New macros.
(current_namespace, global_namespace): Declare extern.
(struct lang_decl_flags): New field in_namespace.
(DECL_NAMESPACE_USING, DECL_NAMESPACE_USERS): New macros.
(DECL_NAMESPACE, SET_DECL_NAMESPACE): New macros.
(TREE_INDIRECT_USING): New macro.
* decl2.c (current_namespace, global_namespace): Declare. The
value is a NAMESPACE_DECL now, not a TREE_LIST.
(is_namespace_ancestor, namespace_ancestor): New static functions.
(add_using_namespace, ambiguous_decl): Likewise.
(lookup_using_namespace): New support function for lookup_name.
(qualified_lookup_using_namespace): New support function for
do_scoped_id and lookup_namespace_name.
(get_namespace_id): Mark as obsolete.
(current_namespace_id): Likewise.
(do_namespace_alias): Implement.
(do_using_directive): Implement as call to add_using_namespace.
* decl.c (binding_for_name): New function.
(push_namespace, pop_namespace): Implement.
(push_decl): Don't install a FUNCTION_DECL in the global branch.
(lookup_namespace_name): Implement using qualified lookup.
(lookup_name_real): For global scoping, lookup in
global_namespace. For namespace scoping, lookup in given
namespace. For unscoped lookup, iterate over namespace,
considering using directives.
(init_decl_processing): Initialize global_namespace.
(grokvardecl): Build assembler name as static name for globals.
(grokdeclarator): Remove old namespace mangling.
(xref_tag): When installing a global binding for the
tag, make sure we have an identifier.
* method.c (build_overload_nested_name): Mangle namespaces.
(build_qualified_name): Likewise.
(build_decl_overload_real): Likewise.
* lex.c (build_lang_decl): Set namespace for new declaration to
current_namespace.
(do_scoped_id): Find global names in global or current
namespace, or using qualified namespace lookup, depending on
context.
* init.c (build_member_call): When scope is namespace, use
build_x_function_call instead.
(build_offset_ref): When scope is namespace, collapse processing
to lookup_namespace_name instead.
* error.c (dump_decl): Support NAMESPACE_DECL.
* decl.c (pushdecl): Bind globals to current namespace.
(push_overloaded_decl): Likewise.
(lookup_tag): Likewise.
(lookup_name_current_level): Likewise.
(xref_tag): Likewise.
(start_function): Likewise.
* lex.c (do_identifier): Likewise.
(identifier_typedecl_value): Likewise.
(real_yylex): Likewise.
* method.c (do_inline_function_hair): Likewise.
* parse.y (unscoped): Likewise.
* pt.c (check_explicit_specialization): Likewise.
(lookup_template_class): Likewise.
* rtti.c (call_void_fn): Likewise.
* sig.c (build_sigtable): Likewise.
* ptree.c (lang_print_xnode): New function.
Tue Feb 24 01:40:24 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Don't instantiate if pedantic
and the args use template parms.
* pt.c (push_tinst_level): If the instantiation uses template parms,
fail silently.
* decl.c (xref_basetypes): Do call complete_type for basetypes
that involve template parameters.
Tue Feb 24 00:36:43 1998 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (process_init_constructor): Fix labeled init check.
Mon Feb 23 05:08:55 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c, call.c, decl.c, method.c, cp-tree.h: Remove unused NARGS
argument to tsubst and friends.
* pt.c (tsubst, FUNCTION_DECL): Tidy.
* typeck.c (build_x_function_call): Handle static member function
templates like non-templates. Handle friend templates like normal
function templates.
* pt.c (tsubst, *_PARM): Don't use orig_level.
(get_bindings): Don't call add_to_template_args.
(instantiate_template): Likewise.
(tsubst, FUNCTION_DECL): Call add_to_template_args as appropriate.
* ptree.c (print_lang_type): Print index/level for template parms.
Mon Feb 23 02:52:29 1998 Mark Mitchell <mmitchell@usa.net>
* Make-lang.in (cc1plus): Note that cc1plus depends on
cp/cp-tree.h and cp/cp-tree.def.
* cp-tree.def (TEMPLATE_CONST_PARM): Remove.
(TEMPLATE_PARM_INDEX): New tree code, used to indicate a
position in a template parameter list.
* cp-tree.h (template_parm_index): New structure, used as the tree
structure for a TEMPLATE_PARM_INDEX.
(TEMPLATE_PARM_IDX): New macro.
(TEMPLATE_PARM_LEVEL): Likewise.
(TEMPLATE_PARM_DESCENDANTS): Likewise.
(TEMPLATE_PARM_ORIG_LEVEL): Likewise.
(TEMPLATE_PARM_DECL): Likewise.
(TEMPLATE_TYPE_PARM_INDEX): Likewise.
(TEMPLATE_TYPE_ORIG_LEVEL): Likewise.
(TEMPLATE_TYPE_DECL): Likewise.
(TEMPLATE_CONST_IDX): Remove.
(TEMPLATE_CONST_LEVEL): Likewise.
(TEMPLATE_CONST_SET_INFO): Likewise.
(TEMPLATE_TYPE_SET_INFO): Likewise.
(TEMPLATE_TYPE_IDX): Redefine in terms of TEMPLATE_PARM_INDEX
node.
(TEMPLATE_TYPE_LEVEL): Likewise.
* decl.c (decls_match): Call comp_template_parms, rather than
expanding it inline.
(duplicate_decls): If two template declarations are being merged,
then their TEMPLATE_INFOs should be merged as well.
(grokfndecl): Save template-id information when declaring a friend
with explicit template arguments. Pass arguments to
check_explicit_specialization via correct convention; at some
point check_explicit_specialization changed, but these call-sites
did not.
(grokdeclarator): Tidy up slightly.
* decl2.c (check_classfn): Tidy up slightly. Don't assume that
two template functions with the same DECL_ASSEMBLER_NAME the same,
since the names are not yet mangled.
* error.c (dump_decl): Use TEMPLATE_PARM_INDEX instead of
TEMPLATE_CONST_PARM.
(dump_expr): Likewise. Use the TEMPLATE_PARM_DECL to get at the
decl for a non-type parameter, rather than printing `<tparm ...>'.
* friend.c (is_friend): Handle TEMPLATE_DECL friends.
(do_friend): Deal with template friends.
* lex.c (do_pending_inlines): Call
maybe_begin_member_template_processing, rather than
conditionally calling begin_member_template_processing.
(process_next_inline): Likewise. Call
maybe_end_member_template_processing, rather than
conditionally calling end_member_template_processing.
(do_pending_defargs): Likewise.
(do_identifier): Use TEMPLATE_PARM_INDEX instead of
TEMPLATE_CONST_PARM.
* method.c (build_mangled_template_parm_index): New function.
(build_overload_value): Use it.
(build_overload_name): Likewise.
* pt.c (finish_member_template_decl): Allow friend declarations.
(template_class_depth): New function.
(is_member_template): Rename, and modify, to become...
(is_member_or_friend_template): New function.
(end_member_template_processing): Rename, and modify, to become...
(maybe_end_member_template_processing).
(build_template_parm_index): New function.
(reduce_template_parm_level): New function.
(process_template_parm): Modify to use build_template_parm_index.
(push_template_decl): Deal with friend templates.
(uses_template_parms): Use TEMPLATE_PARM_INDEX instead of
TEMPLATE_CONST_PARM.
(tsubst_friend_function): New function.
(instantiate_class_template): Generate the DECL_FRIENDLIST
for a new instantiation by using tsubst_friend_function rather
than just tsubst.
(tsubst): Don't tsubst into a type which is a TEMPLATE_DECL.
Use TEMPLATE_PARM_INDEX instead of TEMPLATE_CONST_PARM, and the
appropriate new macros. Use reduce_template_parm_level to
generate lower-level template parameters. Handle tsubst'ing into
TEMPLATE_DECLS that declare TEMPLATE_TEMPLATE_PARMS. Don't forget
to tsubst the DECL_CONTEXT and DECL_CLASS_CONTEXT of newly created
templates. Similarly for the template parameters for a new
template.
(tsubst_copy): Tidy up slightly. Use TEMPLATE_PARM_INDEX instead
of TEMPLATE_CONST_PARM. Handle TYPE_DECLs by tsubsting into them.
(unify): Use TEMPLATE_PARM_INDEX instead of TEMPLATE_CONST_PARM.
(get_bindings): Call add_to_template_args if necessary.
(instantiate_decl): Handle instantiations of friend templates.
* search.c (lookup_field_1): Don't treat the TYPE_FIELDS of a
TEMPLATE_TYPE_PARM as a list of fields; it's not!
* spew.c (yylex): Do a little manual constant propagation to
clarify the code.
Sun Feb 22 19:53:29 1998 Jeffrey A Law (law@cygnus.com)
* error.c: Include sys/types.h.
Thu Feb 19 14:49:09 1998 Jeffrey A Law (law@cygnus.com)
* method.c (build_mangled_name): Start CPP directives in column zero.
Thu Feb 19 10:36:48 1998 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (process_init_constructor): Sorry about non-trivial
labeled initializers.
* parse.y (initlist): Re-enable labeled initializers.
Thu Feb 19 10:15:55 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* pt.c (coerce_template_parms): Add a new parameter, is_tmpl_parm,
all callers changed. Rely on the new parameter instead of arg
being a TREE_LIST when determine whether we are working inside
template template parameter. Clean up is_type test.
Thu Feb 19 10:04:12 1998 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert_to_pointer): Preserve TREE_CONSTANT.
* typeck2.c (initializer_constant_valid_p): Allow conversions
between pointers and references.
1998-02-19 Brendan Kehoe <brendan@cygnus.com>
* typeck.c (build_unary_op): Only warn about incr/decr a pointer
if pedantic || warn_pointer_arith.
Thu Feb 19 09:37:21 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* pt.c (unify): Handle TEMPLATE_DECL.
1998-02-18 Brendan Kehoe <brendan@cygnus.com>
* cp-tree.h (strip_attrs): Remove decl.
1998-02-18 Doug Evans <devans@cygnus.com>
* decl.c (duplicate_decls): Call merge_machine_decl_attributes.
Update olddecl's attributes too.
(strip_attrs): Remove function.
* typeck.c (common_type): Call merge_machine_type_attributes.
Tue Feb 17 14:07:52 1998 Mark Mitchell <mmitchell@usa.net>
* parse.y (initdcl0_innards): New grammar symbol.
(nomods_initdecls, nomods_initdcl0): Change type from itype to
none, since the resulting value is never used.
(parse_decl): New function.
(datadef): Remove redundant actions.
(initdcl0, notype_initdcl0, nomods_initdcl0): Use initdcl0_innards.
* parse.c: Regenerated.
Tue Feb 17 11:54:16 1998 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (simple_stmt): Use getdecls() to check for decl.
Sat Feb 14 11:50:51 1998 Manfred Hollstein <manfred@s-direktnet.de>
* Make-lang.in (DEMANGLER_INSTALL_NAME, DEMANGLER_CROSS_NAME): New
macros.
(c++.install-common): Install c++filt properly as native or as cross
variant.
(c++.uninstall): Add c++filt.
Fri Feb 13 14:55:37 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (standard_conversion): Fix multi-level ptr conversions.
Fri Feb 13 14:06:22 1998 Mike Stump <mrs@wrs.com>
* init.c (build_new): Propagate error_mark_node up.
Fri Feb 13 13:24:32 1998 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (simple_stmt): If the condition isn't a declaration,
start the controlled block after the test.
Fri Feb 13 02:26:10 1998 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* call.c (build_over_call): Convert builtin abs, labs and fabs to
tree-codes.
* decl.c (init_decl_processing): Re-enable abs, labs and fabs as
builtins.
Fri Feb 13 01:36:42 1998 Jason Merrill <jason@yorick.cygnus.com>
* call.c (standard_conversion): A BASE_CONV replaces an RVALUE_CONV.
Fri Feb 13 00:21:59 1998 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h: Add access_protected_virtual_node.
* class.c (init_class_processing): Initialize it.
* decl.c (xref_basetypes): Use it.
* parse.y (base_class_access_list): Likewise.
* Make-lang.in (DEMANGLER_PROG): Add $(exeext).
(c++.install-common): Install c++filt.
Thu Feb 12 12:46:51 1998 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* decl.c (shadow_tag): Give error for typedef-ing built-in types.
Wed Feb 11 23:28:05 1998 Mark Mitchell <mmitchell@usa.net>
* call.c (reference_binding): Use comptypes when comparing
TYPE_MAIN_VARIANTS to handle non-canonical array/index types.
Wed Feb 11 16:42:04 1998 Mark Mitchell <mmitchell@usa.net>
* tree.c (is_overloaded_fn): Use really_overloaded_fn.
(really_overloaded_fn): Move check here from is_overloaded_fn.
(get_first_fn): Use really_overloaded_fn and is_overloaded_fn.
Wed Feb 11 15:54:18 1998 Mark Mitchell <mmitchell@usa.net>
* typeck.c (build_ptrmemfunc): Type-check pointer-to-member
conversions.
Mon Feb 9 22:23:31 1998 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (push_template_decl): Return the decl passed in, or an
equivalent duplicate.
* decl.c (pushtag): Use the return value from push_template_decl.
(duplicate_decls): When duplicating a template declaration, merge
the DECL_TEMPLATE_RESULTs as well.
(make_implicit_typename): Don't try to dive into typename types to
find a context for making a new implicit typename.
(start_decl): Use the return value from push_template_decl.
(grokdeclarator): Complain about declarations list `const operator
int'. Since we don't correctly handle in-class initializations of
non-static data members, complain about this (now illegal)
practice. Issue an error for initializations of non-const statics
since that is illegal as well, and since we don't handle that case
correctly either.
(start_function): Use the return value from push_template_decl.
(start_method): Likewise.
* decl2.c (grokfield): Likewise. Since the change to
grokdeclarator ensures that all initialized fields are in fact
static, remove a redundant test for TREE_PUBLIC.
* parse.y (initlist): Disable labeled initializers since they do
not work as per the documentation, and since they do not use the
same syntax as the C front end.
* pt.c (push_template_decl): Return the decl passed in, or an
equivalent duplicate.
(lookup_template_class): When searching in a nested context,
use the right arguments.
(uses_template_parms): Handle the DECL_INITIAL for a CONST_DECL.
* typeck.c (build_component_ref): Assign the correct type to the
result of build_vfn_ref.
Tue Feb 10 23:56:46 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (convert_nontype_argument): Fix typo.
(check_explicit_specialization): Allow old-style specialization
of class template members.
Tue Feb 10 20:36:52 1998 Jason Merrill <jason@yorick.cygnus.com>
Manfred Hollstein <manfred@s-direktnet.de>
* decl.c (grokdeclarator): Use DECL_USE_TEMPLATE instead
when deciding to override DECL_ASSEMBLER_NAME.
Tue Feb 10 15:30:55 1998 Andrew MacLeod <amacleod@torpedo.to.cygnus.com>
* decl2.c (lang_f_options): Add -fsquangle to option processing list.
* cp-tree.h (flag_do_squangling): Add declaration.
* lang-options.h: Add -fsquangle and -fno-squangle.
* method.c: Add macros and static variables for squangling.
(build_overload_name): Rename to build_mangled_name, add logic for B
compression, and split into process_modifiers and
process_overload_item.
(process_modifiers): New function, to handle constant, reference,
and pointer types.
(process_overload_item): New function, handles issue of type codes.
(build_overload_name): New function, start squangling and call
build_mangled_name.
(ALLOCATE_TYPEVEC, DEALLOCATE_TYPEVEC): Remove macro and expand inline.
(start_squangling): New function to initialize squangling structs.
(end_squangling): New function to destroy squangling structs.
(nrepeats): Rename variable to Nrepeats.
(issue_nrepeats): New function for issuing 'n' type repeats.
(check_ktype): New function to check for type K name compression.
(build_overload_nested_name): Add a check for K name compression.
(build_qualified_name): Add a check for K name compression and don't
use DECL_ASSEMBLER_NAME when squangling is on.
(check_btype): New function, checks for B type compression.
(build_static_name, build_decl_overload_real): Initiate squangling.
(build_typename_overload, build_overload_with_type): Initiate
squangling
Sun Feb 8 23:47:38 1998 scott snyder <sss@d0linux01.fnal.gov>
* method.c (make_thunk): Avoid name buffer overflow.
Sat Feb 7 16:48:54 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Call cp_finish_decl for vars even if we
don't define them yet.
* parse.y (nomods_initdcl0): Add constructor_declarator case.
Fri Feb 6 21:32:25 1998 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
* config-lang.in (diff_excludes): Use basename only.
Thu Feb 5 19:10:40 1998 Jason Merrill <jason@yorick.cygnus.com>
* tinfo2.cc: Add tinfo for signed char.
Thu Feb 5 14:38:23 1998 Mike Stump <mrs@wrs.com>
* search.c (compute_access): Handle protected constructors in derived
classes as accessible.
Wed Feb 4 01:26:49 1998 Jason Merrill <jason@yorick.cygnus.com>
* expr.c (cplus_expand_expr, PCC_STATIC_STRUCT_RETURN code):
Call convert_from_reference sooner.
Tue Feb 3 23:50:52 1998 Mark Mitchell <mmitchell@usa.net>
* cvt.c (ocp_convert): Obtain the constant values from constant
decls even if the destination type is the same as the type of the
decl.
* decl2.c (finish_file): Make sure that static inlines with
definitions are not marked DECL_EXTERNAL before returning.
Tue Feb 3 22:43:42 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c: Lose arg_looking_for_template.
(lookup_name_real): Likewise.
* parse.y: Lose processing_template_arg, template_arg1.
(primary): Likewise.
* spew.c (yylex): Set lastiddecl for PTYPENAMEs, too.
Tue Feb 3 22:04:01 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* error.c (dump_decl): Fix type of default arguments for template
template parameters and nontype template parameters.
* parse.y (template_parm): Handle invalid default template
template arguments here.
* parse.y (template_parm): Use template_arg instead of PTYPENAME
for default template template argument.
* pt.c (coerce_template_parms): Merge default template argument
codes. Can treat RECORD_TYPE as template name if it is implicitly
created. Fix argument index in error message.
* typeck.c (comptypes): Merge template argument comparison codes in
TEMPLATE_TEMPLATE_PARM and RECORD_TYPE.
Tue Jan 6 01:42:44 1998 Mumit Khan <khan@xraylith.wisc.edu>
* lex.c (file_name_nondirectory): Also check for '/'.
Mon Feb 2 11:24:22 1998 Mark Mitchell <mmitchell@usa.net>
* parse.y (primary): Deal with statement-expressions in
templates.
* pt.c (tsubst_copy): Handle BIND_EXPR.
* tree.c (mapcar): Likewise.
* call.c (add_template_candidate_real): Pass extra parameter to
fn_type_unification.
* cp-tree.h (fn_type_unification): Add parameter.
* pt.c (fn_type_unification): Add additional parameter to deal with
static member functions.
(get_bindings): Deal with static member functions.
* cp-tree.h (DECL_NONSTATIC_MEMBER_FUNCTION_P): New macro.
(revert_static_member_fn): Declare.
* decl.c (revert_static_member_fn): Remove declaration. Change
linkage from internal to external.
(cp_finish_decl): Deal with virtual functions in classes local to
template functions.
* decl2.c (finish_file): Don't forget to emit increment/decrement
expressions in initializers for file-scope variables.
* parse.y (typename_sub2): If the typename doesn't names a
template, rather than a type, issue an error message.
* pt.c (check_explicit_specialization): Handle specializations of
static member functions.
(coerce_template_parms): Handle offset references to lists of
member functions.
* search.c (note_debug_info_needed): Don't crash when handed a
type which is being defined.
* typeck.c (complete_type): Don't crash when handed NULL_TREE;
that can happen with some illegal code.
Mon Feb 2 00:57:38 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* call.c (user_harshness): Initialize `code' to 0.
(build_method_call): Initialize `candidates', `cp' and `len' to 0.
(null_ptr_cst_p): Add parentheses around && within ||.
(standard_conversion): Likewise.
(z_candidate): Likewise.
(build_user_type_conversion_1): Initialize `args' to NULL_TREE.
(build_object_call): Likewise for `mem_args'.
(build_new_op): Likewise for `mem_arglist'. Add `return' from
default case in enumeration switch.
* class.c (build_vtable_entry): Add explicit braces to avoid
ambiguous `else'.
(build_class_init_list): Likewise.
(finish_struct_1): Initialize `width' to 0.
(instantiate_type): Initialize `name' to NULL_TREE. Add
explicit braces to avoid ambiguous `else'.
* cvt.c (convert_to_aggr): Add explicit braces to avoid ambiguous
`else'.
* decl.c (grok_reference_init): Eliminate unused parameter, all
callers changed.
(record_builtin_type): Initialize `tdecl' to NULL_TREE.
(init_decl_processing): Initialize `vb_off_identifier' to NULL_TREE.
(cp_finish_decl): Initialize `ttype' to NULL_TREE.
(grokdeclarator): Add parentheses around && within ||. Add
explicit braces to avoid ambiguous `else'.
(grokparms): Initialize `type' to NULL_TREE.
(xref_tag): Remove unused label `just_return'.
(finish_enum): Initialize `minnode' and `maxnode' to NULL_TREE.
(finish_function): Initialize `cond' and `thenclause' to NULL_TREE.
(hack_incomplete_structures): Add parentheses around assignment
used as truth value.
* decl2.c (coerce_delete_type): Hide definition of `e3'.
* error.c: Include <stdlib.h>.
(dump_expr): Change the type of `i' to size_t. Remove unused
label `error'.
* except.c (init_exception_processing): Remove unused variable `d'.
(expand_throw): Likewise for `label'.
* friend.c (add_friends): Add explicit braces to avoid ambiguous
`else'.
* init.c (sort_member_init): Initialize `last_field' to NULL_TREE.
(sort_base_init): Likewise for `binfo'.
(expand_member_init): Likewise for `rval'.
(build_member_call): Add parentheses around assignment used as
truth value.
(build_offset_ref): Add explicit braces to avoid ambiguous `else'.
(build_new): Initialize `nelts' to NULL_TREE. Initialize
`old_immediate_size_expand' to 0.
(build_new_1): Initialize `nelts' and `alloc_node' to NULL_TREE.
(build_vec_delete_1): Remove unused variable `block'.
(expand_vec_init): Initialize `itype' to NULL_TREE.
* lex.c: Include <strings.h> if we don't have <string.h>. Protect
declaration of `index' and `rindex' with autoconf macros.
(reinit_parse_for_expr): Remove unused variables
`look_for_semicolon' and `look_for_lbrac'.
(cons_up_default_function): Initialize `args' to NULL_TREE.
(readescape): Initialize `firstdig' to 0.
(real_yylex): Add parentheses around assignment used as truth value.
* method.c: Include <strings.h> if we don't have <string.h>.
Protect declaration of `index' with autoconf macro.
* parse.y (primary): Add explicit braces to avoid ambiguous `else'.
Initialize `type' to NULL_TREE.
(structsp): Remove unused variable `id'.
* pt.c (coerce_template_parms): Add explicit braces to avoid
ambiguous `else'.
(lookup_template_class): Initialize `template' to NULL_TREE.
(instantiate_class_template): Remove unused variable `name' and `e'.
(tsubst): Likewise for `i'. Initialize `last' to NULL_TREE.
(do_poplevel): Initialize `saved_warn_unused' to 0.
(type_unification): Remove unused varable `parm'.
(unify): Likewise for `j'.
* repo.c (init_repo): Add parentheses around assignment used as
truth value.
(finish_repo): Remove unused varable `p'.
* search.c (get_binfo): Initialize `type' to NULL_TREE.
(get_base_distance): Likewise.
(lookup_field): Initialize `rval_binfo_h', `type', `basetype_path'
and `new_v' to NULL_TREE.
(lookup_fnfields): Likewise for `rval_binfo_h'.
(breadth_first_search): Add parentheses around assignment used as
truth value.
(get_template_base): Initialize `type' to NULL_TREE.
* sig.c (append_signature_fields): Initialize `last_mfptr' to
NULL_TREE.
(build_signature_table_constructor): Likewise for
`last_rhs_field', `pfn' and `vt_off'.
(build_sigtable): Likewise for `init'.
* tree.c (break_out_calls): Initialize `t2' to NULL_TREE.
(propagate_binfo_offsets): Likewise for `delta'.
(hash_tree_cons): Initialize hashcode to 0.
(can_free): Likewise for `size'.
(cp_tree_equal): Add explicit braces to avoid ambiguous `else'.
* typeck.c (convert_sequence): Hide prototype.
(common_type): Add explicit braces to avoid ambiguous `else'.
(comp_target_types): Likewise.
(build_x_function_call): Initialize `ctypeptr' to NULL_TREE.
(build_function_call_real): Add explicit braces to avoid ambiguous
`else'.
(convert_arguments): Initialize `called_thing' to 0.
(convert_for_initialization): Initialize `savew' and `savee' to 0.
* typeck2.c (incomplete_type_error): Initialize `errmsg' to 0.
(digest_init): Initialize `old_tail_contents' to NULL_TREE.
(build_x_arrow): Likewise for `last_rval'.
* xref.c (GNU_xref_decl): Initialize `cls' to 0.
Sun Feb 1 12:45:34 1998 J"orn Rennecke <amylaar@cygnus.co.uk>
* decl.c (init_decl_processing): Use set_sizetype.
* decl2.c (sizetype): Don't declare.
* typeck.c (c_sizeof): Convert result of *_DIV_EXPR to sizetype.
(c_sizeof_nowarn, build_binary_op_nodefault): Likewise.
(build_component_addr, unary_complex_lvalue): Likewise.
* rtti.c (expand_class_desc): Likewise.
* class.c (get_vfield_offset): Likewise.
Thu Jan 29 10:39:30 1998 Mark Mitchell <mmitchell@usa.net>
* pt.c (convert_nontype_argument): Move check for is_overloaded_fn
early to avoid bogus error. Handle overloaded function
names provided as template arguments correctly.
(coerce_template_parms): Don't mishandle overloaded functions when
dealing with template template parameters.
(lookup_template_class): Issue an error message, rather than
crashing, when the TYPE_DECL provided is not a template type.
Wed Jan 28 23:14:44 1998 Jason Merrill <jason@yorick.cygnus.com>
* class.c (instantiate_type): Don't just return a known type if
it's wrong.
Wed Jan 28 11:04:07 1998 Mark Mitchell <mmitchell@usa.net>
* class.c (instantiate_type): Remove handling of FUNCTION_DECL
since that code could never be reached.
* error.c (dump_decl): Avoid aborting in the midst of printing an
error message about an illegal template declaration.
* parse.y (structsp): Print an error message, rather than crashing,
when a class-head does not name a class.
* pt.c (convert_nontype_argument): Allow REAL_TYPE and COMPLEX_TYPE
template arguments as a g++ extension.
* cp-tree.def (ALIGNOF_EXPR): New tree code.
* decl2.c (grok_alignof): If processing_template_decl, just store
the expression.
* typeck.c (c_alignof): Likewise.
* decl2.c (build_expr_from_tree): Handle ALIGNOF_EXPR.
* error.c (dump_expr): Likewise.
* pt.c (tsubst_copy): Likewise.
* tree.c (cp_tree_equal): Likewise.
* pt.c (uses_template_parms): Correctly determine whether or not a
SIZEOF_EXPR/ALIGNOF_EXPR uses template parameters so that constant
folding can be done.
* cp-tree.h (grok_enum_decls): Remove type parameter.
* decl.c (grok_enum_decls): Likewise.
* decl2.c (grok_x_components): Call grok_enum_decls
unconditionally, since it will do nothing if there is no
current_local_enum. Use the new calling sequence.
* pt.c (tsubst_enum): Use the new calling sequence for
grok_enum_decls.
* decl.c (start_function): Make member functions of local classes
in extern inline functions have comdat linkage here...
(grokdeclarator): Rather than here.
Wed Jan 28 10:55:47 1998 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (convert_nontype_argument): Use decl_constant_value.
Tue Jan 27 16:42:21 1998 Mark Mitchell <mmitchell@usa.net>
* call.c (add_template_candidate_real): New function.
(add_template_candidate): Use it.
(add_template_conv_candidate): Likewise.
(joust): Pass extra argument to more_specialized.
* class.c (instantiate_type): Handle a single FUNCTION_DECL.
(is_local_class): Remove.
(finish_struct): Check TI_PENDING_SPECIALIZATION_FLAG.
* cp-tree.h (is_local_class): Remove.
(perform_array_to_pointer_conversion): Likewise.
(finish_member_template_decl): Add.
(check_explicit_specialization): Return a tree, not an int.
(more_specialized): Take additional argument.
(get_bindings): Likewise.
(TI_PENDING_SPECIALIZATION_FLAG): New macro.
* cvt.c (perform_qualification_conversions): Use comp_ptr_ttypes.
(perform_array_to_pointer_conversion): Remove.
* decl.c (saved_scope): Add processing_specialization,
processing_explicit_instantiation fields.
(maybe_push_to_top_level): Save them.
(pop_from_top_level): Restore them.
(grokfndecl): Use new return value from
check_explicit_specialization.
(start_decl): Don't check flag_guiding_decls before pushing
decls.
(cp_finish_decl): Remove previous (bogus) change.
(grok_declarator): Use decl_function_context rather than
is_local_class.
* decl2.c (finish_file): Pass extra argument to get_bindings.
(build_expr_from_tree): Let build_x_component_ref check
validity of arguments rather than doing it here.
* lex.c (cons_up_default_function): Remove code fooling with
processing_specialization, processing_explicit_instantiation
flags, as that is now done in {maybe_push_top,pop_from}_top_level.
* method.c (build_overload_identifier): Mangle local classes in
template functions correctly.
* parse.y (finish_member_template_decl): Move to pt.c.
* pt.c (finish_member_template_decl): Moved here from parse.y.
(print_candidates): New function.
(determine_specialization): Change interface. Properly look for
most specialized versions of template candidates.
(check_explicit_specialization): Fully process explicit
instantiations.
(push_template_decl): Avoid looking at CLASSTYPE fields in
FUNCTION_DECLS.
(determine_overloaded_function): Remove.
(convert_nontype_argument): Change name from
convert_nontype_parameter. Use determine_overloaded_function
instead of instantiate_type.
(mangle_class_name_for_template): Handle type contexts as well as
function contexts.
(classtype_mangled_name): Likewise.
(lookup_template_class): Likewise.
(tsubst): Likewise.
(more_specialized): Take explict template arguments as a
parameter.
(most_specialized): Likewise.
(get_bindings): Likewise. Check that return types match before
proclaiming a function a match.
(do_decl_instantiation): Remove code searching for function to
instantiate; that is now done in check_explicit_specialization.
(add_maybe_template): Pass extra argument to get_bindings.
* tree.c (really_overloaded_fn): Use is_overloaded_fn to simplify
implementation.
* typeck.c (build_component_ref): Check for invalid arguments.
Tue Jan 27 01:44:02 1998 Jason Merrill <jason@yorick.cygnus.com>
* expr.c (cplus_expand_expr, AGGR_INIT_EXPR): Don't check that
return_target and call_target are equivalent.
* pt.c (type_unification_real): Just accept function parms that
don't use any template parms.
Sun Jan 25 03:30:00 1998 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (cp_finish_decl): When bailing on a comdat variable, also
unset DECL_NOT_REALLY_EXTERN.
* parse.y (typename_sub*): Fix std::.
Sat Jan 24 12:13:54 1998 Jason Merrill <jason@yorick.cygnus.com>
* error.c (dump_decl): Fix type default template args.
(dump_type): Hand TEMPLATE_DECL off to dump_decl.
Fri Jan 23 18:34:37 1998 Mumit Khan <khan@xraylith.wisc.edu>
* lex.c (DIR_SEPARATOR): Define to be '/' if not already defined.
(file_name_nondirectory): Use.
Wed Jan 21 10:29:57 1998 Kriang Lerdsuwanakij <lerdsuwa@scf.usc.edu>
* pt.c (coerce_template_parms): Don't access elements of ARGLIST
that are not really present. Substitute default arguments in
template template arguments. Correctly convert TEMPLATE_DECL to
TEMPLATE_TEMPLATE_PARM.
(comp_template_args): TEMPLATE_DECL and TEMPLATE_TEMPLATE_PARM
are no longer treated specially here.
* parse.y (template_template_parm): Fix copy error.
* decl.c (grokdeclarator): Warn about missing `typename' for nested
type created from template template parameters.
* parse.y (bad_parm): Likewise
* class.c (finish_struct): Handle TEMPLATE_TEMPLATE_PARM.
(push_nested_class): Likewise.
* cp-tree.def (TEMPLATE_TEMPLATE_PARM): New tree code.
* cp-tree.h (DECL_TEMPLATE_TEMPLATE_PARM_P): New macro.
(copy_template_template_parm): Declare.
* decl.c (arg_looking_for_template): New variable.
(lookup_name_real): Handle TEMPLATE_TEMPLATE_PARM.
Try to return TEMPLATE_DECL or TEMPLATE_TEMPLATE_PARM
node if arg_looking_for_template is nonzero.
(pushdecl): Handle TEMPLATE_TEMPLATE_PARM.
(grok_op_properties, xref_tag, xref_basetypes): Likewise.
(grokdeclarator): Handle TEMPLATE_DECL.
* decl2.c (constructor_name_full): Handle TEMPLATE_TEMPLATE_PARM.
* error.c (dump_type): Add TEMPLATE_DECL and TEMPLATE_TEMPLATE_PARM.
(dump_type_prefix, dump_type_suffix): Handle TEMPLATE_TEMPLATE_PARM.
(dump_decl): Handle unnamed template type parameters.
Handle template template parameters.
(dump_function_name): Handle template template parameters.
* init.c (is_aggr_typedef, is_aggr_type, get_aggr_from_typedef):
Handle TEMPLATE_TEMPLATE_PARM.
* method.c (build_template_template_parm_names): New function.
(build_template_parm_names): Handle TEMPLATE_DECL.
(build_overload_nested_name, build_overload_name):
Handle TEMPLATE_TEMPLATE_PARM.
* parse.y (maybe_identifier): New nonterminal.
(template_type_parm): Use it.
(template_template_parm, template_arg1): New nonterminal.
(template_parm): Add template_template_parm rules.
(template_arg): Set processing_template_arg.
(template_arg1): Rules moved from template_arg.
(primary, nonnested_type): Set arg_looking_for_template if we are
processing template arguments.
* pt.c (begin_member_template_processing): Handle TEMPLATE_DECL.
(process_template_parm): Handle template template parameters.
(coerce_template_parms, comp_template_args): Likewise.
(mangle_class_name_for_template, lookup_template_class): Likewise.
(uses_template_parms): Handle TEMPLATE_DECL and
TEMPLATE_TEMPLATE_PARM.
(current_template_args): Handle TEMPLATE_DECL.
(tsubst, tsubst_copy, unify): Handle TEMPLATE_TEMPLATE_PARM.
* search.c (dfs_walk, dfs_record_inheritance):
Handle TEMPLATE_TEMPLATE_PARM.
* tree.c (copy_template_template_parm): New function.
(mapcar): Handle TEMPLATE_TEMPLATE_PARM.
* typeck.c (comptypes): Handle TEMPLATE_TEMPLATE_PARM.
Mon Jan 19 22:40:03 1998 Mark Mitchell <mmitchell@usa.net>
* decl.c (start_decl): Don't allow duplicate definitions of static
data members.
* call.c (build_user_type_conversion_1): Handle user-defined
template conversion operators correctly.
* decl2.c (build_expr_from_tree): Issue an error message if the
object in a COMPONENT_REF is a TEMPLATE_DECL.
* typeck.c (incomplete_type_error): Handle TEMPLATE_TYPE_PARMs.
* class.c (is_local_class): New function.
* cp-tree.h (is_local_class): Declare it.
(last_tree): Likewise.
(begin_tree): Likewise.
(end_tree): Likewise.
(lookup_template_class): Change prototype.
* decl.c (cp_finish_decl): Check for NULL where necessary.
Consider FUNCTION_DECLS to declare objects with top-level binding,
when calling make_decl_rtl.
(grokdeclarator): Give members of local classes internal linkage.
(start_function): Remove declaration of last_tree.
(finish_function): Set flag_keep_inline_functions around call to
rest_of_compilation if we are processing a member function in a
local class.
(start_method): Call push_template_decl for member functions of
local classes in template functions.
* decl2.c (import_export_decl): Don't give external linkage to
instantiations of templates with internal linkage.
* parse.y (last_tree): Remove declaration.
(template_type): Pass extra parameter to lookup_template_class.
(self_template_type): Likewise.
(structsp): Move call to reset_specialization into left_curly.
(left_curly): Call reset_specialization, and begin_tree.
* pt.c (saved_trees): New variable.
(mangle_class_name_for_template): Change prototype. Use
additional function context to name local classes in templates
correctly.
(classtype_mangled_name): Pass the context.
(push_template_decl): Handle local classes and templates, and
member functions for such classes.
(convert_nontype_parameter): Fix handling of pointer-to-member
constants.
(lookup_template_class): Handle local classes in templates.
(tsubst): Likewise. Don't assume that template instantiations
have external linkage; pay attention to the template declaration.
(mark_decl_instantiated): Likewise.
(begin_tree): New function.
(end_tree): Likewise.
* decl.c (xref_basetypes): Don't call complete_type for basetypes
that involve template parameters; that can lead to infinite
recursion unnecessarily.
* pt.c (register_specialization): Do not register specializations
that aren't ready to be registered yet.
(check_explicit_specialization): Handle explicit specialization of
constructors and destructors.
(build_template_decl): New function.
(push_template_delc): Handle out-of-class specializations of
member templates.
* pt.c (check_explicit_specialization): Set up the template
information before registering the specialization.
(coerce_template_parms): Fix thinko.
(tsubst): Handle specializations of member templates correctly.
* class.c (finish_struct_methods): Remove calls to
check_explicit_specialization from here.
(finish_struct): And insert them here.
* cp-tree.h (perform_qualification_conversions): New function.
(perform_array_to_pointer_conversion): Likewise.
(begin_explicit_instantiation): Likewise.
(end_explicit_instantiation): Likewise.
(determine_specialization): Renamed from
determine_explicit_specialization.
(comp_template_parms): New function.
(processing_explicit_instantiation): New variable.
* cvt.c (perform_qualification_conversions): New function.
(perform_array_to_pointer_conversion): Likewise.
* decl.c (duplicate_decls): Don't consider template functions
alike unless they have the same parameters. Refine handling of
instantiation/specialization mismatches.
(start_decl): Don't call pushdecl for template specializations,
since they don't affect overloading.
(start_function): Likewise.
(grokfndecl): Call check_explicit_specialization a little later.
Don't call duplicate_decls for memberm template specializations.
(grokdeclarator): Don't update template_count for classes that are
themselves specializations. Remove use of `2' as parameter to
grokfndecl since that value isn't used.
* lex.c (cons_up_default_function): Save and restore
processing_explicit_instantiation around calls to grokfield.
* parse.y (finish_member_template_decl): New function.
(component_decl_1): Use it.
(fn.def2): Likewise.
(template_arg_list_opt): New nonterminal.
(template_type): Use it.
(self_template_type): Likewise.
(template_id): Likewise.
(object_template_id): Likewise.
(notype_template_declarator): Likwise.
(begin_explicit_instantiation): Likewise.
(end_explicit_instantiation): Likewise.
(explicit_instantiation): Use them.
* pt.c (coerce_template_parms): Add parameters.
(processing_explicit_instantiation): New variable.
(convert_nontype_parameter): New function.
(determine_overloaded_function): Likewise.
(begin_explicit_instantiation): Likewise.
(end_explicit_instantiation): Likewise.
(retrieve_specialization): Likewise.
(register_specialization): Likewise.
(processing_explicit_specialization): Removed.
(determine_specialization): Handle specializations of member
functions of template class instantiations.
(check_explicit_specialization): Refine to conform to standard.
(comp_template_parms): New function.
(coerce_template_parms): Call convert_nontype_parameter.
(tsubst): Refine handling of member templates. Use
register_specialization.
(instantiate_template): Use retrieve_specialization.
(do_decl_instantiation): Likewise.
(instantiate_decl): Likewise.
(type_unification): Improve handling of explict template
arguments.
* tree.c (mapcar): Return error_mark_node, rather than aborting,
on VAR_DECLS, FUNCTION_DECLS, and CONST_DECLS.
* typeck.c (build_unary_op): Call determine_specialization, rather
than determine_explicit_specialization.
Mon Jan 19 13:18:51 1998 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_up_reference): A TARGET_EXPR has side effects.
Fri Jan 16 11:40:50 1998 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* error.c (dump_decl): For enum tags, output the tag, not its value.
1998-01-13 Brendan Kehoe <brendan@cygnus.com>
* decl.c (init_decl_processing): Only call init_rtti_processing
FLAG_RTTI is set.
Mon Jan 12 01:35:18 1998 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new_1): Split out from build_new.
(build_new): Just return a NEW_EXPR.
* expr.c (cplus_expand_expr): Handle NEW_EXPR.
* decl2.c (get_temp_regvar): Tweak.
* cp-tree.h (TREE_CALLS_NEW): Comment out.
* class.c (resolves_to_fixed_type_p): Remove use.
* method.c (build_opfncall): Likewise.
* call.c (build_new_op): Likewise.
Wed Jan 7 23:47:13 1998 Jason Merrill <jason@yorick.cygnus.com>
* exception.cc (__eh_alloc, __eh_free): New fns.
(__cp_push_exception, __cp_pop_exception): Use them.
(__uncatch_exception): Call terminate here if no exception.
* except.c (build_terminate_handler): New fn.
(expand_start_catch_block): Use it.
(expand_exception_blocks): Likewise.
(alloc_eh_object): New fn.
(expand_throw): Use it. Protect exception init with terminate.
* typeck.c (build_modify_expr): Remove code that ignores trivial
methods.
Mon Dec 22 11:36:27 1997 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* call.c (add_builtin_candidate): Add default case in enumeration
switch.
(build_new_op): Likewise.
(convert_like): Likewise.
* cvt.c (build_expr_type_conversion): Likewise.
* tree.c (real_lvalue_p): Likewise.
(lvalue_p): Likewise.
(cp_tree_equal): Likewise.
* typeck.c (comptypes): Likewise.
(build_component_ref): Likewise.
(build_function_call_real): Likewise.
(build_binary_op_nodefault): Likewise.
(build_unary_op): Likewise.
(build_modify_expr): Likewise.
* typeck2.c (initializer_constant_valid_p): Likewise.
Sun Dec 21 15:59:00 1997 Nick Clifton <nickc@cygnus.com>
* decl2.c (lang_decode_option): Add support for -Wunknown-pragmas.
Thu Dec 18 14:51:50 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (coerce_template_parms): Make sure to digest_init if
possible.
* decl.c (duplicate_decls): Make the newdecl virtual if the
olddecl was, just as is done with other attributes of olddecl.
Thu Dec 18 14:43:19 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (unary_complex_lvalue): Ignore op0 when taking the
address of an OFFSET_REF.
* cp-tree.def: Add AGGR_INIT_EXPR.
* error.c, tree.c, typeck.c: Replace uses of NEW_EXPR with
AGGR_INIT_EXPR where appropriate.
* expr.c (cplus_expand_expr): Likewise. Simplify.
* decl2.c (finish_file): Remove call to register_exception_table.
Wed Dec 17 17:08:52 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* pt.c (instantiate_class_template): Don't do injection when
processing_template_decl is true, as pollutes current_binding_level
for base classes.
Wed Dec 17 21:17:39 1997 Peter Schmid <schmid@ltoi.iap.physik.tu-darmstadt.de>
* pt.c (maybe_fold_nontype_arg): Add prototype.
Tue Dec 16 10:31:20 1997 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (mapcar): Handle TRY_CATCH_EXPR et al.
* error.c (dump_expr): Likewise.
Mon Dec 15 12:22:04 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_function_call_real): Remove "inline called before
definition" pedwarn.
* pt.c (coerce_template_parms): Use maybe_fold_nontype_arg.
Sun Dec 14 22:34:20 1997 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert_to_pointer): Fix base conversion of pm's.
* pt.c (type_unification_real): Change __null to type void* with
a warning.
Sun Dec 14 20:38:35 1997 Mark Mitchell <mmitchell@usa.net>
* call.c (implicit_conversion): Don't call
build_user_type_conversion_1 with a NULL expr, since it will
crash.
* pt.c (unify): Don't try to unify array bounds if either array is
unbounded.
Fri Dec 12 16:09:14 1997 Jason Merrill <jason@yorick.cygnus.com>
* errfn.c (cp_pedwarn, cp_pedwarn_at, cp_error_at, cp_warning_at):
Replace extern decls with casts.
* decl.c (expand_start_early_try_stmts): Don't mess with a sequence.
Update last_parm_cleanup_insn.
(store_after_parms): Remove.
* cp-tree.h: Adjust.
Thu Dec 11 22:18:37 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (comdat_linkage): Also set DECL_COMDAT.
(finish_file): Check DECL_COMDAT instead of weak|one_only.
(import_export_vtable): Use make_decl_one_only instead of
comdat_linkage for win32 tweak.
(import_export_decl): Likewise.
* pt.c (mark_decl_instantiated): Likewise.
* decl2.c (finish_file): Lose handling of templates in pending_statics.
Thu Dec 11 21:12:09 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Lose call to expand_builtin_throw.
* except.c (expand_builtin_throw): Remove.
* cp-tree.h: Remove ptr_ptr_type_node.
* decl.c: Likewise.
Thu Dec 11 20:43:33 1997 Teemu Torma <tot@trema.com>
* decl.c (ptr_ptr_type_node): Define.
(init_decl_processing): Initialize it.
* cp-tree.h: Declare it.
* exception.cc (__cp_exception_info): Use __get_eh_info.
(__cp_push_exception): Likewise.
(__cp_pop_exception): Likewise.
From Scott Snyder <snyder@d0sgif.fnal.gov>:
* except.c (expand_builtin_throw): Use get_saved_pc_ref instead of
saved_pc.
(init_exception_processing): Removed saved_pc initialization.
Wed Dec 10 11:04:45 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Defer all templates but inline functions.
Mon Dec 8 23:17:13 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (expand_vec_init): Don't fold a list of parameters.
* decl.c (copy_args_p): Handle copy elision for types with virtual
bases.
* call.c (build_over_call): Likewise.
Sun Dec 7 22:38:12 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (lookup_template_function): Copy the template arguments,
not just the list containing them, to the permanent obstack.
Sun Dec 7 15:53:06 1997 Jason Merrill <jason@yorick.cygnus.com>
* except.c (expand_start_catch_block): suspend_momentary for the
terminate handler.
* error.c (dump_decl): Handle LOOKUP_EXPR.
Sun Dec 7 15:45:07 1997 Mark Mitchell <mmitchell@usa.net>
* rtti.c (build_dynamic_cast): Copy the cast-to type to the
permanent obstack if we are processing a template decl.
* typeck.c (build_static_cast): Likewise.
(build_const_cast): Likewise.
(build_reinterpret_cast): Likewise.
* pt.c (coerce_template_parms): Coerce some expressions, even
when processing_template_decl.
Sun Dec 7 01:46:33 1997 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* typeck.c (build_binary_op_nodefault, pointer_diff): Symmetric
handling of pointer difference expressions.
* typeck.c (comp_target_types): Comparison of function/method types
is independent of nptrs.
Sun Dec 7 01:40:27 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (tsubst): Avoid creating pointer to reference and
reference to reference types.
Sat Dec 6 01:29:37 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (do_id): New nonterminal.
(template_id): Use it.
Fri Dec 5 01:17:34 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (template_id): do_identifier for PFUNCNAMEs, too.
* spew.c (yylex): Don't do_identifier here.
* decl2.c (build_expr_from_tree): Revert last change.
* decl2.c (build_expr_from_tree): Expand the name for a method call.
* parse.y (object_template_id): Don't try to take the DECL_NAME.
Wed Dec 3 20:02:39 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): Use a TARGET_EXPR instead of SAVE_EXPR for
alloc_expr.
* call.c (build_op_delete_call): Adjust.
* except.c (expand_end_catch_block): Lose rethrow region.
(expand_start_catch_block): Likewise.
(expand_end_catch_block): Don't expand_leftover_cleanups.
Wed Dec 3 13:24:04 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* pt.c (tsubst): Remove tree_cons call (places redundant info into
DECL_TEMPLATE_INSTANTIATION).
Wed Dec 3 11:44:52 1997 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (is_overloaded_fn): Handle getting a fn template.
(really_overloaded_fn): Likewise.
* error.c (dump_decl): Handle TEMPLATE_ID_EXPRs better.
* pt.c (check_explicit_specialization): Tweak.
(determine_explicit_specialization): Tweak.
* tree.c, cp-tree.h (get_target_expr): New fn.
Wed Dec 3 08:47:27 1997 Paul Eggert <eggert@twinsun.com>
* pt.c (check_explicit_specialization): Fix misspelling in
diagnostic: `preceeded'.
* typeck.c (get_delta_difference): Fix misspelling in diagnostic:
`conversiona'.
1997-12-02 Mark Mitchell <mmitchell@usa.net>
* pt.c (determine_explicit_specialization): Avoid an internal
error for bad specializations.
* method.c (build_overload_value): Handle SCOPE_REF.
Tue Dec 2 19:18:50 1997 Mike Stump <mrs@wrs.com>
* class.c (prepare_fresh_vtable): Enable even more complex MI
vtable names.
Tue Dec 2 01:37:19 1997 Jason Merrill <jason@yorick.cygnus.com>
* exception.cc (__check_eh_spec): Optimize a bit.
* exception.cc (__cp_pop_exception): Lose handler arg.
* except.c (do_pop_exception): Likewise.
(push_eh_cleanup): Let the cleanup mechanism supply the handler.
(expand_end_catch_block): Likewise.
Fri Nov 28 01:58:14 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (check_explicit_specialization): Complain about using a
template-id for a non-specialization.
Fri Nov 28 12:35:19 1997 Scott Christley <scottc@net-community.com>
* repo.c: Prototype rindex only if needed.
* xref.c: Likewise.
Fri Nov 28 01:56:35 1997 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* error.c (dump_decl): Handle TEMPLATE_ID_EXPR.
Thu Nov 27 00:59:46 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_const_cast): Handle references here instead of
handing off to convert_to_reference.
* except.c: Lose Unexpected, SetTerminate, SetUnexpected,
TerminateFunctionCall.
(init_exception_processing): Likewise. Terminate et al are now
the fns, not ADDR_EXPRs.
(various): Lose redundant assemble_external calls.
(do_unwind): s/BuiltinReturnAddress/builtin_return_address_fndecl/.
* cp-tree.h (struct lang_decl_flags): Add comdat.
(DECL_COMDAT): New macro.
* decl.c (duplicate_decls): Propagate it.
(cp_finish_decl): Handle it.
* decl2.c (import_export_decl): Just set DECL_COMDAT on VAR_DECLs.
* class.c: Remove static pending_hard_virtuals.
(add_virtual_function): Take pointers to pending_virtuals
and pending_hard_virtuals.
(finish_struct_1): Pass them. Declare pending_hard_virtuals.
Wed Nov 26 20:28:49 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_vtable): If we support one_only but not
weak symbols, mark instantiated template vtables one_only.
(import_export_decl): Likewise for tinfo functions.
(finish_vtable_vardecl): Also write out vtables from explicitly
instantiated template classes.
* pt.c (mark_class_instantiated): Revert last change.
* except.c (expand_throw): Call mark_used on the destructor.
Wed Nov 26 15:13:48 1997 Jeffrey A Law (law@cygnus.com)
* lex.c (lang_init): Enable flag_exceptions by default if no
command line switch was specified.
1997-11-26 Mark Mitchell <mmitchell@usa.net>
* pt.c (unify): Handle `void' template parameters in
specializations.
Wed Nov 26 01:11:24 1997 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (build_dynamic_cast): Handle template case here.
(build_dynamic_cast_1): Not here.
* typeck2.c (digest_init): Make copies where appropriate.
* decl2.c (delete_sanity): resolve_offset_ref.
* except.c: Call terminate without caching so many bits.
* except.c (expand_start_catch_block): Fix catching a reference
to pointer.
Tue Nov 25 11:28:21 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): Copy size to the saveable obstack.
* init.c (build_new): Stick a CLEANUP_POINT_EXPR inside the
TRY_CATCH_EXPR for now.
Mon Nov 24 12:15:55 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (mark_addressable): Don't assume a FUNCTION_DECL
has DECL_LANG_SPECIFIC.
* exception.cc (struct cp_eh_info): Add handlers field.
(__cp_push_exception): Initialize it.
(__cp_pop_exception): Decrement it. Don't pop unless it's 0.
(__throw_bad_exception): Remove.
* except.c (call_eh_info): Add handlers field.
(get_eh_handlers): New fn.
(push_eh_cleanup): Increment handlers.
Fri Nov 21 12:22:07 1997 Jason Merrill <jason@yorick.cygnus.com>
* except.c (expand_start_eh_spec): Use the try/catch code.
(expand_end_eh_spec): Likewise. Call __check_eh_spec instead of
doing everything inline.
(init_exception_processing): throw_type_match now takes
const void pointers.
* exception.cc (__check_eh_spec): New fn.
* inc/exception: Neither terminate nor unexpected return.
* decl.c: Make const_ptr_type_node public.
* tinfo2.cc (__throw_type_match_rtti): Take the typeinfos constly.
* except.c (expand_start_catch_block): We only need the rethrow
region for non-sjlj exceptions.
(expand_end_catch_block): Likewise. Use outer_context_label_stack.
Thu Nov 20 14:40:17 1997 Jason Merrill <jason@yorick.cygnus.com>
* Make-lang.in (CXX_LIB2FUNCS): Add new op new and op delete objs.
(various.o): Likewise.
* inc/new: Add placement deletes. Add throw specs for default new.
* new.cc (set_new_handler): Move here from libgcc2.
* new1.cc (new (nothrow)): Catch a bad_alloc thrown from the handler.
(new): Move from libgcc2. Throw bad_alloc.
* new2.cc: Move the rest of the op news and op deletes from libgcc2.
* decl.c (init_decl_processing): Update exception specs on new and
delete.
* method.c (build_decl_overload_real): Don't mess with global
placement delete.
* init.c (build_new): Check for null throw spec, not nothrow_t.
* decl.c (duplicate_decls): Don't complain about different exceptions
from an internal declaration.
* call.c (build_op_delete_call): Fix check for member fns again.
* decl2.c (import_export_decl): Interface hackery affects
virtual synthesized methods.
Wed Nov 19 18:24:14 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): Don't just complain about a mismatched
scope, fix it.
* decl.c (make_implicit_typename): Handle case where t is not
actually from context.
* tree.c (get_type_decl): Lose identifier case.
* spew.c (yylex): Lose useless call to identifier_typedecl_value.
* parse.y (nonnested_type): Just use lookup_name.
(complex_type_name): Just use IDENTIFIER_GLOBAL_VALUE.
Wed Nov 19 11:45:07 1997 Michael Tiemann <tiemann@axon.cygnus.com>
* error.c (dump_function_name): Test DECL_LANG_SPECIFIC in case
T was built in C language context (for example, by
output_func_start_profiler).
Wed Nov 19 10:39:27 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (make_implicit_typename): New fn.
(lookup_name_real): Use it. Use current_class_type as the context.
Mon Nov 17 23:42:03 1997 Bruno Haible <haible@ilog.fr>
* pt.c (do_poplevel): Don't prohibit jumps into this contour.
Mon Nov 17 02:01:28 1997 Jason Merrill <jason@yorick.cygnus.com>
* friend.c (do_friend): Warn about non-template friends in templates.
* call.c (build_op_delete_call): Fix handling of inherited delete.
* search.c (dfs_record_inheritance): Ignore template type parms.
Sat Nov 15 00:30:51 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_op): Fix copy error.
(build_op_new_call): New fn.
(build_op_delete_call): New fn.
* cp-tree.h: Declare them.
* init.c (build_new): Use them. Support placement delete.
(build_x_delete): Use build_op_delete_call.
(build_delete): Likewise.
* decl2.c (delete_sanity): Likewise.
(coerce_delete_type): Don't complain about placement delete.
Thu Nov 13 01:52:36 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_function_call): Remove unused 'obj' parm.
* cp-tree.h, typeck.c: Adjust.
* init.c (build_new): Make the cleanup last longer.
(expand_vec_init): Call do_pending_stack_adjust.
Wed Nov 12 11:04:33 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (do_type_instantiation): Fix typo.
(mark_class_instantiated): If we support one_only but not weak
symbols, don't mark this as known.
* init.c (build_new): Handle vec delete in EH cleanup.
Wed Nov 12 08:11:55 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* call.c (build_method_call): Call complete_type before checking
for destructor.
Sun Nov 9 01:29:55 1997 Jim Wilson (wilson@cygnus.com)
* decl.c (add_block_current_level): Delete.
* init.c (build_vec_delete_1): Delete build_block and
add_block_current_level calls.
Wed Nov 12 00:48:16 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): Handle freeing allocated memory when the
constructor throws.
* call.c (build_new_method_call): Fix flags arg.
* pt.c (do_type_instantiation): Don't try to instantiate
member templates.
(mark_decl_instantiated): If we support one_only but not
weak symbols, mark this one_only.
* decl2.c (import_export_vtable): Don't defer handling of vtables
if MULTIPLE_SYMBOL_SPACES.
Tue Nov 11 12:02:12 1997 Jason Merrill <jason@yorick.cygnus.com>
* except.c (expand_end_catch_block): Lose call to __sjpopnthrow.
Tue Nov 11 02:53:44 1997 Jason Merrill <jason@lasher.cygnus.com>
* except.c (do_pop_exception): Return a value.
Mon Nov 10 20:25:31 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_method_call): Handle getting a
TEMPLATE_ID_EXPR around a TEMPLATE_DECL. Don't look for a field
if we got template parms.
* typeck.c (build_x_function_call): Remember the TEMPLATE_ID_EXPR,
not just the args.
* decl2.c (build_expr_from_tree): Tweak last change.
* pt.c (tsubst_copy): Use get_first_fn instead of TREE_VALUE.
(maybe_fold_nontype_arg): Split out from tsubst_copy.
* tree.c (get_first_fn): Just return a TEMPLATE_ID_EXPR.
Mon Nov 10 20:08:38 1997 Kriang Lerdsuwanakij <lerdsuwa@scf-fs.usc.edu>
* pt.c (tsubst_copy): Handle explicit template arguments in
function calls.
* typeck.c (build_x_function_call): Likewise.
* decl2.c (build_expr_from_tree): Lookup function name if it
hasn't been done.
* pt.c (tsubst): Instantiate template functions properly when
template parameter does not appear in function arguments and return
type.
(comp_template_args): Handle member templates required by tsubst.
Mon Nov 10 20:08:38 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Tweak conditions for pedwarn in
previous change.
Mon Nov 10 20:08:29 1997 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* pt.c (coerce_template_parms): Tweak error message.
* decl.c (grokdeclarator): If -Wreturn-type, warn everytime a
return type defaults to `int', even if there are storage-class
specifiers.
Mon Nov 10 03:04:20 1997 Jason Merrill <jason@yorick.cygnus.com>
Complete nested exception support.
* except.c (do_pop_exception): Split out...
(push_eh_cleanup): From here. Handle the EH region by hand.
(expand_start_catch_block): Add a new level for the catch parm.
Move the rethrow region outside the two cleanup regions.
Protect the initializer for the catch parm with terminate.
(expand_end_catch_block): Likewise. End the region for the eh_cleanup.
* exception.cc (__cp_pop_exception): Now takes two parms. Handle
popping off the middle of the stack.
* tree.c (lvalue_p, real_lvalue_p): Handle TRY_CATCH_EXPR,
WITH_CLEANUP_EXPR, and UNSAVE_EXPR.
(build_cplus_new): Only wrap CALL_EXPRs.
* init.c (expand_default_init): Handle a TRY_CATCH_EXPR around
the constructor call.
Sun Nov 9 18:00:26 1997 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
* Make-lang.in (c++.distdir): Make inc subdirectory.
Fri Nov 7 11:57:28 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Put back some code.
Thu Nov 6 11:28:14 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Remove redundant code.
* method.c (emit_thunk): Don't let the backend defer generic thunks.
Wed Nov 5 23:52:50 1997 Jason Merrill <jason@yorick.cygnus.com>
* except.c (call_eh_info): Split out...
(push_eh_info): From here.
(expand_builtin_throw): Use it.
(expand_start_catch_block): Move region start back.
Tue Nov 4 13:45:10 1997 Doug Evans <devans@canuck.cygnus.com>
* lex.c (MULTIBYTE_CHARS): #undef if cross compiling.
(real_yylex): Record wide strings using target endianness, not host.
1997-11-03 Brendan Kehoe <brendan@lisa.cygnus.com>
* repo.c (rindex): Add decl unconditionally.
(get_base_filename, open_repo_file): Don't cast rindex.
* xref.c (rindex): Add decl unconditionally.
(index): Remove unused decl.
(open_xref_file): Don't cast rindex.
Sun Nov 2 15:04:12 1997 Jason Merrill <jason@yorick.cygnus.com>
* class.c (build_vbase_path): Propagate the result type properly.
1997-11-01 Brendan Kehoe <brendan@lisa.cygnus.com>
* except.c (expand_builtin_throw) [!DWARF2_UNWIND_INFO]: Replace
remaining use of saved_throw_type with a call to get_eh_type.
1997-10-31 Brendan Kehoe <brendan@lisa.cygnus.com>
* lex.c (FILE_NAME_NONDIRECTORY): Delete macro.
(file_name_nondirectory): New function, doing the same as the macro.
(set_typedecl_interface_info): Use it instead of the macro.
(check_newline): Likewise.
(handle_cp_pragma): Likewise.
* repo.c (get_base_filename): Cast result of rindex to char*.
(open_repo_file): Likewise.
* xref.c (open_xref_file): Likewise.
* error.c (dump_char): Make its arg int, not char.
* except.c (push_eh_info): Pass the number of fields - 1 down, not
the exact number of fields.
Fri Oct 31 01:47:57 1997 Jason Merrill <jason@yorick.cygnus.com>
Support for nested exceptions.
* tinfo2.cc (__is_pointer): New fn.
* exception.cc (struct cp_eh_info): Define.
(__cp_exception_info, __uncatch_exception): New fns.
(__cp_push_exception, __cp_pop_exception): New fns.
* except.c: Lose saved_throw_{type,value,cleanup,in_catch}.
Lose empty_fndecl.
(init_exception_processing): Likewise. __eh_pc is now external.
(push_eh_info): New fn.
(get_eh_{info,value,type,caught}): New fns.
(push_eh_cleanup): Just call __cp_pop_exception.
(expand_start_catch_block): Use push_eh_info. Start the eh region
sooner.
(expand_end_eh_spec): Use push_eh_info.
(expand_throw): Call __cp_push_exception to set up the exception info.
Just pass the destructor or 0 as the cleanup. Call __uncatch_exception
when we rethrow.
(expand_builtin_throw): Don't refer to empty_fndecl.
Thu Oct 23 02:01:30 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): SET_DECL_IMPLICIT_INSTANTIATION on new decl.
1997-10-22 Brendan Kehoe <brendan@cygnus.com>
* method.c (build_template_parm_names, build_decl_overload_real):
Add static to definitions.
* pt.c (add_to_template_args, note_template_header,
processing_explicit_specialization, type_unification_real): Likewise.
({determine,check}_explicit_specialization): Use a single string for
error messages.
Mon Oct 20 12:06:34 1997 Jason Merrill <jason@yorick.cygnus.com>
* except.c (expand_exception_blocks): Call do_pending_stack_adjust.
(expand_end_catch_block): Likewise.
(expand_end_eh_spec): Likewise.
Mon Oct 20 11:44:20 1997 Mark Mitchell <mmitchell@usa.net>
* decl.c (duplicate_decls): Handle template specializations
correctly.
* error.c (dump_function_name): Fix printing of specializations of
member functions that are not member templates.
* cp-tree.h (processing_specialization): Make global.
* pt.c (processing_specialization): Likewise.
* lex.c (cons_up_default_function): Save and restore
processing_specialization to avoid confusion.
Mon Oct 20 10:52:22 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (init_decl_processing): Give null_node unknown* type.
* typeck.c (comp_target_types): Handle UNKNOWN_TYPE.
(common_type): Likewise.
* error.c (args_as_string): Recognize null_node.
Sun Oct 19 09:13:01 1997 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
* typeck.c (rationalize_conditional_expr): Handle {MIN,MAX}_EXPR.
(unary_complex_lvalue): Call it for {MIN,MAX}_EXPR.
* decl.c (init_decl_processing): Call using_eh_for_cleanups.
* Make-lang.in (g++): Include prefix.o.
Thu Oct 16 15:31:09 1997 Judy Goldberg <judygold@sanwafp.com>
* pt.c (determine_explicit_specialization): Initialize "dummy"
to keep Purify quiet.
Thu Oct 16 00:14:48 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (build_overload_value): Handle TEMPLATE_CONST_PARMs here.
(build_overload_int): Not here.
Wed Oct 15 00:35:28 1997 Mike Stump <mrs@wrs.com>
* class.c (build_type_pathname): Remove.
(prepare_fresh_vtable): Fix problem with complex MI vtable names.
1997-10-14 Brendan Kehoe <brendan@lisa.cygnus.com>
* parse.y (unary_expr): Give a pedwarn if someone tries to use the
&&label GNU extension.
Tue Oct 14 12:01:00 1997 Mark Mitchell <mmitchell@usa.net>
* decl.c (pushtag): Unset DECL_ASSEMBLER_NAME before setting it,
so as to avoid incorrect manglings.
* method.c (build_decl_overload_real): Don't mangle return types
for constructors.
Tue Oct 14 11:46:14 1997 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (scratchalloc, build_scratch_list, make_scratch_vec,
scratch_tree_cons): Define as macros for now.
* call.c, class.c, cvt.c, decl.c, decl2.c, except.c, expr.c, init.c,
lex.c, method.c, parse.y, pt.c, rtti.c, search.c, tree.c, typeck.c,
typeck2.c: Use them and the expression_obstack variants.
Mon Oct 13 17:41:26 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* decl.c (store_return_init): Allow classes with explicit ctors to
be used with the named return values extension.
Fri Oct 10 12:21:11 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Fix previous change.
Thu Oct 9 12:08:21 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Fix thinko.
(instantiate_decl): Really use the original template.
* call.c (build_new_method_call): Use simple constructor_name for
error messages.
Wed Oct 8 22:44:42 1997 Jeffrey A Law <law@cygnus.com>
* method.c (build_underscore_int): Don't use ANSI specific
features.
Wed Oct 8 00:18:22 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_prevtable_vardecl): Check DECL_REALLY_EXTERN
for our key method; it might have been inlined by -O3.
Tue Oct 7 23:00:12 1997 Mark Mitchell <mmitchell@usa.net>
* decl.c (make_typename_type): Do not try to call lookup_field for
non-aggregate types.
Tue Oct 7 22:52:10 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_reinterpret_cast): Tweak.
Tue Oct 7 22:45:31 1997 Alexandre Oliva <oliva@dcc.unicamp.br>
* typeck.c (build_reinterpret_cast): Converting a void pointer
to function pointer with a reinterpret_cast produces a warning
if -pedantic is issued.
Tue Oct 7 22:43:43 1997 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* typeck.c (c_expand_return): Don't warn about returning a
reference-type variable as a reference.
Tue Oct 7 21:11:22 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (build_static_name): Fix typo.
1997-10-07 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (duplicate_decls): Make sure DECL_LANG_SPECIFIC is set on
OLDDECL before we try to do DECL_USE_TEMPLATE.
Tue Oct 7 00:48:36 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (duplicate_decls): Don't warn about template instances.
* typeck.c (mark_addressable): Lose ancient code that unsets
DECL_EXTERNAL.
* pt.c (do_decl_instantiation): Lose support for instantiating
non-templates.
* call.c (build_new_function_call): Fix handling of null explicit
template args.
(build_new_method_call): Likewise.
Mon Oct 6 23:44:34 1997 Mark Mitchell <mmitchell@usa.net>
* method.c (build_underscore_int): Fix typo.
1997-10-06 Brendan Kehoe <brendan@lisa.cygnus.com>
* tree.c (print_lang_statistics): #if 0 call to
print_inline_obstack_statistics until its definition is checked in.
Mon Oct 6 09:27:29 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Move dump_tree_statistics to end.
* pt.c (instantiate_decl): Look for the original template.
(tsubst): Set DECL_IMPLICIT_INSTANTIATION on partial instantiations
of member templates.
Wed Oct 1 08:41:38 1997 Jason Merrill <jason@yorick.cygnus.com>
* Makefile.in (g++FAQ.*): New rules.
(CONFLICTS): Update.
* g++FAQ.texi: Moved from libg++.
* parse.y (PFUNCNAME): Only specify the type once.
1997-10-01 Brendan Kehoe <brendan@lasher.cygnus.com>
* lex.c (real_yylex): Clean up the code to fully behave the way
the c-lex.c parser does for complex and real numbers.
Tue Sep 30 08:51:36 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (build_decl_overload_real): Reformat.
Tue Sep 30 00:18:26 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (synthesize_method): If at_eof, determine our linkage.
1997-09-29 Paul Eggert <eggert@twinsun.com>
* lex.c (real_yylex): Treat `$' just like `_', except issue a
diagnostic if !dollars_in_ident or if pedantic.
* lang-specs.h (@c++): -ansi no longer implies -$.
* decl2.c (lang_decode_option):
-traditional and -ansi now do not mess with
dollars_in_ident.
Mon Sep 29 19:57:51 1997 H.J. Lu <hjl@gnu.ai.mit.edu>
* Makefile.in (parse.o, decl.o): Also depend on
$(srcdir)/../except.h $(srcdir)/../output.h.
(decl2.o): Also depend on $(srcdir)/../expr.h ../insn-codes.h
$(srcdir)/../except.h $(srcdir)/../output.h.
(typeck.o, init.o): Also depend on $(srcdir)/../expr.h
../insn-codes.h.
* call.c, cp-tree.h, decl.c, tree.c: Finish prototyping.
* expr.c (cplus_expand_expr): Make it static.
* decl2.c, init.c, typeck.c: Include "expr.h".
(expand_expr): Use proper values when calling the function.
Mon Sep 29 11:05:54 1997 Alexandre Oliva <oliva@dcc.unicamp.br>
* lang-options.h: New -Wold-style-cast flag.
* cp-tree.h (warn_old_style_cast): New variable.
* decl2.c (warn_old_style_cast): Likewise.
(lang_decode_option): Support -Wold-style-cast.
(reparse_absdcl_as_casts): Produce old-style-cast warning.
Mon Sep 29 09:20:53 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* decl.c (cp_finish_decl): Allow expand_aggr_init to set
TREE_USED, reset value based on already_used.
* init.c (expand_member_init): Revert change.
Mon Sep 29 08:57:53 1997 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h, decl.c, decl2.c, pt.c:
Lose DECL_C_STATIC and DECL_PUBLIC. Don't pretend statics are public.
* decl2.c (lang_decode_option): Add missing ;.
Sat Sep 27 16:22:48 1997 Jason Merrill <jason@yorick.cygnus.com>
* friend.c (do_friend): Disable injection for all template-derived
decls.
* decl2.c (lang_decode_option): Handle -fguiding-decls.
* parse.y (notype_template_declarator): New nonterminal.
(direct_notype_declarator): Use it.
(complex_direct_notype_declarator): Likewise.
(object_template_id): Accept any kind of identifier after TEMPLATE.
(notype_qualified_id): Don't add template declarators here.
Sat Sep 27 16:21:58 1997 Mark Mitchell <mmitchell@usa.net>
* call.c (add_template_candidate): Add explicit_targs parameter.
(build_scoped_method_call): Use it.
(build_overload_call_real): Likewise.
(build_user_type_conversion_1): Likewise.
(build_new_function_call): Likewise.
(build_object_call): Likewise.
(build_new_op): Likewise.
(build_new_method_call): Likewise.
(build_new_function_call): Handle TEMPLATE_ID_EXPR.
(build_new_method_call): Likewise.
* class.c (finish_struct_methods): Add specialization pass to
determine which methods were specializing which other methods.
(instantiate_type): Handle TEMPLATE_ID_EXPR.
* cp-tree.def (TEMPLATE_ID_EXPR): New tree code.
* cp-tree.h (name_mangling_version): New variable.
(flag_guiding_decls): Likewise.
(build_template_decl_overload): New function.
(begin_specialization): Likewise.
(reset_specialization): Likewise.
(end_specialization): Likewise.
(determine_explicit_specialization): Likewise.
(check_explicit_specialization): Likewise.
(lookup_template_function): Likewise.
(fn_type_unification): Add explicit_targs parameter.
(type_unification): Likewise.
* decl.c (duplicate_decls): Add smarts for explicit
specializations.
(grokdeclarator): Handle TEMPLATE_ID_EXPR, and function
specializations.
(grokfndecl): Call check_explicit_specialization.
* decl2.c (lang_decode_option): Handle -fname-mangling-version.
(build_expr_from_tree): Handle TEMPLATE_ID_EXPR.
(check_classfn): Handle specializations.
* error.c (dump_function_name): Print specialization arguments.
* friend.c (do_friend): Don't call pushdecl for template
instantiations.
* init.c (build_member_call): Handle TEMPLATE_ID_EXPR.
* lang-options.h: Add -fname-mangling-version, -fguiding-decls,
and -fno-guiding-decls.
* lex.c (identifier_type): Return PFUNCNAME for template function
names.
* method.c (build_decl_overload_real): New function.
(build_template_parm_names): New function.
(build_overload_identifier): Use it.
(build_underscore_int): New function.
(build_overload_int): Use it. Add levels for template
parameters.
(build_overload_name): Likewise. Also, handle TYPENAME_TYPEs.
(build_overload_nested_names): Handle template type parameters.
(build_template_decl_overload): New function.
* parse.y (YYSTYPE): New ntype member.
(nested_name_specifier): Use it.
(nested_name_specifier_1): Likewise.
(PFUNCNAME): New token.
(template_id, object_template_id): New non-terminals.
(template_parm_list): Note specializations.
(template_def): Likewise.
(structsp): Likewise.
(fn.def2): Handle member template specializations.
(component_decl_1): Likewise.
(direct_notype_declarator): Handle template-ids.
(component_decl_1): Likewise.
(direct_notype_declarator): Handle template-ids.
(primary): Handle TEMPLATE_ID_EXPR, and template-ids.
* pt.c (processing_specializations): New variable.
(template_header_count): Likewise.
(type_unification_real): New function.
(processing_explicit_specialization): Likewise.
(note_template_header): Likewise.
(is_member_template): Handle specializations.
(end_template_decl): Call reset_specialization.
(push_template_decl): Handle member template specializations.
(tsubst): Likewise.
(tsubst_copy): Handle TEMPLATE_ID_EXPR.
(instantiate_template): Handle specializations.
(instantiate_decl): Likewise.
(fn_type_unification): Handle explicit_targs.
(type_unification): Likewise. Allow incomplete unification
without an error message, if allow_incomplete.
(get_bindings): Use new calling sequence for fn_type_unification.
* spew.c (yylex): Handle PFUNCNAME.
* tree.c (is_overloaded_fn): Handle TEMPLATE_ID_EXPR.
(really_overloaded_fn): Likewise.
(get_first_fn): Handle function templates.
* typeck.c (build_x_function_call): Use really_overloaded_fn.
Handle TEMPLATE_ID_EXPR.
(build_x_unary_op): Likewise.
(build_unary_op): Likewise.
(mark_addressable): Templates whose address is taken are marked
as used.
1997-09-25 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* decl.c (init_decl_processing): Declare __builtin_constant_p as
accepting any kind of type, not only int.
Fri Sep 26 00:22:56 1997 Jason Merrill <jason@yorick.cygnus.com>
* search.c (get_matching_virtual): Notice virtual bases when sorrying
about covariant returns.
* parse.y (member_init): Also imply typename here. Remove ancient
extension for initializing base members.
Thu Sep 25 11:11:13 1997 Jason Merrill <jason@yorick.cygnus.com>
Handle multi-level typenames and implicit typename in base list.
* parse.y (typename_sub{,[0-2]}): New rules.
(structsp, rule TYPENAME_KEYWORD): Use typename_sub.
(nonnested_type): New rule.
(complete_type_name): Use it.
(base_class.1): Use typename_sub and nonnested_type.
(nested_name_specifier): Don't elide std:: here.
* decl.c (make_typename_type): Handle getting a type for NAME.
(lookup_name_real): Turn std:: into :: here.
Rvalue conversions were removed in London.
* call.c (is_subseq): Don't consider lvalue transformations.
(build_conv): LVALUE_CONV and RVALUE_CONV get IDENTITY_RANK.
(joust): Re-enable ?: kludge.
1997-09-22 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (start_function): Up warning of no return type to be a
pedwarn.
Mon Sep 22 14:15:34 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* init.c (expand_member_init): Don't set TREE_USED.
* decl.c (cp_finish_decl): Mark decls used if type has TREE_USED
set,don't clear TREE_USED wholesale.
Sat Sep 20 15:31:00 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Do require_complete_type before
build_cplus_new.
Thu Sep 18 16:47:52 1997 Jason Merrill <jason@yorick.cygnus.com>
* search.c (lookup_field): Call complete_type in all cases.
* decl.c (finish_function): Just warn about flowing off the end.
Wed Sep 17 10:31:25 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokparms): Don't bash a permanent list node if we're
in a function.
1997-09-17 Brendan Kehoe <brendan@lisa.cygnus.com>
* Makefile.in (CONFLICTS): Fix s/r conflict count to 18.
Tue Sep 16 14:06:56 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_op): Give better error for syntactically
correct, but semantically invalid, use of undeclared template.
* call.c (compare_qual): Handle pmfs.
* decl.c (store_parm_decls): last_parm_cleanup_insn is the insn
after the exception spec.
Mon Sep 15 11:52:13 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (null_ptr_cst_p): Integer type, not integral type.
* call.c (joust): Disable warnings until they can be moved to the
right place.
Fri Sep 12 16:11:13 1997 Per Bothner <bothner@cygnus.com>
* Makefile.in, config-lang.in: Convert to autoconf.
Thu Sep 11 17:14:55 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): Add implicit 'typename' to types from
base classes.
* pt.c (most_specialized_class): Fix typo.
(tsubst): Move constant folding to TREE_VEC case.
Thu Sep 11 10:08:45 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (do_poplevel): Don't warn about unused local variables
while processing_template_decl since we don't always know whether
or not they will need constructing/destructing.
* pt.c (uses_template_parms): Check the values of an enumeration
type to make sure they don't depend on template parms.
* decl.c (make_typename_type): Don't lookup the field if the
context uses template parms, even if we're not
processing_template_decl at the moment.
* pt.c (coerce_template_parms): Avoid looking at the
TYPE_LANG_DECL portion of a typename type, since there won't be
one.
(tsubst): Do constant folding as necessary to make sure that
arguments passed to lookup_template_class really are constants.
Wed Sep 10 11:21:55 1997 Jason Merrill <jason@yorick.cygnus.com>
* except.c (expand_builtin_throw): #ifndef DWARF2_UNWIND_INFO.
* decl2.c (finish_file): Only register exception tables if we
need to.
* decl.c (init_decl_processing): Add __builtin_[fs]p.
Tue Sep 9 19:49:38 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (unify): Just return 0 for a TYPENAME_TYPE.
Tue Sep 9 17:57:25 1997 Mark Mitchell <mmitchell@usa.net>
* error.c (dump_decl): Avoid crashing when presented with a
uninitialized constant, as can occur with a template parameter.
(dump_expr): Make sure that there are enough levels of
current_template_parms before we start diving through them.
1997-09-09 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (build_indirect_ref): Heed FLAG_VOLATILE similar to
c-typeck.c.
Tue Sep 9 09:36:39 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* except.c (expand_throw): Call build_delete for all
exception types, not just objects with destructors.
Mon Sep 8 02:33:20 1997 Jody Goldberg <jodyg@idt.net>
* decl.c (current_local_enum): Remove static.
* pt.c (tsubst_enum): Save and restore value of current_local_enum
in case template is expanded in enum decl.
(instantiate_class_template): Use new tsubst_enum signature.
(tsubst_expr): Likewise.
Mon Sep 8 01:21:43 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (begin_member_template_processing): Take a function as
argument, not a set of template arguments. Use the template
parameters, rather than the arguments. Handle non-type parameters
correctly. Push a binding level for the parameters so that multiple
member templates using the same parameter names can be declared.
(end_member_template_processing): Pop the binding level.
(push_template_decl): Mark member templates as static when
appropriate.
* lex.c (do_pending_inlines): Pass the function, not its template
arguments, to begin_member_template_processing.
(process_next_inline): Likewise.
(do_pending_defargs): Likewise.
* error.c (dump_expr): Obtain the correct declaration for a
TEMPLATE_CONST_PARM.
* call.c (add_template_conv_candidate): New function.
(build_object_call): Handle member templates, as done in the other
build_ functions.
Sat Sep 6 10:20:27 1997 Mark Mitchell <mmitchell@usa.net>
* decl.c (replace_defag): Undo previous change.
* lex.c (do_pending_defargs): Deal with member templates.
* pt.c (is_member_template): Avoid crashing when passed a
non-function argument.
Fri Sep 5 17:27:38 1997 Jason Merrill <jason@yorick.cygnus.com>
* class.c (grow_method): Remove check for redeclaration.
Fri Sep 5 01:37:17 1997 Mark Mitchell <mmitchell@usa.net>
* cp-tree.h (INNERMOST_TEMPLATE_PARMS): New macro.
(DECL_INNERMOST_TEMPLATE_PARMS): Likewise.
(PRIMARY_TEMPLATE_P): Use it.
* call.c (build_overload_call_real): Use it.
* class.c (instantiate_type): Likewise.
* decl.c (decls_match): Likewise.
* method.c (build_overload_identifier): Likewise.
* pt.c (push_template_decl): Likewise.
(classtype_mangled_name): Likewise.
(lookup_template_class): Likewise.
* cp-tree.h (DECL_NTPARMS): Change name from DECL_NT_PARMS to
DECL_NTPARMS to conform to usage elsewhere.
* call.c (add_template_candidate): Likewise.
* class.c (instantiate_type): Likewise.
* pt.c (instantiate_template): Likewise.
(get_bindings): Likewise.
* class.c (grow_method): Use DECL_FUNCTION_TEMPLATE_P instead of
is_member_template.
* pt.c (unify): Undo changes to allow multiple levels of template
parameters.
(type_unification): Likewise.
(fn_type_unification): Likewise.
(get_class_bindings): Likewise.
* cp-tree.h (Likewise).
* decl.c (replace_defarg): Check that the type of the default
parameter does not invlove a template type before complaining
about the initialization.
* error.c (dump_expr): Deal with template constant parameters in
member templates correctly.
* pt.c (is_member_template): Deal with class specializations
correctly.
(tsubst): Handle "partial instantiation" of member templates
correctly.
Wed Sep 3 12:30:24 1997 Mark Mitchell <mmitchell@usa.net>
* pt.c (type_unification): Change calling sequence to allow for
multiple levels of template parameters.
(tsubst_expr): Likewise.
(tsubst): Likewise.
(tsubst_copy): Likewise.
(instantiate_template): Likewise.
(unify): Likewise.
* call.c (build_overload_call_real): Use it.
(add_builtin_candidate): Use it.
(build_new_method_call): Use it.
* class.c (instantiate_type): Use it.
* decl.c (grokdeclarator): Use it.
* decl2.c (finish_file): Use it.
* method.c (build_overload_identifier): Use it.
* call.c (add_template_candidate): Add additional parameter for
the function return type. Call fn_type_unification istead of
type_unification.
(build_user_type_conversion_1): Handle member templates.
(build_new_function_call): Likewise.
(build_new_op): Likewise.
(build_new_method_call): Likewise.
* class.c (grow_method): Don't give an error message indicating
that two member templates with the same name are ambiguous.
(finish_struct): Treat member template functions just like member
functions.
* cp-tree.h (check_member_template): Add declaration.
(begin_member_template_processing): Likewise.
(end_member_template_processing): Likewise.
(fn_type_unification): Likewise.
(is_member_template): Likewise.
(tsubst): Change prototype.
(tsubst_expr): Likewise.
(tsubst_copy): Likewise.
(instantiate_template): Likewise.
(get_bindings): Likewise.
* decl.c (decls_match): Handle multiple levels of template
parameters.
(pushdecl): Handle template type params just like other type
declarations.
(push_class_level_binding): Return immediately if the
class_binding_level is NULL.
(grokfndecl): If check_classfn() returns a member_template, use
the result of the template, not the template itself.
* decl2.c (check_member_template): New function. Check to see
that the entity declared to be a member template can be one.
(check_classfn): Allow redeclaration of member template functions
with different types; the new functions can be specializations or
explicit instantiations.
* error.c (dump_decl): Handle multiple levels of template
parameters.
(dump_function_decl): Update to handle function templates.
* lex.c (do_pending_inlines): Set up template parameter context
for member templates.
(process_next_inline): Likewise.
* method.c (build_overload_identifier): Adjust for multiple levels
of template parameters.
* parse.y (fn.def2): Add member templates.
(component_decl_1): Likewise.
* pt.c (begin_member_template_processing): New function.
(end_member_template_processing): Likewise.
(is_member_template): Likewise.
(fn_type_unification): Likewise.
(current_template_parms): Return a vector of all the template
parms, not just the innermost level of parms.
(push_template_decl): Deal with the possibility of member
templates.
(lookup_template_class): Likewise.
(uses_template_parms): Likewise.
(tsubst): Modify processing to TEMPLATE_TYPE_PARM and
TEMPLATE_CONST_PARM to deal with multiple levels of template
arguments. Add processing of TEMPLATE_DECL to produce new
TEMPLATE_DECLs from old ones.
(do_decl_instantiation): Handle member templates.
* search.c (lookup_fnfields_1): Handle member template conversion
operators.
* tree.c (cp_tree_equal): Check the levels, as well as the
indices, of TEMPLATE_CONST_PARMs.
* typeck.c (comptypes): Check the levels, as well as the indices,
fo TEMPLATE_TYPE_PARMs.
(build_x_function_call): Treat member templates like member
functions.
Wed Sep 3 11:09:25 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (c_expand_return): Always convert_for_initialization
before checking for returning a pointer to local.
* pt.c (type_unification): If strict and the function parm doesn't
use template parms, just compare types.
Wed Sep 3 10:35:49 1997 Klaus Espenlaub <kespenla@student.informatik.uni-ulm.de>
* method.c (build_overloaded_value): Replace direct call
to the floating point emulator with REAL_VALUE_TO_DECIMAL macro.
Wed Sep 3 00:02:53 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (convert_arguments): Don't arbitrarily choose the first
of a set of overloaded functions.
Tue Sep 2 12:09:13 1997 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (real_yylex): Don't elide __FUNCTION__.
* method.c (build_overload_value): Add in_template parm.
(build_overload_int): Likewise.
(build_overload_identifier): Pass it.
* decl.c (duplicate_decls): Don't bash a previous template
definition with a redeclaration.
* pt.c (unify): float doesn't match double.
* pt.c (do_type_instantiation): Handle getting a _TYPE or a
TYPE_DECL. Handle getting non-template types.
* parse.y (explicit_instantiation): Use typespec instead of
aggr template_type.
Tue Sep 2 10:27:08 1997 Richard Henderson <rth@cygnus.com>
* typeck.c (build_ptrmemfunc1): Clean up ptr->int cast warnings.
Mon Sep 1 13:19:04 1997 Eugene Mamchits <eugin@ips.ras.ru>
* call.c (add_builtin_candidate): Add missing TREE_TYPE.
(compare_ics): Likewise.
Mon Sep 1 13:19:04 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (joust): Warn about choosing one conversion op over
another because of 'this' argument when the other return type is
better.
(source_type): New fn.
* call.c (build_new_op): Strip leading REF_BIND from first operand
to builtin operator.
* decl2.c (mark_vtable_entries): Mark abort_fndecl as used when we
use its RTL.
Thu Aug 28 09:45:23 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (null_ptr_cst_p): Remove support for (void*)0.
Wed Aug 27 02:03:34 1997 Jeffrey A Law <law@cygnus.com>
* typeck.c (expand_target_expr): Make definition match declaration.
* class.c (get_basefndecls): Make definition match declaration.
Mon Aug 25 14:30:02 1997 Jason Merrill <jason@yorick.cygnus.com>
* input.c (sub_getch): Eventually give up and release the input file.
* decl.c (cp_finish_decl): If #p i/i, put inline statics in the
right place.
* call.c (joust): Tweak message.
Sat Aug 23 18:02:59 1997 Mark Mitchell <mmitchell@usa.net>
* error.c (type_as_string): Put const/volatile on template type
parameters where appropriate.
Sat Aug 23 17:47:22 1997 Jeffrey A Law <law@cygnus.com>
* call.c (strictly_better): Make arguments unsigned ints.
Thu Aug 21 18:48:44 1997 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (real_yylex): Refer to __complex instead of complex.
Thu Aug 21 22:25:46 1997 J"orn Rennecke <amylaar@cygnus.co.uk>
* lex.c (real_yylex): Don't use getc directly.
Wed Aug 20 17:25:08 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (is_subseq): Don't try to be clever.
Wed Aug 20 03:13:36 1997 H.J. Lu (hjl@gnu.ai.mit.edu)
* parse.y, pt.c: Include "except.h".
* call.c, class.c, class.h, cp-tree.h, cvt.c, decl.c, decl2.c,
error.c, except.c, expr.c, friend.c, g++spec.c, init.c, input.c,
lex.c, lex.h, method.c, parse.y, pt.c, repo.c, rtti.c, search.c,
sig.c, spew.c, tree.c, typeck.c, typeck2.c, xref.c: Finish
prototyping.
Wed Aug 20 01:34:40 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (mark_vtable_entries): Instead of replacing pure
virtuals with a reference to __pure_virtual, copy the decl and
change the RTL.
Tue Aug 19 02:26:07 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (lookup_nested_type_by_name): Handle typedef wierdness.
* typeck2.c (my_friendly_abort): Report bugs to egcs-bugs@cygnus.com.
* pt.c (instantiate_class_template): Call repo_template_used
before finish_prevtable_vardecl.
* call.c (is_subseq): New fn.
(compare_ics): Use it.
* repo.c (finish_repo): Don't crash on no args.
* parse.y (named_complex_class_head_sans_basetype): Handle
explicit global scope.
* decl2.c (handle_class_head): New fn.
* pt.c (unify): Add CONST_DECL case.
Thu Aug 14 10:05:13 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* rtti.c (permanent_obstack): Fix decl to not be a pointer.
* cp-tree.h (report_type_mismatch): Add prototype.
* call.c (build_overload_call_real): Remove erroneous fourth
argument to report_type_mismatch.
(build_user_type_conversion_1): Remove erroneous second arg to
tourney.
(build_new_function_call): Likewise.
(build_object_call): Likewise.
(build_new_op): Likewise.
(build_new_method_call): Likewise.
Wed Aug 13 19:19:25 1997 Jason Merrill <jason@yorick.cygnus.com>
* error.c (dump_decl): Don't bother processing a function with no
DECL_LANG_SPECIFIC.
* method.c (emit_thunk): Call init_function_start in the macro case.
Wed Aug 13 10:46:19 1997 H.J. Lu (hjl@gnu.ai.mit.edu)
* decl2.c (DEFAULT_VTABLE_THUNKS): Define to be 0 if not
defined and used to set flag_vtable_thunks.
Tue Aug 12 20:13:57 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y: Don't clear the inlines from their obstack until they've
all been processed.
* decl.c (duplicate_decls): Don't complain about exception
specification mismatch if flag_exceptions is off.
Mon Aug 11 15:01:56 1997 Marc Lehmann <pcg@goof.com>
* Make-lang.in (c++.distclean): Remove g++.c on make distclean.
Sun Aug 10 12:06:09 1997 Paul Eggert <eggert@twinsun.com>
* cp-tree.h: Replace STDIO_PROTO with PROTO in include files.
* cvt.c, error.c, except.c, expr.c, friend.c, init.c, rtti.c:
Include <stdio.h> before include files that formerly used STDIO_PROTO.
* decl.c, g++spec.c, lex.c, method.c, repo.c:
Include "config.h" first, as per autoconf manual.
Fri Aug 8 11:47:48 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (duplicate_decls): Tweak wording.
* lex.c (do_pending_defargs): Don't die if we see a default arg
that isn't a DEFAULT_ARG.
* error.c (dump_expr): Handle DEFAULT_ARG.
* decl2.c (lang_decode_option): Handle -fhandle-exceptions.
* lang-options.h: Add -fhandle-exceptions.
* class.c (build_vtable): Vtables are artificial.
(prepare_fresh_vtable): Likewise.
Wed Aug 6 11:02:36 1997 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (ocp_convert): After converting to the target type, set
LOOKUP_NO_CONVERSION.
* call.c (joust): Warn about potentially confusing promotion rules
with -Wsign-promo.
* cp-tree.h, lang-options.h, decl2.c: Support -Wsign-promo.
Tue Aug 5 15:15:07 1997 Michael Meissner <meissner@cygnus.com>
* exception.cc: Declare __terminate_func with noreturn attribute.
Fri Aug 1 03:18:15 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y: Break out eat_saved_input, handle errors.
(function_try_block): Use compstmt instead of compstmt_or_error.
Thu Jul 31 17:14:04 1997 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (build_cplus_new): Don't set TREE_ADDRESSABLE.
Fri Jul 4 01:45:16 1997 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* Make-lang.in (cplib2.txt, cplib2.ready): Instead of checking for
existence of cc1plus check whether $(LANGUAGES) contains C++.
Wed Jul 30 13:04:21 1997 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* method.c (do_build_copy_constructor): When copying an anonymous
union member loop around to handle nested anonymous unions. Use
the offset of the member relative to the outer structure, not the
union.
Tue Jul 29 21:17:29 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (resolve_args): New fn.
(build_new_function_call): Use it.
(build_object_call): Likewise.
(build_new_method_call): Likewise.
Mon Jul 28 16:02:36 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): tsubst all default parms from templates.
Wed Jul 23 13:36:25 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (struct cp_function): Add static_labelno.
(push_cp_function_context): Save it.
(pop_cp_function_context): Restore it.
Tue Jul 22 14:43:29 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_component_ref_1): Convert from reference.
Tue Jul 22 11:06:23 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* parse.y (current_declspecs, prefix_attributes): Initialize to
NULL_TREE.
* parse.y (initdcl0): Make sure CURRENT_DECLSPECS is non-nil
before we try to force it to be a TREE_LIST.
(decl): Make sure $1.t is non-nil.
Sun Jul 20 11:53:07 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (uses_template_parms): Handle template first-parse codes.
* decl.c (cp_finish_decl): Only warn about user-defined statics.
Fri Jul 18 17:56:08 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (unify): Handle BOOLEAN_TYPE.
* cp-tree.h: Lose PARM_DEFAULT_FROM_TEMPLATE.
* pt.c (tsubst): Don't set it.
* call.c (build_over_call): Use uses_template_parms.
Thu Jul 17 18:06:30 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (build_overload_nested_name): Use static_labelno
instead of var_labelno.
(build_qualified_name): New fn.
(build_overload_name): Split out from here.
(build_static_name): Use build_qualified_name.
* decl.c (cp_finish_decl): Statics in extern inline functions
have comdat linkage.
(start_function): Initialize static_labelno.
Thu Jul 17 11:20:17 1997 Benjamin Kosnik <bkoz@rhino.cygnus.com>
* class.c (finish_struct_methods): Add check of warn_ctor_dtor_privacy
before "all member functions in class [] are private".
Wed Jul 16 23:47:08 1997 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (do_scoped_id): convert_from_reference.
* init.c (build_offset_ref): Likewise.
Wed Jul 16 12:34:29 1997 Benjamin Kosnik <bkoz@lisa.cygnus.com>
* error.c (dump_expr): Check TREE_OPERAND before dump_expr_list.
Mon Jul 14 03:23:46 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (get_member_function_from_ptrfunc): Promote index
before saving it.
Sun Jul 13 00:11:52 1997 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (layout_basetypes): Move non-virtual destructor warning.
* decl.c (xref_basetypes): Remove non-virtual destructor warning.
Sat Jul 12 12:47:12 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Call add_defarg_fn for the function
type, too.
* lex.c (add_defarg_fn): Adjust.
(do_pending_defargs): Adjust. Don't skip the first parm.
Fri Jul 11 01:39:50 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (build_enumerator): Global enumerators are also readonly.
* rtti.c (build_dynamic_cast_1): Renamed from build_dynamic_cast.
(build_dynamic_cast): Call it and convert_from_reference.
* lex.c (add_defarg_fn): New fn.
(snarf_defarg): Don't add to defarg_types.
(do_pending_defargs): Lose defarg_types. All fns we process now
have defargs.
* decl.c (grokfndecl): Call add_defarg_fn.
* Makefile.in (CONFLICTS): Expect 18 s/r conflicts.
* cp-tree.def: Add DEFAULT_ARG.
* spew.c (yylex): Call snarf_defarg as appropriate.
* parse.y: New tokens DEFARG and DEFARG_MARKER.
(defarg_again, pending_defargs, defarg, defarg1): New rules.
(structsp): Use pending_defargs.
(parms, full_parm): Use defarg.
* lex.c (init_lex): Initialize inline_text_firstobj.
(do_pending_inlines): Never pass the obstack to feed_input.
(process_next_inline): Call end_input instead of restore_pending_input.
(clear_inline_text_obstack, reinit_parse_for_expr, do_pending_defargs,
finish_defarg, feed_defarg, snarf_defarg, maybe_snarf_defarg): New fns.
* input.c (end_input): New fn.
(sub_getch): At the end of some fed input, just keep returning EOF
until someone calls end_input.
Remove 'obstack' field from struct input_source.
* decl.c (grokparms): Handle DEFAULT_ARG.
(replace_defarg): New fn.
* cp-tree.h (DEFARG_LENGTH, DEFARG_POINTER): New macros.
Wed Jul 9 13:44:12 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (implicit_conversion): If nothing else works, try binding
an rvalue to a reference.
Wed Jul 9 13:04:38 1997 Geoffrey Noer <noer@cygnus.com>
* decl.c (init_decl_processing): Fix Jun 30 patch -- move
ifndef for Cygwin32 to include SIGSEGV.
Thu Jul 3 01:44:05 1997 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): Only complain about pointers without
copy stuff if there are any constructors.
* rtti.c (build_dynamic_cast): Call complete_type on the types.
* decl.c (grokfndecl): If the function we chose doesn't actually
match, die.
* decl2.c (grokclassfn): Don't specify 'const int' for the
artificial destructor parm.
* pt.c (type_unification): If we are called recursively, nothing
decays.
Mon Jun 30 17:53:21 1997 Geoffrey Noer <noer@cygnus.com>
* decl.c (init_decl_processing): Stop trying to catch signals
other than SIGABRT since the Cygwin32 library doesn't support
them correctly yet. This fixes a situation in which g++ causes
a hang on SIGSEGVs and other such signals in our Win32-hosted
tools.
Mon Jun 30 14:50:01 1997 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (mapcar, case CALL_EXPR): Handle all the parse node data.
Fri Jun 27 15:18:49 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (store_init_value): Always return the value if our
type needs constructing.
* method.c (hack_identifier): Convert class statics from
reference, too.
Thu Jun 26 11:44:46 1997 Jason Merrill <jason@yorick.cygnus.com>
* Make-lang.in (cplib2.ready): Add $(LANGUAGES) dependency.
Thu Jun 19 16:49:28 1997 Mike Stump <mrs@cygnus.com>
* typeck.c (c_expand_return): Make sure we clean up temporaries at
the end of return x;
Thu Jun 19 12:28:43 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* lex.c (check_for_missing_semicolon): Also check for CV_QUALIFIER.
Tue Jun 17 18:35:57 1997 Mike Stump <mrs@cygnus.com>
* except.c (expand_builtin_throw): Add support
-fno-sjlj-exceptions -fPIC exception handling on the SPARC.
Mon Jun 16 01:24:37 1997 Jason Merrill <jason@yorick.cygnus.com>
* repo.c (extract_string): Null-terminate.
* cp-tree.h (TI_SPEC_INFO): New macro.
(CLASSTYPE_TI_SPEC_INFO): New macro.
* pt.c (push_template_decl): Correctly determine # of template parms
for partial specs.
* call.c (compare_ics): Really fix 'this' conversions.
* pt.c (do_decl_instantiation): Don't crash on explicit inst of
non-template fn.
* pt.c (push_template_decl): Complain about mismatch in # of
template parms between a class template and a member template.
Sun Jun 15 02:38:20 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (synthesize_method): You can't call
function_cannot_inline_p after finish_function.
* decl.c (finish_function): Turn on flag_inline_functions and turn
off DECL_INLINE before handing a synthesized method to the
backend.
Thu Jun 12 17:35:28 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (synthesize_method): Remove July 30 change to never set
DECL_INLINE if at_eof.
Thu Jun 12 15:25:08 1997 Mike Stump <mrs@cygnus.com>
* xref.c (GNU_xref_member): Ensure that the node has a
decl_lang_specific part before checking DECL_FRIEND_P.
Thu Jun 12 12:36:05 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Diagnose non-class types used
as bases.
Wed Jun 11 17:33:40 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_conditional_expr): Use convert_for_initialization
instead of convert_and_check.
Wed Jun 11 12:31:33 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* parse.y (typespec): Don't pedwarn for typeof.
Tue Jun 10 00:22:09 1997 Jason Merrill <jason@yorick.cygnus.com>
* repo.c (finish_repo): Only check changes if we would write a
repo file.
* call.c (compare_ics): Fix handling of 'this' conversions.
* pt.c (do_decl_instantiation): Support static data too. Rename
from do_function_instantiation.
* cp-tree.h: Adjust.
* parse.y: Adjust.
* repo.c (extract_string): New fn.
(get_base_filename): Use it.
(init_repo): Compare old args with current args.
Mon Jun 9 14:25:30 1997 Mike Stump <mrs@cygnus.com>
* Makefile.in, Make-lang.in: Protect C-ls with a comment
character, idea from Paul Eggert <eggert@twinsun.com>.
Mon Jun 9 01:52:03 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (c_expand_return): Be more persistent in looking for
returned temps.
* cvt.c (build_up_reference): Use NOP_EXPR for switching from
pointer to reference.
* class.c (build_vbase_path): Don't do anything if PATH has no steps.
Sun Jun 8 03:07:05 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_member_call, build_offset_ref):
Use do_scoped_id instead of do_identifier.
* cvt.c (convert): Remove bogosity.
Sat Jun 7 20:50:17 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* cvt.c (build_up_reference): Do checks of ARGTYPE and
TARGET_TYPE before trying to use get_binfo.
Fri Jun 6 17:36:39 1997 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_up_reference): Call get_binfo to get access control.
* decl2.c (import_export_decl): If we don't support weaks, leave
statics undefined.
Fri Jun 6 15:55:49 1997 Mike Stump <mrs@cygnus.com>
* except.c (expand_builtin_throw): Add support for machines that
cannot access globals after throw's epilogue when
-fno-sjlj-exceptions is used.
Thu Jun 5 16:28:43 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y: 'std::' becomes '::'.
* lex.c (real_yylex): Remove 'namespace' warning.
* init.c (build_member_call): Ignore 'std::'.
(build_offset_ref): Likewise.
* decl2.c (do_using_directive): Ignore 'using namespace std;'.
(do_toplevel_using_decl): Ignore 'using std::whatever'.
* decl.c (push_namespace): Just sorry.
(pop_namespace): Nop.
(init_decl_processing): Declare std namespace.
Tue Jun 3 18:08:23 1997 Jason Merrill <jason@yorick.cygnus.com>
* search.c (push_class_decls): A name which ambiguously refers to
several instantiations of the same template just refers to the
template.
Tue Jun 3 12:30:40 1997 Benjamin Kosnik <bkoz@cirdan.cygnus.com>
* decl.c (build_enumerator): Fix problem with unsigned long
enumerated values being smashed to ints, causing overflow
when computing next enumerated value (for enum values around
MAX_VAL).
Mon Jun 2 17:40:56 1997 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_component_ref): Only call mark_used on a decl.
Thu May 29 15:54:17 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (build_c_cast): Make the check for a ptr to function
more specific before possible default_conversion call.
Thu May 29 13:02:06 1997 Mike Stump <mrs@cygnus.com>
* except.c (expand_exception_blocks): Simplify and fix and make
sure we don't end a region in a sequence, as expand_end_bindings
doesn't like it.
Wed May 28 17:08:03 1997 Mike Stump <mrs@cygnus.com>
* except.c (init_exception_processing): Mark terminate as not
returning so that the optimizer can optimize better.
Tue May 27 19:49:19 1997 Mike Stump <mrs@cygnus.com>
* cvt.c (convert): Don't do any extra work, if we can avoid it
easily.
Tue May 27 18:21:47 1997 Mike Stump <mrs@cygnus.com>
* *.[chy]: Change cp_convert to ocp_convert, change convert to
cp_convert. convert is now reserved for the backend, and doesn't
have the semantics a frontend person should ever want.
Fri May 23 10:58:31 1997 Jason Merrill <jason@yorick.cygnus.com>
* lang-specs.h: Define __EXCEPTIONS if exceptions are enabled.
Lose -traditional support.
Thu May 22 15:41:28 1997 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (get_tinfo_var): Use TYPE_PRECISION (sizetype).
* parse.y (self_reference): Do it for templates, too.
* class.c (pushclass): Don't overload_template_name; the alias
generated by build_self_reference serves the same purpose.
* tree.c (list_hash): Make static, take more args.
(list_hash_lookup): Likewise.
(list_hash_add): Make static.
(list_hash_canon): Lose.
(hash_tree_cons): Only build a new node if one isn't already in the
hashtable.
(hash_tree_chain): Use hash_tree_cons.
* cp-tree.h: Adjust.
* decl.c (grokfndecl): Just check IDENTIFIER_GLOBAL_VALUE instead
of calling lookup_name.
Wed May 21 18:24:19 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): TYPE_VALUES for an enum
doesn't refer to the CONST_DECLs.
Tue May 20 21:09:32 1997 Bob Manson <manson@charmed.cygnus.com>
* rtti.c (get_tinfo_var): Either INT_TYPE_SIZE or 32, whichever
is bigger.
(expand_class_desc): Convert the last argument to a sizetype.
Tue May 20 13:55:57 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* gxx.gperf (__complex, __complex__, __imag, __imag__, __real,
__real__): Add reswords.
* hash.h: Regenerate.
* lex.h (rid): Add RID_COMPLEX.
(RID_LAST_MODIFIER): Set to RID_COMPLEX.
* lex.c (init_lex): Add building of RID_COMPLEX.
(real_yylex): General cleanup in line with what c-lex.c also has,
sans the cruft for traditional; add handling of SPEC_IMAG, complex
types, and imaginary numeric constants.
* parse.y (REALPART, IMAGPART): Add tokens.
(unary_expr): Add REALPART and IMAGPART rules.
* cp-tree.h (complex_{integer,float,double,long}_type_node): Declare.
* decl.c (complex_{integer,float,double,long}_type_node): Define
types.
(init_decl_processing): Set up the types.
(grokdeclarator): Add handling of RID_COMPLEX. Set and use
DEFAULTED_INT instead of EXPLICIT_INT when we default to int type.
* call.c (build_new_op): Add REALPART_EXPR and IMAGPART_EXPR cases.
* cvt.c (cp_convert): Handle COMPLEX_TYPE.
* error.c (dump_type_prefix, dump_type, dump_type_suffix): Add
COMPLEX_TYPE case.
* method.c (build_overload_name): Add handling of the different
COMPLEX_TYPEs, prefixing them with `J'.
* pt.c (process_template_parm): Don't let them use a COMPLEX_TYPE
as a template parm.
(uses_template_parms, tsubst, unify): Add COMPLEX_TYPE case.
* tree.c (lvalue_p): Add REALPART_EXPR and IMAGPART_EXPR cases.
(mapcar): Handle COMPLEX_CST.
* typeck.c (build_binary_op_nodefault): Handle COMPLEX_TYPE.
(common_type): Add code for complex types.
(build_unary_op): Add REALPART_EXPR and IMAGPART_EXPR cases.
(convert_for_assignment): Likewise.
(mark_addressable): Add REALPART_EXPR and IMAGPART_EXPR cases.
Mon May 19 12:26:27 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Don't pass the MINUS_EXPR for an array domain to
tsubst_expr, as it might try to do overload resolution.
Sat May 17 10:48:31 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Oops.
Fri May 16 14:23:57 1997 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.def: Add TAG_DEFN.
* pt.c (tsubst_enum): New fn.
(instantiate_class_template): Use it.
(tsubst_expr): Support TAG_DEFN.
(tsubst): Support local enums.
(tsubst_copy): Likewise.
* decl.c (finish_enum): Likewise.
(start_enum): If this is a local enum, switch to permanent_obstack.
Wed May 14 19:08:28 1997 Mike Stump <mrs@cygnus.com>
* decl.c (store_parm_decls): Set last_parm_cleanup_insn here.
(finish_function): Put the base init code for constructors just
after the parm cleanup insns.
(struct cp_function): Add last_parm_cleanup_insn.
(push_cp_function_context): Likewise.
(pop_cp_function_context): Likewise.
Tue May 13 15:51:20 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_copy): Handle BIT_NOT_EXPR.
Wed May 7 11:17:59 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* method.c (emit_thunk) [ASM_OUTPUT_MI_THUNK]: Build up the RTL
for THUNK_FNDECL before we switch to temporary allocation.
Mon May 5 14:46:53 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_op): Handle null arg2 for ?:.
Thu May 1 18:26:37 1997 Mike Stump <mrs@cygnus.com>
* except.c (expand_exception_blocks): Ensure that we flow through
the end of the exception region for the exception specification.
Move exception region for the exception specification in, so that
it doesn't protect the parm cleanup. Remove some obsolete code.
* decl.c (store_parm_decls): Likewise.
(finish_function): Likewise.
Tue Apr 29 15:38:54 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): Fix nothrow handling.
Tue Apr 29 14:29:50 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* init.c (emit_base_init): Don't warn about the initialization
list for an artificial member.
Fri Apr 25 17:47:59 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* expr.c (do_case): Handle !START case for the error msg.
Fri Apr 25 11:55:23 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c, lang-options.h: New option -Weffc++.
* class.c, decl.c, init.c, typeck.c: Move Effective C++ warnings
to -Weffc++.
* decl2.c (finish_prevtable_vardecl): Change NO_LINKAGE_HEURISTICS
to MULTIPLE_SYMBOL_SPACES.
Wed Apr 23 18:06:50 1997 Jason Merrill <jason@yorick.cygnus.com>
* method.c (emit_thunk, generic case): Set current_function_is_thunk.
* method.c (emit_thunk, macro case): Set up DECL_RESULT.
* typeck.c (c_expand_return): Don't complain about returning void
to void in an artificial function.
* method.c (make_thunk): Change settings of READONLY/VOLATILE,
don't set DECL_RESULT, set DECL_ARTIFICIAL.
(emit_thunk, generic code): Also set up DECL_LANG_SPECIFIC.
Wed Apr 23 14:43:06 1997 Mike Stump <mrs@cygnus.com>
* init.c (init_decl_processing): Add support for setjmp/longjmp based
exception handling.
* except.c (init_exception_processing): Likewise.
(expand_end_catch_block): Likewise.
(expand_exception_blocks): Likewise.
(expand_throw): Likewise.
* exception.cc (__default_terminate): Likewise.
* init.c (perform_member_init): Use new method of expr level
cleanups, instead of cleanups_this_call and friends.
(emit_base_init): Likewise.
(expand_aggr_vbase_init_1): Likewise.
(expand_vec_init): Likewise.
* decl.c (cp_finish_decl): Likewise.
(expand_static_init): Likewise.
(store_parm_decls): Likewise.
(cplus_expand_expr_stmt): Likewise.
* decl2.c (finish_file): Likewise.
* Make-lang.in (exception.o): Ok to compile with -O now.
* decl.c (maybe_build_cleanup_1): We no longer have to unsave, as
we know it will be done later by the backend.
* decl2.c (lang_f_options): Remove support for short temps.
* lang-options.h: Likewise.
Wed Apr 23 04:12:06 1997 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (varargs_function_p): New fn.
* method.c (emit_thunk): Replace broken generic code with code to
generate a heavyweight thunk function.
Tue Apr 22 02:45:18 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (process_template_parm): pedwarn about floating-point parms.
* decl.c (grokdeclarator): inline no longer implies static.
* spew.c (yylex): Always return the TYPE_DECL if we got a scope.
Mon Apr 21 15:42:27 1997 Jason Merrill <jason@yorick.cygnus.com>
* class.c (check_for_override): The signature of an overriding
function is not changed.
* call.c (build_over_call): Move setting of conv into the loop.
Note: this change, along with the related changes of the 18th thru
the 20th of April, fix an infinite loop problem in conversions.
Sun Apr 20 16:24:29 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_user_type_conversion_1): Really ignore rvalue
conversions when looking for a REFERENCE_TYPE.
* cvt.c (build_up_reference): Eviscerate, use build_unary_op.
* cp-tree.h (TREE_REFERENCE_EXPR): #if 0.
* typeck.c (decay_conversion): Don't set TREE_REFERENCE_EXPR.
(build_unary_op): Likewise.
* call.c (build_over_call): See through a CONVERT_EXPR around the
ADDR_EXPR for on a temporary.
* typeck.c (c_expand_return): See through a CONVERT_EXPR around
the ADDR_EXPR for a local variable.
Fri Apr 18 12:11:33 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_user_type_conversion_1): If we're trying to
convert to a REFERENCE_TYPE, only consider lvalue conversions.
(build_new_function_call): Print candidates.
(implicit_conversion): Try a temp binding if the lvalue conv is BAD.
(reference_binding): Binding a temporary of a reference-related type
is BAD.
Thu Apr 17 14:37:22 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* inc/typeinfo (type_info::before): Add cv-qualifier-seq.
* tinfo2.cc (type_info::before): Likewise.
Mon Apr 14 12:38:17 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (implicit_conversion): Oops.
Fri Apr 11 02:18:30 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (implicit_conversion): Try to find a reference conversion
before binding a const reference to a temporary.
Wed Apr 2 12:51:36 1997 Mike Stump <mrs@cygnus.com>
* exception.cc (__default_unexpected): Call terminate by default,
so that if the user overrides terminate, the correct function will
be called.
Wed Mar 19 14:14:45 1997 Mike Stump <mrs@cygnus.com>
* parse.y (left_curly): Avoid trying to use any fields of
error_mark_node, as there aren't any.
Thu Mar 13 16:33:22 1997 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (do_identifier): Avoid breaking on overloaded methods
as default arguments.
Wed Mar 12 13:55:10 1997 Hans-Peter Nilsson <Hans-Peter.Nilsson@axis.se>
* call.c (add_template_candidate): Initialize the variable "dummy".
Mon Mar 10 15:13:14 1997 Brendan Kehoe <brendan@canuck.cygnus.com>
* decl.c (start_decl): Make sure TYPE isn't an error_mark_node
before we try to use TYPE_SIZE and TREE_CONSTANT on it.
Fri Mar 7 13:19:36 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* cp-tree.h (comp_ptr_ttypes, more_specialized): Add decl.
(debug_binfo): Delete decl, not needed.
* tree.c (fnaddr_from_vtable_entry, function_arg_chain,
promotes_to_aggr_type): Delete fns.
* cp-tree.h (FNADDR_FROM_VTABLE_ENTRY,
SET_FNADDR_FROM_VTABLE_ENTRY, FUNCTION_ARG_CHAIN,
PROMOTES_TO_AGGR_TYPE): Delete alternates to #if 1.
* decl.c (pending_invalid_xref{,_file,_line}): Delete unused vars.
* friend.c (is_friend_type): Delete fn.
* cp-tree.h (is_friend_type): Delete decl.
* decl.c (original_result_rtx, double_ftype_double,
double_ftype_double_double, int_ftype_int, long_ftype_long,
float_ftype_float, ldouble_ftype_ldouble, last_dtor_insn): Make static.
* typeck.c (original_result_rtx, warn_synth): Delete extern decls.
* decl.c (push_overloaded_decl{,_top_level}): Make static, adding
fwd decls.
* cp-tree.h (push_overloaded_decl{,_top_level}): Delete decls.
* decl.c (pushdecl_nonclass_level): #if 0, unused.
* cp-tree.h (pushdecl_nonclass_level): #if 0 decl.
* lex.c (reinit_lang_specific): #if 0, unused.
* cp-tree.h (reinit_lang_specific): #if 0 decl.
* decl.c (revert_static_member_fn): Make static, adding fwd decl.
* cp-tree.h (revert_static_member_fn): Delete decl.
* class.c (root_lang_context_p): Delete fn.
* cp-tree.h (root_lang_context_p): Delete decl.
* decl.c (set_current_level_tags_transparency): #if 0, unused.
* cp-tree.h (set_current_level_tags_transparency): #if 0 decl.
* lex.c (set_vardecl_interface_info): Make static.
* cp-tree.h (set_vardecl_interface_info): Delete decl.
* call.c (find_scoped_type): Make static.
* cp-tree.h (find_scoped_type): Delete decl.
* search.c (convert_pointer_to_vbase): Make static.
* cp-tree.h (convert_pointer_to_vbase): Delete decl.
* decl.c (const_ptr_type_node): Likewise.
* cp-tree.h (const_ptr_type_node): Delete decl.
* typeck.c (common_base_type): Make static.
* cp-tree.h (common_base_types): Delete erroneous decl.
* pt.c (classtype_mangled_name): Make static.
* cp-tree.h (classtype_mangled_name): Delete decl.
* lex.c (check_newline): Make static.
* cp-tree.h (check_newline): Delete decl.
* typeck.c (build_x_array_ref): Delete fn, same idea as
grok_array_decl.
* cp-tree.h (build_x_array_ref): Delete decl.
* lex.c (copy_decl_lang_specific): Delete fn, same idea as
copy_lang_decl.
* cp-tree.h (copy_decl_lang_specific): #if 0 decl.
* class.c (build_vtable_entry): Make static.
* cp-tree.h (build_vtable_entry): Delete decl.
* class.c (build_vbase_pointer): Make static.
* cp-tree.h (build_vbase_pointer): Delete decl.
* sig.c (build_sptr_ref): Add forward decl and make static.
* cp-tree.h (build_sptr_ref): Delete decl.
* call.c (build_new_method_call): Add forward decl and make static.
* cp-tree.h (build_new_method_call): Delete decl.
* call.c (build_object_call): Make static.
* class.c (check_for_override, complete_type_p, mark_overriders):
Likewise.
* decl.c (cp_function_chain): Likewise.
* lex.c (set_typedecl_interface_info, reinit_parse_for_block):
Likewise.
* pt.c (comp_template_args, get_class_bindings, push_tinst_level):
Likewise.
* tree.c (build_cplus_array_type_1): Likewise.
* typeck.c (comp_ptr_ttypes_{const,real,reinterpret}): Likewise.
(comp_target_parms): Likewise.
* init.c (build_builtin_call): Make static.
* cp-tree.h (build_builtin_call): Delete decl.
* typeck.c (binary_op_error): Delete decl.
* cp-tree.h (binary_op_error): Likewise.
Thu Mar 6 16:13:52 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* call.c (build_method_call): Compare against error_mark_node
directly, rather than the ERROR_MARK tree code.
* cvt.c (cp_convert): Likewise.
* decl.c (print_binding_level): Likewise.
(duplicate_decls): Likewise.
(grokdeclarator): Likewise.
(grokdeclarator): Likewise.
* init.c (expand_aggr_init_1): Likewise.
(decl_constant_value): Likewise.
* method.c (build_opfncall): Likewise.
(hack_identifier): Likewise.
* typeck.c (build_modify_expr): Likewise.
* typeck.c (build_c_cast): Don't decl TYPE as register tree.
Sun Mar 2 02:54:36 1997 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* pt.c (unify): Strip NOP_EXPR wrappers before unifying integer values.
* pt.c (coerce_template_parms): Add new error message.
* method.c (build_overload_value): Implement name mangling for
floating-point template arguments.
* method.c (build_overload_int, icat, dicat): Fix mangling of template
arguments whose absolute value doesn't fit in a signed word.
Mon Mar 3 12:14:54 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* friend.c: New file; put all of the friend stuff in here.
* init.c: Instead of here.
* Makefile.in (CXX_OBJS): Add friend.o.
(friend.o): Add dependencies.
* Make-lang.in (CXX_SRCS): Add $(srcdir)/cp/friend.c.
Sun Mar 2 11:04:43 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_scoped_method_call): Complain if the scope isn't a
base.
Wed Feb 26 11:31:06 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (left_curly): Don't crash on erroneous type.
* init.c (build_delete): Fix type of ref.
Tue Feb 25 12:41:48 1997 Jason Merrill <jason@yorick.cygnus.com>
* search.c (get_vbase_1): Renamed from get_vbase.
(get_vbase): Wrapper, now non-static.
(convert_pointer_to_vbase): Now static.
* call.c (build_scoped_method_call): Accept a binfo for BASETYPE.
* init.c (build_delete): Pass one.
(build_partial_cleanup_for): Use build_scoped_method_call.
* decl.c (finish_function): Pass a binfo.
Mon Feb 24 15:00:12 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Only synthesize non-trivial copy ctors.
* typeck.c (build_c_cast): Lose other reference to flag.
* call.c (build_field_call): Don't look for [cd]tor_identifier.
* decl2.c (delete_sanity): Remove meaningless use of
LOOKUP_HAS_IN_CHARGE.
* decl.c (finish_function): Use build_scoped_method_call instead
of build_delete for running vbase dtors.
* init.c (build_delete): Call overload resolution code instead of
duplicating it badly.
Thu Feb 20 15:12:15 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Call mark_used before trying to elide
the call.
* decl.c (implicitly_declare): Don't set DECL_ARTIFICIAL.
Wed Feb 19 11:18:53 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (build_modify_expr): Always pedwarn for a cast to
non-reference used as an lvalue.
Wed Feb 19 10:35:37 1997 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert_to_pointer): Convert from 0 to a pmf properly.
Tue Feb 18 15:40:57 1997 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (handler): Fix template typo.
Sun Feb 16 02:12:28 1997 Jason Merrill <jason@yorick.cygnus.com>
* error.c (lang_decl_name): New fn.
* tree.c (lang_printable_name): Use it.
Fri Feb 14 16:57:05 1997 Mike Stump <mrs@cygnus.com>
* g++spec.c: Include config.h so that we can catch bzero #defines
from the config file.
Tue Feb 11 13:50:48 1997 Mike Stump <mrs@cygnus.com>
* new1.cc: Include a declaration for malloc, to avoid warning, and
avoid lossing on systems that require one (ones that define malloc
in xm.h).
Mon Feb 10 22:51:13 1997 Bruno Haible <bruno@linuix.mathematik.uni-karlsruhe.de>
* decl2.c (max_tinst_depth): New variable.
(lang_decode_option): Parse "-ftemplate-depth-NN" command line
option.
* pt.c (max_tinst_depth): Variable moved.
* lang-options.h: Declare "-ftemplate-depth-NN" command line option
as legal.
Fri Feb 7 15:43:34 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (xref_basetypes): Allow a base class that depends on
template parms to be incomplete.
* decl2.c (build_expr_from_tree): Support typeid(type).
* rtti.c (get_typeid): Support templates.
(expand_si_desc, expand_class_desc): Fix string length.
(expand_ptr_desc, expand_attr_desc, expand_generic_desc): Likewise.
Tue Feb 4 11:28:24 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (unify, case TEMPLATE_CONST_PARM): Use cp_tree_equal.
* pt.c (tsubst): Put it back for -fno-ansi-overloading.
Mon Feb 3 18:41:12 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst, case FUNCTION_DECL): Lose obsolete code that
smashes together template and non-template decls of the same
signature.
Thu Jan 30 19:18:00 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Don't recurse for the type of a TYPENAME_TYPE.
Wed Jan 29 11:40:35 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (duplicate_decls): Next route, pedwarn about different
exceptions if -pedantic *or* olddecl !DECL_IN_SYSTEM_HEADER.
Tue Jan 28 20:43:29 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* cp-tree.h (HAS_DEFAULT_IMPLEMENTATION): Delete macro.
(struct lang_type): Delete has_default_implementation member.
Increase dummy to 21.
* decl.c (start_method): Delete usage.
* cp-tree.h (build_call, null_ptr_cst_p, in_function_p,
store_after_parms, start_decl_1, auto_function): Add decls.
(get_arglist_len_in_bytes, declare_implicit_exception,
have_exceptions_p, make_type_decl, typedecl_for_tag,
store_in_parms, pop_implicit_try_blocks, push_exception_cleanup,
build_component_type_expr, cplus_exception_name,
{make,clear}_anon_parm_name, dont_see_typename): Removed decls.
* call.c (build_this): Make static.
(is_complete): Likewise.
(implicit_conversion): Likewise.
(reference_binding): Likewise.
(standard_conversion): Likewise.
(strip_top_quals): Likewise.
(non_reference): Likewise.
(build_conv): Likewise.
(user_harshness): Likewise.
(rank_for_ideal): Likewise.
* decl.c (start_decl_1): Delete forward decl.
(push_decl_level): Make static.
(resume_binding_level): Make static.
(namespace_bindings_p): Make static.
(declare_namespace_level): Make static.
(lookup_name_real): Make static.
(duplicate_decls): Make static. Take register off NEWDECL and
OLDDECL parm decls.
* decl2.c (get_sentry): Make static.
(temp_name_p): Delete fn.
* except.c (auto_function): Delete decl.
* lex.c (handle_{cp,sysv}_pragma): Make static.
(handle_sysv_pragma) [HANDLE_SYSV_PRAGMA]: Add forward decl.
* method.c (do_build_{copy_constructor,assign_ref}): Make static.
* pt.c (tsubst_expr_values): Make static.
* rtti.c (combine_strings): Delete decl.
Tue Jan 28 16:40:40 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (push_template_decl): Handle getting a typedef.
* call.c (build_new_function_call): Complain about void arg.
Tue Jan 28 15:25:09 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (duplicate_decls): Give pedwarn of different exceptions
if -pedantic, instead of olddecl !DECL_IN_SYSTEM_HEADER.
Mon Jan 27 19:21:29 1997 Mike Stump <mrs@cygnus.com>
* except.c (expand_throw): Don't expand the cleanup tree here,
since we are not going to write the rtl out. Fixes problem with
-g -O on SPARC.
Mon Jan 27 16:24:35 1997 Sean McNeil <sean@mcneil.com>
* Make-lang.in: Add $(exeext) as necessary.
Mon Jan 27 13:20:39 1997 Mike Stump <mrs@cygnus.com>
* parse.y (handler_seq): Must have at least one catch clause.
Sat Jan 25 12:00:05 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (add_builtin_candidate): Restore ?: hack.
* decl.c (grok_op_properties): More warnings.
Sat Jan 25 08:50:03 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (duplicate_decls): On second thought, do it as a pedwarn
still but only if !DECL_IN_SYSTEM_HEADER (olddecl).
* decl.c (duplicate_decls): Scale back to a warning, and only do
'em if -pedantic.
Fri Jan 24 17:52:54 1997 Mike Stump <mrs@cygnus.com>
* decl.c (duplicate_decls): pedwarn mismatched exception
specifications.
Thu Jan 23 18:18:54 1997 Mike Stump <mrs@cygnus.com>
* call.c (build_new_method_call): Don't display the invisible
argument for controlling virtual bases.
Thu Jan 23 16:48:10 1997 Mike Stump <mrs@cygnus.com>
* new: Add nothrow new and delete, bad_alloc and throw specifications
for delete.
* decl.c (init_decl_processing): Add throw specification for delete.
* new.cc (nothrow): Define.
* lex.c (real_yylex): Removing warning that throw and friends are
keywords.
* new1.cc (operator new (size_t sz, const nothrow_t&)): Define.
* new2.cc (operator new[] (size_t sz, const nothrow_t&): Define.
* Make-lang.in: Add new{1,2}.{cc,o}.
Thu Jan 23 16:39:06 1997 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (cons_up_default_function): Fix return type of synth op=.
* init.c (emit_base_init): Add warnings for uninitialized members
and bases.
* decl.c (xref_basetypes): Add warning for non-polymorphic type
with destructor used as base type.
* decl.c (grok_op_properties): Add warning for op= returning void.
* typeck.c (c_expand_return): Add warning for op= returning anything
other than *this.
* class.c (finish_struct_1): Add warning for class with pointers
but not copy ctor or copy op=.
* cp-tree.h (TI_PENDING_TEMPLATE_FLAG): New macro.
* pt.c (add_pending_template): Use it instead of LANG_FLAG_0.
(instantiate_template): If -fexternal-templates, add this
instantiation to pending_templates.
* decl2.c (copy_assignment_arg_p): Disable old hack to support
Booch components.
Tue Jan 21 18:32:04 1997 Mike Stump <mrs@cygnus.com>
* cvt.c (cp_convert): pedwarn enum to pointer conversions.
Mon Jan 20 17:59:51 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (standard_conversion): Handle getting references. Tack
on RVALUE_CONV here. Do it for non-class types, too.
(reference_binding): Pass references to standard_conversion.
(implicit_conversion): Likewise.
(add_builtin_candidate): Disable one ?: kludge.
(convert_like): Handle RVALUE_CONVs for non-class types.
(joust): Disable the other ?: kludge.
Mon Jan 20 14:53:13 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (init_decl_processing): Add code to build up common
function types beforehand, to avoid creation then removal of
things already in the hash table.
Mon Jan 20 14:43:49 1997 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (finish_function): Also zero out DECL_INCOMING_RTL for
the arguments.
* error.c (dump_expr, TEMPLATE_CONST_PARM): Don't require
current_template_parms.
Fri Jan 17 10:25:42 1997 Jason Merrill <jason@yorick.cygnus.com>
* search.c (lookup_field): Don't return a function, check want_type.
Thu Jan 16 18:14:35 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* init.c (build_new): Make sure PLACEMENT has a type.
Thu Jan 16 17:40:28 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): Support new (nothrow).
Wed Jan 15 12:38:14 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Also do push_to_top_level before setting
up DECL_INITIAL.
* cp-tree.h (PARM_DEFAULT_FROM_TEMPLATE): New macro.
* pt.c (tsubst): Defer instantiation of default args.
* call.c (build_over_call): Until here.
Wed Jan 15 10:08:10 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* search.c (lookup_field): Make sure we have an
IDENTIFIER_CLASS_VALUE before we try to return it.
Thu Jan 9 07:19:01 1997 Brendan Kehoe <brendan@lisa.cygnus.com>
* call.c (build_method_call): Delete unused var PARM.
(build_overload_call_real): Likewise.
(build_object_call): Delete unused var P.
(build_new_op): Likewise.
* decl.c (builtin_type_tdescs_{arr, len, max}): #if 0 out static
var definitions, which are never used.
(shadow_tag): Delete unused var FN.
* expr.c (cplus_expand_expr): Delete unused var ORIGINAL_TARGET.
* init.c (build_new): Delete unused var ALLOC_TEMP.
* method.c (hack_identifier): Delete unused var CONTEXT.
(do_build_copy_constructor): Delete unused var NAME.
(synthesize_method): Delete unused var BASE.
* pt.c (lookup_template_class): Delete unused var CODE_TYPE_NODE.
* rtti.c (build_headof): Delete unused var VPTR.
(get_typeid): Delete unused var T.
* typeck.c (build_conditional_expr): Delete unused vars ORIG_OP1
and ORIG_OP2.
(build_ptrmemfunc): Delete unused vars U and NINDEX.
* typeck2.c (build_functional_cast): Delete unused var BINFO.
Wed Jan 8 13:09:54 1997 Jason Merrill <jason@yorick.cygnus.com>
* search.c (lookup_field): Use IDENTIFIER_CLASS_VALUE to look up
things in a type being defined.
* decl.c (finish_enum): Reverse the values so that they are in
the correct order.
* pt.c (instantiate_class_template): Don't initialize
BINFO_BASETYPES until the vector is filled out.
(unify): Don't abort on conflicting bindings, just fail.
(instantiate_decl): Do push_tinst_level before any tsubsting.
* method.c (build_overload_value): Handle getting a
TEMPLATE_CONST_PARM for a pointer.
Tue Jan 7 14:00:58 1997 Jason Merrill <jason@yorick.cygnus.com>
* init.c (expand_member_init): Don't give 'not a base' error for
templates.
* pt.c (instantiate_decl): Call import_export_decl later.
* pt.c (instantiate_class_template): Return a value.
* parse.y (extension): New rule for __extension__.
(extdef, unary_expr, decl, component_decl): Use it.
Tue Jan 7 09:20:28 1997 Mike Stump <mrs@cygnus.com>
* class.c (base_binfo): Remove unused base_has_virtual member.
(finish_base_struct): Likewise.
(finish_struct_1): Likewise.
Tue Dec 31 20:25:50 1996 Mike Stump <mrs@cygnus.com>
* search.c (expand_upcast_fixups): Fix bogus code generation
problem where the generated code uses the wrong index into the
runtime built vtable on the stack. Old code could clobber random
stack values.
Tue Dec 31 15:16:56 1996 Mike Stump <mrs@cygnus.com>
* init.c (perform_member_init): Make sure the partial EH cleanups
live on the function_obstack.
Fri Dec 27 10:31:40 1996 Paul Eggert <eggert@twinsun.com>
* Make-lang.in (g++spec.o): Don't use $< with an explicit target;
this isn't portable to some versions of `make' (e.g. Solaris 2.5.1).
Tue Dec 24 10:24:03 1996 Jeffrey A Law <law@cygnus.com>
* decl.c (grokvardecl): Avoid ANSI style initialization.
Sun Dec 22 04:22:06 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Tweak arg types for a FUNCTION_TYPE.
Fri Dec 20 17:09:25 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Call grok_{ctor,op}_properties.
Fri Dec 20 12:17:12 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* g++spec.c (lang_specific_driver): Put missing hyphen in front of
arguments we compare against. Start the count of I at 1, not 0,
since argv[0] is still the command.
Thu Dec 19 11:53:57 1996 Stan Shebs <shebs@andros.cygnus.com>
* lang-specs.h: Accept .cp as an C++ extension.
Mon Dec 16 22:43:31 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* cp-tree.h (ptr_reasonably_similar): Add decl.
Thu Dec 12 15:00:35 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (grokvardecl): Change SPECBITS parm to be the SPECBITS_IN
pointer. New local SPECBITS with the parm's value.
(grokdeclarator): Pass &specbits down.
* parse.y (expr_no_commas): Make sure $$ is not an error_mark_node
before we try to do C_SET_EXP_ORIGINAL_CODE on it.
* search.c (envelope_add_decl): Check that the CLASSTYPE_CID of
CONTEXT is not 0 before we try to use TYPE_DERIVES_FROM.
* decl.c (cplus_expand_expr_stmt): Only expand the expr if EXP is
not an error_mark_node.
Sat Dec 7 17:20:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (TYPE_MAIN_DECL): Use TYPE_STUB_DECL.
* *.c: Use TYPE_MAIN_DECL instead of TYPE_NAME where appropriate.
Fri Dec 6 14:40:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): When giving an anonymous struct a name,
replace TYPE_NAME instead of TYPE_IDENTIFIER (so TYPE_STUB_DECL is
not affected).
* typeck2.c (build_m_component_ref): If component is a pointer
to data member, resolve the OFFSET_REF now.
* call.c (convert_like): Don't go into infinite recursion.
* pt.c (coerce_template_parms): Use tsubst_expr for non-type args.
* class.c (finish_struct_1): Set DECL_ARTIFICIAL on the vptr.
* tree.c (layout_basetypes): And on the vbase ptr.
Thu Dec 5 02:11:28 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (BOOL_TYPE_SIZE): Define in terms of POINTER_SIZE or
CHAR_TYPE_SIZE so bool is always the same size as another type.
* decl.c (pushtag): Set DECL_IGNORED_P for DWARF, too.
Tue Dec 3 23:18:37 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (grok_x_components): Remove synthesized methods from
TYPE_METHODS of an anonymous union, complain about member
functions.
* decl.c (shadow_tag): Wipe out memory of synthesized methods in
anonymous unions.
(finish_function): Just clear the DECL_RTL of our arguments.
Fri Nov 29 21:54:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Emit DWARF debugging info for static data
members.
* pt.c (tsubst): If t is a stub decl, return the stub decl for type.
Wed Nov 27 14:47:15 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (build_component_ref): Don't die if COMPONENT isn't a
IDENTIFIER_NODE.
Wed Nov 27 16:05:19 1996 Michael Meissner <meissner@tiktok.cygnus.com>
* Make-lang.in (g++-cross$(exeext)): Fix typo.
Wed Nov 27 08:14:00 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
Make the g++ driver now be a standalone program, rather than one
that tries to run the gcc driver after munging up the options.
* Make-lang.in (g++.c, g++spec.o): New rules.
(g++.o): New rule, based on gcc.o with -DLANG_SPECIFIC_DRIVER
added.
(g++$(exeext)): New rule, based on xgcc rule.
(g++-cross$(exeext)): Now just copies g++$(exeext) over.
* g++spec.c: New file.
* g++.c: Removed file.
Tue Nov 26 19:01:09 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (build_up_reference): Arrange for any temporary values
that have been keep in registers until now to be put into memory.
Mon Nov 25 15:16:41 1996 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Make-lang.in (c++.stage[1234]): Depend upon stage[1-4]-start, so
that make -j3 bootstrap works better.
Sun Nov 24 02:09:39 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (pushtag): Do pushdecl for anon tags.
Thu Nov 21 16:30:24 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (c_expand_return): Fix logic.
(unary_complex_lvalue): Avoid unused warning on address of INIT_EXPR.
Wed Nov 20 18:47:31 1996 Bob Manson <manson@charmed.cygnus.com>
* g++.c (main): Make sure arglist has a final NULL entry. Add
PEXECUTE_LAST to the flags passed to pexecute, as otherwise
stdin/stdout of the invoked program are redirected to
nowheresville.
Tue Nov 19 16:12:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (implicitly_declare): Set DECL_ARTIFICIAL.
Tue Nov 19 15:48:19 1996 Mike Stump <mrs@cygnus.com>
* init.c (resolve_offset_ref): Handle obj.vfn better.
* typeck.c (build_component_ref): Set TREE_TYPE on result from
build_vfn_ref.
Tue Nov 19 13:14:33 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (convert_for_assignment): Also handle anachronistic
implicit conversions from (::*)() to cv void*.
* cvt.c (cp_convert_to_pointer): Likewise.
Mon Nov 18 17:05:26 1996 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (handle_cp_pragma): Fix bogus warning.
Mon Nov 18 16:10:43 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (cp_convert_to_pointer): Avoid thinking a POINTER_TYPE
(METHOD_TYPE) is a TYPE_PTRMEMFUNC_P.
Thu Nov 14 23:18:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): Support DWARF2_DEBUG.
* search.c (dfs_debug_mark): Likewise.
* decl2.c (finish_vtable_vardecl): Likewise.
* decl.c (pushtag, finish_enum): Likewise.
* lex.c (check_newline): Use debug_* instead of calling *out
functions directly.
Thu Nov 14 15:21:46 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* Make-lang.in (cplib2.ready): Add else clause to avoid problems
on some picky hosts.
Wed Nov 13 12:32:07 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): A class has a non-trivial copy
constructor if it has virtual functions.
* cvt.c (cp_convert): Always call a constructor.
* call.c (reference_binding): Still tack on a REF_BIND
for bad conversions.
(build_user_type_conversion_1): Propagate ICS_BAD_FLAG.
* typeck.c (convert_arguments): Pass LOOKUP_ONLYCONVERTING.
(c_expand_return): Likewise.
* typeck2.c (digest_init): Likewise for { }.
* init.c (expand_aggr_init_1): Keep the CONSTRUCTOR handling.
* cvt.c (cp_convert): Handle failure better.
Wed Nov 13 11:51:20 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* g++.c (main): Also set PEXECUTE_SEARCH, to make the invocation
of GCC be path-relative.
Wed Nov 13 11:27:16 1996 Michael Meissner <meissner@tiktok.cygnus.com>
* Make-lang.in (g++-cross): G++-cross doesn't need version.o, but
it does need choose-temp.o and pexecute.o.
Wed Nov 13 07:53:38 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* g++.c (error) [!HAVE_VPRINTF]: Put error back for the only time
that we still use it.
(P_tmpdir, R_OK, W_OK, X_OK) [__MSDOS__]: Delete unnecessary macros.
Wed Nov 13 02:00:26 1996 Jason Merrill <jason@yorick.cygnus.com>
* init.c (expand_default_init): Avoid calling constructors to
initialize reference temps.
* cvt.c (convert_to_reference): Fix.
Tue Nov 12 19:10:07 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert): Simplify for flag_ansi_overloading.
(convert_to_reference): Likewise.
* typeck.c (convert_for_initialization): Likewise.
* init.c (expand_default_init): Likewise.
(expand_aggr_init_1): Likewise.
* cp-tree.h (CONV_NONCONVERTING): Lose.
* typeck.c (build_c_cast): Lose allow_nonconverting parm.
* *.c: Adjust.
* call.c (build_user_type_conversion_1): Assume LOOKUP_ONLYCONVERTING.
Tue Nov 12 16:29:04 1996 Brendan Kehoe <brendan@canuck.cygnus.com>
* pt.c (tsubst_expr): Reverse args to expand_start_catch_block.
Tue Nov 12 15:26:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* init.c (expand_aggr_init_1): Don't crash on non-constructor
TARGET_EXPR.
Tue Nov 12 14:00:50 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* g++.c: Include gansidecl.h.
(VPROTO, PVPROTO, VA_START): Delete.
(choose_temp_base_try, choose_temp_base, perror_exec,
run_dos) [__MSDOS__]: Delete fns.
(pfatal_with_name): Delete fn.
(temp_filename): Declare like in gcc.c.
(pexecute, pwait, choose_temp_base): Declare from gcc.c.
(error_count, signal_count): Define.
(error): Delete both definitions.
(PEXECUTE_{FIRST,LAST,SEARCH,VERBOSE}): Define from gcc.c.
(pfatal_pexecute): Add fn from gcc.c.
(main): Rename local VERBOSE var to VERBOSE_FLAG. Rewrite the
code to use the pexecute stuff also used by gcc.c.
(MIN_FATAL_STATUS): Define.
* Make-lang.in (g++): Add dependency on and linking with
choose-temp.o and pexecute.o.
* cp-tree.h: Include gansidecl.h.
(STDIO_PROTO): Delete #undef/#define.
* cvt.c (NULL): Delete #undef/#define.
* expr.c (NULL): Likewise.
* init.c (NULL): Likewise.
* rtti.c (NULL): Likewise.
* xref.c (NULL): Likewise.
* cp-tree.h (build_user_type_conversion): Add prototype.
* call.c (build_user_type_conversion): Delete prototype. Correct
decl of FLAGS arg to be an int.
* cvt.c (build_user_type_conversion): Likewise.
Tue Nov 12 12:16:20 1996 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.def: Add TRY_BLOCK and HANDLER.
* except.c (expand_start_catch_block): Support templates.
* parse.y (try_block, handler_seq): Likewise.
* pt.c (tsubst_expr): Support TRY_BLOCK and HANDLER.
Mon Nov 11 13:57:31 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (current_template_args): New fn.
(push_template_decl): Use it.
* decl.c (grokdeclarator): Use it.
* decl2.c (build_expr_from_tree): Dereference ref vars.
* decl.c (grokdeclarator): Generalize handling of TYPENAME_TYPEs in
the decl-specifier-seq.
* decl.c (grok_op_properties): Don't force the type of a conversion
op to be complete. Don't warn about converting to the same type
for template instantiations.
* decl2.c (finish_file): Don't call instantiate_decl on synthesized
methods.
Mon Nov 11 13:20:34 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (get_delta_difference): Remove previous bogusness.
Don't give errors if force is set.
Fri Nov 8 17:38:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Don't emit debug info.
* decl.c (pushdecl): Lose obsolete code.
(grokdeclarator): Still do the long long thing after complaining.
* search.c (note_debug_info_needed): Don't do anything if we're in a
template.
* method.c (synthesize_method): For non-local classes,
push_to_top_level first.
Fri Nov 8 11:52:28 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (get_delta_difference): Add no_error parameter.
(build_ptrmemfunc): Call get_delta_difference with no_error set;
we don't want error messages when converting unrelated
pointer-to-member functions.
Thu Nov 7 11:16:24 1996 Mike Stump <mrs@cygnus.com>
* error.c (dump_expr): Improve the wording on error messages that
involve pointer to member functions.
Tue Nov 5 17:12:05 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (cp_convert_to_pointer): Move code for conversions from
(::*)() to void* or (*)() up a bit, so that we can convert from
METHOD_TYPEs as well.
Tue Nov 5 14:54:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (get_tinfo_fn): Make sure 'type' is permanent.
There are no 'member' types.
(get_tinfo_fn_dynamic): Diagnose typeid of overloaded fn.
(build_x_typeid): Handle errors.
Mon Nov 4 17:43:12 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (convert_for_assignment): Handle anachronistic implicit
conversions from (::*)() to void* or (*)().
* cvt.c (cp_convert_to_pointer): Likewise.
(cp_convert_to_pointer_force): Remove cp_convert_to_pointer
conversions from here.
* decl2.c (lang_decode_option): Add -W{no-,}pmf-conversions.
* lang-options.h: Likewise.
* decl2.c (warn_pmf2ptr): Define.
* cp-tree.h: Declare it.
* typeck2.c (digest_init): Allow pmfs down into
convert_for_initialization.
Sun Nov 3 09:43:00 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (c_expand_return): Fix for returning overloaded fn.
Fri Nov 1 08:53:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (DIRECT_BIND): Change from INDIRECT_BIND.
* decl.c (grok_reference_init): Pass DIRECT_BIND.
* cvt.c (build_up_reference): Don't mark 'this' addressable. Use
DIRECT_BIND.
* call.c (convert_like): Don't pass INDIRECT_BIND.
* typeck.c (convert_arguments): Likewise.
* typeck.c (mark_addressable): Allow &this if flag_this_is_variable.
Thu Oct 31 17:08:49 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (mark_addressable): Support TARGET_EXPR, unify with
similar code in build_up_ref.
* cvt.c (build_up_reference): Drastically simplify.
Mon Oct 28 12:45:05 1996 Jeffrey A Law <law@cygnus.com>
* typeck.c (signed_or_unsigned_type): If the given type already
as the correct signedness, then just return it.
* typeck.c ({un,}signed_type): If can't do anything, call
signed_or_unsigned_type.
Thu Oct 24 14:21:59 1996 Bob Manson <manson@charmed.cygnus.com>
* decl2.c (copy_assignment_arg_p): Don't buy the farm if
current_class_type is NULL.
Wed Oct 23 00:43:10 1996 Jason Merrill <jason@gerbil.cygnus.com>
* class.c (finish_struct_1): Avoid empty structs by adding a field
so layout_type gets the mode right.
* typeck.c (c_expand_return): Drastically simplify.
Mon Oct 21 22:34:02 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (decay_conversion): Handle overloaded methods.
Fri Oct 18 16:03:48 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): A TARGET_EXPR has side-effects.
Thu Oct 17 11:31:59 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (convert_to_pointer_force): Add code to support pointer to
member function to pointer to function conversions.
* init.c (resolve_offset_ref): Add code to allow faked up objects,
ignoring them if they are not used, and giving an error, if they
are needed.
* typeck.c (get_member_function_from_ptrfunc): Fold e1 to improve
code, and so that we can give an error, if we needed an object,
and one was not provided.
(build_c_cast): Don't call default_conversion when we want to
convert to pointer to function from a METHOD_TYPE.
Mon Oct 14 00:28:51 1996 Jason Merrill <jason@yorick.cygnus.com>
* Make-lang.in (cplib2.ready): Fix logic.
* decl.c (shadow_tag): Only complain about non-artificial function
members.
* class.c (finish_struct_1): Add synthesized methods to TYPE_METHODS.
Fri Oct 11 16:12:40 1996 Jason Merrill <jason@yorick.cygnus.com>
* expr.c (cplus_expand_expr): Pre-tweak call_target like
expand_inline_function would.
* pt.c (mark_decl_instantiated): If extern_p, call
mark_inline_for_output.
Thu Oct 10 15:58:08 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (unary_complex_lvalue): Add code to handle intermediate
pmd conversions.
* typeck.c (get_delta_difference): Fix wording, as we can be used
for pointer to data members.
Tue Oct 8 12:43:51 1996 Bob Manson <manson@charmed.cygnus.com>
* pt.c (tsubst): If the function decl isn't a member of this
template, return a copy of the decl (including copying the
lang-specific part) so we don't hose ourselves later.
Thu Oct 3 16:24:28 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct): Remove DWARF-specific tag handling.
* decl.c (pushtag): Likewise.
(finish_function): Always clear DECL_ARGUMENTS on function decls with
no saved RTX.
* decl2.c (finish_file): Emit DWARF debugging info for static data
members.
Wed Oct 2 21:58:01 1996 Bob Manson <manson@charmed.cygnus.com>
* decl.c (duplicate_decls): Make sure the old DECL_LANG_SPECIFIC
isn't the same as the new one before we whack it.
Mon Sep 30 13:38:24 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c, cp-tree.h, cvt.c, decl.c, decl2.c, gxx.gperf, hash.h,
lex.c, method.c, parse.y, typeck.c, typeck2.c: Remove
warn_traditional and warn_strict_prototypes; remove ancient
'overload' code; remove references to flag_traditional.
Mon Sep 30 12:58:40 1996 Mike Stump <mrs@cygnus.com>
* input.c (sub_getch): Handle 8-bit characters in string literals.
Sun Sep 29 03:12:01 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (mapcar): Handle CONSTRUCTORs.
(copy_to_permanent): Handle expression_obstack properly.
* Make-lang.in (cplib2.txt): Also depend on the headers.
* rtti.c (get_tinfo_var): Don't assume that POINTER_SIZE ==
INT_TYPE_SIZE.
(expand_class_desc): Use USItype for offset field.
* tinfo.h (struct __class_type_info): Likewise.
* method.c (build_overload_int): TYPE_PRECISION should be applied
to types.
Sat Sep 28 14:44:50 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_op): A COND_EXPR involving void must be a
builtin.
Fri Sep 27 16:40:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_x_component_ref): New fn.
(build_object_ref): Use it.
* parse.y (primary): Use it.
* decl2.c (build_expr_from_tree): Use it.
* cp-tree.h: Declare it.
* decl.c (start_decl): Variable-sized arrays cannot be initialized.
* error.c (dump_type_suffix): Handle variable arrays.
Fri Sep 27 13:14:05 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* Make-lang.in (exception.o): Put back compiling it with -fPIC.
Fri Sep 27 03:00:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): Don't try to look up anything in a
TYPENAME_TYPE.
* tinfo2.cc (__throw_type_match_rtti): Oops.
Thu Sep 26 22:11:05 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* Make-lang.in (exception.o): Use -fno-PIC for now.
Thu Sep 26 10:59:00 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (build_dynamic_cast): Pass tinfo fns rather than
calling them.
(get_tinfo_fn_dynamic): Extracted from build_typeid.
* tinfo2.cc (__dynamic_cast): Adjust.
* rtti.c (build_typeid): Use resolves_to_fixed_type_p.
(build_x_typeid): Likewise.
* parse.y: Call build_x_typeid instead of build_typeid.
* cp-tree.def: Add TYPEID_EXPR.
* pt.c (tsubst_copy): Handle typeid.
* decl2.c (build_expr_from_tree): Likewise.
* rtti.c (build_x_typeid): Throw bad_typeid from here.
(build_typeid): Not here.
* cp-tree.h: Declare build_x_typeid.
Wed Sep 25 17:26:16 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (convert_like): Pull out constant values.
* tree.c (mapcar): Use build_cplus_array_type, not build_array_type.
Wed Sep 25 17:28:53 1996 Michael Meissner <meissner@tiktok.cygnus.com>
* decl.c (init_decl_processing): Create short int types before
creating size_t in case a machine description needs to use
unsigned short for size_t.
Tue Sep 24 18:18:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* Make-lang.in (exception.o): Turn off pic.
* tinfo2.cc (__throw_type_match_rtti): Fix cv-variants of the same
type, multi-level ptr conversions.
* rtti.c (call_void_fn): Renamed and genericized from throw_bad_cast.
(throw_bad_cast): Use it.
(throw_bad_typeid): New fn.
(build_typeid): Throw bad_typeid as needed.
Use build_call.
(synthesize_tinfo_fn): Handle functions and arrays before checking
for cv-quals.
* Remove .h from standard C++ headers, add new.h, move into inc
subdirectory.
* exception*: Remove pointer from object, constructors. Add
default exception::what that uses type_info::name. Add
__throw_bad_typeid.
* init.c (build_new): Don't add a cookie to new (void *) T[2].
Mon Sep 23 15:21:53 1996 Jason Merrill <jason@yorick.cygnus.com>
* Make-lang.in: Building C++ code depends on cc1plus.
Mon Sep 23 12:38:40 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (struct saved_scope): Declare PROCESSING_TEMPLATE_DECL as
a HOST_WIDE_INT, not a tree.
Mon Sep 23 12:36:02 1996 Jason Merrill <jason@yorick.cygnus.com>
* exception.cc: Don't include <stdlib.h>.
* Make-lang.in (c++.clean): Remove cplib2.*.
Mon Sep 23 09:42:19 1996 Doug Evans <dje@canuck.cygnus.com>
* parse.y (component_decl_1, component_costructor_declarator case):
Pass attributes/prefix_attributes in tree list.
Mon Sep 23 01:18:50 1996 Jason Merrill <jason@yorick.cygnus.com>
* tinfo{,2}.cc: #include <stddef.h> instead of <stdlib.h>.
Sun Sep 22 05:31:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (do_identifier): Don't do deferred lookup in a template
header.
* typeck2.c (store_init_value): Oops.
* new.{h,cc}, exception.{h,cc}, typeinfo.h, tinfo{2.cc,.cc,.h}:
New files for C++ lang-support library.
* Make-lang.in (CXX_EXTRA_HEADERS): Define.
(CXX_LIB2FUNCS): Define.
And rules for building the C++ lang-support code.
* config-lang.in (headers): Define.
(lib2funcs): Define.
Sat Sep 21 19:17:28 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (build_expr_from_tree): If CONSTRUCTOR has a type, call
digest_init.
* pt.c (tsubst_copy): Compute type for CONSTRUCTOR.
* typeck2.c (store_init_value): Check for initializing pmf with { }
here.
(process_init_constructor): Not here.
Thu Sep 19 16:41:07 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (begin_template_parm_list): Increment
processing_template_decl here.
(end_template_parm_list): Not here.
(process_template_parm): No need to add 1 to it now.
* *.c: Use processing_template_decl instead of current_template_parms
to check for being in a template.
* pt.c (uses_template_parms): Handle SCOPE_REF. Fix CONSTRUCTOR.
(tsubst_copy): Handle CONSTRUCTOR.
(instantiate_decl): Set up context properly for variables.
* decl2.c (build_expr_from_tree): Handle CONSTRUCTOR.
* class.c (finish_struct): Reverse CLASSTYPE_TAGS.
Wed Sep 18 13:30:20 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* lex.c (enum tree_node_kind) [GATHER_STATISTICS]: Put the enum back.
Wed Sep 18 04:24:07 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (make_thunk): Call comdat_linkage before setting the
TREE_CODE.
* decl2.c (comdat_linkage): Use make_decl_one_only.
(import_export_decl): Likewise.
* decl.c (init_decl_processing): Check supports_one_only instead of
SUPPORTS_WEAK.
Sat Sep 14 08:34:41 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (grokfield): Tighten checking for access decls.
* decl.c (make_typename_type): Resolve references to
current_class_type. Set CLASSTYPE_GOT_SEMICOLON.
(lookup_name_real): Types that depend on a template parameter get
an implicit 'typename' unless they're in the current scope.
(start_decl_1): We don't care about incomplete types that depend
on a template parm.
(grokdeclarator): Resolve 'typename's in the type specifier that
refer to members of the current scope.
* call.c (build_over_call): Remove 'inline called before
definition' diagnostic.
(build_method_call): Likewise.
* decl.c (duplicate_decls): Downgrade 'used before declared
inline' to a warning, only with -Winline.
Fri Sep 13 17:31:40 1996 Stan Shebs <shebs@andros.cygnus.com>
* mpw-make.sed: Fix include paths, add @DASH_C_FLAG@ to compile.
Wed Sep 11 22:38:13 1996 Gerald Baumgartner <gb@cs.purdue.edu>
* call.c (build_method_call): When calling a signature
default implementation, as in other cases, let instance_ptr simply
be instance.
Wed Sep 11 22:14:44 1996 Mike Stump <mrs@cygnus.com>
* parse.y (simple_stmt): Cleanup and use do_poplevel ().
Wed Sep 11 22:10:48 1996 Mike Stump <mrs@cygnus.com>
* except.c (expand_start_catch_block): Add a pushlevel so that -g
works on hppa and SPARC.
Wed Sep 11 10:18:06 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (build_indirect_ref): Catch PTR being an error_mark_node.
Mon Sep 9 19:51:14 1996 Gerald Baumgartner <gb@cs.purdue.edu>
* call.c (build_over_call): Check first whether DECL_CONTEXT exists
before testing whether it's a signature.
Sun Sep 8 16:06:57 1996 Gerald Baumgartner <gb@cs.purdue.edu>
* call.c (build_new_method_call): Don't complain about signature
pointers and references not being an aggr type.
(build_this): If a signature pointer or reference was passed in,
just return it.
(build_new_method_call): If instance is a signature pointer, set
basetype to the signature type of instance.
* sig.c (build_signature_method_call): Deleted basetype and
instance parameters, they can be found as the DECL_CONTEXT of
function and as the first argument passed in.
* cp-tree.h: Changed declaration of build_signature_method_call.
* call.c (build_method_call): Deleted first two arguments in call
of build_signature_method_call.
(build_over_call): Added call to build_signature_method_call.
Thu Sep 5 16:51:28 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_c_cast): Don't tack a non_lvalue_expr onto a
target_expr.
Thu Sep 5 10:05:38 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* cvt.c (convert_to_reference): Use %#T, not %#D, for error.
Wed Sep 4 17:16:09 1996 Bob Manson <manson@charmed.cygnus.com>
* except.c (expand_start_try_stmts): Move to except.c in the backend.
(expand_end_try_stmts): Remove.
* init.c (perform_member_init): Use add_partial_entry () instead
of directly manipulating lists.
(emit_base_init): Likewise.
Wed Sep 4 12:14:36 1996 Mike Stump <mrs@cygnus.com>
* except.c (expand_exception_blocks): Always make sure USE and
CLOBBER insns that came at the end still do, the backend relies
upon this.
Wed Sep 4 07:44:48 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): We can only use a TARGET_EXPR of the
right type.
Tue Sep 3 19:26:05 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (convert_to_reference): Revert last change, don't complain
about temp without target decl.
Tue Sep 3 10:22:56 1996 Mike Stump <mrs@cygnus.com>
* decl.c (grokdeclarator): Don't core dump when void() is given.
Tue Sep 3 02:38:56 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (copy_args_p): Don't crash.
Fri Aug 30 14:26:57 1996 Mike Stump <mrs@cygnus.com>
* pt.c (tsubst): And support template args inside the exception
specification.
* pt.c (tsubst): Add support for exception specifications in
template functions.
Fri Aug 30 10:01:55 1996 Mike Stump <mrs@cygnus.com>
* cp-tree.def (DECL_STMT): Eliminate the throw spec field, only 3
fields now.
* cp-tree.h (start_decl): Eliminate the throw spec parameter.
(start_function): Likewise.
(start_method): Likewise.
(grokfield): Likewise.
(make_call_declarator): Add throw spec parameter.
(set_quals_and_spec): Add routine.
* lex.c (set_quals_and_spec): Likewise.
* decl.h (grokdeclarator): Eliminate the throw spec parameter.
* decl.c (shadow_tag): Eliminate the throw spec parameter to
grokdeclarator.
(groktypename): Likewise.
(start_decl): Eliminate the throw spec parameter. Eliminate the
throw spec parameter to grokdeclarator. Eliminate the throw spec
field in DECL_STMT.
(cp_finish_decl): Eliminate the throw spec field in DECL_STMT.
(grokfndecl): Remove useless set of raises.
(grokdeclarator): Eliminate the throw spec parameter. Eliminate
the throw spec parameter to start_decl. Pull the throw spec out
of the call declarator.
(grokparms): Eliminate the throw spec parameter to grokdeclarator.
(start_function): Eliminate the throw spec parameter. Eliminate
the throw spec parameter to grokdeclarator.
(start_method): Likewise.
* decl2.c (grokfield): Likewise.
(grokbitfield): Eliminate the throw spec parameter to grokdeclarator.
(grokoptypename): Likewise.
(finish_file): Eliminate the throw spec parameter to
start_function. Add throw spec to make_call_declarator.
* except.c (init_exception_processing): Add throw spec to
make_call_declarator. Eliminate the throw spec parameter to
start_decl.
(expand_start_catch_block): Eliminate the throw spec parameter to
grokdeclarator.
(expand_builtin_throw): Add throw spec to make_call_declarator.
Eliminate the throw spec parameter to start_function.
(start_anon_func): Likewise.
* lex.c (make_call_declarator): Add throw spec parameter.
(set_quals_and_spec): New routine.
(cons_up_default_function): Add throw spec to make_call_declarator.
Eliminate the throw spec parameter to grokfield.
* method.c (synthesize_method): Eliminate the throw spec parameter
to start_function.
* pt.c (process_template_parm): Eliminate the throw spec parameter
to grokdeclarator.
(tsubst): Add throw spec to make_call_declarator.
(tsubst_expr): Eliminate the throw spec parameter to start_decl.
(do_function_instantiation): Eliminate the throw spec parameter to
grokdeclarator. Eliminate the throw spec parameter to
start_function.
* rtti.c (synthesize_tinfo_fn): Eliminate the throw spec parameter
to start_function.
* parse.y (datadef): Remove non-winning optimization.
(decl): Likewise.
(fndef): Remove ambiguous error productions uncovered by grammar
fixing.
(constructor_declarator): Add exception_specification_opt here.
(component_constructor_declarator): Likewise.
(direct_after_type_declarator): Likewise.
(complex_direct_notype_declarator): Likewise.
(direct_abstract_declarator): Likewise.
(fn.def1): Remove exception_specification_opt.
(fn.def2): Likewise.
(condition): Likewise.
(initdcl0): Likewise.
(initdcl): Likewise.
(notype_initdcl0): Likewise.
(nomods_initdcl0): Likewise.
(component_decl_1): Likewise.
(component_declarator): Likewise.
(after_type_component_declarator0): Likewise.
(after_type_component_declarator): Likewise.
(notype_component_declarator): Likewise.
Wed Aug 28 01:40:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Also use an INIT_EXPR when
initializing anything from an rvalue.
* call.c (build_over_call): Call stabilize_reference when building
an INIT_EXPR instead of calling the copy ctor.
* call.c (joust): Extend the previous change to all comparisons.
* decl2.c, method.c, lex.c: Use MAKE_DECL_ONE_ONLY and
NO_LINKAGE_HEURISTICS.
* decl2.c (finish_file): Emit any statics that weren't already.
* typeck.c (build_static_cast): Implement.
* tree.c (build_cplus_new): Handle getting a TARGET_EXPR.
* decl.c (grokparms): Use can_convert_arg instead of
implicit_conversion directly.
(copy_args_p): New fn.
* cvt.c (convert_to_reference): Don't complain about temp with
static_cast.
(build_up_reference): Handle TARGET_EXPRs.
* call.c (build_over_call): Elide unnecessary temps.
(can_convert*): Use new overloading code.
Tue Aug 27 13:12:21 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c: Move TYPE_PTR*_MACROS ...
* cp-tree.h: To here.
* typeck.c (build_reinterpret_cast): Implement.
* call.c (add_builtin_candidate): Use TYPE_PTROB_P instead of
ptr_complete_ob.
(joust): If we're comparing a function to a builtin and the worst
conversion for the builtin is worse than the worst conversion for the
function, take the function.
* typeck.c (build_const_cast): Implement.
(comp_ptr_ttypes_const): Like comp_ptr_ttypes, for const_cast.
(comp_ptr_ttypes_reinterpret): Like cpt, for reinterpret_cast.
Tue Aug 27 13:14:58 1996 Bob Manson <manson@charmed.cygnus.com>
* rtti.c (build_dynamic_cast): Don't try to dereference exprtype
too early. Make sure we explode if exprtype turns out to be a
NULL_TREE when it shouldn't be.
Tue Aug 27 10:56:21 1996 Mike Stump <mrs@cygnus.com>
* cp-tree.h: New routine make_call_declarator.
* lex.c (make_call_declarator): Define it.
* except.c (init_exception_processing): Use it.
(expand_builtin_throw): Likewise.
(start_anon_func): Likewise.
* decl2.c (finish_file): Likewise.
* lex.c (cons_up_default_function): Likewise.
* parse.y: Likewise.
* pt.c (tsubst): Likewise.
Mon Aug 26 17:40:03 1996 Mike Stump <mrs@cygnus.com>
* decl2.c (groktypefield): Remove unused code.
Mon Aug 26 17:00:33 1996 Mike Stump <mrs@cygnus.com>
* gxx.gperf: Change TYPE_QUAL into CV_QUALIFIER.
* parse.y: Likewise. Change maybe_type_qual into maybe_cv_qualifier.
Change type_quals into cv_qualifiers. Change nonempty_type_quals into
nonempty_cv_qualifiers.
* hash.h: Rebuild.
* lex.c (make_pointer_declarator): Change type_quals into
cv_qualifiers.
(make_reference_declarator): Likewise.
Thu Aug 22 01:09:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_function): Only check interface_* for templates
with flag_alt_external_templates.
* call.c (build_new_op): Check for comparison of different enum types.
(build_over_call): Fix arg # output.
* typeck.c (build_component_ref): Handle pre-found TYPE_DECL.
Wed Aug 21 00:13:15 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_op): Check for erroneous args.
* call.c (build_new_method_call): Add missing args to cp_error.
* tree.c (error_type): Don't print reference-to-array.
* typeck.c (convert_for_assignment): Don't say contravariance for
removing const.
Tue Aug 20 13:23:00 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_over_call): Diagnose bad convs for `this'.
* lex.c (cons_up_default_function): Set DECL_ARTIFICIAL
on _ctor_arg.
* call.c (convert_like): Handle bad convs.
(build_over_call): Handle bad convs better.
* decl2.c: -fansi-overloading is now the default.
* call.c (build_new_method_call): Check for erroneous args.
* pt.c (instantiate_class_template): Propagate
TYPE_USES_MULTIPLE_INHERITANCE.
Tue Aug 20 13:09:57 1996 Mike Stump <mrs@cygnus.com>
* call.c (enforce_access): Add static to routine.
Sun Aug 18 14:35:54 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_user_type_conversion_1): Fix bad handling.
(compare_ics): Likewise.
Sat Aug 17 21:54:11 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (standard_conversion): Oops.
Sat Aug 17 16:28:11 1996 Geoffrey Noer <noer@cygnus.com>
* g++.c: Update test for win32 (&& ! cygwin32).
Sat Aug 17 03:45:31 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (comp_ptr_ttypes_real): Handle OFFSET_TYPEs properly.
(ptr_reasonably_similar): New fn.
* call.c (BAD_RANK): New rank.
(ICS_BAD_FLAG): New macro.
(standard_conversion): Handle almost-right pointer conversions.
(reference_binding): Handle bad rvalue bindings.
(add_*_candidate): Stuff.
(build_over_call): Pass bad conversions to convert_for_initialization.
(compare_ics): Handle bad convs.
(joust): Likewise.
Fri Aug 16 15:02:19 1996 Bob Manson <manson@charmed.cygnus.com>
* init.c (expand_vec_init): Use ptrdiff_type_node instead of
integer_type_node when computing pointer offsets.
Fri Aug 16 01:28:32 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (lvalue_type): New fn.
(error_type): New fn.
* call.c (op_error): Use error_type.
(add_conv_candidate): Use lvalue_type.
(add_builtin_candidates): Likewise.
* error.c (args_as_string): Use error_type.
Thu Aug 15 17:27:13 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Evaluate DECL_INITIAL of a VAR_DECL here.
(tsubst): Not here.
* decl.c (init_decl_processing): With -ansi, __null's type is the
signed integral type with the same number of bits as a pointer.
Introduce a new variable null_node for it.
* cp-tree.h: Adjust.
* call.c (null_ptr_cst_p): Adjust.
Thu Aug 15 17:09:54 1996 Mike Stump <mrs@cygnus.com>
* except.c (do_unwind): Mark %i7 as used on the SPARC so we can
optimize.
Thu Aug 15 01:36:49 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_decl): Ignore #pragma interface for tinfo
fns of classes without virtual functions.
* call.c (add_function_candidate): Handle `this' specially.
(compare_ics): Likewise.
Tue Aug 13 12:16:10 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_conditional_expr): Fix handling of __null.
* decl2.c (comdat_linkage): New fn.
(import_export_vtable): Use it.
(import_export_decl): Use it.
* method.c (make_thunk): Use it.
Mon Aug 12 00:09:18 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (end_template_decl): If we don't actually have parms, return.
* parse.y (template_header): Accept 'template <>'.
* errfn.c: Allow 5 args.
Sun Aug 11 15:20:58 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (make_temp_vec): New fn.
* pt.c (push_template_decl): Handle partial specs.
(instantiate_class_template): Likewise.
(more_specialized): Use get_bindings.
(more_specialized_class): New fn.
(get_class_bindings): New fn.
(most_specialized_class): New fn.
(do_function_instantiation): List candidates for ambiguous case.
* decl.c (duplicate_decls): Lose reference to DECL_TEMPLATE_MEMBERS.
(shadow_tag): Call push_template_decl for partial specializations.
* parse.y: Likewise.
* cp-tree.h (DECL_TEMPLATE_SPECIALIZATIONS): Replaces
DECL_TEMPLATE_MEMBERS.
* call.c (print_z_candidates): Reduce duplication.
Fri Aug 9 14:36:08 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (lang_decode_option): Allow -fansi-overloading.
Thu Aug 8 17:04:18 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (get_bindings): New fn.
(most_specialized): Likewise.
(do_function_instantiation): Use them.
(add_maybe_template): New fn.
* cp-tree.h (DECL_MAYBE_TEMPLATE): New macro.
* call.c (build_new_op): Handle guiding decls.
(build_new_function_call): Likewise.
* decl2.c (finish_file): Likewise.
* decl2.c (mark_used): Do synthesis here.
* call.c (build_method_call): Not here.
(build_over_call): Or here.
* typeck.c (build_function_call_real): Or here.
* tree.c (bot_manip): Call mark_used on functions used in default
args.
Thu Aug 8 17:48:16 1996 Michael Meissner <meissner@tiktok.cygnus.com>
* decl2.c (import_export_vtable): Delete code that disabled vtable
heuristic on systems with ASM_OUTPUT_EXTERNAL.
Wed Aug 7 12:44:11 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_x_function_call): Handle static call context
better.
* decl.c (finish_function): Set the DECL_CONTEXT of the result to
the function, not its outer block.
* call.c (build_field_call): Pass fields on to build_opfncall
regardless of TYPE_OVERLOADS_CALL_EXPR.
(build_method_call): Pass on to build_new_method_call sooner.
* typeck.c (build_ptrmemfunc): Just return what instantiate_type
gives us.
* class.c (instantiate_type): Don't put a POINTER_TYPE to
METHOD_TYPE on an expression. Also make a copy of rhs instead of
modifying it.
Tue Aug 6 12:58:46 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (compare_ics): Handle qual_conv after lvalue_conv.
(add_builtin_candidate): Don't take enums for ++.
(build_new_method_call): Handle non-aggregates and field calls.
Move new overloading code from...
* cvt.c: Here.
* decl.c (grokparms): Don't check default args in templates.
Mon Aug 5 17:17:06 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_new_op): Fix args to build_unary_op.
(add_builtin_candidates): Don't call type_promotes_to on float.
* decl.c (grokparms): Check the type of the default arg.
* cvt.c (build_new_op): Pass non-overloaded cases on rather than
returning NULL_TREE.
* typeck.c (build_x_binary_op): Avoid doing extra work.
(build_x_unary_op): Likewise.
(build_x_conditional_expr): Likewise.
* cvt.c (build_over_call): Return.
(add_builtin_candidate): Fix MEMBER_REF.
(build_new_op): Likewise.
Mon Aug 5 17:07:47 1996 Mike Stump <mrs@cygnus.com>
* method.c (build_overload_name): Put bug fix into code but leave
disabled for now so we can be bug compatible with older releases
that do repeats incorrectly. In the future, we can enable it.
Mon Aug 5 13:46:28 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (convert_like): Don't call build_cplus_new twice.
* call.c, cp-tree.h, cvt.c, decl2.c, init.c, method.c, pt.c, typeck.c:
Control new overloading code with -fansi-overloading.
Sun Aug 4 15:29:11 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_over_call): Call build_cplus_new.
* call.c (build_method_call): Likewise.
* typeck.c (build_function_call_real): Likewise.
(build_conditional_expr): If both operands are TARGET_EXPRs, wrap
the COND_EXPR in a TARGET_EXPR so they use the same slot.
* cvt.c (build_up_reference): Propagate INDIRECT_BIND to
recursive calls.
* typeck.c (complete_type): Propagate
TYPE_NEEDS_{CONSTRUCTING,DESTRUCTOR}.
Sat Aug 3 14:05:07 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (joust): More ?: kludging. Sigh.
(build_over_call): Don't try to synthesize global fns.
* search.c (lookup_conversions): Use binfo marking.
Sat Aug 3 12:33:42 1996 Bob Manson <manson@charmed.cygnus.com>
* search.c (build_mi_matrix): Use the correct value of cid
when determining the new mi_size.
Sat Aug 3 01:27:41 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (add_builtin_candidates): Do consider type conversion ops
for the first parms of += et al.
(strip_top_quals): New fn.
(reference_binding): Use it instead of TYPE_MAIN_VARIANT.
(implicit_conversion): Likewise.
(add_builtin_candidates): Be careful about arrays.
(build_new_method_call): Handle vtable optimization.
Fri Aug 2 01:26:59 1996 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h (LOOKUP_NO_TEMP_BIND): New flag.
* cvt.c (reference_binding): Use it.
(implicit_conversion): Use it.
(add_builtin_candidate, COND_EXPR): Use it.
* cvt.c (build_new_function_call): Check for error args.
* typeck.c (comptypes): Just check DERIVED_FROM_P, not UNIQUELY.
* gxx.gperf: Add __null.
* hash.h: Regenerate.
* lex.h: Add RID_NULL.
* lex.c (init_lex): Create null_pointer_node here, stick it in
RID_NULL.
* decl.c (init_decl_processing): Still set its type here.
* cvt.c (cp_convert_to_pointer): Don't produce null_pointer_node.
(convert_to_pointer_force): Likewise.
(null_ptr_cst_p): Check for null_pointer_node; only accept (void*)0
if (! pedantic).
* call.c (convert_harshness): Use null_ptr_cst_p.
* typeck.c (convert_for_assignment): Likewise. Don't produce
null_pointer_node.
* error.c (args_as_string): Handle lists of actual args, too.
* cvt.c (null_ptr_cst): Support (void*)0 for now.
(build_user_type_conversion_1): Improve diagnostics.
(build_new_function_call): Likewise.
(build_object_call): Likewise.
(build_new_method_call): Likewise. Move call before def diagnostic...
(build_over_call): Here.
* cvt.c (build_new_method_call): Don't complain about no match if
LOOKUP_SPECULATIVELY.
(build_over_call): Fix 'this' for virtual fn.
(build_new_method_call): Add diagnostic.
Thu Aug 1 16:45:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (add_function_candidate): Expect 'this' and 'in_chrg' for
constructors to be passed in.
(build_over_call): Likewise.
(build_user_type_conversion_1): Pass them in.
(convert_like): Likewise.
(build_object_call): Handle overloaded conversions.
(build_over_call): Pass the right args to build_vfn_ref.
(standard_conversion): Fix pmf convs.
(joust): Handle comparing statics and non-statics.
(build_new_method_call): New fn.
* call.c (build_method_call): Call it if NEW_OVER.
Thu Aug 1 16:06:14 1996 Mike Stump <mrs@cygnus.com>
* lex.c (do_identifier): Don't use %O on IDENTIFIER_OPNAME_Ps, use
%D instead.
Thu Aug 1 15:24:02 1996 Mike Stump <mrs@cygnus.com>
* except.c (expand_throw): Use maybe_build_cleanup_and_delete
instead of just maybe_build_cleanup so that we deallocate the
thrown object.
Thu Aug 1 15:18:00 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl2.c (finish_prevtable_vardecl): Make non-static for pt.c's use.
* cp-tree.h (finish_prevtable_vardecl): Add decl.
Thu Aug 1 11:53:51 1996 Bob Manson <manson@charmed.cygnus.com>
* pt.c (instantiate_class_template): Call complete_type. Also, if
we're at the end of the file and we just instantiated a template
class with a vtable, call finish_prevtable_vardecl.
* error.c (dump_decl): Don't explode (or explode more gracefully
as appropriate) if the object being dumped has a null type.
(dump_expr): Likewise.
* search.c (build_mi_matrix): Ensure that mi_size is large enough,
by counting the number of nodes that we'll need before allocating
the array.
(lookup_fnfields): Fix comment.
(breadth_first_search): Fix comment.
Wed Jul 31 09:57:05 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Propagate TYPE_PACKED and
TYPE_ALIGN.
* class.c (finish_struct): Call cplus_decl_attributes here.
(finish_struct_1): Not here.
* cp-tree.h: Adjust.
* pt.c (type_unification): New parameter STRICT.
(unify): If STRICT, don't allow cv addition or base deduction.
* call.c, class.c, cvt.c, cp-tree.h: Adjust.
Tue Jul 30 13:06:13 1996 Jason Merrill <jason@yorick.cygnus.com>
* search.c (get_template_base{_recursive}): New fns.
* pt.c (more_specialized): New fn.
(do_function_instantiation): Use it.
(unify): Handle base deduction.
* cvt.c (joust): Use more_specialized.
Don't arbitrarily choose between non-builtin candidates.
(build_over_call): Call require_complete_type.
* decl.c (start_function): Statics are static even in a #pragma
interface file.
* decl2.c (import_export_vtable): Disable vtable heuristic on
systems with ASM_OUTPUT_EXTERNAL.
* cvt.c (compare_ics): Fix comparison of PMEM_CONV and BASE_CONV.
(standard_conversion): No std conv to enum type.
* cvt.c (standard_conversion): Fix order of args to DERIVED_FROM_P
for ptm's.
* cvt.c (reference_binding): Bind directly to a base subobject of
a class rvalue.
* cvt.c (build_new_op): Enforce access control.
Tue Jul 30 09:22:53 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck2.c (process_init_constructor): When scanning the
union for a named field, skip things that aren't FIELD_DECLs.
* method.c (synthesize_method): Don't scan fndecl's rtl if
we're at the end of the file; just assume the function can't
be inlined.
Mon Jul 29 15:48:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_builtin_candidate): Stick a dummy conversion in if
it failed.
* cvt.c (build_user_type_conversion_1): Handle overloaded
conversion ops.
* cvt.c (add_builtin_candidates): Don't consider type conversion
operators for the first parameter of operator=.
Mon Jul 29 15:33:55 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (complete_type): Only call layout_type if we're not
expanding a template.
Mon Jul 29 14:40:38 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (compare_ics): Oops.
* cvt.c (op_error): Oops.
* cp-tree.def: Add RVALUE_CONV, rename EXACT_CONV to IDENTITY_CONV.
* cvt.c: Add IDENTITY_RANK before others. Use real_lvalue_p.
(build_conv): Use them.
(implicit_conversion): Use them.
(convert_like): Handle them.
(build_new_op): Handle builtin COND_EXPR again.
(add_builtin_candidates): Strip cv-quals. Fix oops. Include enums
in lists of types for COND_EXPR.
(add_builtin_candidate): Add enum candidates for COND_EXPR.
Mon Jul 29 12:05:40 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (build_modify_expr): Always attempt to build a call to
the assignment operator, even if we're using a default one.
(convert_for_initialization): Call complete_type.
Mon Jul 29 11:25:08 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (reference_binding): A REF_BIND gets the reference type.
(implicit_conversion): Likewise.
(convert_like): Likewise.
(compare_ics): Likewise.
(compare_qual): Likewise.
(print_z_candidates): Handle no candidates.
(build_new_op): Don't handle builtin COND_EXPR for now.
Sat Jul 27 11:27:47 1996 Stan Shebs <shebs@andros.cygnus.com>
* cvt.c (build_builtin_candidate): Init local var in an ANSI way.
Fri Jul 26 01:07:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (joust): If the candidates are the same, arbitrarily pick one.
* cvt.c (build_builtin_candidate): Oops.
(build_new_op): Oops.
* method.c (build_opfncall): Pass COND_EXPR on.
* cvt.c (build_builtin_candidate): Reorganize, support COND_EXPR.
(add_builtin_candidate{,s}): Likewise.
(add_builtin_candidates): Likewise.
(print_z_candidates, op_error, build_new_op): Likewise.
(type_decays_to): New fn.
* lex.c (init_lex): Just say ?: for COND_EXPR.
Thu Jul 25 09:33:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (complete_type): Call layout_type rather than building
a new array type.
* cvt.c (add_builtin_candidate): Pointer arithmetic candidates
only use ptrdiff_t.
Wed Jul 24 12:45:08 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c: Always compile the new overloading code (but don't use it).
(implicit_conversion): Add a BASE_CONV when converting to
the same class type.
(convert_like): Handle BASE_CONV.
Tue Jul 23 12:46:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_new_op): Support {MAX,MIN}_EXPR.
(add_builtin_candidate): Likewise.
NEW_OVER changes:
* typeck.c (build_x_function_call): Try an operator function
whenever we call an object of class type.
* method.c (build_opfncall): Pass CALL_EXPRs through.
* cvt.c (implicit_conversion): Do const-ref case first.
(add_conv_candidate, build_object_call, op_error): New fns.
(ptr_complete_ob, TYPE_PTROB_P): void is not an object type.
({add,build}_builtin_candidate{,s}, print_z_candidates): Display
builtin candidates.
(build_new_op): Handle CALL_EXPR. Don't try to decay void.
Fall back on preincrement handling. Use op_error.
Handle warn_synth.
(convert_like): Pass INDIRECT_BIND. Don't try to do anything with
an error_mark_node.
(build_over_call): Handle PROMOTE_PROTOTYPES and ellipsis promotions
properly.
Mon Jul 22 16:21:55 1996 Bob Manson <manson@charmed.cygnus.com>
* pt.c (tsubst_expr): Handle CONTINUE_STMT.
Mon Jul 22 15:38:58 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_component_ref_1): Use build_component_ref
instead of open coding it here.
Mon Jul 22 12:18:54 1996 Jason Merrill <jason@yorick.cygnus.com>
* g++.c (main): Don't link with -lg++.
NEW_OVER changes:
* cvt.c (convert_to_reference): Don't use convert_from_reference on
result of build_type_conversion.
(cp_convert): Only call build_method_call for ctors if
build_type_conversion failed.
(ptr_complete_ob): New function.
(TYPE_PTR{,OB,MEM}_P): New macros.
({add,build}_builtin_candidate{,s}): New functions.
(print_z_candidates): Handle builtins.
(build_user_type_conversion_1): Don't use conversion fns for
converting to a base type.
(build_user_type_conversion_1): Set ICS_USER_FLAG on AMBIG_CONVs.
(build_user_type_conversion): Use convert_from_reference.
(build_new_op): New function.
(build_over_call): Fix handling of methods.
(compare_ics): Handle AMBIG_CONV properly.
* typeck2.c: Increment abort count.
* method.c (build_opfncall): Forward most requests to build_new_op.
* cp-tree.h (IS_OVERLOAD_TYPE): Tweak.
Fri Jul 19 17:59:29 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* error.c (dump_expr, case CONSTRUCTOR, case CAST_EXPR): Take out
invalid second argument to dump_expr_list.
Fri Jul 19 14:04:05 1996 Mike Stump <mrs@cygnus.com>
* decl.c (lookup_name_real): Make sure we do obj->X::i correctly.
Thu Jul 18 14:48:23 1996 Bob Manson <manson@charmed.cygnus.com>
* decl2.c (import_export_vtable): ASM_OUTPUT_EXTERNAL, not
ASSEMBLE_EXTERNAL.
Mon Jul 15 17:48:43 1996 Mike Stump <mrs@cygnus.com>
* typeck2.c (process_init_constructor): New pedwarn for using { }
to initialize a pointer to member function.
* typeck.c (build_ptrmemfunc1): Avoid use of digest_init so that
we can avoid the new error.
Mon Jul 15 15:42:03 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_ptrmemfunc1): New function to hide details of
pointer to member functions better.
Mon Jul 15 14:23:02 1996 Mike Stump <mrs@cygnus.com>
* init.c (resolve_offset_ref): Resolve OFFSET_REFs that are
methods into the actual method, as we know the implied object is
not used.
Mon Jul 15 13:08:29 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* parse.y (maybecomma_warn): Only emit the pedwarn if we're not
inside a system header.
Fri Jul 12 16:30:05 1996 Bob Manson <manson@charmed.cygnus.com>
* call.c (build_method_call): Call complete_type on the
instance type.
Thu Jul 11 17:16:40 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_component_ref): Always build up an OFFSET_REF
for obj_ptr->func so that we can know which object to use in a
method call.
Wed Jul 10 19:36:37 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_ptrmemfunc): Remove sorry, now we can cast
around things. Also improve maintainability.
Wed Jul 10 18:20:11 1996 Bob Manson <manson@charmed.cygnus.com>
* decl.c (grokdeclarator): Check for overflow when evaluating an
array dimension.
Wed Jul 10 17:26:19 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert): Don't check for ambiguity with constructor
if NEW_OVER.
* typeck.c (build_x_function_call): Pass function overload
questions to new overloading code if NEW_OVER.
* init.c (expand_aggr_init_1): Only check for type conversion ops
if we're doing copy-initialization (i.e. LOOKUP_ONLYCONVERTING).
Don't check for ambiguity with constructor if NEW_OVER.
* cvt.c (convert_to_reference): Dereference the result of a type
conversion operator.
(build_conv): Propagate ICS_USER_FLAG.
(implicit_conversion): Call instantiate_type.
Pass LOOKUP_ONLYCONVERTING instead of LOOKUP_NORMAL.
(add_function_candidate): Fix cv-quals on argtype.
(print_z_candidates): New function.
(build_new_function_call): Call it.
(build_user_type_conversion_1): If LOOKUP_ONLYCONVERTING, don't
consider non-converting constructors.
Call print_z_candidates.
Return an AMBIG_CONV for an ambiguous conversion.
(build_user_type_conversion): Handle AMBIG_CONV.
(convert_like): Fix test for building TARGET_EXPR.
Call instantiate_type.
Handle AMBIG_CONV and LVALUE_CONV.
(build_over_call): Handle 0 args and ellipsis.
* cp-tree.def: Add AMBIG_CONV.
Tue Jul 9 17:48:48 1996 Mike Stump <mrs@cygnus.com>
* decl.c (lookup_name_real): If we find mem in obj when parsing
`obj->mem', make sure we return the right value.
Tue Jul 9 16:11:28 1996 Bob Manson <manson@charmed.cygnus.com>
* search.c (get_base_distance): Call complete_type.
Tue Jul 9 12:46:34 1996 Mike Stump <mrs@cygnus.com>
* decl.c (store_bindings): Make static.
Mon Jul 8 16:42:31 1996 Jason Merrill <jason@yorick.cygnus.com>
* init.c (expand_aggr_init_1): Don't check type conversions if
NEW_OVER.
* cvt.c (z_candidate): Put back template field.
(add_function_candidate): Set it.
(add_template_candidate): Likewise.
(joust): Use it.
(compare_qual): Handle references and pointers to members.
(compare_ics): Handle reference bindings.
* decl.c (duplicate_decls): Propagate DECL_ONE_ONLY.
Mon Jul 8 16:18:56 1996 Bob Manson <manson@charmed.cygnus.com>
* call.c (compute_conversion_costs): Call complete_type.
* tree.c (vec_binfo_member): Use comptypes instead of comparing
pointers, so we can handle template parameters.
Fri Jul 5 16:51:53 1996 Bob Manson <manson@charmed.cygnus.com>
* cvt.c (cp_convert_to_pointer): We have to call complete_type
here; let's make it explicit instead of a side effect of an
error check.
Wed Jul 3 16:29:51 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (z_candidate): Remove template field.
(reference_binding): Handle binding to temporary.
(implicit_conversion): Likewise.
(add_function_candidate): Handle artificial constructor parms.
Handle functions with too few parms.
(add_template_candidate): New function.
(build_user_type_conversion_1): Handle constructors.
(convert_like): Likewise.
(build_over_call): Likewise.
(build_new_function_call): Support templates.
(compare_ics): Fix reference, inheritance handling.
Mon Jul 1 22:58:18 1996 Bob Manson <manson@charmed.cygnus.com>
* decl.c: Add signed_size_zero_node.
(init_decl_processing): Build it.
* class.c (prepare_fresh_vtable): Use it instead of size_zero_node
when we're trying to make a negative delta.
Mon Jul 1 17:56:19 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
Stop doing this damn index==strchr variable name confusion.
* class.c (add_virtual_function): Change local var INDEX to be
named IDX.
(add_method): Likewise.
* lex.c (print_parse_statistics): Likewise.
* search.c (make_memoized_table_entry): Likewise.
(lookup_fnfields_here): Likewise.
(lookup_field): Likewise.
(lookup_fnfields): Likewise.
(get_baselinks): Likewise.
* sig.c (build_signature_table_constructor): Likewise.
(build_signature_method_call): Likewise.
* typeck.c (build_x_array_ref): Change INDEX parm to be named IDX.
(get_member_function_from_ptrfunc): Likewise.
(build_ptrmemfunc): Change local var INDEX to be IDX.
(c_expand_start_case): Likewise.
Sat Jun 29 14:05:46 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert_to_pointer): Move user-defined type conversion
handling to before extraction of TYPE_PTRMEMFUNC_FN_TYPE.
(convert_to_reference): Use build_type_conversion to convert to
the reference type directly.
(standard_conversion): Fix void* case, non-conversions.
(reference_binding): Fix expr == 0 case, non-conversions.
(convert_like): Support REF_BIND.
(compare_qual): Split out from compare_ics.
(compare_ics): Use it, handle icses with only a qual_conv.
* init.c (expand_vec_init): Don't crash if decl is NULL.
Fri Jun 28 11:52:51 1996 Stan Shebs <shebs@andros.cygnus.com>
* mpw-config.in: New file, configury for Mac MPW.
* mpw-make.sed: New file, makefile editing for MPW.
Thu Jun 27 15:18:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Call repo_template_used.
* search.c (lookup_conversions): Only lookup conversions in
complete types.
Thu Jun 27 12:59:53 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* cp-tree.def: Renamed from tree.def, to avoid confusion with
gcc's tree.def.
* cp-tree.h, lex.c: Include cp-tree.def.
* Makefile.in (CXX_TREE_H): Reference cp-tree.def.
Wed Jun 26 18:29:47 1996 Bob Manson <manson@charmed.cygnus.com>
* init.c (build_vec_delete_1): Call complete_type.
Mon Jun 24 17:17:32 1996 Mike Stump <mrs@cygnus.com>
* except.c (start_anon_func): Make sure anonymous functions are
never external.
Fri Jun 21 15:10:58 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (finish_function): If function_depth > 1, set nested.
* decl2.c (grokbitfield): Revert Bob's change.
* class.c (finish_struct_1): Fix handling of named bitfield widths.
Thu Jun 20 23:35:38 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (add_pending_template): Handle types.
(lookup_template_class): With -fexternal-templates, just add the class
to pending_templates instead of instantiating it now.
* decl2.c (finish_file): Handle types in pending_templates.
Thu Jun 20 14:08:40 1996 Bob Manson <manson@charmed.cygnus.com>
* decl2.c (grokbitfield): Handle constant decls appropriately.
Give an appropriate error message now instead of spewing core
later.
Thu Jun 20 13:01:51 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c: Don't turn on thunks by default for now.
Wed Jun 19 11:37:04 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (complete_type): Handle error_mark_node.
(common_type, OFFSET_TYPE): Handle template_type_parms.
Tue Jun 18 10:02:15 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): If at_eof, call import_export_decl
regardless of DECL_INLINE.
* typeck.c (mark_addressable): Set TREE_ADDRESSABLE on CONSTRUCTORs.
* class.c (finish_struct_bits): Copy TYPE_SIZE.
* rtti.c (build_dynamic_cast): Support templates.
* tree.def: Support DYNAMIC_CAST_EXPR.
* pt.c (tsubst_copy): Likewise.
* decl2.c (build_expr_from_tree): Likewise.
Mon Jun 17 15:23:36 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_static_cast): Support templates.
(build_const_cast): Likewise.
* tree.def: Support CONST/STATIC_CAST_EXPR.
* pt.c (tsubst_copy): Likewise.
* decl2.c (build_expr_from_tree): Likewise.
Sun Jun 16 12:33:57 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_vtable_vardecl): Don't trust
TREE_SYMBOL_REFERENCED for vtables of local classes.
Fri Jun 14 18:13:36 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_copy): Handle operator T.
Wed Jun 12 17:52:40 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* init.c (build_delete): Move creation of PARMS inside test of
TYPE_HAS_DESTRUCTOR, since it's never used outside of that block.
Tue Jun 11 15:09:18 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (build_conditional_expr): Don't assume that
the arguments to ?: are always pointers or records.
Tue Jun 11 13:56:23 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_decl): Still emit static/weak/comdat
copies of inline template functions with -fno-implicit-templates.
Tue Jun 11 11:42:13 1996 Bob Manson <manson@charmed.cygnus.com>
* init.c (build_delete): Determine the complete basetype
path to the destructor we're calling.
Fri Jun 7 15:30:10 1996 Bob Manson <manson@charmed.cygnus.com>
* decl.c (build_enumerator): Always copy the INTEGER_CST used to
initialize the enum, because we really and truly don't know where
it came from.
(start_enum): Don't copy integer_zero_node because
build_enumerator will do it.
Fri Jun 7 11:11:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (finish_function): Do access control on base destructors.
* pt.c (tsubst, case FUNCTION_DECL): Set up
IDENTIFIER_GLOBAL_VALUE for member functions so pushdecl doesn't
hose us.
Fri Jun 7 10:37:33 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (build_up_reference): If we have already extended the
lifetime of the temporary, don't try it again.
* typeck.c (c_expand_return): Don't try and convert the return
value twice when we want a reference, once is enough.
Tue Jun 4 15:41:45 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_expr, case DECL_STMT): Don't pass
LOOKUP_ONLYCONVERTING at all for now.
* search.c (add_conversions): Put the conversion function in
TREE_VALUE, the basetype in TREE_PURPOSE.
* cvt.c (build_type_conversion): Adjust.
* cvt.c (build_expr_type_conversion): Adjust.
* call.c (user_harshness): Adjust.
Mon Jun 3 15:30:52 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (emit_thunk): Pretend this is a FUNCTION_DECL for the
backend's benefit.
Mon Jun 10 18:58:19 1996 Mike Stump <mrs@cygnus.com>
* except.c (expand_start_catch_block): Add a dummy region, if we
get an error, so that we can avoid core dumping later.
Fri May 31 14:56:13 1996 Mike Stump <mrs@cygnus.com>
* cp-tree.h (OFFSET_REF): Remove.
* tree.def (CP_OFFSET_REF): Rename to OFFSET_REF.
* expr.c (cplus_expand_expr): Cleanup callers of expand_expr.
* init.c (expand_aggr_init_1): Likewise.
(build_new): Likewise.
* typeck.c (expand_target_expr): Likewise.
Fri May 31 14:22:08 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_modify_expr): Don't use TREE_VALUE on a
TARGET_EXPR.
Wed May 29 17:04:33 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (build_up_reference): Redo how and when temporaries are
created.
* decl.c (grok_reference_init): Don't try and be smart about
running cleanups.
Wed May 29 16:02:08 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (build_up_reference): Add NULL_TREE to all calls to build
(TARGET_EXPR...), now that it has 4 arguments.
* tree.c (build_cplus_new): Likewise.
Thu May 23 16:40:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* error.c (dump_expr, case CAST_EXPR): Handle T() properly.
* pt.c (instantiate_decl): Don't call push/pop_cp_function_context.
* decl.c (struct saved_scope): Remove named_labels,
{base,member}_init_list.
(maybe_push_to_top_level): Don't set them. Call
push_cp_function_context if appropriate.
(pop_from_top_level): Likewise.
* method.c (do_build_assign_ref): Remove obsolete check of
TYPE_HAS_ASSIGN_REF (basetype).
* decl.c (grokfndecl): Diagnose user definition of
implicitly-declared methods.
Thu May 23 12:13:08 1996 Bob Manson <manson@charmed.cygnus.com>
* method.c (do_build_copy_constructor): Add code to give
meaningful error messages instead of crashing.
(do_build_assign_ref): Don't synthesize assignment operators for
classes containing reference or const members.
* class.c (struct base_info): Remove cant_synth_copy_ctor
and cant_synth_asn_ref.
(finish_base_struct): Remove the code that tries to conditionalize
synthesis of copy constructors & assignment operators based on
access permissions. Instead, let it fail when it tries to
synthesize the copy constructor. This will give meaningful error
messages instead of silently generating code to perform a bitcopy.
Wed May 22 11:45:19 1996 Bob Manson <manson@charmed.cygnus.com>
* lex.c (real_yylex): Remove old-n-crufty #if 0 code for
determining types for constant values.
* decl.c (struct named_label_list): Use instead of stuffing
random items into a TREE_LIST node.
(named_label_uses): Use the new struct.
(poplevel): Likewise.
(lookup_label): Likewise.
(define_label): Add an error message to tell the user the line
where the goto is located in addition to the destination of the
goto.
(init_decl_processing): Use NULL instead of NULL_TREE to initialize
named_label_uses.
(finish_function): Likewise.
(start_decl): Complain about defining a static data member
in a different type from which it was declared.
Wed May 22 09:33:23 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_expr_type_conversion): Adjust.
Tue May 21 11:21:56 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_method_call): Always convert 'this' to the
appropriate type.
* search.c (add_conversions): Put the conversion function in
TREE_VALUE, the type in TREE_PURPOSE.
* cvt.c (build_type_conversion): Adjust.
* call.c (user_harshness): Adjust.
* method.c (emit_thunk): Call temporary_allocation and
permanent_allocation around the ASM_OUTPUT_MI_THUNK case, too.
* tree.c (build_cplus_array_type): Handle tweaking of
TYPE_MAIN_VARIANT here.
* typeck.c (common_type): Not here.
* typeck.c (complete_type): Only try to complete an array type if
it has a domain.
Mon May 20 14:55:59 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokvardecl): Call complete_type.
(grokdeclarator): Call complete_type for PARM_DECLs.
Fri May 17 16:41:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Re-set
CLASSTYPE_GOT_SEMICOLON after calling finish_struct_1.
Fri May 17 14:56:55 1996 Mike Stump <mrs@cygnus.com>
* cp-tree.h (cp_expand_decl_cleanup): Remove, the backend is now
smart enough to do it right.
* tree.c (cp_expand_decl_cleanup): Likewise.
* decl.c (cp_finish_decl): Use expand_decl_cleanup instead of
cp_expand_decl_cleanup.
(store_parm_decls): Likewise.
(hack_incomplete_structures): Likewise.
* except.c (push_eh_cleanup): Likewise.
Fri May 17 13:13:51 1996 Mike Stump <mrs@cygnus.com>
* expr.c (expand_expr, cond UNSAVE_EXPR): Move from the C++
frontend to the backend where it belongs.
* tree.c (unsave_expr): Likewise.
(unsave_expr_now): Likewise.
* tree.def (UNSAVE_EXPR): Likewise.
* cp-tree.h (unsave_expr): Likewise.
(unsave_expr_now): Likewise.
Fri May 17 11:02:41 1996 Mike Stump <mrs@cygnus.com>
* init.c (emit_base_init): Make sure the partial EH cleanups live
on the function_obstack.
Thu May 16 15:29:33 1996 Bob Manson <manson@charmed.cygnus.com>
* expr.c (do_case): Don't try to dereference null TREE_TYPEs
when checking for pointer types.
Thu May 16 13:38:58 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Remove obsolete check for
access declarations.
Thu May 16 13:34:15 1996 Mike Stump <mrs@cygnus.com>
* call.c (build_overload_call): Simplify calls to
build_overload_call by removing last parameter.
(build_method_call): Likewise.
* cp-tree.h: Likewise.
* method.c (build_opfncall): Likewise.
* typeck.c (build_x_function_call): Likewise.
Thu May 16 13:15:43 1996 Mike Stump <mrs@cygnus.com>
* call.c (default_parm_conversions): Factor out common code.
(build_method_call): Use it.
(build_overload_call_real): Use it.
Wed May 15 14:46:14 1996 Mike Stump <mrs@cygnus.com>
* call.c (build_method_call): Allow implicit & on METHOD_TYPEs,
but pedwarn as the code is bogus.
* typeck.c (decay_conversion): Likewise.
(build_function_call_real): Use build_addr_func instead of
default_conversion. Don't allow pointer-to-method functions down
here.
(build_unary_op): Use real pointer-to-member functions instead of
fake ones.
(build_ptrmemfunc): Use build_addr_func instead of build_unary_op.
(convert_for_assignment): Removed some obsolete code.
* decl2.c (reparse_absdcl_as_expr): Pass current_class_ref to
build_x_function_call instead of current_class_ptr. Only call
digest_init once on an initializer, we do this just checking
TREE_TYPE.
(build_expr_from_tree): Pass current_class_ref to
build_x_function_call instead of current_class_ptr.
* init.c (build_member_call): Likewise.
* pase.y: Likewise.
* error.c (dump_expr): Handle OFFSET_REFs better.
* pt.c (unify): Handle pointer-to-member functions better.
* decl.c (finish_function): Clear out current_class_ref just like
we do for current_class_ptr.
* typeck.c (get_delta_difference): Handle virtual bases better.
Tue May 14 16:37:37 1996 Jason Merrill <jason@yorick.cygnus.com>
* sig.c (build_signature_table_constructor): Use the delta for
the original basetype for this virtual function with thunks.
(build_signature_method_call): We still need to adjust 'this'
with thunks.
Tue May 14 16:27:25 1996 Mike Stump <mrs@cygnus.com>
* call.c (build_addr_func): New routine. Used to get the `real'
address of a function or a method. Needed to avoid getting a
pointer-to-member function.
(build_call): New routine to build CALL_EXPRs.
(build_method_call): Use it.
* cvt.c (convert_to_aggr): Likewise.
* typeck.c (build_function_call_real): Likewise.
* sig.c (build_signature_table_constructor): Use build_addr_func.
* cp-tree.h (build_call, build_addr_func): Declare them.
Tue May 14 12:47:47 1996 Mike Stump <mrs@cygnus.com>
* cp-tree.h (LOOKUP_AGGR): Remove, unused.
* parse.y: Remove uses of LOOKUP_AGGR.
Tue May 14 12:07:51 1996 Mike Stump <mrs@cygnus.com>
* *.[chy]: Rename current_class_decl to current_class_ptr, and
C_C_D to current_class_ref.
Mon May 13 16:55:23 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c (convert_harshness): Tighten up pointer conversions.
Sat May 11 04:33:50 1996 Doug Evans <dje@canuck.cygnus.com>
* decl2.c (finish_vtable_vardecl): Surround DECL_ONE_ONLY with ifdef.
(finish_file): Likewise.
Fri May 10 11:09:57 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (convert_fn_ptr): We don't use thunks for pmfs.
* method.c (emit_thunk): Set flag_omit_frame_pointer in default
code.
Thu May 9 18:18:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c: Turn on thunks by default where supported.
Tue May 7 20:39:57 1996 Mike Stump <mrs@cygnus.com>
* cp-tree.h (build_overload_call_maybe): Removed.
* call.c (build_overload_call_real): Invert meaning of last arg to
be require_complete.
(build_overload_call): Likewise.
* typeck.c (build_x_function_call): Use build_overload_call_real
instead of build_overload_call_maybe.
Mon May 6 01:23:32 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Don't try to emit functions that haven't
been compiled.
Fri May 3 09:30:13 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_vtable_vardecl): Oops.
* decl.c (maybe_push_to_top_level): Do save previous_class_*.
Also store the bindings from previous_class_values.
(pop_from_top_level): Restore them.
Thu May 2 21:56:49 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_vtable_vardecl): Only write out vtable if its
symbol has been referenced.
(finish_file): Re-join synthesis/vtable loop with inline emission
loop, disable inlining when an inline is output.
Thu May 2 17:20:02 1996 Mike Stump <mrs@cygnus.com>
* except.c (init_exception_processing): Setup saved_in_catch.
(push_eh_cleanup): Reset __eh_in_catch.
(expand_start_catch_block): Set __eh_in_catch.
Thu May 2 16:21:17 1996 Mike Stump <mrs@cygnus.com>
* except.c (push_eh_cleanup): Add tracking for whether or not we
have an active exception object.
(expand_builtin_throw): Use it to make sure a rethrow without an
exception object is caught.
Thu May 2 11:26:41 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (maybe_push_to_top_level): Clear out class-level bindings
cache.
Wed May 1 11:26:52 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Also use sentries for vars with
DECL_ONE_ONLY or DECL_WEAK set (should any such happen to be
created).
* lex.c (handle_cp_pragma): Disable #pragma
interface/implementation if SUPPORTS_ONE_ONLY > 1.
Tue Apr 30 11:25:46 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (emit_thunk): Wrap default case in
temporary/permanent_allocation.
* method.c (make_thunk): Use DECL_ONE_ONLY.
(emit_thunk): Call assemble_end_function.
Mon Apr 29 15:38:29 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_vtable): Use DECL_ONE_ONLY.
(import_export_decl): Likewise.
(finish_prevtable_vardecl): Disable vtable hack if
SUPPORTS_ONE_ONLY > 1.
Mon Apr 29 14:32:47 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_modify_expr): PREINCREMENT_EXPR and
PREDECREMENT_EXPRs take two arguments, not one.
Mon Apr 29 00:27:53 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (build_vtable_entry): Don't build thunks for abstract
virtuals.
* lex.c (real_yylex): Fix handling of __PRETTY_FUNCTION__ like C
frontend.
Sat Apr 27 16:45:35 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (set_rtti_entry): Use size_zero_node.
(build_vtable): Likewise.
Sat Apr 27 14:48:57 1996 Jason Merrill <jason@phydeaux.cygnus.com>
* class.c (finish_struct_1): Pass size_zero_node to set_rtti_entry.
(prepare_fresh_vtable): Likewise.
Fri Apr 26 13:14:14 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (emit_thunk): Call mark_used on the target function.
* call.c (build_method_call): Don't warn about pending templates.
Thu Apr 25 14:55:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Fix list walking logic.
* typeck2.c (check_for_new_type): Only warn if -pedantic.
Wed Apr 24 15:41:15 1996 Bob Manson <manson@charmed.cygnus.com>
* class.c (finish_struct_1): Remove old code for
dont_allow_type_definitions.
* cp-tree.h: Likewise.
* spew.c: Make sure cp-tree.h is included before parse.h, so the
definition of flagged_type_tree is found before it is used.
* lex.c: Likewise.
* parse.y: Added the ftype member to the type union, and changed a
number of rules to use it instead of ttype. Added calls to
check_for_new_type() as appropriate.
* typeck2.c (check_for_new_type): New function for checking
if a newly defined type appears in the specified tree.
* cp-tree.h: Add new type flagged_type_tree. Add a prototype
for check_for_new_type().
Wed Apr 24 00:36:21 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Only use a sentry if the decl is public.
* pt.c (tsubst_expr, DECL_STMT): If we don't have an initializer,
don't pass LOOKUP_ONLYCONVERTING.
Tue Apr 23 17:18:47 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (common_type): Fix the ARRAY_TYPE case so it
properly keeps track of const and volatile type modifiers.
Tue Apr 23 10:52:56 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (cp_tree_equal): C++ version of simple_cst_equal.
* pt.c (comp_template_args): Use it.
* rtti.c (get_tinfo_fn, build_dynamic_cast, expand_*_desc): Call
assemble_external for artificial function decls.
* decl.c (cp_finish_decl): Oops.
Mon Apr 22 17:28:27 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (import_export_decl): Put static data member templates
into common storage, or make them weak, depending on whether they
are dynamically or statically initialized.
(get_sentry): New function.
(finish_file): Do import_export_decl for static data members before
building the init/fini functions. Don't init/fini a variable that's
EXTERNAL. Use a sentry for variables in common. Fix mismatching
push/pop_temp_slots.
* decl.c (cp_finish_decl): If DECL_NOT_REALLY_EXTERN, do the
expand_static_init thang.
* method.c (get_id_2): New function.
Mon Apr 22 15:32:45 1996 Bob Manson <manson@charmed.cygnus.com>
* parse.y (empty_parms): Make sure we use C++-style prototypes
when we're declaring member functions.
Sun Apr 21 10:08:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* Makefile.in (CONFLICTS): 16 s/r conflicts.
* parse.y (self_template_type): New nonterminal.
Thu Apr 18 08:56:54 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (make_typename_type): Handle getting a TYPE_DECL for a
name.
* parse.y (base_class.1): Allow 'typename foo::bar'.
* lex.c (check_newline): Remove #pragma code that plays with the
input stream, since we now deal with tokens. Clear nextchar when
we're done.
(handle_cp_pragma): Use real_yylex.
(handle_sysv_pragma): Don't do skipline here. Only call real_yylex
in one place.
* lex.c (check_for_missing_semicolon): Handle SELFNAME.
* lex.c (handle_cp_pragma): Fix "#pragma implementation".
Wed Apr 17 16:51:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* parse.y: New token SELFNAME for potential constructor.
* spew.c (yylex): Handle it.
* lex.c (identifier_type): Produce it.
* parse.y (complete_type_name): In :: case, don't push class binding.
(complex_type_name): Likewise.
Wed Apr 17 15:02:40 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_reinterpret_cast): Handle pointer to member
functions.
Wed Apr 17 12:28:26 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* lex.c (handle_cp_pragma): New function, with decl, doing the cc1plus
pragmas.
(check_newline): Put the vtable/unit/implementation/interface pragma
code into handle_cp_pragma, replacing it with a call.
(handle_sysv_pragma): Give int return type, and take FINPUT and TOKEN
args. Get the next token after handling the pragma token.
Wed Apr 17 10:28:34 1996 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (cp_convert_to_pointer): Avoid doing base analysis on pmfs.
(convert_to_pointer_force): Likewise.
* init.c (build_new): Fix array new without -fcheck-new.
Tue Apr 16 13:44:58 1996 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h, call.c, class.c, decl.c, parse.y, pt.c, rtti.c,
tree.c: Lose TYPE_NESTED_NAME.
* parse.y (nested_name_specifier_1): Don't treat non-identifiers
as identifiers.
* tree.def: Add VEC_INIT_EXPR.
* expr.c (cplus_expand_expr): Handle it.
* init.c (build_new): Use it instead of the RTL_EXPR nastiness and
the extra file-scope symbol nastiness.
Mon Apr 15 16:21:29 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (make_thunk): Thunks are static.
(emit_thunk): Use ASM_OUTPUT_MI_THUNK if it's defined.
* decl2.c (mark_vtable_entries): Emit thunks as needed.
(finish_file): Don't emit them here.
Sun Apr 14 11:34:39 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (build_dynamic_cast): Handle null pointers.
(ifnonnull): New function.
Fri Apr 12 09:08:27 1996 Bob Manson <manson@charmed.cygnus.com>
* call.c (build_method_call): Remember the original basetype we
were called with. Give an error message instead of trying
(incorrectly) to call a non-static member function through a
non-inherited class.
* search.c (expand_upcast_fixups): Mark the new fixup as
DECL_ARTIFICIAL.
Thu Apr 11 03:57:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): Use a TARGET_EXPR for alloc_expr.
* class.c (set_rtti_entry): Fix for thunks.
* decl2.c (import_export_decl): Still emit typeinfo fns for
cv-variants of builtin types.
* rtti.c (expand_class_desc): Set up base_info_type_node here.
(init_rtti_processing): Instead of here.
Wed Apr 10 14:17:13 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (init_rtti_processing): Do init regardless of -frtti.
(build_typeid): Only complain about taking dynamic typeid without
-frtti.
* decl2.c: flag_rtti defaults to 1.
* rtti.c (get_tinfo_var): The general class case is now smaller.
(init_rtti_processing): Pack the latter three fields of base_info
into 32 bits.
Wed Apr 10 13:50:14 1996 Mike Stump <mrs@cygnus.com>
* init.c (expand_member_init): Don't dump if name is NULL_TREE.
Wed Apr 10 12:56:02 1996 Mike Stump <mrs@cygnus.com>
* search.c (make_memoized_table_entry): Undefer the pop, if necessary.
(push_memoized_context): Split out code to undefer pop_type_level to
(clear_memoized_cache): here.
(pop_memoized_context): We can only handle one layer of deferral of
pop_type_level so clear the cache, if there was a previous level.
Tue Apr 9 23:06:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (init_rtti_processing): Build up base_info_type_node.
(expand_class_desc): Use one pointer to an array of base_info
structs, passed using a CONSTRUCTOR.
Tue Apr 9 14:20:57 1996 Mike Stump <mrs@cygnus.com>
* class.c (build_vbase_path): Remove block extern for
flag_assume_nonnull_objects here.
(build_vfn_ref): Split out functionality into build_vtbl_ref.
(build_vtbl_ref): New routine.
(build_vtable): Set up rtti info here.
(add_virtual_function): Note in CLASSTYPE_RTTI the best
place where we can get the rtti pointers from to avoid having to
search around for a place.
(finish_base_struct): Likewise.
(finish_struct_1): Likewise. Never create totally new vtables
with totally new vtable pointers for rtti. Disable code to layout
vtable pointers better until we want to break binary
compatibility.
* rtti.c (build_headof_sub): New routine to convert down to a
sub-object that has an rtti pointer in the vtable.
(build_headof): Use it. Also, use build_vtbl_ref now to be more
maintainable.
(build_dynamic_cast): Make sure we have saved it, if we need to.
* search.c (dfs_init_vbase_pointers): Disable code that deals with
a more efficient vtable layout, enable later.
* call.c (flag_assume_nonnull_objects): Moved declaration to
* cp-tree.h: here. Declare build_vtbl_ref.
* pt.c (instantiate_class_template): Use NULL_TREE instead of 0 in
function calls that want a tree.
Tue Apr 9 12:10:26 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (build_dynamic_cast): Handle downcasting to X* given
other X subobjects in the most derived type. Ack.
* rtti.c (build_dynamic_cast): No need to strip cv-quals here,
get_typeid will do it for us.
(get_typeid_1): Break out call-building for expand_*_desc to use.
(get_typeid): Call it.
(expand_*_desc): Likewise.
* decl.c (init_decl_processing): Don't set TYPE_BUILT_IN on char *
and void *.
(init_decl_processing): Lose builtin_type_tdescs lossage.
* decl2.c (finish_vtable_vardecl): Remove obsolete code.
Mon Apr 8 17:23:23 1996 Bob Manson <manson@charmed.cygnus.com>
* pt.c (tsubst): When calling set_nested_typename, use
TYPE_NESTED_NAME (current_class_type) instead of
current_class_name.
* decl.c (pushdecl): Likewise.
(pushdecl_class_level): Likewise.
(grokdeclarator): Use NULL_TREE instead of 0 in the call to
set_nested_typename.
Sun Apr 7 10:44:31 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (synthesize_tinfo_fn): Handle arrays.
* cp-tree.h (DECL_REALLY_EXTERN): New macro.
Sat Apr 6 13:56:27 1996 Jason Merrill <jason@yorick.cygnus.com>
* rtti.c (throw_bad_cast): Use entry point __throw_bad_cast.
(init_rtti_processing): Lose bad_cast_type.
(build_dynamic_cast): Use throw_bad_cast.
* rtti.c (synthesize_tinfo_fn): Handle enums and pmfs.
* decl2.c (finish_file): Don't synthesize artificial functions
that are external and not inline.
* rtti.c (get_tinfo_fn): If at_eof, call import_export_decl.
* decl2.c (finish_file): Handle having new inlines added to
saved_inlines by synthesis.
* rtti.c (get_bad_cast_node): Don't require <typeinfo>.
Fri Apr 5 17:02:09 1996 Jason Merrill <jason@yorick.cygnus.com>
RTTI rewrite to initialize nodes as needed, not require that
users #include <typeinfo>, complete functionality and reduce wasted
space.
* rtti.c (init_rtti_processing): New fn.
(build_typeid): The vtable entry is now a function.
(get_tinfo_var): New fn.
(get_tinfo_fn): Likewise.
(get_typeid): Use it.
(build_dynamic_cast): Declare and use entry point __dynamic_cast.
(build_*_desc): Rename to expand_*_desc and rewrite to use entry
points __rtti_*.
(add_uninstantiated_desc, get_def_to_follow, build_t_desc): Lose.
(synthesize_tinfo_fn): New fn.
* method.c (build_t_desc_overload): Lose.
(build_overload_with_type): More generic.
* decl.c (init_decl_processing): Call init_rtti_processing.
* class.c (set_rtti_entry): Use get_tinfo_fn.
* decl2.c (mark_vtable_entries): Mark the rtti function.
(finish_prevtable_vardecl): Don't build_t_desc.
(import_export_decl): Handle tinfo functions.
(finish_file): Likewise.
* typeck.c (inline_conversion): New fn.
(build_function_call_real): Use it.
* cp-tree.h: Add decls.
* method.c (hack_identifier): Also convert component_refs from
references.
* lex.c (cons_up_default_function): Use the type, not the name, in
declspecs.
* decl2.c (import_export_vtable): Fix weak vtables.
Fri Apr 5 13:30:17 1996 Bob Manson <manson@charmed.cygnus.com>
* search.c (get_base_distance_recursive): Fix access checks for
protected bases.
Fri Apr 5 11:02:06 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* call.c (unary_complex_lvalue): Delete unneeded decl, it's in
cp-tree.h.
(convert_harshness): Add prototypes wrapped by PROTO.
* decl2.c (grok_function_init): Likewise.
(do_toplevel_using_decl): Change to void return type.
* class.c (build_vtable_entry): Remove decl of make_thunk.
(merge_overrides): Fix order of arg definitions.
(finish_vtbls): Likewise.
(fixup_vtable_deltas): Likewise.
(modify_all_direct_vtables): Likewise.
(modify_all_indirect_vtables): Likewise.
* search.c (get_base_distance_recursive): Likewise.
(get_abstract_virtuals_1): Likewise.
(fixup_virtual_upcast_offsets): Likewise.
(lookup_fnfields_1): Add prototypes wrapped by PROTO.
* init.c (perform_member_init): Fix order of arg definitions.
(expand_aggr_init_1): Add prototypes wrapped by PROTO.
* cp-tree.h (make_thunk): Add decl.
(overload_template_name, push_template_decl): Add decls.
(do_toplevel_using_decl): Change to void return type.
(vec_binfo_member): Add decl.
Thu Apr 4 13:33:10 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (mark_addressable, convert_for_assignment,
convert_for_initialization, pointer_int_sum, pointer_diff,
unary_complex_lvalue): Add prototypes wrapped by PROTO.
(convert_sequence): #if 0 fn decl, since definition also is.
Thu Apr 4 11:00:53 1996 Mike Stump <mrs@cygnus.com>
* rtti.c (build_dynamic_cast): Make sure we strip qualifiers on
cast to pointer types for type searching.
Wed Apr 3 17:10:57 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (get_delta_difference): Use cp_error, not error, in the
case where BINFO == 0.
Wed Apr 3 12:01:02 1996 Mike Stump <mrs@cygnus.com>
* call.c (build_method_call): Fix wording of error messages so
constructors come out right.
Tue Apr 2 16:06:59 1996 Bob Manson <manson@charmed.cygnus.com>
* decl.c (push_overloaded_decl): Don't warn about hidden
constructors when both the type and the function are declared
in a system header file.
Mon Apr 1 09:03:13 1996 Bob Manson <manson@charmed.cygnus.com>
* class.c (finish_struct_1): Propagate the TYPE_PACKED
flag for the type to the type's fields.
Sat Mar 30 12:14:33 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* parse.y (complex_parmlist, ELLIPSES): Take out ARM-based warning.
Fri Mar 29 15:51:36 1996 Bob Manson <manson@charmed.cygnus.com>
* class.c (base_info, finish_base_struct): Replace
needs_virtual_dtor with base_has_virtual.
(finish_struct_1): Remove the old code that tried to make default
destructors virtual. Use base_has_virtual when checking if we need
to add a vtable entry for the rtti code.
Fri Mar 29 14:02:36 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (push_template_decl): Complain about template decl with
inappropriate declaration.
Fri Mar 29 12:15:35 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (build_x_unary_op): Remove bogus check for taking
the address of a member function.
Fri Mar 29 11:56:02 1996 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (constructor_declarator): Only push the class if
we are not already in the class.
Fri Mar 29 09:41:02 1996 Jeffrey A. Law <law@cygnus.com>
* method.c (emit_thunk): Remove current_call_is_indirect nonsense.
Add additional argument to INIT_CUMULATIVE_ARGS.
Thu Mar 28 16:41:39 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (shadow_tag): Fix error about anon union with methods.
* parse.y (self_reference): Only generate a self-reference if this
is a non-template class.
(opt.component_decl_list): Only use it if it was generated.
* parse.y (component_decl_1): Use constructor_declarator.
(fn.def2): Likewise.
(notype_component_declarator0): Likewise.
Thu Mar 28 15:11:35 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (build_x_unary_op): Add checks for taking the address
of a TARGET_EXPR or of a member function, and give appropriate
warnings.
Thu Mar 28 14:49:26 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (process_template_parm): Allow template type parms to be
used as types for template const parms.
Wed Mar 27 15:51:19 1996 Mike Stump <mrs@cygnus.com>
* init.c (expand_vec_init): Ensure the eh cleanups are on the
function_obstack.
Wed Mar 27 10:14:30 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (lookup_name_real): Be even more picky about the
ambiguous lookup warning.
(grokdeclarator): Tweak SCOPE_REF constructor declarators here.
* parse.y (constructor_declarator): Rather than here.
* parse.y (constructor_declarator): New nonterminal.
(fn.def1): Use it.
(explicit_instantiation): Likewise.
Tue Mar 26 13:41:33 1996 Jason Merrill <jason@yorick.cygnus.com>
Add implicit declaration of class name at class scope.
* decl.c (lookup_name_real): Restrict pedwarn about ambiguous lookup.
* parse.y (self_reference): New nonterminal.
(opt.component_decl_list): Use it.
(fn.def1): Add nested_name_specifier type_name cases.
* class.c (build_self_reference): New function.
(finish_struct): Handle access_default later, move self-reference
decl to the end.
* pt.c (lookup_template_class): Handle getting a TYPE_DECL.
* cp-tree.h: Adjust.
* pt.c (do_function_instantiation): Separate handling of member
functions and non-member functions properly.
Mon Mar 25 14:23:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (process_template_parm): Improve error for 'volatile class K'.
* class.c (finish_struct_1): Check the right slot for destructors.
* decl.c (start_enum): Complain about enum templates.
Mon Mar 25 13:25:31 1996 Mike Stump <mrs@cygnus.com>
* init.c (resolve_offset_ref): Offset pointers to member data by one.
* typeck.c (unary_complex_lvalue): Likewise.
Mon Mar 25 13:30:42 1996 Bob Manson <manson@charmed.cygnus.com>
* typeck.c (c_expand_return): Check for a returned local
array name, similar to the check for an ADDR_EXPR.
Mon Mar 25 13:07:19 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (cp_finish_decl): Don't build cleanups for static
variables here.
Fri Mar 22 17:57:55 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_modify_expr): Fix error messages to be more
accurate.
* cp-tree.h (assop_as_string): Parallel to op_as_string, but for
assignment operators.
* error.c (assop_as_string): Likewise. Add support for `%Q' for
assignment operators.
Fri Mar 22 13:48:29 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Call bad_specifiers for typedefs. Also
give an error if initialized. pedwarn about nested type with the
same name as its enclosing class.
* pt.c (tsubst, case TYPE_DECL): Set DECL_CONTEXT.
* typeck.c (require_complete_type): Be sure to instantiate the
MAIN_VARIANT of the type.
* decl2.c (finish_file): Instantiate pending templates before
processing static constructors and destructors.
* pt.c (instantiate_decl): Don't instantiate functions at toplevel
unless at_eof.
Fri Mar 22 09:30:17 1996 Bob Manson <manson@beauty.cygnus.com>
* decl2.c (delete_sanity): If error_mark_node is passed
in as an expression, quit while we're ahead.
* decl.c (grokdeclarator): Give an error message if `friend'
is combined with any storage class specifiers.
Wed Mar 20 14:51:55 1996 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (named_complex_class_head_sans_basetype): Don't crash on
definition of nonexistent nested type.
* error.c (dump_decl, case TYPE_DECL): Fix decision for whether or
not to say 'typedef'.
Wed Mar 20 00:11:47 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* cp-tree.h (struct lang_type): Make search_slot a tree, not a char*.
* search.c (dfs_walk, dfs_init_vbase_pointers,
expand_upcast_fixups): Remove cast of CLASSTYPE_SEARCH_SLOT.
(dfs_find_vbases): Remove cast for CLASSTYPE_SEARCH_SLOT init.
Tue Mar 19 17:56:03 1996 Jason Merrill <jason@yorick.cygnus.com>
* except.c (build_throw): Support minimal parse.
* pt.c (tsubst_copy): Support THROW_EXPR.
* decl2.c (build_expr_from_tree): Likewise.
* pt.c (mangle_class_name_for_template): Always allocate
scratch_firstobj.
Tue Mar 19 16:34:31 1996 Bob Manson <manson@beauty.cygnus.com>
* cvt.c (cp_convert_to_pointer): Give an appropriate error
when trying to cast from an incomplete type.
Tue Mar 19 16:00:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): Don't bother setting up
CLASSTYPE_TAGS explicitly, as the nested types will add
themselves.
Tue Mar 19 15:48:43 1996 Bob Manson <manson@beauty.cygnus.com>
* decl.c (shadow_tag): Remove old error check for usage of
an enum without a previous declaration.
(xref_tag): Add error message about usage of enums without a
previous declaration.
Tue Mar 19 09:21:35 1996 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (do_identifier): Only do name consistency check if we're
parsing.
* pt.c (push_template_decl): Don't crash if we get a member defn
that doesn't match.
* decl.c (xref_tag_from_type): New function to do an xref without
always having to figure out code_type_node.
* cp-tree.h: Declare it.
* pt.c (instantiate_class_template): Use it for friend classes.
(lookup_template_class): Use it.
* typeck2.c (build_functional_cast): Pull out a single parm before
passing it to build_c_cast.
Tue Mar 19 09:07:15 1996 Bob Manson <manson@beauty.cygnus.com>
* expr.c (do_case): Give an error message if a pointer is
given as a case value.
Mon Mar 18 21:57:54 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_c_cast): Don't pull single TEMPLATE_DECL out of
an overload list.
* lex.c (cons_up_default_function): Really, now, interface hackery
does not apply to synthesized methods.
Mon Mar 18 18:20:57 1996 Mike Stump <mrs@cygnus.com>
* call.c (build_method_call): Ctors and dtors now have special names
with respect to lookups.
* class.c (add_method): Likewise.
(grow_method): Likewise.
(finish_struct_methods): Likewise.
(warn_hidden): Likewise.
(finish_struct_1): Likewise.
* cvt.c (convert_to_reference): Likewise.
(convert_to_aggr): Likewise.
(cp_convert): Likewise.
* decl2.c (check_classfn): Likewise.
* init.c (expand_member_init): Likewise.
(expand_default_init): Likewise.
(expand_aggr_init_1): Likewise.
(build_offset_ref): Likewise.
(build_new): Likewise.
(build_delete): Likewise.
* lex.c (do_inline_function_hair): Likewise.
* search.c (lookup_field_1): Likewise.
(lookup_fnfields_here): Likewise.
(lookup_field): Likewise.
(lookup_fnfields): Likewise.
(get_virtual_destructor): Likewise.
(dfs_debug_mark): Likewise.
(dfs_pushdecls): Likewise.
(dfs_compress_decls): Likewise.
* tree.c (layout_basetypes): Likewise.
* typeck.c (build_component_ref): Likewise.
(build_x_function_call): Likewise.
(build_modify_expr): Likewise.
(convert_for_initialization): Likewise.
(build_functional_cast): Likewise.
* cp-tree.h (CLASSTYPE_FIRST_CONVERSION): Likewise.
(CTOR_NAME): New.
(DTOR_NAME): New.
* decl.c (ctor_identifier): New.
(dtor_identifier): New.
(init_decl_processing): Set them.
Mon Mar 18 18:00:51 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_component_ref): Don't get confused by fields whose
context has no type name, like pointer to member functions.
Mon Mar 18 13:19:03 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Handle typedef without declarator.
* pt.c (tsubst): Handle SCOPE_REF in declarator.
* parse.y (bad_parm): Catch another case of missing `typename'.
* lex.c (yyprint): Handle TYPE_DECLs.
* decl.c (start_function): Don't try to be clever.
* lex.c: Lose compiler_error_with_decl.
* typeck2.c: Lose error_with_aggr_type.
(incomplete_type_error): Use cp_* instead of old functions.
(readonly_error): Likewise.
* typeck.c (convert_arguments): Likewise.
* search.c (lookup_nested_field): Likewise.
* method.c (make_thunk): Likewise.
* decl.c (grokparms): Likewise.
* cp-tree.h: Update.
* tree.c (min_tree_cons): Call copy_to_permanent for the purpose
and value.
Mon Mar 18 11:25:52 1996 Bob Manson <manson@beauty.cygnus.com>
* method.c (build_opfncall): When deleting a pointer to an
array, build a new pointer to the tree past any ARRAY_TYPE
nodes.
Mon Mar 18 10:11:46 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (lookup_name_real): Initialize local var TYPE to NULL_TREE.
Fri Mar 15 11:03:57 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): Only call import_export_decl if at_eof
and ! DECL_INLINE.
* decl.c (finish_function): Don't set nested based on
hack_decl_function_context.
* parse.y (function_try_block): Check for nested function.
(pending_inlines): Likewise.
* decl2.c (build_expr_from_tree): If a unary op already has a
type, just return it.
* decl2.c (finish_prevtable_vardecl): Use ADJUST_VTABLE_LINKAGE.
* decl2.c (walk_vtables): vardecl_fn returns int; return 1 if it does.
(finish_file): Check the return value of walk_vtables.
(finish_prevtable_vardecl): Return int.
(finish_vtable_vardecl): Likewise.
(prune_vtable_vardecl): Likewise.
* lex.c (set_vardecl_interface_info): Likewise.
* cp-tree.h: Adjust return types.
* class.c (delete_duplicate_fields_1): Don't complain about
duplicate nested types if they're the same type.
(finish_struct): Remove check for duplicate.
* decl2.c (grokfield): Don't check for typedef of anonymous type.
Thu Mar 14 10:00:19 1996 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h: Lose SIGNATURE_GROKKING_TYPEDEF.
* decl.c (grokdeclarator): Lose special handling of class-level
typedef. Lose SIGNATURE_GROKKING_TYPEDEF. Set
SIGNATURE_HAS_OPAQUE_TYPEDECLS later.
* cvt.c (convert_pointer_to_real): Retain cv-quals in conversion.
* pt.c (tsubst_copy): Strip cv-quals from destructor name types.
* search.c (compute_access): Fix handling of anonymous union
members.
* class.c (finish_struct_anon): Propagate TREE_{PRIVATE,PROTECTED}
from anonymous unions to their members.
* typeck.c (build_x_function_call): For static member functions,
hand off to build_member_call.
Wed Mar 13 14:03:34 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_component_ref): Handle OFFSET_REFs.
* init.c (expand_vec_init): Fix init == 0 case.
Tue Mar 12 14:36:02 1996 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_new): pedwarn about init and array new.
(expand_vec_init): Handle lists, use convert_for_initialization.
* typeck.c (convert_for_initialization): Pass LOOKUP_NO_CONVERSION
when converting to an aggregate type.
* cvt.c (cp_convert): Pass it through.
* typeck.c (build_conditional_expr): Handle user-defined
conversions to slightly different types.
* decl.c (grokdeclarator): Force an array type in a parm to be
permanent.
* decl2.c (do_using_directive): Sorry.
(do_namespace_alias): Likewise.
* lex.c (real_yylex): Warn about using the `namespace' keyword.
Sun Mar 10 22:26:09 1996 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (datadef): Move call to note_list_got_semicolon up.
Fri Mar 8 11:47:26 1996 Mike Stump <mrs@cygnus.com>
* tree.c (unsave_expr): Don't unsave, UNSAVE_EXPRs.
Fri Mar 8 11:29:06 1996 Mike Stump <mrs@cygnus.com>
* decl.c (cp_finish_decl): The exception regions have to be
nested, not overlapping. We start the exception region for a
decl, after it has been fully built, and all temporaries for it
have been cleaned up.
Thu Mar 7 17:46:06 1996 Mike Stump <mrs@cygnus.com>
* tree.c (vec_binfo_member): Don't core dump if we have no bases.
Thu Mar 7 14:11:49 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.def: Add RETURN_INIT.
* pt.c (instantiate_decl): Handle RETURN_INIT.
* decl.c (store_return_init): Handle minimal_parse_mode.
* tree.c (cp_build_type_variant): Just return an error_mark_node.
* decl.c (make_typename_type): Don't try to get the file and line
of an identifier.
* typeck.c (comptypes): Handle TYPENAME_TYPE.
Wed Mar 6 18:47:50 1996 Per Bothner <bothner@kalessin.cygnus.com>
* decl.c (poplevel): Make sure we clear out and restore old local
non-VAR_DECL values by default when they go out of scope.
Wed Mar 6 09:57:36 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (build_overload_value): Use DECL_ASSEMBLER_NAME in
referring to addresses of variables and functions.
* error.c (dump_expr): Support SIZEOF_EXPR.
* init.c (do_friend): Use the return value of check_classfn.
* typeck.c (convert_arguments): Call complete_type.
* method.c (hack_identifier): After giving an error, set value to
error_mark_node.
Tue Mar 5 16:00:15 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (hack_decl_function_context): Kludge around DECL_CONTEXT
lossage for local classes.
* cp-tree.h: Declare it.
* decl.c (lookup_name_real): Evil, painful hack for local classes.
(grokfndecl): Set DECL_CLASS_CONTEXT and DECL_NO_STATIC_CHAIN here.
Use hack_decl_function_context.
(grokdeclarator): Don't set DECL_NO_STATIC_CHAIN here.
(start_function): Use hack_decl_function_context.
(finish_function): Likewise.
* method.c (synthesize_method): Likewise.
* lex.c (process_next_inline): Likewise.
(do_pending_inlines): Likewise.
* decl2.c (finish_file): Unset DECL_STATIC_FUNCTION_P when we're
done with it.
Mon Mar 4 22:38:39 1996 Gerald Baumgartner <gb@alexander.cs.purdue.edu>
* sig.c (build_signature_pointer_or_reference_type): Align
signature pointers/references on 8-byte boundaries so they can be
grabbed 2 words at a time on a Sparc.
Tue Mar 5 10:21:01 1996 Jason Merrill <jason@yorick.cygnus.com>
* method.c (hack_identifier): Requiring a static chain is now a
hard error.
* decl.c (grokdeclarator): Set DECL_NO_STATIC_CHAIN on nested
functions.
Mon Mar 4 20:03:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* init.c (build_offset_ref): Call complete_type.
* decl.c (pop_from_top_level): Always pop previous_class_type.
* parse.y: Handle multiple decls in a for-init-statement.
* pt.c (tsubst_expr): Likewise.
* pt.c (tsubst): Use tsubst_expr for the second operand of an
ARRAY_REF.
* decl.c (maybe_push_to_top_level): Don't save previous_class_type.
(poplevel_class): Set it here.
(pop_from_top_level): Pop it here if we're returning to class scope.
* class.c (pushclass): Don't set it here.
* decl.c (maybe_push_to_top_level): Save current_template_parms,
and clear it if !pseudo.
(pop_from_top_level): Restore it.
* decl2.c (finish_file): Push the dummy each time we walk the list
of vtables.
* error.c (dump_expr): Support LOOKUP_EXPR and actually do
something for CAST_EXPR.
Mon Feb 19 14:49:18 1996 Rusty Russell <rusty@adelaide.maptek.com.au>
* cvt.c (cp_convert): Warn about implicit conversion of the
address of a function to bool, as it is always true.
Fri Feb 23 23:06:01 1996 Rusty Russell <rusty@adelaide.maptek.com.au>
* typeck.c (c_expand_return): Fix warning for local externs returned.
Mon Mar 4 15:03:11 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (mapcar): Propagate const and volatile properly.
* typeck.c (complete_type): Be sure to instantiate the
MAIN_VARIANT of the type.
* method.c (synthesize_method): Class interface hackery does not
apply to synthesized methods.
Mon Mar 4 14:05:23 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (comp_template_args): Use comptypes rather than just
checking for TEMPLATE_TYPE_PARM equivalence.
* typeck.c (build_x_function_call): Call complete_type before
checking TYPE_OVERLOADS_CALL_EXPR.
Mon Mar 4 18:48:30 1996 Manfred Hollstein <manfred@lts.sel.alcatel.de>
* g++.c (main): Check also for new define ALT_LIBM.
Fri Mar 1 13:09:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_class_template): If we don't have a pattern
yet, that's OK.
(coerce_template_parms): If we see a local class, bail.
* decl.c (grok_reference_init): Make sure there's a type before
checking its code.
* pt.c (do_function_instantiation): Avoid crashing on invalid decls.
(push_template_decl): Likewise.
* parse.y (named_class_head): Set
CLASSTYPE_TEMPLATE_SPECIALIZATION here if we have basetypes.
* decl.c (xref_tag): Diagnose redeclaration of template
type-parameter name.
* error.c (dump_type): Handle anonymous template type parms.
* pt.c (instantiate_template): Use TYPE_MAIN_DECL instead of
TYPE_STUB_DECL.
(coerce_template_parms): Likewise.
Thu Feb 29 16:26:01 1996 Mike Stump <mrs@cygnus.com>
* class.c (instantiate_type, case {ARRAY,INDIRECT}_REF,
case ADDR_EXPR): Don't modify rhs if a subinstantiation fails.
Thu Feb 29 08:20:25 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_template): Take the MAIN_VARIANT of the type
before trying to get its STUB_DECL.
(coerce_template_parms): Likewise.
* parse.y (template_type_parm): If they didn't use 'class',
pretend they did after giving an error.
* pt.c (coerce_template_parms): Diagnose use of local class.
* decl.c (grok_reference_init): Use instantiate_type.
* error.c (dump_expr): Handle TEMPLATE_DECLs.
* parse.y (named_class_head): Diagnose mismatching types and tags.
* decl.c (pushdecl): Type decls and class templates clash with
artificial type decls, not hide them.
* decl.c (redeclaration_error_message): Diagnose redefinition of
templates properly.
(duplicate_decls): Diagnose disallowed overloads for template
functions, too.
* decl.c (start_decl): Call complete_type before checking for a
destructor.
* pt.c (tsubst): Use tsubst_expr on the elts of a VEC.
* decl.c (xref_tag): A TEMPLATE_TYPE_PARM is a match.
Wed Feb 28 09:28:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grok_op_properties): Don't check for operator++(int) in
a template.
* tree.c (perm_manip): Return a copy of variable and function
decls with external linkage.
* tree.def: Change some of the min tree codes to type "1".
* pt.c (uses_template_parms): Handle 'e's, return 1 for LOOKUP_EXPRs.
* method.c (build_overload_int): Emit something arbitrary for
anything but an INTEGER_CST if we're in a template.
* decl.c (cp_finish_decl): Call complete_type before deciding
whether or not to lay out the decl.
* lex.c (do_identifier): Check for DECL_INITIAL before using it.
Tue Feb 27 16:35:32 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck2.c (build_x_arrow): Call complete_type.
* pt.c (add_pending_template): Broken out.
(lookup_template_class): If -fexternal-templates, call it for all
the methods of implemented types.
(instantiate_class_template): Instead of instantiating them here.
(instantiate_decl): Handle -fexternal-templates earlier.
Tue Feb 27 15:51:32 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* search.c, lex.c, decl.c, class.c, cp-tree.h: Don't wrap the
memoized lookup stuff inside GATHER_STATISTICS.
Tue Feb 27 10:38:08 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (start_decl): Complain about array of incomplete type
here.
(grokdeclarator): Not here.
* parse.y (template_parm): Expand full_parm inline so we can set
the rule's precedence.
* pt.c (tsubst_expr): If we're in a template, just do tsubst_copy.
(tsubst): tsubst_expr the DECL_INITIAL of FIELD_DECLs.
* decl2.c (grokbitfield): Don't check for integer constant here.
* class.c (finish_struct_1): Check here.
* decl.c (define_label): Make the min decl go on permanent_obstack.
* pt.c (unify): Don't handle CONST_DECLs.
(uses_template_parms): Don't check DECL_INITIAL on a CONST_DECL.
(tsubst_copy): Likewise.
* lex.c (do_identifier): Do pull the DECL_INITIAL out of a
CONST_DECL for a template parm.
Mon Feb 26 12:48:18 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokdeclarator): Complain about array of incomplete type
here.
(start_decl_1): Not here.
* pt.c (tsubst): Handle pointer-to-function declarators.
* method.c (hack_identifier): If pedantic, diagnose local class
methods that require a static chain.
* decl.c (grok_op_properties): No longer static.
* cp-tree.h: Declare it.
* pt.c (tsubst): Call it for operators.
Use tsubst_copy for TREE_VECs.
* parse.y (template_arg): The expr has precedence like '>'.
Fri Feb 23 14:51:52 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (coerce_template_parms): Don't coerce an expression using
template parms.
(uses_template_parms): Also check DECL_INITIAL in CONST_DECLs.
(tsubst): Don't use build_index_2_type if the max_value uses template
parms.
* method.c (build_overload_int): Emit something arbitrary for an
expression using template parms.
* parse.y (template_close_bracket): New non-terminal to catch use
of '>>' instead of '> >' in template class names.
(template_type): Use it.
* Makefile.in (CONFLICTS): Causes one more r/r conflict.
* tree.def: Add CAST_EXPR.
* typeck2.c (build_functional_cast): Use CAST_EXPR instead of
CONVERT_EXPR for minimal_parse_mode.
* typeck.c (build_c_cast): Likewise.
* pt.c (tsubst_copy): Likewise.
* decl2.c (build_expr_from_tree): Likewise.
* error.c (dump_expr): Likewise.
Fri Feb 23 10:36:46 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* except.c (SetTerminate, SetUnexpected): Put back global vars.
(init_exception_processing): Put back decl/init of
set_unexpected_fndecl and set_terminate_fndecl, needed to get the
fns from libstdc++.
* decl.c (struct binding_level): Delete ACCEPT_ANY bitfield.
(declare_uninstantiated_type_level, uninstantiated_type_level_p):
Delete unused fns.
* cp-tree.h (declare_uninstantiated_type_level,
uninstantiated_type_level_p): Delete prototypes.
Thu Feb 22 19:36:15 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst_expr): Add default return.
Thu Feb 22 16:47:24 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* error.c (fndecl_as_string): Delete unused arg CNAME.
* sig.c (build_signature_table_constructor,
build_signature_method_call): Fix calls.
* class.c (the_null_vtable_entry): Delete var definition.
(init_class_processing): Delete tree the_null_vtable_entry init.
* decl.c (no_print_{functions, builtins}): Declare as static.
(__tp_desc_type_node): #if 0 var definition.
(init_type_desc): #if 0 init of __tp_desc_type_node.
(vb_off_identifier): Move var decl into init_decl_processing.
(current_function_assigns_this): Declare as static.
(int_ftype_ptr_ptr_int, void_ftype_ptr_int_int): Delete var decls.
(init_decl_processing): Delete init of void_ftype_ptr_ptr_int.
Move decls of string_ftype_ptr_ptr and int_ftype_string_string here.
* decl2.c (delete_sanity): Delete definition/mod of local var ELT_SIZE.
* init.c (BI_header_type, BI_header_size): Declare as static.
* pt.c (template_classes): Delete unused var.
(add_pending_template): Delete decl for non-existent fn.
(lookup_template_class): Delete vars CODE and TAG_CODE.
(instantiate_template): Delete unused var TARGS.
* cp-tree.h (vb_off_identifier, current_function_assigns_this):
Delete decls.
(__tp_desc_type_node): #if 0 var decl.
(fndecl_as_string): Fix prototype.
Thu Feb 22 15:56:19 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.def: Add GOTO_STMT.
* pt.c (tsubst_expr): Support goto and labels.
* decl.c (define_label): Support minimal parsing.
* parse.y (simple_stmt): Likewise.
Thu Feb 22 15:30:12 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* xref.c (GNU_xref_member): Only define/set var I if
XREF_SHORT_MEMBER_NAMES is defined, to match when it's actually
used.
(GNU_xref_end_scope): Delete unused fifth arg TRNS.
(GNU_xref_end): Fix call.
* decl.c (poplevel, poplevel_class, finish_method): Fix calls.
* cp-tree.h (GNU_xref_end_scope): Fix prototype.
* tree.c (build_exception_variant): Delete unused vars I, A, T,
T2, and CNAME.
(layout_vbasetypes): Delete unused var NONVIRTUAL_VAR_SIZE.
(mapcar): Delete unused var CODE.
(build_cplus_new): Delete unused arg WITH_CLEANUP_P.
(break_out_cleanups): Fix call.
(bot_manip): Likewise.
* call.c (build_method_call): Likewise.
* cvt.c (build_up_reference, convert_to_reference, cp_convert):
Likewise.
* typeck.c (unary_complex_lvalue, build_modify_expr,
convert_for_initialization): Likewise.
* typeck2.c (build_functional_cast): Likewise.
* cp-tree.h (build_cplus_new): Fix prototype.
* repo.c (open_repo_file): Delete unused var Q.
(repo_compile_flags, repo_template_declared,
repo_template_defined, repo_class_defined, repo_inline_used,
repo_vtable_used, repo_tinfo_used): #if 0 unused fns.
(repo_get_id, repo_vtable_used): Declare as static.
* cp-tree.h (mark_{decl,class}_instantiated, finish_repo): Add
prototypes.
Thu Feb 22 14:53:35 1996 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (pending_inlines): Add function_try_block case.
* pt.c (unify): Fix for template const parms.
Thu Feb 22 13:24:15 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* lex.c (extract_interface_info): Delete forward decl.
(default_copy_constructor_body, default_assign_ref_body): Delete
decls for non-existent functions.
(synth_firstobj, inline_text_firstobjs): Delete unused vars.
(init_lex): Delete setting them.
(cons_up_default_function): Delete unused vars FUNC_BUF,
FUNC_LEN, and COMPLEX. Delete code setting COMPLEX. Delete old
#if 0'd synth code.
(toplevel, expression_obstack): Delete unused extern decls.
(tree_node_kind): Delete unused enum.
(tree_node_counts, tree_node_sizes): Wrap with #ifdef
GATHER_STATISTICS.
(tree_node_kind_names): Delete unused extern decl.
(synth_obstack): Delete unused var.
(init_lex): Don't set it.
(init_parse): Add decl before use.
(reduce_count): Only define #ifdef GATHER_STATISTICS && REDUCE_LENGTH.
(current_unit_{name, language}): Delete unused vars.
(check_newline): Don't bother setting them, just accept the #pragma.
* cp-tree.h (init_repo, peek_yylex): Add prototypes.
(current_unit_{name, language}): Delete decls.
* search.c: Wrap all of the memoized functions, macros, and
variables inside #ifdef GATHER_STATISTICS.
(lookup_field, lookup_fnfields): Likewise.
(init_search_processing): Likewise.
(reinit_search_statistics): Wrap whole function.
* lex.c (reinit_lang_specific): Wrap call to reinit_search_statistics.
* decl.c (finish_function): Only call pop_memoized_context if
GATHER_STATISTICS is defined.
(start_function): Likewise for push_memoized_context.
* class.c (pushclass, popclass): Likewise.
* cp-tree.h (CLASSTYPE_MTABLE_ENTRY): Move definition from here...
* search.c (CLASSTYPE_MTABLE_ENTRY): ... to here.
* cvt.c (cp_convert): Delete unused local var FORM.
* cp-tree.h (can_convert, can_convert_arg, real_lvalue_p): Add
prototypes.
Thu Feb 22 13:19:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (do_poplevel): Oops; really return what we get from
poplevel this time.
Thu Feb 22 11:41:44 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* cp-tree.h (is_aggr_type): Add prototype.
* cp-tree.h ({push,pop}_cp_function_context): Add decls.
* method.c ({push,pop}_cp_function_context): Delete decls.
* except.c (start_eh_unwinder, end_eh_unwinder): Declare as void.
(SetUnexpected, SetTerminate): Delete unused vars.
(init_exception_processing): Don't set SetUnexpected or
SetTerminate. Don't set SET_UNEXPECTED_FNDECL or SET_TERMINATE_FNDECL.
(output_exception_table_entry): Delete unused array LABEL.
(expand_internal_throw): Delete unused var PARAMS.
(expand_start_catch_block): Delete unused var CLEANUP.
(emit_exception_table): Delete unused var EH_NODE_DECL.
(expand_builtin_throw): Delete unused vars UNWIND_AND_THROW and
GOTO_UNWIND_AND_THROW. Don't set them.
(end_eh_unwinder): Add top decl.
(pop_rtl_from_perm): Delete unused decl of PERMANENT_OBSTACK.
(exception_section, push_rtl_perm, do_function_call,
lang_interim_eh, push_eh_cleanup, eh_outer_context,
expand_end_eh_spec, end_eh_unwinder): Declare as static.
(saved_pc, saved_throw_type, saved_throw_value, saved_cleanup,
throw_used): Likewise.
* cp-tree.h (expand_end_eh_spec): Delete prototype.
* search.c (dfs_mark, dfs_mark_vtable_path,
dfs_unmark_vtable_path, dfs_mark_new_vtable,
dfs_unmark_new_vtable, dfs_clear_search_slot,
dfs_search_slot_nonempty_p, bfs_markedp, bfs_unmarkedp,
bfs_marked_vtable_pathp, bfs_unmarked_vtable_pathp,
bfs_marked_new_vtablep, bfs_unmarked_new_vtablep): #if 0 unused
functions.
(n_fields_searched, n_calls_lookup_field, n_calls_lookup_field_1,
n_calls_lookup_fnfields, n_calls_lookup_fnfields_1,
n_calls_get_base_type, n_outer_fields_searched, n_contexts_saved):
Only define #ifdef GATHER_STATISTICS.
(reinit_search_statistics): Only init some vars if GATHER_STATISTICS
is defined.
(vbase_decl): Delete var definition.
(init_search): Delete old decl.
(init_vbase_pointers): Delete building of VBASE_DECL, since it's
never actually used.
(expand_indirect_vtbls_init): Delete init of VBASE_DECL.
(get_base_distance_recursive): Delete unused fourth arg
BASETYPE_PATH. Fix call .
(get_base_distance): Fix call.
(push_class_decls): Delete unused var ID.
(make_memoized_table_entry): Declare as static.
(breadth_first_search): Declare as static.
(tree_has_any_destructor_p): Declare as static.
(pop_class_decls): Delete unused arg pop_class_decls.
* class.c (popclass): Fix call to pop_class_decls.
* cp-tree.h (make_memoized_table_entry, breadth_first_search,
tree_has_any_destructor_p): Delete prototypes.
* rtti.c (build_ptmf_desc): Delete unused arg TYPE.
(build_t_desc): Fix call. Delete unused vars ELEMS and TT.
(build_dynamic_cast): Delete unused local vars TMP1 and RETVAL.
(build_user_desc): Delete unused var T.
(build_class_desc): Delete unused vars T and OFF.
(build_t_desc): Delete unused var NAME_STRING.
(build_headof): Make static.
(get_bad_cast_node): Likewise.
(get_def_to_follow): Likewise.
* cp-tree.h (init_type_desc): Add prototype.
(build_headof): Remove prototype.
Thu Feb 22 00:54:22 1996 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (tsubst): Only look for matching decls at file scope for
non-member functions.
* call.c (build_scoped_method_call): Handle scoped destructor
calls in templates.
* decl.c (*_top_level): Also save previous_class_values.
* pt.c (tsubst_expr): Support do {} while loops.
* parse.y (simple_stmt): Likewise.
* tree.def: Likewise.
* method.c (build_overload_identifier): For a class nested in a
template class, don't mangle in the template parms from our
context.
* lex.c, cp-tree.h: Remove support for template instantiations in
the pending_inlines code.
* pt.c: Remove dead functions and unused arguments.
(uses_template_parms): TYPENAME_TYPEs always use template parms.
* parse.y: Stop passing anything to end_template_decl.
* tree.c (print_lang_statistics): Only print tinst info #ifdef
GATHER_STATISTICS.
Wed Feb 21 16:57:33 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* init.c (expand_recursive_init{,_1}): Delete decls.
(sort_member_init): Delete unused var INIT.
(emit_base_init): Delete unused var X.
(build_offset_ref): Delete unused var CNAME.
(sort_member_init): Delete unused var FIELDS_TO_UNMARK.
(emit_base_init): Delete unused local var BASE. Delete extern
decl of IN_CHARGE_IDENTIFIER.
(build_delete): Delete unused local var VIRTUAL_SIZE.
* init.c (build_vec_delete): Delete unused third arg ELT_SIZE.
(build_delete): Fix call.
* decl2.c (delete_sanity): Likewise.
* cp-tree.h (build_vec_delete): Update prototype.
* typeck.c (common_base_type): Delete unused var TMP.
(build_binary_op): Delete local var ARGS_SAVE.
(build_array_ref): Delete unused var ITYPE.
(c_expand_return): Delete unused var USE_TEMP.
* typeck.c (compexcepttypes): Delete unused arg STRICT.
(comptypes): Fix calls.
* decl.c (duplicate_decls): Likewise.
* cp-tree.h (compexcepttypes): Delete extra arg.
* decl2.c (check_classfn): Delete unused second arg CNAME.
* decl.c (start_decl, grokfndecl): Fix calls.
* init.c (do_friend): Likewise.
* cp-tree.h (check_classfn): Update prototype.
* cp-tree.h (signature_error, import_export_vtable,
append_signature_fields, id_in_current_class, mark_used,
copy_assignment_arg_p): Add decls.
* decl2.c (mark_used): Delete decl.
* class.c (n_*): Wrap with #ifdef GATHER_STATISTICS.
* class.c (get_vtable_entry): Disable unused function.
(doing_hard_virtuals): Delete unused static global var.
(finish_struct_1): Don't init DOING_HARD_VIRTUALS.
(prepare_fresh_vtable): Delete unused vars PATH and RESULT.
(overrides): Delete unused vars RETTYPE and BASE_RETTYPE.
(modify_one_vtable): Delete unused var OLD_RTTI.
(finish_struct_anon): Delete unused vars OFFSET and X.
(finish_struct_bits): Delete unused var METHOD_VEC.
(get_basefndecls): Delete unused var PURPOSE. Delete unused
for-scope local variable METHODS.
* call.c (user_harshness): Delete unused/unneeded arg PARM.
(ideal_candidate): Delete unused args BASETYPE and PARMS.
(build_method_call): Delete unused args passed into ideal_candidate.
(build_overload_call_real): Likewise. Delete unused var OVERLOAD_NAME.
* cp-tree.h (synthesize_method): Add decl.
* decl.c (note_level_for_for): Give void return type.
(pushdecl_nonclass_level): Likewise.
(finish_function): Delete unused vars VFIELDS and ALLOCATED_THIS.
(poplevel): Delete unused var IMPLICIT_TRY_BLOCK.
(suspend_binding_level): Delete unused var LEVEL.
(duplicate_decls): Delete unused var CTYPE.
(duplicate_decls): Delete unused var PREVIOUS_C_DECL.
(init_decl_processing): Delete unused vars FLOAT_ENDLINK and
PTR_ENDLINK.
(grokdeclarator): Delete unused var C.
(grokdeclarator): Delete unused var SIZE_VARIES.
(grokparms): Delete unused var SAW_VOID.
(start_function): Delete unused var OLDDECL.
(cplus_expand_expr_stmt): Delete unused var
REMOVE_IMPLICIT_IMMEDIATELY.
* cp-tree.h (pushdecl_nonclass_level): Fix prototype.
* Makefile.in (CONFLICTS): Update to 12 shift/reduce.
Wed Feb 21 00:06:17 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (build_min): Set TREE_COMPLEXITY to lineno.
(build_min_nt): Likewise.
* pt.c (do_pushlevel): Emit line note.
(do_poplevel): Return what we get from poplevel.
(tsubst_expr): Set lineno from TREE_COMPLEXITY in stmt nodes.
* parse.y: Use do_pushlevel and do_poplevel.
* cp-tree.h: Declare do_poplevel.
* cp-tree.h: Declare at_eof.
* decl.c (cp_finish_decl): Pass it to rest_of_decl_compilation.
* decl2.c (import_export_decl): Renamed from import_export_inline.
(finish_file): Call it to do interface handling for statics.
* pt.c (tsubst_copy): Call mark_used on variables and functions
used here.
* decl2.c (finish_file): Don't emit statics we can't generate.
* pt.c (instantiate_decl): Don't set interface on instantiations
we can't generate.
* cp-tree.h (struct tinst_level): Change 'classname' to 'decl'.
* tree.c (print_lang_statistics): Print max template depth.
* pt.c (push_tinst_level): Dump entire instantiation context.
(instantiate_class_template): Use it and pop_tinst_level.
(instantiate_decl): Likewise.
* call.c class.c cp-tree.h decl.c decl2.c error.c lex.c method.c
pt.c ptree.c tree.def: Remove all traces of UNINSTANTIATED_P_TYPE.
Tue Feb 20 18:21:51 1996 Jason Merrill <jason@yorick.cygnus.com>
* call.c class.c cp-tree.h cvt.c decl.c decl2.c error.c expr.c
init.c lex.c method.c parse.y pt.c repo.c search.c spew.c tree.c
tree.def typeck.c typeck2.c xref.c: Massive, systemic changes for
the new template implementation.
Tue Feb 20 17:14:29 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl2.c (check_cp_case_value): Use STRIP_TYPE_NOPS.
Thu Feb 15 18:44:42 1996 Mike Stump <mrs@cygnus.com>
* decl.c (cp_finish_decl): Delay emitting the debug information for
a typedef that has been installed as the canonical typedef, if the
type has not yet been defined.
Thu Feb 15 09:39:08 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (grokfield): Still call pop_nested_class for access decls.
Wed Feb 14 17:30:04 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (lookup_label): Call label_rtx.
* decl.c (make_binding_level): New function.
(pushlevel, pushlevel_class): Call it instead of explicit
duplicate calls to xmalloc.
* decl.c (init_decl_processing): Delete useless build_pointer_type
call.
* decl.c (float_ftype_float, ldouble_ftype_ldouble): Add definitions.
(sizet_ftype_string): Delete variable.
(init_decl_processing): Add built-in functions fabsf, fabsl,
sqrtf, sqrtl, sinf, sin, sinl, cosf, cos, cosl. New local
variable strlen_ftype, used for strlen.
Wed Feb 14 16:21:25 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (push_to_top_level): Start from current_binding_level
again for now; the stl hacks depend on g++ being broken in this
way, and it'll be fixed in the template rewrite.
* tree.def: Add USING_DECL.
* decl2.c (do_class_using_decl): Implement.
(grokfield): Pass access decls off to do_class_using_decl instead of
grokdeclarator.
* error.c (dump_decl): Handle USING_DECLs.
* decl.c (grokdeclarator): Remove code for handling access decls.
* class.c (finish_struct_1): Adjust accordingly, treat using-decls
as access decls for now.
(finish_struct): Don't check USING_DECLs for other uses of the name.
* search.c (get_matching_virtual): Use cp_error_at.
Wed Feb 14 10:36:58 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* typeck.c (comptypes): Default COMP_TYPE_ATTRIBUTES to 1, to
match c-typeck.c.
(self_promoting_args_p): Move the check that TYPE is non-nil
before trying to look at its main variant.
(unsigned_type, signed_type): Add checking of DI/SI/HI/QI nodes.
* cp-tree.h (DECL_WAITING_FRIENDS, SET_DECL_WAITING_FRIENDS):
Delete macros.
* init.c (xref_friend, embrace_waiting_friends): Delete functions.
(do_friend): Delete call to xref_friend.
* class.c (finish_struct_1): Delete call to embrace_waiting_friends.
* typeck.c (convert_sequence): #if 0 unused function.
* cp-tree.h (DECL_IN_MEMORY_P): New macro w/ the check that used to
be in decl_in_memory_p.
(decl_in_memory_p): Delete decl.
* expr.c (decl_in_memory_p): Delete fn.
* typeck.c (mark_addressable): Use DECL_IN_MEMORY_P.
* decl.c (cp_finish_decl): Use DECL_IN_MEMORY_P.
Tue Feb 13 12:51:21 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): Check for a pure-specifier on a
non-virtual function here.
* decl2.c (grok_function_init): Don't check whether the function
is virtual here.
(grokfield): Don't call check_for_override here.
* decl.c (push_to_top_level): Start from inner_binding_level,
check class_shadowed in class levels.
Mon Feb 12 17:46:59 1996 Mike Stump <mrs@cygnus.com>
* decl.c (resume_level): Ignore things that don't have names, instead
of core dumping.
Mon Feb 12 15:47:44 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl2.c (grokfield): Set DECL_VINDEX properly for FUNCTION_DECLs.
Sat Feb 10 17:59:45 1996 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_1): Set DECL_VINDEX properly on a
synthesized dtor.
* parse.y (complete_type_name): Bind global_scope earlier.
(complex_type_name): Likewise.
(qualified_type_name): Remove.
Thu Feb 8 15:15:14 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (grokfndecl): Move code that looks for virtuals in base
classes...
* class.c (check_for_override): ... to a new function.
(finish_struct_1): Call it.
* cp-tree.h: Declare warn_sign_compare.
* typeck.c (build_binary_op_nodefault): Check warn_sign_compare
rather than extra_warnings to decide whether to warn about
comparison of signed and unsigned.
* decl2.c (lang_decode_option): Handle warn_sign_compare. -Wall
implies -Wsign-compare. -Wall doesn't imply -W.
Wed Feb 7 15:27:57 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_component_ref): Fix to handle anon unions in base
classes as well.
Wed Feb 7 14:29:12 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* class.c (resolves_to_fixed_type_p): Delete code dealing with
a WITH_CLEANUP_EXPR, since we don't generate them any more.
* cvt.c (build_up_reference): Likewise.
* decl.c (grok_reference_init): Likewise.
(cp_finish_decl): Likewise.
* error.c (dump_expr): Likewise.
* tree.c (real_lvalue_p): Likewise.
(lvalue_p): Likewise.
(build_cplus_new): Likewise.
(unsave_expr_now): Likewise.
* typeck.c (unary_complex_lvalue, build_modify_expr,
c_expand_return): Likewise.
Tue Feb 6 13:39:22 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
Make the C++ front-end pay attention to attributes for structures.
* class.c (finish_struct): New argument ATTRIBUTES, passed down into
finish_struct_1.
(finish_struct_1): New argument ATTRIBUTES; call cplus_decl_attributes.
Take out old round_up_size use and setting the DECL_ALIGN possibly
using it. Take out setting of TYPE_ALIGN to round_up_size, which
can override what the attribute set.
* cp-tree.h (finish_struct): Update prototype.
* parse.y (template_instantiate_once): Pass a NULL_TREE for the
attributes to finish_struct.
(structsp): For a CLASS decl, add maybe_attribute to rule and pass that
value down into finish_struct.
* Makefile.in (CONFLICTS): Switch to 7 shift/reduce conflicts.
Tue Feb 6 13:12:15 1996 Per Bothner <bothner@kalessin.cygnus.com>
* decl.c (poplevel): Re-word dead for local handling.
(pushdecl): Remove useless DECL_DEAD_FOR_LOCAL test.
(cp_finish_decl): If is_for_scope, check for duplicates so
we can disable is_for_scope. Otherwise, preserve_temp_slots.
* lex.c (do_identifier): Use global binding in preference of
dead for local variable.
Mon Feb 5 17:46:46 1996 Mike Stump <mrs@cygnus.com>
* init.c (initializing_context): Handle anon union changes, the
context where fields of anon unions can be initialized now has to be
found by walking up the TYPE_CONTEXT chain.
Fri Feb 2 14:54:04 1996 Doug Evans <dje@charmed.cygnus.com>
* decl.c (start_decl): #ifdef out code to set DECL_COMMON
if ASM_OUTPUT{,_ALIGNED}_BSS is defined.
(obscure_complex_init): If bss is supported, always set
DECL_INITIAL to error_mark_node.
Thu Feb 1 16:19:56 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* init.c (is_friend): Make sure there's a context before we see if
it's an aggr type.
Thu Feb 1 15:44:53 1996 Mike Stump <mrs@cygnus.com>
* init.c (is_friend): Classes are not friendly with nested classes.
Thu Feb 1 15:27:37 1996 Doug Evans <dje@charmed.cygnus.com>
* lex.c (check_newline): Pass last character read to HANDLE_PRAGMA,
and record its result.
Thu Feb 1 09:27:01 1996 Mike Stump <mrs@cygnus.com>
* class.c (finish_struct_anon): Switch around code to not move anon
union elements around, nor mess up their contexts, nor offsets,
instead we now build up the right number of COMPONENT_REFs for all
the anon unions that may be present at build_component_ref time.
* typeck.c (lookup_anon_field): New routine to handle field lookup
on fields without names. We find them, based upon their unique type
instead.
* typeck.c (build_component_ref): Allow FIELD_DECL components.
Handle finding components in anonymous unions, and ensure that a
COMPONENT_REF is built for each level as necessary.
Tue Jan 30 18:18:23 1996 Mike Stump <mrs@cygnus.com>
* cvt.c (build_up_reference): Make the INDIRECT_BIND case come after
code that ensures that copy ctors are used if appropriate.
Tue Jan 30 17:35:14 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* init.c (build_vec_delete): Only give an error if base isn't an
error_mark_node.
Mon Jan 29 17:09:06 1996 Mike Stump <mrs@cygnus.com>
* spew.c (do_aggr): `new struct S;' isn't a forward declaration.
(yylex): If we see `new', keep slurping.
Thu Jan 25 18:31:36 1996 Mike Stump <mrs@cygnus.com>
* class.c (finish_struct_1): Move code for handling anon unions...
(finish_struct_anon): to here. Fixup so that we do the offset
calculations right, and so that the fields are physically moved to
the containers's chain.
Thu Jan 25 18:27:37 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (grokdeclarator): Avoid trying to get an operand off an
identifier node.
Wed Jan 24 11:25:30 1996 Jim Wilson <wilson@chestnut.cygnus.com>
* typeck.c (pointer_int_sum): Use TYPE_PRECISION (sizetype) not
POINTER_SIZE to agree with expr.c.
Thu Jan 25 13:01:23 1996 Mike Stump <mrs@cygnus.com>
* search.c (lookup_field): Don't report ambiguities if protect is 0,
instead return NULL_TREE.
Wed Jan 24 13:01:26 1996 Mike Stump <mrs@cygnus.com>
* class.c (finish_struct_1): Call warn_hidden if we want warnings
about overloaded virtual functions.
(warn_hidden): New routine to warn of virtual functions that are
hidden by other virtual functions, that are not overridden.
(get_basefndecls): New routine, used by warn_hidden.
(mark_overriders): New routine, used by warn_hidden.
* search.c (get_matching_virtual): Remove old warning that just
isn't very useful.
Tue Jan 23 12:26:10 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (output_builtin_tdesc_entries): #if 0 the function definition.
* typeck.c (null_ptr_cst_p): Delete unused fn.
(build_function_call_maybe): Delete unused fn.
* expr.c (extract_init): #if 0 the code after unconditional return 0
for now.
Delete old cadillac code.
* edsel.c: Remove file.
* Make-lang.in (CXX_SRCS): Take edsel.c off the list.
* Makefile.in (CXX_OBJS): Delete edsel.o.
(edsel.o): Delete rule.
* cp-tree.h (flag_cadillac): Delete var decl.
* lang-options.h: Delete "-fcadillac" and "-fno-cadillac".
* decl2.c (flag_cadillac): Delete var definition.
(lang_decode_option): Delete handling of -fcadillac and -fno-cadillac.
(grokfield): Delete code depending on flag_cadillac.
(finish_anon_union): Likewise.
* class.c (finish_struct_1): Likewise.
(pushclass): Likewise.
(popclass): Likewise.
(push_lang_context): Likewise.
(pop_lang_context): Likewise.
* decl.c (init_decl_processing): Likewise.
(start_decl): Likewise.
(cp_finish_decl): Likewise.
(xref_tag): Likewise.
(finish_enum): Likewise.
(start_function): Likewise.
(finish_function): Likewise.
(finish_stmt): Likewise.
* lex.c (lang_init): Likewise.
(check_newline): Likewise.
* lex.c (do_pending_inlines): Delete synthesized method kludge.
Delete defunct, ancient garbage collection implementation.
* rtti.c: New file with the RTTI stuff from gc.c.
* gc.c: Removed file (moved the remaining stuff into rtti.c).
* Makefile.in (CXX_OBJS): Replace gc.o with rtti.o.
(rtti.o): New rule, replacing gc.o.
* Make-lang.in (CXX_SRCS): Replace gc.c with rtti.c.
* cp-tree.h: Delete gc-related fn decls.
(DECL_GC_OFFSET): Delete macro.
(flag_gc): Delete extern decl.
* decl.c (current_function_obstack_index): Delete var decl.
(current_function_obstack_usage): Delete var decl.
(start_function): Delete clearing of current_function_obstack_index
and current_function_obstack_usage.
(init_decl_processing): Delete code relying on -fgc.
Delete call to init_gc_processing.
(cp_finish_decl): Delete calls to build_static_gc_entry and
type_needs_gc_entry. Delete gc code setting DECL_GC_OFFSET.
(store_parm_decls): Delete -fgc calls to cp_expand_decl_cleanup
and to expand_expr of a __gc_main call.
(maybe_gc_cleanup): Delete var decl.
(finish_function): Delete call to expand_gc_prologue_and_epilogue.
* decl2.c (flag_gc): Delete var decl.
(lang_f_options): Delete offering of -fgc.
(lang_decode_option): Delete -fgc and -fno-gc handling.
(get_temp_regvar): Delete gc code.
* init.c (build_new): Delete gc code.
* lex.c (init_lex): Delete checking of flag_gc.
* typeck.c (convert_arguments): Delete gc code.
(build_component_addr): Delete -fgc warning.
(build_modify_expr): Delete gc code.
* decl2.c (build_push_scope): Delete fn.
* cp-tree.h (build_push_scope): Delete decl.
* search.c (clear_search_slots): Delete fn.
* cp-tree.h (clear_search_slots): Delete decl.
* search.c (tree_needs_constructor_p): Delete fn.
* cp-tree.h (tree_needs_constructor_p): Delete decl.
* tree.c (id_cmp): Delete fn.
* tree.c (set_fnaddr_from_vtable_entry): Delete fn.
* cp-tree.h (set_fnaddr_from_vtable_entry): Delete decl.
* tree.c (decl_value_member): Delete fn.
* cp-tree.h (decl_value_member): Delete decl.
* tree.c (list_hash_lookup_or_cons): Delete fn.
* cp-tree.h (list_hash_lookup_or_cons): Delete decl.
* method.c (cplus_exception_name): Delete fn.
(EXCEPTION_NAME_{PREFIX, LENGTH}): Delete macros.
* spew.c (shift_tokens): Delete fn.
Mon Jan 22 17:49:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* except.c (init_exception_processing): Pass 1 to needs_pop in calls
to cp_finish_decl.
* parse.y: Likewise.
Mon Jan 22 17:34:29 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* tree.c (build_cplus_staticfn_type): Delete function definition;
never used.
* cp-tree.h (build_cplus_staticfn_type): Delete decl.
* tree.c (virtual_member): Delete function definition; never used.
* cp-tree.h (virtual_member): Delete decl.
Fri Jan 19 18:03:14 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_component_ref): Handle getting vbase pointers
out of complex multiple inheritance better.
Fri Jan 19 16:27:40 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (build_object_ref): Make sure we use the real type, not
any reference type.
Fri Jan 19 16:01:47 1996 Mike Stump <mrs@cygnus.com>
* tree.c (build_exception_variant): Don't create new types if we
don't have to, also build new types on the right obstack.
Fri Jan 19 14:09:44 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (store_bindings): Split out from push_to_top_level.
(push_to_top_level): Call it for b->type_shadowed on class binding
levels.
Fri Jan 19 13:53:14 1996 Mike Stump <mrs@cygnus.com>
* search.c (expand_upcast_fixups): Fix so that offsets stored in
vbase_offsets are always right. Fixes a problem where virtual base
upcasting and downcasting could be wrong during conversions on this
during virtual function dispatch at ctor/dtor time when dynamic
vtable fixups for deltas are needed. This only sounds easier than
it is. :-)
(fixup_virtual_upcast_offsets): Change to reflect new calling
convention for expand_upcast_fixups.
Fri Jan 19 12:23:08 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl2.c (grokbitfield): Strip the NOPs from WIDTH before we
check that it's usable as the bitfield width.
Wed Jan 17 21:22:40 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl2.c (grokfield): Call cplus_decl_attributes with the attrlist.
Pass a null tree to grokdeclarator for its ATTRLIST arg, since it's
only ever used for functions in it.
Wed Jan 17 12:10:38 1996 Jason Merrill <jason@yorick.cygnus.com>
* parse.y (qualified_type_name): Use the TYPE_DECL, not the type.
(nested_type): Likewise.
(nested_name_specifier): Use lastiddecl.
* decl.c (grokdeclarator): Adjust accordingly.
* init.c (expand_member_init): Likewise.
* parse.y (base_class): Likewise.
* typeck2.c (build_functional_cast): Likewise.
* typeck2.c (build_functional_cast): Fill in name after we've
checked for non-aggr type.
Wed Jan 17 10:18:01 1996 Mike Stump <mrs@cygnus.com>
* decl2.c (warn_pointer_arith): Default to on.
Tue Jan 16 12:45:38 1996 Jason Merrill <jason@yorick.cygnus.com>
* lex.c (is_rid): New function.
* decl.c (grokdeclarator): Diagnose reserved words used as
declarator-ids.
Tue Jan 16 11:39:40 1996 Jason Merrill <jason@yorick.cygnus.com>
* tree.c (get_decl_list): Don't lose cv-quals.
* decl.c (grokdeclarator): Fix SCOPE_REF handling and diagnose
typespecs used as declarator-ids.
Tue Jan 16 11:09:42 1996 Mike Stump <mrs@cygnus.com>
* decl.c (poplevel): When poping a level, don't give a warning for
any subblocks that already exist.
Tue Jan 16 00:25:33 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_object_ref): Finish what I started.
* parse.y (qualified_type_name): Don't check TYPE_BUILT_IN.
* decl2.c (constructor_name_full): Handle TEMPLATE_TYPE_PARMs.
* decl.c (grokdeclarator): Also accept TEMPLATE_TYPE_PARM as a
scope.
Mon Jan 15 16:19:32 1996 Jason Merrill <jason@yorick.cygnus.com>
* decl.c (xref_tag): Handle passing a type in directly.
* parse.y (qualified_type_name): Pull out the type.
(nested_type): Likewise.
Take types directly instead of as identifiers.
* call.c (build_scoped_method_call): Take types directly instead of
as identifiers.
* decl.c (xref_basetypes): Likewise.
* init.c (expand_member_init): Likewise.
(build_member_call): Likewise.
(build_offset_ref): Likewise.
* typeck2.c (build_scoped_ref): Likewise, remove bogus code.
* method.c (do_build_assign_ref): Likewise.
* decl.c (grokdeclarator): Handle a type appearing as the
declarator-id for constructors.
* method.c (do_build_copy_constructor): current_base_init_list now
uses the types directly, not their names.
* init.c (sort_base_init): Likewise.
(expand_member_init): Likewise.
* init.c (is_aggr_type): New function, like is_aggr_typedef.
Mon Jan 15 08:45:01 1996 Jeffrey A Law <law@cygnus.com>
* tree.c (layout_basetypes): Call build_lang_field_decl instead
of build_lang_decl if first arg is a FIELD_DECL.
Thu Jan 11 14:55:07 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (cp_finish_decl): Only clear TREE_USED if DECL_NAME is
non-empty.
* except.c (expand_start_catch_block): Set TREE_USED to avoid
warnings about the catch handler.
Mon Jan 8 17:35:12 1996 Jason Merrill <jason@yorick.cygnus.com>
* typeck.c (build_modify_expr): Use a COMPOUND_EXPR instead of
expand_target_expr.
Thu Jan 4 12:30:32 1996 Brendan Kehoe <brendan@lisa.cygnus.com>
Fix access control to use trees rather than integers.
* class.c (access_{default, public, protected, private,
default_virtual, public_virtual, private_virtual}_node): Add
definitions.
(init_class_processing): Do creation of those nodes.
* cp-tree.h (access_type): Delete enum decl.
(access_{default, public, protected, private, default_virtual,
public_virtual, private_virtual}_node): Add decls.
(compute_access): Change return type.
* search.c (compute_access): Have tree return type, instead of enum.
(lookup_field): Declare THIS_V and NEW_V to be tree nodes.
* lex.c (real_yylex): Use yylval.ttype for giving the value of the
access_* node for each of RID_{PUBLIC, PRIVATE, PROTECTED}.
* parse.y (VISSPEC): Make ttype rather than itype.
(base_class_access_list): Likewise.
* *.[cy]: Change all refs of `access_public' to `access_public_node',
etc.
* call.c (build_method_call): Make ACCESS be a tree.
* class.c (alter_access, finish_struct_1, filter_struct): Likewise.
* cvt.c (convert_to_aggr): Likewise.
* init.c (build_offset_ref, resolve_offset_ref, build_delete):
Likewise.
* method.c (hack_identifier): Likewise.
* typeck.c (build_component_ref_1, build_component_ref): ): Likewise.
Thu Jan 4 11:02:20 1996 Mike Stump <mrs@cygnus.com>
* typeck.c (pointer_int_sum, pointer_diff): Make code agree with C
frontend, and make it more consistent with respect to
warn_pointer_arith.
Tue Jan 2 00:13:38 1996 Rusty Russell <rusty@adelaide.maptek.com.au>
* decl.c (pushdecl): Check for duplicate parameter names.
Wed Jan 3 09:25:48 1996 Mike Stump <mrs@cygnus.com>
* decl.c (expand_static_init): Call assemble_external for atexit.
Wed Jan 3 07:55:19 1996 Mike Stump <mrs@cygnus.com>
* except.c (do_unwind): Remove some generated dead code.
(eh_outer_context): New routine, factor out some common code from
expand_builtin_throw and end_eh_unwinder. Add code to do return
address masking for the PA.
(expand_builtin_throw): Use eh_outer_context instead of open coding
it here.
(end_eh_unwinder): Likewise.
Tue Jan 2 17:00:56 1996 Mike Stump <mrs@cygnus.com>
* except.c (expand_throw): Call assemble_external for __empty, if we
use it.
Thu Dec 28 11:13:15 1995 Mike Stump <mrs@cygnus.com>
* except.c (expand_builtin_throw): Use RETURN_ADDR_OFFSET instead of
NORMAL_RETURN_ADDR_OFFSET.
(end_eh_unwinder): Likewise.
Wed Dec 27 22:18:16 1995 Mike Stump <mrs@cygnus.com>
* gc.c (build_dynamic_cast): Make sure we don't cast away const
when dealing with references, and make sure we handle dynamic
casting to a cv qualified reference.
Thu Dec 21 23:50:35 1995 Mike Stump <mrs@cygnus.com>
* except.c (struct eh_context): New structure top hold eh context
information.
(push_eh_context): New routine.
(pop_eh_context): Likewise.
* decl.c (push_cp_function_context): Use them.
(pop_cp_function_context): Likewise.
Wed Dec 20 12:42:51 1995 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Also prune uninteresting functions in the
inline emission loop.
Wed Dec 20 02:32:07 1995 Jeffrey A Law <law@cygnus.com>
* sig.c (build_signature_table_constructor): Mark functions
in the signature as referenced.
Tue Dec 19 22:36:56 1995 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (finish_file): Do all the vtable/synthesis stuff before
the inline emission stuff.
Mon Dec 18 15:51:33 1995 Jason Merrill <jason@yorick.cygnus.com>
* cp-tree.h, decl2.c (flag_weak): New flag to control the use of
weak symbols.
* lang-options.h: Add -f{no-,}weak.
* decl.c (init_decl_processing): If the target does not support weak
symbols, don't use them.
* decl2.c, pt.c: s/SUPPORTS_WEAK/flag_weak/.
Sun Dec 17 21:13:23 1995 Rusty Russell <rusty@adelaide.maptek.com.au>
* init.c (expand_member_init): warning for base init after members.
Fri Dec 15 15:32:18 1995 Jason Merrill <jason@yorick.cygnus.com>
* cvt.c (build_expr_type_conversion): Don't convert to a reference
type.
Thu Dec 14 16:05:58 1995 Mike Stump <mrs@cygnus.com>
* method.c (report_type_mismatch): Improve wording for volatile
mismatches.
Thu Dec 14 14:16:26 1995 Mike Stump <mrs@cygnus.com>
* init.c (expand_aggr_init_1): Use expand_aggr_init_1 instead of
expand_assignment, as the later doesn't handle things that have
copy constructors well. The compiler would do bitwise copying,
instead of ctor calling in some cases.
Wed Dec 13 17:05:54 1995 Paul Eggert <eggert@twinsun.com>
* g++.c (my_strerror): Return "cannot access" if errno is 0.
(pfatal_with_name, perror_exec): Don't assume that
the returned value from my_strerror contains no '%'s.
(concat): Remove.
(sys_nerror): Declare only if HAVE_STRERROR is not defined.
Wed Dec 13 16:22:38 1995 Jason Merrill <jason@yorick.cygnus.com>
Lose CLASSTYPE_METHODS/DECL_NEXT_METHOD chain; make
TYPE_METHODS/TREE_CHAIN mean what they used to.
* decl2.c (constructor_name_full): Refer to CLASSTYPE_METHOD_VEC
instead of TYPE_METHODS.
* decl.c (duplicate_decls): Lose references to DECL_NEXT_METHOD.
* tree.c (tree_copy_lang_decl_for_deferred_output): Likewise.
* cp-tree.h (CLASSTYPE_METHODS): Lose.
(CLASSTYPE_METHOD_VEC): Point to lang_spec->methods instead of
TYPE_METHODS.
(struct lang_decl): Lose next_method field.
(DECL_NEXT_METHOD): Lose.
* class.c (finish_struct_methods): Don't mess with TYPE_METHODS.
(finish_struct): Just use TYPE_METHODS; we don't need fn_fields
anymore.
(finish_struct_methods): Don't mess with the TREE_CHAINs in
fn_fields.
* search.c (add_conversions): Don't use TREE_CHAIN to traverse method
vector.
* call.c (build_method_call): Synthesize here even when not inlining.
* typeck.c (build_function_call_real): Likewise.
Wed Dec 13 15:02:39 1995 Ian Lance Taylor <ian@cygnus.com>
* cp/lex.c (check_newline): If DBX_DEBUGGING_INFO and write_symbols
== DBX_DEBUG, call dbxout_start_new_source_file and
dbxout_resume_previous_source_file when appropriate.
Tue Dec 12 20:38:55 1995 Mike Stump <mrs@cygnus.com>
* except.c (start_anon_func): Push to the top level.
(end_anon_func): Pop from the top level.
Mon Dec 11 18:56:14 1995 Mike Stump <mrs@cygnus.com>
* cp-tree.h (build_cleanup): New routine to build cleanups.
* decl.c (expand_static_init): Use build_cleanup to build a cleanup
call at ctor time and use atexit to run it later.
* decl2.c (build_cleanup): New routine, taken from finish_file.
(finish_file): Use build_cleanup instead, and don't put function
local statics in global dtor list.
Wed Dec 6 14:34:29 1995 Mike Stump <mrs@cygnus.com>
* except.c (expand_throw): Ensure that we have cleanups, if we try
and expand cleanups.
Wed Dec 6 11:48:21 1995 Mike Stump <mrs@cygnus.com>
* except.c (expand_throw): Add logic to manage dynamic cleanups for
the EH object.
(expand_end_catch_block): Use the magic of expand_goto, instead of
emit_jump so that we get the cleanup for any catch clause parameter
and the cleanup for the exception object. Update to reflect label
changes.
(push_eh_cleanup): New routine to register a cleanup for an
exception object.
(empty_fndecl): Used to default cleanup actions to
nothing.
(init_exception_processing): Setup empty_fndecl. Setup
saved_cleanup.
(expand_start_catch_block): Update to reflect label changes. Call
push_eh_object to register the cleanup for the EH object.
(start_anon_func): New routine to start building lambda expressions
from trees.
(end_anon_func): New routine to end them.
(struct labelNode): Change so that we can use tree labels, or rtx
labels.
(saved_cleanup): Object to check for dynamic cleanups for the
exception handling object.
(push_label_entry): Change so that we can use tree labels, or rtx
labels.
(pop_label_entry): Likewise.
(top_label_entry): Likewise.
(expand_start_all_catch): Use tree label instead of rtx label, so
that we can get the magic of expand_goto.
(expand_end_all_catch): Update to reflect label changes.
* class.c (build_vfn_ref): Remove building_cleanup logic, as we now
use UNSAVE_EXPRs.
* typeck.c (get_member_function_from_ptrfunc): Remove remnants of
building_cleanup logic, as we now use UNSAVE_EXPRs.
* cp-tree.h (unsave_expr): Declare it.
* decl.c (building_cleanup): Remove.
(maybe_build_cleanup): Remove building_cleanup logic, and use
UNSAVE_EXPR instead.
Sun Dec 3 01:34:58 1995 Mike Stump <mrs@cygnus.com>
* gc.c (build_t_desc): Update error message to say <typeinfo>.
Thu Nov 30 12:30:05 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (pushdecl): Only warn about shadowing a local variable if
warn_shadow is true.
Sun Nov 26 16:06:55 1995 Rusty Russell <rusty@adelaide.maptek.com.au>
* typeck.c (build_binary_op_nodefault): Added warning about
comparisons between different enum types with -Wall, unless
-fenum-int-equiv set.
Wed Nov 22 15:44:02 1995 Mike Stump <mrs@cygnus.com>
* class.c (finish_struct_1): Skip down to the inner type in
multidimensional arrays. Ensures ctors will be made for types that
need constructing.
Wed Nov 22 14:19:22 1995 Mike Stump <mrs@cygnus.com>
* decl.c (last_dtor_insn): New to track the last compiler generated
insn in a dtor.
(store_parm_decls): Set it.
(finish_function): Use it to see if the dtor is empty. Avoid doing
vtable setup all the time, if we can.
(struct cp_function): Add last_dtor_insn.
(push_cp_function_context): Save it.
(pop_cp_function_context): Restore it.
Wed Nov 22 11:52:19 1995 Paul Russell <Rusty.Russell@adelaide.maptek.com.au>
* typeck.c (build_unary_op): Set TREE_NO_UNUSED_WARNING to avoid
warnings.
Tue Nov 21 17:15:23 1995 Mike Stump <mrs@cygnus.com>
* typeck.c (expand_target_expr): Make sure targets get put into the
current temp_slot_level, so that the free_temp_slots call will reuse
them.
Tue Nov 21 13:32:03 1995 Mike Stump <mrs@cygnus.com>
* class.c (finish_struct_1): Delay delta fixups for virtual bases
until after we have done the hard virtuals, to avoid a bogus `every
virtual function must have a unique final overrider' for virtual
functions that are only overridden by hard virtuals.
Thu Nov 9 13:35:30 1995 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (do_function_instantiation): Don't try to find a file-scope
template for a member function.
Tue Nov 14 06:20:35 1995 Mike Stump <mrs@cygnus.com>
* g++.c (main): Add handling of -nodefaultlibs.
Mon Nov 13 15:45:34 1995 Mike Stump <mrs@cygnus.com>
* cp-tree.h (INDIRECT_BIND): Add a way for the frontend to
distinguish between direct bindings of reference variables, and
indirect bindings of reference variables.
* cvt.c (build_up_reference): Use it.
* typeck.c (convert_arguments): Use it to indicate this is an
indirect binding.
* decl.c (cp_finish_decl): Ensure that we reuse stack slots as fast
as they are unused.
(expand_static_init): Likewise.
(cplus_expand_expr_stmt): Likewise.
* decl2.c (finish_file): Likewise.
* init.c (perform_member_init): Likewise.
(emit_base_init): Likewise.
(expand_aggr_vbase_init_1): Likewise.
Fri Nov 10 09:18:09 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (push_namespace): Rewrite to use build_lang_decl, so we
get a DECL_LANG_SPECIFIC node.
* cp-tree.h (lang_decl_flags): Add new member `level'.
(NAMESPACE_LEVEL): Don't use decl.arguments, instead use the
decl_flags level member.
Mon Nov 6 18:36:13 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* call.c (build_method_call): Make sure instance has a
TYPE_LANG_SPECIFIC node before we dive into it.
Sat Nov 4 20:01:52 1995 Jason Molenda <crash@phydeaux.cygnus.com>
* method.c (make_thunk): Use TREE_SET_CODE to set thunk's tree code.
Thu Nov 2 17:56:57 1995 Mike Stump <mrs@cygnus.com>
* decl.c (duplicate_decls): When smashing decls, smash staticness in
the usual way.
Thu Nov 2 16:44:02 1995 Mike Stump <mrs@cygnus.com>
* decl.c (poplevel): Handle the merging of subblocks of cleanups
when finishing blocks that have already been created (usually due to
the fixup goto code). Fixes bad debugging information.
Wed Nov 1 12:33:53 1995 Jason Merrill <jason@yorick.cygnus.com>
* method.c (hack_identifier): Don't abort when we get a TREE_LIST
that's not a list of overloaded functions.
Wed Nov 1 11:38:58 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl2.c (mark_vtable_entries): Check DECL_LANG_SPECIFIC on fn
before trying to use DECL_ABSTRACT_VIRTUAL_P.
Tue Oct 31 11:56:55 1995 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (mark_used): New function for hooking into setting of
TREE_USED on decls.
* call.c (build_method_call): Use it.
* class.c (instantiate_type): Likewise.
* init.c (build_offset_ref): Likewise. Don't call assemble_external
for all like-named functions.
* method.c (hack_identifier): Likewise.
(emit_thunk): Don't call assemble_external.
(make_thunk): Create thunk as a FUNCTION_DECL so that it
gets the right mode and ENCODE_SECTION_INFO works.
* parse.y: Use mark_used. Pass operator names to do_identifier.
* lex.c (do_identifier): Handle operator names.
* decl2.c (grokclassfn): Tweak __in_chrg attributes.
Thu Oct 26 16:45:58 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* errfn.c: Include stdio.h.
(cp_sprintf): Take out decl of sprintf, and cast sprintf to errorfn*.
Wed Oct 25 18:58:41 1995 Mike Stump <mrs@cygnus.com>
* typeck2.c (digest_init): Always convert initializers to the
right type.
Wed Oct 25 13:25:24 1995 Mike Stump <mrs@cygnus.com>
* init.c (member_init_ok_or_else): Don't allow member initializers
for indirect members, as it is invalid.
Wed Oct 25 11:35:28 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* decl.c (grokdeclarator): Don't allow `friend signed ()'.
Fri Oct 20 10:30:59 1995 Mike Stump <mrs@cygnus.com>
* parse.y (for.init.statement): Catch compound statements inside for
initializations, if we're being pedantic.
Fri Oct 20 10:03:42 1995 Mike Stump <mrs@cygnus.com>
* decl.c (lookup_tag): Return NULL_TREE if we don't find what we are
looking for.
Thu Oct 19 14:26:10 1995 Mike Stump <mrs@cygnus.com>
* error.c (dump_expr): Don't core dump when a boolean expression is
used as a default argument.
Thu Oct 19 10:36:30 1995 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_bits): Check aggregate_value_p instead of
RETURN_IN_MEMORY.
Wed Oct 18 18:12:32 1995 Jason Merrill <jason@yorick.cygnus.com>
* class.c (finish_struct_bits): Also set TREE_ADDRESSABLE on a
BLKmode type that would otherwise be returned in registers.
Mon Oct 16 12:32:19 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* g++.c (WITHLIBC): New macro.
(main): Declare saw_libc. Use WITHLIBC if `-lc' was used; set
saw_libc and pass it at the end if it was set.
Wed Oct 11 16:30:34 1995 Brendan Kehoe <brendan@lisa.cygnus.com>
* parse.y (fn.def1): Call split_specs_attrs in
declmods notype_declarator case.
Index: head/contrib/gcc/cp/cp-tree.h
===================================================================
--- head/contrib/gcc/cp/cp-tree.h (revision 52750)
+++ head/contrib/gcc/cp/cp-tree.h (revision 52751)
@@ -1,3520 +1,3524 @@
/* Definitions for C++ parsing and type checking.
Copyright (C) 1987, 92-97, 1998, 1999 Free Software Foundation, Inc.
Hacked by Michael Tiemann (tiemann@cygnus.com)
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. */
#ifndef _CP_TREE_H
#define _CP_TREE_H
/* Usage of TREE_LANG_FLAG_?:
0: BINFO_MARKED (BINFO nodes).
COMPOUND_STMT_NO_SCOPE (in COMPOUND_STMT).
NEW_EXPR_USE_GLOBAL (in NEW_EXPR).
DELETE_EXPR_USE_GLOBAL (in DELETE_EXPR).
LOOKUP_EXPR_GLOBAL (in LOOKUP_EXPR).
TREE_NEGATED_INT (in INTEGER_CST).
TREE_INDIRECT_USING (in NAMESPACE_DECL).
IDENTIFIER_MARKED (used by search routines).
LOCAL_BINDING_P (in CPLUS_BINDING)
1: IDENTIFIER_VIRTUAL_P.
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
DELETE_EXPR_USE_VEC (in DELETE_EXPR).
(TREE_CALLS_NEW) (in _EXPR or _REF) (commented-out).
TYPE_USES_COMPLEX_INHERITANCE (in _TYPE).
C_DECLARED_LABEL_FLAG.
INHERITED_VALUE_BINDING_P (in CPLUS_BINDING)
BASELINK_P (in TREE_LIST)
2: IDENTIFIER_OPNAME_P.
BINFO_VBASE_MARKED.
BINFO_FIELDS_MARKED.
TYPE_VIRTUAL_P.
3: TYPE_USES_VIRTUAL_BASECLASSES (in a class TYPE).
BINFO_VTABLE_PATH_MARKED.
BINFO_PUSHDECLS_MARKED.
(TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
4: BINFO_NEW_VTABLE_MARKED.
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
or FIELD_DECL).
5: Not used.
6: Not used.
Usage of TYPE_LANG_FLAG_?:
0: C_TYPE_FIELDS_READONLY (in RECORD_TYPE or UNION_TYPE).
1: TYPE_HAS_CONSTRUCTOR.
2: TYPE_HAS_DESTRUCTOR.
3: TYPE_FOR_JAVA.
4: TYPE_NEEDS_DESTRUCTOR.
5: IS_AGGR_TYPE.
6: TYPE_BUILT_IN.
Usage of DECL_LANG_FLAG_?:
0: DECL_ERROR_REPORTED (in VAR_DECL).
DECL_TEMPLATE_PARM_P (in CONST_DECL, TYPE_DECL, or TEMPLATE_DECL)
1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
3: DECL_IN_AGGR_P.
4: DECL_MAYBE_TEMPLATE.
5: DECL_INTERFACE_KNOWN.
6: DECL_THIS_STATIC (in VAR_DECL or FUNCTION_DECL).
7: DECL_DEAD_FOR_LOCAL (in VAR_DECL).
Usage of language-independent fields in a language-dependent manner:
TYPE_ALIAS_SET
This field is used by TYPENAME_TYPEs, TEMPLATE_TYPE_PARMs, and so
forth as a substitute for the mark bits provided in `lang_type'.
At present, only the six low-order bits are used.
TYPE_BINFO
For an ENUMERAL_TYPE, this is ENUM_TEMPLATE_INFO.
For a TYPENAME_TYPE, this is TYPENAME_TYPE_FULLNAME.
For a TEMPLATE_TEMPLATE_PARM, this is
TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO.
DECL_SAVED_INSNS/DECL_FIELD_SIZE
For a static VAR_DECL, this is DECL_INIT_PRIORITY.
*/
/* Language-dependent contents of an identifier. */
struct lang_identifier
{
struct tree_identifier ignore;
tree namespace_bindings;
tree bindings;
tree class_value;
tree class_template_info;
struct lang_id2 *x;
};
struct lang_id2
{
tree label_value, implicit_decl;
tree error_locus;
};
typedef struct
{
tree t;
int new_type_flag;
} flagged_type_tree;
typedef struct
{
char common[sizeof (struct tree_common)];
struct rtx_def *rtl; /* Unused, but required to match up with what
the middle-end expects. */
HOST_WIDE_INT index;
HOST_WIDE_INT level;
HOST_WIDE_INT orig_level;
tree decl;
} template_parm_index;
typedef struct ptrmem_cst
{
char common[sizeof (struct tree_common)];
tree member;
}* ptrmem_cst_t;
/* Nonzero if this binding is for a local scope, as opposed to a class
or namespace scope. */
#define LOCAL_BINDING_P(NODE) TREE_LANG_FLAG_0(NODE)
/* Nonzero if BINDING_VALUE is from a base class of the class which is
currently being defined. */
#define INHERITED_VALUE_BINDING_P(NODE) TREE_LANG_FLAG_1(NODE)
/* For a binding between a name and an entity at a non-local scope,
defines the scope where the binding is declared. (Either a class
_TYPE node, or a NAMESPACE_DECL.) This macro should be used only
for namespace-level bindings; on the IDENTIFIER_BINDING list
BINDING_LEVEL is used instead. */
#define BINDING_SCOPE(NODE) (((struct tree_binding*)NODE)->scope.scope)
/* This is the declaration bound to the name. Possible values:
variable, overloaded function, namespace, template, enumerator. */
#define BINDING_VALUE(NODE) (((struct tree_binding*)NODE)->value)
/* If name is bound to a type, this is the type (struct, union, enum). */
#define BINDING_TYPE(NODE) TREE_TYPE(NODE)
#define IDENTIFIER_GLOBAL_VALUE(NODE) \
namespace_binding (NODE, global_namespace)
#define SET_IDENTIFIER_GLOBAL_VALUE(NODE, VAL) \
set_namespace_binding (NODE, global_namespace, VAL)
#define IDENTIFIER_NAMESPACE_VALUE(NODE) \
namespace_binding (NODE, current_namespace)
#define SET_IDENTIFIER_NAMESPACE_VALUE(NODE, VAL) \
set_namespace_binding (NODE, current_namespace, VAL)
struct tree_binding
{
char common[sizeof (struct tree_common)];
union {
tree scope;
struct binding_level *level;
} scope;
tree value;
};
/* The overloaded FUNCTION_DECL. */
#define OVL_FUNCTION(NODE) (((struct tree_overload*)NODE)->function)
#define OVL_CHAIN(NODE) TREE_CHAIN(NODE)
/* Polymorphic access to FUNCTION and CHAIN. */
#define OVL_CURRENT(NODE) \
((TREE_CODE(NODE)==OVERLOAD) ? OVL_FUNCTION(NODE) : NODE)
#define OVL_NEXT(NODE) \
((TREE_CODE(NODE)==OVERLOAD) ? TREE_CHAIN(NODE) : NULL_TREE)
/* If set, this was imported in a using declaration.
This is not to confuse with being used somewhere, which
is not important for this node. */
#define OVL_USED(NODE) TREE_USED(NODE)
struct tree_overload
{
char common[sizeof (struct tree_common)];
tree function;
};
/* A `baselink' is a TREE_LIST whose TREE_PURPOSE is a BINFO
indicating a particular base class, and whose TREE_VALUE is a
(possibly overloaded) function from that base class. */
#define BASELINK_P(NODE) \
(TREE_CODE ((NODE)) == TREE_LIST && TREE_LANG_FLAG_1 ((NODE)))
#define SET_BASELINK_P(NODE) \
(TREE_LANG_FLAG_1 ((NODE)) = 1)
#define WRAPPER_PTR(NODE) (((struct tree_wrapper*)NODE)->u.ptr)
#define WRAPPER_INT(NODE) (((struct tree_wrapper*)NODE)->u.i)
struct tree_wrapper
{
char common[sizeof (struct tree_common)];
union {
void *ptr;
int i;
} u;
};
#define SRCLOC_FILE(NODE) (((struct tree_srcloc*)NODE)->filename)
#define SRCLOC_LINE(NODE) (((struct tree_srcloc*)NODE)->linenum)
struct tree_srcloc
{
char common[sizeof (struct tree_common)];
char *filename;
int linenum;
};
/* To identify to the debug emitters if it should pay attention to the
flag `-Wtemplate-debugging'. */
#define HAVE_TEMPLATES 1
/* Macros for access to language-specific slots in an identifier. */
#define IDENTIFIER_NAMESPACE_BINDINGS(NODE) \
(((struct lang_identifier *)(NODE))->namespace_bindings)
#define IDENTIFIER_TEMPLATE(NODE) \
(((struct lang_identifier *)(NODE))->class_template_info)
/* The IDENTIFIER_BINDING is the innermost CPLUS_BINDING for the
identifier. It's TREE_CHAIN is the next outermost binding. Each
BINDING_VALUE is a DECL for the associated declaration. Thus,
name lookup consists simply of pulling off the node at the front
of the list (modulo oddities for looking up the names of types,
and such.) You can use BINDING_SCOPE or BINDING_LEVEL to
determine the scope that bound the name. */
#define IDENTIFIER_BINDING(NODE) \
(((struct lang_identifier*) (NODE))->bindings)
/* The IDENTIFIER_VALUE is the value of the IDENTIFIER_BINDING, or
NULL_TREE if there is no binding. */
#define IDENTIFIER_VALUE(NODE) \
(IDENTIFIER_BINDING (NODE) \
? BINDING_VALUE (IDENTIFIER_BINDING (NODE)) \
: NULL_TREE)
/* If IDENTIFIER_CLASS_VALUE is set, then NODE is bound in the current
class, and IDENTIFIER_CLASS_VALUE is the value binding. This is
just a pointer to the BINDING_VALUE of one of the bindings in the
IDENTIFIER_BINDINGs list, so any time that this is non-NULL so is
IDENTIFIER_BINDING. */
#define IDENTIFIER_CLASS_VALUE(NODE) \
(((struct lang_identifier *) (NODE))->class_value)
/* The amount of time used by the file whose special "time identifier"
is NODE, represented as an INTEGER_CST. See get_time_identifier. */
#define TIME_IDENTIFIER_TIME(NODE) IDENTIFIER_BINDING(NODE)
/* For a "time identifier" this is a INTEGER_CST. The
TREE_INT_CST_LOW is 1 if the corresponding file is "interface only".
The TRE_INT_CST_HIGH is 1 if it is "interface unknown". */
#define TIME_IDENTIFIER_FILEINFO(NODE) IDENTIFIER_CLASS_VALUE (NODE)
/* TREE_TYPE only indicates on local and class scope the current
type. For namespace scope, the presence of a type in any namespace
is indicated with global_type_node, and the real type behind must
be found through lookup. */
#define IDENTIFIER_TYPE_VALUE(NODE) (identifier_type_value(NODE))
#define REAL_IDENTIFIER_TYPE_VALUE(NODE) (TREE_TYPE (NODE))
#define SET_IDENTIFIER_TYPE_VALUE(NODE,TYPE) (TREE_TYPE (NODE) = TYPE)
#define IDENTIFIER_HAS_TYPE_VALUE(NODE) (IDENTIFIER_TYPE_VALUE (NODE) ? 1 : 0)
#define LANG_ID_FIELD(NAME,NODE) \
(((struct lang_identifier *)(NODE))->x \
? ((struct lang_identifier *)(NODE))->x->NAME : 0)
#define SET_LANG_ID(NODE,VALUE,NAME) \
(((struct lang_identifier *)(NODE))->x == 0 \
? ((struct lang_identifier *)(NODE))->x \
= (struct lang_id2 *)perm_calloc (1, sizeof (struct lang_id2)) : 0, \
((struct lang_identifier *)(NODE))->x->NAME = (VALUE))
#define IDENTIFIER_LABEL_VALUE(NODE) LANG_ID_FIELD(label_value, NODE)
#define SET_IDENTIFIER_LABEL_VALUE(NODE,VALUE) \
SET_LANG_ID(NODE, VALUE, label_value)
#define IDENTIFIER_IMPLICIT_DECL(NODE) LANG_ID_FIELD(implicit_decl, NODE)
#define SET_IDENTIFIER_IMPLICIT_DECL(NODE,VALUE) \
SET_LANG_ID(NODE, VALUE, implicit_decl)
#define IDENTIFIER_ERROR_LOCUS(NODE) LANG_ID_FIELD(error_locus, NODE)
#define SET_IDENTIFIER_ERROR_LOCUS(NODE,VALUE) \
SET_LANG_ID(NODE, VALUE, error_locus)
#define IDENTIFIER_VIRTUAL_P(NODE) TREE_LANG_FLAG_1(NODE)
/* Nonzero if this identifier is the prefix for a mangled C++ operator name. */
#define IDENTIFIER_OPNAME_P(NODE) TREE_LANG_FLAG_2(NODE)
/* Nonzero if this identifier is the name of a type-conversion
operator. */
#define IDENTIFIER_TYPENAME_P(NODE) \
(! strncmp (IDENTIFIER_POINTER (NODE), \
OPERATOR_TYPENAME_FORMAT, \
strlen (OPERATOR_TYPENAME_FORMAT)))
/* Nonzero means reject anything that ANSI standard C forbids. */
extern int pedantic;
/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is read-only. */
#define C_TYPE_FIELDS_READONLY(type) TYPE_LANG_FLAG_0 (type)
/* Record in each node resulting from a binary operator
what operator was specified for it. */
#define C_EXP_ORIGINAL_CODE(exp) ((enum tree_code) TREE_COMPLEXITY (exp))
/* Store a value in that field. */
#define C_SET_EXP_ORIGINAL_CODE(exp, code) \
(TREE_COMPLEXITY (exp) = (int)(code))
/* If non-zero, a VAR_DECL whose cleanup will cause a throw to the
next exception handler. */
extern tree exception_throw_decl;
extern tree double_type_node, long_double_type_node, float_type_node;
extern tree char_type_node, unsigned_char_type_node, signed_char_type_node;
extern tree ptrdiff_type_node;
extern tree short_integer_type_node, short_unsigned_type_node;
extern tree long_integer_type_node, long_unsigned_type_node;
extern tree long_long_integer_type_node, long_long_unsigned_type_node;
extern tree unsigned_type_node;
extern tree string_type_node, char_array_type_node, int_array_type_node;
extern tree wchar_array_type_node;
extern tree wchar_type_node, signed_wchar_type_node, unsigned_wchar_type_node;
extern tree complex_integer_type_node;
extern tree complex_float_type_node;
extern tree complex_double_type_node;
extern tree complex_long_double_type_node;
extern tree intQI_type_node, unsigned_intQI_type_node;
extern tree intHI_type_node, unsigned_intHI_type_node;
extern tree intSI_type_node, unsigned_intSI_type_node;
extern tree intDI_type_node, unsigned_intDI_type_node;
#if HOST_BITS_PER_WIDE_INT >= 64
extern tree intTI_type_node, unsigned_intTI_type_node;
#endif
extern tree java_byte_type_node;
extern tree java_short_type_node;
extern tree java_int_type_node;
extern tree java_long_type_node;
extern tree java_float_type_node;
extern tree java_double_type_node;
extern tree java_char_type_node;
extern tree java_boolean_type_node;
extern int current_function_returns_value;
extern int current_function_returns_null;
extern tree current_function_return_value;
extern tree current_namespace;
extern tree global_namespace;
extern tree ridpointers[];
extern tree ansi_opname[];
extern tree ansi_assopname[];
/* Nonzero means `$' can be in an identifier. */
extern int dollars_in_ident;
/* Nonzero means allow type mismatches in conditional expressions;
just make their values `void'. */
extern int flag_cond_mismatch;
/* Nonzero means don't recognize the keyword `asm'. */
extern int flag_no_asm;
/* For cross referencing. */
extern int flag_gnu_xref;
/* For environments where you can use GNU binutils (as, ld in particular). */
extern int flag_gnu_binutils;
/* Nonzero means warn about implicit declarations. */
extern int warn_implicit;
/* Nonzero means warn about usage of long long when `-pedantic'. */
extern int warn_long_long;
/* Nonzero means warn when all ctors or dtors are private, and the class
has no friends. */
extern int warn_ctor_dtor_privacy;
/* Nonzero means warn about function definitions that default the return type
or that use a null return and have a return-type other than void. */
extern int warn_return_type;
/* Nonzero means give string constants the type `const char *', as mandated
by the standard. */
extern int flag_const_strings;
/* Nonzero means warn about deprecated conversion from string constant to
`char *'. */
extern int warn_write_strings;
/* Nonzero means warn about sizeof(function) or addition/subtraction
of function pointers. */
extern int warn_pointer_arith;
/* Nonzero means warn about suggesting putting in ()'s. */
extern int warn_parentheses;
/* Nonzero means warn about multiple (redundant) decls for the same single
variable or function. */
extern int warn_redundant_decls;
/* Warn if initializer is not completely bracketed. */
extern int warn_missing_braces;
/* Warn about comparison of signed and unsigned values. */
extern int warn_sign_compare;
/* Warn about a subscript that has type char. */
extern int warn_char_subscripts;
/* Nonzero means warn about pointer casts that can drop a type qualifier
from the pointer target type. */
extern int warn_cast_qual;
/* Warn about *printf or *scanf format/argument anomalies. */
extern int warn_format;
/* Nonzero means warn about non virtual destructors in classes that have
virtual functions. */
extern int warn_nonvdtor;
/* Non-zero means warn when we convert a pointer to member function
into a pointer to (void or function). */
extern int warn_pmf2ptr;
/* Nonzero means warn about violation of some Effective C++ style rules. */
extern int warn_ecpp;
/* Nonzero means warn where overload resolution chooses a promotion from
unsigned to signed over a conversion to an unsigned of the same size. */
extern int warn_sign_promo;
/* Non-zero means warn when a function is declared extern and later inline. */
extern int warn_extern_inline;
/* Non-zero means warn when an old-style cast is used. */
extern int warn_old_style_cast;
/* Nonzero means to treat bitfields as unsigned unless they say `signed'. */
extern int flag_signed_bitfields;
/* 3 means write out only virtuals function tables `defined'
in this implementation file.
2 means write out only specific virtual function tables
and give them (C) public access.
1 means write out virtual function tables and give them
(C) public access.
0 means write out virtual function tables and give them
(C) static access (default).
-1 means declare virtual function tables extern. */
extern int write_virtuals;
/* True for more efficient but incompatible (not fully tested)
vtable implementation (using thunks).
0 is old behavior; 1 is new behavior. */
extern int flag_vtable_thunks;
/* INTERFACE_ONLY nonzero means that we are in an "interface"
section of the compiler. INTERFACE_UNKNOWN nonzero means
we cannot trust the value of INTERFACE_ONLY. If INTERFACE_UNKNOWN
is zero and INTERFACE_ONLY is zero, it means that we are responsible
for exporting definitions that others might need. */
extern int interface_only, interface_unknown;
/* Nonzero means we should attempt to elide constructors when possible. */
extern int flag_elide_constructors;
/* Nonzero means enable obscure ANSI features and disable GNU extensions
that might cause ANSI-compliant code to be miscompiled. */
extern int flag_ansi;
/* Nonzero means recognize and handle signature language constructs. */
extern int flag_handle_signatures;
/* Nonzero means that member functions defined in class scope are
inline by default. */
extern int flag_default_inline;
/* The name-mangling scheme to use. Versions of gcc before 2.8 use
version 0. */
extern int name_mangling_version;
/* Nonzero means that guiding declarations are allowed. */
extern int flag_guiding_decls;
/* Nonzero if squashed mangling is to be performed.
This uses the B and K codes to reference previously seen class types
and class qualifiers. */
extern int flag_do_squangling;
+/* Nonzero means generate separate instantiation control files and juggle
+ them at link time. */
+extern int flag_use_repository;
+
/* Nonzero if we want to issue diagnostics that the standard says are not
required. */
extern int flag_optional_diags;
/* Nonzero means do not consider empty argument prototype to mean function
takes no arguments. */
extern int flag_strict_prototype;
/* Nonzero means output .vtable_{entry,inherit} for use in doing vtable gc. */
extern int flag_vtable_gc;
/* Nonzero means make the default pedwarns warnings instead of errors.
The value of this flag is ignored if -pedantic is specified. */
extern int flag_permissive;
/* Nonzero if we want to obey access control semantics. */
extern int flag_access_control;
/* C++ language-specific tree codes. */
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) SYM,
enum cplus_tree_code {
__DUMMY = LAST_AND_UNUSED_TREE_CODE,
#include "cp-tree.def"
LAST_CPLUS_TREE_CODE
};
#undef DEFTREECODE
enum languages { lang_c, lang_cplusplus, lang_java };
/* Macros to make error reporting functions' lives easier. */
#define TYPE_IDENTIFIER(NODE) (DECL_NAME (TYPE_NAME (NODE)))
#define TYPE_NAME_STRING(NODE) (IDENTIFIER_POINTER (TYPE_IDENTIFIER (NODE)))
#define TYPE_NAME_LENGTH(NODE) (IDENTIFIER_LENGTH (TYPE_IDENTIFIER (NODE)))
#define TYPE_ASSEMBLER_NAME_STRING(NODE) (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (NODE))))
#define TYPE_ASSEMBLER_NAME_LENGTH(NODE) (IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (TYPE_NAME (NODE))))
/* The _DECL for this _TYPE. */
#define TYPE_MAIN_DECL(NODE) (TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE)))
/* Nonzero if T is a class (or struct or union) type. Also nonzero
for template type parameters and typename types. Despite its name,
this macro has nothing to do with the definition of aggregate given
in the standard. Think of this macro as MAYBE_CLASS_TYPE_P. */
#define IS_AGGR_TYPE(t) \
(TREE_CODE (t) == TEMPLATE_TYPE_PARM \
|| TREE_CODE (t) == TYPENAME_TYPE \
|| TREE_CODE (t) == TYPEOF_TYPE \
|| TYPE_LANG_FLAG_5 (t))
/* Set IS_AGGR_TYPE for T to VAL. T must be a class, struct, or
union type. */
#define SET_IS_AGGR_TYPE(T, VAL) \
(TYPE_LANG_FLAG_5 (T) = (VAL))
/* Nonzero if T is a class type. Zero for template type parameters,
typename types, and so forth. */
#define CLASS_TYPE_P(t) \
(IS_AGGR_TYPE_CODE (TREE_CODE (t)) && IS_AGGR_TYPE (t))
#define IS_AGGR_TYPE_CODE(t) (t == RECORD_TYPE || t == UNION_TYPE)
#define IS_AGGR_TYPE_2(TYPE1,TYPE2) \
(TREE_CODE (TYPE1) == TREE_CODE (TYPE2) \
&& IS_AGGR_TYPE (TYPE1)&IS_AGGR_TYPE (TYPE2))
#define IS_OVERLOAD_TYPE(t) \
(IS_AGGR_TYPE (t) || TREE_CODE (t) == ENUMERAL_TYPE)
/* In a *_TYPE, nonzero means a built-in type. */
#define TYPE_BUILT_IN(NODE) TYPE_LANG_FLAG_6(NODE)
/* True if this a "Java" type, defined in 'extern "Java"'. */
#define TYPE_FOR_JAVA(NODE) TYPE_LANG_FLAG_3(NODE)
/* The type qualifiers for this type, including the qualifiers on the
elements for an array type. */
#define CP_TYPE_QUALS(NODE) \
((TREE_CODE (NODE) != ARRAY_TYPE) \
? TYPE_QUALS (NODE) : cp_type_quals (NODE))
/* Nonzero if this type is const-qualified. */
#define CP_TYPE_CONST_P(NODE) \
((CP_TYPE_QUALS (NODE) & TYPE_QUAL_CONST) != 0)
/* Nonzero if this type is volatile-qualified. */
#define CP_TYPE_VOLATILE_P(NODE) \
((CP_TYPE_QUALS (NODE) & TYPE_QUAL_VOLATILE) != 0)
/* Nonzero if this type is restrict-qualified. */
#define CP_TYPE_RESTRICT_P(NODE) \
((CP_TYPE_QUALS (NODE) & TYPE_QUAL_RESTRICT) != 0)
/* Nonzero if this type is const-qualified, but not
volatile-qualified. Other qualifiers are ignored. This macro is
used to test whether or not it is OK to bind an rvalue to a
reference. */
#define CP_TYPE_CONST_NON_VOLATILE_P(NODE) \
((CP_TYPE_QUALS (NODE) & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) \
== TYPE_QUAL_CONST)
#define DELTA_FROM_VTABLE_ENTRY(ENTRY) \
(!flag_vtable_thunks ? \
TREE_VALUE (CONSTRUCTOR_ELTS (ENTRY)) \
: TREE_CODE (TREE_OPERAND ((ENTRY), 0)) != THUNK_DECL ? integer_zero_node \
: build_int_2 (THUNK_DELTA (TREE_OPERAND ((ENTRY), 0)), 0))
/* Virtual function addresses can be gotten from a virtual function
table entry using this macro. */
#define FNADDR_FROM_VTABLE_ENTRY(ENTRY) \
(!flag_vtable_thunks ? \
TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (ENTRY)))) \
: TREE_CODE (TREE_OPERAND ((ENTRY), 0)) != THUNK_DECL ? (ENTRY) \
: DECL_INITIAL (TREE_OPERAND ((ENTRY), 0)))
#define SET_FNADDR_FROM_VTABLE_ENTRY(ENTRY,VALUE) \
(TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (ENTRY)))) = (VALUE))
#define FUNCTION_ARG_CHAIN(NODE) (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (NODE))))
#define PROMOTES_TO_AGGR_TYPE(NODE,CODE) \
(((CODE) == TREE_CODE (NODE) \
&& IS_AGGR_TYPE (TREE_TYPE (NODE))) \
|| IS_AGGR_TYPE (NODE))
/* Nonzero iff TYPE is uniquely derived from PARENT. Under MI, PARENT can
be an ambiguous base class of TYPE, and this macro will be false. */
#define UNIQUELY_DERIVED_FROM_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, 0, (tree *)0) >= 0)
#define ACCESSIBLY_DERIVED_FROM_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, -1, (tree *)0) >= 0)
#define ACCESSIBLY_UNIQUELY_DERIVED_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, 1, (tree *)0) >= 0)
#define DERIVED_FROM_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, 0, (tree *)0) != -1)
/* This structure provides additional information above and beyond
what is provide in the ordinary tree_type. In the past, we used it
for the types of class types, template parameters types, typename
types, and so forth. However, there can be many (tens to hundreds
of thousands) of template parameter types in a compilation, and
there's no need for this additional information in that case.
Therefore, we now use this data structure only for class types.
In the past, it was thought that there would be relatively few
class types. However, in the presence of heavy use of templates,
many (i.e., thousands) of classes can easily be generated.
Therefore, we should endeavor to keep the size of this structure to
a minimum. */
struct lang_type
{
struct
{
unsigned has_type_conversion : 1;
unsigned has_init_ref : 1;
unsigned has_default_ctor : 1;
unsigned uses_multiple_inheritance : 1;
unsigned const_needs_init : 1;
unsigned ref_needs_init : 1;
unsigned has_const_assign_ref : 1;
unsigned anon_union : 1;
unsigned has_nonpublic_ctor : 2;
unsigned has_nonpublic_assign_ref : 2;
unsigned vtable_needs_writing : 1;
unsigned has_assign_ref : 1;
unsigned gets_new : 2;
unsigned gets_delete : 2;
unsigned has_call_overloaded : 1;
unsigned has_array_ref_overloaded : 1;
unsigned has_arrow_overloaded : 1;
unsigned interface_only : 1;
unsigned interface_unknown : 1;
unsigned needs_virtual_reinit : 1;
unsigned marks: 6;
unsigned vec_delete_takes_size : 1;
unsigned declared_class : 1;
unsigned being_defined : 1;
unsigned redefined : 1;
unsigned debug_requested : 1;
unsigned use_template : 2;
unsigned got_semicolon : 1;
unsigned ptrmemfunc_flag : 1;
unsigned is_signature : 1;
unsigned is_signature_pointer : 1;
unsigned is_signature_reference : 1;
unsigned has_opaque_typedecls : 1;
unsigned sigtable_has_been_generated : 1;
unsigned was_anonymous : 1;
unsigned has_real_assign_ref : 1;
unsigned has_const_init_ref : 1;
unsigned has_complex_init_ref : 1;
unsigned has_complex_assign_ref : 1;
unsigned has_abstract_assign_ref : 1;
unsigned non_aggregate : 1;
unsigned is_partial_instantiation : 1;
unsigned has_mutable : 1;
unsigned com_interface : 1;
/* When adding a flag here, consider whether or not it ought to
apply to a template instance if it applies to the template.
If so, make sure to copy it in instantiate_class_template! */
/* The MIPS compiler gets it wrong if this struct also
does not fill out to a multiple of 4 bytes. Add a
member `dummy' with new bits if you go over the edge. */
unsigned dummy : 10;
} type_flags;
int vsize;
int vfield_parent;
union tree_node *vfields;
union tree_node *vbases;
union tree_node *tags;
union tree_node *search_slot;
unsigned char align;
/* Room for another three unsigned chars. */
union tree_node *size;
union tree_node *abstract_virtuals;
union tree_node *friend_classes;
union tree_node *rtti;
union tree_node *methods;
union tree_node *signature;
union tree_node *signature_pointer_to;
union tree_node *signature_reference_to;
union tree_node *template_info;
tree befriending_classes;
};
/* Indicates whether or not (and how) a template was expanded for this class.
0=no information yet/non-template class
1=implicit template instantiation
2=explicit template specialization
3=explicit template instantiation */
#define CLASSTYPE_USE_TEMPLATE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.use_template)
/* Fields used for storing information before the class is defined.
After the class is defined, these fields hold other information. */
/* List of friends which were defined inline in this class definition. */
#define CLASSTYPE_INLINE_FRIENDS(NODE) (TYPE_NONCOPIED_PARTS (NODE))
/* Nonzero for _CLASSTYPE means that operator new and delete are defined,
respectively. */
#define TYPE_GETS_NEW(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.gets_new)
#define TYPE_GETS_DELETE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.gets_delete)
#define TYPE_GETS_REG_DELETE(NODE) (TYPE_GETS_DELETE (NODE) & 1)
/* Nonzero for _CLASSTYPE means that operator vec delete is defined and
takes the optional size_t argument. */
#define TYPE_VEC_DELETE_TAKES_SIZE(NODE) \
(TYPE_LANG_SPECIFIC(NODE)->type_flags.vec_delete_takes_size)
#define TYPE_VEC_NEW_USES_COOKIE(NODE) \
(TYPE_NEEDS_DESTRUCTOR (NODE) \
|| (TYPE_LANG_SPECIFIC (NODE) && TYPE_VEC_DELETE_TAKES_SIZE (NODE)))
/* Nonzero means that this _CLASSTYPE node defines ways of converting
itself to other types. */
#define TYPE_HAS_CONVERSION(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_type_conversion)
/* Nonzero means that this _CLASSTYPE node overloads operator=(X&). */
#define TYPE_HAS_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_assign_ref)
#define TYPE_HAS_CONST_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_const_assign_ref)
/* Nonzero means that this _CLASSTYPE node has an X(X&) constructor. */
#define TYPE_HAS_INIT_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_init_ref)
#define TYPE_HAS_CONST_INIT_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_const_init_ref)
/* Nonzero means that this type is being defined. I.e., the left brace
starting the definition of this type has been seen. */
#define TYPE_BEING_DEFINED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.being_defined)
/* Nonzero means that this type has been redefined. In this case, if
convenient, don't reprocess any methods that appear in its redefinition. */
#define TYPE_REDEFINED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.redefined)
/* Nonzero means that this type is a signature. */
# define IS_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)?TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature:0)
# define SET_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature=1)
# define CLEAR_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature=0)
/* Nonzero means that this type is a signature pointer type. */
# define IS_SIGNATURE_POINTER(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature_pointer)
/* Nonzero means that this type is a signature reference type. */
# define IS_SIGNATURE_REFERENCE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature_reference)
/* Nonzero means that this signature contains opaque type declarations. */
#define SIGNATURE_HAS_OPAQUE_TYPEDECLS(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_opaque_typedecls)
/* Nonzero means that a signature table has been generated
for this signature. */
#define SIGTABLE_HAS_BEEN_GENERATED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.sigtable_has_been_generated)
/* If NODE is a class, this is the signature type that contains NODE's
signature after it has been computed using sigof(). */
#define CLASSTYPE_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature)
/* If NODE is a signature pointer or signature reference, this is the
signature type the pointer/reference points to. */
#define SIGNATURE_TYPE(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature)
/* If NODE is a signature, this is a vector of all methods defined
in the signature or in its base types together with their default
implementations. */
#define SIGNATURE_METHOD_VEC(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature)
/* If NODE is a signature, this is the _TYPE node that contains NODE's
signature pointer type. */
#define SIGNATURE_POINTER_TO(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature_pointer_to)
/* If NODE is a signature, this is the _TYPE node that contains NODE's
signature reference type. */
#define SIGNATURE_REFERENCE_TO(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature_reference_to)
/* The is the basetype that contains NODE's rtti. */
#define CLASSTYPE_RTTI(NODE) (TYPE_LANG_SPECIFIC(NODE)->rtti)
/* Nonzero means that this _CLASSTYPE node overloads operator(). */
#define TYPE_OVERLOADS_CALL_EXPR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_call_overloaded)
/* Nonzero means that this _CLASSTYPE node overloads operator[]. */
#define TYPE_OVERLOADS_ARRAY_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_array_ref_overloaded)
/* Nonzero means that this _CLASSTYPE node overloads operator->. */
#define TYPE_OVERLOADS_ARROW(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_arrow_overloaded)
/* Nonzero means that this _CLASSTYPE (or one of its ancestors) uses
multiple inheritance. If this is 0 for the root of a type
hierarchy, then we can use more efficient search techniques. */
#define TYPE_USES_MULTIPLE_INHERITANCE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.uses_multiple_inheritance)
/* Nonzero means that this _CLASSTYPE (or one of its ancestors) uses
virtual base classes. If this is 0 for the root of a type
hierarchy, then we can use more efficient search techniques. */
#define TYPE_USES_VIRTUAL_BASECLASSES(NODE) (TREE_LANG_FLAG_3(NODE))
/* Vector member functions defined in this class. Each element is
either a FUNCTION_DECL, a TEMPLATE_DECL, or an OVERLOAD. All
functions with the same name end up in the same slot. The first
two elements are for constructors, and destructors, respectively.
These are followed by ordinary member functions. There may be
empty entries at the end of the vector. */
#define CLASSTYPE_METHOD_VEC(NODE) (TYPE_LANG_SPECIFIC(NODE)->methods)
/* The first type conversion operator in the class (the others can be
searched with TREE_CHAIN), or the first non-constructor function if
there are no type conversion operators. */
#define CLASSTYPE_FIRST_CONVERSION(NODE) \
TREE_VEC_LENGTH (CLASSTYPE_METHOD_VEC (NODE)) > 2 \
? TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (NODE), 2) \
: NULL_TREE;
/* Mark bits for depth-first and breath-first searches. */
/* Get the value of the Nth mark bit. */
#define CLASSTYPE_MARKED_N(NODE, N) \
(((CLASS_TYPE_P (NODE) ? TYPE_LANG_SPECIFIC (NODE)->type_flags.marks \
: TYPE_ALIAS_SET (NODE)) & (1 << N)) != 0)
/* Set the Nth mark bit. */
#define SET_CLASSTYPE_MARKED_N(NODE, N) \
(CLASS_TYPE_P (NODE) \
? (TYPE_LANG_SPECIFIC (NODE)->type_flags.marks |= (1 << (N))) \
: (TYPE_ALIAS_SET (NODE) |= (1 << (N))))
/* Clear the Nth mark bit. */
#define CLEAR_CLASSTYPE_MARKED_N(NODE, N) \
(CLASS_TYPE_P (NODE) \
? (TYPE_LANG_SPECIFIC (NODE)->type_flags.marks &= ~(1 << (N))) \
: (TYPE_ALIAS_SET (NODE) &= ~(1 << (N))))
/* Get the value of the mark bits. */
#define CLASSTYPE_MARKED(NODE) CLASSTYPE_MARKED_N(NODE, 0)
#define CLASSTYPE_MARKED2(NODE) CLASSTYPE_MARKED_N(NODE, 1)
#define CLASSTYPE_MARKED3(NODE) CLASSTYPE_MARKED_N(NODE, 2)
#define CLASSTYPE_MARKED4(NODE) CLASSTYPE_MARKED_N(NODE, 3)
#define CLASSTYPE_MARKED5(NODE) CLASSTYPE_MARKED_N(NODE, 4)
#define CLASSTYPE_MARKED6(NODE) CLASSTYPE_MARKED_N(NODE, 5)
/* Macros to modify the above flags */
#define SET_CLASSTYPE_MARKED(NODE) SET_CLASSTYPE_MARKED_N(NODE, 0)
#define CLEAR_CLASSTYPE_MARKED(NODE) CLEAR_CLASSTYPE_MARKED_N(NODE, 0)
#define SET_CLASSTYPE_MARKED2(NODE) SET_CLASSTYPE_MARKED_N(NODE, 1)
#define CLEAR_CLASSTYPE_MARKED2(NODE) CLEAR_CLASSTYPE_MARKED_N(NODE, 1)
#define SET_CLASSTYPE_MARKED3(NODE) SET_CLASSTYPE_MARKED_N(NODE, 2)
#define CLEAR_CLASSTYPE_MARKED3(NODE) CLEAR_CLASSTYPE_MARKED_N(NODE, 2)
#define SET_CLASSTYPE_MARKED4(NODE) SET_CLASSTYPE_MARKED_N(NODE, 3)
#define CLEAR_CLASSTYPE_MARKED4(NODE) CLEAR_CLASSTYPE_MARKED_N(NODE, 3)
#define SET_CLASSTYPE_MARKED5(NODE) SET_CLASSTYPE_MARKED_N(NODE, 4)
#define CLEAR_CLASSTYPE_MARKED5(NODE) CLEAR_CLASSTYPE_MARKED_N(NODE, 4)
#define SET_CLASSTYPE_MARKED6(NODE) SET_CLASSTYPE_MARKED_N(NODE, 5)
#define CLEAR_CLASSTYPE_MARKED6(NODE) CLEAR_CLASSTYPE_MARKED_N(NODE, 5)
/* A list of the nested tag-types (class, struct, union, or enum)
found within this class. The TREE_PURPOSE of each node is the name
of the type; the TREE_VALUE is the type itself. This list includes
nested member class templates. */
#define CLASSTYPE_TAGS(NODE) (TYPE_LANG_SPECIFIC(NODE)->tags)
/* If this class has any bases, this is the number of the base class from
which our VFIELD is based, -1 otherwise. If this class has no base
classes, this is not used.
In D : B1, B2, PARENT would be 0, if D's vtable came from B1,
1, if D's vtable came from B2. */
#define CLASSTYPE_VFIELD_PARENT(NODE) (TYPE_LANG_SPECIFIC(NODE)->vfield_parent)
/* Remove when done merging. */
#define CLASSTYPE_VFIELD(NODE) TYPE_VFIELD(NODE)
/* The number of virtual functions defined for this
_CLASSTYPE node. */
#define CLASSTYPE_VSIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->vsize)
/* The virtual base classes that this type uses. */
#define CLASSTYPE_VBASECLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->vbases)
/* The virtual function pointer fields that this type contains. */
#define CLASSTYPE_VFIELDS(NODE) (TYPE_LANG_SPECIFIC(NODE)->vfields)
/* Number of baseclasses defined for this type.
0 means no base classes. */
#define CLASSTYPE_N_BASECLASSES(NODE) \
(TYPE_BINFO_BASETYPES (NODE) ? TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES(NODE)) : 0)
/* Used for keeping search-specific information. Any search routine
which uses this must define what exactly this slot is used for. */
#define CLASSTYPE_SEARCH_SLOT(NODE) (TYPE_LANG_SPECIFIC(NODE)->search_slot)
/* These are the size, mode and alignment of the type without its
virtual base classes, for when we use this type as a base itself. */
#define CLASSTYPE_SIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->size)
#define CLASSTYPE_ALIGN(NODE) (TYPE_LANG_SPECIFIC(NODE)->align)
/* A cons list of virtual functions which cannot be inherited by
derived classes. When deriving from this type, the derived
class must provide its own definition for each of these functions. */
#define CLASSTYPE_ABSTRACT_VIRTUALS(NODE) (TYPE_LANG_SPECIFIC(NODE)->abstract_virtuals)
/* Nonzero means that this aggr type has been `closed' by a semicolon. */
#define CLASSTYPE_GOT_SEMICOLON(NODE) (TYPE_LANG_SPECIFIC (NODE)->type_flags.got_semicolon)
/* Nonzero means that the main virtual function table pointer needs to be
set because base constructors have placed the wrong value there.
If this is zero, it means that they placed the right value there,
and there is no need to change it. */
#define CLASSTYPE_NEEDS_VIRTUAL_REINIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.needs_virtual_reinit)
/* Nonzero means that if this type has virtual functions, that
the virtual function table will be written out. */
#define CLASSTYPE_VTABLE_NEEDS_WRITING(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.vtable_needs_writing)
/* Nonzero means that this type has an X() constructor. */
#define TYPE_HAS_DEFAULT_CONSTRUCTOR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_default_ctor)
/* Nonzero means the type declared a ctor as private or protected. We
use this to make sure we don't try to generate a copy ctor for a
class that has a member of type NODE. */
#define TYPE_HAS_NONPUBLIC_CTOR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_nonpublic_ctor)
/* Ditto, for operator=. */
#define TYPE_HAS_NONPUBLIC_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_nonpublic_assign_ref)
/* Nonzero means that this type contains a mutable member */
#define CLASSTYPE_HAS_MUTABLE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_mutable)
#define TYPE_HAS_MUTABLE_P(NODE) (cp_has_mutable_p (NODE))
/* Nonzero means that this type is meant for communication via COM. */
#define CLASSTYPE_COM_INTERFACE(NODE) \
(TYPE_LANG_SPECIFIC(NODE)->type_flags.com_interface)
/* A list of class types of which this type is a friend. The
TREE_VALUE is normally a TYPE, but will be a TEMPLATE_DECL in the
case of a template friend. */
#define CLASSTYPE_FRIEND_CLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->friend_classes)
/* A list of the classes which grant friendship to this class. */
#define CLASSTYPE_BEFRIENDING_CLASSES(NODE) \
(TYPE_LANG_SPECIFIC (NODE)->befriending_classes)
/* Say whether this node was declared as a "class" or a "struct". */
#define CLASSTYPE_DECLARED_CLASS(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.declared_class)
/* Nonzero if this class has const members which have no specified initialization. */
#define CLASSTYPE_READONLY_FIELDS_NEED_INIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.const_needs_init)
/* Nonzero if this class has ref members which have no specified initialization. */
#define CLASSTYPE_REF_FIELDS_NEED_INIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.ref_needs_init)
/* Nonzero if this class is included from a header file which employs
`#pragma interface', and it is not included in its implementation file. */
#define CLASSTYPE_INTERFACE_ONLY(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_only)
/* Same as above, but for classes whose purpose we do not know. */
#define CLASSTYPE_INTERFACE_UNKNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown)
#define CLASSTYPE_INTERFACE_KNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown == 0)
#define SET_CLASSTYPE_INTERFACE_UNKNOWN_X(NODE,X) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown = !!(X))
#define SET_CLASSTYPE_INTERFACE_UNKNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown = 1)
#define SET_CLASSTYPE_INTERFACE_KNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown = 0)
/* Nonzero if a _DECL node requires us to output debug info for this class. */
#define CLASSTYPE_DEBUG_REQUESTED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.debug_requested)
/* Additional macros for inheritance information. */
/* The BINFO_INHERITANCE_CHAIN is used opposite to the description in
gcc/tree.h. In particular if D is derived from B then the BINFO
for B (in D) will have a BINFO_INHERITANCE_CHAIN pointing to
D. In tree.h, this pointer is described as pointing in other
direction. There is a different BINFO for each path to a virtual
base; BINFOs for virtual bases are not shared. In addition, shared
versions of each of the virtual class BINFOs are stored in
CLASSTYPE_VBASECLASSES.
We use TREE_VIA_PROTECTED and TREE_VIA_PUBLIC, but private
inheritance is indicated by the absence of the other two flags, not
by TREE_VIAR_PRIVATE, which is unused.
The TREE_CHAIN is for scratch space in search.c. */
/* Nonzero means marked by DFS or BFS search, including searches
by `get_binfo' and `get_base_distance'. */
#define BINFO_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED(BINFO_TYPE(NODE)):TREE_LANG_FLAG_0(NODE))
/* Macros needed because of C compilers that don't allow conditional
expressions to be lvalues. Grr! */
#define SET_BINFO_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_0(NODE)=1))
#define CLEAR_BINFO_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_0(NODE)=0))
/* Nonzero means marked in search through virtual inheritance hierarchy. */
#define BINFO_VBASE_MARKED(NODE) CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
/* Modifier macros */
#define SET_BINFO_VBASE_MARKED(NODE) SET_CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
#define CLEAR_BINFO_VBASE_MARKED(NODE) CLEAR_CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
/* Nonzero means marked in search for members or member functions. */
#define BINFO_FIELDS_MARKED(NODE) \
(TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED2 (BINFO_TYPE (NODE)):TREE_LANG_FLAG_2(NODE))
#define SET_BINFO_FIELDS_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED2(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_2(NODE)=1))
#define CLEAR_BINFO_FIELDS_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED2(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_2(NODE)=0))
/* Nonzero means that this class is on a path leading to a new vtable. */
#define BINFO_VTABLE_PATH_MARKED(NODE) \
(TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED3(BINFO_TYPE(NODE)):TREE_LANG_FLAG_3(NODE))
#define SET_BINFO_VTABLE_PATH_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED3(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_3(NODE)=1))
#define CLEAR_BINFO_VTABLE_PATH_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED3(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_3(NODE)=0))
/* Nonzero means that this class has a new vtable. */
#define BINFO_NEW_VTABLE_MARKED(NODE) \
(TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED4(BINFO_TYPE(NODE)):TREE_LANG_FLAG_4(NODE))
#define SET_BINFO_NEW_VTABLE_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED4(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_4(NODE)=1))
#define CLEAR_BINFO_NEW_VTABLE_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED4(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_4(NODE)=0))
/* Nonzero means this class has done dfs_pushdecls. */
#define BINFO_PUSHDECLS_MARKED(NODE) BINFO_VTABLE_PATH_MARKED (NODE)
#define SET_BINFO_PUSHDECLS_MARKED(NODE) SET_BINFO_VTABLE_PATH_MARKED (NODE)
#define CLEAR_BINFO_PUSHDECLS_MARKED(NODE) CLEAR_BINFO_VTABLE_PATH_MARKED (NODE)
/* Used by various search routines. */
#define IDENTIFIER_MARKED(NODE) TREE_LANG_FLAG_0 (NODE)
/* Accessor macros for the vfield slots in structures. */
/* Get the assoc info that caused this vfield to exist. */
#define VF_BINFO_VALUE(NODE) TREE_PURPOSE (NODE)
/* Get that same information as a _TYPE. */
#define VF_BASETYPE_VALUE(NODE) TREE_VALUE (NODE)
/* Get the value of the top-most type dominating the non-`normal' vfields. */
#define VF_DERIVED_VALUE(NODE) (VF_BINFO_VALUE (NODE) ? BINFO_TYPE (VF_BINFO_VALUE (NODE)) : NULL_TREE)
/* Get the value of the top-most type that's `normal' for the vfield. */
#define VF_NORMAL_VALUE(NODE) TREE_TYPE (NODE)
/* Nonzero for TREE_LIST node means that this list of things
is a list of parameters, as opposed to a list of expressions. */
#define TREE_PARMLIST(NODE) ((NODE)->common.unsigned_flag) /* overloaded! */
/* For FUNCTION_TYPE or METHOD_TYPE, a list of the exceptions that
this type can raise. Each TREE_VALUE is a _TYPE. The TREE_VALUE
will be NULL_TREE to indicate a throw specification of `(...)', or,
equivalently, no throw specification. */
#define TYPE_RAISES_EXCEPTIONS(NODE) TYPE_NONCOPIED_PARTS (NODE)
/* For FUNCTION_TYPE or METHOD_TYPE, return 1 iff it is declared `throw()'. */
#define TYPE_NOTHROW_P(NODE) \
(TYPE_RAISES_EXCEPTIONS (NODE) \
&& TREE_VALUE (TYPE_RAISES_EXCEPTIONS (NODE)) == NULL_TREE)
/* The binding level associated with the namespace. */
#define NAMESPACE_LEVEL(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.level)
/* If a DECL has DECL_LANG_SPECIFIC, it is either a lang_decl_flags or
a lang_decl (which has lang_decl_flags as its initial prefix). A
FUNCTION_DECL, NAMESPACE_DECL, TYPE_DECL, or USING_DECL may have a
full lang_decl. A FIELD_DECL, or a static data member VAR_DECL,
will have only lang_decl_flags. Thus, one should only access the
members of lang_decl that are not in lang_decl_flags for DECLs that
are not FIELD_DECLs or VAR_DECLs. */
struct lang_decl_flags
{
#ifdef ONLY_INT_FIELDS
int language : 8;
#else
enum languages language : 8;
#endif
unsigned operator_attr : 1;
unsigned constructor_attr : 1;
unsigned friend_attr : 1;
unsigned static_function : 1;
unsigned const_memfunc : 1;
unsigned volatile_memfunc : 1;
unsigned abstract_virtual : 1;
unsigned permanent_attr : 1 ;
unsigned constructor_for_vbase_attr : 1;
unsigned mutable_flag : 1;
unsigned is_default_implementation : 1;
unsigned saved_inline : 1;
unsigned use_template : 2;
unsigned nonconverting : 1;
unsigned declared_inline : 1;
unsigned not_really_extern : 1;
unsigned needs_final_overrider : 1;
unsigned bitfield : 1;
unsigned defined_in_class : 1;
unsigned dummy : 4;
tree access;
tree context;
tree memfunc_pointer_to;
tree template_info;
struct binding_level *level;
};
struct lang_decl
{
struct lang_decl_flags decl_flags;
tree main_decl_variant;
tree befriending_classes;
struct pending_inline *pending_inline_info;
};
/* Non-zero if NODE is a _DECL with TREE_READONLY set. */
#define TREE_READONLY_DECL_P(NODE) \
(TREE_READONLY (NODE) && TREE_CODE_CLASS (TREE_CODE (NODE)) == 'd')
/* Non-zero iff DECL is memory-based. The DECL_RTL of
certain const variables might be a CONST_INT, or a REG
in some cases. We cannot use `memory_operand' as a test
here because on most RISC machines, a variable's address
is not, by itself, a legitimate address. */
#define DECL_IN_MEMORY_P(NODE) \
(DECL_RTL (NODE) != NULL_RTX && GET_CODE (DECL_RTL (NODE)) == MEM)
/* For FUNCTION_DECLs: return the language in which this decl
was declared. */
#define DECL_LANGUAGE(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.language)
/* For FUNCTION_DECLs: nonzero means that this function is a constructor. */
#define DECL_CONSTRUCTOR_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_attr)
/* There ought to be a better way to find out whether or not something is
a destructor. */
#define DECL_DESTRUCTOR_P(NODE) \
(DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (NODE)) \
&& DECL_LANGUAGE (NODE) == lang_cplusplus)
#define DECL_CONV_FN_P(NODE) \
(IDENTIFIER_TYPENAME_P (DECL_NAME (NODE)) && TREE_TYPE (DECL_NAME (NODE)))
/* For FUNCTION_DECLs: nonzero means that this function is a constructor
for an object with virtual baseclasses. */
#define DECL_CONSTRUCTOR_FOR_VBASE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_for_vbase_attr)
/* Non-zero for a FUNCTION_DECL that declares a type-info function. */
#define DECL_TINFO_FN_P(NODE) \
(TREE_CODE (NODE) == FUNCTION_DECL \
&& DECL_ARTIFICIAL (NODE) \
&& DECL_LANG_SPECIFIC(NODE)->decl_flags.mutable_flag)
/* Mark NODE as a type-info function. */
#define SET_DECL_TINFO_FN_P(NODE) \
(DECL_LANG_SPECIFIC((NODE))->decl_flags.mutable_flag = 1)
/* For FUNCTION_DECLs: nonzero means that this function is a default
implementation of a signature method. */
#define IS_DEFAULT_IMPLEMENTATION(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.is_default_implementation)
/* Nonzero for _DECL means that this decl appears in (or will appear
in) as a member in a RECORD_TYPE or UNION_TYPE node. It is also for
detecting circularity in case members are multiply defined. In the
case of a VAR_DECL, it is also used to determine how program storage
should be allocated. */
#define DECL_IN_AGGR_P(NODE) (DECL_LANG_FLAG_3(NODE))
/* Nonzero if the DECL was defined in the class definition itself,
rather than outside the class. */
#define DECL_DEFINED_IN_CLASS_P(DECL) \
(DECL_LANG_SPECIFIC (DECL)->decl_flags.defined_in_class)
/* Nonzero for FUNCTION_DECL means that this decl is just a
friend declaration, and should not be added to the list of
member functions for this class. */
#define DECL_FRIEND_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.friend_attr)
/* A TREE_LIST of the types which have befriended this FUNCTION_DECL. */
#define DECL_BEFRIENDING_CLASSES(NODE) \
(DECL_LANG_SPECIFIC(NODE)->befriending_classes)
/* Nonzero for FUNCTION_DECL means that this decl is a static
member function. */
#define DECL_STATIC_FUNCTION_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.static_function)
/* Nonzero for a class member means that it is shared between all objects
of that class. */
#define SHARED_MEMBER_P(NODE) \
(TREE_CODE (NODE) == VAR_DECL || TREE_CODE (NODE) == TYPE_DECL \
|| TREE_CODE (NODE) == CONST_DECL)
/* Nonzero for FUNCTION_DECL means that this decl is a non-static
member function. */
#define DECL_NONSTATIC_MEMBER_FUNCTION_P(NODE) \
(TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE)
/* Nonzero for FUNCTION_DECL means that this decl is a member function
(static or non-static). */
#define DECL_FUNCTION_MEMBER_P(NODE) \
(DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE))
/* Nonzero for FUNCTION_DECL means that this member function
has `this' as const X *const. */
#define DECL_CONST_MEMFUNC_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.const_memfunc)
/* Nonzero for FUNCTION_DECL means that this member function
has `this' as volatile X *const. */
#define DECL_VOLATILE_MEMFUNC_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.volatile_memfunc)
/* Nonzero for a DECL means that this member is a non-static member. */
#define DECL_NONSTATIC_MEMBER_P(NODE) \
((TREE_CODE (NODE) == FUNCTION_DECL \
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE)) \
|| TREE_CODE (NODE) == FIELD_DECL)
/* Nonzero for _DECL means that this member object type
is mutable. */
#define DECL_MUTABLE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.mutable_flag)
/* Nonzero for _DECL means that this constructor is a non-converting
constructor. */
#define DECL_NONCONVERTING_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.nonconverting)
/* Nonzero for FUNCTION_DECL means that this member function
exists as part of an abstract class's interface. */
#define DECL_ABSTRACT_VIRTUAL_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.abstract_virtual)
/* Nonzero for FUNCTION_DECL means that this member function
must be overridden by derived classes. */
#define DECL_NEEDS_FINAL_OVERRIDER_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.needs_final_overrider)
/* Nonzero if allocated on permanent_obstack. */
#define LANG_DECL_PERMANENT(LANGDECL) ((LANGDECL)->decl_flags.permanent_attr)
/* The _TYPE context in which this _DECL appears. This field holds the
class where a virtual function instance is actually defined, and the
lexical scope of a friend function defined in a class body. */
#define DECL_CLASS_CONTEXT(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.context)
#define DECL_REAL_CONTEXT(NODE) \
((TREE_CODE (NODE) == FUNCTION_DECL && DECL_FUNCTION_MEMBER_P (NODE)) \
? DECL_CLASS_CONTEXT (NODE) : CP_DECL_CONTEXT (NODE))
/* NULL_TREE in DECL_CONTEXT represents the global namespace. */
#define CP_DECL_CONTEXT(NODE) \
(DECL_CONTEXT (NODE) ? DECL_CONTEXT (NODE) : global_namespace)
#define FROB_CONTEXT(NODE) ((NODE) == global_namespace ? NULL_TREE : (NODE))
/* For a virtual function, the base where we find its vtable entry.
For a non-virtual function, the base where it is defined. */
#define DECL_VIRTUAL_CONTEXT(NODE) DECL_CONTEXT (NODE)
/* 1 iff NODE has namespace scope, including the global namespace. */
#define DECL_NAMESPACE_SCOPE_P(NODE) \
(DECL_CONTEXT (NODE) == NULL_TREE \
|| TREE_CODE (DECL_CONTEXT (NODE)) == NAMESPACE_DECL)
/* 1 iff NODE is a class member. */
#define DECL_CLASS_SCOPE_P(NODE) \
(DECL_CONTEXT (NODE) \
&& TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (NODE))) == 't')
/* 1 iff NODE is function-local. */
#define DECL_FUNCTION_SCOPE_P(NODE) \
(DECL_CONTEXT (NODE) \
&& TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL)
/* For a NAMESPACE_DECL: the list of using namespace directives
The PURPOSE is the used namespace, the value is the namespace
that is the common ancestor. */
#define DECL_NAMESPACE_USING(NODE) DECL_VINDEX(NODE)
/* In a NAMESPACE_DECL, the DECL_INITIAL is used to record all users
of a namespace, to record the transitive closure of using namespace. */
#define DECL_NAMESPACE_USERS(NODE) DECL_INITIAL (NODE)
/* In a NAMESPACE_DECL, points to the original namespace if this is
a namespace alias. */
#define DECL_NAMESPACE_ALIAS(NODE) DECL_ABSTRACT_ORIGIN (NODE)
#define ORIGINAL_NAMESPACE(NODE) \
(DECL_NAMESPACE_ALIAS (NODE) ? DECL_NAMESPACE_ALIAS (NODE) : (NODE))
/* In a non-local VAR_DECL with static storage duration, this is the
initialization priority. If this value is zero, the NODE will be
initialized at the DEFAULT_INIT_PRIORITY. */
#define DECL_INIT_PRIORITY(NODE) (DECL_FIELD_SIZE ((NODE)))
/* In a TREE_LIST concatenating using directives, indicate indirekt
directives */
#define TREE_INDIRECT_USING(NODE) ((NODE)->common.lang_flag_0)
/* In a VAR_DECL for a variable declared in a for statement,
this is the shadowed (local) variable. */
#define DECL_SHADOWED_FOR_VAR(NODE) DECL_RESULT(NODE)
/* Points back to the decl which caused this lang_decl to be allocated. */
#define DECL_MAIN_VARIANT(NODE) (DECL_LANG_SPECIFIC(NODE)->main_decl_variant)
/* For a FUNCTION_DECL: if this function was declared inline inside of
a class declaration, this is where the text for the function is
squirreled away. */
#define DECL_PENDING_INLINE_INFO(NODE) (DECL_LANG_SPECIFIC(NODE)->pending_inline_info)
/* True if on the saved_inlines (see decl2.c) list. */
#define DECL_SAVED_INLINE(DECL) \
(DECL_LANG_SPECIFIC(DECL)->decl_flags.saved_inline)
/* For a FUNCTION_DECL: if this function was declared inside a signature
declaration, this is the corresponding member function pointer that was
created for it. */
#define DECL_MEMFUNC_POINTER_TO(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.memfunc_pointer_to)
/* For a FIELD_DECL: this points to the signature member function from
which this signature member function pointer was created. */
#define DECL_MEMFUNC_POINTING_TO(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.memfunc_pointer_to)
/* For a VAR_DECL or FUNCTION_DECL: template-specific information. */
#define DECL_TEMPLATE_INFO(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.template_info)
/* Template information for a RECORD_TYPE or UNION_TYPE. */
#define CLASSTYPE_TEMPLATE_INFO(NODE) (TYPE_LANG_SPECIFIC(NODE)->template_info)
/* Template information for an ENUMERAL_TYPE. Although an enumeration may
not be a primary template, it may be declared within the scope of a
primary template and the enumeration constants may depend on
non-type template parameters. */
#define ENUM_TEMPLATE_INFO(NODE) (TYPE_BINFO (NODE))
/* Template information for a template template parameter. */
#define TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO(NODE) (TYPE_BINFO (NODE))
/* Template information for an ENUMERAL_, RECORD_, or UNION_TYPE. */
#define TYPE_TEMPLATE_INFO(NODE) \
(TREE_CODE (NODE) == ENUMERAL_TYPE \
? ENUM_TEMPLATE_INFO (NODE) : \
(TREE_CODE (NODE) == TEMPLATE_TEMPLATE_PARM \
? TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (NODE) \
: CLASSTYPE_TEMPLATE_INFO (NODE)))
/* Set the template information for an ENUMERAL_, RECORD_, or
UNION_TYPE to VAL. */
#define SET_TYPE_TEMPLATE_INFO(NODE, VAL) \
(TREE_CODE (NODE) == ENUMERAL_TYPE \
? (ENUM_TEMPLATE_INFO (NODE) = VAL) \
: (CLASSTYPE_TEMPLATE_INFO (NODE) = VAL))
#define TI_TEMPLATE(NODE) (TREE_PURPOSE (NODE))
#define TI_ARGS(NODE) (TREE_VALUE (NODE))
#define TI_SPEC_INFO(NODE) (TREE_CHAIN (NODE))
#define TI_PENDING_TEMPLATE_FLAG(NODE) TREE_LANG_FLAG_1 (NODE)
/* The TEMPLATE_DECL instantiated or specialized by NODE. This
TEMPLATE_DECL will be the immediate parent, not the most general
template. For example, in:
template <class T> struct S { template <class U> void f(U); }
the FUNCTION_DECL for S<int>::f<double> will have, as its
DECL_TI_TEMPLATE, `template <class U> S<int>::f<U>'.
As a special case, for a member friend template of a template
class, this value will not be a TEMPLATE_DECL, but rather a
LOOKUP_EXPR or IDENTIFIER_NODE indicating the name of the template
and any explicit template arguments provided. For example, in:
template <class T> struct S { friend void f<int>(int, double); }
the DECL_TI_TEMPLATE will be a LOOKUP_EXPR for `f' and the
DECL_TI_ARGS will be {int}. */
#define DECL_TI_TEMPLATE(NODE) TI_TEMPLATE (DECL_TEMPLATE_INFO (NODE))
/* The template arguments used to obtain this decl from the most
general form of DECL_TI_TEMPLATE. For the example given for
DECL_TI_TEMPLATE, the DECL_TI_ARGS will be {int, double}. These
are always the full set of arguments required to instantiate this
declaration from the most general template specialized here. */
#define DECL_TI_ARGS(NODE) TI_ARGS (DECL_TEMPLATE_INFO (NODE))
#define CLASSTYPE_TI_TEMPLATE(NODE) TI_TEMPLATE (CLASSTYPE_TEMPLATE_INFO (NODE))
#define CLASSTYPE_TI_ARGS(NODE) TI_ARGS (CLASSTYPE_TEMPLATE_INFO (NODE))
#define CLASSTYPE_TI_SPEC_INFO(NODE) TI_SPEC_INFO (CLASSTYPE_TEMPLATE_INFO (NODE))
#define ENUM_TI_TEMPLATE(NODE) \
TI_TEMPLATE (ENUM_TEMPLATE_INFO (NODE))
#define ENUM_TI_ARGS(NODE) \
TI_ARGS (ENUM_TEMPLATE_INFO (NODE))
/* Like DECL_TI_TEMPLATE, but for an ENUMERAL_, RECORD_, or UNION_TYPE. */
#define TYPE_TI_TEMPLATE(NODE) \
(TI_TEMPLATE (TYPE_TEMPLATE_INFO (NODE)))
/* Like DECL_TI_ARGS, , but for an ENUMERAL_, RECORD_, or UNION_TYPE. */
#define TYPE_TI_ARGS(NODE) \
(TI_ARGS (TYPE_TEMPLATE_INFO (NODE)))
#define INNERMOST_TEMPLATE_PARMS(NODE) TREE_VALUE(NODE)
/* Nonzero if the NODE corresponds to the template parameters for a
member template, whose inline definition is being processed after
the class definition is complete. */
#define TEMPLATE_PARMS_FOR_INLINE(NODE) TREE_LANG_FLAG_1 (NODE)
#define DECL_SAVED_TREE(NODE) DECL_MEMFUNC_POINTER_TO (NODE)
#define COMPOUND_STMT_NO_SCOPE(NODE) TREE_LANG_FLAG_0 (NODE)
#define NEW_EXPR_USE_GLOBAL(NODE) TREE_LANG_FLAG_0 (NODE)
#define DELETE_EXPR_USE_GLOBAL(NODE) TREE_LANG_FLAG_0 (NODE)
#define DELETE_EXPR_USE_VEC(NODE) TREE_LANG_FLAG_1 (NODE)
#define LOOKUP_EXPR_GLOBAL(NODE) TREE_LANG_FLAG_0 (NODE)
/* The TYPE_MAIN_DECL for a class template type is a TYPE_DECL, not a
TEMPLATE_DECL. This macro determines whether or not a given class
type is really a template type, as opposed to an instantiation or
specialization of one. */
#define CLASSTYPE_IS_TEMPLATE(NODE) \
(CLASSTYPE_TEMPLATE_INFO (NODE) \
&& !CLASSTYPE_USE_TEMPLATE (NODE) \
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (NODE)))
/* The name used by the user to name the typename type. Typically,
this is an IDENTIFIER_NODE, and the same as the DECL_NAME on the
corresponding TYPE_DECL. However, this may also be a
TEMPLATE_ID_EXPR if we had something like `typename X::Y<T>'. */
#define TYPENAME_TYPE_FULLNAME(NODE) TYPE_BINFO (NODE)
/* Nonzero if NODE is an implicit typename. */
#define IMPLICIT_TYPENAME_P(NODE) \
(TREE_CODE (NODE) == TYPENAME_TYPE && TREE_TYPE (NODE))
/* Nonzero in INTEGER_CST means that this int is negative by dint of
using a twos-complement negated operand. */
#define TREE_NEGATED_INT(NODE) (TREE_LANG_FLAG_0 (NODE))
#if 0 /* UNUSED */
/* Nonzero in any kind of _EXPR or _REF node means that it is a call
to a storage allocation routine. If, later, alternate storage
is found to hold the object, this call can be ignored. */
#define TREE_CALLS_NEW(NODE) (TREE_LANG_FLAG_1 (NODE))
#endif
/* Nonzero in any kind of _TYPE that uses multiple inheritance
or virtual baseclasses. */
#define TYPE_USES_COMPLEX_INHERITANCE(NODE) (TREE_LANG_FLAG_1 (NODE))
#if 0 /* UNUSED */
/* Nonzero in IDENTIFIER_NODE means that this name is not the name the user
gave; it's a DECL_NESTED_TYPENAME. Someone may want to set this on
mangled function names, too, but it isn't currently. */
#define TREE_MANGLED(NODE) (FOO)
#endif
#if 0 /* UNUSED */
/* Nonzero in IDENTIFIER_NODE means that this name is overloaded, and
should be looked up in a non-standard way. */
#define DECL_OVERLOADED(NODE) (FOO)
#endif
/* Nonzero if this (non-TYPE)_DECL has its virtual attribute set.
For a FUNCTION_DECL, this is when the function is a virtual function.
For a VAR_DECL, this is when the variable is a virtual function table.
For a FIELD_DECL, when the field is the field for the virtual function table.
For an IDENTIFIER_NODE, nonzero if any function with this name
has been declared virtual.
For a _TYPE if it uses virtual functions (or is derived from
one that does). */
#define TYPE_VIRTUAL_P(NODE) (TREE_LANG_FLAG_2 (NODE))
extern int flag_new_for_scope;
/* This flag is true of a local VAR_DECL if it was declared in a for
statement, but we are no longer in the scope of the for. */
#define DECL_DEAD_FOR_LOCAL(NODE) DECL_LANG_FLAG_7 (NODE)
/* This flag is set on a VAR_DECL that is a DECL_DEAD_FOR_LOCAL
if we already emitted a warning about using it. */
#define DECL_ERROR_REPORTED(NODE) DECL_LANG_FLAG_0 (NODE)
/* This _DECL represents a compiler-generated entity. */
#define SET_DECL_ARTIFICIAL(NODE) (DECL_ARTIFICIAL (NODE) = 1)
/* Record whether a typedef for type `int' was actually `signed int'. */
#define C_TYPEDEF_EXPLICITLY_SIGNED(exp) DECL_LANG_FLAG_1 ((exp))
/* In a FIELD_DECL, nonzero if the decl was originally a bitfield. */
#define DECL_C_BIT_FIELD(NODE) \
(DECL_LANG_SPECIFIC (NODE) && DECL_LANG_SPECIFIC (NODE)->decl_flags.bitfield)
#define SET_DECL_C_BIT_FIELD(NODE) \
(DECL_LANG_SPECIFIC (NODE)->decl_flags.bitfield = 1)
/* Nonzero if the type T promotes to itself.
ANSI C states explicitly the list of types that promote;
in particular, short promotes to int even if they have the same width. */
#define C_PROMOTING_INTEGER_TYPE_P(t) \
(TREE_CODE ((t)) == INTEGER_TYPE \
&& (TYPE_MAIN_VARIANT (t) == char_type_node \
|| TYPE_MAIN_VARIANT (t) == signed_char_type_node \
|| TYPE_MAIN_VARIANT (t) == unsigned_char_type_node \
|| TYPE_MAIN_VARIANT (t) == short_integer_type_node \
|| TYPE_MAIN_VARIANT (t) == short_unsigned_type_node))
#define INTEGRAL_CODE_P(CODE) \
(CODE == INTEGER_TYPE || CODE == ENUMERAL_TYPE || CODE == BOOLEAN_TYPE)
#define ARITHMETIC_TYPE_P(TYPE) (INTEGRAL_TYPE_P (TYPE) || FLOAT_TYPE_P (TYPE))
/* Mark which labels are explicitly declared.
These may be shadowed, and may be referenced from nested functions. */
#define C_DECLARED_LABEL_FLAG(label) TREE_LANG_FLAG_1 (label)
/* Nonzero for _TYPE means that the _TYPE defines
at least one constructor. */
#define TYPE_HAS_CONSTRUCTOR(NODE) (TYPE_LANG_FLAG_1(NODE))
/* When appearing in an INDIRECT_REF, it means that the tree structure
underneath is actually a call to a constructor. This is needed
when the constructor must initialize local storage (which can
be automatically destroyed), rather than allowing it to allocate
space from the heap.
When appearing in a SAVE_EXPR, it means that underneath
is a call to a constructor.
When appearing in a CONSTRUCTOR, it means that it was
a GNU C constructor expression.
When appearing in a FIELD_DECL, it means that this field
has been duly initialized in its constructor. */
#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4(NODE))
#define EMPTY_CONSTRUCTOR_P(NODE) (TREE_CODE (NODE) == CONSTRUCTOR \
&& CONSTRUCTOR_ELTS (NODE) == NULL_TREE \
&& ! TREE_HAS_CONSTRUCTOR (NODE))
#if 0
/* Indicates that a NON_LVALUE_EXPR came from a C++ reference.
Used to generate more helpful error message in case somebody
tries to take its address. */
#define TREE_REFERENCE_EXPR(NODE) (TREE_LANG_FLAG_3(NODE))
#endif
/* Nonzero for _TYPE means that the _TYPE defines a destructor. */
#define TYPE_HAS_DESTRUCTOR(NODE) (TYPE_LANG_FLAG_2(NODE))
#if 0
/* Nonzero for _TYPE node means that creating an object of this type
will involve a call to a constructor. This can apply to objects
of ARRAY_TYPE if the type of the elements needs a constructor. */
#define TYPE_NEEDS_CONSTRUCTING(NODE) ... defined in ../tree.h ...
#endif
/* Nonzero means that an object of this type can not be initialized using
an initializer list. */
#define CLASSTYPE_NON_AGGREGATE(NODE) \
(TYPE_LANG_SPECIFIC (NODE)->type_flags.non_aggregate)
#define TYPE_NON_AGGREGATE_CLASS(NODE) \
(IS_AGGR_TYPE (NODE) && CLASSTYPE_NON_AGGREGATE (NODE))
/* Nonzero if there is a user-defined X::op=(x&) for this class. */
#define TYPE_HAS_REAL_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_real_assign_ref)
#define TYPE_HAS_COMPLEX_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_complex_assign_ref)
#define TYPE_HAS_ABSTRACT_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_abstract_assign_ref)
#define TYPE_HAS_COMPLEX_INIT_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_complex_init_ref)
/* Nonzero for _TYPE node means that destroying an object of this type
will involve a call to a destructor. This can apply to objects
of ARRAY_TYPE is the type of the elements needs a destructor. */
#define TYPE_NEEDS_DESTRUCTOR(NODE) (TYPE_LANG_FLAG_4(NODE))
/* Nonzero for class type means that initialization of this type can use
a bitwise copy. */
#define TYPE_HAS_TRIVIAL_INIT_REF(NODE) \
(TYPE_HAS_INIT_REF (NODE) && ! TYPE_HAS_COMPLEX_INIT_REF (NODE))
/* Nonzero for class type means that assignment of this type can use
a bitwise copy. */
#define TYPE_HAS_TRIVIAL_ASSIGN_REF(NODE) \
(TYPE_HAS_ASSIGN_REF (NODE) && ! TYPE_HAS_COMPLEX_ASSIGN_REF (NODE))
#define TYPE_PTRMEM_P(NODE) \
(TREE_CODE (NODE) == POINTER_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) == OFFSET_TYPE)
#define TYPE_PTR_P(NODE) \
(TREE_CODE (NODE) == POINTER_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) != OFFSET_TYPE)
#define TYPE_PTROB_P(NODE) \
(TYPE_PTR_P (NODE) && TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) != VOID_TYPE)
#define TYPE_PTROBV_P(NODE) \
(TYPE_PTR_P (NODE) && TREE_CODE (TREE_TYPE (NODE)) != FUNCTION_TYPE)
#define TYPE_PTRFN_P(NODE) \
(TREE_CODE (NODE) == POINTER_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) == FUNCTION_TYPE)
/* Nonzero for _TYPE node means that this type is a pointer to member
function type. */
#define TYPE_PTRMEMFUNC_P(NODE) \
(TREE_CODE(NODE) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (NODE))
#define TYPE_PTRMEMFUNC_FLAG(NODE) \
(TYPE_LANG_SPECIFIC(NODE)->type_flags.ptrmemfunc_flag)
/* A pointer-to-function member type looks like:
struct {
short __delta;
short __index;
union {
P __pfn;
short __delta2;
} __pfn_or_delta2;
};
where P is a POINTER_TYPE to a METHOD_TYPE appropriate for the
pointer to member. The fields are used as follows:
If __INDEX is -1, then the function to call is non-virtual, and
is located at the address given by __PFN.
If __INDEX is zero, then this a NULL pointer-to-member.
Otherwise, the function to call is virtual. Then, __DELTA2 gives
the offset from an instance of the object to the virtual function
table, and __INDEX - 1 is the index into the vtable to use to
find the function.
The value to use for the THIS parameter is the address of the
object plus __DELTA.
For example, given:
struct B1 {
int i;
};
struct B2 {
double d;
void f();
};
struct S : public B1, B2 {};
the pointer-to-member for `&S::f' looks like:
{ 4, -1, { &f__2B2 } };
The `4' means that given an `S*' you have to add 4 bytes to get to
the address of the `B2*'. Then, the -1 indicates that this is a
non-virtual function. Of course, `&f__2B2' is the name of that
function.
(Of course, the exactl values may differ depending on the mangling
scheme, sizes of types, and such.). */
/* Get the POINTER_TYPE to the METHOD_TYPE associated with this
pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true,
before using this macro. */
#define TYPE_PTRMEMFUNC_FN_TYPE(NODE) (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (NODE)))))))
/* Returns `A' for a type like `int (A::*)(double)' */
#define TYPE_PTRMEMFUNC_OBJECT_TYPE(NODE) \
TYPE_METHOD_BASETYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (NODE)))
/* These are use to manipulate the canonical RECORD_TYPE from the
hashed POINTER_TYPE, and can only be used on the POINTER_TYPE. */
#define TYPE_GET_PTRMEMFUNC_TYPE(NODE) ((tree)TYPE_LANG_SPECIFIC(NODE))
#define TYPE_SET_PTRMEMFUNC_TYPE(NODE, VALUE) (TYPE_LANG_SPECIFIC(NODE) = ((struct lang_type *)(void*)(VALUE)))
/* These are to get the delta2 and pfn fields from a TYPE_PTRMEMFUNC_P. */
#define DELTA2_FROM_PTRMEMFUNC(NODE) delta2_from_ptrmemfunc ((NODE))
#define PFN_FROM_PTRMEMFUNC(NODE) pfn_from_ptrmemfunc ((NODE))
/* For a pointer-to-member constant `X::Y' this is the RECORD_TYPE for
`X'. */
#define PTRMEM_CST_CLASS(NODE) \
(TYPE_PTRMEM_P (TREE_TYPE (NODE)) \
? TYPE_OFFSET_BASETYPE (TREE_TYPE (TREE_TYPE (NODE))) \
: TYPE_PTRMEMFUNC_OBJECT_TYPE (TREE_TYPE (NODE)))
/* For a pointer-to-member constant `X::Y' this is the _DECL for
`Y'. */
#define PTRMEM_CST_MEMBER(NODE) (((ptrmem_cst_t) NODE)->member)
/* Nonzero for VAR_DECL and FUNCTION_DECL node means that `extern' was
specified in its declaration. */
#define DECL_THIS_EXTERN(NODE) (DECL_LANG_FLAG_2(NODE))
/* Nonzero for VAR_DECL and FUNCTION_DECL node means that `static' was
specified in its declaration. */
#define DECL_THIS_STATIC(NODE) (DECL_LANG_FLAG_6(NODE))
/* Nonzero in FUNCTION_DECL means it is really an operator.
Just used to communicate formatting information to dbxout.c. */
#define DECL_OPERATOR(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.operator_attr)
#define ANON_UNION_P(NODE) (DECL_NAME (NODE) == 0)
/* Nonzero if TYPE is an anonymous union type. We have to use a flag for
this because "A union for which objects or pointers are declared is not
an anonymous union" [class.union]. */
#define ANON_UNION_TYPE_P(NODE) \
(TYPE_LANG_SPECIFIC (NODE) \
&& TYPE_LANG_SPECIFIC (NODE)->type_flags.anon_union)
#define SET_ANON_UNION_TYPE_P(NODE) \
(TYPE_LANG_SPECIFIC (NODE)->type_flags.anon_union = 1)
#define UNKNOWN_TYPE LANG_TYPE
/* Define fields and accessors for nodes representing declared names. */
#if 0
/* C++: A derived class may be able to directly use the virtual
function table of a base class. When it does so, it may
still have a decl node used to access the virtual function
table (so that variables of this type can initialize their
virtual function table pointers by name). When such thievery
is committed, know exactly which base class's virtual function
table is the one being stolen. This effectively computes the
transitive closure. */
#define DECL_VPARENT(NODE) ((NODE)->decl.arguments)
#endif
#define TYPE_WAS_ANONYMOUS(NODE) (TYPE_LANG_SPECIFIC (NODE)->type_flags.was_anonymous)
/* C++: all of these are overloaded! These apply only to TYPE_DECLs. */
/* The format of each node in the DECL_FRIENDLIST is as follows:
The TREE_PURPOSE will be the name of a function, i.e., an
IDENTIFIER_NODE. The TREE_VALUE will be itself a TREE_LIST, the
list of functions with that name which are friends. The
TREE_PURPOSE of each node in this sublist will be error_mark_node,
if the function was declared a friend individually, in which case
the TREE_VALUE will be the function_decl. If, however, all
functions with a given name in a class were declared to be friends,
the TREE_PUROSE will be the class type, and the TREE_VALUE will be
NULL_TREE. */
#define DECL_FRIENDLIST(NODE) (DECL_INITIAL (NODE))
#define FRIEND_NAME(LIST) (TREE_PURPOSE (LIST))
#define FRIEND_DECLS(LIST) (TREE_VALUE (LIST))
/* The DECL_ACCESS, if non-NULL, is a TREE_LIST. The TREE_PURPOSE of
each node is a type; the TREE_VALUE is the access granted for this
DECL in that type. The DECL_ACCESS is set by access declarations.
For example, if a member that would normally be public in a
derived class is made protected, then the derived class and the
protected_access_node will appear in the DECL_ACCESS for the node. */
#define DECL_ACCESS(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.access)
/* C++: all of these are overloaded!
These apply to PARM_DECLs and VAR_DECLs. */
#define DECL_REFERENCE_SLOT(NODE) ((tree)(NODE)->decl.arguments)
#define SET_DECL_REFERENCE_SLOT(NODE,VAL) ((NODE)->decl.arguments=VAL)
/* Accessor macros for C++ template decl nodes. */
/* The DECL_TEMPLATE_PARMS are a list. The TREE_PURPOSE of each node
is a INT_CST whose TREE_INT_CST_HIGH indicates the level of the
template parameters, with 1 being the outermost set of template
parameters. The TREE_VALUE is a vector, whose elements are the
template parameters at each level. Each element in the vector is a
TREE_LIST, whose TREE_VALUE is a PARM_DECL (if the parameter is a
non-type parameter), or a TYPE_DECL (if the parameter is a type
parameter). The TREE_PURPOSE is the default value, if any. The
TEMPLATE_PARM_INDEX for the parameter is avilable as the
DECL_INITIAL (for a PARM_DECL) or as the TREE_TYPE (for a
TYPE_DECL). */
#define DECL_TEMPLATE_PARMS(NODE) DECL_ARGUMENTS(NODE)
#define DECL_INNERMOST_TEMPLATE_PARMS(NODE) \
INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (NODE))
#define DECL_NTPARMS(NODE) \
TREE_VEC_LENGTH (DECL_INNERMOST_TEMPLATE_PARMS (NODE))
/* For function, method, class-data templates. */
#define DECL_TEMPLATE_RESULT(NODE) DECL_RESULT(NODE)
/* For a static member variable template, the
DECL_TEMPLATE_INSTANTIATIONS list contains the explicitly and
implicitly generated instantiations of the variable. There are no
partial instantiations of static member variables, so all of these
will be full instantiations.
For a class template the DECL_TEMPLATE_INSTANTIATIONS lists holds
all instantiations and specializations of the class type, including
partial instantiations and partial specializations.
In both cases, the TREE_PURPOSE of each node contains the arguments
used; the TREE_VALUE contains the generated variable. The template
arguments are always complete. For example, given:
template <class T> struct S1 {
template <class U> struct S2 {};
template <class U> struct S2<U*> {};
};
the record for the partial specialization will contain, as its
argument list, { {T}, {U*} }, and will be on the
DECL_TEMPLATE_INSTANTIATIONS list for `template <class T> template
<class U> struct S1<T>::S2'.
This list is not used for function templates. */
#define DECL_TEMPLATE_INSTANTIATIONS(NODE) DECL_VINDEX(NODE)
/* For a function template, the DECL_TEMPLATE_SPECIALIZATIONS lists
contains all instantiations and specializations of the function,
including partial instantiations. For a partial instantiation
which is a specialization, this list holds only full
specializations of the template that are instantiations of the
partial instantiation. For example, given:
template <class T> struct S {
template <class U> void f(U);
template <> void f(T);
};
the `S<int>::f<int>(int)' function will appear on the
DECL_TEMPLATE_SPECIALIZATIONS list for both `template <class T>
template <class U> void S<T>::f(U)' and `template <class T> void
S<int>::f(T)'. In the latter case, however, it will have only the
innermost set of arguments (T, in this case). The DECL_TI_TEMPLATE
for the function declaration will point at the specialization, not
the fully general template.
For a class template, this list contains the partial
specializations of this template. (Full specializations are not
recorded on this list.) The TREE_PURPOSE holds the innermost
arguments used in the partial specialization (e.g., for `template
<class T> struct S<T*, int>' this will be `T*'.) The TREE_VALUE
holds the innermost template parameters for the specialization
(e.g., `T' in the example above.) The TREE_TYPE is the _TYPE node
for the partial specialization.
This list is not used for static variable templates. */
#define DECL_TEMPLATE_SPECIALIZATIONS(NODE) DECL_SIZE(NODE)
#define DECL_TEMPLATE_INJECT(NODE) DECL_INITIAL(NODE)
/* Nonzero for a DECL which is actually a template parameter. */
#define DECL_TEMPLATE_PARM_P(NODE) \
DECL_LANG_FLAG_0 (NODE)
#define DECL_TEMPLATE_TEMPLATE_PARM_P(NODE) \
(TREE_CODE (NODE) == TEMPLATE_DECL && DECL_TEMPLATE_PARM_P (NODE))
#define DECL_FUNCTION_TEMPLATE_P(NODE) \
(TREE_CODE (NODE) == TEMPLATE_DECL \
&& TREE_CODE (DECL_TEMPLATE_RESULT (NODE)) == FUNCTION_DECL)
/* Nonzero for a DECL that represents a template class. */
#define DECL_CLASS_TEMPLATE_P(NODE) \
(TREE_CODE (NODE) == TEMPLATE_DECL \
&& TREE_CODE (DECL_TEMPLATE_RESULT (NODE)) == TYPE_DECL \
&& !DECL_TEMPLATE_TEMPLATE_PARM_P (NODE))
/* Nonzero if NODE which declares a type. */
#define DECL_DECLARES_TYPE_P(NODE) \
(TREE_CODE (NODE) == TYPE_DECL || DECL_CLASS_TEMPLATE_P (NODE))
/* A `primary' template is one that has its own template header. A
member function of a class template is a template, but not primary.
A member template is primary. Friend templates are primary, too. */
/* Returns the primary template corresponding to these parameters. */
#define DECL_PRIMARY_TEMPLATE(NODE) \
(TREE_TYPE (DECL_INNERMOST_TEMPLATE_PARMS (NODE)))
/* Returns non-zero if NODE is a primary template. */
#define PRIMARY_TEMPLATE_P(NODE) (DECL_PRIMARY_TEMPLATE (NODE) == NODE)
#define CLASSTYPE_TEMPLATE_LEVEL(NODE) \
(TREE_INT_CST_HIGH (TREE_PURPOSE (CLASSTYPE_TI_TEMPLATE (NODE))))
/* Indicates whether or not (and how) a template was expanded for this
FUNCTION_DECL or VAR_DECL.
0=normal declaration, e.g. int min (int, int);
1=implicit template instantiation
2=explicit template specialization, e.g. int min<int> (int, int);
3=explicit template instantiation, e.g. template int min<int> (int, int); */
#define DECL_USE_TEMPLATE(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.use_template)
#define DECL_TEMPLATE_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) & 1)
#define CLASSTYPE_TEMPLATE_INSTANTIATION(NODE) \
(CLASSTYPE_USE_TEMPLATE (NODE) & 1)
#define DECL_TEMPLATE_SPECIALIZATION(NODE) (DECL_USE_TEMPLATE (NODE) == 2)
#define SET_DECL_TEMPLATE_SPECIALIZATION(NODE) (DECL_USE_TEMPLATE (NODE) = 2)
#define CLASSTYPE_TEMPLATE_SPECIALIZATION(NODE) \
(CLASSTYPE_USE_TEMPLATE (NODE) == 2)
#define SET_CLASSTYPE_TEMPLATE_SPECIALIZATION(NODE) \
(CLASSTYPE_USE_TEMPLATE (NODE) = 2)
#define DECL_IMPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) == 1)
#define SET_DECL_IMPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) = 1)
#define CLASSTYPE_IMPLICIT_INSTANTIATION(NODE) \
(CLASSTYPE_USE_TEMPLATE(NODE) == 1)
#define SET_CLASSTYPE_IMPLICIT_INSTANTIATION(NODE) \
(CLASSTYPE_USE_TEMPLATE(NODE) = 1)
#define DECL_EXPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) == 3)
#define SET_DECL_EXPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) = 3)
#define CLASSTYPE_EXPLICIT_INSTANTIATION(NODE) \
(CLASSTYPE_USE_TEMPLATE(NODE) == 3)
#define SET_CLASSTYPE_EXPLICIT_INSTANTIATION(NODE) \
(CLASSTYPE_USE_TEMPLATE(NODE) = 3)
/* Non-zero if DECL is a friend function which is an instantiation
from the point of view of the compiler, but not from the point of
view of the language. For example given:
template <class T> struct S { friend void f(T) {}; };
the declaration of `void f(int)' generated when S<int> is
instantiated will not be a DECL_TEMPLATE_INSTANTIATION, but will be
a DECL_FRIEND_PSUEDO_TEMPLATE_INSTANTIATION. */
#define DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION(DECL) \
(DECL_TEMPLATE_INFO (DECL) && !DECL_USE_TEMPLATE (DECL))
/* Non-zero if TYPE is a partial instantiation of a template class,
i.e., an instantiation whose instantiation arguments involve
template types. */
#define PARTIAL_INSTANTIATION_P(TYPE) \
(TYPE_LANG_SPECIFIC (TYPE)->type_flags.is_partial_instantiation)
/* Non-zero iff we are currently processing a declaration for an
entity with its own template parameter list, and which is not a
full specialization. */
#define PROCESSING_REAL_TEMPLATE_DECL_P() \
(processing_template_decl > template_class_depth (current_class_type))
/* This function may be a guiding decl for a template. */
#define DECL_MAYBE_TEMPLATE(NODE) DECL_LANG_FLAG_4 (NODE)
/* Nonzero if this VAR_DECL or FUNCTION_DECL has already been
instantiated, i.e. its definition has been generated from the
pattern given in the the template. */
#define DECL_TEMPLATE_INSTANTIATED(NODE) DECL_LANG_FLAG_1(NODE)
/* We know what we're doing with this decl now. */
#define DECL_INTERFACE_KNOWN(NODE) DECL_LANG_FLAG_5 (NODE)
/* This function was declared inline. This flag controls the linkage
semantics of 'inline'; whether or not the function is inlined is
controlled by DECL_INLINE. */
#define DECL_THIS_INLINE(NODE) \
(DECL_LANG_SPECIFIC (NODE)->decl_flags.declared_inline)
/* DECL_EXTERNAL must be set on a decl until the decl is actually emitted,
so that assemble_external will work properly. So we have this flag to
tell us whether the decl is really not external. */
#define DECL_NOT_REALLY_EXTERN(NODE) \
(DECL_LANG_SPECIFIC (NODE)->decl_flags.not_really_extern)
#define DECL_REALLY_EXTERN(NODE) \
(DECL_EXTERNAL (NODE) && ! DECL_NOT_REALLY_EXTERN (NODE))
#define THUNK_DELTA(DECL) ((DECL)->decl.frame_size.i)
/* ...and for unexpanded-parameterized-type nodes. */
#define UPT_TEMPLATE(NODE) TREE_PURPOSE(TYPE_VALUES(NODE))
#define UPT_PARMS(NODE) TREE_VALUE(TYPE_VALUES(NODE))
/* An un-parsed default argument looks like an identifier. */
#define DEFARG_NODE_CHECK(t) TREE_CHECK(t, DEFAULT_ARG)
#define DEFARG_LENGTH(NODE) (DEFARG_NODE_CHECK(NODE)->identifier.length)
#define DEFARG_POINTER(NODE) (DEFARG_NODE_CHECK(NODE)->identifier.pointer)
#define builtin_function(NAME, TYPE, CODE, LIBNAME) \
define_function (NAME, TYPE, CODE, (void (*) PROTO((tree)))pushdecl, LIBNAME)
/* These macros provide convenient access to the various _STMT nodes
created when parsing template declarations. */
#define IF_COND(NODE) TREE_OPERAND (NODE, 0)
#define THEN_CLAUSE(NODE) TREE_OPERAND (NODE, 1)
#define ELSE_CLAUSE(NODE) TREE_OPERAND (NODE, 2)
#define WHILE_COND(NODE) TREE_OPERAND (NODE, 0)
#define WHILE_BODY(NODE) TREE_OPERAND (NODE, 1)
#define DO_COND(NODE) TREE_OPERAND (NODE, 0)
#define DO_BODY(NODE) TREE_OPERAND (NODE, 1)
#define RETURN_EXPR(NODE) TREE_OPERAND (NODE, 0)
#define EXPR_STMT_EXPR(NODE) TREE_OPERAND (NODE, 0)
#define FOR_INIT_STMT(NODE) TREE_OPERAND (NODE, 0)
#define FOR_COND(NODE) TREE_OPERAND (NODE, 1)
#define FOR_EXPR(NODE) TREE_OPERAND (NODE, 2)
#define FOR_BODY(NODE) TREE_OPERAND (NODE, 3)
#define SWITCH_COND(NODE) TREE_OPERAND (NODE, 0)
#define SWITCH_BODY(NODE) TREE_OPERAND (NODE, 1)
#define CASE_LOW(NODE) TREE_OPERAND (NODE, 0)
#define CASE_HIGH(NODE) TREE_OPERAND (NODE, 1)
#define GOTO_DESTINATION(NODE) TREE_OPERAND (NODE, 0)
#define TRY_STMTS(NODE) TREE_OPERAND (NODE, 0)
#define TRY_HANDLERS(NODE) TREE_OPERAND (NODE, 1)
#define HANDLER_PARMS(NODE) TREE_OPERAND (NODE, 0)
#define HANDLER_BODY(NODE) TREE_OPERAND (NODE, 1)
#define COMPOUND_BODY(NODE) TREE_OPERAND (NODE, 0)
#define ASM_CV_QUAL(NODE) TREE_OPERAND (NODE, 0)
#define ASM_STRING(NODE) TREE_OPERAND (NODE, 1)
#define ASM_OUTPUTS(NODE) TREE_OPERAND (NODE, 2)
#define ASM_INPUTS(NODE) TREE_OPERAND (NODE, 3)
#define ASM_CLOBBERS(NODE) TREE_OPERAND (NODE, 4)
/* An enumeration of the kind of tags that C++ accepts. */
enum tag_types { record_type, class_type, union_type, enum_type,
signature_type };
/* Zero means prototype weakly, as in ANSI C (no args means nothing).
Each language context defines how this variable should be set. */
extern int strict_prototype;
extern int strict_prototypes_lang_c, strict_prototypes_lang_cplusplus;
/* Non-zero means that if a label exists, and no other identifier
applies, use the value of the label. */
extern int flag_labels_ok;
/* Non-zero means to collect statistics which might be expensive
and to print them when we are done. */
extern int flag_detailed_statistics;
/* Non-zero means warn in function declared in derived class has the
same name as a virtual in the base class, but fails to match the
type signature of any virtual function in the base class. */
extern int warn_overloaded_virtual;
/* Nonzero means warn about use of multicharacter literals. */
extern int warn_multichar;
/* Non-zero means warn if a non-templatized friend function is
declared in a templatized class. This behavior is warned about with
flag_guiding_decls in do_friend. */
extern int warn_nontemplate_friend;
/* in c-common.c */
extern void declare_function_name PROTO((void));
extern void decl_attributes PROTO((tree, tree, tree));
extern void init_function_format_info PROTO((void));
extern void record_function_format PROTO((tree, tree, int, int, int));
extern void check_function_format PROTO((tree, tree, tree));
/* Print an error message for invalid operands to arith operation CODE.
NOP_EXPR is used as a special case (see truthvalue_conversion). */
extern void binary_op_error PROTO((enum tree_code));
extern tree cp_build_qualified_type PROTO((tree, int));
extern tree canonical_type_variant PROTO((tree));
extern void c_expand_expr_stmt PROTO((tree));
/* Validate the expression after `case' and apply default promotions. */
extern tree check_case_value PROTO((tree));
/* Concatenate a list of STRING_CST nodes into one STRING_CST. */
extern tree combine_strings PROTO((tree));
extern void constant_expression_warning PROTO((tree));
extern tree convert_and_check PROTO((tree, tree));
extern void overflow_warning PROTO((tree));
extern void unsigned_conversion_warning PROTO((tree, tree));
extern void c_apply_type_quals_to_decl PROTO((int, tree));
/* Read the rest of the current #-directive line. */
#if USE_CPPLIB
extern char *get_directive_line PROTO((void));
#define GET_DIRECTIVE_LINE() get_directive_line ()
#else
extern char *get_directive_line PROTO((FILE *));
#define GET_DIRECTIVE_LINE() get_directive_line (finput)
#endif
/* Subroutine of build_binary_op, used for comparison operations.
See if the operands have both been converted from subword integer types
and, if so, perhaps change them both back to their original type. */
extern tree shorten_compare PROTO((tree *, tree *, tree *, enum tree_code *));
/* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
or validate its data type for an `if' or `while' statement or ?..: exp. */
extern tree truthvalue_conversion PROTO((tree));
extern tree type_for_mode PROTO((enum machine_mode, int));
extern tree type_for_size PROTO((unsigned, int));
extern int c_get_alias_set PROTO((tree));
/* in decl{2}.c */
extern tree void_list_node;
extern tree void_zero_node;
extern tree default_function_type;
extern tree vtable_entry_type;
extern tree sigtable_entry_type;
extern tree __t_desc_type_node;
#if 0
extern tree __tp_desc_type_node;
#endif
extern tree __access_mode_type_node;
extern tree __bltn_desc_type_node, __user_desc_type_node;
extern tree __class_desc_type_node, __attr_desc_type_node;
extern tree __ptr_desc_type_node, __func_desc_type_node;
extern tree __ptmf_desc_type_node, __ptmd_desc_type_node;
extern tree type_info_type_node;
extern tree class_star_type_node;
extern tree this_identifier;
extern tree ctor_identifier, dtor_identifier;
extern tree pfn_identifier;
extern tree index_identifier;
extern tree delta_identifier;
extern tree delta2_identifier;
extern tree pfn_or_delta2_identifier;
extern tree tag_identifier;
extern tree vt_off_identifier;
/* A node that is a list (length 1) of error_mark_nodes. */
extern tree error_mark_list;
extern tree ptr_type_node;
extern tree class_type_node, record_type_node, union_type_node, enum_type_node;
extern tree unknown_type_node;
extern tree opaque_type_node, signature_type_node;
/* Node for "pointer to (virtual) function".
This may be distinct from ptr_type_node so gdb can distinguish them. */
#define vfunc_ptr_type_node \
(flag_vtable_thunks ? vtable_entry_type : ptr_type_node)
/* The type of a vtbl, i.e., an array of vtable entries. */
extern tree vtbl_type_node;
/* The type of a class vtbl pointer, i.e., a pointer to a vtable entry. */
extern tree vtbl_ptr_type_node;
extern tree delta_type_node;
extern tree std_node;
extern tree long_long_integer_type_node, long_long_unsigned_type_node;
/* For building calls to `delete'. */
extern tree integer_two_node, integer_three_node;
extern tree boolean_type_node, boolean_true_node, boolean_false_node;
extern tree null_node;
extern tree anonymous_namespace_name;
/* The FUNCTION_DECL for the default `::operator delete'. */
extern tree global_delete_fndecl;
/* in pt.c */
/* These values are used for the `STRICT' parameter to type_unfication and
fn_type_unification. Their meanings are described with the
documentation for fn_type_unification. */
typedef enum unification_kind_t {
DEDUCE_CALL,
DEDUCE_CONV,
DEDUCE_EXACT
} unification_kind_t;
extern tree current_template_parms;
extern HOST_WIDE_INT processing_template_decl;
extern tree last_tree;
/* The template currently being instantiated, and where the instantiation
was triggered. */
struct tinst_level
{
tree decl;
int line;
char *file;
struct tinst_level *next;
};
extern int minimal_parse_mode;
extern void maybe_print_template_context PROTO ((void));
/* in class.c */
/* When parsing a class definition, the access specifier most recently
given by the user, or, if no access specifier was given, the
default value appropriate for the kind of class (i.e., struct,
class, or union). */
extern tree current_access_specifier;
extern tree current_class_name;
extern tree current_class_type;
extern tree current_class_ptr;
extern tree previous_class_type;
extern tree current_class_ref;
extern int current_class_depth;
extern tree current_lang_name;
extern tree lang_name_cplusplus, lang_name_c, lang_name_java;
/* The low-water mark on the class-cache obstack. */
extern char *class_cache_firstobj;
/* Points to the name of that function. May not be the DECL_NAME
of CURRENT_FUNCTION_DECL due to overloading */
extern tree original_function_name;
/* in init.c */
extern tree global_base_init_list;
extern tree current_base_init_list, current_member_init_list;
extern int current_function_just_assigned_this;
extern int current_function_parms_stored;
/* Here's where we control how name mangling takes place. */
#define OPERATOR_ASSIGN_FORMAT "__a%s"
#define OPERATOR_FORMAT "__%s"
#define OPERATOR_TYPENAME_FORMAT "__op"
/* Cannot use '$' up front, because this confuses gdb
(names beginning with '$' are gdb-local identifiers).
Note that all forms in which the '$' is significant are long enough
for direct indexing (meaning that if we know there is a '$'
at a particular location, we can index into the string at
any other location that provides distinguishing characters). */
/* Define NO_DOLLAR_IN_LABEL in your favorite tm file if your assembler
doesn't allow '$' in symbol names. */
#ifndef NO_DOLLAR_IN_LABEL
#define JOINER '$'
#define VPTR_NAME "$v"
#define THROW_NAME "$eh_throw"
#define DESTRUCTOR_DECL_PREFIX "_$_"
#define AUTO_VTABLE_NAME "__vtbl$me__"
#define AUTO_TEMP_NAME "_$tmp_"
#define AUTO_TEMP_FORMAT "_$tmp_%d"
#define VTABLE_BASE "$vb"
#define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt$%s")
#define VFIELD_BASE "$vf"
#define VFIELD_NAME "_vptr$"
#define VFIELD_NAME_FORMAT "_vptr$%s"
#define VBASE_NAME "_vb$"
#define VBASE_NAME_FORMAT "_vb$%s"
#define STATIC_NAME_FORMAT "_%s$%s"
#define ANON_AGGRNAME_FORMAT "$_%d"
#else /* NO_DOLLAR_IN_LABEL */
#ifndef NO_DOT_IN_LABEL
#define JOINER '.'
#define VPTR_NAME ".v"
#define THROW_NAME ".eh_throw"
#define DESTRUCTOR_DECL_PREFIX "_._"
#define AUTO_VTABLE_NAME "__vtbl.me__"
#define AUTO_TEMP_NAME "_.tmp_"
#define AUTO_TEMP_FORMAT "_.tmp_%d"
#define VTABLE_BASE ".vb"
#define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt.%s")
#define VFIELD_BASE ".vf"
#define VFIELD_NAME "_vptr."
#define VFIELD_NAME_FORMAT "_vptr.%s"
#define VBASE_NAME "_vb."
#define VBASE_NAME_FORMAT "_vb.%s"
#define STATIC_NAME_FORMAT "_%s.%s"
#define ANON_AGGRNAME_FORMAT "._%d"
#else /* NO_DOT_IN_LABEL */
#define VPTR_NAME "__vptr"
#define VPTR_NAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), VPTR_NAME, sizeof (VPTR_NAME) - 1))
#define THROW_NAME "__eh_throw"
#define DESTRUCTOR_DECL_PREFIX "__destr_"
#define DESTRUCTOR_NAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), DESTRUCTOR_DECL_PREFIX, \
sizeof (DESTRUCTOR_DECL_PREFIX) - 1))
#define IN_CHARGE_NAME "__in_chrg"
#define AUTO_VTABLE_NAME "__vtbl_me__"
#define AUTO_TEMP_NAME "__tmp_"
#define TEMP_NAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), AUTO_TEMP_NAME, \
sizeof (AUTO_TEMP_NAME) - 1))
#define AUTO_TEMP_FORMAT "__tmp_%d"
#define VTABLE_BASE "__vtb"
#define VTABLE_NAME "__vt_"
#define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt_%s")
#define VTABLE_NAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), VTABLE_NAME, \
sizeof (VTABLE_NAME) - 1))
#define VFIELD_BASE "__vfb"
#define VFIELD_NAME "__vptr_"
#define VFIELD_NAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), VFIELD_NAME, \
sizeof (VFIELD_NAME) - 1))
#define VFIELD_NAME_FORMAT "_vptr_%s"
#define VBASE_NAME "__vb_"
#define VBASE_NAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), VBASE_NAME, \
sizeof (VBASE_NAME) - 1))
#define VBASE_NAME_FORMAT "__vb_%s"
#define STATIC_NAME_FORMAT "__static_%s_%s"
#define ANON_AGGRNAME_PREFIX "__anon_"
#define ANON_AGGRNAME_P(ID_NODE) \
(!strncmp (IDENTIFIER_POINTER (ID_NODE), ANON_AGGRNAME_PREFIX, \
sizeof (ANON_AGGRNAME_PREFIX) - 1))
#define ANON_AGGRNAME_FORMAT "__anon_%d"
#define ANON_PARMNAME_FORMAT "__%d"
#define ANON_PARMNAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == '_' \
&& IDENTIFIER_POINTER (ID_NODE)[1] == '_' \
&& IDENTIFIER_POINTER (ID_NODE)[2] <= '9')
#endif /* NO_DOT_IN_LABEL */
#endif /* NO_DOLLAR_IN_LABEL */
#define THIS_NAME "this"
#define DESTRUCTOR_NAME_FORMAT "~%s"
#define FILE_FUNCTION_PREFIX_LEN 9
#define CTOR_NAME "__ct"
#define DTOR_NAME "__dt"
#define IN_CHARGE_NAME "__in_chrg"
#define VTBL_PTR_TYPE "__vtbl_ptr_type"
#define VTABLE_DELTA_NAME "__delta"
#define VTABLE_INDEX_NAME "__index"
#define VTABLE_PFN_NAME "__pfn"
#define VTABLE_DELTA2_NAME "__delta2"
#define SIGNATURE_FIELD_NAME "__s_"
#define SIGNATURE_FIELD_NAME_FORMAT "__s_%s"
#define SIGNATURE_OPTR_NAME "__optr"
#define SIGNATURE_SPTR_NAME "__sptr"
#define SIGNATURE_POINTER_NAME "__sp_"
#define SIGNATURE_POINTER_NAME_FORMAT "__%s%s%ssp_%s"
#define SIGNATURE_REFERENCE_NAME "__sr_"
#define SIGNATURE_REFERENCE_NAME_FORMAT "__%s%s%ssr_%s"
#define SIGTABLE_PTR_TYPE "__sigtbl_ptr_type"
#define SIGTABLE_NAME_FORMAT "__st_%s_%s"
#define SIGTABLE_NAME_FORMAT_LONG "__st_%s_%s_%d"
#define SIGTABLE_TAG_NAME "__tag"
#define SIGTABLE_VB_OFF_NAME "__vb_off"
#define SIGTABLE_VT_OFF_NAME "__vt_off"
#define EXCEPTION_CLEANUP_NAME "exception cleanup"
#define THIS_NAME_P(ID_NODE) (strcmp(IDENTIFIER_POINTER (ID_NODE), "this") == 0)
#if !defined(NO_DOLLAR_IN_LABEL) || !defined(NO_DOT_IN_LABEL)
#define VPTR_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == JOINER \
&& IDENTIFIER_POINTER (ID_NODE)[1] == 'v')
#define DESTRUCTOR_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[1] == JOINER \
&& IDENTIFIER_POINTER (ID_NODE)[2] == '_')
#define VTABLE_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[1] == 'v' \
&& IDENTIFIER_POINTER (ID_NODE)[2] == 't' \
&& IDENTIFIER_POINTER (ID_NODE)[3] == JOINER)
#define VBASE_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[1] == 'v' \
&& IDENTIFIER_POINTER (ID_NODE)[2] == 'b' \
&& IDENTIFIER_POINTER (ID_NODE)[3] == JOINER)
#define TEMP_NAME_P(ID_NODE) (!strncmp (IDENTIFIER_POINTER (ID_NODE), AUTO_TEMP_NAME, sizeof (AUTO_TEMP_NAME)-1))
#define VFIELD_NAME_P(ID_NODE) (!strncmp (IDENTIFIER_POINTER (ID_NODE), VFIELD_NAME, sizeof(VFIELD_NAME)-1))
/* For anonymous aggregate types, we need some sort of name to
hold on to. In practice, this should not appear, but it should
not be harmful if it does. */
#define ANON_AGGRNAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == JOINER \
&& IDENTIFIER_POINTER (ID_NODE)[1] == '_')
#define ANON_PARMNAME_FORMAT "_%d"
#define ANON_PARMNAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == '_' \
&& IDENTIFIER_POINTER (ID_NODE)[1] <= '9')
#endif /* !defined(NO_DOLLAR_IN_LABEL) || !defined(NO_DOT_IN_LABEL) */
/* Store the vbase pointer field name for type TYPE into pointer BUF. */
#define FORMAT_VBASE_NAME(BUF,TYPE) do { \
char *wbuf = (char *) alloca (TYPE_ASSEMBLER_NAME_LENGTH (TYPE) \
+ sizeof (VBASE_NAME) + 1); \
sprintf (wbuf, VBASE_NAME_FORMAT, TYPE_ASSEMBLER_NAME_STRING (TYPE)); \
(BUF) = wbuf; \
} while (0)
/* Returns non-zero iff ID_NODE is an IDENTIFIER_NODE whose name is
`main'. */
#define MAIN_NAME_P(ID_NODE) \
(strcmp (IDENTIFIER_POINTER (ID_NODE), "main") == 0)
/* Returns non-zero iff NODE is a declaration for the global function
`main'. */
#define DECL_MAIN_P(NODE) \
(TREE_CODE (NODE) == FUNCTION_DECL \
&& DECL_LANGUAGE (NODE) == lang_c \
&& DECL_NAME (NODE) != NULL_TREE \
&& MAIN_NAME_P (DECL_NAME (NODE)))
/* Define the sets of attributes that member functions and baseclasses
can have. These are sensible combinations of {public,private,protected}
cross {virtual,non-virtual}. */
/* in class.c. */
extern tree access_default_node; /* 0 */
extern tree access_public_node; /* 1 */
extern tree access_protected_node; /* 2 */
extern tree access_private_node; /* 3 */
extern tree access_default_virtual_node; /* 4 */
extern tree access_public_virtual_node; /* 5 */
extern tree access_protected_virtual_node; /* 6 */
extern tree access_private_virtual_node; /* 7 */
/* Things for handling inline functions. */
struct pending_inline
{
struct pending_inline *next; /* pointer to next in chain */
int lineno; /* line number we got the text from */
char *filename; /* name of file we were processing */
tree fndecl; /* FUNCTION_DECL that brought us here */
int token; /* token we were scanning */
int token_value; /* value of token we were scanning (YYSTYPE) */
char *buf; /* pointer to character stream */
int len; /* length of stream */
unsigned int can_free : 1; /* free this after we're done with it? */
unsigned int deja_vu : 1; /* set iff we don't want to see it again. */
unsigned int interface : 2; /* 0=interface 1=unknown 2=implementation */
};
/* in method.c */
extern struct pending_inline *pending_inlines;
/* Positive values means that we cannot make optimizing assumptions about
`this'. Negative values means we know `this' to be of static type. */
extern int flag_this_is_variable;
/* Nonzero means generate 'rtti' that give run-time type information. */
extern int flag_rtti;
/* Nonzero means do emit exported implementations of functions even if
they can be inlined. */
extern int flag_implement_inlines;
/* Nonzero means templates obey #pragma interface and implementation. */
extern int flag_external_templates;
/* Nonzero means templates are emitted where they are instantiated. */
extern int flag_alt_external_templates;
/* Nonzero means implicit template instantiations are emitted. */
extern int flag_implicit_templates;
/* Nonzero if we want to emit defined symbols with common-like linkage as
weak symbols where possible, in order to conform to C++ semantics.
Otherwise, emit them as local symbols. */
extern int flag_weak;
/* Nonzero to enable experimental ABI changes. */
extern int flag_new_abi;
/* Nonzero to not ignore namespace std. */
extern int flag_honor_std;
/* Nonzero if we're done parsing and into end-of-file activities. */
extern int at_eof;
enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, OP_FLAG, TYPENAME_FLAG };
/* The following two can be derived from the previous one */
extern tree current_class_name; /* IDENTIFIER_NODE: name of current class */
/* Some macros for char-based bitfields. */
#define B_SET(a,x) (a[x>>3] |= (1 << (x&7)))
#define B_CLR(a,x) (a[x>>3] &= ~(1 << (x&7)))
#define B_TST(a,x) (a[x>>3] & (1 << (x&7)))
/* These are uses as bits in flags passed to build_method_call
to control its error reporting behavior.
LOOKUP_PROTECT means flag access violations.
LOOKUP_COMPLAIN mean complain if no suitable member function
matching the arguments is found.
LOOKUP_NORMAL is just a combination of these two.
LOOKUP_NONVIRTUAL means make a direct call to the member function found
LOOKUP_GLOBAL means search through the space of overloaded functions,
as well as the space of member functions.
LOOKUP_HAS_IN_CHARGE means that the "in charge" variable is already
in the parameter list.
LOOKUP_ONLYCONVERTING means that non-conversion constructors are not tried.
DIRECT_BIND means that if a temporary is created, it should be created so
that it lives as long as the current variable bindings; otherwise it
only lives until the end of the complete-expression.
LOOKUP_SPECULATIVELY means return NULL_TREE if we cannot find what we are
after. Note, LOOKUP_COMPLAIN is checked and error messages printed
before LOOKUP_SPECULATIVELY is checked.
LOOKUP_NO_CONVERSION means that user-defined conversions are not
permitted. Built-in conversions are permitted.
LOOKUP_DESTRUCTOR means explicit call to destructor.
LOOKUP_NO_TEMP_BIND means temporaries will not be bound to references.
These are used in global lookup to support elaborated types and
qualifiers.
LOOKUP_PREFER_TYPES means not to accept objects, and possibly namespaces.
LOOKUP_PREFER_NAMESPACES means not to accept objects, and possibly types.
LOOKUP_PREFER_BOTH means class-or-namespace-name.
LOOKUP_TEMPLATES_EXPECTED means that class templates also count
as types. */
#define LOOKUP_PROTECT (1)
#define LOOKUP_COMPLAIN (2)
#define LOOKUP_NORMAL (3)
/* #define LOOKUP_UNUSED (4) */
#define LOOKUP_NONVIRTUAL (8)
#define LOOKUP_GLOBAL (16)
#define LOOKUP_HAS_IN_CHARGE (32)
#define LOOKUP_SPECULATIVELY (64)
#define LOOKUP_ONLYCONVERTING (128)
#define DIRECT_BIND (256)
#define LOOKUP_NO_CONVERSION (512)
#define LOOKUP_DESTRUCTOR (512)
#define LOOKUP_NO_TEMP_BIND (1024)
#define LOOKUP_PREFER_TYPES (2048)
#define LOOKUP_PREFER_NAMESPACES (4096)
#define LOOKUP_PREFER_BOTH (6144)
#define LOOKUP_TEMPLATES_EXPECTED (8192)
#define LOOKUP_NAMESPACES_ONLY(f) \
(((f) & LOOKUP_PREFER_NAMESPACES) && !((f) & LOOKUP_PREFER_TYPES))
#define LOOKUP_TYPES_ONLY(f) \
(!((f) & LOOKUP_PREFER_NAMESPACES) && ((f) & LOOKUP_PREFER_TYPES))
#define LOOKUP_QUALIFIERS_ONLY(f) ((f) & LOOKUP_PREFER_BOTH)
/* These flags are used by the conversion code.
CONV_IMPLICIT : Perform implicit conversions (standard and user-defined).
CONV_STATIC : Perform the explicit conversions for static_cast.
CONV_CONST : Perform the explicit conversions for const_cast.
CONV_REINTERPRET: Perform the explicit conversions for reinterpret_cast.
CONV_PRIVATE : Perform upcasts to private bases.
CONV_FORCE_TEMP : Require a new temporary when converting to the same
aggregate type. */
#define CONV_IMPLICIT 1
#define CONV_STATIC 2
#define CONV_CONST 4
#define CONV_REINTERPRET 8
#define CONV_PRIVATE 16
/* #define CONV_NONCONVERTING 32 */
#define CONV_FORCE_TEMP 64
#define CONV_STATIC_CAST (CONV_IMPLICIT | CONV_STATIC | CONV_FORCE_TEMP)
#define CONV_OLD_CONVERT (CONV_IMPLICIT | CONV_STATIC | CONV_CONST \
| CONV_REINTERPRET)
#define CONV_C_CAST (CONV_IMPLICIT | CONV_STATIC | CONV_CONST \
| CONV_REINTERPRET | CONV_PRIVATE | CONV_FORCE_TEMP)
/* Used by build_expr_type_conversion to indicate which types are
acceptable as arguments to the expression under consideration. */
#define WANT_INT 1 /* integer types, including bool */
#define WANT_FLOAT 2 /* floating point types */
#define WANT_ENUM 4 /* enumerated types */
#define WANT_POINTER 8 /* pointer types */
#define WANT_NULL 16 /* null pointer constant */
#define WANT_ARITH (WANT_INT | WANT_FLOAT)
/* Used with comptypes, and related functions, to guide type
comparison. */
#define COMPARE_STRICT 0 /* Just check if the types are the
same. */
#define COMPARE_BASE 1 /* Check to see if the second type is
derived from the first, or if both
are pointers (or references) and
the types pointed to by the second
type is derived from the pointed to
by the first. */
#define COMPARE_RELAXED 2 /* Like COMPARE_DERIVED, but in
reverse. Also treat enmeration
types as the same as integer types
of the same width. */
#define COMPARE_REDECLARATION 4 /* The comparsion is being done when
another declaration of an existing
entity is seen. */
#define COMPARE_NO_ATTRIBUTES 8 /* The comparison should ignore
extra-linguistic type attributes. */
/* Used with push_overloaded_decl. */
#define PUSH_GLOBAL 0 /* Push the DECL into namespace scope,
regardless of the current scope. */
#define PUSH_LOCAL 1 /* Push the DECL into the current
scope. */
#define PUSH_USING 2 /* We are pushing this DECL as the
result of a using declaration. */
/* Returns nonzero iff TYPE1 and TYPE2 are the same type, in the usual
sense of `same'. */
#define same_type_p(type1, type2) \
comptypes ((type1), (type2), COMPARE_STRICT)
/* Returns nonzero iff TYPE1 and TYPE2 are the same type, or if TYPE2
is derived from TYPE1, or if TYPE2 is a pointer (reference) to a
class derived from the type pointed to (referred to) by TYPE1. */
#define same_or_base_type_p(type1, type2) \
comptypes ((type1), (type2), COMPARE_BASE)
/* These macros are used to access a TEMPLATE_PARM_INDEX. */
#define TEMPLATE_PARM_IDX(NODE) (((template_parm_index*) NODE)->index)
#define TEMPLATE_PARM_LEVEL(NODE) (((template_parm_index*) NODE)->level)
#define TEMPLATE_PARM_DESCENDANTS(NODE) (TREE_CHAIN (NODE))
#define TEMPLATE_PARM_ORIG_LEVEL(NODE) (((template_parm_index*) NODE)->orig_level)
#define TEMPLATE_PARM_DECL(NODE) (((template_parm_index*) NODE)->decl)
/* These macros are for accessing the fields of TEMPLATE_TYPE_PARM
and TEMPLATE_TEMPLATE_PARM nodes. */
#define TEMPLATE_TYPE_PARM_INDEX(NODE) (TYPE_FIELDS (NODE))
#define TEMPLATE_TYPE_IDX(NODE) \
(TEMPLATE_PARM_IDX (TEMPLATE_TYPE_PARM_INDEX (NODE)))
#define TEMPLATE_TYPE_LEVEL(NODE) \
(TEMPLATE_PARM_LEVEL (TEMPLATE_TYPE_PARM_INDEX (NODE)))
#define TEMPLATE_TYPE_ORIG_LEVEL(NODE) \
(TEMPLATE_PARM_ORIG_LEVEL (TEMPLATE_TYPE_PARM_INDEX (NODE)))
#define TEMPLATE_TYPE_DECL(NODE) \
(TEMPLATE_PARM_DECL (TEMPLATE_TYPE_PARM_INDEX (NODE)))
/* in lex.c */
/* Indexed by TREE_CODE, these tables give C-looking names to
operators represented by TREE_CODES. For example,
opname_tab[(int) MINUS_EXPR] == "-". */
extern char **opname_tab, **assignop_tab;
/* in call.c */
extern int check_dtor_name PROTO((tree, tree));
extern int get_arglist_len_in_bytes PROTO((tree));
extern tree build_vfield_ref PROTO((tree, tree));
extern tree resolve_scope_to_name PROTO((tree, tree));
extern tree build_scoped_method_call PROTO((tree, tree, tree, tree));
extern tree build_addr_func PROTO((tree));
extern tree build_call PROTO((tree, tree, tree));
extern tree build_method_call PROTO((tree, tree, tree, tree, int));
extern int null_ptr_cst_p PROTO((tree));
extern tree type_decays_to PROTO((tree));
extern tree build_user_type_conversion PROTO((tree, tree, int));
extern tree build_new_function_call PROTO((tree, tree));
extern tree build_new_op PROTO((enum tree_code, int, tree, tree, tree));
extern tree build_op_new_call PROTO((enum tree_code, tree, tree, int));
extern tree build_op_delete_call PROTO((enum tree_code, tree, tree, int, tree));
extern int can_convert PROTO((tree, tree));
extern int can_convert_arg PROTO((tree, tree, tree));
extern int enforce_access PROTO((tree, tree));
extern tree convert_default_arg PROTO((tree, tree, tree));
extern tree convert_arg_to_ellipsis PROTO((tree));
extern int is_properly_derived_from PROTO((tree, tree));
/* in class.c */
extern tree build_vbase_path PROTO((enum tree_code, tree, tree, tree, int));
extern tree build_vtbl_ref PROTO((tree, tree));
extern tree build_vfn_ref PROTO((tree *, tree, tree));
extern void add_method PROTO((tree, tree *, tree));
extern int currently_open_class PROTO((tree));
extern tree get_vfield_offset PROTO((tree));
extern void duplicate_tag_error PROTO((tree));
extern tree finish_struct PROTO((tree, tree, int));
extern void finish_struct_1 PROTO((tree, int));
extern int resolves_to_fixed_type_p PROTO((tree, int *));
extern void init_class_processing PROTO((void));
extern int is_empty_class PROTO((tree));
extern void pushclass PROTO((tree, int));
extern void popclass PROTO((void));
extern void push_nested_class PROTO((tree, int));
extern void pop_nested_class PROTO((void));
extern void push_lang_context PROTO((tree));
extern void pop_lang_context PROTO((void));
extern tree instantiate_type PROTO((tree, tree, int));
extern void print_class_statistics PROTO((void));
extern void push_cache_obstack PROTO((void));
extern unsigned HOST_WIDE_INT skip_rtti_stuff PROTO((tree *, tree));
extern void build_self_reference PROTO((void));
extern void warn_hidden PROTO((tree));
extern tree get_enclosing_class PROTO((tree));
int is_base_of_enclosing_class PROTO((tree, tree));
extern void unreverse_member_declarations PROTO((tree));
extern void invalidate_class_lookup_cache PROTO((void));
extern void maybe_note_name_used_in_class PROTO((tree, tree));
extern void note_name_declared_in_class PROTO((tree, tree));
/* in cvt.c */
extern tree convert_to_reference PROTO((tree, tree, int, int, tree));
extern tree convert_from_reference PROTO((tree));
extern tree convert_pointer_to_real PROTO((tree, tree));
extern tree convert_pointer_to PROTO((tree, tree));
extern tree ocp_convert PROTO((tree, tree, int, int));
extern tree cp_convert PROTO((tree, tree));
extern tree convert PROTO((tree, tree));
extern tree convert_force PROTO((tree, tree, int));
extern tree build_type_conversion PROTO((tree, tree, int));
extern tree build_expr_type_conversion PROTO((int, tree, int));
extern tree type_promotes_to PROTO((tree));
extern tree perform_qualification_conversions PROTO((tree, tree));
/* decl.c */
/* resume_binding_level */
extern void set_identifier_local_value PROTO((tree, tree));
extern int global_bindings_p PROTO((void));
extern int toplevel_bindings_p PROTO((void));
extern int namespace_bindings_p PROTO((void));
extern void keep_next_level PROTO((void));
extern int kept_level_p PROTO((void));
extern void declare_parm_level PROTO((void));
extern void declare_pseudo_global_level PROTO((void));
extern int pseudo_global_level_p PROTO((void));
extern void set_class_shadows PROTO((tree));
extern void pushlevel PROTO((int));
extern void note_level_for_for PROTO((void));
extern void pushlevel_temporary PROTO((int));
extern tree poplevel PROTO((int, int, int));
extern void resume_level PROTO((struct binding_level *));
extern void delete_block PROTO((tree));
extern void insert_block PROTO((tree));
extern void add_block_current_level PROTO((tree));
extern void set_block PROTO((tree));
extern void pushlevel_class PROTO((void));
extern void print_binding_stack PROTO((void));
extern void print_binding_level PROTO((struct binding_level *));
extern void push_namespace PROTO((tree));
extern void pop_namespace PROTO((void));
extern void maybe_push_to_top_level PROTO((int));
extern void push_to_top_level PROTO((void));
extern void pop_from_top_level PROTO((void));
extern tree identifier_type_value PROTO((tree));
extern void set_identifier_type_value PROTO((tree, tree));
extern void pop_everything PROTO((void));
extern void pushtag PROTO((tree, tree, int));
extern tree make_anon_name PROTO((void));
extern void clear_anon_tags PROTO((void));
extern int decls_match PROTO((tree, tree));
extern int duplicate_decls PROTO((tree, tree));
extern tree pushdecl PROTO((tree));
extern tree pushdecl_top_level PROTO((tree));
extern void pushdecl_class_level PROTO((tree));
#if 0
extern void pushdecl_nonclass_level PROTO((tree));
#endif
extern tree pushdecl_namespace_level PROTO((tree));
extern tree push_using_decl PROTO((tree, tree));
extern tree push_using_directive PROTO((tree));
extern void push_class_level_binding PROTO((tree, tree));
extern tree implicitly_declare PROTO((tree));
extern tree lookup_label PROTO((tree));
extern tree shadow_label PROTO((tree));
extern tree define_label PROTO((char *, int, tree));
extern void push_switch PROTO((void));
extern void pop_switch PROTO((void));
extern void define_case_label PROTO((void));
extern tree getdecls PROTO((void));
extern tree gettags PROTO((void));
#if 0
extern void set_current_level_tags_transparency PROTO((int));
#endif
extern tree binding_for_name PROTO((tree, tree));
extern tree namespace_binding PROTO((tree, tree));
extern void set_namespace_binding PROTO((tree, tree, tree));
extern tree lookup_namespace_name PROTO((tree, tree));
extern tree build_typename_type PROTO((tree, tree, tree, tree));
extern tree make_typename_type PROTO((tree, tree));
extern tree lookup_name_nonclass PROTO((tree));
extern tree lookup_function_nonclass PROTO((tree, tree));
extern tree lookup_name PROTO((tree, int));
extern tree lookup_name_current_level PROTO((tree));
extern tree lookup_type_current_level PROTO((tree));
extern tree lookup_name_namespace_only PROTO((tree));
extern void begin_only_namespace_names PROTO((void));
extern void end_only_namespace_names PROTO((void));
extern tree namespace_ancestor PROTO((tree, tree));
extern int lookup_using_namespace PROTO((tree,tree,tree,tree,int));
extern int qualified_lookup_using_namespace PROTO((tree,tree,tree,int));
extern tree auto_function PROTO((tree, tree, enum built_in_function));
extern void init_decl_processing PROTO((void));
extern int init_type_desc PROTO((void));
extern tree define_function
PROTO((const char *, tree, enum built_in_function,
void (*) (tree), const char *));
extern tree check_tag_decl PROTO((tree));
extern void shadow_tag PROTO((tree));
extern tree groktypename PROTO((tree));
extern tree start_decl PROTO((tree, tree, int, tree, tree));
extern void start_decl_1 PROTO((tree));
extern void cp_finish_decl PROTO((tree, tree, tree, int, int));
extern void finish_decl PROTO((tree, tree, tree));
extern void expand_static_init PROTO((tree, tree));
extern int complete_array_type PROTO((tree, tree, int));
extern tree build_ptrmemfunc_type PROTO((tree));
/* the grokdeclarator prototype is in decl.h */
extern int parmlist_is_exprlist PROTO((tree));
extern int copy_args_p PROTO((tree));
extern int grok_ctor_properties PROTO((tree, tree));
extern void grok_op_properties PROTO((tree, int, int));
extern tree xref_tag PROTO((tree, tree, int));
extern tree xref_tag_from_type PROTO((tree, tree, int));
extern void xref_basetypes PROTO((tree, tree, tree, tree));
extern tree start_enum PROTO((tree));
extern tree finish_enum PROTO((tree));
extern tree build_enumerator PROTO((tree, tree, tree));
extern int start_function PROTO((tree, tree, tree, int));
extern void expand_start_early_try_stmts PROTO((void));
extern void store_parm_decls PROTO((void));
extern void store_return_init PROTO((tree, tree));
extern void finish_function PROTO((int, int, int));
extern tree start_method PROTO((tree, tree, tree));
extern tree finish_method PROTO((tree));
extern void hack_incomplete_structures PROTO((tree));
extern tree maybe_build_cleanup_and_delete PROTO((tree));
extern tree maybe_build_cleanup PROTO((tree));
extern void cplus_expand_expr_stmt PROTO((tree));
extern void finish_stmt PROTO((void));
extern void push_cp_function_context PROTO((tree));
extern void pop_cp_function_context PROTO((tree));
extern int in_function_p PROTO((void));
extern void replace_defarg PROTO((tree, tree));
extern void print_other_binding_stack PROTO((struct binding_level *));
extern void revert_static_member_fn PROTO((tree*, tree*, tree*));
extern void fixup_anonymous_union PROTO((tree));
extern int check_static_variable_definition PROTO((tree, tree));
extern void push_local_binding PROTO((tree, tree, int));
extern int push_class_binding PROTO((tree, tree));
extern tree check_default_argument PROTO((tree, tree));
extern tree push_overloaded_decl PROTO((tree, int));
extern void clear_identifier_class_values PROTO((void));
extern void storetags PROTO((tree));
extern int vtable_decl_p PROTO((tree, void *));
extern int vtype_decl_p PROTO((tree, void *));
extern int sigtable_decl_p PROTO((tree, void *));
typedef int (*walk_globals_pred) PROTO((tree, void *));
typedef int (*walk_globals_fn) PROTO((tree *, void *));
extern int walk_globals PROTO((walk_globals_pred,
walk_globals_fn,
void *));
typedef int (*walk_namespaces_fn) PROTO((tree, void *));
extern int walk_namespaces PROTO((walk_namespaces_fn,
void *));
extern int wrapup_globals_for_namespace PROTO((tree, void *));
/* in decl2.c */
extern int check_java_method PROTO((tree));
extern int lang_decode_option PROTO((int, char **));
extern tree grok_method_quals PROTO((tree, tree, tree));
extern void warn_if_unknown_interface PROTO((tree));
extern void grok_x_components PROTO((tree));
extern void maybe_retrofit_in_chrg PROTO((tree));
extern void maybe_make_one_only PROTO((tree));
extern void grokclassfn PROTO((tree, tree, enum overload_flags, tree));
extern tree grok_alignof PROTO((tree));
extern tree grok_array_decl PROTO((tree, tree));
extern tree delete_sanity PROTO((tree, tree, int, int));
extern tree check_classfn PROTO((tree, tree));
extern void check_member_template PROTO((tree));
extern tree grokfield PROTO((tree, tree, tree, tree, tree));
extern tree grokbitfield PROTO((tree, tree, tree));
extern tree groktypefield PROTO((tree, tree));
extern tree grokoptypename PROTO((tree, tree));
extern int copy_assignment_arg_p PROTO((tree, int));
extern void cplus_decl_attributes PROTO((tree, tree, tree));
extern tree constructor_name_full PROTO((tree));
extern tree constructor_name PROTO((tree));
extern void setup_vtbl_ptr PROTO((void));
extern void mark_inline_for_output PROTO((tree));
extern void clear_temp_name PROTO((void));
extern tree get_temp_name PROTO((tree, int));
extern tree get_temp_regvar PROTO((tree, tree));
extern void finish_anon_union PROTO((tree));
extern tree finish_table PROTO((tree, tree, tree, int));
extern void finish_builtin_type PROTO((tree, const char *,
tree *, int, tree));
extern tree coerce_new_type PROTO((tree));
extern tree coerce_delete_type PROTO((tree));
extern void comdat_linkage PROTO((tree));
extern void import_export_class PROTO((tree));
extern void import_export_vtable PROTO((tree, tree, int));
extern void import_export_decl PROTO((tree));
extern tree build_cleanup PROTO((tree));
extern void finish_file PROTO((void));
extern tree reparse_absdcl_as_expr PROTO((tree, tree));
extern tree reparse_absdcl_as_casts PROTO((tree, tree));
extern tree build_expr_from_tree PROTO((tree));
extern tree reparse_decl_as_expr PROTO((tree, tree));
extern tree finish_decl_parsing PROTO((tree));
extern tree check_cp_case_value PROTO((tree));
extern void set_decl_namespace PROTO((tree, tree, int));
extern tree current_decl_namespace PROTO((void));
extern void push_decl_namespace PROTO((tree));
extern void pop_decl_namespace PROTO((void));
extern void push_scope PROTO((tree));
extern void pop_scope PROTO((tree));
extern void do_namespace_alias PROTO((tree, tree));
extern void do_toplevel_using_decl PROTO((tree));
extern void do_local_using_decl PROTO((tree));
extern tree do_class_using_decl PROTO((tree));
extern void do_using_directive PROTO((tree));
extern void check_default_args PROTO((tree));
extern void mark_used PROTO((tree));
extern tree handle_class_head PROTO((tree, tree, tree));
extern tree lookup_arg_dependent PROTO((tree, tree, tree));
extern void finish_static_data_member_decl PROTO((tree, tree, tree, int, int));
/* in errfn.c */
/* The cp_* functions aren't suitable for ATTRIBUTE_PRINTF. */
extern void cp_error PVPROTO((const char *, ...));
extern void cp_error_at PVPROTO((const char *, ...));
extern void cp_warning PVPROTO((const char *, ...));
extern void cp_warning_at PVPROTO((const char *, ...));
extern void cp_pedwarn PVPROTO((const char *, ...));
extern void cp_pedwarn_at PVPROTO((const char *, ...));
extern void cp_compiler_error PVPROTO((const char *, ...));
extern void cp_sprintf PVPROTO((const char *, ...));
extern void cp_deprecated PROTO((const char*));
/* in error.c */
extern void init_error PROTO((void));
extern char *fndecl_as_string PROTO((tree, int));
extern char *type_as_string PROTO((tree, int));
extern char *type_as_string_real PROTO((tree, int, int));
extern char *args_as_string PROTO((tree, int));
extern char *decl_as_string PROTO((tree, int));
extern char *expr_as_string PROTO((tree, int));
extern char *code_as_string PROTO((enum tree_code, int));
extern char *language_as_string PROTO((enum languages, int));
extern char *parm_as_string PROTO((int, int));
extern char *op_as_string PROTO((enum tree_code, int));
extern char *assop_as_string PROTO((enum tree_code, int));
extern char *cv_as_string PROTO((tree, int));
extern char *lang_decl_name PROTO((tree, int));
extern char *cp_file_of PROTO((tree));
extern int cp_line_of PROTO((tree));
/* in except.c */
extern void init_exception_processing PROTO((void));
extern void expand_start_catch_block PROTO((tree, tree));
extern void expand_end_catch_block PROTO((void));
extern void expand_builtin_throw PROTO((void));
extern void expand_start_eh_spec PROTO((void));
extern void expand_exception_blocks PROTO((void));
extern tree start_anon_func PROTO((void));
extern void end_anon_func PROTO((void));
extern void expand_throw PROTO((tree));
extern tree build_throw PROTO((tree));
extern void mark_all_runtime_matches PROTO((void));
/* in expr.c */
extern void init_cplus_expand PROTO((void));
extern void fixup_result_decl PROTO((tree, struct rtx_def *));
extern int extract_init PROTO((tree, tree));
extern void do_case PROTO((tree, tree));
/* friend.c */
extern int is_friend PROTO((tree, tree));
extern void make_friend_class PROTO((tree, tree));
extern void add_friend PROTO((tree, tree));
extern void add_friends PROTO((tree, tree, tree));
extern tree do_friend PROTO((tree, tree, tree, tree, tree, enum overload_flags, tree, int));
/* in init.c */
extern void init_init_processing PROTO((void));
extern void expand_direct_vtbls_init PROTO((tree, tree, int, int, tree));
extern void emit_base_init PROTO((tree, int));
extern void check_base_init PROTO((tree));
extern void expand_member_init PROTO((tree, tree, tree));
extern void expand_aggr_init PROTO((tree, tree, int));
extern int is_aggr_typedef PROTO((tree, int));
extern int is_aggr_type PROTO((tree, int));
extern tree get_aggr_from_typedef PROTO((tree, int));
extern tree get_type_value PROTO((tree));
extern tree build_member_call PROTO((tree, tree, tree));
extern tree build_offset_ref PROTO((tree, tree));
extern tree resolve_offset_ref PROTO((tree));
extern tree decl_constant_value PROTO((tree));
extern tree build_new PROTO((tree, tree, tree, int));
extern tree build_new_1 PROTO((tree));
extern tree expand_vec_init PROTO((tree, tree, tree, tree, int));
extern tree build_x_delete PROTO((tree, int, tree));
extern tree build_delete PROTO((tree, tree, tree, int, int));
extern tree build_vbase_delete PROTO((tree, tree));
extern tree build_vec_delete PROTO((tree, tree, tree, tree, int));
/* in input.c */
/* in lex.c */
extern char *file_name_nondirectory PROTO((const char *));
extern tree make_pointer_declarator PROTO((tree, tree));
extern tree make_reference_declarator PROTO((tree, tree));
extern tree make_call_declarator PROTO((tree, tree, tree, tree));
extern void set_quals_and_spec PROTO((tree, tree, tree));
extern char *operator_name_string PROTO((tree));
extern void lang_init PROTO((void));
extern void lang_finish PROTO((void));
#if 0
extern void reinit_lang_specific PROTO((void));
#endif
extern void reinit_parse_for_function PROTO((void));
extern void print_parse_statistics PROTO((void));
extern void extract_interface_info PROTO((void));
extern void do_pending_inlines PROTO((void));
extern void process_next_inline PROTO((tree));
extern struct pending_input *save_pending_input PROTO((void));
extern void restore_pending_input PROTO((struct pending_input *));
extern void yyungetc PROTO((int, int));
extern void reinit_parse_for_method PROTO((int, tree));
extern void reinit_parse_for_block PROTO((int, struct obstack *));
extern tree cons_up_default_function PROTO((tree, tree, int));
extern void check_for_missing_semicolon PROTO((tree));
extern void note_got_semicolon PROTO((tree));
extern void note_list_got_semicolon PROTO((tree));
extern void do_pending_lang_change PROTO((void));
extern int identifier_type PROTO((tree));
extern void see_typename PROTO((void));
extern tree do_identifier PROTO((tree, int, tree));
extern tree do_scoped_id PROTO((tree, int));
extern tree identifier_typedecl_value PROTO((tree));
extern int real_yylex PROTO((void));
extern int is_rid PROTO((tree));
extern tree build_lang_decl PROTO((enum tree_code, tree, tree));
extern void retrofit_lang_decl PROTO((tree));
extern tree build_lang_field_decl PROTO((enum tree_code, tree, tree));
extern void copy_lang_decl PROTO((tree));
extern tree make_lang_type PROTO((enum tree_code));
extern void dump_time_statistics PROTO((void));
extern void compiler_error PVPROTO((const char *, ...))
ATTRIBUTE_PRINTF_1;
extern void yyerror PROTO((const char *));
extern void clear_inline_text_obstack PROTO((void));
extern void maybe_snarf_defarg PROTO((void));
extern tree snarf_defarg PROTO((void));
extern void add_defarg_fn PROTO((tree));
extern void do_pending_defargs PROTO((void));
extern int identifier_type PROTO((tree));
extern void yyhook PROTO((int));
extern int cp_type_qual_from_rid PROTO((tree));
/* in method.c */
extern void init_method PROTO((void));
extern void do_inline_function_hair PROTO((tree, tree));
extern char *build_overload_name PROTO((tree, int, int));
extern tree build_static_name PROTO((tree, tree));
extern tree build_decl_overload PROTO((tree, tree, int));
extern tree build_decl_overload_real PROTO((tree, tree, tree, tree,
tree, int));
extern void set_mangled_name_for_decl PROTO((tree));
extern tree build_typename_overload PROTO((tree));
extern tree build_overload_with_type PROTO((tree, tree));
extern tree build_destructor_name PROTO((tree));
extern tree build_opfncall PROTO((enum tree_code, int, tree, tree, tree));
extern tree hack_identifier PROTO((tree, tree));
extern tree make_thunk PROTO((tree, int));
extern void emit_thunk PROTO((tree));
extern void synthesize_method PROTO((tree));
extern tree get_id_2 PROTO((char *, tree));
/* in pt.c */
extern void check_template_shadow PROTO ((tree));
extern tree innermost_args PROTO ((tree));
extern tree tsubst PROTO ((tree, tree, int, tree));
extern tree tsubst_expr PROTO ((tree, tree, int, tree));
extern tree tsubst_copy PROTO ((tree, tree, int, tree));
extern void maybe_begin_member_template_processing PROTO((tree));
extern void maybe_end_member_template_processing PROTO((void));
extern tree finish_member_template_decl PROTO((tree));
extern void begin_template_parm_list PROTO((void));
extern void begin_specialization PROTO((void));
extern void reset_specialization PROTO((void));
extern void end_specialization PROTO((void));
extern void begin_explicit_instantiation PROTO((void));
extern void end_explicit_instantiation PROTO((void));
extern tree check_explicit_specialization PROTO((tree, tree, int, int));
extern tree process_template_parm PROTO((tree, tree));
extern tree end_template_parm_list PROTO((tree));
extern void end_template_decl PROTO((void));
extern tree current_template_args PROTO((void));
extern tree push_template_decl PROTO((tree));
extern tree push_template_decl_real PROTO((tree, int));
extern void redeclare_class_template PROTO((tree, tree));
extern tree lookup_template_class PROTO((tree, tree, tree, tree, int));
extern tree lookup_template_function PROTO((tree, tree));
extern int uses_template_parms PROTO((tree));
extern tree instantiate_class_template PROTO((tree));
extern tree instantiate_template PROTO((tree, tree));
extern void overload_template_name PROTO((tree));
extern int fn_type_unification PROTO((tree, tree, tree, tree, tree, unification_kind_t));
struct tinst_level *tinst_for_decl PROTO((void));
extern void mark_decl_instantiated PROTO((tree, int));
extern int more_specialized PROTO((tree, tree, tree));
extern void mark_class_instantiated PROTO((tree, int));
extern void do_decl_instantiation PROTO((tree, tree, tree));
extern void do_type_instantiation PROTO((tree, tree));
extern tree instantiate_decl PROTO((tree));
extern tree do_poplevel PROTO((void));
extern tree get_bindings PROTO((tree, tree, tree));
/* CONT ... */
extern void add_tree PROTO((tree));
extern void begin_tree PROTO((void));
extern void end_tree PROTO((void));
extern void add_maybe_template PROTO((tree, tree));
extern void pop_tinst_level PROTO((void));
extern int more_specialized_class PROTO((tree, tree));
extern void do_pushlevel PROTO((void));
extern int is_member_template PROTO((tree));
extern int template_parms_equal PROTO((tree, tree));
extern int comp_template_parms PROTO((tree, tree));
extern int template_class_depth PROTO((tree));
extern int is_specialization_of PROTO((tree, tree));
extern int comp_template_args PROTO((tree, tree));
extern void maybe_process_partial_specialization PROTO((tree));
extern void maybe_check_template_type PROTO((tree));
extern tree most_specialized_instantiation PROTO((tree, tree));
extern void print_candidates PROTO((tree));
extern int instantiate_pending_templates PROTO((void));
extern int processing_specialization;
extern int processing_explicit_instantiation;
extern int processing_template_parmlist;
/* in repo.c */
extern void repo_template_used PROTO((tree));
extern void repo_template_instantiated PROTO((tree, int));
extern void init_repo PROTO((const char *));
extern void finish_repo PROTO((void));
/* in rtti.c */
extern void init_rtti_processing PROTO((void));
extern tree get_tinfo_fn_dynamic PROTO((tree));
extern tree build_typeid PROTO((tree));
extern tree build_x_typeid PROTO((tree));
extern tree get_tinfo_fn PROTO((tree));
extern tree get_typeid PROTO((tree));
extern tree get_typeid_1 PROTO((tree));
extern tree build_dynamic_cast PROTO((tree, tree));
extern void synthesize_tinfo_fn PROTO((tree));
/* in search.c */
extern int types_overlap_p PROTO((tree, tree));
extern tree get_vbase PROTO((tree, tree));
extern tree get_binfo PROTO((tree, tree, int));
extern int get_base_distance PROTO((tree, tree, int, tree *));
extern int accessible_p PROTO((tree, tree));
extern tree lookup_field PROTO((tree, tree, int, int));
extern int lookup_fnfields_1 PROTO((tree, tree));
extern tree lookup_fnfields PROTO((tree, tree, int));
extern tree lookup_member PROTO((tree, tree, int, int));
extern tree lookup_nested_tag PROTO((tree, tree));
extern tree get_matching_virtual PROTO((tree, tree, int));
extern tree get_abstract_virtuals PROTO((tree));
extern tree init_vbase_pointers PROTO((tree, tree));
extern void expand_indirect_vtbls_init PROTO((tree, tree, tree));
extern void clear_search_slots PROTO((tree));
extern tree get_vbase_types PROTO((tree));
extern void note_debug_info_needed PROTO((tree));
extern void push_class_decls PROTO((tree));
extern void pop_class_decls PROTO((void));
extern void unuse_fields PROTO((tree));
extern void print_search_statistics PROTO((void));
extern void init_search_processing PROTO((void));
extern void reinit_search_statistics PROTO((void));
extern tree current_scope PROTO((void));
extern tree lookup_conversions PROTO((tree));
extern tree binfo_for_vtable PROTO((tree));
extern tree dfs_walk PROTO((tree,
tree (*)(tree, void *),
tree (*) (tree, void *),
void *));
extern tree dfs_unmark PROTO((tree, void *));
extern tree markedp PROTO((tree, void *));
/* in semantics.c */
extern void finish_expr_stmt PROTO((tree));
extern tree begin_if_stmt PROTO((void));
extern void finish_if_stmt_cond PROTO((tree, tree));
extern tree finish_then_clause PROTO((tree));
extern void begin_else_clause PROTO((void));
extern void finish_else_clause PROTO((tree));
extern void finish_if_stmt PROTO((void));
extern tree begin_while_stmt PROTO((void));
extern void finish_while_stmt_cond PROTO((tree, tree));
extern void finish_while_stmt PROTO((tree));
extern tree begin_do_stmt PROTO((void));
extern void finish_do_body PROTO((tree));
extern void finish_do_stmt PROTO((tree, tree));
extern void finish_return_stmt PROTO((tree));
extern tree begin_for_stmt PROTO((void));
extern void finish_for_init_stmt PROTO((tree));
extern void finish_for_cond PROTO((tree, tree));
extern void finish_for_expr PROTO((tree, tree));
extern void finish_for_stmt PROTO((tree, tree));
extern void finish_break_stmt PROTO((void));
extern void finish_continue_stmt PROTO((void));
extern void begin_switch_stmt PROTO((void));
extern tree finish_switch_cond PROTO((tree));
extern void finish_switch_stmt PROTO((tree, tree));
extern void finish_case_label PROTO((tree, tree));
extern void finish_goto_stmt PROTO((tree));
extern tree begin_try_block PROTO((void));
extern void finish_try_block PROTO((tree));
extern void finish_handler_sequence PROTO((tree));
extern tree begin_handler PROTO((void));
extern void finish_handler_parms PROTO((tree));
extern void finish_handler PROTO((tree));
extern tree begin_compound_stmt PROTO((int));
extern tree finish_compound_stmt PROTO((int, tree));
extern void finish_asm_stmt PROTO((tree, tree, tree, tree, tree));
extern tree finish_parenthesized_expr PROTO((tree));
extern tree begin_stmt_expr PROTO((void));
extern tree finish_stmt_expr PROTO((tree, tree));
extern tree finish_call_expr PROTO((tree, tree, int));
extern tree finish_increment_expr PROTO((tree, enum tree_code));
extern tree finish_this_expr PROTO((void));
extern tree finish_object_call_expr PROTO((tree, tree, tree));
extern tree finish_qualified_object_call_expr PROTO((tree, tree, tree));
extern tree finish_pseudo_destructor_call_expr PROTO((tree, tree, tree));
extern tree finish_qualified_call_expr PROTO ((tree, tree));
extern tree finish_label_address_expr PROTO((tree));
extern tree finish_unary_op_expr PROTO((enum tree_code, tree));
extern tree finish_id_expr PROTO((tree));
extern int begin_new_placement PROTO((void));
extern tree finish_new_placement PROTO((tree, int));
extern int begin_function_definition PROTO((tree, tree));
extern tree begin_constructor_declarator PROTO((tree, tree));
extern tree finish_declarator PROTO((tree, tree, tree, tree, int));
extern void finish_translation_unit PROTO((void));
extern tree finish_template_type_parm PROTO((tree, tree));
extern tree finish_template_template_parm PROTO((tree, tree));
extern tree finish_parmlist PROTO((tree, int));
extern tree begin_class_definition PROTO((tree));
extern tree finish_class_definition PROTO((tree, tree, int, int));
extern void finish_default_args PROTO((void));
extern void begin_inline_definitions PROTO((void));
extern void finish_inline_definitions PROTO((void));
extern tree finish_member_class_template PROTO((tree));
extern void finish_template_decl PROTO((tree));
extern tree finish_template_type PROTO((tree, tree, int));
extern void enter_scope_of PROTO((tree));
extern tree finish_base_specifier PROTO((tree, tree, int));
extern void finish_member_declaration PROTO((tree));
extern void check_multiple_declarators PROTO((void));
extern tree finish_typeof PROTO((tree));
/* in sig.c */
extern tree build_signature_pointer_type PROTO((tree));
extern tree build_signature_reference_type PROTO((tree));
extern tree build_signature_pointer_constructor PROTO((tree, tree));
extern tree build_signature_method_call PROTO((tree, tree));
extern tree build_optr_ref PROTO((tree));
extern void append_signature_fields PROTO((tree));
/* in spew.c */
extern void init_spew PROTO((void));
extern int peekyylex PROTO((void));
extern int yylex PROTO((void));
extern tree arbitrate_lookup PROTO((tree, tree, tree));
/* in tree.c */
extern int pod_type_p PROTO((tree));
extern void unshare_base_binfos PROTO((tree));
extern int member_p PROTO((tree));
extern int real_lvalue_p PROTO((tree));
extern tree build_min PVPROTO((enum tree_code, tree, ...));
extern tree build_min_nt PVPROTO((enum tree_code, ...));
extern tree min_tree_cons PROTO((tree, tree, tree));
extern int lvalue_p PROTO((tree));
extern int lvalue_or_else PROTO((tree, const char *));
extern tree build_cplus_new PROTO((tree, tree));
extern tree get_target_expr PROTO((tree));
extern tree break_out_cleanups PROTO((tree));
extern tree break_out_calls PROTO((tree));
extern tree build_cplus_method_type PROTO((tree, tree, tree));
extern tree build_cplus_staticfn_type PROTO((tree, tree, tree));
extern tree build_cplus_array_type PROTO((tree, tree));
extern int layout_basetypes PROTO((tree, int));
extern tree build_vbase_pointer_fields PROTO((tree));
extern tree build_base_fields PROTO((tree));
extern tree hash_tree_cons PROTO((tree, tree, tree));
extern tree hash_tree_chain PROTO((tree, tree));
extern tree hash_chainon PROTO((tree, tree));
extern tree make_binfo PROTO((tree, tree, tree, tree));
extern tree binfo_value PROTO((tree, tree));
extern tree reverse_path PROTO((tree));
extern int count_functions PROTO((tree));
extern int is_overloaded_fn PROTO((tree));
extern tree get_first_fn PROTO((tree));
extern tree binding_init PROTO((struct tree_binding*));
extern int bound_pmf_p PROTO((tree));
extern tree ovl_cons PROTO((tree, tree));
extern tree scratch_ovl_cons PROTO((tree, tree));
extern int ovl_member PROTO((tree, tree));
extern tree build_overload PROTO((tree, tree));
extern tree fnaddr_from_vtable_entry PROTO((tree));
extern tree function_arg_chain PROTO((tree));
extern int promotes_to_aggr_type PROTO((tree, enum tree_code));
extern int is_aggr_type_2 PROTO((tree, tree));
extern char *lang_printable_name PROTO((tree, int));
extern tree build_exception_variant PROTO((tree, tree));
extern tree copy_template_template_parm PROTO((tree));
extern tree copy_to_permanent PROTO((tree));
extern tree permanent_p PROTO((tree));
extern void print_lang_statistics PROTO((void));
extern void __eprintf
PROTO((const char *, const char *, unsigned, const char *));
extern tree array_type_nelts_total PROTO((tree));
extern tree array_type_nelts_top PROTO((tree));
extern tree break_out_target_exprs PROTO((tree));
extern tree get_type_decl PROTO((tree));
extern tree vec_binfo_member PROTO((tree, tree));
extern tree hack_decl_function_context PROTO((tree));
extern tree decl_namespace_context PROTO((tree));
extern tree lvalue_type PROTO((tree));
extern tree error_type PROTO((tree));
extern tree make_temp_vec PROTO((int));
extern tree build_ptr_wrapper PROTO((void *));
extern tree build_expr_ptr_wrapper PROTO((void *));
extern tree build_int_wrapper PROTO((int));
extern tree build_srcloc_here PROTO((void));
extern int varargs_function_p PROTO((tree));
extern int really_overloaded_fn PROTO((tree));
extern int cp_tree_equal PROTO((tree, tree));
extern int can_free PROTO((struct obstack *, tree));
extern tree mapcar PROTO((tree, tree (*) (tree)));
extern tree no_linkage_check PROTO((tree));
extern void debug_binfo PROTO((tree));
extern void push_expression_obstack PROTO((void));
extern tree build_dummy_object PROTO((tree));
extern tree maybe_dummy_object PROTO((tree, tree *));
extern int is_dummy_object PROTO((tree));
extern tree search_tree PROTO((tree, tree (*)(tree)));
extern int cp_valid_lang_attribute PROTO((tree, tree, tree, tree));
extern tree make_ptrmem_cst PROTO((tree, tree));
#define scratchalloc expralloc
#define scratch_tree_cons expr_tree_cons
#define build_scratch_list build_expr_list
#define make_scratch_vec make_temp_vec
#define push_scratch_obstack push_expression_obstack
/* in typeck.c */
extern int string_conv_p PROTO((tree, tree, int));
extern tree condition_conversion PROTO((tree));
extern tree target_type PROTO((tree));
extern tree require_complete_type PROTO((tree));
extern tree require_complete_type_in_void PROTO((tree));
extern tree complete_type PROTO((tree));
extern tree complete_type_or_else PROTO((tree, tree));
extern int type_unknown_p PROTO((tree));
extern int fntype_p PROTO((tree));
extern tree commonparms PROTO((tree, tree));
extern tree original_type PROTO((tree));
extern tree common_type PROTO((tree, tree));
extern int compexcepttypes PROTO((tree, tree));
extern int comptypes PROTO((tree, tree, int));
extern int comp_target_types PROTO((tree, tree, int));
extern int compparms PROTO((tree, tree));
extern int comp_target_types PROTO((tree, tree, int));
extern int comp_cv_qualification PROTO((tree, tree));
extern int comp_cv_qual_signature PROTO((tree, tree));
extern int self_promoting_args_p PROTO((tree));
extern tree unsigned_type PROTO((tree));
extern tree signed_type PROTO((tree));
extern tree signed_or_unsigned_type PROTO((int, tree));
extern tree expr_sizeof PROTO((tree));
extern tree c_sizeof PROTO((tree));
extern tree c_sizeof_nowarn PROTO((tree));
extern tree c_alignof PROTO((tree));
extern tree inline_conversion PROTO((tree));
extern tree decay_conversion PROTO((tree));
extern tree default_conversion PROTO((tree));
extern tree build_object_ref PROTO((tree, tree, tree));
extern tree build_component_ref_1 PROTO((tree, tree, int));
extern tree build_component_ref PROTO((tree, tree, tree, int));
extern tree build_x_component_ref PROTO((tree, tree, tree, int));
extern tree build_x_indirect_ref PROTO((tree, const char *));
extern tree build_indirect_ref PROTO((tree, const char *));
extern tree build_array_ref PROTO((tree, tree));
extern tree build_x_function_call PROTO((tree, tree, tree));
extern tree get_member_function_from_ptrfunc PROTO((tree *, tree));
extern tree build_function_call_real PROTO((tree, tree, int, int));
extern tree build_function_call PROTO((tree, tree));
extern tree build_function_call_maybe PROTO((tree, tree));
extern tree convert_arguments PROTO((tree, tree, tree, int));
extern tree build_x_binary_op PROTO((enum tree_code, tree, tree));
extern tree build_binary_op PROTO((enum tree_code, tree, tree));
extern tree build_binary_op_nodefault PROTO((enum tree_code, tree, tree, enum tree_code));
extern tree build_x_unary_op PROTO((enum tree_code, tree));
extern tree build_unary_op PROTO((enum tree_code, tree, int));
extern tree unary_complex_lvalue PROTO((enum tree_code, tree));
extern int mark_addressable PROTO((tree));
extern tree build_x_conditional_expr PROTO((tree, tree, tree));
extern tree build_conditional_expr PROTO((tree, tree, tree));
extern tree build_x_compound_expr PROTO((tree));
extern tree build_compound_expr PROTO((tree));
extern tree build_static_cast PROTO((tree, tree));
extern tree build_reinterpret_cast PROTO((tree, tree));
extern tree build_const_cast PROTO((tree, tree));
extern tree build_c_cast PROTO((tree, tree));
extern tree build_x_modify_expr PROTO((tree, enum tree_code, tree));
extern tree build_modify_expr PROTO((tree, enum tree_code, tree));
extern tree convert_for_initialization PROTO((tree, tree, tree, int, const char *, tree, int));
extern void c_expand_asm_operands PROTO((tree, tree, tree, tree, int, char *, int));
extern void c_expand_return PROTO((tree));
extern tree c_expand_start_case PROTO((tree));
extern int comp_ptr_ttypes PROTO((tree, tree));
extern int ptr_reasonably_similar PROTO((tree, tree));
extern tree build_ptrmemfunc PROTO((tree, tree, int));
extern int cp_type_quals PROTO((tree));
extern int cp_has_mutable_p PROTO((tree));
extern int at_least_as_qualified_p PROTO((tree, tree));
extern int more_qualified_p PROTO((tree, tree));
extern tree build_ptrmemfunc1 PROTO((tree, tree, tree, tree, tree));
extern void expand_ptrmemfunc_cst PROTO((tree, tree *, tree *, tree *, tree *));
extern tree delta2_from_ptrmemfunc PROTO((tree));
extern tree pfn_from_ptrmemfunc PROTO((tree));
/* in typeck2.c */
extern tree error_not_base_type PROTO((tree, tree));
extern tree binfo_or_else PROTO((tree, tree));
extern void readonly_error PROTO((tree, const char *, int));
extern void abstract_virtuals_error PROTO((tree, tree));
extern void signature_error PROTO((tree, tree));
extern void incomplete_type_error PROTO((tree, tree));
extern void my_friendly_abort PROTO((int))
ATTRIBUTE_NORETURN;
extern void my_friendly_assert PROTO((int, int));
extern tree store_init_value PROTO((tree, tree));
extern tree digest_init PROTO((tree, tree, tree *));
extern tree build_scoped_ref PROTO((tree, tree));
extern tree build_x_arrow PROTO((tree));
extern tree build_m_component_ref PROTO((tree, tree));
extern tree build_functional_cast PROTO((tree, tree));
extern char *enum_name_string PROTO((tree, tree));
extern void report_case_error PROTO((int, tree, tree, tree));
extern void check_for_new_type PROTO((const char *, flagged_type_tree));
extern tree initializer_constant_valid_p PROTO((tree, tree));
/* in xref.c */
extern void GNU_xref_begin PROTO((const char *));
extern void GNU_xref_end PROTO((int));
extern void GNU_xref_file PROTO((const char *));
extern void GNU_xref_start_scope PROTO((HOST_WIDE_INT));
extern void GNU_xref_end_scope PROTO((HOST_WIDE_INT, HOST_WIDE_INT, int, int));
extern void GNU_xref_ref PROTO((tree, const char *));
extern void GNU_xref_decl PROTO((tree, tree));
extern void GNU_xref_call PROTO((tree, const char *));
extern void GNU_xref_function PROTO((tree, tree));
extern void GNU_xref_assign PROTO((tree));
extern void GNU_xref_hier PROTO((tree, tree, int, int, int));
extern void GNU_xref_member PROTO((tree, tree));
/* -- end of C++ */
#endif /* not _CP_TREE_H */
Index: head/contrib/gcc/cp/decl2.c
===================================================================
--- head/contrib/gcc/cp/decl2.c (revision 52750)
+++ head/contrib/gcc/cp/decl2.c (revision 52751)
@@ -1,5292 +1,5300 @@
/* Process declarations and variables for C compiler.
Copyright (C) 1988, 92-98, 1999 Free Software Foundation, Inc.
Hacked by Michael Tiemann (tiemann@cygnus.com)
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. */
/* Process declarations and symbol lookup for C front end.
Also constructs types; the standard scalar types at initialization,
and structure, union, array and enum types when they are declared. */
/* ??? not all decl nodes are given the most useful possible
line numbers. For example, the CONST_DECLs for enum values. */
#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "flags.h"
#include "cp-tree.h"
#include "decl.h"
#include "lex.h"
#include "output.h"
#include "except.h"
#include "expr.h"
#include "defaults.h"
#include "toplev.h"
#include "dwarf2out.h"
#include "dwarfout.h"
#include "splay-tree.h"
#include "varray.h"
#if USE_CPPLIB
#include "cpplib.h"
extern cpp_reader parse_in;
#endif
/* This structure contains information about the initializations
and/or destructions required for a particular priority level. */
typedef struct priority_info_s {
/* A label indicating where we should generate the next
initialization with this priority. */
rtx initialization_sequence;
/* A label indicating where we should generate the next destruction
with this priority. */
rtx destruction_sequence;
/* Non-zero if there have been any initializations at this priority
throughout the translation unit. */
int initializations_p;
/* Non-zero if there have been any destructions at this priority
throughout the translation unit. */
int destructions_p;
} *priority_info;
static tree get_sentry PROTO((tree));
static void mark_vtable_entries PROTO((tree));
static void grok_function_init PROTO((tree, tree));
static int finish_vtable_vardecl PROTO((tree *, void *));
static int prune_vtable_vardecl PROTO((tree *, void *));
static int finish_sigtable_vardecl PROTO((tree *, void *));
static int is_namespace_ancestor PROTO((tree, tree));
static void add_using_namespace PROTO((tree, tree, int));
static tree ambiguous_decl PROTO((tree, tree, tree,int));
static tree build_anon_union_vars PROTO((tree, tree*, int, int));
static int acceptable_java_type PROTO((tree));
static void output_vtable_inherit PROTO((tree));
static void start_objects PROTO((int, int));
static void finish_objects PROTO((int, int));
static tree merge_functions PROTO((tree, tree));
static tree decl_namespace PROTO((tree));
static tree validate_nonmember_using_decl PROTO((tree, tree *, tree *));
static void do_nonmember_using_decl PROTO((tree, tree, tree, tree,
tree *, tree *));
static void start_static_storage_duration_function PROTO((void));
static int generate_inits_for_priority PROTO((splay_tree_node, void *));
static void finish_static_storage_duration_function PROTO((void));
static priority_info get_priority_info PROTO((int));
static void do_static_initialization PROTO((tree, tree, tree, int));
static void do_static_destruction PROTO((tree, tree, int));
static void do_static_initialization_and_destruction PROTO((tree, tree));
static void generate_ctor_or_dtor_function PROTO((int, int));
static int generate_ctor_and_dtor_functions_for_priority
PROTO((splay_tree_node, void *));
extern int current_class_depth;
/* A list of virtual function tables we must make sure to write out. */
tree pending_vtables;
/* A list of static class variables. This is needed, because a
static class variable can be declared inside the class without
an initializer, and then initialized, staticly, outside the class. */
static varray_type pending_statics;
static size_t pending_statics_used;
/* A list of functions which were declared inline, but which we
may need to emit outline anyway. */
static varray_type saved_inlines;
static size_t saved_inlines_used;
/* Used to help generate temporary names which are unique within
a function. Reset to 0 by start_function. */
int temp_name_counter;
/* Same, but not reset. Local temp variables and global temp variables
can have the same name. */
static int global_temp_name_counter;
/* Flag used when debugging spew.c */
extern int spew_debug;
/* Nonzero if we're done parsing and into end-of-file activities. */
int at_eof;
/* Functions called along with real static constructors and destructors. */
tree static_ctors, static_dtors;
/* The current open namespace, and ::. */
tree current_namespace;
tree global_namespace;
/* The stack for namespaces of current declarations. */
static tree decl_namespace_list;
/* C (and C++) language-specific option variables. */
/* Nonzero means allow type mismatches in conditional expressions;
just make their values `void'. */
int flag_cond_mismatch;
/* Nonzero means give `double' the same size as `float'. */
int flag_short_double;
/* Nonzero means don't recognize the keyword `asm'. */
int flag_no_asm;
/* Nonzero means don't recognize any extension keywords. */
int flag_no_gnu_keywords;
/* Nonzero means don't recognize the non-ANSI builtin functions. */
int flag_no_builtin;
/* Nonzero means don't recognize the non-ANSI builtin functions.
-ansi sets this. */
int flag_no_nonansi_builtin;
/* Nonzero means do some things the same way PCC does. Only provided so
the compiler will link. */
int flag_traditional;
/* Nonzero means to treat bitfields as unsigned unless they say `signed'. */
int flag_signed_bitfields = 1;
/* Nonzero means enable obscure ANSI features and disable GNU extensions
that might cause ANSI-compliant code to be miscompiled. */
int flag_ansi;
/* Nonzero means do emit exported implementations of functions even if
they can be inlined. */
int flag_implement_inlines = 1;
/* Nonzero means do emit exported implementations of templates, instead of
multiple static copies in each file that needs a definition. */
int flag_external_templates;
/* Nonzero means that the decision to emit or not emit the implementation of a
template depends on where the template is instantiated, rather than where
it is defined. */
int flag_alt_external_templates;
/* Nonzero means that implicit instantiations will be emitted if needed. */
int flag_implicit_templates = 1;
/* Nonzero means that implicit instantiations of inline templates will be
emitted if needed, even if instantiations of non-inline templates
aren't. */
int flag_implicit_inline_templates = 1;
/* Nonzero means warn about implicit declarations. */
int warn_implicit = 1;
/* Nonzero means warn about usage of long long when `-pedantic'. */
int warn_long_long = 1;
/* Nonzero means warn when all ctors or dtors are private, and the class
has no friends. */
int warn_ctor_dtor_privacy = 1;
/* True if we want to implement vtables using "thunks".
The default is off. */
#ifndef DEFAULT_VTABLE_THUNKS
#define DEFAULT_VTABLE_THUNKS 0
#endif
int flag_vtable_thunks = DEFAULT_VTABLE_THUNKS;
/* True if we want to deal with repository information. */
int flag_use_repository;
/* Nonzero if we want to issue diagnostics that the standard says are not
required. */
int flag_optional_diags = 1;
/* Nonzero means give string constants the type `const char *', as mandated
by the standard. */
int flag_const_strings = 1;
/* Nonzero means warn about deprecated conversion from string constant to
`char *'. */
int warn_write_strings;
/* Nonzero means warn about pointer casts that can drop a type qualifier
from the pointer target type. */
int warn_cast_qual;
/* Nonzero means warn about sizeof(function) or addition/subtraction
of function pointers. */
int warn_pointer_arith = 1;
/* Nonzero means warn for any function def without prototype decl. */
int warn_missing_prototypes;
/* Nonzero means warn about multiple (redundant) decls for the same single
variable or function. */
int warn_redundant_decls;
/* Warn if initializer is not completely bracketed. */
int warn_missing_braces;
/* Warn about comparison of signed and unsigned values. */
int warn_sign_compare;
/* Warn about *printf or *scanf format/argument anomalies. */
int warn_format;
/* Warn about a subscript that has type char. */
int warn_char_subscripts;
/* Warn if a type conversion is done that might have confusing results. */
int warn_conversion;
/* Warn if adding () is suggested. */
int warn_parentheses;
/* Non-zero means warn in function declared in derived class has the
same name as a virtual in the base class, but fails to match the
type signature of any virtual function in the base class. */
int warn_overloaded_virtual;
/* Non-zero means warn when declaring a class that has a non virtual
destructor, when it really ought to have a virtual one. */
int warn_nonvdtor;
/* Non-zero means warn when a function is declared extern and later inline. */
int warn_extern_inline;
/* Non-zero means warn when the compiler will reorder code. */
int warn_reorder;
/* Non-zero means warn when synthesis behavior differs from Cfront's. */
int warn_synth;
/* Non-zero means warn when we convert a pointer to member function
into a pointer to (void or function). */
int warn_pmf2ptr = 1;
/* Nonzero means warn about violation of some Effective C++ style rules. */
int warn_ecpp;
/* Nonzero means warn where overload resolution chooses a promotion from
unsigned to signed over a conversion to an unsigned of the same size. */
int warn_sign_promo;
/* Nonzero means warn when an old-style cast is used. */
int warn_old_style_cast;
/* Warn about #pragma directives that are not recognised. */
int warn_unknown_pragmas; /* Tri state variable. */
/* Nonzero means warn about use of multicharacter literals. */
int warn_multichar = 1;
/* Nonzero means warn when non-templatized friend functions are
declared within a template */
int warn_nontemplate_friend = 1;
/* Nonzero means complain about deprecated features. */
int warn_deprecated = 1;
/* Nonzero means `$' can be in an identifier. */
#ifndef DOLLARS_IN_IDENTIFIERS
#define DOLLARS_IN_IDENTIFIERS 1
#endif
int dollars_in_ident = DOLLARS_IN_IDENTIFIERS;
/* Nonzero for -fno-strict-prototype switch: do not consider empty
argument prototype to mean function takes no arguments. */
int flag_strict_prototype = 2;
int strict_prototype = 1;
int strict_prototypes_lang_c, strict_prototypes_lang_cplusplus = 1;
/* Nonzero means that labels can be used as first-class objects */
int flag_labels_ok;
/* Non-zero means to collect statistics which might be expensive
and to print them when we are done. */
int flag_detailed_statistics;
/* C++ specific flags. */
/* Zero means that `this' is a *const. This gives nice behavior in the
2.0 world. 1 gives 1.2-compatible behavior. 2 gives Spring behavior.
-2 means we're constructing an object and it has fixed type. */
int flag_this_is_variable;
/* 3 means write out only virtuals function tables `defined'
in this implementation file.
0 means write out virtual function tables and give them
(C) static access (default). */
int write_virtuals;
/* Nonzero means we should attempt to elide constructors when possible. */
int flag_elide_constructors = 1;
/* Nonzero means recognize and handle signature language constructs. */
int flag_handle_signatures;
/* Nonzero means that member functions defined in class scope are
inline by default. */
int flag_default_inline = 1;
/* Controls whether compiler generates 'type descriptor' that give
run-time type information. */
int flag_rtti = 1;
/* Nonzero if we wish to output cross-referencing information
for the GNU class browser. */
extern int flag_gnu_xref;
/* Nonzero if we want to support huge (> 2^(sizeof(short)*8-1) bytes)
objects. */
int flag_huge_objects;
/* Nonzero if we want to conserve space in the .o files. We do this
by putting uninitialized data and runtime initialized data into
.common instead of .data at the expense of not flagging multiple
definitions. */
int flag_conserve_space;
/* Nonzero if we want to obey access control semantics. */
int flag_access_control = 1;
/* Nonzero if we want to understand the operator names, i.e. 'bitand'. */
int flag_operator_names;
/* Nonzero if we want to check the return value of new and avoid calling
constructors if it is a null pointer. */
int flag_check_new;
/* Nonzero if we want the new ANSI rules for pushing a new scope for `for'
initialization variables.
0: Old rules, set by -fno-for-scope.
2: New ANSI rules, set by -ffor-scope.
1: Try to implement new ANSI rules, but with backup compatibility
(and warnings). This is the default, for now. */
int flag_new_for_scope = 1;
/* Nonzero if we want to emit defined symbols with common-like linkage as
weak symbols where possible, in order to conform to C++ semantics.
Otherwise, emit them as local symbols. */
int flag_weak = 1;
/* Nonzero to enable experimental ABI changes. */
int flag_new_abi;
/* Nonzero to not ignore namespace std. */
int flag_honor_std;
/* Maximum template instantiation depth. Must be at least 17 for ANSI
compliance. */
int max_tinst_depth = 17;
/* The name-mangling scheme to use. Must be 1 or greater to support
template functions with identical types, but different template
arguments. */
int name_mangling_version = 2;
/* Nonzero means that guiding declarations are allowed. */
int flag_guiding_decls;
/* Nonzero if squashed mangling is to be performed.
This uses the B and K codes to reference previously seen class types
and class qualifiers. */
int flag_do_squangling;
/* Nonzero means output .vtable_{entry,inherit} for use in doing vtable gc. */
int flag_vtable_gc;
/* Nonzero means make the default pedwarns warnings instead of errors.
The value of this flag is ignored if -pedantic is specified. */
int flag_permissive;
/* Table of language-dependent -f options.
STRING is the option name. VARIABLE is the address of the variable.
ON_VALUE is the value to store in VARIABLE
if `-fSTRING' is seen as an option.
(If `-fno-STRING' is seen as an option, the opposite value is stored.) */
static struct { const char *string; int *variable; int on_value;}
lang_f_options[] =
{
/* C/C++ options. */
{"signed-char", &flag_signed_char, 1},
{"unsigned-char", &flag_signed_char, 0},
{"signed-bitfields", &flag_signed_bitfields, 1},
{"unsigned-bitfields", &flag_signed_bitfields, 0},
{"short-enums", &flag_short_enums, 1},
{"short-double", &flag_short_double, 1},
{"cond-mismatch", &flag_cond_mismatch, 1},
{"asm", &flag_no_asm, 0},
{"builtin", &flag_no_builtin, 0},
/* C++-only options. */
{"access-control", &flag_access_control, 1},
{"check-new", &flag_check_new, 1},
{"conserve-space", &flag_conserve_space, 1},
{"const-strings", &flag_const_strings, 1},
{"default-inline", &flag_default_inline, 1},
{"dollars-in-identifiers", &dollars_in_ident, 1},
{"elide-constructors", &flag_elide_constructors, 1},
{"external-templates", &flag_external_templates, 1},
{"for-scope", &flag_new_for_scope, 2},
{"gnu-keywords", &flag_no_gnu_keywords, 0},
{"handle-exceptions", &flag_exceptions, 1},
{"handle-signatures", &flag_handle_signatures, 1},
{"honor-std", &flag_honor_std, 1},
{"huge-objects", &flag_huge_objects, 1},
{"implement-inlines", &flag_implement_inlines, 1},
{"implicit-inline-templates", &flag_implicit_inline_templates, 1},
{"implicit-templates", &flag_implicit_templates, 1},
{"labels-ok", &flag_labels_ok, 1},
{"nonansi-builtins", &flag_no_nonansi_builtin, 0},
{"operator-names", &flag_operator_names, 1},
{"optional-diags", &flag_optional_diags, 1},
{"permissive", &flag_permissive, 1},
{"repo", &flag_use_repository, 1},
{"rtti", &flag_rtti, 1},
{"squangle", &flag_do_squangling, 1},
{"stats", &flag_detailed_statistics, 1},
{"strict-prototype", &flag_strict_prototype, 1},
{"this-is-variable", &flag_this_is_variable, 1},
{"vtable-gc", &flag_vtable_gc, 1},
{"vtable-thunks", &flag_vtable_thunks, 1},
{"weak", &flag_weak, 1},
{"xref", &flag_gnu_xref, 1}
};
/* Decode the string P as a language-specific option.
Return the number of strings consumed for a valid option.
Otherwise return 0. */
int
lang_decode_option (argc, argv)
int argc
#if !USE_CPPLIB
ATTRIBUTE_UNUSED
#endif
;
char **argv;
{
int strings_processed;
char *p = argv[0];
#if USE_CPPLIB
strings_processed = cpp_handle_option (&parse_in, argc, argv);
#else
strings_processed = 0;
#endif /* ! USE_CPPLIB */
if (!strcmp (p, "-ftraditional") || !strcmp (p, "-traditional"))
/* ignore */;
else if (p[0] == '-' && p[1] == 'f')
{
/* Some kind of -f option.
P's value is the option sans `-f'.
Search for it in the table of options. */
int found = 0;
size_t j;
p += 2;
/* Try special -f options. */
if (!strcmp (p, "handle-exceptions")
|| !strcmp (p, "no-handle-exceptions"))
warning ("-fhandle-exceptions has been renamed to -fexceptions (and is now on by default)");
if (!strcmp (p, "memoize-lookups")
|| !strcmp (p, "no-memoize-lookups")
|| !strcmp (p, "save-memoized")
|| !strcmp (p, "no-save-memoized")
|| !strcmp (p, "no-all-virtual")
|| !strcmp (p, "no-enum-int-equiv")
|| !strcmp (p, "nonnull-objects")
|| !strcmp (p, "ansi-overloading"))
{
/* ignore */
found = 1;
}
else if (!strcmp (p, "all-virtual")
|| !strcmp (p, "enum-int-equiv")
|| !strcmp (p, "no-nonnull-objects")
|| !strcmp (p, "no-ansi-overloading"))
{
warning ("-f%s is no longer supported", p);
found = 1;
}
else if (! strcmp (p, "alt-external-templates"))
{
flag_external_templates = 1;
flag_alt_external_templates = 1;
found = 1;
cp_deprecated ("-falt-external-templates");
}
else if (! strcmp (p, "no-alt-external-templates"))
{
flag_alt_external_templates = 0;
found = 1;
}
else if (!strcmp (p, "repo"))
{
flag_use_repository = 1;
flag_implicit_templates = 0;
found = 1;
}
else if (!strcmp (p, "guiding-decls"))
{
flag_guiding_decls = 1;
name_mangling_version = 0;
found = 1;
}
else if (!strcmp (p, "no-guiding-decls"))
{
flag_guiding_decls = 0;
found = 1;
}
else if (!strcmp (p, "this-is-variable"))
{
flag_this_is_variable = 1;
found = 1;
cp_deprecated ("-fthis-is-variable");
}
else if (!strcmp (p, "external-templates"))
{
flag_external_templates = 1;
found = 1;
cp_deprecated ("-fexternal-templates");
}
else if (!strcmp (p, "handle-signatures"))
{
flag_handle_signatures = 1;
found = 1;
cp_deprecated ("-fhandle-signatures");
}
else if (!strcmp (p, "new-abi"))
{
flag_new_abi = 1;
flag_do_squangling = 1;
flag_honor_std = 1;
flag_vtable_thunks = 1;
}
else if (!strcmp (p, "no-new-abi"))
{
flag_new_abi = 0;
flag_do_squangling = 0;
flag_honor_std = 0;
}
else if (!strncmp (p, "template-depth-", 15))
{
max_tinst_depth =
read_integral_parameter (p + 15, p - 2, max_tinst_depth);
}
else if (!strncmp (p, "name-mangling-version-", 22))
{
name_mangling_version =
read_integral_parameter (p + 22, p - 2, name_mangling_version);
}
else for (j = 0;
!found && j < sizeof (lang_f_options) / sizeof (lang_f_options[0]);
j++)
{
if (!strcmp (p, lang_f_options[j].string))
{
*lang_f_options[j].variable = lang_f_options[j].on_value;
/* A goto here would be cleaner,
but breaks the vax pcc. */
found = 1;
}
if (p[0] == 'n' && p[1] == 'o' && p[2] == '-'
&& ! strcmp (p+3, lang_f_options[j].string))
{
*lang_f_options[j].variable = ! lang_f_options[j].on_value;
found = 1;
}
}
return found;
}
else if (p[0] == '-' && p[1] == 'W')
{
int setting = 1;
/* The -W options control the warning behavior of the compiler. */
p += 2;
if (p[0] == 'n' && p[1] == 'o' && p[2] == '-')
setting = 0, p += 3;
if (!strcmp (p, "implicit"))
warn_implicit = setting;
else if (!strcmp (p, "long-long"))
warn_long_long = setting;
else if (!strcmp (p, "return-type"))
warn_return_type = setting;
else if (!strcmp (p, "ctor-dtor-privacy"))
warn_ctor_dtor_privacy = setting;
else if (!strcmp (p, "write-strings"))
warn_write_strings = setting;
else if (!strcmp (p, "cast-qual"))
warn_cast_qual = setting;
else if (!strcmp (p, "char-subscripts"))
warn_char_subscripts = setting;
else if (!strcmp (p, "pointer-arith"))
warn_pointer_arith = setting;
else if (!strcmp (p, "missing-prototypes"))
warn_missing_prototypes = setting;
else if (!strcmp (p, "redundant-decls"))
warn_redundant_decls = setting;
else if (!strcmp (p, "missing-braces"))
warn_missing_braces = setting;
else if (!strcmp (p, "sign-compare"))
warn_sign_compare = setting;
else if (!strcmp (p, "format"))
warn_format = setting;
else if (!strcmp (p, "conversion"))
warn_conversion = setting;
else if (!strcmp (p, "parentheses"))
warn_parentheses = setting;
else if (!strcmp (p, "non-virtual-dtor"))
warn_nonvdtor = setting;
else if (!strcmp (p, "extern-inline"))
warn_extern_inline = setting;
else if (!strcmp (p, "reorder"))
warn_reorder = setting;
else if (!strcmp (p, "synth"))
warn_synth = setting;
else if (!strcmp (p, "pmf-conversions"))
warn_pmf2ptr = setting;
else if (!strcmp (p, "effc++"))
warn_ecpp = setting;
else if (!strcmp (p, "sign-promo"))
warn_sign_promo = setting;
else if (!strcmp (p, "old-style-cast"))
warn_old_style_cast = setting;
else if (!strcmp (p, "overloaded-virtual"))
warn_overloaded_virtual = setting;
else if (!strcmp (p, "multichar"))
warn_multichar = setting;
else if (!strcmp (p, "unknown-pragmas"))
/* Set to greater than 1, so that even unknown pragmas in
system headers will be warned about. */
warn_unknown_pragmas = setting * 2;
else if (!strcmp (p, "non-template-friend"))
warn_nontemplate_friend = setting;
else if (!strcmp (p, "deprecated"))
warn_deprecated = setting;
else if (!strcmp (p, "comment"))
; /* cpp handles this one. */
else if (!strcmp (p, "comments"))
; /* cpp handles this one. */
else if (!strcmp (p, "trigraphs"))
; /* cpp handles this one. */
else if (!strcmp (p, "import"))
; /* cpp handles this one. */
else if (!strcmp (p, "all"))
{
warn_return_type = setting;
warn_unused = setting;
warn_implicit = setting;
warn_switch = setting;
warn_format = setting;
warn_parentheses = setting;
warn_missing_braces = setting;
warn_sign_compare = setting;
warn_multichar = setting;
/* We save the value of warn_uninitialized, since if they put
-Wuninitialized on the command line, we need to generate a
warning about not using it without also specifying -O. */
if (warn_uninitialized != 1)
warn_uninitialized = (setting ? 2 : 0);
/* Only warn about unknown pragmas that are not in system
headers. */
warn_unknown_pragmas = 1;
/* C++-specific warnings. */
warn_ctor_dtor_privacy = setting;
warn_nonvdtor = setting;
warn_reorder = setting;
warn_nontemplate_friend = setting;
}
else return strings_processed;
}
else if (!strcmp (p, "-ansi"))
flag_no_nonansi_builtin = 1, flag_ansi = 1,
flag_no_gnu_keywords = 1, flag_operator_names = 1;
#ifdef SPEW_DEBUG
/* Undocumented, only ever used when you're invoking cc1plus by hand, since
it's probably safe to assume no sane person would ever want to use this
under normal circumstances. */
else if (!strcmp (p, "-spew-debug"))
spew_debug = 1;
#endif
else
return strings_processed;
return 1;
}
/* Incorporate `const' and `volatile' qualifiers for member functions.
FUNCTION is a TYPE_DECL or a FUNCTION_DECL.
QUALS is a list of qualifiers. */
tree
grok_method_quals (ctype, function, quals)
tree ctype, function, quals;
{
tree fntype = TREE_TYPE (function);
tree raises = TYPE_RAISES_EXCEPTIONS (fntype);
int type_quals = TYPE_UNQUALIFIED;
int dup_quals = TYPE_UNQUALIFIED;
do
{
int tq = cp_type_qual_from_rid (TREE_VALUE (quals));
if (type_quals & tq)
dup_quals |= tq;
else
type_quals |= tq;
quals = TREE_CHAIN (quals);
}
while (quals);
if (dup_quals != TYPE_UNQUALIFIED)
cp_error ("duplicate type qualifiers in %s declaration",
TREE_CODE (function) == FUNCTION_DECL
? "member function" : "type");
ctype = cp_build_qualified_type (ctype, type_quals);
fntype = build_cplus_method_type (ctype, TREE_TYPE (fntype),
(TREE_CODE (fntype) == METHOD_TYPE
? TREE_CHAIN (TYPE_ARG_TYPES (fntype))
: TYPE_ARG_TYPES (fntype)));
if (raises)
fntype = build_exception_variant (fntype, raises);
TREE_TYPE (function) = fntype;
return ctype;
}
/* Warn when -fexternal-templates is used and #pragma
interface/implementation is not used all the times it should be,
inform the user. */
void
warn_if_unknown_interface (decl)
tree decl;
{
static int already_warned = 0;
if (already_warned++)
return;
if (flag_alt_external_templates)
{
struct tinst_level *til = tinst_for_decl ();
int sl = lineno;
char *sf = input_filename;
if (til)
{
lineno = til->line;
input_filename = til->file;
}
cp_warning ("template `%#D' instantiated in file without #pragma interface",
decl);
lineno = sl;
input_filename = sf;
}
else
cp_warning_at ("template `%#D' defined in file without #pragma interface",
decl);
}
/* A subroutine of the parser, to handle a component list. */
void
grok_x_components (specs)
tree specs;
{
struct pending_inline **p;
tree t;
specs = strip_attrs (specs);
check_tag_decl (specs);
t = groktypename (build_decl_list (specs, NULL_TREE));
/* The only case where we need to do anything additional here is an
anonymous union field, e.g.: `struct S { union { int i; }; };'. */
if (t == NULL_TREE || !ANON_UNION_TYPE_P (t))
return;
fixup_anonymous_union (t);
finish_member_declaration (build_lang_field_decl (FIELD_DECL,
NULL_TREE,
t));
/* Ignore any inline function definitions in the anonymous union
since an anonymous union may not have function members. */
p = &pending_inlines;
for (; *p; *p = (*p)->next)
if (DECL_CONTEXT ((*p)->fndecl) != t)
break;
}
/* Constructors for types with virtual baseclasses need an "in-charge" flag
saying whether this constructor is responsible for initialization of
virtual baseclasses or not. All destructors also need this "in-charge"
flag, which additionally determines whether or not the destructor should
free the memory for the object.
This function adds the "in-charge" flag to member function FN if
appropriate. It is called from grokclassfn and tsubst.
FN must be either a constructor or destructor. */
void
maybe_retrofit_in_chrg (fn)
tree fn;
{
tree basetype, arg_types, parms, parm, fntype;
if (DECL_CONSTRUCTOR_P (fn)
&& TYPE_USES_VIRTUAL_BASECLASSES (DECL_CLASS_CONTEXT (fn))
&& ! DECL_CONSTRUCTOR_FOR_VBASE_P (fn))
/* OK */;
else if (! DECL_CONSTRUCTOR_P (fn)
&& TREE_CHAIN (DECL_ARGUMENTS (fn)) == NULL_TREE)
/* OK */;
else
return;
if (DECL_CONSTRUCTOR_P (fn))
DECL_CONSTRUCTOR_FOR_VBASE_P (fn) = 1;
/* First add it to DECL_ARGUMENTS... */
parm = build_decl (PARM_DECL, in_charge_identifier, integer_type_node);
/* Mark the artificial `__in_chrg' parameter as "artificial". */
SET_DECL_ARTIFICIAL (parm);
DECL_ARG_TYPE (parm) = integer_type_node;
TREE_READONLY (parm) = 1;
parms = DECL_ARGUMENTS (fn);
TREE_CHAIN (parm) = TREE_CHAIN (parms);
TREE_CHAIN (parms) = parm;
/* ...and then to TYPE_ARG_TYPES. */
arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
basetype = TREE_TYPE (TREE_VALUE (arg_types));
arg_types = hash_tree_chain (integer_type_node, TREE_CHAIN (arg_types));
fntype = build_cplus_method_type (basetype, TREE_TYPE (TREE_TYPE (fn)),
arg_types);
if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)))
fntype = build_exception_variant (fntype,
TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)));
TREE_TYPE (fn) = fntype;
}
/* Classes overload their constituent function names automatically.
When a function name is declared in a record structure,
its name is changed to it overloaded name. Since names for
constructors and destructors can conflict, we place a leading
'$' for destructors.
CNAME is the name of the class we are grokking for.
FUNCTION is a FUNCTION_DECL. It was created by `grokdeclarator'.
FLAGS contains bits saying what's special about today's
arguments. 1 == DESTRUCTOR. 2 == OPERATOR.
If FUNCTION is a destructor, then we must add the `auto-delete' field
as a second parameter. There is some hair associated with the fact
that we must "declare" this variable in the manner consistent with the
way the rest of the arguments were declared.
QUALS are the qualifiers for the this pointer. */
void
grokclassfn (ctype, function, flags, quals)
tree ctype, function;
enum overload_flags flags;
tree quals;
{
tree fn_name = DECL_NAME (function);
tree arg_types;
tree parm;
tree qualtype;
if (fn_name == NULL_TREE)
{
error ("name missing for member function");
fn_name = get_identifier ("<anonymous>");
DECL_NAME (function) = fn_name;
}
if (quals)
qualtype = grok_method_quals (ctype, function, quals);
else
qualtype = ctype;
arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
if (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE)
{
/* Must add the class instance variable up front. */
/* Right now we just make this a pointer. But later
we may wish to make it special. */
tree type = TREE_VALUE (arg_types);
int constp = 1;
if ((flag_this_is_variable > 0)
&& (flags == DTOR_FLAG || DECL_CONSTRUCTOR_P (function)))
constp = 0;
parm = build_decl (PARM_DECL, this_identifier, type);
/* Mark the artificial `this' parameter as "artificial". */
SET_DECL_ARTIFICIAL (parm);
DECL_ARG_TYPE (parm) = type;
/* We can make this a register, so long as we don't
accidentally complain if someone tries to take its address. */
DECL_REGISTER (parm) = 1;
if (constp)
TREE_READONLY (parm) = 1;
TREE_CHAIN (parm) = last_function_parms;
last_function_parms = parm;
}
DECL_ARGUMENTS (function) = last_function_parms;
/* First approximations. */
DECL_CONTEXT (function) = ctype;
DECL_CLASS_CONTEXT (function) = ctype;
if (flags == DTOR_FLAG || DECL_CONSTRUCTOR_P (function))
{
maybe_retrofit_in_chrg (function);
arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
}
if (flags == DTOR_FLAG)
{
DECL_ASSEMBLER_NAME (function) = build_destructor_name (ctype);
TYPE_HAS_DESTRUCTOR (ctype) = 1;
}
else
set_mangled_name_for_decl (function);
}
/* Work on the expr used by alignof (this is only called by the parser). */
tree
grok_alignof (expr)
tree expr;
{
tree best, t;
int bestalign;
if (processing_template_decl)
return build_min (ALIGNOF_EXPR, sizetype, expr);
if (TREE_CODE (expr) == COMPONENT_REF
&& DECL_C_BIT_FIELD (TREE_OPERAND (expr, 1)))
error ("`__alignof__' applied to a bit-field");
if (TREE_CODE (expr) == INDIRECT_REF)
{
best = t = TREE_OPERAND (expr, 0);
bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
while (TREE_CODE (t) == NOP_EXPR
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
{
int thisalign;
t = TREE_OPERAND (t, 0);
thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
if (thisalign > bestalign)
best = t, bestalign = thisalign;
}
return c_alignof (TREE_TYPE (TREE_TYPE (best)));
}
else
{
/* ANSI says arrays and fns are converted inside comma.
But we can't convert them in build_compound_expr
because that would break commas in lvalues.
So do the conversion here if operand was a comma. */
if (TREE_CODE (expr) == COMPOUND_EXPR
&& (TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE))
expr = default_conversion (expr);
return c_alignof (TREE_TYPE (expr));
}
}
/* Create an ARRAY_REF, checking for the user doing things backwards
along the way. */
tree
grok_array_decl (array_expr, index_exp)
tree array_expr, index_exp;
{
tree type = TREE_TYPE (array_expr);
tree p1, p2, i1, i2;
if (type == error_mark_node || index_exp == error_mark_node)
return error_mark_node;
if (processing_template_decl)
return build_min (ARRAY_REF, type ? TREE_TYPE (type) : NULL_TREE,
array_expr, index_exp);
if (type == NULL_TREE)
{
/* Something has gone very wrong. Assume we are mistakenly reducing
an expression instead of a declaration. */
error ("parser may be lost: is there a '{' missing somewhere?");
return NULL_TREE;
}
if (TREE_CODE (type) == OFFSET_TYPE
|| TREE_CODE (type) == REFERENCE_TYPE)
type = TREE_TYPE (type);
/* If they have an `operator[]', use that. */
if (IS_AGGR_TYPE (type) || IS_AGGR_TYPE (TREE_TYPE (index_exp)))
return build_opfncall (ARRAY_REF, LOOKUP_NORMAL,
array_expr, index_exp, NULL_TREE);
/* Otherwise, create an ARRAY_REF for a pointer or array type. It
is a little-known fact that, if `a' is an array and `i' is an
int, you can write `i[a]', which means the same thing as `a[i]'. */
if (TREE_CODE (type) == ARRAY_TYPE)
p1 = array_expr;
else
p1 = build_expr_type_conversion (WANT_POINTER, array_expr, 0);
if (TREE_CODE (TREE_TYPE (index_exp)) == ARRAY_TYPE)
p2 = index_exp;
else
p2 = build_expr_type_conversion (WANT_POINTER, index_exp, 0);
i1 = build_expr_type_conversion (WANT_INT | WANT_ENUM, array_expr, 0);
i2 = build_expr_type_conversion (WANT_INT | WANT_ENUM, index_exp, 0);
if ((p1 && i2) && (i1 && p2))
error ("ambiguous conversion for array subscript");
if (p1 && i2)
array_expr = p1, index_exp = i2;
else if (i1 && p2)
array_expr = p2, index_exp = i1;
else
{
cp_error ("invalid types `%T[%T]' for array subscript",
type, TREE_TYPE (index_exp));
return error_mark_node;
}
if (array_expr == error_mark_node || index_exp == error_mark_node)
error ("ambiguous conversion for array subscript");
return build_array_ref (array_expr, index_exp);
}
/* Given the cast expression EXP, checking out its validity. Either return
an error_mark_node if there was an unavoidable error, return a cast to
void for trying to delete a pointer w/ the value 0, or return the
call to delete. If DOING_VEC is 1, we handle things differently
for doing an array delete. If DOING_VEC is 2, they gave us the
array size as an argument to delete.
Implements ARM $5.3.4. This is called from the parser. */
tree
delete_sanity (exp, size, doing_vec, use_global_delete)
tree exp, size;
int doing_vec, use_global_delete;
{
tree t, type;
/* For a regular vector delete (aka, no size argument) we will pass
this down as a NULL_TREE into build_vec_delete. */
tree maxindex = NULL_TREE;
if (exp == error_mark_node)
return exp;
if (processing_template_decl)
{
t = build_min (DELETE_EXPR, void_type_node, exp, size);
DELETE_EXPR_USE_GLOBAL (t) = use_global_delete;
DELETE_EXPR_USE_VEC (t) = doing_vec;
return t;
}
if (TREE_CODE (exp) == OFFSET_REF)
exp = resolve_offset_ref (exp);
exp = convert_from_reference (exp);
t = stabilize_reference (exp);
t = build_expr_type_conversion (WANT_POINTER, t, 1);
if (t == NULL_TREE || t == error_mark_node)
{
cp_error ("type `%#T' argument given to `delete', expected pointer",
TREE_TYPE (exp));
return error_mark_node;
}
if (doing_vec == 2)
{
maxindex = build_binary_op (MINUS_EXPR, size, integer_one_node);
pedwarn ("anachronistic use of array size in vector delete");
}
type = TREE_TYPE (t);
/* As of Valley Forge, you can delete a pointer to const. */
/* You can't delete functions. */
if (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
{
error ("cannot delete a function");
return error_mark_node;
}
/* Deleting ptr to void is undefined behaviour [expr.delete/3]. */
if (TREE_CODE (TREE_TYPE (type)) == VOID_TYPE)
cp_warning ("`%T' is not a pointer-to-object type", type);
/* An array can't have been allocated by new, so complain. */
if (TREE_CODE (t) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (t, 0)) == VAR_DECL
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == ARRAY_TYPE)
cp_warning ("deleting array `%#D'", TREE_OPERAND (t, 0));
/* Deleting a pointer with the value zero is valid and has no effect. */
if (integer_zerop (t))
return build1 (NOP_EXPR, void_type_node, t);
if (doing_vec)
return build_vec_delete (t, maxindex, integer_one_node,
integer_zero_node, use_global_delete);
else
{
if (IS_AGGR_TYPE (TREE_TYPE (type))
&& TYPE_GETS_REG_DELETE (TREE_TYPE (type)))
{
/* Only do access checking here; we'll be calling op delete
from the destructor. */
tree tmp = build_op_delete_call (DELETE_EXPR, t, size_zero_node,
LOOKUP_NORMAL, NULL_TREE);
if (tmp == error_mark_node)
return error_mark_node;
}
return build_delete (type, t, integer_three_node,
LOOKUP_NORMAL, use_global_delete);
}
}
/* Report an error if the indicated template declaration is not the
sort of thing that should be a member template. */
void
check_member_template (tmpl)
tree tmpl;
{
tree decl;
my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 0);
decl = DECL_TEMPLATE_RESULT (tmpl);
if (TREE_CODE (decl) == FUNCTION_DECL
|| (TREE_CODE (decl) == TYPE_DECL
&& IS_AGGR_TYPE (TREE_TYPE (decl))))
{
if (current_function_decl)
/* 14.5.2.2 [temp.mem]
A local class shall not have member templates. */
cp_error ("declaration of member template `%#D' in local class",
decl);
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_VIRTUAL_P (decl))
{
/* 14.5.2.3 [temp.mem]
A member function template shall not be virtual. */
cp_error
("invalid use of `virtual' in template declaration of `%#D'",
decl);
DECL_VIRTUAL_P (decl) = 0;
}
/* The debug-information generating code doesn't know what to do
with member templates. */
DECL_IGNORED_P (tmpl) = 1;
}
else
cp_error ("template declaration of `%#D'", decl);
}
/* Return true iff TYPE is a valid Java parameter or return type. */
static int
acceptable_java_type (type)
tree type;
{
if (TREE_CODE (type) == VOID_TYPE || TYPE_FOR_JAVA (type))
return 1;
if (TREE_CODE (type) == POINTER_TYPE)
{
type = TREE_TYPE (type);
if (TREE_CODE (type) == RECORD_TYPE)
{
tree args; int i;
if (! TYPE_FOR_JAVA (type))
return 0;
if (! CLASSTYPE_TEMPLATE_INFO (type))
return 1;
args = CLASSTYPE_TI_ARGS (type);
i = TREE_VEC_LENGTH (args);
while (--i >= 0)
{
type = TREE_VEC_ELT (args, i);
if (TREE_CODE (type) == POINTER_TYPE)
type = TREE_TYPE (type);
if (! TYPE_FOR_JAVA (type))
return 0;
}
return 1;
}
}
return 0;
}
/* For a METHOD in a Java class CTYPE, return 1 if
the parameter and return types are valid Java types.
Otherwise, print appropriate error messages, and return 0. */
int
check_java_method (method)
tree method;
{
int jerr = 0;
tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (method));
tree ret_type = TREE_TYPE (TREE_TYPE (method));
if (! acceptable_java_type (ret_type))
{
cp_error ("Java method '%D' has non-Java return type `%T'",
method, ret_type);
jerr++;
}
for (; arg_types != NULL_TREE; arg_types = TREE_CHAIN (arg_types))
{
tree type = TREE_VALUE (arg_types);
if (! acceptable_java_type (type))
{
cp_error ("Java method '%D' has non-Java parameter type `%T'",
method, type);
jerr++;
}
}
return jerr ? 0 : 1;
}
/* Sanity check: report error if this function FUNCTION is not
really a member of the class (CTYPE) it is supposed to belong to.
CNAME is the same here as it is for grokclassfn above. */
tree
check_classfn (ctype, function)
tree ctype, function;
{
tree fn_name = DECL_NAME (function);
tree fndecl, fndecls;
tree method_vec = CLASSTYPE_METHOD_VEC (complete_type (ctype));
tree *methods = 0;
tree *end = 0;
if (DECL_USE_TEMPLATE (function)
&& is_member_template (DECL_TI_TEMPLATE (function)))
/* Since this is a specialization of a member template,
we're not going to find the declaration in the class.
For example, in:
struct S { template <typename T> void f(T); };
template <> void S::f(int);
we're not going to find `S::f(int)', but there's no
reason we should, either. We let our callers know we didn't
find the method, but we don't complain. */
return NULL_TREE;
if (method_vec != 0)
{
methods = &TREE_VEC_ELT (method_vec, 0);
end = TREE_VEC_END (method_vec);
/* First suss out ctors and dtors. */
if (*methods && fn_name == DECL_NAME (OVL_CURRENT (*methods))
&& DECL_CONSTRUCTOR_P (function))
goto got_it;
if (*++methods && fn_name == DECL_NAME (OVL_CURRENT (*methods))
&& DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (function)))
goto got_it;
while (++methods != end && *methods)
{
fndecl = *methods;
if (fn_name == DECL_NAME (OVL_CURRENT (*methods)))
{
got_it:
for (fndecls = *methods; fndecls != NULL_TREE;
fndecls = OVL_NEXT (fndecls))
{
fndecl = OVL_CURRENT (fndecls);
/* The DECL_ASSEMBLER_NAME for a TEMPLATE_DECL, or
for a for member function of a template class, is
not mangled, so the check below does not work
correctly in that case. Since mangled destructor
names do not include the type of the arguments,
we can't use this short-cut for them, either.
(It's not legal to declare arguments for a
destructor, but some people try.) */
if (!DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (function))
&& (DECL_ASSEMBLER_NAME (function)
!= DECL_NAME (function))
&& (DECL_ASSEMBLER_NAME (fndecl)
!= DECL_NAME (fndecl))
&& (DECL_ASSEMBLER_NAME (function)
== DECL_ASSEMBLER_NAME (fndecl)))
return fndecl;
/* We cannot simply call decls_match because this
doesn't work for static member functions that are
pretending to be methods, and because the name
may have been changed by asm("new_name"). */
if (DECL_NAME (function) == DECL_NAME (fndecl))
{
tree p1 = TYPE_ARG_TYPES (TREE_TYPE (function));
tree p2 = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
/* Get rid of the this parameter on functions that become
static. */
if (DECL_STATIC_FUNCTION_P (fndecl)
&& TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE)
p1 = TREE_CHAIN (p1);
if (same_type_p (TREE_TYPE (TREE_TYPE (function)),
TREE_TYPE (TREE_TYPE (fndecl)))
&& compparms (p1, p2)
&& (DECL_TEMPLATE_SPECIALIZATION (function)
== DECL_TEMPLATE_SPECIALIZATION (fndecl))
&& (!DECL_TEMPLATE_SPECIALIZATION (function)
|| (DECL_TI_TEMPLATE (function)
== DECL_TI_TEMPLATE (fndecl))))
return fndecl;
}
}
break; /* loser */
}
}
}
if (methods != end && *methods)
{
tree fndecl = *methods;
cp_error ("prototype for `%#D' does not match any in class `%T'",
function, ctype);
cp_error_at ("candidate%s: %+#D", OVL_NEXT (fndecl) ? "s are" : " is",
OVL_CURRENT (fndecl));
while (fndecl = OVL_NEXT (fndecl), fndecl)
cp_error_at (" %#D", OVL_CURRENT(fndecl));
}
else
{
methods = 0;
if (TYPE_SIZE (ctype) == 0)
incomplete_type_error (function, ctype);
else
cp_error ("no `%#D' member function declared in class `%T'",
function, ctype);
}
/* If we did not find the method in the class, add it to avoid
spurious errors (unless the CTYPE is not yet defined, in which
case we'll only confuse ourselves when the function is declared
properly within the class. */
if (TYPE_SIZE (ctype))
add_method (ctype, methods, function);
return NULL_TREE;
}
/* We have just processed the DECL, which is a static data member.
Its initializer, if present, is INIT. The ASMSPEC_TREE, if
present, is the assembly-language name for the data member.
NEED_POP and FLAGS are as for cp_finish_decl. */
void
finish_static_data_member_decl (decl, init, asmspec_tree, need_pop, flags)
tree decl;
tree init;
tree asmspec_tree;
int need_pop;
int flags;
{
char* asmspec = 0;
if (asmspec_tree)
asmspec = TREE_STRING_POINTER (asmspec_tree);
my_friendly_assert (TREE_PUBLIC (decl), 0);
/* We cannot call pushdecl here, because that would fill in the
decl of our TREE_CHAIN. Instead, we modify cp_finish_decl to do
the right thing, namely, to put this decl out straight away. */
/* current_class_type can be NULL_TREE in case of error. */
if (!asmspec && current_class_type)
{
DECL_INITIAL (decl) = error_mark_node;
DECL_ASSEMBLER_NAME (decl)
= build_static_name (current_class_type, DECL_NAME (decl));
}
if (! processing_template_decl)
{
if (!pending_statics)
VARRAY_TREE_INIT (pending_statics, 32, "pending_statics");
if (pending_statics_used == pending_statics->num_elements)
VARRAY_GROW (pending_statics,
2 * pending_statics->num_elements);
VARRAY_TREE (pending_statics, pending_statics_used) = decl;
++pending_statics_used;
}
/* Static consts need not be initialized in the class definition. */
if (init != NULL_TREE && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
{
static int explanation = 0;
error ("initializer invalid for static member with constructor");
if (explanation++ == 0)
error ("(you really want to initialize it separately)");
init = 0;
}
/* Force the compiler to know when an uninitialized static const
member is being used. */
if (CP_TYPE_CONST_P (TREE_TYPE (decl)) && init == 0)
TREE_USED (decl) = 1;
DECL_INITIAL (decl) = init;
DECL_IN_AGGR_P (decl) = 1;
DECL_CONTEXT (decl) = current_class_type;
DECL_CLASS_CONTEXT (decl) = current_class_type;
cp_finish_decl (decl, init, asmspec_tree, need_pop, flags);
}
/* Process the specs, declarator (NULL if omitted) and width (NULL if omitted)
of a structure component, returning a FIELD_DECL node.
QUALS is a list of type qualifiers for this decl (such as for declaring
const member functions).
This is done during the parsing of the struct declaration.
The FIELD_DECL nodes are chained together and the lot of them
are ultimately passed to `build_struct' to make the RECORD_TYPE node.
C++:
If class A defines that certain functions in class B are friends, then
the way I have set things up, it is B who is interested in permission
granted by A. However, it is in A's context that these declarations
are parsed. By returning a void_type_node, class A does not attempt
to incorporate the declarations of the friends within its structure.
DO NOT MAKE ANY CHANGES TO THIS CODE WITHOUT MAKING CORRESPONDING
CHANGES TO CODE IN `start_method'. */
tree
grokfield (declarator, declspecs, init, asmspec_tree, attrlist)
tree declarator, declspecs, init, asmspec_tree, attrlist;
{
register tree value;
char *asmspec = 0;
int flags = LOOKUP_ONLYCONVERTING;
/* Convert () initializers to = initializers. */
if (init == NULL_TREE && declarator != NULL_TREE
&& TREE_CODE (declarator) == CALL_EXPR
&& TREE_OPERAND (declarator, 0)
&& (TREE_CODE (TREE_OPERAND (declarator, 0)) == IDENTIFIER_NODE
|| TREE_CODE (TREE_OPERAND (declarator, 0)) == SCOPE_REF)
&& parmlist_is_exprlist (TREE_OPERAND (declarator, 1)))
{
init = TREE_OPERAND (declarator, 1);
declarator = TREE_OPERAND (declarator, 0);
flags = 0;
}
if (declspecs == NULL_TREE
&& TREE_CODE (declarator) == SCOPE_REF
&& TREE_CODE (TREE_OPERAND (declarator, 1)) == IDENTIFIER_NODE)
{
/* Access declaration */
if (! IS_AGGR_TYPE_CODE (TREE_CODE (TREE_OPERAND (declarator, 0))))
;
else if (TREE_COMPLEXITY (declarator) == current_class_depth)
pop_nested_class ();
return do_class_using_decl (declarator);
}
if (init
&& TREE_CODE (init) == TREE_LIST
&& TREE_VALUE (init) == error_mark_node
&& TREE_CHAIN (init) == NULL_TREE)
init = NULL_TREE;
value = grokdeclarator (declarator, declspecs, FIELD, init != 0, attrlist);
if (! value || value == error_mark_node)
/* friend or constructor went bad. */
return value;
/* Pass friendly classes back. */
if (TREE_CODE (value) == VOID_TYPE)
return void_type_node;
if (DECL_NAME (value) != NULL_TREE
&& IDENTIFIER_POINTER (DECL_NAME (value))[0] == '_'
&& ! strcmp (IDENTIFIER_POINTER (DECL_NAME (value)), "_vptr"))
cp_error ("member `%D' conflicts with virtual function table field name",
value);
/* Stash away type declarations. */
if (TREE_CODE (value) == TYPE_DECL)
{
DECL_NONLOCAL (value) = 1;
DECL_CONTEXT (value) = current_class_type;
DECL_CLASS_CONTEXT (value) = current_class_type;
/* Now that we've updated the context, we need to remangle the
name for this TYPE_DECL. */
DECL_ASSEMBLER_NAME (value) = DECL_NAME (value);
if (!uses_template_parms (value))
DECL_ASSEMBLER_NAME (value) =
get_identifier (build_overload_name (TREE_TYPE (value), 1, 1));
return value;
}
if (IS_SIGNATURE (current_class_type)
&& TREE_CODE (value) != FUNCTION_DECL)
{
error ("field declaration not allowed in signature");
return void_type_node;
}
if (DECL_IN_AGGR_P (value))
{
cp_error ("`%D' is already defined in `%T'", value,
DECL_CONTEXT (value));
return void_type_node;
}
if (asmspec_tree)
asmspec = TREE_STRING_POINTER (asmspec_tree);
if (init)
{
if (IS_SIGNATURE (current_class_type)
&& TREE_CODE (value) == FUNCTION_DECL)
{
error ("function declarations cannot have initializers in signature");
init = NULL_TREE;
}
else if (TREE_CODE (value) == FUNCTION_DECL)
{
grok_function_init (value, init);
init = NULL_TREE;
}
else if (pedantic && TREE_CODE (value) != VAR_DECL)
/* Already complained in grokdeclarator. */
init = NULL_TREE;
else
{
/* We allow initializers to become parameters to base
initializers. */
if (TREE_CODE (init) == TREE_LIST)
{
if (TREE_CHAIN (init) == NULL_TREE)
init = TREE_VALUE (init);
else
init = digest_init (TREE_TYPE (value), init, (tree *)0);
}
if (TREE_CODE (init) == CONST_DECL)
init = DECL_INITIAL (init);
else if (TREE_READONLY_DECL_P (init))
init = decl_constant_value (init);
else if (TREE_CODE (init) == CONSTRUCTOR)
init = digest_init (TREE_TYPE (value), init, (tree *)0);
my_friendly_assert (TREE_PERMANENT (init), 192);
if (init == error_mark_node)
/* We must make this look different than `error_mark_node'
because `decl_const_value' would mis-interpret it
as only meaning that this VAR_DECL is defined. */
init = build1 (NOP_EXPR, TREE_TYPE (value), init);
else if (processing_template_decl)
;
else if (! TREE_CONSTANT (init))
{
/* We can allow references to things that are effectively
static, since references are initialized with the address. */
if (TREE_CODE (TREE_TYPE (value)) != REFERENCE_TYPE
|| (TREE_STATIC (init) == 0
&& (TREE_CODE_CLASS (TREE_CODE (init)) != 'd'
|| DECL_EXTERNAL (init) == 0)))
{
error ("field initializer is not constant");
init = error_mark_node;
}
}
}
}
/* The corresponding pop_obstacks is in cp_finish_decl. */
push_obstacks_nochange ();
if (processing_template_decl && ! current_function_decl
&& (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == FUNCTION_DECL))
value = push_template_decl (value);
if (attrlist)
cplus_decl_attributes (value, TREE_PURPOSE (attrlist),
TREE_VALUE (attrlist));
if (TREE_CODE (value) == VAR_DECL)
{
finish_static_data_member_decl (value, init, asmspec_tree,
/*need_pop=*/1, flags);
return value;
}
if (TREE_CODE (value) == FIELD_DECL)
{
if (asmspec)
{
/* This must override the asm specifier which was placed
by grokclassfn. Lay this out fresh. */
DECL_RTL (value) = NULL_RTX;
DECL_ASSEMBLER_NAME (value) = get_identifier (asmspec);
}
if (DECL_INITIAL (value) == error_mark_node)
init = error_mark_node;
cp_finish_decl (value, init, asmspec_tree, 1, flags);
DECL_INITIAL (value) = init;
DECL_IN_AGGR_P (value) = 1;
return value;
}
if (TREE_CODE (value) == FUNCTION_DECL)
{
if (asmspec)
{
/* This must override the asm specifier which was placed
by grokclassfn. Lay this out fresh. */
DECL_RTL (value) = NULL_RTX;
DECL_ASSEMBLER_NAME (value) = get_identifier (asmspec);
}
cp_finish_decl (value, init, asmspec_tree, 1, flags);
/* Pass friends back this way. */
if (DECL_FRIEND_P (value))
return void_type_node;
#if 0 /* Just because a fn is declared doesn't mean we'll try to define it. */
if (current_function_decl && ! IS_SIGNATURE (current_class_type))
cp_error ("method `%#D' of local class must be defined in class body",
value);
#endif
DECL_IN_AGGR_P (value) = 1;
return value;
}
my_friendly_abort (21);
/* NOTREACHED */
return NULL_TREE;
}
/* Like `grokfield', but for bitfields.
WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node. */
tree
grokbitfield (declarator, declspecs, width)
tree declarator, declspecs, width;
{
register tree value = grokdeclarator (declarator, declspecs, BITFIELD,
0, NULL_TREE);
if (! value) return NULL_TREE; /* friends went bad. */
/* Pass friendly classes back. */
if (TREE_CODE (value) == VOID_TYPE)
return void_type_node;
if (TREE_CODE (value) == TYPE_DECL)
{
cp_error ("cannot declare `%D' to be a bitfield type", value);
return NULL_TREE;
}
/* Usually, finish_struct_1 catches bitifields with invalid types.
But, in the case of bitfields with function type, we confuse
ourselves into thinking they are member functions, so we must
check here. */
if (TREE_CODE (value) == FUNCTION_DECL)
{
cp_error ("cannot declare bitfield `%D' with funcion type",
DECL_NAME (value));
return NULL_TREE;
}
if (IS_SIGNATURE (current_class_type))
{
error ("field declaration not allowed in signature");
return void_type_node;
}
if (DECL_IN_AGGR_P (value))
{
cp_error ("`%D' is already defined in the class %T", value,
DECL_CONTEXT (value));
return void_type_node;
}
GNU_xref_member (current_class_name, value);
if (TREE_STATIC (value))
{
cp_error ("static member `%D' cannot be a bitfield", value);
return NULL_TREE;
}
cp_finish_decl (value, NULL_TREE, NULL_TREE, 0, 0);
if (width != error_mark_node)
{
constant_expression_warning (width);
DECL_INITIAL (value) = width;
SET_DECL_C_BIT_FIELD (value);
}
DECL_IN_AGGR_P (value) = 1;
return value;
}
tree
grokoptypename (declspecs, declarator)
tree declspecs, declarator;
{
tree t = grokdeclarator (declarator, declspecs, TYPENAME, 0, NULL_TREE);
return build_typename_overload (t);
}
/* When a function is declared with an initializer,
do the right thing. Currently, there are two possibilities:
class B
{
public:
// initialization possibility #1.
virtual void f () = 0;
int g ();
};
class D1 : B
{
public:
int d1;
// error, no f ();
};
class D2 : B
{
public:
int d2;
void f ();
};
class D3 : B
{
public:
int d3;
// initialization possibility #2
void f () = B::f;
};
*/
int
copy_assignment_arg_p (parmtype, virtualp)
tree parmtype;
int virtualp ATTRIBUTE_UNUSED;
{
if (current_class_type == NULL_TREE)
return 0;
if (TREE_CODE (parmtype) == REFERENCE_TYPE)
parmtype = TREE_TYPE (parmtype);
if ((TYPE_MAIN_VARIANT (parmtype) == current_class_type)
#if 0
/* Non-standard hack to support old Booch components. */
|| (! virtualp && DERIVED_FROM_P (parmtype, current_class_type))
#endif
)
return 1;
return 0;
}
static void
grok_function_init (decl, init)
tree decl;
tree init;
{
/* An initializer for a function tells how this function should
be inherited. */
tree type = TREE_TYPE (decl);
if (TREE_CODE (type) == FUNCTION_TYPE)
cp_error ("initializer specified for non-member function `%D'", decl);
#if 0
/* We'll check for this in finish_struct_1. */
else if (DECL_VINDEX (decl) == NULL_TREE)
cp_error ("initializer specified for non-virtual method `%D'", decl);
#endif
else if (integer_zerop (init))
{
#if 0
/* Mark this function as being "defined". */
DECL_INITIAL (decl) = error_mark_node;
/* pure virtual destructors must be defined. */
/* pure virtual needs to be defined (as abort) only when put in
vtbl. For wellformed call, it should be itself. pr4737 */
if (!DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (decl)))
{
extern tree abort_fndecl;
/* Give this node rtl from `abort'. */
DECL_RTL (decl) = DECL_RTL (abort_fndecl);
}
#endif
DECL_ABSTRACT_VIRTUAL_P (decl) = 1;
if (DECL_NAME (decl) == ansi_opname [(int) MODIFY_EXPR])
{
tree parmtype
= TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (decl))));
if (copy_assignment_arg_p (parmtype, 1))
TYPE_HAS_ABSTRACT_ASSIGN_REF (current_class_type) = 1;
}
}
else
cp_error ("invalid initializer for virtual method `%D'", decl);
}
void
cplus_decl_attributes (decl, attributes, prefix_attributes)
tree decl, attributes, prefix_attributes;
{
if (decl == NULL_TREE || decl == void_type_node)
return;
if (TREE_CODE (decl) == TEMPLATE_DECL)
decl = DECL_TEMPLATE_RESULT (decl);
decl_attributes (decl, attributes, prefix_attributes);
if (TREE_CODE (decl) == TYPE_DECL)
SET_IDENTIFIER_TYPE_VALUE (DECL_NAME (decl), TREE_TYPE (decl));
}
/* CONSTRUCTOR_NAME:
Return the name for the constructor (or destructor) for the
specified class. Argument can be RECORD_TYPE, TYPE_DECL, or
IDENTIFIER_NODE. When given a template, this routine doesn't
lose the specialization. */
tree
constructor_name_full (thing)
tree thing;
{
if (TREE_CODE (thing) == TEMPLATE_TYPE_PARM
|| TREE_CODE (thing) == TEMPLATE_TEMPLATE_PARM
|| TREE_CODE (thing) == TYPENAME_TYPE)
thing = TYPE_NAME (thing);
else if (IS_AGGR_TYPE_CODE (TREE_CODE (thing)))
{
if (TYPE_WAS_ANONYMOUS (thing) && TYPE_HAS_CONSTRUCTOR (thing))
thing = DECL_NAME (OVL_CURRENT (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (thing), 0)));
else
thing = TYPE_NAME (thing);
}
if (TREE_CODE (thing) == TYPE_DECL
|| (TREE_CODE (thing) == TEMPLATE_DECL
&& TREE_CODE (DECL_TEMPLATE_RESULT (thing)) == TYPE_DECL))
thing = DECL_NAME (thing);
my_friendly_assert (TREE_CODE (thing) == IDENTIFIER_NODE, 197);
return thing;
}
/* CONSTRUCTOR_NAME:
Return the name for the constructor (or destructor) for the
specified class. Argument can be RECORD_TYPE, TYPE_DECL, or
IDENTIFIER_NODE. When given a template, return the plain
unspecialized name. */
tree
constructor_name (thing)
tree thing;
{
tree t;
thing = constructor_name_full (thing);
t = IDENTIFIER_TEMPLATE (thing);
if (!t)
return thing;
return t;
}
/* Cache the value of this class's main virtual function table pointer
in a register variable. This will save one indirection if a
more than one virtual function call is made this function. */
void
setup_vtbl_ptr ()
{
extern tree base_init_expr;
if (base_init_expr == 0
&& DECL_CONSTRUCTOR_P (current_function_decl))
{
if (processing_template_decl)
add_tree (build_min_nt
(CTOR_INITIALIZER,
current_member_init_list, current_base_init_list));
else
emit_base_init (current_class_type, 0);
}
}
/* Record the existence of an addressable inline function. */
void
mark_inline_for_output (decl)
tree decl;
{
decl = DECL_MAIN_VARIANT (decl);
if (DECL_SAVED_INLINE (decl))
return;
my_friendly_assert (TREE_PERMANENT (decl), 363);
DECL_SAVED_INLINE (decl) = 1;
if (!saved_inlines)
VARRAY_TREE_INIT (saved_inlines, 32, "saved_inlines");
if (saved_inlines_used == saved_inlines->num_elements)
VARRAY_GROW (saved_inlines,
2 * saved_inlines->num_elements);
VARRAY_TREE (saved_inlines, saved_inlines_used) = decl;
++saved_inlines_used;
}
void
clear_temp_name ()
{
temp_name_counter = 0;
}
/* Hand off a unique name which can be used for variable we don't really
want to know about anyway, for example, the anonymous variables which
are needed to make references work. Declare this thing so we can use it.
The variable created will be of type TYPE.
STATICP is nonzero if this variable should be static. */
tree
get_temp_name (type, staticp)
tree type;
int staticp;
{
char buf[sizeof (AUTO_TEMP_FORMAT) + 20];
tree decl;
int toplev = toplevel_bindings_p ();
push_obstacks_nochange ();
if (toplev || staticp)
{
end_temporary_allocation ();
sprintf (buf, AUTO_TEMP_FORMAT, global_temp_name_counter++);
decl = pushdecl_top_level (build_decl (VAR_DECL, get_identifier (buf), type));
}
else
{
sprintf (buf, AUTO_TEMP_FORMAT, temp_name_counter++);
decl = pushdecl (build_decl (VAR_DECL, get_identifier (buf), type));
}
TREE_USED (decl) = 1;
TREE_STATIC (decl) = staticp;
DECL_ARTIFICIAL (decl) = 1;
/* If this is a local variable, then lay out its rtl now.
Otherwise, callers of this function are responsible for dealing
with this variable's rtl. */
if (! toplev)
{
expand_decl (decl);
expand_decl_init (decl);
}
pop_obstacks ();
return decl;
}
/* Get a variable which we can use for multiple assignments.
It is not entered into current_binding_level, because
that breaks things when it comes time to do final cleanups
(which take place "outside" the binding contour of the function). */
tree
get_temp_regvar (type, init)
tree type, init;
{
tree decl;
decl = build_decl (VAR_DECL, NULL_TREE, type);
TREE_USED (decl) = 1;
DECL_REGISTER (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_RTL (decl) = assign_temp (type, 2, 0, 1);
/* We can expand these without fear, since they cannot need
constructors or destructors. */
expand_expr (build_modify_expr (decl, INIT_EXPR, init),
NULL_RTX, VOIDmode, 0);
return decl;
}
/* Hunts through the global anonymous union ANON_DECL, building
appropriate VAR_DECLs. Stores cleanups on the list of ELEMS, and
returns a VAR_DECL whose size is the same as the size of the
ANON_DECL, if one is available. */
static tree
build_anon_union_vars (anon_decl, elems, static_p, external_p)
tree anon_decl;
tree* elems;
int static_p;
int external_p;
{
tree type = TREE_TYPE (anon_decl);
tree main_decl = NULL_TREE;
tree field;
for (field = TYPE_FIELDS (type);
field != NULL_TREE;
field = TREE_CHAIN (field))
{
tree decl;
if (DECL_ARTIFICIAL (field))
continue;
if (TREE_CODE (field) != FIELD_DECL)
{
cp_pedwarn_at ("`%#D' invalid; an anonymous union can only have non-static data members",
field);
continue;
}
if (TREE_PRIVATE (field))
cp_pedwarn_at ("private member `%#D' in anonymous union", field);
else if (TREE_PROTECTED (field))
cp_pedwarn_at ("protected member `%#D' in anonymous union", field);
if (DECL_NAME (field) == NULL_TREE
&& TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
{
decl = build_anon_union_vars (field, elems, static_p, external_p);
if (!decl)
continue;
}
else if (DECL_NAME (field) == NULL_TREE)
continue;
else
{
decl = build_decl (VAR_DECL, DECL_NAME (field), TREE_TYPE (field));
/* tell `pushdecl' that this is not tentative. */
DECL_INITIAL (decl) = error_mark_node;
TREE_PUBLIC (decl) = 0;
TREE_STATIC (decl) = static_p;
DECL_EXTERNAL (decl) = external_p;
decl = pushdecl (decl);
DECL_INITIAL (decl) = NULL_TREE;
}
/* Only write out one anon union element--choose the one that
can hold them all. */
if (main_decl == NULL_TREE
&& simple_cst_equal (DECL_SIZE (decl),
DECL_SIZE (anon_decl)) == 1)
main_decl = decl;
else
/* ??? This causes there to be no debug info written out
about this decl. */
TREE_ASM_WRITTEN (decl) = 1;
if (DECL_NAME (field) == NULL_TREE
&& TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
/* The remainder of the processing was already done in the
recursive call. */
continue;
/* If there's a cleanup to do, it belongs in the
TREE_PURPOSE of the following TREE_LIST. */
*elems = scratch_tree_cons (NULL_TREE, decl, *elems);
TREE_TYPE (*elems) = type;
}
return main_decl;
}
/* Finish off the processing of a UNION_TYPE structure.
If there are static members, then all members are
static, and must be laid out together. If the
union is an anonymous union, we arrange for that
as well. PUBLIC_P is nonzero if this union is
not declared static. */
void
finish_anon_union (anon_union_decl)
tree anon_union_decl;
{
tree type = TREE_TYPE (anon_union_decl);
tree elems = NULL_TREE;
tree main_decl;
int public_p = TREE_PUBLIC (anon_union_decl);
int static_p = TREE_STATIC (anon_union_decl);
int external_p = DECL_EXTERNAL (anon_union_decl);
if (TYPE_FIELDS (type) == NULL_TREE)
return;
if (public_p)
{
error ("global anonymous unions must be declared static");
return;
}
main_decl = build_anon_union_vars (anon_union_decl, &elems,
static_p, external_p);
if (main_decl == NULL_TREE)
{
warning ("anonymous union with no members");
return;
}
if (static_p)
{
make_decl_rtl (main_decl, 0, toplevel_bindings_p ());
DECL_RTL (anon_union_decl) = DECL_RTL (main_decl);
}
/* The following call assumes that there are never any cleanups
for anonymous unions--a reasonable assumption. */
expand_anon_union_decl (anon_union_decl, NULL_TREE, elems);
}
/* Finish processing a builtin type TYPE. It's name is NAME,
its fields are in the array FIELDS. LEN is the number of elements
in FIELDS minus one, or put another way, it is the maximum subscript
used in FIELDS.
It is given the same alignment as ALIGN_TYPE. */
void
finish_builtin_type (type, name, fields, len, align_type)
tree type;
const char *name;
tree fields[];
int len;
tree align_type;
{
register int i;
TYPE_FIELDS (type) = fields[0];
for (i = 0; i < len; i++)
{
layout_type (TREE_TYPE (fields[i]));
DECL_FIELD_CONTEXT (fields[i]) = type;
TREE_CHAIN (fields[i]) = fields[i+1];
}
DECL_FIELD_CONTEXT (fields[i]) = type;
DECL_CLASS_CONTEXT (fields[i]) = type;
TYPE_ALIGN (type) = TYPE_ALIGN (align_type);
layout_type (type);
#if 0 /* not yet, should get fixed properly later */
TYPE_NAME (type) = make_type_decl (get_identifier (name), type);
#else
TYPE_NAME (type) = build_decl (TYPE_DECL, get_identifier (name), type);
#endif
TYPE_STUB_DECL (type) = TYPE_NAME (type);
layout_decl (TYPE_NAME (type), 0);
}
/* Auxiliary functions to make type signatures for
`operator new' and `operator delete' correspond to
what compiler will be expecting. */
tree
coerce_new_type (type)
tree type;
{
int e1 = 0, e2 = 0;
if (TREE_CODE (type) == METHOD_TYPE)
type = build_function_type (TREE_TYPE (type), TREE_CHAIN (TYPE_ARG_TYPES (type)));
if (! same_type_p (TREE_TYPE (type), ptr_type_node))
e1 = 1, error ("`operator new' must return type `void *'");
/* Technically the type must be `size_t', but we may not know
what that is. */
if (TYPE_ARG_TYPES (type) == NULL_TREE)
e1 = 1, error ("`operator new' takes type `size_t' parameter");
else if (! same_type_p (TREE_VALUE (TYPE_ARG_TYPES (type)), sizetype))
e2 = 1, error ("`operator new' takes type `size_t' as first parameter");
if (e2)
type = build_function_type (ptr_type_node, tree_cons (NULL_TREE, sizetype, TREE_CHAIN (TYPE_ARG_TYPES (type))));
else if (e1)
type = build_function_type (ptr_type_node, TYPE_ARG_TYPES (type));
return type;
}
tree
coerce_delete_type (type)
tree type;
{
int e1 = 0, e2 = 0;
#if 0
e3 = 0;
#endif
tree arg_types = TYPE_ARG_TYPES (type);
if (TREE_CODE (type) == METHOD_TYPE)
{
type = build_function_type (TREE_TYPE (type), TREE_CHAIN (arg_types));
arg_types = TREE_CHAIN (arg_types);
}
if (TREE_TYPE (type) != void_type_node)
e1 = 1, error ("`operator delete' must return type `void'");
if (arg_types == NULL_TREE
|| ! same_type_p (TREE_VALUE (arg_types), ptr_type_node))
e2 = 1, error ("`operator delete' takes type `void *' as first parameter");
#if 0
if (arg_types
&& TREE_CHAIN (arg_types)
&& TREE_CHAIN (arg_types) != void_list_node)
{
/* Again, technically this argument must be `size_t', but again
we may not know what that is. */
tree t2 = TREE_VALUE (TREE_CHAIN (arg_types));
if (! same_type_p (t2, sizetype))
e3 = 1, error ("second argument to `operator delete' must be of type `size_t'");
else if (TREE_CHAIN (TREE_CHAIN (arg_types)) != void_list_node)
{
e3 = 1;
if (TREE_CHAIN (TREE_CHAIN (arg_types)))
error ("too many arguments in declaration of `operator delete'");
else
error ("`...' invalid in specification of `operator delete'");
}
}
if (e3)
arg_types = tree_cons (NULL_TREE, ptr_type_node,
build_tree_list (NULL_TREE, sizetype));
else if (e3 |= e2)
{
if (arg_types == NULL_TREE)
arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
else
arg_types = tree_cons (NULL_TREE, ptr_type_node, TREE_CHAIN (arg_types));
}
else e3 |= e1;
#endif
if (e2)
arg_types = tree_cons (NULL_TREE, ptr_type_node,
arg_types ? TREE_CHAIN (arg_types): NULL_TREE);
if (e2 || e1)
type = build_function_type (void_type_node, arg_types);
return type;
}
extern tree abort_fndecl;
static void
mark_vtable_entries (decl)
tree decl;
{
tree entries = CONSTRUCTOR_ELTS (DECL_INITIAL (decl));
for (; entries; entries = TREE_CHAIN (entries))
{
tree fnaddr;
tree fn;
fnaddr = (flag_vtable_thunks ? TREE_VALUE (entries)
: FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (entries)));
if (TREE_CODE (fnaddr) == NOP_EXPR)
/* RTTI offset. */
continue;
fn = TREE_OPERAND (fnaddr, 0);
TREE_ADDRESSABLE (fn) = 1;
if (DECL_LANG_SPECIFIC (fn) && DECL_ABSTRACT_VIRTUAL_P (fn))
{
TREE_OPERAND (fnaddr, 0) = fn = copy_node (fn);
DECL_RTL (fn) = DECL_RTL (abort_fndecl);
mark_used (abort_fndecl);
}
if (TREE_CODE (fn) == THUNK_DECL && DECL_EXTERNAL (fn))
{
DECL_EXTERNAL (fn) = 0;
emit_thunk (fn);
}
mark_used (fn);
}
}
/* Set DECL up to have the closest approximation of "initialized common"
linkage available. */
void
comdat_linkage (decl)
tree decl;
{
if (flag_weak)
make_decl_one_only (decl);
else if (TREE_CODE (decl) == FUNCTION_DECL || DECL_VIRTUAL_P (decl))
/* We can just emit functions and vtables statically; it doesn't really
matter if we have multiple copies. */
TREE_PUBLIC (decl) = 0;
else
{
/* Static data member template instantiations, however, cannot
have multiple copies. */
if (DECL_INITIAL (decl) == 0
|| DECL_INITIAL (decl) == error_mark_node)
DECL_COMMON (decl) = 1;
else if (EMPTY_CONSTRUCTOR_P (DECL_INITIAL (decl)))
{
DECL_COMMON (decl) = 1;
DECL_INITIAL (decl) = error_mark_node;
}
else
{
/* We can't do anything useful; leave vars for explicit
instantiation. */
DECL_EXTERNAL (decl) = 1;
DECL_NOT_REALLY_EXTERN (decl) = 0;
}
}
if (DECL_LANG_SPECIFIC (decl))
DECL_COMDAT (decl) = 1;
}
/* For win32 we also want to put explicit instantiations in
linkonce sections, so that they will be merged with implicit
instantiations; otherwise we get duplicate symbol errors. */
void
maybe_make_one_only (decl)
tree decl;
{
/* This is not necessary on targets that support weak symbols, because
the implicit instantiations will defer to the explicit one. */
if (! supports_one_only () || SUPPORTS_WEAK)
return;
/* We can't set DECL_COMDAT on functions, or finish_file will think
we can get away with not emitting them if they aren't used. We need
to for variables so that cp_finish_decl will update their linkage,
because their DECL_INITIAL may not have been set properly yet. */
make_decl_one_only (decl);
if (TREE_CODE (decl) == VAR_DECL && DECL_LANG_SPECIFIC (decl))
DECL_COMDAT (decl) = 1;
}
/* Set TREE_PUBLIC and/or DECL_EXTERN on the vtable DECL,
based on TYPE and other static flags.
Note that anything public is tagged TREE_PUBLIC, whether
it's public in this file or in another one. */
void
import_export_vtable (decl, type, final)
tree decl, type;
int final;
{
if (DECL_INTERFACE_KNOWN (decl))
return;
if (TYPE_FOR_JAVA (type))
{
TREE_PUBLIC (decl) = 1;
DECL_EXTERNAL (decl) = 1;
DECL_INTERFACE_KNOWN (decl) = 1;
}
else if (CLASSTYPE_INTERFACE_KNOWN (type))
{
TREE_PUBLIC (decl) = 1;
DECL_EXTERNAL (decl) = ! CLASSTYPE_VTABLE_NEEDS_WRITING (type);
DECL_INTERFACE_KNOWN (decl) = 1;
}
else
{
/* We can only wait to decide if we have real non-inline virtual
functions in our class, or if we come from a template. */
int found = CLASSTYPE_TEMPLATE_INSTANTIATION (type);
if (! found && ! final)
{
tree method;
for (method = TYPE_METHODS (type); method != NULL_TREE;
method = TREE_CHAIN (method))
if (DECL_VINDEX (method) != NULL_TREE
&& ! DECL_THIS_INLINE (method)
&& ! DECL_ABSTRACT_VIRTUAL_P (method))
{
found = 1;
break;
}
}
if (final || ! found)
{
comdat_linkage (decl);
DECL_EXTERNAL (decl) = 0;
}
else
{
TREE_PUBLIC (decl) = 1;
DECL_EXTERNAL (decl) = 1;
}
}
}
/* Determine whether or not we want to specifically import or export CTYPE,
using various heuristics. */
void
import_export_class (ctype)
tree ctype;
{
/* -1 for imported, 1 for exported. */
int import_export = 0;
if (CLASSTYPE_INTERFACE_KNOWN (ctype))
return;
/* If MULTIPLE_SYMBOL_SPACES is defined and we saw a #pragma interface,
we will have CLASSTYPE_INTERFACE_ONLY set but not
CLASSTYPE_INTERFACE_KNOWN. In that case, we don't want to use this
heuristic because someone will supply a #pragma implementation
elsewhere, and deducing it here would produce a conflict. */
if (CLASSTYPE_INTERFACE_ONLY (ctype))
return;
#ifdef VALID_MACHINE_TYPE_ATTRIBUTE
/* FIXME this should really use some sort of target-independent macro. */
if (lookup_attribute ("dllimport", TYPE_ATTRIBUTES (ctype)))
import_export = -1;
else if (lookup_attribute ("dllexport", TYPE_ATTRIBUTES (ctype)))
import_export = 1;
#endif
/* If we got -fno-implicit-templates, we import template classes that
weren't explicitly instantiated. */
if (import_export == 0
&& CLASSTYPE_IMPLICIT_INSTANTIATION (ctype)
&& ! flag_implicit_templates)
import_export = -1;
/* Base our import/export status on that of the first non-inline,
non-abstract virtual function, if any. */
if (import_export == 0
&& TYPE_VIRTUAL_P (ctype)
&& ! CLASSTYPE_TEMPLATE_INSTANTIATION (ctype))
{
tree method;
for (method = TYPE_METHODS (ctype); method != NULL_TREE;
method = TREE_CHAIN (method))
{
if (DECL_VINDEX (method) != NULL_TREE
&& !DECL_THIS_INLINE (method)
&& !DECL_ABSTRACT_VIRTUAL_P (method))
{
import_export = (DECL_REALLY_EXTERN (method) ? -1 : 1);
break;
}
}
}
#ifdef MULTIPLE_SYMBOL_SPACES
if (import_export == -1)
import_export = 0;
#endif
if (import_export)
{
SET_CLASSTYPE_INTERFACE_KNOWN (ctype);
CLASSTYPE_VTABLE_NEEDS_WRITING (ctype) = (import_export > 0);
CLASSTYPE_INTERFACE_ONLY (ctype) = (import_export < 0);
}
}
/* We need to describe to the assembler the relationship between
a vtable and the vtable of the parent class. */
static void
output_vtable_inherit (vars)
tree vars;
{
tree parent;
rtx op[2];
op[0] = XEXP (DECL_RTL (vars), 0); /* strip the mem ref */
parent = binfo_for_vtable (vars);
if (parent == TYPE_BINFO (DECL_CONTEXT (vars)))
op[1] = const0_rtx;
else if (parent)
{
parent = TYPE_BINFO_VTABLE (BINFO_TYPE (parent));
op[1] = XEXP (DECL_RTL (parent), 0); /* strip the mem ref */
}
else
my_friendly_abort (980826);
output_asm_insn (".vtable_inherit %c0, %c1", op);
}
static int
finish_vtable_vardecl (t, data)
tree *t;
void *data ATTRIBUTE_UNUSED;
{
tree vars = *t;
tree ctype = DECL_CONTEXT (vars);
import_export_class (ctype);
import_export_vtable (vars, ctype, 1);
if (! DECL_EXTERNAL (vars)
&& (DECL_INTERFACE_KNOWN (vars)
|| TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (vars))
|| (hack_decl_function_context (vars) && TREE_USED (vars)))
&& ! TREE_ASM_WRITTEN (vars))
{
/* Write it out. */
mark_vtable_entries (vars);
if (TREE_TYPE (DECL_INITIAL (vars)) == 0)
store_init_value (vars, DECL_INITIAL (vars));
if (write_symbols == DWARF_DEBUG || write_symbols == DWARF2_DEBUG)
{
/* Mark the VAR_DECL node representing the vtable itself as a
"gratuitous" one, thereby forcing dwarfout.c to ignore it.
It is rather important that such things be ignored because
any effort to actually generate DWARF for them will run
into trouble when/if we encounter code like:
#pragma interface
struct S { virtual void member (); };
because the artificial declaration of the vtable itself (as
manufactured by the g++ front end) will say that the vtable
is a static member of `S' but only *after* the debug output
for the definition of `S' has already been output. This causes
grief because the DWARF entry for the definition of the vtable
will try to refer back to an earlier *declaration* of the
vtable as a static member of `S' and there won't be one.
We might be able to arrange to have the "vtable static member"
attached to the member list for `S' before the debug info for
`S' get written (which would solve the problem) but that would
require more intrusive changes to the g++ front end. */
DECL_IGNORED_P (vars) = 1;
}
/* Always make vtables weak. */
if (flag_weak)
comdat_linkage (vars);
rest_of_decl_compilation (vars, NULL_PTR, 1, 1);
if (flag_vtable_gc)
output_vtable_inherit (vars);
return 1;
}
else if (! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (vars)))
/* We don't know what to do with this one yet. */
return 0;
*t = TREE_CHAIN (vars);
return 0;
}
static int
prune_vtable_vardecl (t, data)
tree *t;
void *data ATTRIBUTE_UNUSED;
{
*t = TREE_CHAIN (*t);
return 1;
}
static int
finish_sigtable_vardecl (t, data)
tree *t;
void *data ATTRIBUTE_UNUSED;
{
/* We don't need to mark sigtable entries as addressable here as is done
for vtables. Since sigtables, unlike vtables, are always written out,
that was already done in build_signature_table_constructor. */
rest_of_decl_compilation (*t, NULL_PTR, 1, 1);
*t = TREE_CHAIN (*t);
return 1;
}
/* Determines the proper settings of TREE_PUBLIC and DECL_EXTERNAL for an
inline function or template instantiation at end-of-file. */
void
import_export_decl (decl)
tree decl;
{
if (DECL_INTERFACE_KNOWN (decl))
return;
if (DECL_TEMPLATE_INSTANTIATION (decl)
|| DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (decl))
{
DECL_NOT_REALLY_EXTERN (decl) = 1;
if ((DECL_IMPLICIT_INSTANTIATION (decl)
|| DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (decl))
&& (flag_implicit_templates
|| (flag_implicit_inline_templates && DECL_THIS_INLINE (decl))))
{
if (!TREE_PUBLIC (decl))
/* Templates are allowed to have internal linkage. See
[basic.link]. */
;
else
comdat_linkage (decl);
}
else
DECL_NOT_REALLY_EXTERN (decl) = 0;
}
else if (DECL_FUNCTION_MEMBER_P (decl))
{
tree ctype = DECL_CLASS_CONTEXT (decl);
import_export_class (ctype);
if (CLASSTYPE_INTERFACE_KNOWN (ctype)
&& (! DECL_ARTIFICIAL (decl) || DECL_VINDEX (decl)))
{
DECL_NOT_REALLY_EXTERN (decl)
= ! (CLASSTYPE_INTERFACE_ONLY (ctype)
|| (DECL_THIS_INLINE (decl) && ! flag_implement_inlines
&& !DECL_VINDEX (decl)));
/* Always make artificials weak. */
if (DECL_ARTIFICIAL (decl) && flag_weak)
comdat_linkage (decl);
else
maybe_make_one_only (decl);
}
else
comdat_linkage (decl);
}
else if (DECL_TINFO_FN_P (decl))
{
tree ctype = TREE_TYPE (DECL_NAME (decl));
if (IS_AGGR_TYPE (ctype))
import_export_class (ctype);
if (IS_AGGR_TYPE (ctype) && CLASSTYPE_INTERFACE_KNOWN (ctype)
&& TYPE_VIRTUAL_P (ctype)
/* If the type is a cv-qualified variant of a type, then we
must emit the tinfo function in this translation unit
since it will not be emitted when the vtable for the type
is output (which is when the unqualified version is
generated). */
&& same_type_p (ctype, TYPE_MAIN_VARIANT (ctype)))
{
DECL_NOT_REALLY_EXTERN (decl)
= ! (CLASSTYPE_INTERFACE_ONLY (ctype)
|| (DECL_THIS_INLINE (decl) && ! flag_implement_inlines
&& !DECL_VINDEX (decl)));
/* Always make artificials weak. */
if (flag_weak)
comdat_linkage (decl);
}
else if (TYPE_BUILT_IN (ctype)
&& same_type_p (ctype, TYPE_MAIN_VARIANT (ctype)))
DECL_NOT_REALLY_EXTERN (decl) = 0;
else
comdat_linkage (decl);
}
else
comdat_linkage (decl);
DECL_INTERFACE_KNOWN (decl) = 1;
}
tree
build_cleanup (decl)
tree decl;
{
tree temp;
tree type = TREE_TYPE (decl);
if (TREE_CODE (type) == ARRAY_TYPE)
temp = decl;
else
{
mark_addressable (decl);
temp = build1 (ADDR_EXPR, build_pointer_type (type), decl);
}
temp = build_delete (TREE_TYPE (temp), temp,
integer_two_node,
LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0);
return temp;
}
extern int parse_time, varconst_time;
static tree
get_sentry (base)
tree base;
{
tree sname = get_id_2 ("__sn", base);
/* For struct X foo __attribute__((weak)), there is a counter
__snfoo. Since base is already an assembler name, sname should
be globally unique */
tree sentry = IDENTIFIER_GLOBAL_VALUE (sname);
if (! sentry)
{
push_obstacks_nochange ();
end_temporary_allocation ();
sentry = build_decl (VAR_DECL, sname, integer_type_node);
TREE_PUBLIC (sentry) = 1;
DECL_ARTIFICIAL (sentry) = 1;
TREE_STATIC (sentry) = 1;
TREE_USED (sentry) = 1;
DECL_COMMON (sentry) = 1;
pushdecl_top_level (sentry);
cp_finish_decl (sentry, NULL_TREE, NULL_TREE, 0, 0);
pop_obstacks ();
}
return sentry;
}
/* Start the process of running a particular set of global constructors
or destructors. Subroutine of do_[cd]tors. */
static void
start_objects (method_type, initp)
int method_type, initp;
{
tree fnname;
char type[10];
/* Make ctor or dtor function. METHOD_TYPE may be 'I' or 'D'. */
if (initp != DEFAULT_INIT_PRIORITY)
{
char joiner;
#ifdef JOINER
joiner = JOINER;
#else
joiner = '_';
#endif
sprintf (type, "%c%c%.5u", method_type, joiner, initp);
}
else
sprintf (type, "%c", method_type);
fnname = get_file_function_name_long (type);
start_function (void_list_node,
make_call_declarator (fnname, void_list_node, NULL_TREE,
NULL_TREE),
NULL_TREE, 0);
#if defined(ASM_OUTPUT_CONSTRUCTOR) && defined(ASM_OUTPUT_DESTRUCTOR)
/* It can be a static function as long as collect2 does not have
to scan the object file to find its ctor/dtor routine. */
TREE_PUBLIC (current_function_decl) = 0;
#endif
store_parm_decls ();
pushlevel (0);
clear_last_expr ();
push_momentary ();
expand_start_bindings (0);
/* We cannot allow these functions to be elided, even if they do not
have external linkage. And, there's no point in deferring
copmilation of thes functions; they're all going to have to be
out anyhow. */
current_function_cannot_inline
= "static constructors and destructors cannot be inlined";
}
/* Finish the process of running a particular set of global constructors
or destructors. Subroutine of do_[cd]tors. */
static void
finish_objects (method_type, initp)
int method_type, initp;
{
char *fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
/* Finish up. */
expand_end_bindings (getdecls (), 1, 0);
poplevel (1, 0, 0);
pop_momentary ();
finish_function (lineno, 0, 0);
if (initp == DEFAULT_INIT_PRIORITY)
{
if (method_type == 'I')
assemble_constructor (fnname);
else
assemble_destructor (fnname);
}
#if defined (ASM_OUTPUT_SECTION_NAME) && defined (ASM_OUTPUT_CONSTRUCTOR)
/* If we're using init priority we can't use assemble_*tor, but on ELF
targets we can stick the references into named sections for GNU ld
to collect. */
else
{
char buf[15];
sprintf (buf, ".%ctors.%.5u", method_type == 'I' ? 'c' : 'd',
/* invert the numbering so the linker puts us in the proper
order; constructors are run from right to left, and the
linker sorts in increasing order. */
MAX_INIT_PRIORITY - initp);
named_section (NULL_TREE, buf, 0);
assemble_integer (gen_rtx_SYMBOL_REF (Pmode, fnname),
POINTER_SIZE / BITS_PER_UNIT, 1);
}
#endif
}
/* The names of the parameters to the function created to handle
initializations and destructions for objects with static storage
duration. */
#define INITIALIZE_P_IDENTIFIER "__initialize_p"
#define PRIORITY_IDENTIFIER "__priority"
/* The name of the function we create to handle initializations and
destructions for objects with static storage duration. */
#define SSDF_IDENTIFIER "__static_initialization_and_destruction"
/* The declaration for the __INITIALIZE_P argument. */
static tree initialize_p_decl;
/* The declaration for the __PRIORITY argument. */
static tree priority_decl;
/* The declaration for the static storage duration function. */
static tree ssdf_decl;
/* All the static storage duration functions created in this
translation unit. */
static varray_type ssdf_decls;
static size_t ssdf_decls_used;
/* A map from priority levels to information about that priority
level. There may be many such levels, so efficient lookup is
important. */
static splay_tree priority_info_map;
/* Begins the generation of the function that will handle all
initialization and destruction of objects with static storage
duration. The function generated takes two parameters of type
`int': __INITIALIZE_P and __PRIORITY. If __INITIALIZE_P is
non-zero, it performs initializations. Otherwise, it performs
destructions. It only performs those initializations or
destructions with the indicated __PRIORITY. The generated function
returns no value.
It is assumed that this function will only be called once per
translation unit. */
static void
start_static_storage_duration_function ()
{
static unsigned ssdf_number;
tree parm_types;
tree type;
char id[sizeof (SSDF_IDENTIFIER) + 1 /* '\0' */ + 32];
/* Create the identifier for this function. It will be of the form
SSDF_IDENTIFIER_<number>. */
sprintf (id, "%s_%u", SSDF_IDENTIFIER, ssdf_number++);
if (ssdf_number == 0)
{
/* Overflow occurred. That means there are at least 4 billion
initialization functions. */
sorry ("too many initialization functions required");
my_friendly_abort (19990430);
}
/* Create the parameters. */
parm_types = void_list_node;
parm_types = perm_tree_cons (NULL_TREE, integer_type_node, parm_types);
parm_types = perm_tree_cons (NULL_TREE, integer_type_node, parm_types);
type = build_function_type (void_type_node, parm_types);
/* Create the FUNCTION_DECL itself. */
ssdf_decl = build_lang_decl (FUNCTION_DECL,
get_identifier (id),
type);
TREE_PUBLIC (ssdf_decl) = 0;
DECL_ARTIFICIAL (ssdf_decl) = 1;
/* Put this function in the list of functions to be called from the
static constructors and destructors. */
if (!ssdf_decls)
{
VARRAY_TREE_INIT (ssdf_decls, 32, "ssdf_decls");
/* Take this opportunity to initialize the map from priority
numbers to information about that priority level. */
priority_info_map = splay_tree_new (splay_tree_compare_ints,
/*delete_key_fn=*/0,
/*delete_value_fn=*/
(splay_tree_delete_value_fn) &free);
/* We always need to generate functions for the
DEFAULT_INIT_PRIORITY so enter it now. That way when we walk
priorities later, we'll be sure to find the
DEFAULT_INIT_PRIORITY. */
get_priority_info (DEFAULT_INIT_PRIORITY);
}
if (ssdf_decls_used == ssdf_decls->num_elements)
VARRAY_GROW (ssdf_decls, 2 * ssdf_decls_used);
VARRAY_TREE (ssdf_decls, ssdf_decls_used) = ssdf_decl;
++ssdf_decls_used;
/* Create the argument list. */
initialize_p_decl = build_decl (PARM_DECL,
get_identifier (INITIALIZE_P_IDENTIFIER),
integer_type_node);
DECL_CONTEXT (initialize_p_decl) = ssdf_decl;
DECL_ARG_TYPE (initialize_p_decl) = integer_type_node;
TREE_USED (initialize_p_decl) = 1;
priority_decl = build_decl (PARM_DECL, get_identifier (PRIORITY_IDENTIFIER),
integer_type_node);
DECL_CONTEXT (priority_decl) = ssdf_decl;
DECL_ARG_TYPE (priority_decl) = integer_type_node;
TREE_USED (priority_decl) = 1;
TREE_CHAIN (initialize_p_decl) = priority_decl;
DECL_ARGUMENTS (ssdf_decl) = initialize_p_decl;
/* Start the function itself. This is equivalent to declarating the
function as:
static void __ssdf (int __initialize_p, init __priority_p);
It is static because we only need to call this function from the
various constructor and destructor functions for this module. */
start_function (/*specs=*/NULL_TREE,
ssdf_decl,
/*attrs=*/NULL_TREE,
/*pre_parsed_p=*/1);
/* Set up the scope of the outermost block in the function. */
store_parm_decls ();
pushlevel (0);
clear_last_expr ();
push_momentary ();
expand_start_bindings (0);
/* This function must not be deferred because we are depending on
its compilation to tell us what is TREE_SYMBOL_REFERENCED. */
current_function_cannot_inline
= "static storage duration functions cannot be inlined";
}
/* Generate the initialization code for the priority indicated in N. */
static int
generate_inits_for_priority (n, data)
splay_tree_node n;
void *data ATTRIBUTE_UNUSED;
{
int priority = (int) n->key;
priority_info pi = (priority_info) n->value;
/* For each priority N which has been used generate code which looks
like:
if (__priority == N) {
if (__initialize_p)
...
else
...
}
We use the sequences we've accumulated to fill in the `...'s. */
expand_start_cond (build_binary_op (EQ_EXPR,
priority_decl,
build_int_2 (priority, 0)),
/*exit_flag=*/0);
/* Do the initializations. */
expand_start_cond (build_binary_op (NE_EXPR,
initialize_p_decl,
integer_zero_node),
/*exit_flag=*/0);
if (pi->initialization_sequence)
{
rtx insns;
push_to_sequence (pi->initialization_sequence);
insns = gen_sequence ();
end_sequence ();
emit_insn (insns);
pi->initialization_sequence = NULL_RTX;
pi->initializations_p = 1;
}
/* Do the destructions. */
expand_start_else ();
if (pi->destruction_sequence)
{
rtx insns;
push_to_sequence (pi->destruction_sequence);
insns = gen_sequence ();
end_sequence ();
emit_insn (insns);
pi->destruction_sequence = NULL_RTX;
pi->destructions_p = 1;
}
/* Close out the conditionals. */
expand_end_cond ();
expand_end_cond ();
/* Don't stop iterating. */
return 0;
}
/* Finish the generation of the function which performs initialization
and destruction of objects with static storage duration. After
this point, no more such objects can be created. */
static void
finish_static_storage_duration_function ()
{
splay_tree_foreach (priority_info_map,
generate_inits_for_priority,
/*data=*/0);
/* Close out the function. */
expand_end_bindings (getdecls (), 1, 0);
poplevel (1, 0, 0);
pop_momentary ();
finish_function (lineno, 0, 0);
}
/* Return the information about the indicated PRIORITY level. If no
code to handle this level has yet been generated, generate the
appropriate prologue. */
static priority_info
get_priority_info (priority)
int priority;
{
priority_info pi;
splay_tree_node n;
n = splay_tree_lookup (priority_info_map,
(splay_tree_key) priority);
if (!n)
{
/* Create a new priority information structure, and insert it
into the map. */
pi = (priority_info) xmalloc (sizeof (struct priority_info_s));
pi->initialization_sequence = NULL_RTX;
pi->destruction_sequence = NULL_RTX;
pi->initializations_p = 0;
pi->destructions_p = 0;
splay_tree_insert (priority_info_map,
(splay_tree_key) priority,
(splay_tree_value) pi);
}
else
pi = (priority_info) n->value;
return pi;
}
/* Generate code to do the static initialization of DECL. The
initialization is INIT. If DECL may be initialized more than once
in different object files, SENTRY is the guard variable to
check. PRIORITY is the priority for the initialization. */
static void
do_static_initialization (decl, init, sentry, priority)
tree decl;
tree init;
tree sentry;
int priority;
{
priority_info pi;
/* Get the priority information for this PRIORITY, */
pi = get_priority_info (priority);
if (!pi->initialization_sequence)
start_sequence ();
else
push_to_sequence (pi->initialization_sequence);
/* Tell the debugger that we are at the location of the static
variable in question. */
emit_note (input_filename, lineno);
/* If there's a SENTRY, we only do the initialization if it is
zero, i.e., if we are the first to initialize it. */
if (sentry)
expand_start_cond (build_binary_op (EQ_EXPR,
build_unary_op (PREINCREMENT_EXPR,
sentry,
/*noconvert=*/0),
integer_one_node),
/*exit_flag=*/0);
/* Prepare a binding level for temporaries created during the
initialization. */
expand_start_target_temps ();
if (IS_AGGR_TYPE (TREE_TYPE (decl))
|| TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
expand_aggr_init (decl, init, 0);
else if (TREE_CODE (init) == TREE_VEC)
expand_expr (expand_vec_init (decl, TREE_VEC_ELT (init, 0),
TREE_VEC_ELT (init, 1),
TREE_VEC_ELT (init, 2), 0),
const0_rtx, VOIDmode, EXPAND_NORMAL);
else
expand_assignment (decl, init, 0, 0);
/* The expression might have involved increments and decrements. */
emit_queue ();
/* Cleanup any temporaries needed for the initial value. */
expand_end_target_temps ();
/* Cleanup any deferred pops from function calls. This would be done
by expand_end_cond, but we also need it when !SENTRY, since we are
constructing these sequences by parts. */
do_pending_stack_adjust ();
/* Close the conditional opened above. */
if (sentry)
expand_end_cond ();
/* Save the sequence for later use. */
pi->initialization_sequence = get_insns ();
end_sequence ();
}
/* Generate code to do the static destruction of DECL. If DECL may be
initialized more than once in different object files, SENTRY is the
guard variable to check. PRIORITY is the priority for the
destruction. */
static void
do_static_destruction (decl, sentry, priority)
tree decl;
tree sentry;
int priority;
{
rtx new_insns;
priority_info pi;
/* If we don't need a destructor, there's nothing to do. */
if (!TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (decl)))
return;
/* Get the priority information for this PRIORITY, */
pi = get_priority_info (priority);
if (!pi->destruction_sequence)
start_sequence ();
else
push_to_sequence (pi->destruction_sequence);
/* Start a new sequence to handle just this destruction. */
start_sequence ();
/* Tell the debugger that we are at the location of the static
variable in question. */
emit_note (input_filename, lineno);
/* If there's a SENTRY, we only do the destruction if it is one,
i.e., if we are the last to destroy it. */
if (sentry)
expand_start_cond (build_binary_op (EQ_EXPR,
build_unary_op (PREDECREMENT_EXPR,
sentry,
/*nonconvert=*/1),
integer_zero_node),
/*exit_flag=*/0);
/* Actually to the destruction. */
expand_expr_stmt (build_cleanup (decl));
/* Cleanup any deferred pops from function calls. This would be done
by expand_end_cond, but we also need it when !SENTRY, since we are
constructing these sequences by parts. */
do_pending_stack_adjust ();
/* Close the conditional opened above. */
if (sentry)
expand_end_cond ();
/* Insert the NEW_INSNS before the current insns. (Destructions are
run in reverse order of initializations.) */
new_insns = gen_sequence ();
end_sequence ();
if (pi->destruction_sequence)
emit_insn_before (new_insns, pi->destruction_sequence);
else
emit_insn (new_insns);
/* Save the sequence for later use. */
pi->destruction_sequence = get_insns ();
end_sequence ();
}
/* Add code to the static storage duration function that will handle
DECL (a static variable that needs initializing and/or destruction)
with the indicated PRIORITY. If DECL needs initializing, INIT is
the initializer. */
static void
do_static_initialization_and_destruction (decl, init)
tree decl;
tree init;
{
tree sentry = NULL_TREE;
int priority;
/* Deal gracefully with error. */
if (decl == error_mark_node)
return;
/* The only things that can be initialized are variables. */
my_friendly_assert (TREE_CODE (decl) == VAR_DECL, 19990420);
/* If this object is not defined, we don't need to do anything
here. */
if (DECL_EXTERNAL (decl))
return;
/* Also, if the initializer already contains errors, we can bail out
now. */
if (init && TREE_CODE (init) == TREE_LIST
&& value_member (error_mark_node, init))
return;
/* Trick the compiler into thinking we are at the file and line
where DECL was declared so that error-messages make sense, and so
that the debugger will show somewhat sensible file and line
information. */
input_filename = DECL_SOURCE_FILE (decl);
lineno = DECL_SOURCE_LINE (decl);
/* Because of:
[class.access.spec]
Access control for implicit calls to the constructors,
the conversion functions, or the destructor called to
create and destroy a static data member is performed as
if these calls appeared in the scope of the member's
class.
we pretend we are in a static member function of the class of
which the DECL is a member. */
if (member_p (decl))
{
DECL_CLASS_CONTEXT (current_function_decl) = DECL_CONTEXT (decl);
DECL_STATIC_FUNCTION_P (current_function_decl) = 1;
}
/* We need a sentry if this is an object with external linkage that
might be initialized in more than one place. */
if (TREE_PUBLIC (decl) && (DECL_COMMON (decl)
|| DECL_ONE_ONLY (decl)
|| DECL_WEAK (decl)))
sentry = get_sentry (DECL_ASSEMBLER_NAME (decl));
/* Generate the code to actually do the intialization and
destruction. */
priority = DECL_INIT_PRIORITY (decl);
if (!priority)
priority = DEFAULT_INIT_PRIORITY;
do_static_initialization (decl, init, sentry, priority);
do_static_destruction (decl, sentry, priority);
/* Now that we're done with DECL we don't need to pretend to be a
member of its class any longer. */
DECL_CLASS_CONTEXT (current_function_decl) = NULL_TREE;
DECL_STATIC_FUNCTION_P (current_function_decl) = 0;
}
/* Generate a static constructor (if CONSTRUCTOR_P) or destructor
(otherwise) that will initialize all gobal objects with static
storage duration having the indicated PRIORITY. */
static void
generate_ctor_or_dtor_function (constructor_p, priority)
int constructor_p;
int priority;
{
char function_key;
tree arguments;
size_t i;
/* We use `I' to indicate initialization and `D' to indicate
destruction. */
if (constructor_p)
function_key = 'I';
else
function_key = 'D';
/* Begin the function. */
start_objects (function_key, priority);
/* Call the static storage duration function with appropriate
arguments. */
for (i = 0; i < ssdf_decls_used; ++i)
{
arguments = tree_cons (NULL_TREE, build_int_2 (priority, 0),
NULL_TREE);
arguments = tree_cons (NULL_TREE, build_int_2 (constructor_p, 0),
arguments);
expand_expr_stmt (build_function_call (VARRAY_TREE (ssdf_decls, i),
arguments));
}
/* If we're generating code for the DEFAULT_INIT_PRIORITY, throw in
calls to any functions marked with attributes indicating that
they should be called at initialization- or destruction-time. */
if (priority == DEFAULT_INIT_PRIORITY)
{
tree fns;
for (fns = constructor_p ? static_ctors : static_dtors;
fns;
fns = TREE_CHAIN (fns))
expand_expr_stmt (build_function_call (TREE_VALUE (fns), NULL_TREE));
}
/* Close out the function. */
finish_objects (function_key, priority);
}
/* Generate constructor and destructor functions for the priority
indicated by N. */
static int
generate_ctor_and_dtor_functions_for_priority (n, data)
splay_tree_node n;
void *data ATTRIBUTE_UNUSED;
{
int priority = (int) n->key;
priority_info pi = (priority_info) n->value;
/* Generate the functions themselves, but only if they are really
needed. */
if (pi->initializations_p
|| (priority == DEFAULT_INIT_PRIORITY && static_ctors))
generate_ctor_or_dtor_function (/*constructor_p=*/1,
priority);
if (pi->destructions_p
|| (priority == DEFAULT_INIT_PRIORITY && static_dtors))
generate_ctor_or_dtor_function (/*constructor_p=*/0,
priority);
/* Keep iterating. */
return 0;
}
/* This routine is called from the last rule in yyparse ().
Its job is to create all the code needed to initialize and
destroy the global aggregates. We do the destruction
first, since that way we only need to reverse the decls once. */
void
finish_file ()
{
extern int lineno;
int start_time, this_time;
tree vars;
int reconsider;
size_t i;
at_eof = 1;
/* Bad parse errors. Just forget about it. */
if (! global_bindings_p () || current_class_type || decl_namespace_list)
return;
start_time = get_run_time ();
/* Otherwise, GDB can get confused, because in only knows
about source for LINENO-1 lines. */
lineno -= 1;
interface_unknown = 1;
interface_only = 0;
/* We now have to write out all the stuff we put off writing out.
These include:
o Template specializations that we have not yet instantiated,
but which are needed.
o Initialization and destruction for non-local objects with
static storage duration. (Local objects with static storage
duration are initialized when their scope is first entered,
and are cleaned up via atexit.)
o Virtual function tables.
All of these may cause others to be needed. For example,
instantiating one function may cause another to be needed, and
generating the intiailzer for an object may cause templates to be
instantiated, etc., etc. */
this_time = get_run_time ();
parse_time -= this_time - start_time;
varconst_time += this_time - start_time;
start_time = get_run_time ();
permanent_allocation (1);
do
{
/* Non-zero if we need a static storage duration function on
this iteration through the loop. */
int need_ssdf_p = 0;
reconsider = 0;
/* If there are templates that we've put off instantiating, do
them now. */
instantiate_pending_templates ();
/* Write out signature-tables and virtual tables as required.
Note that writing out the virtual table for a template class
may cause the instantiation of members of that class. */
if (flag_handle_signatures
&& walk_globals (sigtable_decl_p,
finish_sigtable_vardecl,
/*data=*/0))
reconsider = 1;
if (walk_globals (vtable_decl_p,
finish_vtable_vardecl,
/*data=*/0))
reconsider = 1;
/* The list of objects with static storage duration is built up
in reverse order, so we reverse it here. We also clear
STATIC_AGGREGATES so that any new aggregates added during the
initialization of these will be initialized in the correct
order when we next come around the loop. */
vars = nreverse (static_aggregates);
static_aggregates = NULL_TREE;
while (vars)
{
if (! TREE_ASM_WRITTEN (TREE_VALUE (vars)))
rest_of_decl_compilation (TREE_VALUE (vars), 0, 1, 1);
if (!need_ssdf_p)
{
/* We need to start a new initialization function each
time through the loop. That's because we need to
know which vtables have been referenced, and
TREE_SYMBOL_REFERENCED isn't computed until a
function is finished, and written out. That's a
deficiency in the back-end. When this is fixed,
these initialization functions could all become
inline, with resulting performance improvements. */
start_static_storage_duration_function ();
need_ssdf_p = 1;
}
do_static_initialization_and_destruction (TREE_VALUE (vars),
TREE_PURPOSE (vars));
reconsider = 1;
vars = TREE_CHAIN (vars);
}
/* Finish up the static storage duration function for this
round. */
if (need_ssdf_p)
finish_static_storage_duration_function ();
/* Go through the various inline functions, and see if any need
synthesizing. */
for (i = 0; i < saved_inlines_used; ++i)
{
tree decl = VARRAY_TREE (saved_inlines, i);
import_export_decl (decl);
if (DECL_ARTIFICIAL (decl) && ! DECL_INITIAL (decl)
&& TREE_USED (decl)
&& (! DECL_REALLY_EXTERN (decl) || DECL_INLINE (decl)))
{
/* Even though we're already at the top-level, we push
there again. That way, when we pop back a few lines
hence, all of our state is restored. Otherwise,
finish_function doesn't clean things up, and we end
up with CURRENT_FUNCTION_DECL set. */
push_to_top_level ();
if (DECL_TINFO_FN_P (decl))
synthesize_tinfo_fn (decl);
else
synthesize_method (decl);
pop_from_top_level ();
reconsider = 1;
}
}
/* Mark all functions that might deal with exception-handling as
referenced. */
mark_all_runtime_matches ();
/* We lie to the back-end, pretending that some functions are
not defined when they really are. This keeps these functions
from being put out unncessarily. But, we must stop lying
when the functions are referenced, or if they are not comdat
since they need to be put out now. */
for (i = 0; i < saved_inlines_used; ++i)
{
tree decl = VARRAY_TREE (saved_inlines, i);
if (DECL_NOT_REALLY_EXTERN (decl)
&& DECL_INITIAL (decl)
&& (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
|| !DECL_COMDAT (decl)))
DECL_EXTERNAL (decl) = 0;
}
if (saved_inlines_used
&& wrapup_global_declarations (&VARRAY_TREE (saved_inlines, 0),
saved_inlines_used))
reconsider = 1;
if (walk_namespaces (wrapup_globals_for_namespace, /*data=*/0))
reconsider = 1;
/* Static data members are just like namespace-scope globals. */
for (i = 0; i < pending_statics_used; ++i)
{
tree decl = VARRAY_TREE (pending_statics, i);
if (TREE_ASM_WRITTEN (decl))
continue;
import_export_decl (decl);
if (DECL_NOT_REALLY_EXTERN (decl) && ! DECL_IN_AGGR_P (decl))
DECL_EXTERNAL (decl) = 0;
}
if (pending_statics
&& wrapup_global_declarations (&VARRAY_TREE (pending_statics, 0),
pending_statics_used))
reconsider = 1;
}
while (reconsider);
/* We give C linkage to static constructors and destructors. */
push_lang_context (lang_name_c);
/* Generate initialization and destruction functions for all
priorities for which they are required. */
if (priority_info_map)
splay_tree_foreach (priority_info_map,
generate_ctor_and_dtor_functions_for_priority,
/*data=*/0);
/* We're done with the splay-tree now. */
if (priority_info_map)
splay_tree_delete (priority_info_map);
/* We're done with static constructors, so we can go back to "C++"
linkage now. */
pop_lang_context ();
/* Now delete from the chain of variables all virtual function tables.
We output them all ourselves, because each will be treated
specially. */
walk_globals (vtable_decl_p, prune_vtable_vardecl, /*data=*/0);
/* Now, issue warnings about static, but not defined, functions,
etc. */
walk_namespaces (wrapup_globals_for_namespace, /*data=*/&reconsider);
finish_repo ();
this_time = get_run_time ();
parse_time -= this_time - start_time;
varconst_time += this_time - start_time;
if (flag_detailed_statistics)
{
dump_tree_statistics ();
dump_time_statistics ();
}
}
/* This is something of the form 'A()()()()()+1' that has turned out to be an
expr. Since it was parsed like a type, we need to wade through and fix
that. Unfortunately, since operator() is left-associative, we can't use
tail recursion. In the above example, TYPE is `A', and DECL is
`()()()()()'.
Maybe this shouldn't be recursive, but how often will it actually be
used? (jason) */
tree
reparse_absdcl_as_expr (type, decl)
tree type, decl;
{
/* do build_functional_cast (type, NULL_TREE) at bottom */
if (TREE_OPERAND (decl, 0) == NULL_TREE)
return build_functional_cast (type, NULL_TREE);
/* recurse */
decl = reparse_absdcl_as_expr (type, TREE_OPERAND (decl, 0));
decl = build_x_function_call (decl, NULL_TREE, current_class_ref);
if (TREE_CODE (decl) == CALL_EXPR
&& (! TREE_TYPE (decl)
|| TREE_CODE (TREE_TYPE (decl)) != VOID_TYPE))
decl = require_complete_type (decl);
return decl;
}
/* This is something of the form `int ((int)(int)(int)1)' that has turned
out to be an expr. Since it was parsed like a type, we need to wade
through and fix that. Since casts are right-associative, we are
reversing the order, so we don't have to recurse.
In the above example, DECL is the `(int)(int)(int)', and EXPR is the
`1'. */
tree
reparse_absdcl_as_casts (decl, expr)
tree decl, expr;
{
tree type;
if (TREE_CODE (expr) == CONSTRUCTOR
&& TREE_TYPE (expr) == 0)
{
type = groktypename (TREE_VALUE (TREE_OPERAND (decl, 1)));
decl = TREE_OPERAND (decl, 0);
if (IS_SIGNATURE (type))
{
error ("cast specifies signature type");
return error_mark_node;
}
expr = digest_init (type, expr, (tree *) 0);
if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0)
{
int failure = complete_array_type (type, expr, 1);
if (failure)
my_friendly_abort (78);
}
}
while (decl)
{
type = groktypename (TREE_VALUE (TREE_OPERAND (decl, 1)));
decl = TREE_OPERAND (decl, 0);
expr = build_c_cast (type, expr);
}
if (warn_old_style_cast && ! in_system_header
&& current_lang_name != lang_name_c)
warning ("use of old-style cast");
return expr;
}
/* Given plain tree nodes for an expression, build up the full semantics. */
tree
build_expr_from_tree (t)
tree t;
{
if (t == NULL_TREE || t == error_mark_node)
return t;
switch (TREE_CODE (t))
{
case IDENTIFIER_NODE:
return do_identifier (t, 0, NULL_TREE);
case LOOKUP_EXPR:
if (LOOKUP_EXPR_GLOBAL (t))
return do_scoped_id (TREE_OPERAND (t, 0), 0);
else
return do_identifier (TREE_OPERAND (t, 0), 0, NULL_TREE);
case TEMPLATE_ID_EXPR:
return (lookup_template_function
(build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1))));
case INDIRECT_REF:
return build_x_indirect_ref
(build_expr_from_tree (TREE_OPERAND (t, 0)), "unary *");
case CAST_EXPR:
return build_functional_cast
(TREE_TYPE (t), build_expr_from_tree (TREE_OPERAND (t, 0)));
case REINTERPRET_CAST_EXPR:
return build_reinterpret_cast
(TREE_TYPE (t), build_expr_from_tree (TREE_OPERAND (t, 0)));
case CONST_CAST_EXPR:
return build_const_cast
(TREE_TYPE (t), build_expr_from_tree (TREE_OPERAND (t, 0)));
case DYNAMIC_CAST_EXPR:
return build_dynamic_cast
(TREE_TYPE (t), build_expr_from_tree (TREE_OPERAND (t, 0)));
case STATIC_CAST_EXPR:
return build_static_cast
(TREE_TYPE (t), build_expr_from_tree (TREE_OPERAND (t, 0)));
case PREDECREMENT_EXPR:
case PREINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
case NEGATE_EXPR:
case BIT_NOT_EXPR:
case ABS_EXPR:
case TRUTH_NOT_EXPR:
case ADDR_EXPR:
case CONVERT_EXPR: /* Unary + */
if (TREE_TYPE (t))
return t;
return build_x_unary_op (TREE_CODE (t),
build_expr_from_tree (TREE_OPERAND (t, 0)));
case PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
case TRUNC_DIV_EXPR:
case CEIL_DIV_EXPR:
case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR:
case BIT_AND_EXPR:
case BIT_ANDTC_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
case TRUNC_MOD_EXPR:
case FLOOR_MOD_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
case TRUTH_AND_EXPR:
case TRUTH_OR_EXPR:
case RSHIFT_EXPR:
case LSHIFT_EXPR:
case RROTATE_EXPR:
case LROTATE_EXPR:
case EQ_EXPR:
case NE_EXPR:
case MAX_EXPR:
case MIN_EXPR:
case LE_EXPR:
case GE_EXPR:
case LT_EXPR:
case GT_EXPR:
case MEMBER_REF:
return build_x_binary_op
(TREE_CODE (t),
build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1)));
case DOTSTAR_EXPR:
return build_m_component_ref
(build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1)));
case SCOPE_REF:
return build_offset_ref (TREE_OPERAND (t, 0), TREE_OPERAND (t, 1));
case ARRAY_REF:
if (TREE_OPERAND (t, 0) == NULL_TREE)
/* new-type-id */
return build_parse_node (ARRAY_REF, NULL_TREE,
build_expr_from_tree (TREE_OPERAND (t, 1)));
return grok_array_decl (build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1)));
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
{
tree r = build_expr_from_tree (TREE_OPERAND (t, 0));
if (TREE_CODE_CLASS (TREE_CODE (r)) != 't')
r = TREE_TYPE (r);
return TREE_CODE (t) == SIZEOF_EXPR ? c_sizeof (r) : c_alignof (r);
}
case MODOP_EXPR:
return build_x_modify_expr
(build_expr_from_tree (TREE_OPERAND (t, 0)),
TREE_CODE (TREE_OPERAND (t, 1)),
build_expr_from_tree (TREE_OPERAND (t, 2)));
case ARROW_EXPR:
return build_x_arrow
(build_expr_from_tree (TREE_OPERAND (t, 0)));
case NEW_EXPR:
return build_new
(build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1)),
build_expr_from_tree (TREE_OPERAND (t, 2)),
NEW_EXPR_USE_GLOBAL (t));
case DELETE_EXPR:
return delete_sanity
(build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1)),
DELETE_EXPR_USE_VEC (t), DELETE_EXPR_USE_GLOBAL (t));
case COMPOUND_EXPR:
if (TREE_OPERAND (t, 1) == NULL_TREE)
return build_x_compound_expr
(build_expr_from_tree (TREE_OPERAND (t, 0)));
else
my_friendly_abort (42);
case METHOD_CALL_EXPR:
if (TREE_CODE (TREE_OPERAND (t, 0)) == SCOPE_REF)
{
tree ref = TREE_OPERAND (t, 0);
return build_scoped_method_call
(build_expr_from_tree (TREE_OPERAND (t, 1)),
build_expr_from_tree (TREE_OPERAND (ref, 0)),
TREE_OPERAND (ref, 1),
build_expr_from_tree (TREE_OPERAND (t, 2)));
}
else
{
tree fn = TREE_OPERAND (t, 0);
/* We can get a TEMPLATE_ID_EXPR here on code like:
x->f<2>();
so we must resolve that. However, we can also get things
like a BIT_NOT_EXPR here, when referring to a destructor,
and things like that are not correctly resolved by
build_expr_from_tree. So, just use build_expr_from_tree
when we really need it. */
if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
fn = lookup_template_function
(TREE_OPERAND (fn, 0),
build_expr_from_tree (TREE_OPERAND (fn, 1)));
return build_method_call
(build_expr_from_tree (TREE_OPERAND (t, 1)),
fn,
build_expr_from_tree (TREE_OPERAND (t, 2)),
NULL_TREE, LOOKUP_NORMAL);
}
case CALL_EXPR:
if (TREE_CODE (TREE_OPERAND (t, 0)) == SCOPE_REF)
{
tree ref = TREE_OPERAND (t, 0);
return build_member_call
(build_expr_from_tree (TREE_OPERAND (ref, 0)),
TREE_OPERAND (ref, 1),
build_expr_from_tree (TREE_OPERAND (t, 1)));
}
else
{
tree name = TREE_OPERAND (t, 0);
tree id;
tree args = build_expr_from_tree (TREE_OPERAND (t, 1));
if (args != NULL_TREE && TREE_CODE (name) == LOOKUP_EXPR
&& !LOOKUP_EXPR_GLOBAL (name)
&& TREE_CODE ((id = TREE_OPERAND (name, 0))) == IDENTIFIER_NODE
&& (!current_class_type
|| !lookup_member (current_class_type, id, 0, 0)))
{
/* Do Koenig lookup if there are no class members. */
name = do_identifier (id, 0, args);
}
else if (TREE_CODE (name) == TEMPLATE_ID_EXPR
|| ! really_overloaded_fn (name))
name = build_expr_from_tree (name);
return build_x_function_call (name, args, current_class_ref);
}
case COND_EXPR:
return build_x_conditional_expr
(build_expr_from_tree (TREE_OPERAND (t, 0)),
build_expr_from_tree (TREE_OPERAND (t, 1)),
build_expr_from_tree (TREE_OPERAND (t, 2)));
case TREE_LIST:
{
tree purpose, value, chain;
if (t == void_list_node)
return t;
purpose = TREE_PURPOSE (t);
if (purpose)
purpose = build_expr_from_tree (purpose);
value = TREE_VALUE (t);
if (value)
value = build_expr_from_tree (value);
chain = TREE_CHAIN (t);
if (chain && chain != void_type_node)
chain = build_expr_from_tree (chain);
return expr_tree_cons (purpose, value, chain);
}
case COMPONENT_REF:
{
tree object = build_expr_from_tree (TREE_OPERAND (t, 0));
tree field = TREE_OPERAND (t, 1);
/* We use a COMPONENT_REF to indicate things of the form `x.b'
and `x.A::b'. We must distinguish between those cases
here. */
if (TREE_CODE (field) == SCOPE_REF)
return build_object_ref (object,
TREE_OPERAND (field, 0),
TREE_OPERAND (field, 1));
else
return build_x_component_ref (object, field,
NULL_TREE, 1);
}
case THROW_EXPR:
return build_throw (build_expr_from_tree (TREE_OPERAND (t, 0)));
case CONSTRUCTOR:
{
tree r;
/* digest_init will do the wrong thing if we let it. */
if (TREE_TYPE (t) && TYPE_PTRMEMFUNC_P (TREE_TYPE (t)))
return t;
r = build_nt (CONSTRUCTOR, NULL_TREE,
build_expr_from_tree (CONSTRUCTOR_ELTS (t)));
TREE_HAS_CONSTRUCTOR (r) = TREE_HAS_CONSTRUCTOR (t);
if (TREE_TYPE (t))
return digest_init (TREE_TYPE (t), r, 0);
return r;
}
case TYPEID_EXPR:
if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (t, 0))) == 't')
return get_typeid (TREE_OPERAND (t, 0));
return build_x_typeid (build_expr_from_tree (TREE_OPERAND (t, 0)));
case VAR_DECL:
return convert_from_reference (t);
default:
return t;
}
}
/* This is something of the form `int (*a)++' that has turned out to be an
expr. It was only converted into parse nodes, so we need to go through
and build up the semantics. Most of the work is done by
build_expr_from_tree, above.
In the above example, TYPE is `int' and DECL is `*a'. */
tree
reparse_decl_as_expr (type, decl)
tree type, decl;
{
decl = build_expr_from_tree (decl);
if (type)
return build_functional_cast (type, build_expr_list (NULL_TREE, decl));
else
return decl;
}
/* This is something of the form `int (*a)' that has turned out to be a
decl. It was only converted into parse nodes, so we need to do the
checking that make_{pointer,reference}_declarator do. */
tree
finish_decl_parsing (decl)
tree decl;
{
extern int current_class_depth;
switch (TREE_CODE (decl))
{
case IDENTIFIER_NODE:
return decl;
case INDIRECT_REF:
return make_pointer_declarator
(NULL_TREE, finish_decl_parsing (TREE_OPERAND (decl, 0)));
case ADDR_EXPR:
return make_reference_declarator
(NULL_TREE, finish_decl_parsing (TREE_OPERAND (decl, 0)));
case BIT_NOT_EXPR:
TREE_OPERAND (decl, 0) = finish_decl_parsing (TREE_OPERAND (decl, 0));
return decl;
case SCOPE_REF:
push_nested_class (TREE_TYPE (TREE_OPERAND (decl, 0)), 3);
TREE_COMPLEXITY (decl) = current_class_depth;
return decl;
case ARRAY_REF:
TREE_OPERAND (decl, 0) = finish_decl_parsing (TREE_OPERAND (decl, 0));
return decl;
case TREE_LIST:
/* For attribute handling. */
TREE_VALUE (decl) = finish_decl_parsing (TREE_VALUE (decl));
return decl;
default:
my_friendly_abort (5);
return NULL_TREE;
}
}
tree
check_cp_case_value (value)
tree value;
{
if (value == NULL_TREE)
return value;
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
STRIP_TYPE_NOPS (value);
if (TREE_READONLY_DECL_P (value))
{
value = decl_constant_value (value);
STRIP_TYPE_NOPS (value);
}
value = fold (value);
if (TREE_CODE (value) != INTEGER_CST
&& value != error_mark_node)
{
cp_error ("case label `%E' does not reduce to an integer constant",
value);
value = error_mark_node;
}
else
/* Promote char or short to int. */
value = default_conversion (value);
constant_expression_warning (value);
return value;
}
/* Return 1 if root encloses child. */
static int
is_namespace_ancestor (root, child)
tree root, child;
{
if (root == child)
return 1;
if (root == global_namespace)
return 1;
if (child == global_namespace)
return 0;
return is_namespace_ancestor (root, CP_DECL_CONTEXT (child));
}
/* Return the namespace that is the common ancestor
of two given namespaces. */
tree
namespace_ancestor (ns1, ns2)
tree ns1, ns2;
{
if (is_namespace_ancestor (ns1, ns2))
return ns1;
return namespace_ancestor (CP_DECL_CONTEXT (ns1), ns2);
}
/* Insert used into the using list of user. Set indirect_flag if this
directive is not directly from the source. Also find the common
ancestor and let our users know about the new namespace */
static void
add_using_namespace (user, used, indirect)
tree user;
tree used;
int indirect;
{
tree t;
/* Using oneself is a no-op. */
if (user == used)
return;
my_friendly_assert (TREE_CODE (user) == NAMESPACE_DECL, 380);
my_friendly_assert (TREE_CODE (used) == NAMESPACE_DECL, 380);
/* Check if we already have this. */
t = purpose_member (used, DECL_NAMESPACE_USING (user));
if (t != NULL_TREE)
{
if (!indirect)
/* Promote to direct usage. */
TREE_INDIRECT_USING (t) = 0;
return;
}
/* Add used to the user's using list. */
DECL_NAMESPACE_USING (user)
= perm_tree_cons (used, namespace_ancestor (user, used),
DECL_NAMESPACE_USING (user));
TREE_INDIRECT_USING (DECL_NAMESPACE_USING (user)) = indirect;
/* Add user to the used's users list. */
DECL_NAMESPACE_USERS (used)
= perm_tree_cons (user, 0, DECL_NAMESPACE_USERS (used));
/* Recursively add all namespaces used. */
for (t = DECL_NAMESPACE_USING (used); t; t = TREE_CHAIN (t))
/* indirect usage */
add_using_namespace (user, TREE_PURPOSE (t), 1);
/* Tell everyone using us about the new used namespaces. */
for (t = DECL_NAMESPACE_USERS (user); t; t = TREE_CHAIN (t))
add_using_namespace (TREE_PURPOSE (t), used, 1);
}
/* Combines two sets of overloaded functions into an OVERLOAD chain, removing
duplicates. The first list becomes the tail of the result.
The algorithm is O(n^2). We could get this down to O(n log n) by
doing a sort on the addresses of the functions, if that becomes
necessary. */
static tree
merge_functions (s1, s2)
tree s1;
tree s2;
{
for (; s2; s2 = OVL_NEXT (s2))
{
tree fn = OVL_CURRENT (s2);
if (! ovl_member (fn, s1))
s1 = build_overload (fn, s1);
}
return s1;
}
/* This should return an error not all definitions define functions.
It is not an error if we find two functions with exactly the
same signature, only if these are selected in overload resolution.
old is the current set of bindings, new the freshly-found binding.
XXX Do we want to give *all* candidates in case of ambiguity?
XXX In what way should I treat extern declarations?
XXX I don't want to repeat the entire duplicate_decls here */
static tree
ambiguous_decl (name, old, new, flags)
tree name;
tree old;
tree new;
int flags;
{
tree val, type;
my_friendly_assert (old != NULL_TREE, 393);
/* Copy the value. */
val = BINDING_VALUE (new);
if (val)
switch (TREE_CODE (val))
{
case TEMPLATE_DECL:
/* If we expect types or namespaces, and not templates,
or this is not a template class. */
if (LOOKUP_QUALIFIERS_ONLY (flags)
&& !DECL_CLASS_TEMPLATE_P (val))
val = NULL_TREE;
break;
case TYPE_DECL:
if (LOOKUP_NAMESPACES_ONLY (flags))
val = NULL_TREE;
break;
case NAMESPACE_DECL:
if (LOOKUP_TYPES_ONLY (flags))
val = NULL_TREE;
break;
default:
if (LOOKUP_QUALIFIERS_ONLY (flags))
val = NULL_TREE;
}
if (!BINDING_VALUE (old))
BINDING_VALUE (old) = val;
else if (val && val != BINDING_VALUE (old))
{
if (is_overloaded_fn (BINDING_VALUE (old))
&& is_overloaded_fn (val))
{
BINDING_VALUE (old) = merge_functions (BINDING_VALUE (old),
val);
}
else
{
/* Some declarations are functions, some are not. */
if (flags & LOOKUP_COMPLAIN)
{
/* If we've already given this error for this lookup,
BINDING_VALUE (old) is error_mark_node, so let's not
repeat ourselves. */
if (BINDING_VALUE (old) != error_mark_node)
{
cp_error ("use of `%D' is ambiguous", name);
cp_error_at (" first declared as `%#D' here",
BINDING_VALUE (old));
}
cp_error_at (" also declared as `%#D' here", val);
}
return error_mark_node;
}
}
/* ... and copy the type. */
type = BINDING_TYPE (new);
if (LOOKUP_NAMESPACES_ONLY (flags))
type = NULL_TREE;
if (!BINDING_TYPE (old))
BINDING_TYPE (old) = type;
else if (type && BINDING_TYPE (old) != type)
{
if (flags & LOOKUP_COMPLAIN)
{
cp_error ("`%D' denotes an ambiguous type",name);
cp_error_at (" first type here", BINDING_TYPE (old));
cp_error_at (" other type here", type);
}
}
return old;
}
/* Add the bindings of name in used namespaces to val.
The using list is defined by usings, and the lookup goes to scope.
Returns zero on errors. */
int
lookup_using_namespace (name, val, usings, scope, flags)
tree name, val, usings, scope;
int flags;
{
tree iter;
tree val1;
/* Iterate over all used namespaces in current, searching for using
directives of scope. */
for (iter = usings; iter; iter = TREE_CHAIN (iter))
if (TREE_VALUE (iter) == scope)
{
val1 = binding_for_name (name, TREE_PURPOSE (iter));
/* Resolve ambiguities. */
val = ambiguous_decl (name, val, val1, flags);
}
return val != error_mark_node;
}
/* [namespace.qual]
Excepts the name to lookup and its qualifying scope.
Returns the name/type pair found into the CPLUS_BINDING result,
or 0 on error. */
int
qualified_lookup_using_namespace (name, scope, result, flags)
tree name;
tree scope;
tree result;
int flags;
{
/* Maintain a list of namespaces visited... */
tree seen = NULL_TREE;
/* ... and a list of namespace yet to see. */
tree todo = NULL_TREE;
tree usings;
while (scope && (result != error_mark_node))
{
seen = temp_tree_cons (scope, NULL_TREE, seen);
result = ambiguous_decl (name, result,
binding_for_name (name, scope), flags);
if (!BINDING_VALUE (result) && !BINDING_TYPE (result))
/* Consider using directives. */
for (usings = DECL_NAMESPACE_USING (scope); usings;
usings = TREE_CHAIN (usings))
/* If this was a real directive, and we have not seen it. */
if (!TREE_INDIRECT_USING (usings)
&& !purpose_member (TREE_PURPOSE (usings), seen))
todo = temp_tree_cons (TREE_PURPOSE (usings), NULL_TREE, todo);
if (todo)
{
scope = TREE_PURPOSE (todo);
todo = TREE_CHAIN (todo);
}
else
scope = NULL_TREE; /* If there never was a todo list. */
}
return result != error_mark_node;
}
/* [namespace.memdef]/2 */
/* Set the context of a declaration to scope. Complain if we are not
outside scope. */
void
set_decl_namespace (decl, scope, friendp)
tree decl;
tree scope;
int friendp;
{
tree old;
if (scope == std_node)
scope = global_namespace;
/* Get rid of namespace aliases. */
scope = ORIGINAL_NAMESPACE (scope);
/* It is ok for friends to be qualified in parallel space. */
if (!friendp && !is_namespace_ancestor (current_namespace, scope))
cp_error ("declaration of `%D' not in a namespace surrounding `%D'",
decl, scope);
DECL_CONTEXT (decl) = FROB_CONTEXT (scope);
if (scope != current_namespace)
{
/* See whether this has been declared in the namespace. */
old = namespace_binding (DECL_NAME (decl), scope);
if (!old)
/* No old declaration at all. */
goto complain;
if (!is_overloaded_fn (decl))
/* Don't compare non-function decls with decls_match here,
since it can't check for the correct constness at this
point. pushdecl will find those errors later. */
return;
/* Since decl is a function, old should contain a function decl. */
if (!is_overloaded_fn (old))
goto complain;
if (processing_template_decl || processing_specialization)
/* We have not yet called push_template_decl to turn the
FUNCTION_DECL into a TEMPLATE_DECL, so the declarations
won't match. But, we'll check later, when we construct the
template. */
return;
for (; old; old = OVL_NEXT (old))
if (decls_match (decl, OVL_CURRENT (old)))
return;
}
else
return;
complain:
cp_error ("`%D' should have been declared inside `%D'",
decl, scope);
}
/* Compute the namespace where a declaration is defined. */
static tree
decl_namespace (decl)
tree decl;
{
while (DECL_CONTEXT (decl))
{
decl = DECL_CONTEXT (decl);
if (TREE_CODE (decl) == NAMESPACE_DECL)
return decl;
if (TREE_CODE_CLASS (TREE_CODE (decl)) == 't')
decl = TYPE_STUB_DECL (decl);
my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd', 390);
}
return global_namespace;
}
/* Return the namespace where the current declaration is declared. */
tree
current_decl_namespace ()
{
tree result;
/* If we have been pushed into a different namespace, use it. */
if (decl_namespace_list)
return TREE_PURPOSE (decl_namespace_list);
if (current_class_type)
result = decl_namespace (TYPE_STUB_DECL (current_class_type));
else if (current_function_decl)
result = decl_namespace (current_function_decl);
else
result = current_namespace;
return result;
}
/* Temporarily set the namespace for the current declaration. */
void
push_decl_namespace (decl)
tree decl;
{
if (TREE_CODE (decl) != NAMESPACE_DECL)
decl = decl_namespace (decl);
decl_namespace_list = tree_cons (decl, NULL_TREE, decl_namespace_list);
}
void
pop_decl_namespace ()
{
decl_namespace_list = TREE_CHAIN (decl_namespace_list);
}
/* Enter a class or namespace scope. */
void
push_scope (t)
tree t;
{
if (TREE_CODE (t) == NAMESPACE_DECL)
push_decl_namespace (t);
else
pushclass (t, 2);
}
/* Leave scope pushed by push_scope. */
void
pop_scope (t)
tree t;
{
if (TREE_CODE (t) == NAMESPACE_DECL)
pop_decl_namespace ();
else
popclass ();
}
/* [basic.lookup.koenig] */
/* A non-zero return value in the functions below indicates an error.
All nodes allocated in the procedure are on the scratch obstack. */
struct arg_lookup
{
tree name;
tree namespaces;
tree classes;
tree functions;
};
static int arg_assoc PROTO((struct arg_lookup*, tree));
static int arg_assoc_args PROTO((struct arg_lookup*, tree));
static int arg_assoc_type PROTO((struct arg_lookup*, tree));
static int add_function PROTO((struct arg_lookup *, tree));
static int arg_assoc_namespace PROTO((struct arg_lookup *, tree));
static int arg_assoc_class PROTO((struct arg_lookup *, tree));
/* Add a function to the lookup structure.
Returns 1 on error. */
static int
add_function (k, fn)
struct arg_lookup *k;
tree fn;
{
if (ovl_member (fn, k->functions))
return 0;
/* We must find only functions, or exactly one non-function. */
if (k->functions && is_overloaded_fn (k->functions)
&& is_overloaded_fn (fn))
k->functions = build_overload (fn, k->functions);
else
if(k->functions)
{
tree f1 = OVL_CURRENT (k->functions);
tree f2 = fn;
if (is_overloaded_fn (f1))
{
fn = f1; f1 = f2; f2 = fn;
}
cp_error_at ("`%D' is not a function,", f1);
cp_error_at (" conflict with `%D'", f2);
cp_error (" in call to `%D'", k->name);
return 1;
}
else
k->functions = fn;
return 0;
}
/* Add functions of a namespace to the lookup structure.
Returns 1 on error. */
static int
arg_assoc_namespace (k, scope)
struct arg_lookup *k;
tree scope;
{
tree value;
if (purpose_member (scope, k->namespaces))
return 0;
k->namespaces = tree_cons (scope, NULL_TREE, k->namespaces);
value = namespace_binding (k->name, scope);
if (!value)
return 0;
for (; value; value = OVL_NEXT (value))
if (add_function (k, OVL_CURRENT (value)))
return 1;
return 0;
}
/* Adds everything associated with class to the lookup structure.
Returns 1 on error. */
static int
arg_assoc_class (k, type)
struct arg_lookup* k;
tree type;
{
tree list, friends, context;
int i;
if (purpose_member (type, k->classes))
return 0;
k->classes = tree_cons (type, NULL_TREE, k->classes);
context = decl_namespace (TYPE_MAIN_DECL (type));
if (arg_assoc_namespace (k, context))
return 1;
/* Process baseclasses. */
for (i = 0; i < CLASSTYPE_N_BASECLASSES (type); i++)
if (arg_assoc_class (k, TYPE_BINFO_BASETYPE (type, i)))
return 1;
/* Process friends. */
for (list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type)); list;
list = TREE_CHAIN (list))
if (k->name == TREE_PURPOSE (list))
for (friends = TREE_VALUE (list); friends;
friends = TREE_CHAIN (friends))
/* Only interested in global functions with potentially hidden
(i.e. unqualified) declarations. */
if (TREE_PURPOSE (list) == error_mark_node && TREE_VALUE (list)
&& decl_namespace (TREE_VALUE (list)) == context)
if (add_function (k, TREE_VALUE (list)))
return 1;
/* Process template arguments. */
if (CLASSTYPE_TEMPLATE_INFO (type))
{
list = innermost_args (CLASSTYPE_TI_ARGS (type));
for (i = 0; i < TREE_VEC_LENGTH (list); ++i)
arg_assoc (k, TREE_VEC_ELT (list, i));
}
return 0;
}
/* Adds everything associated with a given type.
Returns 1 on error. */
static int
arg_assoc_type (k, type)
struct arg_lookup *k;
tree type;
{
switch (TREE_CODE (type))
{
case VOID_TYPE:
case INTEGER_TYPE:
case REAL_TYPE:
case COMPLEX_TYPE:
case CHAR_TYPE:
case BOOLEAN_TYPE:
return 0;
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_P (type))
return arg_assoc_type (k, TYPE_PTRMEMFUNC_FN_TYPE (type));
return arg_assoc_class (k, type);
case POINTER_TYPE:
case REFERENCE_TYPE:
case ARRAY_TYPE:
return arg_assoc_type (k, TREE_TYPE (type));
case UNION_TYPE:
case ENUMERAL_TYPE:
return arg_assoc_namespace (k, decl_namespace (TYPE_MAIN_DECL (type)));
case OFFSET_TYPE:
/* Pointer to member: associate class type and value type. */
if (arg_assoc_type (k, TYPE_OFFSET_BASETYPE (type)))
return 1;
return arg_assoc_type (k, TREE_TYPE (type));
case METHOD_TYPE:
/* The basetype is referenced in the first arg type, so just
fall through. */
case FUNCTION_TYPE:
/* Associate the parameter types. */
if (arg_assoc_args (k, TYPE_ARG_TYPES (type)))
return 1;
/* Associate the return type. */
return arg_assoc_type (k, TREE_TYPE (type));
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
return 0;
case LANG_TYPE:
if (type == unknown_type_node)
return 0;
/* else fall through */
default:
my_friendly_abort (390);
}
return 0;
}
/* Adds everything associated with arguments. Returns 1 on error. */
static int
arg_assoc_args (k, args)
struct arg_lookup* k;
tree args;
{
for (; args; args = TREE_CHAIN (args))
if (arg_assoc (k, TREE_VALUE (args)))
return 1;
return 0;
}
/* Adds everything associated with a given tree_node. Returns 1 on error. */
static int
arg_assoc (k, n)
struct arg_lookup* k;
tree n;
{
if (n == error_mark_node)
return 0;
if (TREE_CODE_CLASS (TREE_CODE (n)) == 't')
return arg_assoc_type (k, n);
if (! type_unknown_p (n))
return arg_assoc_type (k, TREE_TYPE (n));
if (TREE_CODE (n) == ADDR_EXPR)
n = TREE_OPERAND (n, 0);
if (TREE_CODE (n) == COMPONENT_REF)
n = TREE_OPERAND (n, 1);
if (TREE_CODE (n) == OFFSET_REF)
n = TREE_OPERAND (n, 1);
while (TREE_CODE (n) == TREE_LIST)
n = TREE_VALUE (n);
if (TREE_CODE (n) == FUNCTION_DECL)
return arg_assoc_type (k, TREE_TYPE (n));
if (TREE_CODE (n) == TEMPLATE_ID_EXPR)
{
/* [basic.lookup.koenig]
If T is a template-id, its associated namespaces and classes
are the namespace in which the template is defined; for
member templates, the member template's class; the namespaces
and classes associated with the types of the template
arguments provided for template type parameters (excluding
template template parameters); the namespaces in which any
template template arguments are defined; and the classes in
which any member templates used as template template
arguments are defined. [Note: non-type template arguments do
not contribute to the set of associated namespaces. ] */
tree template = TREE_OPERAND (n, 0);
tree args = TREE_OPERAND (n, 1);
tree ctx;
tree arg;
/* First, the template. There may actually be more than one if
this is an overloaded function template. But, in that case,
we only need the first; all the functions will be in the same
namespace. */
template = OVL_CURRENT (template);
ctx = CP_DECL_CONTEXT (template);
if (TREE_CODE (ctx) == NAMESPACE_DECL)
{
if (arg_assoc_namespace (k, ctx) == 1)
return 1;
}
/* It must be a member template. */
else if (arg_assoc_class (k, ctx) == 1)
return 1;
/* Now the arguments. */
for (arg = args; arg != NULL_TREE; arg = TREE_CHAIN (arg))
{
tree t = TREE_VALUE (arg);
if (TREE_CODE (t) == TEMPLATE_DECL)
{
ctx = CP_DECL_CONTEXT (t);
if (TREE_CODE (ctx) == NAMESPACE_DECL)
{
if (arg_assoc_namespace (k, ctx) == 1)
return 1;
}
else if (arg_assoc_class (k, ctx) == 1)
return 1;
}
else if (TREE_CODE_CLASS (TREE_CODE (t)) == 't'
&& arg_assoc_type (k, t) == 1)
return 1;
}
}
else
{
my_friendly_assert (TREE_CODE (n) == OVERLOAD, 980715);
for (; n; n = OVL_CHAIN (n))
if (arg_assoc_type (k, TREE_TYPE (OVL_FUNCTION (n))))
return 1;
}
return 0;
}
/* Performs Koenig lookup depending on arguments, where fns
are the functions found in normal lookup. */
tree
lookup_arg_dependent (name, fns, args)
tree name;
tree fns;
tree args;
{
struct arg_lookup k;
+
k.name = name;
k.functions = fns;
- k.namespaces = NULL_TREE;
k.classes = NULL_TREE;
-
+
+ /* Note that we've already looked at the current namespace during normal
+ unqualified lookup, unless we found a decl in function scope. */
+ if (fns && ! TREE_PERMANENT (OVL_CURRENT (fns)))
+ k.namespaces = NULL_TREE;
+ else
+ k.namespaces = scratch_tree_cons (current_decl_namespace (),
+ NULL_TREE, NULL_TREE);
+
push_scratch_obstack ();
arg_assoc_args (&k, args);
pop_obstacks ();
return k.functions;
}
/* Process a namespace-alias declaration. */
void
do_namespace_alias (alias, namespace)
tree alias, namespace;
{
if (TREE_CODE (namespace) != NAMESPACE_DECL)
{
/* The parser did not find it, so it's not there. */
cp_error ("unknown namespace `%D'", namespace);
return;
}
namespace = ORIGINAL_NAMESPACE (namespace);
/* Build the alias. */
alias = build_lang_decl (NAMESPACE_DECL, alias, void_type_node);
DECL_NAMESPACE_ALIAS (alias) = namespace;
pushdecl (alias);
}
/* Check a non-member using-declaration. Return the name and scope
being used, and the USING_DECL, or NULL_TREE on failure. */
static tree
validate_nonmember_using_decl (decl, scope, name)
tree decl;
tree *scope;
tree *name;
{
if (TREE_CODE (decl) == SCOPE_REF
&& TREE_OPERAND (decl, 0) == std_node)
{
if (namespace_bindings_p ()
&& current_namespace == global_namespace)
/* There's no need for a using declaration at all, here,
since `std' is the same as `::'. We can't just pass this
on because we'll complain later about declaring something
in the same scope as a using declaration with the same
name. We return NULL_TREE which indicates to the caller
that there's no need to do any further processing. */
return NULL_TREE;
*scope = global_namespace;
*name = TREE_OPERAND (decl, 1);
}
else if (TREE_CODE (decl) == SCOPE_REF)
{
*scope = TREE_OPERAND (decl, 0);
*name = TREE_OPERAND (decl, 1);
/* [namespace.udecl]
A using-declaration for a class member shall be a
member-declaration. */
if (TREE_CODE (*scope) != NAMESPACE_DECL)
{
if (TYPE_P (*scope))
cp_error ("`%T' is not a namespace", *scope);
else
cp_error ("`%D' is not a namespace", *scope);
return NULL_TREE;
}
}
else if (TREE_CODE (decl) == IDENTIFIER_NODE
|| TREE_CODE (decl) == TYPE_DECL
|| TREE_CODE (decl) == TEMPLATE_DECL)
{
*scope = global_namespace;
*name = decl;
}
else
my_friendly_abort (382);
if (TREE_CODE_CLASS (TREE_CODE (*name)) == 'd')
*name = DECL_NAME (*name);
/* Make a USING_DECL. */
return push_using_decl (*scope, *name);
}
/* Process local and global using-declarations. */
static void
do_nonmember_using_decl (scope, name, oldval, oldtype, newval, newtype)
tree scope, name;
tree oldval, oldtype;
tree *newval, *newtype;
{
tree decls;
struct tree_binding _decls;
*newval = *newtype = NULL_TREE;
decls = binding_init (&_decls);
if (!qualified_lookup_using_namespace (name, scope, decls, 0))
/* Lookup error */
return;
if (!BINDING_VALUE (decls) && !BINDING_TYPE (decls))
{
cp_error ("`%D' not declared", name);
return;
}
/* Check for using functions. */
if (BINDING_VALUE (decls) && is_overloaded_fn (BINDING_VALUE (decls)))
{
tree tmp, tmp1;
if (oldval && !is_overloaded_fn (oldval))
{
duplicate_decls (OVL_CURRENT (BINDING_VALUE (decls)), oldval);
oldval = NULL_TREE;
}
*newval = oldval;
for (tmp = BINDING_VALUE (decls); tmp; tmp = OVL_NEXT (tmp))
{
tree new_fn = OVL_CURRENT (tmp);
/* [namespace.udecl]
If a function declaration in namespace scope or block
scope has the same name and the same parameter types as a
function introduced by a using declaration the program is
ill-formed. */
for (tmp1 = oldval; tmp1; tmp1 = OVL_NEXT (tmp1))
{
tree old_fn = OVL_CURRENT (tmp1);
if (!OVL_USED (tmp1)
&& compparms (TYPE_ARG_TYPES (TREE_TYPE (new_fn)),
TYPE_ARG_TYPES (TREE_TYPE (old_fn))))
{
/* There was already a non-using declaration in
this scope with the same parameter types. */
cp_error ("`%D' is already declared in this scope",
name);
break;
}
else if (duplicate_decls (new_fn, old_fn))
/* We're re-using something we already used
before. We don't need to add it again. */
break;
}
/* If we broke out of the loop, there's no reason to add
this function to the using declarations for this
scope. */
if (tmp1)
continue;
*newval = build_overload (OVL_CURRENT (tmp), *newval);
if (TREE_CODE (*newval) != OVERLOAD)
*newval = ovl_cons (*newval, NULL_TREE);
OVL_USED (*newval) = 1;
}
}
else
{
*newval = BINDING_VALUE (decls);
if (oldval)
duplicate_decls (*newval, oldval);
}
*newtype = BINDING_TYPE (decls);
if (oldtype && *newtype && oldtype != *newtype)
{
cp_error ("using directive `%D' introduced ambiguous type `%T'",
name, oldtype);
return;
}
}
/* Process a using-declaration not appearing in class or local scope. */
void
do_toplevel_using_decl (decl)
tree decl;
{
tree scope, name, binding;
tree oldval, oldtype, newval, newtype;
decl = validate_nonmember_using_decl (decl, &scope, &name);
if (decl == NULL_TREE)
return;
binding = binding_for_name (name, current_namespace);
oldval = BINDING_VALUE (binding);
oldtype = BINDING_TYPE (binding);
do_nonmember_using_decl (scope, name, oldval, oldtype, &newval, &newtype);
/* Copy declarations found. */
if (newval)
BINDING_VALUE (binding) = newval;
if (newtype)
BINDING_TYPE (binding) = newtype;
return;
}
/* Process a using-declaration at function scope. */
void
do_local_using_decl (decl)
tree decl;
{
tree scope, name;
tree oldval, oldtype, newval, newtype;
decl = validate_nonmember_using_decl (decl, &scope, &name);
if (decl == NULL_TREE)
return;
oldval = lookup_name_current_level (name);
oldtype = lookup_type_current_level (name);
do_nonmember_using_decl (scope, name, oldval, oldtype, &newval, &newtype);
if (newval)
{
if (is_overloaded_fn (newval))
{
tree fn, term;
/* We only need to push declarations for those functions
that were not already bound in the current level.
The old value might be NULL_TREE, it might be a single
function, or an OVERLOAD. */
if (oldval && TREE_CODE (oldval) == OVERLOAD)
term = OVL_FUNCTION (oldval);
else
term = oldval;
for (fn = newval; fn && OVL_CURRENT (fn) != term;
fn = OVL_NEXT (fn))
push_overloaded_decl (OVL_CURRENT (fn),
PUSH_LOCAL | PUSH_USING);
}
else
push_local_binding (name, newval, PUSH_USING);
}
if (newtype)
set_identifier_type_value (name, newtype);
}
tree
do_class_using_decl (decl)
tree decl;
{
tree name, value;
if (TREE_CODE (decl) != SCOPE_REF
|| TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (decl, 0))) != 't')
{
cp_error ("using-declaration for non-member at class scope");
return NULL_TREE;
}
name = TREE_OPERAND (decl, 1);
if (TREE_CODE (name) == BIT_NOT_EXPR)
{
cp_error ("using-declaration for destructor");
return NULL_TREE;
}
if (TREE_CODE (name) == TYPE_DECL)
name = DECL_NAME (name);
my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 980716);
value = build_lang_field_decl (USING_DECL, name, void_type_node);
DECL_INITIAL (value) = TREE_OPERAND (decl, 0);
return value;
}
/* Process a using-directive. */
void
do_using_directive (namespace)
tree namespace;
{
if (namespace == std_node)
return;
/* using namespace A::B::C; */
if (TREE_CODE (namespace) == SCOPE_REF)
namespace = TREE_OPERAND (namespace, 1);
if (TREE_CODE (namespace) == IDENTIFIER_NODE)
{
/* Lookup in lexer did not find a namespace. */
cp_error ("namespace `%T' undeclared", namespace);
return;
}
if (TREE_CODE (namespace) != NAMESPACE_DECL)
{
cp_error ("`%T' is not a namespace", namespace);
return;
}
namespace = ORIGINAL_NAMESPACE (namespace);
if (!toplevel_bindings_p ())
push_using_directive (namespace);
else
/* direct usage */
add_using_namespace (current_namespace, namespace, 0);
}
void
check_default_args (x)
tree x;
{
tree arg = TYPE_ARG_TYPES (TREE_TYPE (x));
int saw_def = 0, i = 0 - (TREE_CODE (TREE_TYPE (x)) == METHOD_TYPE);
for (; arg && arg != void_list_node; arg = TREE_CHAIN (arg), ++i)
{
if (TREE_PURPOSE (arg))
saw_def = 1;
else if (saw_def)
{
cp_error_at ("default argument missing for parameter %P of `%+#D'",
i, x);
break;
}
}
}
void
mark_used (decl)
tree decl;
{
TREE_USED (decl) = 1;
if (processing_template_decl)
return;
assemble_external (decl);
/* Is it a synthesized method that needs to be synthesized? */
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_CLASS_CONTEXT (decl)
&& DECL_ARTIFICIAL (decl) && ! DECL_INITIAL (decl)
/* Kludge: don't synthesize for default args. */
&& current_function_decl)
synthesize_method (decl);
/* If this is a function or variable that is an instance of some
template, we now know that we will need to actually do the
instantiation. A TEMPLATE_DECL may also have DECL_TEMPLATE_INFO,
if it's a partial instantiation, but there's no need to
instantiate such a thing. We check that DECL is not an explicit
instantiation because that is not checked in instantiate_decl. */
if (TREE_CODE (decl) != TEMPLATE_DECL
&& DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)
&& !DECL_EXPLICIT_INSTANTIATION (decl))
instantiate_decl (decl);
}
/* Helper function for named_class_head_sans_basetype nonterminal. We
have just seen something of the form `AGGR SCOPE::ID'. Return a
TYPE_DECL for the type declared by ID in SCOPE. */
tree
handle_class_head (aggr, scope, id)
tree aggr, scope, id;
{
tree decl;
if (TREE_CODE (id) == TYPE_DECL)
decl = id;
else if (DECL_CLASS_TEMPLATE_P (id))
decl = DECL_TEMPLATE_RESULT (id);
else
{
if (scope)
cp_error ("`%T' does not have a nested type named `%D'", scope, id);
else
cp_error ("no file-scope type named `%D'", id);
decl = TYPE_MAIN_DECL (xref_tag (aggr, make_anon_name (), 1));
}
/* This syntax is only allowed when we're defining a type, so we
enter the SCOPE. */
push_scope (CP_DECL_CONTEXT (decl));
/* If we see something like:
template <typename T> struct S::I ....
we must create a TEMPLATE_DECL for the nested type. */
if (PROCESSING_REAL_TEMPLATE_DECL_P ())
decl = push_template_decl (decl);
return decl;
}
Index: head/contrib/gcc/cp/pt.c
===================================================================
--- head/contrib/gcc/cp/pt.c (revision 52750)
+++ head/contrib/gcc/cp/pt.c (revision 52751)
@@ -1,9832 +1,9840 @@
/* Handle parameterized types (templates) for GNU C++.
Copyright (C) 1992, 93-97, 1998, 1999 Free Software Foundation, Inc.
Written by Ken Raeburn (raeburn@cygnus.com) while at Watchmaker Computing.
Rewritten by Jason Merrill (jason@cygnus.com).
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. */
/* Known bugs or deficiencies include:
all methods must be provided in header files; can't use a source
file that contains only the method templates and "just win". */
#include "config.h"
#include "system.h"
#include "obstack.h"
#include "tree.h"
#include "flags.h"
#include "cp-tree.h"
#include "decl.h"
#include "parse.h"
#include "lex.h"
#include "output.h"
#include "defaults.h"
#include "except.h"
#include "toplev.h"
#include "rtl.h"
#include "varray.h"
/* The type of functions taking a tree, and some additional data, and
returning an int. */
typedef int (*tree_fn_t) PROTO((tree, void*));
extern struct obstack permanent_obstack;
extern int lineno;
extern char *input_filename;
tree current_template_parms;
HOST_WIDE_INT processing_template_decl;
/* The PENDING_TEMPLATES is a TREE_LIST of templates whose
instantiations have been deferred, either because their definitions
were not yet available, or because we were putting off doing the
work. The TREE_PURPOSE of each entry is a SRCLOC indicating where
the instantiate request occurred; the TREE_VALUE is a either a DECL
(for a function or static data member), or a TYPE (for a class)
indicating what we are hoping to instantiate. */
static tree pending_templates;
static tree *template_tail = &pending_templates;
static tree maybe_templates;
static tree *maybe_template_tail = &maybe_templates;
int minimal_parse_mode;
int processing_specialization;
int processing_explicit_instantiation;
int processing_template_parmlist;
static int template_header_count;
static tree saved_trees;
static varray_type inline_parm_levels;
static size_t inline_parm_levels_used;
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
#define UNIFY_ALLOW_NONE 0
#define UNIFY_ALLOW_MORE_CV_QUAL 1
#define UNIFY_ALLOW_LESS_CV_QUAL 2
#define UNIFY_ALLOW_DERIVED 4
#define UNIFY_ALLOW_INTEGER 8
#define GTB_VIA_VIRTUAL 1 /* The base class we are examining is
virtual, or a base class of a virtual
base. */
#define GTB_IGNORE_TYPE 2 /* We don't need to try to unify the current
type with the desired type. */
static int resolve_overloaded_unification PROTO((tree, tree, tree, tree,
unification_kind_t, int));
static int try_one_overload PROTO((tree, tree, tree, tree, tree,
unification_kind_t, int));
static int unify PROTO((tree, tree, tree, tree, int));
static void add_pending_template PROTO((tree));
static int push_tinst_level PROTO((tree));
static tree classtype_mangled_name PROTO((tree));
static char *mangle_class_name_for_template PROTO((char *, tree, tree));
static tree tsubst_expr_values PROTO((tree, tree));
static int list_eq PROTO((tree, tree));
static tree get_class_bindings PROTO((tree, tree, tree));
static tree coerce_template_parms PROTO((tree, tree, tree, int, int));
static void tsubst_enum PROTO((tree, tree, tree));
static tree add_to_template_args PROTO((tree, tree));
static tree add_outermost_template_args PROTO((tree, tree));
static void maybe_adjust_types_for_deduction PROTO((unification_kind_t, tree*,
tree*));
static int type_unification_real PROTO((tree, tree, tree, tree,
int, unification_kind_t, int));
static void note_template_header PROTO((int));
static tree maybe_fold_nontype_arg PROTO((tree));
static tree convert_nontype_argument PROTO((tree, tree));
static tree convert_template_argument PROTO ((tree, tree, tree, int,
int , tree));
static tree get_bindings_overload PROTO((tree, tree, tree));
static int for_each_template_parm PROTO((tree, tree_fn_t, void*));
static tree build_template_parm_index PROTO((int, int, int, tree, tree));
static int inline_needs_template_parms PROTO((tree));
static void push_inline_template_parms_recursive PROTO((tree, int));
static tree retrieve_specialization PROTO((tree, tree));
static tree register_specialization PROTO((tree, tree, tree));
static int unregister_specialization PROTO((tree, tree));
static tree reduce_template_parm_level PROTO((tree, tree, int));
static tree build_template_decl PROTO((tree, tree));
static int mark_template_parm PROTO((tree, void *));
static tree tsubst_friend_function PROTO((tree, tree));
static tree tsubst_friend_class PROTO((tree, tree));
static tree get_bindings_real PROTO((tree, tree, tree, int));
static int template_decl_level PROTO((tree));
static tree maybe_get_template_decl_from_type_decl PROTO((tree));
static int check_cv_quals_for_unify PROTO((int, tree, tree));
static tree tsubst_template_arg_vector PROTO((tree, tree, int));
static tree tsubst_template_parms PROTO((tree, tree, int));
static void regenerate_decl_from_template PROTO((tree, tree));
static tree most_specialized PROTO((tree, tree, tree));
static tree most_specialized_class PROTO((tree, tree));
static tree most_general_template PROTO((tree));
static void set_mangled_name_for_template_decl PROTO((tree));
static int template_class_depth_real PROTO((tree, int));
static tree tsubst_aggr_type PROTO((tree, tree, int, tree, int));
static tree tsubst_decl PROTO((tree, tree, tree, tree));
static tree tsubst_arg_types PROTO((tree, tree, int, tree));
static tree tsubst_function_type PROTO((tree, tree, int, tree));
static void check_specialization_scope PROTO((void));
static tree process_partial_specialization PROTO((tree));
static void set_current_access_from_decl PROTO((tree));
static void check_default_tmpl_args PROTO((tree, tree, int, int));
static tree tsubst_call_declarator_parms PROTO((tree, tree, int, tree));
static tree get_template_base_recursive PROTO((tree, tree,
tree, tree, tree, int));
static tree get_template_base PROTO((tree, tree, tree, tree));
static tree try_class_unification PROTO((tree, tree, tree, tree));
static int coerce_template_template_parms PROTO((tree, tree, int,
tree, tree));
static tree determine_specialization PROTO((tree, tree, tree *, int));
static int template_args_equal PROTO((tree, tree));
static void print_template_context PROTO((int));
/* We use TREE_VECs to hold template arguments. If there is only one
level of template arguments, then the TREE_VEC contains the
arguments directly. If there is more than one level of template
arguments, then each entry in the TREE_VEC is itself a TREE_VEC,
containing the template arguments for a single level. The first
entry in the outer TREE_VEC is the outermost level of template
parameters; the last is the innermost.
It is incorrect to ever form a template argument vector containing
only one level of arguments, but which is a TREE_VEC containing as
its only entry the TREE_VEC for that level. */
/* Non-zero if the template arguments is actually a vector of vectors,
rather than just a vector. */
#define TMPL_ARGS_HAVE_MULTIPLE_LEVELS(NODE) \
(NODE != NULL_TREE \
&& TREE_CODE (NODE) == TREE_VEC \
&& TREE_VEC_LENGTH (NODE) > 0 \
&& TREE_VEC_ELT (NODE, 0) != NULL_TREE \
&& TREE_CODE (TREE_VEC_ELT (NODE, 0)) == TREE_VEC)
/* The depth of a template argument vector. When called directly by
the parser, we use a TREE_LIST rather than a TREE_VEC to represent
template arguments. In fact, we may even see NULL_TREE if there
are no template arguments. In both of those cases, there is only
one level of template arguments. */
#define TMPL_ARGS_DEPTH(NODE) \
(TMPL_ARGS_HAVE_MULTIPLE_LEVELS (NODE) ? TREE_VEC_LENGTH (NODE) : 1)
/* The LEVELth level of the template ARGS. Note that template
parameter levels are indexed from 1, not from 0. */
#define TMPL_ARGS_LEVEL(ARGS, LEVEL) \
(TMPL_ARGS_HAVE_MULTIPLE_LEVELS (ARGS) \
? TREE_VEC_ELT ((ARGS), (LEVEL) - 1) : ARGS)
/* Set the LEVELth level of the template ARGS to VAL. This macro does
not work with single-level argument vectors. */
#define SET_TMPL_ARGS_LEVEL(ARGS, LEVEL, VAL) \
(TREE_VEC_ELT ((ARGS), (LEVEL) - 1) = (VAL))
/* Accesses the IDXth parameter in the LEVELth level of the ARGS. */
#define TMPL_ARG(ARGS, LEVEL, IDX) \
(TREE_VEC_ELT (TMPL_ARGS_LEVEL (ARGS, LEVEL), IDX))
/* Set the IDXth element in the LEVELth level of ARGS to VAL. This
macro does not work with single-level argument vectors. */
#define SET_TMPL_ARG(ARGS, LEVEL, IDX, VAL) \
(TREE_VEC_ELT (TREE_VEC_ELT ((ARGS), (LEVEL) - 1), (IDX)) = (VAL))
/* Given a single level of template arguments in NODE, return the
number of arguments. */
#define NUM_TMPL_ARGS(NODE) \
((NODE) == NULL_TREE ? 0 \
: (TREE_CODE (NODE) == TREE_VEC \
? TREE_VEC_LENGTH (NODE) : list_length (NODE)))
/* The number of levels of template parameters given by NODE. */
#define TMPL_PARMS_DEPTH(NODE) \
(TREE_INT_CST_HIGH (TREE_PURPOSE (NODE)))
/* Do any processing required when DECL (a member template declaration
using TEMPLATE_PARAMETERS as its innermost parameter list) is
finished. Returns the TEMPLATE_DECL corresponding to DECL, unless
it is a specialization, in which case the DECL itself is returned. */
tree
finish_member_template_decl (decl)
tree decl;
{
if (decl == NULL_TREE || decl == void_type_node)
return NULL_TREE;
else if (decl == error_mark_node)
/* By returning NULL_TREE, the parser will just ignore this
declaration. We have already issued the error. */
return NULL_TREE;
else if (TREE_CODE (decl) == TREE_LIST)
{
/* Assume that the class is the only declspec. */
decl = TREE_VALUE (decl);
if (IS_AGGR_TYPE (decl) && CLASSTYPE_TEMPLATE_INFO (decl)
&& ! CLASSTYPE_TEMPLATE_SPECIALIZATION (decl))
{
tree tmpl = CLASSTYPE_TI_TEMPLATE (decl);
check_member_template (tmpl);
return tmpl;
}
return NULL_TREE;
}
else if (DECL_TEMPLATE_INFO (decl))
{
if (!DECL_TEMPLATE_SPECIALIZATION (decl))
{
check_member_template (DECL_TI_TEMPLATE (decl));
return DECL_TI_TEMPLATE (decl);
}
else
return decl;
}
else
cp_error ("invalid member template declaration `%D'", decl);
return error_mark_node;
}
/* Returns the template nesting level of the indicated class TYPE.
For example, in:
template <class T>
struct A
{
template <class U>
struct B {};
};
A<T>::B<U> has depth two, while A<T> has depth one.
Both A<T>::B<int> and A<int>::B<U> have depth one, if
COUNT_SPECIALIZATIONS is 0 or if they are instantiations, not
specializations.
This function is guaranteed to return 0 if passed NULL_TREE so
that, for example, `template_class_depth (current_class_type)' is
always safe. */
static int
template_class_depth_real (type, count_specializations)
tree type;
int count_specializations;
{
int depth;
for (depth = 0;
type && TREE_CODE (type) != NAMESPACE_DECL;
type = (TREE_CODE (type) == FUNCTION_DECL)
? DECL_REAL_CONTEXT (type) : TYPE_CONTEXT (type))
{
if (TREE_CODE (type) != FUNCTION_DECL)
{
if (CLASSTYPE_TEMPLATE_INFO (type)
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))
&& ((count_specializations
&& CLASSTYPE_TEMPLATE_SPECIALIZATION (type))
|| uses_template_parms (CLASSTYPE_TI_ARGS (type))))
++depth;
}
else
{
if (DECL_TEMPLATE_INFO (type)
&& PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (type))
&& ((count_specializations
&& DECL_TEMPLATE_SPECIALIZATION (type))
|| uses_template_parms (DECL_TI_ARGS (type))))
++depth;
}
}
return depth;
}
/* Returns the template nesting level of the indicated class TYPE.
Like template_class_depth_real, but instantiations do not count in
the depth. */
int
template_class_depth (type)
tree type;
{
return template_class_depth_real (type, /*count_specializations=*/0);
}
/* Returns 1 if processing DECL as part of do_pending_inlines
needs us to push template parms. */
static int
inline_needs_template_parms (decl)
tree decl;
{
if (! DECL_TEMPLATE_INFO (decl))
return 0;
return (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (most_general_template (decl)))
> (processing_template_decl + DECL_TEMPLATE_SPECIALIZATION (decl)));
}
/* Subroutine of maybe_begin_member_template_processing.
Push the template parms in PARMS, starting from LEVELS steps into the
chain, and ending at the beginning, since template parms are listed
innermost first. */
static void
push_inline_template_parms_recursive (parmlist, levels)
tree parmlist;
int levels;
{
tree parms = TREE_VALUE (parmlist);
int i;
if (levels > 1)
push_inline_template_parms_recursive (TREE_CHAIN (parmlist), levels - 1);
++processing_template_decl;
current_template_parms
= tree_cons (build_int_2 (0, processing_template_decl),
parms, current_template_parms);
TEMPLATE_PARMS_FOR_INLINE (current_template_parms) = 1;
pushlevel (0);
for (i = 0; i < TREE_VEC_LENGTH (parms); ++i)
{
tree parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (parm)) == 'd', 0);
switch (TREE_CODE (parm))
{
case TYPE_DECL:
case TEMPLATE_DECL:
pushdecl (parm);
break;
case PARM_DECL:
{
/* Make a CONST_DECL as is done in process_template_parm.
It is ugly that we recreate this here; the original
version built in process_template_parm is no longer
available. */
tree decl = build_decl (CONST_DECL, DECL_NAME (parm),
TREE_TYPE (parm));
SET_DECL_ARTIFICIAL (decl);
DECL_INITIAL (decl) = DECL_INITIAL (parm);
DECL_TEMPLATE_PARM_P (decl) = 1;
pushdecl (decl);
}
break;
default:
my_friendly_abort (0);
}
}
}
/* Restore the template parameter context for a member template or
a friend template defined in a class definition. */
void
maybe_begin_member_template_processing (decl)
tree decl;
{
tree parms;
int levels = 0;
if (inline_needs_template_parms (decl))
{
parms = DECL_TEMPLATE_PARMS (most_general_template (decl));
levels = TMPL_PARMS_DEPTH (parms) - processing_template_decl;
if (DECL_TEMPLATE_SPECIALIZATION (decl))
{
--levels;
parms = TREE_CHAIN (parms);
}
push_inline_template_parms_recursive (parms, levels);
}
/* Remember how many levels of template parameters we pushed so that
we can pop them later. */
if (!inline_parm_levels)
VARRAY_INT_INIT (inline_parm_levels, 4, "inline_parm_levels");
if (inline_parm_levels_used == inline_parm_levels->num_elements)
VARRAY_GROW (inline_parm_levels, 2 * inline_parm_levels_used);
VARRAY_INT (inline_parm_levels, inline_parm_levels_used) = levels;
++inline_parm_levels_used;
}
/* Undo the effects of begin_member_template_processing. */
void
maybe_end_member_template_processing ()
{
int i;
if (!inline_parm_levels_used)
return;
--inline_parm_levels_used;
for (i = 0;
i < VARRAY_INT (inline_parm_levels, inline_parm_levels_used);
++i)
{
--processing_template_decl;
current_template_parms = TREE_CHAIN (current_template_parms);
poplevel (0, 0, 0);
}
}
/* Returns non-zero iff T is a member template function. We must be
careful as in
template <class T> class C { void f(); }
Here, f is a template function, and a member, but not a member
template. This function does not concern itself with the origin of
T, only its present state. So if we have
template <class T> class C { template <class U> void f(U); }
then neither C<int>::f<char> nor C<T>::f<double> is considered
to be a member template. But, `template <class U> void
C<int>::f(U)' is considered a member template. */
int
is_member_template (t)
tree t;
{
if (!DECL_FUNCTION_TEMPLATE_P (t))
/* Anything that isn't a function or a template function is
certainly not a member template. */
return 0;
/* A local class can't have member templates. */
if (hack_decl_function_context (t))
return 0;
return (DECL_FUNCTION_MEMBER_P (DECL_TEMPLATE_RESULT (t))
/* If there are more levels of template parameters than
there are template classes surrounding the declaration,
then we have a member template. */
&& (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (t)) >
template_class_depth (DECL_CLASS_CONTEXT (t))));
}
#if 0 /* UNUSED */
/* Returns non-zero iff T is a member template class. See
is_member_template for a description of what precisely constitutes
a member template. */
int
is_member_template_class (t)
tree t;
{
if (!DECL_CLASS_TEMPLATE_P (t))
/* Anything that isn't a class template, is certainly not a member
template. */
return 0;
if (!DECL_CLASS_SCOPE_P (t))
/* Anything whose context isn't a class type is surely not a
member template. */
return 0;
/* If there are more levels of template parameters than there are
template classes surrounding the declaration, then we have a
member template. */
return (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (t)) >
template_class_depth (DECL_CONTEXT (t)));
}
#endif
/* Return a new template argument vector which contains all of ARGS,
but has as its innermost set of arguments the EXTRA_ARGS. The
resulting vector will be built on a temporary obstack, and so must
be explicitly copied to the permanent obstack, if required. */
static tree
add_to_template_args (args, extra_args)
tree args;
tree extra_args;
{
tree new_args;
int extra_depth;
int i;
int j;
extra_depth = TMPL_ARGS_DEPTH (extra_args);
new_args = make_temp_vec (TMPL_ARGS_DEPTH (args) + extra_depth);
for (i = 1; i <= TMPL_ARGS_DEPTH (args); ++i)
SET_TMPL_ARGS_LEVEL (new_args, i, TMPL_ARGS_LEVEL (args, i));
for (j = 1; j <= extra_depth; ++j, ++i)
SET_TMPL_ARGS_LEVEL (new_args, i, TMPL_ARGS_LEVEL (extra_args, j));
return new_args;
}
/* Like add_to_template_args, but only the outermost ARGS are added to
the EXTRA_ARGS. In particular, all but TMPL_ARGS_DEPTH
(EXTRA_ARGS) levels are added. This function is used to combine
the template arguments from a partial instantiation with the
template arguments used to attain the full instantiation from the
partial instantiation. */
static tree
add_outermost_template_args (args, extra_args)
tree args;
tree extra_args;
{
tree new_args;
/* If there are more levels of EXTRA_ARGS than there are ARGS,
something very fishy is going on. */
my_friendly_assert (TMPL_ARGS_DEPTH (args) >= TMPL_ARGS_DEPTH (extra_args),
0);
/* If *all* the new arguments will be the EXTRA_ARGS, just return
them. */
if (TMPL_ARGS_DEPTH (args) == TMPL_ARGS_DEPTH (extra_args))
return extra_args;
/* For the moment, we make ARGS look like it contains fewer levels. */
TREE_VEC_LENGTH (args) -= TMPL_ARGS_DEPTH (extra_args);
new_args = add_to_template_args (args, extra_args);
/* Now, we restore ARGS to its full dimensions. */
TREE_VEC_LENGTH (args) += TMPL_ARGS_DEPTH (extra_args);
return new_args;
}
/* We've got a template header coming up; push to a new level for storing
the parms. */
void
begin_template_parm_list ()
{
/* We use a non-tag-transparent scope here, which causes pushtag to
put tags in this scope, rather than in the enclosing class or
namespace scope. This is the right thing, since we want
TEMPLATE_DECLS, and not TYPE_DECLS for template classes. For a
global template class, push_template_decl handles putting the
TEMPLATE_DECL into top-level scope. For a nested template class,
e.g.:
template <class T> struct S1 {
template <class T> struct S2 {};
};
pushtag contains special code to call pushdecl_with_scope on the
TEMPLATE_DECL for S2. */
pushlevel (0);
declare_pseudo_global_level ();
++processing_template_decl;
++processing_template_parmlist;
note_template_header (0);
}
/* This routine is called when a specialization is declared. If it is
illegal to declare a specialization here, an error is reported. */
static void
check_specialization_scope ()
{
tree scope = current_scope ();
/* [temp.expl.spec]
An explicit specialization shall be declared in the namespace of
which the template is a member, or, for member templates, in the
namespace of which the enclosing class or enclosing class
template is a member. An explicit specialization of a member
function, member class or static data member of a class template
shall be declared in the namespace of which the class template
is a member. */
if (scope && TREE_CODE (scope) != NAMESPACE_DECL)
cp_error ("explicit specialization in non-namespace scope `%D'",
scope);
/* [temp.expl.spec]
In an explicit specialization declaration for a member of a class
template or a member template that appears in namespace scope,
the member template and some of its enclosing class templates may
remain unspecialized, except that the declaration shall not
explicitly specialize a class member template if its enclosing
class templates are not explicitly specialized as well. */
if (current_template_parms)
cp_error ("enclosing class templates are not explicitly specialized");
}
/* We've just seen template <>. */
void
begin_specialization ()
{
note_template_header (1);
check_specialization_scope ();
}
/* Called at then end of processing a declaration preceeded by
template<>. */
void
end_specialization ()
{
reset_specialization ();
}
/* Any template <>'s that we have seen thus far are not referring to a
function specialization. */
void
reset_specialization ()
{
processing_specialization = 0;
template_header_count = 0;
}
/* We've just seen a template header. If SPECIALIZATION is non-zero,
it was of the form template <>. */
static void
note_template_header (specialization)
int specialization;
{
processing_specialization = specialization;
template_header_count++;
}
/* We're beginning an explicit instantiation. */
void
begin_explicit_instantiation ()
{
++processing_explicit_instantiation;
}
void
end_explicit_instantiation ()
{
my_friendly_assert(processing_explicit_instantiation > 0, 0);
--processing_explicit_instantiation;
}
/* The TYPE is being declared. If it is a template type, that means it
is a partial specialization. Do appropriate error-checking. */
void
maybe_process_partial_specialization (type)
tree type;
{
if (IS_AGGR_TYPE (type) && CLASSTYPE_USE_TEMPLATE (type))
{
if (CLASSTYPE_IMPLICIT_INSTANTIATION (type)
&& TYPE_SIZE (type) == NULL_TREE)
{
if (current_namespace
!= decl_namespace_context (CLASSTYPE_TI_TEMPLATE (type)))
{
cp_pedwarn ("specializing `%#T' in different namespace", type);
cp_pedwarn_at (" from definition of `%#D'",
CLASSTYPE_TI_TEMPLATE (type));
}
SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (type);
if (processing_template_decl)
push_template_decl (TYPE_MAIN_DECL (type));
}
else if (CLASSTYPE_TEMPLATE_INSTANTIATION (type))
cp_error ("specialization of `%T' after instantiation", type);
}
else if (processing_specialization)
cp_error ("explicit specialization of non-template `%T'", type);
}
/* Retrieve the specialization (in the sense of [temp.spec] - a
specialization is either an instantiation or an explicit
specialization) of TMPL for the given template ARGS. If there is
no such specialization, return NULL_TREE. The ARGS are a vector of
arguments, or a vector of vectors of arguments, in the case of
templates with more than one level of parameters. */
static tree
retrieve_specialization (tmpl, args)
tree tmpl;
tree args;
{
tree s;
my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 0);
/* There should be as many levels of arguments as there are
levels of parameters. */
my_friendly_assert (TMPL_ARGS_DEPTH (args)
== TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)),
0);
for (s = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
s != NULL_TREE;
s = TREE_CHAIN (s))
if (comp_template_args (TREE_PURPOSE (s), args))
return TREE_VALUE (s);
return NULL_TREE;
}
/* Returns non-zero iff DECL is a specialization of TMPL. */
int
is_specialization_of (decl, tmpl)
tree decl;
tree tmpl;
{
tree t;
if (TREE_CODE (decl) == FUNCTION_DECL)
{
for (t = decl;
t != NULL_TREE;
t = DECL_TEMPLATE_INFO (t) ? DECL_TI_TEMPLATE (t) : NULL_TREE)
if (t == tmpl)
return 1;
}
else
{
my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 0);
for (t = TREE_TYPE (decl);
t != NULL_TREE;
t = CLASSTYPE_USE_TEMPLATE (t)
? TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) : NULL_TREE)
if (same_type_p (TYPE_MAIN_VARIANT (t),
TYPE_MAIN_VARIANT (TREE_TYPE (tmpl))))
return 1;
}
return 0;
}
/* Register the specialization SPEC as a specialization of TMPL with
the indicated ARGS. Returns SPEC, or an equivalent prior
declaration, if available. */
static tree
register_specialization (spec, tmpl, args)
tree spec;
tree tmpl;
tree args;
{
tree s;
my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 0);
if (TREE_CODE (spec) == FUNCTION_DECL
&& uses_template_parms (DECL_TI_ARGS (spec)))
/* This is the FUNCTION_DECL for a partial instantiation. Don't
register it; we want the corresponding TEMPLATE_DECL instead.
We use `uses_template_parms (DECL_TI_ARGS (spec))' rather than
the more obvious `uses_template_parms (spec)' to avoid problems
with default function arguments. In particular, given
something like this:
template <class T> void f(T t1, T t = T())
the default argument expression is not substituted for in an
instantiation unless and until it is actually needed. */
return spec;
/* There should be as many levels of arguments as there are
levels of parameters. */
my_friendly_assert (TMPL_ARGS_DEPTH (args)
== TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)),
0);
for (s = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
s != NULL_TREE;
s = TREE_CHAIN (s))
if (comp_template_args (TREE_PURPOSE (s), args))
{
tree fn = TREE_VALUE (s);
if (DECL_TEMPLATE_SPECIALIZATION (spec))
{
if (DECL_TEMPLATE_INSTANTIATION (fn))
{
if (TREE_USED (fn)
|| DECL_EXPLICIT_INSTANTIATION (fn))
{
cp_error ("specialization of %D after instantiation",
fn);
return spec;
}
else
{
/* This situation should occur only if the first
specialization is an implicit instantiation,
the second is an explicit specialization, and
the implicit instantiation has not yet been
used. That situation can occur if we have
implicitly instantiated a member function and
then specialized it later.
We can also wind up here if a friend
declaration that looked like an instantiation
turns out to be a specialization:
template <class T> void foo(T);
class S { friend void foo<>(int) };
template <> void foo(int);
We transform the existing DECL in place so that
any pointers to it become pointers to the
updated declaration.
If there was a definition for the template, but
not for the specialization, we want this to
look as if there is no definition, and vice
versa. */
DECL_INITIAL (fn) = NULL_TREE;
duplicate_decls (spec, fn);
return fn;
}
}
else if (DECL_TEMPLATE_SPECIALIZATION (fn))
{
duplicate_decls (spec, fn);
return fn;
}
}
}
DECL_TEMPLATE_SPECIALIZATIONS (tmpl)
= perm_tree_cons (args, spec, DECL_TEMPLATE_SPECIALIZATIONS (tmpl));
return spec;
}
/* Unregister the specialization SPEC as a specialization of TMPL.
Returns nonzero if the SPEC was listed as a specialization of
TMPL. */
static int
unregister_specialization (spec, tmpl)
tree spec;
tree tmpl;
{
tree* s;
for (s = &DECL_TEMPLATE_SPECIALIZATIONS (tmpl);
*s != NULL_TREE;
s = &TREE_CHAIN (*s))
if (TREE_VALUE (*s) == spec)
{
*s = TREE_CHAIN (*s);
return 1;
}
return 0;
}
/* Print the list of candidate FNS in an error message. */
void
print_candidates (fns)
tree fns;
{
tree fn;
const char *str = "candidates are:";
for (fn = fns; fn != NULL_TREE; fn = TREE_CHAIN (fn))
{
tree f;
for (f = TREE_VALUE (fn); f; f = OVL_NEXT (f))
cp_error_at ("%s %+#D", str, OVL_CURRENT (f));
str = " ";
}
}
/* Returns the template (one of the functions given by TEMPLATE_ID)
which can be specialized to match the indicated DECL with the
explicit template args given in TEMPLATE_ID. The DECL may be
NULL_TREE if none is available. In that case, the functions in
TEMPLATE_ID are non-members.
If NEED_MEMBER_TEMPLATE is non-zero the function is known to be a
specialization of a member template.
The template args (those explicitly specified and those deduced)
are output in a newly created vector *TARGS_OUT.
If it is impossible to determine the result, an error message is
issued. The error_mark_node is returned to indicate failure. */
static tree
determine_specialization (template_id, decl, targs_out,
need_member_template)
tree template_id;
tree decl;
tree* targs_out;
int need_member_template;
{
tree fn;
tree fns;
tree targs;
tree explicit_targs;
tree candidates = NULL_TREE;
tree templates = NULL_TREE;
*targs_out = NULL_TREE;
if (template_id == error_mark_node)
return error_mark_node;
fns = TREE_OPERAND (template_id, 0);
explicit_targs = TREE_OPERAND (template_id, 1);
if (fns == error_mark_node)
return error_mark_node;
/* Check for baselinks. */
if (TREE_CODE (fns) == TREE_LIST)
fns = TREE_VALUE (fns);
for (; fns; fns = OVL_NEXT (fns))
{
tree tmpl;
fn = OVL_CURRENT (fns);
if (TREE_CODE (fn) == TEMPLATE_DECL)
/* DECL might be a specialization of FN. */
tmpl = fn;
else if (need_member_template)
/* FN is an ordinary member function, and we need a
specialization of a member template. */
continue;
else if (TREE_CODE (fn) != FUNCTION_DECL)
/* We can get IDENTIFIER_NODEs here in certain erroneous
cases. */
continue;
else if (!DECL_FUNCTION_MEMBER_P (fn))
/* This is just an ordinary non-member function. Nothing can
be a specialization of that. */
continue;
else
{
tree decl_arg_types;
/* This is an ordinary member function. However, since
we're here, we can assume it's enclosing class is a
template class. For example,
template <typename T> struct S { void f(); };
template <> void S<int>::f() {}
Here, S<int>::f is a non-template, but S<int> is a
template class. If FN has the same type as DECL, we
might be in business. */
if (!same_type_p (TREE_TYPE (TREE_TYPE (decl)),
TREE_TYPE (TREE_TYPE (fn))))
/* The return types differ. */
continue;
/* Adjust the type of DECL in case FN is a static member. */
decl_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
if (DECL_STATIC_FUNCTION_P (fn)
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
decl_arg_types = TREE_CHAIN (decl_arg_types);
if (compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
decl_arg_types))
/* They match! */
candidates = tree_cons (NULL_TREE, fn, candidates);
continue;
}
/* See whether this function might be a specialization of this
template. */
targs = get_bindings (tmpl, decl, explicit_targs);
if (!targs)
/* We cannot deduce template arguments that when used to
specialize TMPL will produce DECL. */
continue;
/* Save this template, and the arguments deduced. */
templates = scratch_tree_cons (targs, tmpl, templates);
}
if (templates && TREE_CHAIN (templates))
{
/* We have:
[temp.expl.spec]
It is possible for a specialization with a given function
signature to be instantiated from more than one function
template. In such cases, explicit specification of the
template arguments must be used to uniquely identify the
function template specialization being specialized.
Note that here, there's no suggestion that we're supposed to
determine which of the candidate templates is most
specialized. However, we, also have:
[temp.func.order]
Partial ordering of overloaded function template
declarations is used in the following contexts to select
the function template to which a function template
specialization refers:
-- when an explicit specialization refers to a function
template.
So, we do use the partial ordering rules, at least for now.
This extension can only serve to make illegal programs legal,
so it's safe. And, there is strong anecdotal evidence that
the committee intended the partial ordering rules to apply;
the EDG front-end has that behavior, and John Spicer claims
that the committee simply forgot to delete the wording in
[temp.expl.spec]. */
tree tmpl = most_specialized (templates, decl, explicit_targs);
if (tmpl && tmpl != error_mark_node)
{
targs = get_bindings (tmpl, decl, explicit_targs);
templates = scratch_tree_cons (targs, tmpl, NULL_TREE);
}
}
if (templates == NULL_TREE && candidates == NULL_TREE)
{
cp_error_at ("template-id `%D' for `%+D' does not match any template declaration",
template_id, decl);
return error_mark_node;
}
else if ((templates && TREE_CHAIN (templates))
|| (candidates && TREE_CHAIN (candidates))
|| (templates && candidates))
{
cp_error_at ("ambiguous template specialization `%D' for `%+D'",
template_id, decl);
chainon (candidates, templates);
print_candidates (candidates);
return error_mark_node;
}
/* We have one, and exactly one, match. */
if (candidates)
{
/* It was a specialization of an ordinary member function in a
template class. */
*targs_out = copy_node (DECL_TI_ARGS (TREE_VALUE (candidates)));
return DECL_TI_TEMPLATE (TREE_VALUE (candidates));
}
/* It was a specialization of a template. */
targs = DECL_TI_ARGS (DECL_RESULT (TREE_VALUE (templates)));
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (targs))
{
*targs_out = copy_node (targs);
SET_TMPL_ARGS_LEVEL (*targs_out,
TMPL_ARGS_DEPTH (*targs_out),
TREE_PURPOSE (templates));
}
else
*targs_out = TREE_PURPOSE (templates);
return TREE_VALUE (templates);
}
/* Check to see if the function just declared, as indicated in
DECLARATOR, and in DECL, is a specialization of a function
template. We may also discover that the declaration is an explicit
instantiation at this point.
Returns DECL, or an equivalent declaration that should be used
instead if all goes well. Issues an error message if something is
amiss. Returns error_mark_node if the error is not easily
recoverable.
FLAGS is a bitmask consisting of the following flags:
2: The function has a definition.
4: The function is a friend.
The TEMPLATE_COUNT is the number of references to qualifying
template classes that appeared in the name of the function. For
example, in
template <class T> struct S { void f(); };
void S<int>::f();
the TEMPLATE_COUNT would be 1. However, explicitly specialized
classes are not counted in the TEMPLATE_COUNT, so that in
template <class T> struct S {};
template <> struct S<int> { void f(); }
template <> void S<int>::f();
the TEMPLATE_COUNT would be 0. (Note that this declaration is
illegal; there should be no template <>.)
If the function is a specialization, it is marked as such via
DECL_TEMPLATE_SPECIALIZATION. Furthermore, its DECL_TEMPLATE_INFO
is set up correctly, and it is added to the list of specializations
for that template. */
tree
check_explicit_specialization (declarator, decl, template_count, flags)
tree declarator;
tree decl;
int template_count;
int flags;
{
int have_def = flags & 2;
int is_friend = flags & 4;
int specialization = 0;
int explicit_instantiation = 0;
int member_specialization = 0;
tree ctype = DECL_CLASS_CONTEXT (decl);
tree dname = DECL_NAME (decl);
if (processing_specialization)
{
/* The last template header was of the form template <>. */
if (template_header_count > template_count)
{
/* There were more template headers than qualifying template
classes. */
if (template_header_count - template_count > 1)
/* There shouldn't be that many template parameter lists.
There can be at most one parameter list for every
qualifying class, plus one for the function itself. */
cp_error ("too many template parameter lists in declaration of `%D'", decl);
SET_DECL_TEMPLATE_SPECIALIZATION (decl);
if (ctype)
member_specialization = 1;
else
specialization = 1;
}
else if (template_header_count == template_count)
{
/* The counts are equal. So, this might be a
specialization, but it is not a specialization of a
member template. It might be something like
template <class T> struct S {
void f(int i);
};
template <>
void S<int>::f(int i) {} */
specialization = 1;
SET_DECL_TEMPLATE_SPECIALIZATION (decl);
}
else
{
/* This cannot be an explicit specialization. There are not
enough headers for all of the qualifying classes. For
example, we might have:
template <>
void S<int>::T<char>::f();
But, we're missing another template <>. */
cp_error("too few template parameter lists in declaration of `%D'", decl);
return decl;
}
}
else if (processing_explicit_instantiation)
{
if (template_header_count)
cp_error ("template parameter list used in explicit instantiation");
if (have_def)
cp_error ("definition provided for explicit instantiation");
explicit_instantiation = 1;
}
else if (ctype != NULL_TREE
&& !TYPE_BEING_DEFINED (ctype)
&& CLASSTYPE_TEMPLATE_INSTANTIATION (ctype)
&& !is_friend)
{
/* This case catches outdated code that looks like this:
template <class T> struct S { void f(); };
void S<int>::f() {} // Missing template <>
We disable this check when the type is being defined to
avoid complaining about default compiler-generated
constructors, destructors, and assignment operators.
Since the type is an instantiation, not a specialization,
these are the only functions that can be defined before
the class is complete. */
/* If they said
template <class T> void S<int>::f() {}
that's bogus. */
if (template_header_count)
{
cp_error ("template parameters specified in specialization");
return decl;
}
if (pedantic)
cp_pedwarn
("explicit specialization not preceded by `template <>'");
specialization = 1;
SET_DECL_TEMPLATE_SPECIALIZATION (decl);
}
else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
{
if (is_friend)
/* This could be something like:
template <class T> void f(T);
class S { friend void f<>(int); } */
specialization = 1;
else
{
/* This case handles bogus declarations like template <>
template <class T> void f<int>(); */
cp_error ("template-id `%D' in declaration of primary template",
declarator);
return decl;
}
}
if (specialization || member_specialization)
{
tree t = TYPE_ARG_TYPES (TREE_TYPE (decl));
for (; t; t = TREE_CHAIN (t))
if (TREE_PURPOSE (t))
{
cp_pedwarn
("default argument specified in explicit specialization");
break;
}
if (current_lang_name == lang_name_c)
cp_error ("template specialization with C linkage");
}
if (specialization || member_specialization || explicit_instantiation)
{
tree tmpl = NULL_TREE;
tree targs = NULL_TREE;
/* Make sure that the declarator is a TEMPLATE_ID_EXPR. */
if (TREE_CODE (declarator) != TEMPLATE_ID_EXPR)
{
tree fns;
my_friendly_assert (TREE_CODE (declarator) == IDENTIFIER_NODE,
0);
if (!ctype)
fns = IDENTIFIER_NAMESPACE_VALUE (dname);
else
fns = dname;
declarator =
lookup_template_function (fns, NULL_TREE);
}
if (declarator == error_mark_node)
return error_mark_node;
if (ctype != NULL_TREE && TYPE_BEING_DEFINED (ctype))
{
if (!explicit_instantiation)
/* A specialization in class scope. This is illegal,
but the error will already have been flagged by
check_specialization_scope. */
return error_mark_node;
else
{
/* It's not legal to write an explicit instantiation in
class scope, e.g.:
class C { template void f(); }
This case is caught by the parser. However, on
something like:
template class C { void f(); };
(which is illegal) we can get here. The error will be
issued later. */
;
}
return decl;
}
else if (TREE_CODE (TREE_OPERAND (declarator, 0)) == LOOKUP_EXPR)
{
/* A friend declaration. We can't do much, because we don't
know what this resolves to, yet. */
my_friendly_assert (is_friend != 0, 0);
my_friendly_assert (!explicit_instantiation, 0);
SET_DECL_IMPLICIT_INSTANTIATION (decl);
return decl;
}
else if (ctype != NULL_TREE
&& (TREE_CODE (TREE_OPERAND (declarator, 0)) ==
IDENTIFIER_NODE))
{
/* Find the list of functions in ctype that have the same
name as the declared function. */
tree name = TREE_OPERAND (declarator, 0);
tree fns = NULL_TREE;
int idx;
if (name == constructor_name (ctype)
|| name == constructor_name_full (ctype))
{
int is_constructor = DECL_CONSTRUCTOR_P (decl);
if (is_constructor ? !TYPE_HAS_CONSTRUCTOR (ctype)
: !TYPE_HAS_DESTRUCTOR (ctype))
{
/* From [temp.expl.spec]:
If such an explicit specialization for the member
of a class template names an implicitly-declared
special member function (clause _special_), the
program is ill-formed.
Similar language is found in [temp.explicit]. */
cp_error ("specialization of implicitly-declared special member function");
return error_mark_node;
}
name = is_constructor ? ctor_identifier : dtor_identifier;
}
if (!IDENTIFIER_TYPENAME_P (name))
{
idx = lookup_fnfields_1 (ctype, name);
if (idx >= 0)
fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (ctype), idx);
}
else
{
tree methods;
/* For a type-conversion operator, we cannot do a
name-based lookup. We might be looking for `operator
int' which will be a specialization of `operator T'.
So, we find *all* the conversion operators, and then
select from them. */
fns = NULL_TREE;
methods = CLASSTYPE_METHOD_VEC (ctype);
if (methods)
for (idx = 2; idx < TREE_VEC_LENGTH (methods); ++idx)
{
tree ovl = TREE_VEC_ELT (methods, idx);
if (!ovl || !DECL_CONV_FN_P (OVL_CURRENT (ovl)))
/* There are no more conversion functions. */
break;
/* Glue all these conversion functions together
with those we already have. */
for (; ovl; ovl = OVL_NEXT (ovl))
fns = ovl_cons (OVL_CURRENT (ovl), fns);
}
}
if (fns == NULL_TREE)
{
cp_error ("no member function `%D' declared in `%T'",
name, ctype);
return error_mark_node;
}
else
TREE_OPERAND (declarator, 0) = fns;
}
/* Figure out what exactly is being specialized at this point.
Note that for an explicit instantiation, even one for a
member function, we cannot tell apriori whether the
instantiation is for a member template, or just a member
function of a template class. Even if a member template is
being instantiated, the member template arguments may be
elided if they can be deduced from the rest of the
declaration. */
tmpl = determine_specialization (declarator, decl,
&targs,
member_specialization);
if (!tmpl || tmpl == error_mark_node)
/* We couldn't figure out what this declaration was
specializing. */
return error_mark_node;
else
{
tree gen_tmpl = most_general_template (tmpl);
if (explicit_instantiation)
{
/* We don't set DECL_EXPLICIT_INSTANTIATION here; that
is done by do_decl_instantiation later. */
int arg_depth = TMPL_ARGS_DEPTH (targs);
int parm_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl));
if (arg_depth > parm_depth)
{
/* If TMPL is not the most general template (for
example, if TMPL is a friend template that is
injected into namespace scope), then there will
be too many levels fo TARGS. Remove some of them
here. */
int i;
tree new_targs;
new_targs = make_temp_vec (parm_depth);
for (i = arg_depth - parm_depth; i < arg_depth; ++i)
TREE_VEC_ELT (new_targs, i - (arg_depth - parm_depth))
= TREE_VEC_ELT (targs, i);
targs = new_targs;
}
decl = instantiate_template (tmpl, targs);
return decl;
}
/* If we though that the DECL was a member function, but it
turns out to be specializing a static member function,
make DECL a static member function as well. */
if (DECL_STATIC_FUNCTION_P (tmpl)
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
{
revert_static_member_fn (&decl, 0, 0);
last_function_parms = TREE_CHAIN (last_function_parms);
}
/* Set up the DECL_TEMPLATE_INFO for DECL. */
DECL_TEMPLATE_INFO (decl)
= perm_tree_cons (tmpl, targs, NULL_TREE);
/* Mangle the function name appropriately. Note that we do
not mangle specializations of non-template member
functions of template classes, e.g. with
template <class T> struct S { void f(); }
and given the specialization
template <> void S<int>::f() {}
we do not mangle S<int>::f() here. That's because it's
just an ordinary member function and doesn't need special
treatment. We do this here so that the ordinary,
non-template, name-mangling algorith will not be used
later. */
if ((is_member_template (tmpl) || ctype == NULL_TREE)
&& name_mangling_version >= 1)
set_mangled_name_for_template_decl (decl);
if (is_friend && !have_def)
/* This is not really a declaration of a specialization.
It's just the name of an instantiation. But, it's not
a request for an instantiation, either. */
SET_DECL_IMPLICIT_INSTANTIATION (decl);
/* Register this specialization so that we can find it
again. */
decl = register_specialization (decl, gen_tmpl, targs);
}
}
return decl;
}
/* TYPE is being declared. Verify that the use of template headers
and such is reasonable. Issue error messages if not. */
void
maybe_check_template_type (type)
tree type;
{
if (template_header_count)
{
/* We are in the scope of some `template <...>' header. */
int context_depth
= template_class_depth_real (TYPE_CONTEXT (type),
/*count_specializations=*/1);
if (template_header_count <= context_depth)
/* This is OK; the template headers are for the context. We
are actually too lenient here; like
check_explicit_specialization we should consider the number
of template types included in the actual declaration. For
example,
template <class T> struct S {
template <class U> template <class V>
struct I {};
};
is illegal, but:
template <class T> struct S {
template <class U> struct I;
};
template <class T> template <class U.
struct S<T>::I {};
is not. */
;
else if (template_header_count > context_depth + 1)
/* There are two many template parameter lists. */
cp_error ("too many template parameter lists in declaration of `%T'", type);
}
}
/* Returns 1 iff PARMS1 and PARMS2 are identical sets of template
parameters. These are represented in the same format used for
DECL_TEMPLATE_PARMS. */
int comp_template_parms (parms1, parms2)
tree parms1;
tree parms2;
{
tree p1;
tree p2;
if (parms1 == parms2)
return 1;
for (p1 = parms1, p2 = parms2;
p1 != NULL_TREE && p2 != NULL_TREE;
p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2))
{
tree t1 = TREE_VALUE (p1);
tree t2 = TREE_VALUE (p2);
int i;
my_friendly_assert (TREE_CODE (t1) == TREE_VEC, 0);
my_friendly_assert (TREE_CODE (t2) == TREE_VEC, 0);
if (TREE_VEC_LENGTH (t1) != TREE_VEC_LENGTH (t2))
return 0;
for (i = 0; i < TREE_VEC_LENGTH (t2); ++i)
{
tree parm1 = TREE_VALUE (TREE_VEC_ELT (t1, i));
tree parm2 = TREE_VALUE (TREE_VEC_ELT (t2, i));
if (TREE_CODE (parm1) != TREE_CODE (parm2))
return 0;
if (TREE_CODE (parm1) == TEMPLATE_TYPE_PARM)
continue;
else if (!same_type_p (TREE_TYPE (parm1), TREE_TYPE (parm2)))
return 0;
}
}
if ((p1 != NULL_TREE) != (p2 != NULL_TREE))
/* One set of parameters has more parameters lists than the
other. */
return 0;
return 1;
}
/* Complain if DECL shadows a template parameter.
[temp.local]: A template-parameter shall not be redeclared within its
scope (including nested scopes). */
void
check_template_shadow (decl)
tree decl;
{
tree olddecl;
/* If we're not in a template, we can't possibly shadow a template
parameter. */
if (!current_template_parms)
return;
/* Figure out what we're shadowing. */
if (TREE_CODE (decl) == OVERLOAD)
decl = OVL_CURRENT (decl);
olddecl = IDENTIFIER_VALUE (DECL_NAME (decl));
/* If there's no previous binding for this name, we're not shadowing
anything, let alone a template parameter. */
if (!olddecl)
return;
/* If we're not shadowing a template parameter, we're done. Note
that OLDDECL might be an OVERLOAD (or perhaps even an
ERROR_MARK), so we can't just blithely assume it to be a _DECL
node. */
if (TREE_CODE_CLASS (TREE_CODE (olddecl)) != 'd'
|| !DECL_TEMPLATE_PARM_P (olddecl))
return;
/* We check for decl != olddecl to avoid bogus errors for using a
name inside a class. We check TPFI to avoid duplicate errors for
inline member templates. */
if (decl == olddecl
|| TEMPLATE_PARMS_FOR_INLINE (current_template_parms))
return;
cp_error_at ("declaration of `%#D'", decl);
cp_error_at (" shadows template parm `%#D'", olddecl);
}
/* Return a new TEMPLATE_PARM_INDEX with the indicated INDEX, LEVEL,
ORIG_LEVEL, DECL, and TYPE. */
static tree
build_template_parm_index (index, level, orig_level, decl, type)
int index;
int level;
int orig_level;
tree decl;
tree type;
{
tree t = make_node (TEMPLATE_PARM_INDEX);
TEMPLATE_PARM_IDX (t) = index;
TEMPLATE_PARM_LEVEL (t) = level;
TEMPLATE_PARM_ORIG_LEVEL (t) = orig_level;
TEMPLATE_PARM_DECL (t) = decl;
TREE_TYPE (t) = type;
return t;
}
/* Return a TEMPLATE_PARM_INDEX, similar to INDEX, but whose
TEMPLATE_PARM_LEVEL has been decreased by LEVELS. If such a
TEMPLATE_PARM_INDEX already exists, it is returned; otherwise, a
new one is created. */
static tree
reduce_template_parm_level (index, type, levels)
tree index;
tree type;
int levels;
{
if (TEMPLATE_PARM_DESCENDANTS (index) == NULL_TREE
|| (TEMPLATE_PARM_LEVEL (TEMPLATE_PARM_DESCENDANTS (index))
!= TEMPLATE_PARM_LEVEL (index) - levels))
{
tree decl
= build_decl (TREE_CODE (TEMPLATE_PARM_DECL (index)),
DECL_NAME (TEMPLATE_PARM_DECL (index)),
type);
tree t
= build_template_parm_index (TEMPLATE_PARM_IDX (index),
TEMPLATE_PARM_LEVEL (index) - levels,
TEMPLATE_PARM_ORIG_LEVEL (index),
decl, type);
TEMPLATE_PARM_DESCENDANTS (index) = t;
/* Template template parameters need this. */
DECL_TEMPLATE_PARMS (decl)
= DECL_TEMPLATE_PARMS (TEMPLATE_PARM_DECL (index));
}
return TEMPLATE_PARM_DESCENDANTS (index);
}
/* Process information from new template parameter NEXT and append it to the
LIST being built. */
tree
process_template_parm (list, next)
tree list, next;
{
tree parm;
tree decl = 0;
tree defval;
int is_type, idx;
parm = next;
my_friendly_assert (TREE_CODE (parm) == TREE_LIST, 259);
defval = TREE_PURPOSE (parm);
parm = TREE_VALUE (parm);
is_type = TREE_PURPOSE (parm) == class_type_node;
if (list)
{
tree p = TREE_VALUE (tree_last (list));
if (TREE_CODE (p) == TYPE_DECL)
idx = TEMPLATE_TYPE_IDX (TREE_TYPE (p));
else if (TREE_CODE (p) == TEMPLATE_DECL)
idx = TEMPLATE_TYPE_IDX (TREE_TYPE (DECL_TEMPLATE_RESULT (p)));
else
idx = TEMPLATE_PARM_IDX (DECL_INITIAL (p));
++idx;
}
else
idx = 0;
if (!is_type)
{
my_friendly_assert (TREE_CODE (TREE_PURPOSE (parm)) == TREE_LIST, 260);
/* is a const-param */
parm = grokdeclarator (TREE_VALUE (parm), TREE_PURPOSE (parm),
PARM, 0, NULL_TREE);
/* [temp.param]
The top-level cv-qualifiers on the template-parameter are
ignored when determining its type. */
TREE_TYPE (parm) = TYPE_MAIN_VARIANT (TREE_TYPE (parm));
/* A template parameter is not modifiable. */
TREE_READONLY (parm) = 1;
if (IS_AGGR_TYPE (TREE_TYPE (parm))
&& TREE_CODE (TREE_TYPE (parm)) != TEMPLATE_TYPE_PARM
&& TREE_CODE (TREE_TYPE (parm)) != TYPENAME_TYPE)
{
cp_error ("`%#T' is not a valid type for a template constant parameter",
TREE_TYPE (parm));
if (DECL_NAME (parm) == NULL_TREE)
error (" a template type parameter must begin with `class' or `typename'");
TREE_TYPE (parm) = void_type_node;
}
else if (pedantic
&& (TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE
|| TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE))
cp_pedwarn ("`%T' is not a valid type for a template constant parameter",
TREE_TYPE (parm));
if (TREE_PERMANENT (parm) == 0)
{
parm = copy_node (parm);
TREE_PERMANENT (parm) = 1;
}
decl = build_decl (CONST_DECL, DECL_NAME (parm), TREE_TYPE (parm));
DECL_INITIAL (parm) = DECL_INITIAL (decl)
= build_template_parm_index (idx, processing_template_decl,
processing_template_decl,
decl, TREE_TYPE (parm));
}
else
{
tree t;
parm = TREE_VALUE (parm);
if (parm && TREE_CODE (parm) == TEMPLATE_DECL)
{
t = make_lang_type (TEMPLATE_TEMPLATE_PARM);
/* This is for distinguishing between real templates and template
template parameters */
TREE_TYPE (parm) = t;
TREE_TYPE (DECL_TEMPLATE_RESULT (parm)) = t;
decl = parm;
}
else
{
t = make_lang_type (TEMPLATE_TYPE_PARM);
/* parm is either IDENTIFIER_NODE or NULL_TREE */
decl = build_decl (TYPE_DECL, parm, t);
}
TYPE_NAME (t) = decl;
TYPE_STUB_DECL (t) = decl;
parm = decl;
TEMPLATE_TYPE_PARM_INDEX (t)
= build_template_parm_index (idx, processing_template_decl,
processing_template_decl,
decl, TREE_TYPE (parm));
}
SET_DECL_ARTIFICIAL (decl);
DECL_TEMPLATE_PARM_P (decl) = 1;
pushdecl (decl);
parm = build_tree_list (defval, parm);
return chainon (list, parm);
}
/* The end of a template parameter list has been reached. Process the
tree list into a parameter vector, converting each parameter into a more
useful form. Type parameters are saved as IDENTIFIER_NODEs, and others
as PARM_DECLs. */
tree
end_template_parm_list (parms)
tree parms;
{
int nparms;
tree parm;
tree saved_parmlist = make_tree_vec (list_length (parms));
current_template_parms
= tree_cons (build_int_2 (0, processing_template_decl),
saved_parmlist, current_template_parms);
for (parm = parms, nparms = 0; parm; parm = TREE_CHAIN (parm), nparms++)
TREE_VEC_ELT (saved_parmlist, nparms) = parm;
--processing_template_parmlist;
return saved_parmlist;
}
/* end_template_decl is called after a template declaration is seen. */
void
end_template_decl ()
{
reset_specialization ();
if (! processing_template_decl)
return;
/* This matches the pushlevel in begin_template_parm_list. */
poplevel (0, 0, 0);
--processing_template_decl;
current_template_parms = TREE_CHAIN (current_template_parms);
(void) get_pending_sizes (); /* Why? */
}
/* Given a template argument vector containing the template PARMS.
The innermost PARMS are given first. */
tree
current_template_args ()
{
tree header;
tree args = NULL_TREE;
int length = TMPL_PARMS_DEPTH (current_template_parms);
int l = length;
/* If there is only one level of template parameters, we do not
create a TREE_VEC of TREE_VECs. Instead, we return a single
TREE_VEC containing the arguments. */
if (length > 1)
args = make_tree_vec (length);
for (header = current_template_parms; header; header = TREE_CHAIN (header))
{
tree a = copy_node (TREE_VALUE (header));
int i;
TREE_TYPE (a) = NULL_TREE;
for (i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
{
tree t = TREE_VEC_ELT (a, i);
/* T will be a list if we are called from within a
begin/end_template_parm_list pair, but a vector directly
if within a begin/end_member_template_processing pair. */
if (TREE_CODE (t) == TREE_LIST)
{
t = TREE_VALUE (t);
if (TREE_CODE (t) == TYPE_DECL
|| TREE_CODE (t) == TEMPLATE_DECL)
t = TREE_TYPE (t);
else
t = DECL_INITIAL (t);
TREE_VEC_ELT (a, i) = t;
}
}
if (length > 1)
TREE_VEC_ELT (args, --l) = a;
else
args = a;
}
return args;
}
/* Return a TEMPLATE_DECL corresponding to DECL, using the indicated
template PARMS. Used by push_template_decl below. */
static tree
build_template_decl (decl, parms)
tree decl;
tree parms;
{
tree tmpl = build_lang_decl (TEMPLATE_DECL, DECL_NAME (decl), NULL_TREE);
DECL_TEMPLATE_PARMS (tmpl) = parms;
DECL_CONTEXT (tmpl) = DECL_CONTEXT (decl);
if (DECL_LANG_SPECIFIC (decl))
{
DECL_CLASS_CONTEXT (tmpl) = DECL_CLASS_CONTEXT (decl);
DECL_STATIC_FUNCTION_P (tmpl) = DECL_STATIC_FUNCTION_P (decl);
DECL_CONSTRUCTOR_P (tmpl) = DECL_CONSTRUCTOR_P (decl);
}
return tmpl;
}
struct template_parm_data
{
/* The level of the template parameters we are currently
processing. */
int level;
/* The index of the specialization argument we are currently
processing. */
int current_arg;
/* An array whose size is the number of template parameters. The
elements are non-zero if the parameter has been used in any one
of the arguments processed so far. */
int* parms;
/* An array whose size is the number of template arguments. The
elements are non-zero if the argument makes use of template
parameters of this level. */
int* arg_uses_template_parms;
};
/* Subroutine of push_template_decl used to see if each template
parameter in a partial specialization is used in the explicit
argument list. If T is of the LEVEL given in DATA (which is
treated as a template_parm_data*), then DATA->PARMS is marked
appropriately. */
static int
mark_template_parm (t, data)
tree t;
void* data;
{
int level;
int idx;
struct template_parm_data* tpd = (struct template_parm_data*) data;
if (TREE_CODE (t) == TEMPLATE_PARM_INDEX)
{
level = TEMPLATE_PARM_LEVEL (t);
idx = TEMPLATE_PARM_IDX (t);
}
else
{
level = TEMPLATE_TYPE_LEVEL (t);
idx = TEMPLATE_TYPE_IDX (t);
}
if (level == tpd->level)
{
tpd->parms[idx] = 1;
tpd->arg_uses_template_parms[tpd->current_arg] = 1;
}
/* Return zero so that for_each_template_parm will continue the
traversal of the tree; we want to mark *every* template parm. */
return 0;
}
/* Process the partial specialization DECL. */
static tree
process_partial_specialization (decl)
tree decl;
{
tree type = TREE_TYPE (decl);
tree maintmpl = CLASSTYPE_TI_TEMPLATE (type);
tree specargs = CLASSTYPE_TI_ARGS (type);
tree inner_args = innermost_args (specargs);
tree inner_parms = INNERMOST_TEMPLATE_PARMS (current_template_parms);
tree main_inner_parms = DECL_INNERMOST_TEMPLATE_PARMS (maintmpl);
int nargs = TREE_VEC_LENGTH (inner_args);
int ntparms = TREE_VEC_LENGTH (inner_parms);
int i;
int did_error_intro = 0;
struct template_parm_data tpd;
struct template_parm_data tpd2;
/* We check that each of the template parameters given in the
partial specialization is used in the argument list to the
specialization. For example:
template <class T> struct S;
template <class T> struct S<T*>;
The second declaration is OK because `T*' uses the template
parameter T, whereas
template <class T> struct S<int>;
is no good. Even trickier is:
template <class T>
struct S1
{
template <class U>
struct S2;
template <class U>
struct S2<T>;
};
The S2<T> declaration is actually illegal; it is a
full-specialization. Of course,
template <class U>
struct S2<T (*)(U)>;
or some such would have been OK. */
tpd.level = TMPL_PARMS_DEPTH (current_template_parms);
tpd.parms = alloca (sizeof (int) * ntparms);
bzero ((PTR) tpd.parms, sizeof (int) * ntparms);
tpd.arg_uses_template_parms = alloca (sizeof (int) * nargs);
bzero ((PTR) tpd.arg_uses_template_parms, sizeof (int) * nargs);
for (i = 0; i < nargs; ++i)
{
tpd.current_arg = i;
for_each_template_parm (TREE_VEC_ELT (inner_args, i),
&mark_template_parm,
&tpd);
}
for (i = 0; i < ntparms; ++i)
if (tpd.parms[i] == 0)
{
/* One of the template parms was not used in the
specialization. */
if (!did_error_intro)
{
cp_error ("template parameters not used in partial specialization:");
did_error_intro = 1;
}
cp_error (" `%D'",
TREE_VALUE (TREE_VEC_ELT (inner_parms, i)));
}
/* [temp.class.spec]
The argument list of the specialization shall not be identical to
the implicit argument list of the primary template. */
if (comp_template_args (inner_args,
innermost_args (CLASSTYPE_TI_ARGS (TREE_TYPE
(maintmpl)))))
cp_error ("partial specialization `%T' does not specialize any template arguments", type);
/* [temp.class.spec]
A partially specialized non-type argument expression shall not
involve template parameters of the partial specialization except
when the argument expression is a simple identifier.
The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. */
my_friendly_assert (nargs == DECL_NTPARMS (maintmpl), 0);
tpd2.parms = 0;
for (i = 0; i < nargs; ++i)
{
tree arg = TREE_VEC_ELT (inner_args, i);
if (/* These first two lines are the `non-type' bit. */
TREE_CODE_CLASS (TREE_CODE (arg)) != 't'
&& TREE_CODE (arg) != TEMPLATE_DECL
/* This next line is the `argument expression is not just a
simple identifier' condition and also the `specialized
non-type argument' bit. */
&& TREE_CODE (arg) != TEMPLATE_PARM_INDEX)
{
if (tpd.arg_uses_template_parms[i])
cp_error ("template argument `%E' involves template parameter(s)", arg);
else
{
/* Look at the corresponding template parameter,
marking which template parameters its type depends
upon. */
tree type =
TREE_TYPE (TREE_VALUE (TREE_VEC_ELT (main_inner_parms,
i)));
if (!tpd2.parms)
{
/* We haven't yet initialized TPD2. Do so now. */
tpd2.arg_uses_template_parms
= (int*) alloca (sizeof (int) * nargs);
/* The number of parameters here is the number in the
main template, which, as checked in the assertion
above, is NARGS. */
tpd2.parms = (int*) alloca (sizeof (int) * nargs);
tpd2.level =
TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (maintmpl));
}
/* Mark the template parameters. But this time, we're
looking for the template parameters of the main
template, not in the specialization. */
tpd2.current_arg = i;
tpd2.arg_uses_template_parms[i] = 0;
bzero ((PTR) tpd2.parms, sizeof (int) * nargs);
for_each_template_parm (type,
&mark_template_parm,
&tpd2);
if (tpd2.arg_uses_template_parms [i])
{
/* The type depended on some template parameters.
If they are fully specialized in the
specialization, that's OK. */
int j;
for (j = 0; j < nargs; ++j)
if (tpd2.parms[j] != 0
&& tpd.arg_uses_template_parms [j])
{
cp_error ("type `%T' of template argument `%E' depends on template parameter(s)",
type,
arg);
break;
}
}
}
}
}
if (retrieve_specialization (maintmpl, specargs))
/* We've already got this specialization. */
return decl;
DECL_TEMPLATE_SPECIALIZATIONS (maintmpl) = CLASSTYPE_TI_SPEC_INFO (type)
= perm_tree_cons (inner_args, inner_parms,
DECL_TEMPLATE_SPECIALIZATIONS (maintmpl));
TREE_TYPE (DECL_TEMPLATE_SPECIALIZATIONS (maintmpl)) = type;
return decl;
}
/* Check that a template declaration's use of default arguments is not
invalid. Here, PARMS are the template parameters. IS_PRIMARY is
non-zero if DECL is the thing declared by a primary template.
IS_PARTIAL is non-zero if DECL is a partial specialization. */
static void
check_default_tmpl_args (decl, parms, is_primary, is_partial)
tree decl;
tree parms;
int is_primary;
int is_partial;
{
const char *msg;
int last_level_to_check;
/* [temp.param]
A default template-argument shall not be specified in a
function template declaration or a function template definition, nor
in the template-parameter-list of the definition of a member of a
class template. */
if (current_class_type
&& !TYPE_BEING_DEFINED (current_class_type)
&& DECL_LANG_SPECIFIC (decl)
/* If this is either a friend defined in the scope of the class
or a member function. */
&& DECL_CLASS_CONTEXT (decl) == current_class_type
/* And, if it was a member function, it really was defined in
the scope of the class. */
&& (!DECL_FUNCTION_MEMBER_P (decl) || DECL_DEFINED_IN_CLASS_P (decl)))
/* We already checked these parameters when the template was
declared, so there's no need to do it again now. This function
was defined in class scope, but we're processing it's body now
that the class is complete. */
return;
if (TREE_CODE (decl) != TYPE_DECL || is_partial || !is_primary)
/* For an ordinary class template, default template arguments are
allowed at the innermost level, e.g.:
template <class T = int>
struct S {};
but, in a partial specialization, they're not allowed even
there, as we have in [temp.class.spec]:
The template parameter list of a specialization shall not
contain default template argument values.
So, for a partial specialization, or for a function template,
we look at all of them. */
;
else
/* But, for a primary class template that is not a partial
specialization we look at all template parameters except the
innermost ones. */
parms = TREE_CHAIN (parms);
/* Figure out what error message to issue. */
if (TREE_CODE (decl) == FUNCTION_DECL)
msg = "default argument for template parameter in function template `%D'";
else if (is_partial)
msg = "default argument in partial specialization `%D'";
else
msg = "default argument for template parameter for class enclosing `%D'";
if (current_class_type && TYPE_BEING_DEFINED (current_class_type))
/* If we're inside a class definition, there's no need to
examine the parameters to the class itself. On the one
hand, they will be checked when the class is defined, and,
on the other, default arguments are legal in things like:
template <class T = double>
struct S { template <class U> void f(U); };
Here the default argument for `S' has no bearing on the
declaration of `f'. */
last_level_to_check = template_class_depth (current_class_type) + 1;
else
/* Check everything. */
last_level_to_check = 0;
for (; parms && TMPL_PARMS_DEPTH (parms) >= last_level_to_check;
parms = TREE_CHAIN (parms))
{
tree inner_parms = TREE_VALUE (parms);
int i, ntparms;
ntparms = TREE_VEC_LENGTH (inner_parms);
for (i = 0; i < ntparms; ++i)
if (TREE_PURPOSE (TREE_VEC_ELT (inner_parms, i)))
{
if (msg)
{
cp_error (msg, decl);
msg = 0;
}
/* Clear out the default argument so that we are not
confused later. */
TREE_PURPOSE (TREE_VEC_ELT (inner_parms, i)) = NULL_TREE;
}
/* At this point, if we're still interested in issuing messages,
they must apply to classes surrounding the object declared. */
if (msg)
msg = "default argument for template parameter for class enclosing `%D'";
}
}
/* Creates a TEMPLATE_DECL for the indicated DECL using the template
parameters given by current_template_args, or reuses a
previously existing one, if appropriate. Returns the DECL, or an
equivalent one, if it is replaced via a call to duplicate_decls.
If IS_FRIEND is non-zero, DECL is a friend declaration. */
tree
push_template_decl_real (decl, is_friend)
tree decl;
int is_friend;
{
tree tmpl;
tree args;
tree info;
tree ctx;
int primary;
int is_partial;
/* See if this is a partial specialization. */
is_partial = (TREE_CODE (decl) == TYPE_DECL && DECL_ARTIFICIAL (decl)
&& TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE
&& CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (decl)));
is_friend |= (TREE_CODE (decl) == FUNCTION_DECL && DECL_FRIEND_P (decl));
if (is_friend)
/* For a friend, we want the context of the friend function, not
the type of which it is a friend. */
ctx = DECL_CONTEXT (decl);
else if (DECL_REAL_CONTEXT (decl)
&& TREE_CODE (DECL_REAL_CONTEXT (decl)) != NAMESPACE_DECL)
/* In the case of a virtual function, we want the class in which
it is defined. */
ctx = DECL_REAL_CONTEXT (decl);
else
/* Otherwise, if we're currently definining some class, the DECL
is assumed to be a member of the class. */
ctx = current_class_type;
if (ctx && TREE_CODE (ctx) == NAMESPACE_DECL)
ctx = NULL_TREE;
if (!DECL_CONTEXT (decl))
DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
/* For determining whether this is a primary template or not, we're really
interested in the lexical context, not the true context. */
if (is_friend)
info = current_class_type;
else
info = ctx;
/* See if this is a primary template. */
if (info && TREE_CODE (info) == FUNCTION_DECL)
primary = 0;
/* Note that template_class_depth returns 0 if given NULL_TREE, so
this next line works even when we are at global scope. */
else if (processing_template_decl > template_class_depth (info))
primary = 1;
else
primary = 0;
if (primary)
{
if (current_lang_name == lang_name_c)
cp_error ("template with C linkage");
if (TREE_CODE (decl) == TYPE_DECL && ANON_AGGRNAME_P (DECL_NAME (decl)))
cp_error ("template class without a name");
if (TREE_CODE (decl) == TYPE_DECL
&& TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
cp_error ("template declaration of `%#T'", TREE_TYPE (decl));
}
/* Check to see that the rules regarding the use of default
arguments are not being violated. */
check_default_tmpl_args (decl, current_template_parms,
primary, is_partial);
if (is_partial)
return process_partial_specialization (decl);
args = current_template_args ();
if (!ctx
|| TREE_CODE (ctx) == FUNCTION_DECL
|| TYPE_BEING_DEFINED (ctx)
|| (is_friend && !DECL_TEMPLATE_INFO (decl)))
{
if (DECL_LANG_SPECIFIC (decl)
&& DECL_TEMPLATE_INFO (decl)
&& DECL_TI_TEMPLATE (decl))
tmpl = DECL_TI_TEMPLATE (decl);
else
{
tmpl = build_template_decl (decl, current_template_parms);
if (DECL_LANG_SPECIFIC (decl)
&& DECL_TEMPLATE_SPECIALIZATION (decl))
{
/* A specialization of a member template of a template
class. */
SET_DECL_TEMPLATE_SPECIALIZATION (tmpl);
DECL_TEMPLATE_INFO (tmpl) = DECL_TEMPLATE_INFO (decl);
DECL_TEMPLATE_INFO (decl) = NULL_TREE;
}
}
}
else
{
tree a, t, current, parms;
int i;
if (CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
cp_error ("must specialize `%#T' before defining member `%#D'",
ctx, decl);
if (TREE_CODE (decl) == TYPE_DECL)
{
if ((IS_AGGR_TYPE_CODE (TREE_CODE (TREE_TYPE (decl)))
|| TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
&& TYPE_TEMPLATE_INFO (TREE_TYPE (decl))
&& TYPE_TI_TEMPLATE (TREE_TYPE (decl)))
tmpl = TYPE_TI_TEMPLATE (TREE_TYPE (decl));
else
{
cp_error ("`%D' does not declare a template type", decl);
return decl;
}
}
else if (! DECL_TEMPLATE_INFO (decl))
{
cp_error ("template definition of non-template `%#D'", decl);
return decl;
}
else
tmpl = DECL_TI_TEMPLATE (decl);
if (is_member_template (tmpl)
&& DECL_FUNCTION_TEMPLATE_P (tmpl)
&& DECL_TEMPLATE_INFO (decl) && DECL_TI_ARGS (decl)
&& DECL_TEMPLATE_SPECIALIZATION (decl))
{
tree new_tmpl;
/* The declaration is a specialization of a member
template, declared outside the class. Therefore, the
innermost template arguments will be NULL, so we
replace them with the arguments determined by the
earlier call to check_explicit_specialization. */
args = DECL_TI_ARGS (decl);
new_tmpl
= build_template_decl (decl, current_template_parms);
DECL_TEMPLATE_RESULT (new_tmpl) = decl;
TREE_TYPE (new_tmpl) = TREE_TYPE (decl);
DECL_TI_TEMPLATE (decl) = new_tmpl;
SET_DECL_TEMPLATE_SPECIALIZATION (new_tmpl);
DECL_TEMPLATE_INFO (new_tmpl) =
perm_tree_cons (tmpl, args, NULL_TREE);
register_specialization (new_tmpl, tmpl, args);
return decl;
}
/* Make sure the template headers we got make sense. */
parms = DECL_TEMPLATE_PARMS (tmpl);
i = TMPL_PARMS_DEPTH (parms);
if (TMPL_ARGS_DEPTH (args) != i)
{
cp_error ("expected %d levels of template parms for `%#D', got %d",
i, decl, TMPL_ARGS_DEPTH (args));
}
else
for (current = decl; i > 0; --i, parms = TREE_CHAIN (parms))
{
a = TMPL_ARGS_LEVEL (args, i);
t = INNERMOST_TEMPLATE_PARMS (parms);
if (TREE_VEC_LENGTH (t) != TREE_VEC_LENGTH (a))
{
if (current == decl)
cp_error ("got %d template parameters for `%#D'",
TREE_VEC_LENGTH (a), decl);
else
cp_error ("got %d template parameters for `%#T'",
TREE_VEC_LENGTH (a), current);
cp_error (" but %d required", TREE_VEC_LENGTH (t));
}
/* Perhaps we should also check that the parms are used in the
appropriate qualifying scopes in the declarator? */
if (current == decl)
current = ctx;
else
current = TYPE_CONTEXT (current);
}
}
DECL_TEMPLATE_RESULT (tmpl) = decl;
TREE_TYPE (tmpl) = TREE_TYPE (decl);
/* Push template declarations for global functions and types. Note
that we do not try to push a global template friend declared in a
template class; such a thing may well depend on the template
parameters of the class. */
if (! ctx
&& !(is_friend && template_class_depth (current_class_type) > 0))
tmpl = pushdecl_namespace_level (tmpl);
if (primary)
DECL_PRIMARY_TEMPLATE (tmpl) = tmpl;
info = perm_tree_cons (tmpl, args, NULL_TREE);
if (TREE_CODE (decl) == TYPE_DECL && DECL_ARTIFICIAL (decl))
{
SET_TYPE_TEMPLATE_INFO (TREE_TYPE (tmpl), info);
if ((!ctx || TREE_CODE (ctx) != FUNCTION_DECL)
&& TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE)
DECL_NAME (decl) = classtype_mangled_name (TREE_TYPE (decl));
}
else if (! DECL_LANG_SPECIFIC (decl))
cp_error ("template declaration of `%#D'", decl);
else
DECL_TEMPLATE_INFO (decl) = info;
return DECL_TEMPLATE_RESULT (tmpl);
}
tree
push_template_decl (decl)
tree decl;
{
return push_template_decl_real (decl, 0);
}
/* Called when a class template TYPE is redeclared with the indicated
template PARMS, e.g.:
template <class T> struct S;
template <class T> struct S {}; */
void
redeclare_class_template (type, parms)
tree type;
tree parms;
{
tree tmpl;
tree tmpl_parms;
int i;
if (!TYPE_TEMPLATE_INFO (type))
{
cp_error ("`%T' is not a template type", type);
return;
}
tmpl = TYPE_TI_TEMPLATE (type);
if (!PRIMARY_TEMPLATE_P (tmpl))
/* The type is nested in some template class. Nothing to worry
about here; there are no new template parameters for the nested
type. */
return;
parms = INNERMOST_TEMPLATE_PARMS (parms);
tmpl_parms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
if (TREE_VEC_LENGTH (parms) != TREE_VEC_LENGTH (tmpl_parms))
{
cp_error_at ("previous declaration `%D'", tmpl);
cp_error ("used %d template parameter%s instead of %d",
TREE_VEC_LENGTH (tmpl_parms),
TREE_VEC_LENGTH (tmpl_parms) == 1 ? "" : "s",
TREE_VEC_LENGTH (parms));
return;
}
for (i = 0; i < TREE_VEC_LENGTH (tmpl_parms); ++i)
{
tree tmpl_parm = TREE_VALUE (TREE_VEC_ELT (tmpl_parms, i));
tree parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
tree tmpl_default = TREE_PURPOSE (TREE_VEC_ELT (tmpl_parms, i));
tree parm_default = TREE_PURPOSE (TREE_VEC_ELT (parms, i));
if (TREE_CODE (tmpl_parm) != TREE_CODE (parm))
{
cp_error_at ("template parameter `%#D'", tmpl_parm);
cp_error ("redeclared here as `%#D'", parm);
return;
}
if (tmpl_default != NULL_TREE && parm_default != NULL_TREE)
{
/* We have in [temp.param]:
A template-parameter may not be given default arguments
by two different declarations in the same scope. */
cp_error ("redefinition of default argument for `%#D'", parm);
cp_error_at (" original definition appeared here", tmpl_parm);
return;
}
if (parm_default != NULL_TREE)
/* Update the previous template parameters (which are the ones
that will really count) with the new default value. */
TREE_PURPOSE (TREE_VEC_ELT (tmpl_parms, i)) = parm_default;
}
}
/* Attempt to convert the non-type template parameter EXPR to the
indicated TYPE. If the conversion is successful, return the
converted value. If the conversion is unsuccesful, return
NULL_TREE if we issued an error message, or error_mark_node if we
did not. We issue error messages for out-and-out bad template
parameters, but not simply because the conversion failed, since we
might be just trying to do argument deduction. By the time this
function is called, neither TYPE nor EXPR may make use of template
parameters. */
static tree
convert_nontype_argument (type, expr)
tree type;
tree expr;
{
tree expr_type = TREE_TYPE (expr);
/* A template-argument for a non-type, non-template
template-parameter shall be one of:
--an integral constant-expression of integral or enumeration
type; or
--the name of a non-type template-parameter; or
--the name of an object or function with external linkage,
including function templates and function template-ids but
excluding non-static class members, expressed as id-expression;
or
--the address of an object or function with external linkage,
including function templates and function template-ids but
excluding non-static class members, expressed as & id-expression
where the & is optional if the name refers to a function or
array; or
--a pointer to member expressed as described in _expr.unary.op_. */
/* An integral constant-expression can include const variables
or enumerators. */
if (INTEGRAL_TYPE_P (expr_type) && TREE_READONLY_DECL_P (expr))
expr = decl_constant_value (expr);
if (is_overloaded_fn (expr))
/* OK for now. We'll check that it has external linkage later.
Check this first since if expr_type is the unknown_type_node
we would otherwise complain below. */
;
else if (TYPE_PTRMEM_P (expr_type)
|| TYPE_PTRMEMFUNC_P (expr_type))
{
if (TREE_CODE (expr) != PTRMEM_CST)
goto bad_argument;
}
else if (TYPE_PTR_P (expr_type)
|| TYPE_PTRMEM_P (expr_type)
|| TREE_CODE (expr_type) == ARRAY_TYPE
|| TREE_CODE (type) == REFERENCE_TYPE
/* If expr is the address of an overloaded function, we
will get the unknown_type_node at this point. */
|| expr_type == unknown_type_node)
{
tree referent;
tree e = expr;
STRIP_NOPS (e);
if (TREE_CODE (type) == REFERENCE_TYPE
|| TREE_CODE (expr_type) == ARRAY_TYPE)
referent = e;
else
{
if (TREE_CODE (e) != ADDR_EXPR)
{
bad_argument:
cp_error ("`%E' is not a valid template argument", expr);
if (TYPE_PTR_P (expr_type))
{
if (TREE_CODE (TREE_TYPE (expr_type)) == FUNCTION_TYPE)
cp_error ("it must be the address of a function with external linkage");
else
cp_error ("it must be the address of an object with external linkage");
}
else if (TYPE_PTRMEM_P (expr_type)
|| TYPE_PTRMEMFUNC_P (expr_type))
cp_error ("it must be a pointer-to-member of the form `&X::Y'");
return NULL_TREE;
}
referent = TREE_OPERAND (e, 0);
STRIP_NOPS (referent);
}
if (TREE_CODE (referent) == STRING_CST)
{
cp_error ("string literal %E is not a valid template argument",
referent);
error ("because it is the address of an object with static linkage");
return NULL_TREE;
}
if (is_overloaded_fn (referent))
/* We'll check that it has external linkage later. */
;
else if (TREE_CODE (referent) != VAR_DECL)
goto bad_argument;
else if (!TREE_PUBLIC (referent))
{
cp_error ("address of non-extern `%E' cannot be used as template argument", referent);
return error_mark_node;
}
}
else if (INTEGRAL_TYPE_P (expr_type)
|| TYPE_PTRMEM_P (expr_type)
|| TYPE_PTRMEMFUNC_P (expr_type)
/* The next two are g++ extensions. */
|| TREE_CODE (expr_type) == REAL_TYPE
|| TREE_CODE (expr_type) == COMPLEX_TYPE)
{
if (! TREE_CONSTANT (expr))
{
non_constant:
cp_error ("non-constant `%E' cannot be used as template argument",
expr);
return NULL_TREE;
}
}
else
{
cp_error ("object `%E' cannot be used as template argument", expr);
return NULL_TREE;
}
switch (TREE_CODE (type))
{
case INTEGER_TYPE:
case BOOLEAN_TYPE:
case ENUMERAL_TYPE:
/* For a non-type template-parameter of integral or enumeration
type, integral promotions (_conv.prom_) and integral
conversions (_conv.integral_) are applied. */
if (!INTEGRAL_TYPE_P (expr_type))
return error_mark_node;
/* It's safe to call digest_init in this case; we know we're
just converting one integral constant expression to another. */
expr = digest_init (type, expr, (tree*) 0);
if (TREE_CODE (expr) != INTEGER_CST)
/* Curiously, some TREE_CONSTANT integral expressions do not
simplify to integer constants. For example, `3 % 0',
remains a TRUNC_MOD_EXPR. */
goto non_constant;
return expr;
case REAL_TYPE:
case COMPLEX_TYPE:
/* These are g++ extensions. */
if (TREE_CODE (expr_type) != TREE_CODE (type))
return error_mark_node;
expr = digest_init (type, expr, (tree*) 0);
if (TREE_CODE (expr) != REAL_CST)
goto non_constant;
return expr;
case POINTER_TYPE:
{
tree type_pointed_to = TREE_TYPE (type);
if (TYPE_PTRMEM_P (type))
{
tree e;
/* For a non-type template-parameter of type pointer to data
member, qualification conversions (_conv.qual_) are
applied. */
e = perform_qualification_conversions (type, expr);
if (TREE_CODE (e) == NOP_EXPR)
/* The call to perform_qualification_conversions will
insert a NOP_EXPR over EXPR to do express conversion,
if necessary. But, that will confuse us if we use
this (converted) template parameter to instantiate
another template; then the thing will not look like a
valid template argument. So, just make a new
constant, of the appropriate type. */
e = make_ptrmem_cst (type, PTRMEM_CST_MEMBER (expr));
return e;
}
else if (TREE_CODE (type_pointed_to) == FUNCTION_TYPE)
{
/* For a non-type template-parameter of type pointer to
function, only the function-to-pointer conversion
(_conv.func_) is applied. If the template-argument
represents a set of overloaded functions (or a pointer to
such), the matching function is selected from the set
(_over.over_). */
tree fns;
tree fn;
if (TREE_CODE (expr) == ADDR_EXPR)
fns = TREE_OPERAND (expr, 0);
else
fns = expr;
fn = instantiate_type (type_pointed_to, fns, 0);
if (fn == error_mark_node)
return error_mark_node;
if (!TREE_PUBLIC (fn))
{
if (really_overloaded_fn (fns))
return error_mark_node;
else
goto bad_argument;
}
expr = build_unary_op (ADDR_EXPR, fn, 0);
my_friendly_assert (same_type_p (type, TREE_TYPE (expr)),
0);
return expr;
}
else
{
/* For a non-type template-parameter of type pointer to
object, qualification conversions (_conv.qual_) and the
array-to-pointer conversion (_conv.array_) are applied.
[Note: In particular, neither the null pointer conversion
(_conv.ptr_) nor the derived-to-base conversion
(_conv.ptr_) are applied. Although 0 is a valid
template-argument for a non-type template-parameter of
integral type, it is not a valid template-argument for a
non-type template-parameter of pointer type.]
The call to decay_conversion performs the
array-to-pointer conversion, if appropriate. */
expr = decay_conversion (expr);
if (expr == error_mark_node)
return error_mark_node;
else
return perform_qualification_conversions (type, expr);
}
}
break;
case REFERENCE_TYPE:
{
tree type_referred_to = TREE_TYPE (type);
if (TREE_CODE (type_referred_to) == FUNCTION_TYPE)
{
/* For a non-type template-parameter of type reference to
function, no conversions apply. If the
template-argument represents a set of overloaded
functions, the matching function is selected from the
set (_over.over_). */
tree fns = expr;
tree fn;
fn = instantiate_type (type_referred_to, fns, 0);
if (fn == error_mark_node)
return error_mark_node;
if (!TREE_PUBLIC (fn))
{
if (really_overloaded_fn (fns))
/* Don't issue an error here; we might get a different
function if the overloading had worked out
differently. */
return error_mark_node;
else
goto bad_argument;
}
my_friendly_assert (same_type_p (type_referred_to,
TREE_TYPE (fn)),
0);
return fn;
}
else
{
/* For a non-type template-parameter of type reference to
object, no conversions apply. The type referred to by the
reference may be more cv-qualified than the (otherwise
identical) type of the template-argument. The
template-parameter is bound directly to the
template-argument, which must be an lvalue. */
if ((TYPE_MAIN_VARIANT (expr_type)
!= TYPE_MAIN_VARIANT (type_referred_to))
|| !at_least_as_qualified_p (type_referred_to,
expr_type)
|| !real_lvalue_p (expr))
return error_mark_node;
else
return expr;
}
}
break;
case RECORD_TYPE:
{
if (!TYPE_PTRMEMFUNC_P (type))
/* This handles templates like
template<class T, T t> void f();
when T is substituted with any class. The second template
parameter becomes invalid and the template candidate is
rejected. */
return error_mark_node;
/* For a non-type template-parameter of type pointer to member
function, no conversions apply. If the template-argument
represents a set of overloaded member functions, the
matching member function is selected from the set
(_over.over_). */
if (!TYPE_PTRMEMFUNC_P (expr_type) &&
expr_type != unknown_type_node)
return error_mark_node;
if (TREE_CODE (expr) == PTRMEM_CST)
{
/* A ptr-to-member constant. */
if (!same_type_p (type, expr_type))
return error_mark_node;
else
return expr;
}
if (TREE_CODE (expr) != ADDR_EXPR)
return error_mark_node;
expr = instantiate_type (type, expr, 0);
if (expr == error_mark_node)
return error_mark_node;
my_friendly_assert (same_type_p (type, TREE_TYPE (expr)),
0);
return expr;
}
break;
default:
/* All non-type parameters must have one of these types. */
my_friendly_abort (0);
break;
}
return error_mark_node;
}
/* Return 1 if PARM_PARMS and ARG_PARMS matches using rule for
template template parameters. Both PARM_PARMS and ARG_PARMS are
vectors of TREE_LIST nodes containing TYPE_DECL, TEMPLATE_DECL
or PARM_DECL.
ARG_PARMS may contain more parameters than PARM_PARMS. If this is
the case, then extra parameters must have default arguments.
Consider the example:
template <class T, class Allocator = allocator> class vector;
template<template <class U> class TT> class C;
C<vector> is a valid instantiation. PARM_PARMS for the above code
contains a TYPE_DECL (for U), ARG_PARMS contains two TYPE_DECLs (for
T and Allocator) and OUTER_ARGS contains the argument that is used to
substitute the TT parameter. */
static int
coerce_template_template_parms (parm_parms, arg_parms, complain,
in_decl, outer_args)
tree parm_parms, arg_parms;
int complain;
tree in_decl, outer_args;
{
int nparms, nargs, i;
tree parm, arg;
my_friendly_assert (TREE_CODE (parm_parms) == TREE_VEC, 0);
my_friendly_assert (TREE_CODE (arg_parms) == TREE_VEC, 0);
nparms = TREE_VEC_LENGTH (parm_parms);
nargs = TREE_VEC_LENGTH (arg_parms);
/* The rule here is opposite of coerce_template_parms. */
if (nargs < nparms
|| (nargs > nparms
&& TREE_PURPOSE (TREE_VEC_ELT (arg_parms, nparms)) == NULL_TREE))
return 0;
for (i = 0; i < nparms; ++i)
{
parm = TREE_VALUE (TREE_VEC_ELT (parm_parms, i));
arg = TREE_VALUE (TREE_VEC_ELT (arg_parms, i));
if (arg == NULL_TREE || arg == error_mark_node
|| parm == NULL_TREE || parm == error_mark_node)
return 0;
if (TREE_CODE (arg) != TREE_CODE (parm))
return 0;
switch (TREE_CODE (parm))
{
case TYPE_DECL:
break;
case TEMPLATE_DECL:
/* We encounter instantiations of templates like
template <template <template <class> class> class TT>
class C; */
{
tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
if (!coerce_template_template_parms (parmparm, argparm,
complain, in_decl,
outer_args))
return 0;
}
break;
case PARM_DECL:
/* The tsubst call is used to handle cases such as
template <class T, template <T> class TT> class D;
i.e. the parameter list of TT depends on earlier parameters. */
if (!same_type_p (tsubst (TREE_TYPE (parm), outer_args,
complain, in_decl),
TREE_TYPE (arg)))
return 0;
break;
default:
my_friendly_abort (0);
}
}
return 1;
}
/* Convert the indicated template ARG as necessary to match the
indicated template PARM. Returns the converted ARG, or
error_mark_node if the conversion was unsuccessful. Error messages
are issued if COMPLAIN is non-zero. This conversion is for the Ith
parameter in the parameter list. ARGS is the full set of template
arguments deduced so far. */
static tree
convert_template_argument (parm, arg, args, complain, i, in_decl)
tree parm;
tree arg;
tree args;
int complain;
int i;
tree in_decl;
{
tree val;
tree inner_args;
int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
inner_args = innermost_args (args);
if (TREE_CODE (arg) == TREE_LIST
&& TREE_TYPE (arg) != NULL_TREE
&& TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE)
{
/* The template argument was the name of some
member function. That's usually
illegal, but static members are OK. In any
case, grab the underlying fields/functions
and issue an error later if required. */
arg = TREE_VALUE (arg);
TREE_TYPE (arg) = unknown_type_node;
}
requires_tmpl_type = TREE_CODE (parm) == TEMPLATE_DECL;
requires_type = (TREE_CODE (parm) == TYPE_DECL
|| requires_tmpl_type);
/* Check if it is a class template. If REQUIRES_TMPL_TYPE is true,
we also accept implicitly created TYPE_DECL as a valid argument.
This is necessary to handle the case where we pass a template name
to a template template parameter in a scope where we've derived from
in instantiation of that template, so the template name refers to that
instantiation. We really ought to handle this better. */
is_tmpl_type
= ((TREE_CODE (arg) == TEMPLATE_DECL
&& TREE_CODE (DECL_TEMPLATE_RESULT (arg)) == TYPE_DECL)
|| (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
&& !TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (arg))
|| (TREE_CODE (arg) == RECORD_TYPE
&& CLASSTYPE_TEMPLATE_INFO (arg)
&& TREE_CODE (TYPE_NAME (arg)) == TYPE_DECL
&& DECL_ARTIFICIAL (TYPE_NAME (arg))
&& requires_tmpl_type
&& is_base_of_enclosing_class (arg, current_class_type)));
if (is_tmpl_type && TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM)
arg = TYPE_STUB_DECL (arg);
else if (is_tmpl_type && TREE_CODE (arg) == RECORD_TYPE)
arg = CLASSTYPE_TI_TEMPLATE (arg);
is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't' || is_tmpl_type;
if (requires_type && ! is_type && TREE_CODE (arg) == SCOPE_REF
&& TREE_CODE (TREE_OPERAND (arg, 0)) == TEMPLATE_TYPE_PARM)
{
cp_pedwarn ("to refer to a type member of a template parameter,");
cp_pedwarn (" use `typename %E'", arg);
arg = make_typename_type (TREE_OPERAND (arg, 0),
TREE_OPERAND (arg, 1));
is_type = 1;
}
if (is_type != requires_type)
{
if (in_decl)
{
if (complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
if (is_type)
cp_error (" expected a constant of type `%T', got `%T'",
TREE_TYPE (parm),
(is_tmpl_type ? DECL_NAME (arg) : arg));
else
cp_error (" expected a type, got `%E'", arg);
}
}
return error_mark_node;
}
if (is_tmpl_type ^ requires_tmpl_type)
{
if (in_decl && complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
if (is_tmpl_type)
cp_error (" expected a type, got `%T'", DECL_NAME (arg));
else
cp_error (" expected a class template, got `%T'", arg);
}
return error_mark_node;
}
if (is_type)
{
if (requires_tmpl_type)
{
tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
if (coerce_template_template_parms (parmparm, argparm, complain,
in_decl, inner_args))
{
val = arg;
/* TEMPLATE_TEMPLATE_PARM node is preferred over
TEMPLATE_DECL. */
if (val != error_mark_node
&& DECL_TEMPLATE_TEMPLATE_PARM_P (val))
val = TREE_TYPE (val);
}
else
{
if (in_decl && complain)
{
cp_error ("type/value mismatch at argument %d in template parameter list for `%D'",
i + 1, in_decl);
cp_error (" expected a template of type `%D', got `%D'", parm, arg);
}
val = error_mark_node;
}
}
else
{
val = groktypename (arg);
if (! processing_template_decl)
{
/* [basic.link]: A name with no linkage (notably, the
name of a class or enumeration declared in a local
scope) shall not be used to declare an entity with
linkage. This implies that names with no linkage
cannot be used as template arguments. */
tree t = no_linkage_check (val);
if (t)
{
if (ANON_AGGRNAME_P (TYPE_IDENTIFIER (t)))
cp_pedwarn
("template-argument `%T' uses anonymous type", val);
else
cp_error
("template-argument `%T' uses local type `%T'",
val, t);
return error_mark_node;
}
}
}
}
else
{
tree t = tsubst (TREE_TYPE (parm), args, complain, in_decl);
if (processing_template_decl)
arg = maybe_fold_nontype_arg (arg);
if (!uses_template_parms (arg) && !uses_template_parms (t))
/* We used to call digest_init here. However, digest_init
will report errors, which we don't want when complain
is zero. More importantly, digest_init will try too
hard to convert things: for example, `0' should not be
converted to pointer type at this point according to
the standard. Accepting this is not merely an
extension, since deciding whether or not these
conversions can occur is part of determining which
function template to call, or whether a given epxlicit
argument specification is legal. */
val = convert_nontype_argument (t, arg);
else
val = arg;
if (val == NULL_TREE)
val = error_mark_node;
else if (val == error_mark_node && complain)
cp_error ("could not convert template argument `%E' to `%T'",
arg, t);
}
return val;
}
/* Convert all template arguments to their appropriate types, and
return a vector containing the innermost resulting template
arguments. If any error occurs, return error_mark_node, and, if
COMPLAIN is non-zero, issue an error message. Some error messages
are issued even if COMPLAIN is zero; for instance, if a template
argument is composed from a local class.
If REQUIRE_ALL_ARGUMENTS is non-zero, all arguments must be
provided in ARGLIST, or else trailing parameters must have default
values. If REQUIRE_ALL_ARGUMENTS is zero, we will attempt argument
deduction for any unspecified trailing arguments.
The resulting TREE_VEC is allocated on a temporary obstack, and
must be explicitly copied if it will be permanent. */
static tree
coerce_template_parms (parms, args, in_decl,
complain,
require_all_arguments)
tree parms, args;
tree in_decl;
int complain;
int require_all_arguments;
{
int nparms, nargs, i, lost = 0;
tree inner_args;
tree new_args;
tree new_inner_args;
inner_args = innermost_args (args);
nargs = NUM_TMPL_ARGS (inner_args);
nparms = TREE_VEC_LENGTH (parms);
if (nargs > nparms
|| (nargs < nparms
&& require_all_arguments
&& TREE_PURPOSE (TREE_VEC_ELT (parms, nargs)) == NULL_TREE))
{
if (complain)
{
cp_error ("wrong number of template arguments (%d, should be %d)",
nargs, nparms);
if (in_decl)
cp_error_at ("provided for `%D'", in_decl);
}
return error_mark_node;
}
new_inner_args = make_temp_vec (nparms);
new_args = add_outermost_template_args (args, new_inner_args);
for (i = 0; i < nparms; i++)
{
tree arg;
tree parm;
/* Get the Ith template parameter. */
parm = TREE_VEC_ELT (parms, i);
/* Calculate the Ith argument. */
if (inner_args && TREE_CODE (inner_args) == TREE_LIST)
{
arg = TREE_VALUE (inner_args);
inner_args = TREE_CHAIN (inner_args);
}
else if (i < nargs)
arg = TREE_VEC_ELT (inner_args, i);
/* If no template argument was supplied, look for a default
value. */
else if (TREE_PURPOSE (parm) == NULL_TREE)
{
/* There was no default value. */
my_friendly_assert (!require_all_arguments, 0);
break;
}
else if (TREE_CODE (TREE_VALUE (parm)) == TYPE_DECL)
arg = tsubst (TREE_PURPOSE (parm), new_args, complain, in_decl);
else
arg = tsubst_expr (TREE_PURPOSE (parm), new_args, complain,
in_decl);
/* Now, convert the Ith argument, as necessary. */
if (arg == NULL_TREE)
/* We're out of arguments. */
{
my_friendly_assert (!require_all_arguments, 0);
break;
}
else if (arg == error_mark_node)
{
cp_error ("template argument %d is invalid", i + 1);
arg = error_mark_node;
}
else
arg = convert_template_argument (TREE_VALUE (parm),
arg, new_args, complain, i,
in_decl);
if (arg == error_mark_node)
lost++;
TREE_VEC_ELT (new_inner_args, i) = arg;
}
if (lost)
return error_mark_node;
return new_inner_args;
}
/* Returns 1 if template args OT and NT are equivalent. */
static int
template_args_equal (ot, nt)
tree ot, nt;
{
if (nt == ot)
return 1;
if (TREE_CODE (nt) != TREE_CODE (ot))
return 0;
if (TREE_CODE (nt) == TREE_VEC)
/* For member templates */
return comp_template_args (ot, nt);
else if (TREE_CODE_CLASS (TREE_CODE (ot)) == 't')
return same_type_p (ot, nt);
else
return (cp_tree_equal (ot, nt) > 0);
}
/* Returns 1 iff the OLDARGS and NEWARGS are in fact identical sets
of template arguments. Returns 0 otherwise. */
int
comp_template_args (oldargs, newargs)
tree oldargs, newargs;
{
int i;
if (TREE_VEC_LENGTH (oldargs) != TREE_VEC_LENGTH (newargs))
return 0;
for (i = 0; i < TREE_VEC_LENGTH (oldargs); ++i)
{
tree nt = TREE_VEC_ELT (newargs, i);
tree ot = TREE_VEC_ELT (oldargs, i);
if (! template_args_equal (ot, nt))
return 0;
}
return 1;
}
/* Given class template name and parameter list, produce a user-friendly name
for the instantiation. */
static char *
mangle_class_name_for_template (name, parms, arglist)
char *name;
tree parms, arglist;
{
static struct obstack scratch_obstack;
static char *scratch_firstobj;
int i, nparms;
if (!scratch_firstobj)
gcc_obstack_init (&scratch_obstack);
else
obstack_free (&scratch_obstack, scratch_firstobj);
scratch_firstobj = obstack_alloc (&scratch_obstack, 1);
#define ccat(c) obstack_1grow (&scratch_obstack, (c));
#define cat(s) obstack_grow (&scratch_obstack, (s), strlen (s))
cat (name);
ccat ('<');
nparms = TREE_VEC_LENGTH (parms);
arglist = innermost_args (arglist);
my_friendly_assert (nparms == TREE_VEC_LENGTH (arglist), 268);
for (i = 0; i < nparms; i++)
{
tree parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
tree arg = TREE_VEC_ELT (arglist, i);
if (i)
ccat (',');
if (TREE_CODE (parm) == TYPE_DECL)
{
cat (type_as_string_real (arg, 0, 1));
continue;
}
else if (TREE_CODE (parm) == TEMPLATE_DECL)
{
if (TREE_CODE (arg) == TEMPLATE_DECL)
{
/* Already substituted with real template. Just output
the template name here */
tree context = DECL_CONTEXT (arg);
if (context)
{
my_friendly_assert (TREE_CODE (context) == NAMESPACE_DECL, 980422);
cat(decl_as_string (DECL_CONTEXT (arg), 0));
cat("::");
}
cat (IDENTIFIER_POINTER (DECL_NAME (arg)));
}
else
/* Output the parameter declaration */
cat (type_as_string_real (arg, 0, 1));
continue;
}
else
my_friendly_assert (TREE_CODE (parm) == PARM_DECL, 269);
if (TREE_CODE (arg) == TREE_LIST)
{
/* New list cell was built because old chain link was in
use. */
my_friendly_assert (TREE_PURPOSE (arg) == NULL_TREE, 270);
arg = TREE_VALUE (arg);
}
/* No need to check arglist against parmlist here; we did that
in coerce_template_parms, called from lookup_template_class. */
cat (expr_as_string (arg, 0));
}
{
char *bufp = obstack_next_free (&scratch_obstack);
int offset = 0;
while (bufp[offset - 1] == ' ')
offset--;
obstack_blank_fast (&scratch_obstack, offset);
/* B<C<char> >, not B<C<char>> */
if (bufp[offset - 1] == '>')
ccat (' ');
}
ccat ('>');
ccat ('\0');
return (char *) obstack_base (&scratch_obstack);
}
static tree
classtype_mangled_name (t)
tree t;
{
if (CLASSTYPE_TEMPLATE_INFO (t)
/* Specializations have already had their names set up in
lookup_template_class. */
&& !CLASSTYPE_TEMPLATE_SPECIALIZATION (t))
{
tree tmpl = most_general_template (CLASSTYPE_TI_TEMPLATE (t));
/* For non-primary templates, the template parameters are
implicit from their surrounding context. */
if (PRIMARY_TEMPLATE_P (tmpl))
{
tree name = DECL_NAME (tmpl);
char *mangled_name = mangle_class_name_for_template
(IDENTIFIER_POINTER (name),
DECL_INNERMOST_TEMPLATE_PARMS (tmpl),
CLASSTYPE_TI_ARGS (t));
tree id = get_identifier (mangled_name);
IDENTIFIER_TEMPLATE (id) = name;
return id;
}
}
return TYPE_IDENTIFIER (t);
}
static void
add_pending_template (d)
tree d;
{
tree ti;
if (TREE_CODE_CLASS (TREE_CODE (d)) == 't')
ti = CLASSTYPE_TEMPLATE_INFO (d);
else
ti = DECL_TEMPLATE_INFO (d);
if (TI_PENDING_TEMPLATE_FLAG (ti))
return;
*template_tail = perm_tree_cons
(build_srcloc_here (), d, NULL_TREE);
template_tail = &TREE_CHAIN (*template_tail);
TI_PENDING_TEMPLATE_FLAG (ti) = 1;
}
/* Return a TEMPLATE_ID_EXPR corresponding to the indicated FNS (which
may be either a _DECL or an overloaded function or an
IDENTIFIER_NODE), and ARGLIST. */
tree
lookup_template_function (fns, arglist)
tree fns, arglist;
{
tree type;
if (fns == NULL_TREE)
{
cp_error ("non-template used as template");
return error_mark_node;
}
type = TREE_TYPE (fns);
if (TREE_CODE (fns) == OVERLOAD || !type)
type = unknown_type_node;
if (processing_template_decl)
return build_min (TEMPLATE_ID_EXPR, type, fns, arglist);
else
return build (TEMPLATE_ID_EXPR, type, fns, arglist);
}
/* Within the scope of a template class S<T>, the name S gets bound
(in build_self_reference) to a TYPE_DECL for the class, not a
TEMPLATE_DECL. If DECL is a TYPE_DECL for current_class_type,
or one of its enclosing classes, and that type is a template,
return the associated TEMPLATE_DECL. Otherwise, the original
DECL is returned. */
static tree
maybe_get_template_decl_from_type_decl (decl)
tree decl;
{
return (decl != NULL_TREE
&& TREE_CODE (decl) == TYPE_DECL
&& DECL_ARTIFICIAL (decl)
&& CLASS_TYPE_P (TREE_TYPE (decl))
&& CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (decl)))
? CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl)) : decl;
}
/* Given an IDENTIFIER_NODE (type TEMPLATE_DECL) and a chain of
parameters, find the desired type.
D1 is the PTYPENAME terminal, and ARGLIST is the list of arguments.
(Actually ARGLIST may be either a TREE_LIST or a TREE_VEC. It will
be a TREE_LIST if called directly from the parser, and a TREE_VEC
otherwise.) Since ARGLIST is build on the decl_obstack, we must
copy it here to keep it from being reclaimed when the decl storage
is reclaimed.
IN_DECL, if non-NULL, is the template declaration we are trying to
instantiate.
If ENTERING_SCOPE is non-zero, we are about to enter the scope of
the class we are looking up.
If the template class is really a local class in a template
function, then the FUNCTION_CONTEXT is the function in which it is
being instantiated. */
tree
lookup_template_class (d1, arglist, in_decl, context, entering_scope)
tree d1, arglist;
tree in_decl;
tree context;
int entering_scope;
{
tree template = NULL_TREE, parmlist;
tree t;
if (TREE_CODE (d1) == IDENTIFIER_NODE)
{
if (IDENTIFIER_VALUE (d1)
&& DECL_TEMPLATE_TEMPLATE_PARM_P (IDENTIFIER_VALUE (d1)))
template = IDENTIFIER_VALUE (d1);
else
{
if (context)
push_decl_namespace (context);
if (current_class_type != NULL_TREE)
template =
maybe_get_template_decl_from_type_decl
(IDENTIFIER_CLASS_VALUE (d1));
if (template == NULL_TREE)
template = lookup_name_nonclass (d1);
if (context)
pop_decl_namespace ();
}
if (template)
context = DECL_CONTEXT (template);
}
else if (TREE_CODE (d1) == TYPE_DECL && IS_AGGR_TYPE (TREE_TYPE (d1)))
{
tree type = TREE_TYPE (d1);
/* If we are declaring a constructor, say A<T>::A<T>, we will get
an implicit typename for the second A. Deal with it. */
if (TREE_CODE (type) == TYPENAME_TYPE && TREE_TYPE (type))
type = TREE_TYPE (type);
if (CLASSTYPE_TEMPLATE_INFO (type))
{
template = CLASSTYPE_TI_TEMPLATE (type);
d1 = DECL_NAME (template);
}
}
else if (TREE_CODE (d1) == ENUMERAL_TYPE
|| (TREE_CODE_CLASS (TREE_CODE (d1)) == 't'
&& IS_AGGR_TYPE (d1)))
{
template = TYPE_TI_TEMPLATE (d1);
d1 = DECL_NAME (template);
}
else if (TREE_CODE (d1) == TEMPLATE_DECL
&& TREE_CODE (DECL_RESULT (d1)) == TYPE_DECL)
{
template = d1;
d1 = DECL_NAME (template);
context = DECL_CONTEXT (template);
}
else
my_friendly_abort (272);
/* With something like `template <class T> class X class X { ... };'
we could end up with D1 having nothing but an IDENTIFIER_VALUE.
We don't want to do that, but we have to deal with the situation,
so let's give them some syntax errors to chew on instead of a
crash. */
if (! template)
{
cp_error ("`%T' is not a template", d1);
return error_mark_node;
}
if (context == NULL_TREE)
context = global_namespace;
if (TREE_CODE (template) != TEMPLATE_DECL)
{
cp_error ("non-template type `%T' used as a template", d1);
if (in_decl)
cp_error_at ("for template declaration `%D'", in_decl);
return error_mark_node;
}
if (DECL_TEMPLATE_TEMPLATE_PARM_P (template))
{
/* Create a new TEMPLATE_DECL and TEMPLATE_TEMPLATE_PARM node to store
template arguments */
tree parm = copy_template_template_parm (TREE_TYPE (template));
tree template2 = TYPE_STUB_DECL (parm);
tree arglist2;
parmlist = DECL_INNERMOST_TEMPLATE_PARMS (template);
arglist2 = coerce_template_parms (parmlist, arglist, template, 1, 1);
if (arglist2 == error_mark_node)
return error_mark_node;
arglist2 = copy_to_permanent (arglist2);
TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (parm)
= perm_tree_cons (template2, arglist2, NULL_TREE);
TYPE_SIZE (parm) = 0;
return parm;
}
else
{
tree template_type = TREE_TYPE (template);
tree gen_tmpl;
tree type_decl;
tree found = NULL_TREE;
int arg_depth;
int parm_depth;
int is_partial_instantiation;
gen_tmpl = most_general_template (template);
parmlist = DECL_TEMPLATE_PARMS (gen_tmpl);
parm_depth = TMPL_PARMS_DEPTH (parmlist);
arg_depth = TMPL_ARGS_DEPTH (arglist);
/* We build up the coerced arguments and such on the
momentary_obstack. */
push_momentary ();
if (arg_depth == 1 && parm_depth > 1)
{
/* We've been given an incomplete set of template arguments.
For example, given:
template <class T> struct S1 {
template <class U> struct S2 {};
template <class U> struct S2<U*> {};
};
we will be called with an ARGLIST of `U*', but the
TEMPLATE will be `template <class T> template
<class U> struct S1<T>::S2'. We must fill in the missing
arguments. */
arglist
= add_outermost_template_args (TYPE_TI_ARGS (TREE_TYPE (template)),
arglist);
arg_depth = TMPL_ARGS_DEPTH (arglist);
}
/* Now we should enough arguments. */
my_friendly_assert (parm_depth == arg_depth, 0);
/* From here on, we're only interested in the most general
template. */
template = gen_tmpl;
/* Calculate the BOUND_ARGS. These will be the args that are
actually tsubst'd into the definition to create the
instantiation. */
if (parm_depth > 1)
{
/* We have multiple levels of arguments to coerce, at once. */
int i;
int saved_depth = TMPL_ARGS_DEPTH (arglist);
tree bound_args = make_temp_vec (parm_depth);
for (i = saved_depth,
t = DECL_TEMPLATE_PARMS (template);
i > 0 && t != NULL_TREE;
--i, t = TREE_CHAIN (t))
{
tree a = coerce_template_parms (TREE_VALUE (t),
arglist, template, 1, 1);
SET_TMPL_ARGS_LEVEL (bound_args, i, a);
/* We temporarily reduce the length of the ARGLIST so
that coerce_template_parms will see only the arguments
corresponding to the template parameters it is
examining. */
TREE_VEC_LENGTH (arglist)--;
}
/* Restore the ARGLIST to its full size. */
TREE_VEC_LENGTH (arglist) = saved_depth;
arglist = bound_args;
}
else
arglist
= coerce_template_parms (INNERMOST_TEMPLATE_PARMS (parmlist),
innermost_args (arglist),
template, 1, 1);
if (arglist == error_mark_node)
/* We were unable to bind the arguments. */
return error_mark_node;
/* In the scope of a template class, explicit references to the
template class refer to the type of the template, not any
instantiation of it. For example, in:
template <class T> class C { void f(C<T>); }
the `C<T>' is just the same as `C'. Outside of the
class, however, such a reference is an instantiation. */
if (comp_template_args (TYPE_TI_ARGS (template_type),
arglist))
{
found = template_type;
if (!entering_scope && PRIMARY_TEMPLATE_P (template))
{
tree ctx;
/* Note that we use DECL_CONTEXT, rather than
CP_DECL_CONTEXT, so that the termination test is
always just `ctx'. We're not interested in namepace
scopes. */
for (ctx = current_class_type;
ctx;
ctx = (TREE_CODE_CLASS (TREE_CODE (ctx)) == 't')
? TYPE_CONTEXT (ctx) : DECL_CONTEXT (ctx))
if (same_type_p (ctx, template_type))
break;
if (!ctx)
/* We're not in the scope of the class, so the
TEMPLATE_TYPE is not the type we want after
all. */
found = NULL_TREE;
}
}
if (!found)
{
for (found = DECL_TEMPLATE_INSTANTIATIONS (template);
found; found = TREE_CHAIN (found))
if (comp_template_args (TREE_PURPOSE (found), arglist))
break;
if (found)
found = TREE_VALUE (found);
}
if (found)
{
pop_momentary ();
return found;
}
/* Since we didn't find the type, we'll have to create it.
Since we'll be saving this type on the
DECL_TEMPLATE_INSTANTIATIONS list, it must be permanent. */
push_obstacks (&permanent_obstack, &permanent_obstack);
/* This type is a "partial instantiation" if any of the template
arguments still inolve template parameters. Note that we set
IS_PARTIAL_INSTANTIATION for partial specializations as
well. */
is_partial_instantiation = uses_template_parms (arglist);
/* Create the type. */
if (TREE_CODE (template_type) == ENUMERAL_TYPE)
{
if (!is_partial_instantiation)
t = start_enum (TYPE_IDENTIFIER (template_type));
else
/* We don't want to call start_enum for this type, since
the values for the enumeration constants may involve
template parameters. And, no one should be interested
in the enumeration constants for such a type. */
t = make_node (ENUMERAL_TYPE);
}
else
{
t = make_lang_type (TREE_CODE (template_type));
CLASSTYPE_DECLARED_CLASS (t)
= CLASSTYPE_DECLARED_CLASS (template_type);
CLASSTYPE_GOT_SEMICOLON (t) = 1;
SET_CLASSTYPE_IMPLICIT_INSTANTIATION (t);
TYPE_FOR_JAVA (t) = TYPE_FOR_JAVA (template_type);
}
/* If we called start_enum above, this information will already
be set up. */
if (!TYPE_NAME (t))
{
TYPE_CONTEXT (t) = FROB_CONTEXT (context);
/* Create a stub TYPE_DECL for it. */
type_decl = build_decl (TYPE_DECL, DECL_NAME (template), t);
SET_DECL_ARTIFICIAL (type_decl);
DECL_CONTEXT (type_decl) = TYPE_CONTEXT (t);
DECL_SOURCE_FILE (type_decl)
= DECL_SOURCE_FILE (TYPE_STUB_DECL (template_type));
DECL_SOURCE_LINE (type_decl)
= DECL_SOURCE_LINE (TYPE_STUB_DECL (template_type));
TYPE_STUB_DECL (t) = TYPE_NAME (t) = type_decl;
}
else
type_decl = TYPE_NAME (t);
/* Set up the template information. We have to figure out which
template is the immediate parent if this is a full
instantiation. */
if (parm_depth == 1 || is_partial_instantiation
|| !PRIMARY_TEMPLATE_P (template))
/* This case is easy; there are no member templates involved. */
found = template;
else
{
/* This is a full instantiation of a member template. There
should be some partial instantiation of which this is an
instance. */
for (found = DECL_TEMPLATE_INSTANTIATIONS (template);
found; found = TREE_CHAIN (found))
{
int success;
tree tmpl = CLASSTYPE_TI_TEMPLATE (TREE_VALUE (found));
/* We only want partial instantiations, here, not
specializations or full instantiations. */
if (CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_VALUE (found))
|| !uses_template_parms (TREE_VALUE (found)))
continue;
/* Temporarily reduce by one the number of levels in the
ARGLIST and in FOUND so as to avoid comparing the
last set of arguments. */
TREE_VEC_LENGTH (arglist)--;
TREE_VEC_LENGTH (TREE_PURPOSE (found)) --;
/* See if the arguments match. If they do, then TMPL is
the partial instantiation we want. */
success = comp_template_args (TREE_PURPOSE (found), arglist);
/* Restore the argument vectors to their full size. */
TREE_VEC_LENGTH (arglist)++;
TREE_VEC_LENGTH (TREE_PURPOSE (found))++;
if (success)
{
found = tmpl;
break;
}
}
if (!found)
my_friendly_abort (0);
}
arglist = copy_to_permanent (arglist);
SET_TYPE_TEMPLATE_INFO (t,
tree_cons (found, arglist, NULL_TREE));
DECL_TEMPLATE_INSTANTIATIONS (template)
= tree_cons (arglist, t,
DECL_TEMPLATE_INSTANTIATIONS (template));
if (TREE_CODE (t) == ENUMERAL_TYPE
&& !is_partial_instantiation)
/* Now that the type has been registered on the instantiations
list, we set up the enumerators. Because the enumeration
constants may involve the enumeration type itself, we make
sure to register the type first, and then create the
constants. That way, doing tsubst_expr for the enumeration
constants won't result in recursive calls here; we'll find
the instantiation and exit above. */
tsubst_enum (template_type, t, arglist);
/* We're done with the permanent obstack, now. */
pop_obstacks ();
/* We're also done with the momentary allocation we started
above. */
pop_momentary ();
/* Reset the name of the type, now that CLASSTYPE_TEMPLATE_INFO
is set up. */
if (TREE_CODE (t) != ENUMERAL_TYPE)
DECL_NAME (type_decl) = classtype_mangled_name (t);
DECL_ASSEMBLER_NAME (type_decl) = DECL_NAME (type_decl);
if (!is_partial_instantiation)
{
DECL_ASSEMBLER_NAME (type_decl)
= get_identifier (build_overload_name (t, 1, 1));
/* For backwards compatibility; code that uses
-fexternal-templates expects looking up a template to
instantiate it. I think DDD still relies on this.
(jason 8/20/1998) */
if (TREE_CODE (t) != ENUMERAL_TYPE
&& flag_external_templates
&& CLASSTYPE_INTERFACE_KNOWN (TREE_TYPE (template))
&& ! CLASSTYPE_INTERFACE_ONLY (TREE_TYPE (template)))
add_pending_template (t);
}
else
/* If the type makes use of template parameters, the
code that generates debugging information will crash. */
DECL_IGNORED_P (TYPE_STUB_DECL (t)) = 1;
return t;
}
}
/* For each TEMPLATE_TYPE_PARM, TEMPLATE_TEMPLATE_PARM, or
TEMPLATE_PARM_INDEX in T, call FN with the parameter and the DATA.
If FN returns non-zero, the iteration is terminated, and
for_each_template_parm returns 1. Otherwise, the iteration
continues. If FN never returns a non-zero value, the value
returned by for_each_template_parm is 0. If FN is NULL, it is
considered to be the function which always returns 1. */
static int
for_each_template_parm (t, fn, data)
tree t;
tree_fn_t fn;
void* data;
{
if (!t)
return 0;
if (TREE_CODE_CLASS (TREE_CODE (t)) == 't'
&& for_each_template_parm (TYPE_CONTEXT (t), fn, data))
return 1;
switch (TREE_CODE (t))
{
case INDIRECT_REF:
case COMPONENT_REF:
/* We assume that the object must be instantiated in order to build
the COMPONENT_REF, so we test only whether the type of the
COMPONENT_REF uses template parms. */
return for_each_template_parm (TREE_TYPE (t), fn, data);
case ARRAY_REF:
case OFFSET_REF:
return (for_each_template_parm (TREE_OPERAND (t, 0), fn, data)
|| for_each_template_parm (TREE_OPERAND (t, 1), fn, data));
case IDENTIFIER_NODE:
if (!IDENTIFIER_TEMPLATE (t))
return 0;
my_friendly_abort (42);
/* aggregates of tree nodes */
case TREE_VEC:
{
int i = TREE_VEC_LENGTH (t);
while (i--)
if (for_each_template_parm (TREE_VEC_ELT (t, i), fn, data))
return 1;
return 0;
}
case TREE_LIST:
if (for_each_template_parm (TREE_PURPOSE (t), fn, data)
|| for_each_template_parm (TREE_VALUE (t), fn, data))
return 1;
return for_each_template_parm (TREE_CHAIN (t), fn, data);
case OVERLOAD:
if (for_each_template_parm (OVL_FUNCTION (t), fn, data))
return 1;
return for_each_template_parm (OVL_CHAIN (t), fn, data);
/* constructed type nodes */
case POINTER_TYPE:
case REFERENCE_TYPE:
return for_each_template_parm (TREE_TYPE (t), fn, data);
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_FLAG (t))
return for_each_template_parm (TYPE_PTRMEMFUNC_FN_TYPE (t),
fn, data);
/* Fall through. */
case UNION_TYPE:
case ENUMERAL_TYPE:
if (! TYPE_TEMPLATE_INFO (t))
return 0;
return for_each_template_parm (TREE_VALUE
(TYPE_TEMPLATE_INFO (t)),
fn, data);
case METHOD_TYPE:
if (for_each_template_parm (TYPE_METHOD_BASETYPE (t), fn, data))
return 1;
/* Fall through. */
case FUNCTION_TYPE:
/* Check the parameter types. Since default arguments are not
instantiated until they are needed, the TYPE_ARG_TYPES may
contain expressions that involve template parameters. But,
no-one should be looking at them yet. And, once they're
instantiated, they don't contain template parameters, so
there's no point in looking at them then, either. */
{
tree parm;
for (parm = TYPE_ARG_TYPES (t); parm; parm = TREE_CHAIN (parm))
if (for_each_template_parm (TREE_VALUE (parm), fn, data))
return 1;
}
/* Check the return type, too. */
return for_each_template_parm (TREE_TYPE (t), fn, data);
case ARRAY_TYPE:
if (for_each_template_parm (TYPE_DOMAIN (t), fn, data))
return 1;
return for_each_template_parm (TREE_TYPE (t), fn, data);
case OFFSET_TYPE:
if (for_each_template_parm (TYPE_OFFSET_BASETYPE (t), fn, data))
return 1;
return for_each_template_parm (TREE_TYPE (t), fn, data);
/* decl nodes */
case TYPE_DECL:
return for_each_template_parm (TREE_TYPE (t), fn, data);
case TEMPLATE_DECL:
/* A template template parameter is encountered */
if (DECL_TEMPLATE_TEMPLATE_PARM_P (t))
return for_each_template_parm (TREE_TYPE (t), fn, data);
/* Already substituted template template parameter */
return 0;
case CONST_DECL:
if (for_each_template_parm (DECL_INITIAL (t), fn, data))
return 1;
goto check_type_and_context;
case FUNCTION_DECL:
case VAR_DECL:
/* ??? What about FIELD_DECLs? */
if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t)
&& for_each_template_parm (DECL_TI_ARGS (t), fn, data))
return 1;
/* fall through */
case PARM_DECL:
check_type_and_context:
if (for_each_template_parm (TREE_TYPE (t), fn, data))
return 1;
if (DECL_CONTEXT (t)
&& for_each_template_parm (DECL_CONTEXT (t), fn, data))
return 1;
return 0;
case CALL_EXPR:
return (for_each_template_parm (TREE_OPERAND (t, 0), fn, data)
|| for_each_template_parm (TREE_OPERAND (t, 1), fn, data));
case ADDR_EXPR:
return for_each_template_parm (TREE_OPERAND (t, 0), fn, data);
/* template parm nodes */
case TEMPLATE_TEMPLATE_PARM:
/* Record template parameters such as `T' inside `TT<T>'. */
if (TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (t)
&& for_each_template_parm (TYPE_TI_ARGS (t), fn, data))
return 1;
case TEMPLATE_TYPE_PARM:
case TEMPLATE_PARM_INDEX:
if (fn)
return (*fn)(t, data);
else
return 1;
/* simple type nodes */
case INTEGER_TYPE:
if (for_each_template_parm (TYPE_MIN_VALUE (t), fn, data))
return 1;
return for_each_template_parm (TYPE_MAX_VALUE (t), fn, data);
case REAL_TYPE:
case COMPLEX_TYPE:
case VOID_TYPE:
case BOOLEAN_TYPE:
case NAMESPACE_DECL:
return 0;
/* constants */
case INTEGER_CST:
case REAL_CST:
case STRING_CST:
return 0;
case ERROR_MARK:
/* Non-error_mark_node ERROR_MARKs are bad things. */
my_friendly_assert (t == error_mark_node, 274);
/* NOTREACHED */
return 0;
case LOOKUP_EXPR:
case TYPENAME_TYPE:
return 1;
case PTRMEM_CST:
return for_each_template_parm (TREE_TYPE (t), fn, data);
case SCOPE_REF:
return for_each_template_parm (TREE_OPERAND (t, 0), fn, data);
case CONSTRUCTOR:
if (TREE_TYPE (t) && TYPE_PTRMEMFUNC_P (TREE_TYPE (t)))
return for_each_template_parm (TYPE_PTRMEMFUNC_FN_TYPE
(TREE_TYPE (t)), fn, data);
return for_each_template_parm (TREE_OPERAND (t, 1), fn, data);
case MODOP_EXPR:
case CAST_EXPR:
case REINTERPRET_CAST_EXPR:
case CONST_CAST_EXPR:
case STATIC_CAST_EXPR:
case DYNAMIC_CAST_EXPR:
case ARROW_EXPR:
case DOTSTAR_EXPR:
case TYPEID_EXPR:
return 1;
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
return for_each_template_parm (TREE_OPERAND (t, 0), fn, data);
default:
switch (TREE_CODE_CLASS (TREE_CODE (t)))
{
case '1':
case '2':
case 'e':
case '<':
{
int i;
for (i = first_rtl_op (TREE_CODE (t)); --i >= 0;)
if (for_each_template_parm (TREE_OPERAND (t, i), fn, data))
return 1;
return 0;
}
default:
break;
}
sorry ("testing %s for template parms",
tree_code_name [(int) TREE_CODE (t)]);
my_friendly_abort (82);
/* NOTREACHED */
return 0;
}
}
int
uses_template_parms (t)
tree t;
{
return for_each_template_parm (t, 0, 0);
}
static struct tinst_level *current_tinst_level;
static struct tinst_level *free_tinst_level;
static int tinst_depth;
extern int max_tinst_depth;
#ifdef GATHER_STATISTICS
int depth_reached;
#endif
int tinst_level_tick;
int last_template_error_tick;
/* Print out all the template instantiations that we are currently
working on. If ERR, we are being called from cp_thing, so do
the right thing for an error message. */
static void
print_template_context (err)
int err;
{
struct tinst_level *p = current_tinst_level;
int line = lineno;
char *file = input_filename;
if (err && p)
{
if (current_function_decl != p->decl
&& current_function_decl != NULL_TREE)
/* We can get here during the processing of some synthesized
method. Then, p->decl will be the function that's causing
the synthesis. */
;
else
{
if (current_function_decl == p->decl)
/* Avoid redundancy with the the "In function" line. */;
else
fprintf (stderr, "%s: In instantiation of `%s':\n",
file, decl_as_string (p->decl, 0));
line = p->line;
file = p->file;
p = p->next;
}
}
for (; p; p = p->next)
{
fprintf (stderr, "%s:%d: instantiated from `%s'\n", file, line,
decl_as_string (p->decl, 0));
line = p->line;
file = p->file;
}
fprintf (stderr, "%s:%d: instantiated from here\n", file, line);
}
/* Called from cp_thing to print the template context for an error. */
void
maybe_print_template_context ()
{
if (last_template_error_tick == tinst_level_tick
|| current_tinst_level == 0)
return;
last_template_error_tick = tinst_level_tick;
print_template_context (1);
}
static int
push_tinst_level (d)
tree d;
{
struct tinst_level *new;
if (tinst_depth >= max_tinst_depth)
{
/* If the instantiation in question still has unbound template parms,
we don't really care if we can't instantiate it, so just return.
This happens with base instantiation for implicit `typename'. */
if (uses_template_parms (d))
return 0;
last_template_error_tick = tinst_level_tick;
error ("template instantiation depth exceeds maximum of %d",
max_tinst_depth);
error (" (use -ftemplate-depth-NN to increase the maximum)");
cp_error (" instantiating `%D'", d);
print_template_context (0);
return 0;
}
if (free_tinst_level)
{
new = free_tinst_level;
free_tinst_level = new->next;
}
else
new = (struct tinst_level *) xmalloc (sizeof (struct tinst_level));
new->decl = d;
new->line = lineno;
new->file = input_filename;
new->next = current_tinst_level;
current_tinst_level = new;
++tinst_depth;
#ifdef GATHER_STATISTICS
if (tinst_depth > depth_reached)
depth_reached = tinst_depth;
#endif
++tinst_level_tick;
return 1;
}
void
pop_tinst_level ()
{
struct tinst_level *old = current_tinst_level;
/* Restore the filename and line number stashed away when we started
this instantiation. */
lineno = old->line;
input_filename = old->file;
extract_interface_info ();
current_tinst_level = old->next;
old->next = free_tinst_level;
free_tinst_level = old;
--tinst_depth;
++tinst_level_tick;
}
struct tinst_level *
tinst_for_decl ()
{
struct tinst_level *p = current_tinst_level;
if (p)
for (; p->next ; p = p->next )
;
return p;
}
/* DECL is a friend FUNCTION_DECL or TEMPLATE_DECL. ARGS is the
vector of template arguments, as for tsubst.
Returns an appropriate tsbust'd friend declaration. */
static tree
tsubst_friend_function (decl, args)
tree decl;
tree args;
{
tree new_friend;
int line = lineno;
char *file = input_filename;
lineno = DECL_SOURCE_LINE (decl);
input_filename = DECL_SOURCE_FILE (decl);
if (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_TEMPLATE_INSTANTIATION (decl)
&& TREE_CODE (DECL_TI_TEMPLATE (decl)) != TEMPLATE_DECL)
/* This was a friend declared with an explicit template
argument list, e.g.:
friend void f<>(T);
to indicate that f was a template instantiation, not a new
function declaration. Now, we have to figure out what
instantiation of what template. */
{
tree template_id;
tree new_args;
tree tmpl;
template_id
= lookup_template_function (tsubst_expr (DECL_TI_TEMPLATE (decl),
args, /*complain=*/1,
NULL_TREE),
tsubst (DECL_TI_ARGS (decl),
args, /*complain=*/1,
NULL_TREE));
/* FIXME: The decl we create via the next tsubst could be
created on a temporary obstack. */
new_friend = tsubst (decl, args, /*complain=*/1, NULL_TREE);
tmpl = determine_specialization (template_id, new_friend,
&new_args,
/*need_member_template=*/0);
new_friend = instantiate_template (tmpl, new_args);
goto done;
}
new_friend = tsubst (decl, args, /*complain=*/1, NULL_TREE);
/* The NEW_FRIEND will look like an instantiation, to the
compiler, but is not an instantiation from the point of view of
the language. For example, we might have had:
template <class T> struct S {
template <class U> friend void f(T, U);
};
Then, in S<int>, template <class U> void f(int, U) is not an
instantiation of anything. */
DECL_USE_TEMPLATE (new_friend) = 0;
if (TREE_CODE (decl) == TEMPLATE_DECL)
DECL_USE_TEMPLATE (DECL_TEMPLATE_RESULT (new_friend)) = 0;
/* The mangled name for the NEW_FRIEND is incorrect. The call to
tsubst will have resulted in a call to
set_mangled_name_for_template_decl. But, the function is not a
template instantiation and should not be mangled like one.
Therefore, we remangle the function name. We don't have to do
this if the NEW_FRIEND is a template since
set_mangled_name_for_template_decl doesn't do anything if the
function declaration still uses template arguments. */
if (TREE_CODE (new_friend) != TEMPLATE_DECL)
{
set_mangled_name_for_decl (new_friend);
DECL_RTL (new_friend) = 0;
make_decl_rtl (new_friend, NULL_PTR, 1);
}
if (DECL_NAMESPACE_SCOPE_P (new_friend))
{
tree old_decl;
tree new_friend_template_info;
tree new_friend_result_template_info;
int new_friend_is_defn;
/* We must save some information from NEW_FRIEND before calling
duplicate decls since that function will free NEW_FRIEND if
possible. */
new_friend_template_info = DECL_TEMPLATE_INFO (new_friend);
if (TREE_CODE (new_friend) == TEMPLATE_DECL)
{
/* This declaration is a `primary' template. */
DECL_PRIMARY_TEMPLATE (new_friend) = new_friend;
new_friend_is_defn
= DECL_INITIAL (DECL_RESULT (new_friend)) != NULL_TREE;
new_friend_result_template_info
= DECL_TEMPLATE_INFO (DECL_RESULT (new_friend));
}
else
{
new_friend_is_defn = DECL_INITIAL (new_friend) != NULL_TREE;
new_friend_result_template_info = NULL_TREE;
}
old_decl = pushdecl_namespace_level (new_friend);
if (old_decl != new_friend)
{
/* This new friend declaration matched an existing
declaration. For example, given:
template <class T> void f(T);
template <class U> class C {
template <class T> friend void f(T) {}
};
the friend declaration actually provides the definition
of `f', once C has been instantiated for some type. So,
old_decl will be the out-of-class template declaration,
while new_friend is the in-class definition.
But, if `f' was called before this point, the
instantiation of `f' will have DECL_TI_ARGS corresponding
to `T' but not to `U', references to which might appear
in the definition of `f'. Previously, the most general
template for an instantiation of `f' was the out-of-class
version; now it is the in-class version. Therefore, we
run through all specialization of `f', adding to their
DECL_TI_ARGS appropriately. In particular, they need a
new set of outer arguments, corresponding to the
arguments for this class instantiation.
The same situation can arise with something like this:
friend void f(int);
template <class T> class C {
friend void f(T) {}
};
when `C<int>' is instantiated. Now, `f(int)' is defined
in the class. */
if (!new_friend_is_defn)
/* On the other hand, if the in-class declaration does
*not* provide a definition, then we don't want to alter
existing definitions. We can just leave everything
alone. */
;
else
{
/* Overwrite whatever template info was there before, if
any, with the new template information pertaining to
the declaration. */
DECL_TEMPLATE_INFO (old_decl) = new_friend_template_info;
if (TREE_CODE (old_decl) != TEMPLATE_DECL)
/* duplicate_decls will take care of this case. */
;
else
{
tree t;
tree new_friend_args;
DECL_TEMPLATE_INFO (DECL_RESULT (old_decl))
= new_friend_result_template_info;
new_friend_args = TI_ARGS (new_friend_template_info);
for (t = DECL_TEMPLATE_SPECIALIZATIONS (old_decl);
t != NULL_TREE;
t = TREE_CHAIN (t))
{
tree spec = TREE_VALUE (t);
DECL_TI_ARGS (spec)
= add_outermost_template_args (new_friend_args,
DECL_TI_ARGS (spec));
DECL_TI_ARGS (spec)
= copy_to_permanent (DECL_TI_ARGS (spec));
}
/* Now, since specializations are always supposed to
hang off of the most general template, we must move
them. */
t = most_general_template (old_decl);
if (t != old_decl)
{
DECL_TEMPLATE_SPECIALIZATIONS (t)
= chainon (DECL_TEMPLATE_SPECIALIZATIONS (t),
DECL_TEMPLATE_SPECIALIZATIONS (old_decl));
DECL_TEMPLATE_SPECIALIZATIONS (old_decl) = NULL_TREE;
}
}
}
/* The information from NEW_FRIEND has been merged into OLD_DECL
by duplicate_decls. */
new_friend = old_decl;
}
}
else if (TYPE_SIZE (DECL_CONTEXT (new_friend)))
{
/* Check to see that the declaration is really present, and,
possibly obtain an improved declaration. */
tree fn = check_classfn (DECL_CONTEXT (new_friend),
new_friend);
if (fn)
new_friend = fn;
}
done:
lineno = line;
input_filename = file;
return new_friend;
}
/* FRIEND_TMPL is a friend TEMPLATE_DECL. ARGS is the vector of
template arguments, as for tsubst.
Returns an appropriate tsbust'd friend type. */
static tree
tsubst_friend_class (friend_tmpl, args)
tree friend_tmpl;
tree args;
{
tree friend_type;
tree tmpl;
/* First, we look for a class template. */
tmpl = lookup_name (DECL_NAME (friend_tmpl), /*prefer_type=*/0);
/* But, if we don't find one, it might be because we're in a
situation like this:
template <class T>
struct S {
template <class U>
friend struct S;
};
Here, in the scope of (say) S<int>, `S' is bound to a TYPE_DECL
for `S<int>', not the TEMPLATE_DECL. */
if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl))
{
tmpl = lookup_name (DECL_NAME (friend_tmpl), /*prefer_type=*/1);
tmpl = maybe_get_template_decl_from_type_decl (tmpl);
}
if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl))
{
/* The friend template has already been declared. Just
check to see that the declarations match, and install any new
default parameters. We must tsubst the default parameters,
of course. We only need the innermost template parameters
because that is all that redeclare_class_template will look
at. */
tree parms
= tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
args, /*complain=*/1);
redeclare_class_template (TREE_TYPE (tmpl), parms);
friend_type = TREE_TYPE (tmpl);
}
else
{
/* The friend template has not already been declared. In this
case, the instantiation of the template class will cause the
injection of this template into the global scope. */
tmpl = tsubst (friend_tmpl, args, /*complain=*/1, NULL_TREE);
/* The new TMPL is not an instantiation of anything, so we
forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE for
the new type because that is supposed to be the corresponding
template decl, i.e., TMPL. */
DECL_USE_TEMPLATE (tmpl) = 0;
DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
/* Inject this template into the global scope. */
friend_type = TREE_TYPE (pushdecl_top_level (tmpl));
}
return friend_type;
}
tree
instantiate_class_template (type)
tree type;
{
tree template, args, pattern, t;
tree typedecl;
if (type == error_mark_node)
return error_mark_node;
if (TYPE_BEING_DEFINED (type) || TYPE_SIZE (type))
return type;
/* We want to allocate temporary vectors of template arguments and
template argument expressions on the momentary obstack, not on
the expression obstack. Otherwise, all the space allocated in
argument coercion and such is simply lost. */
push_momentary ();
/* Figure out which template is being instantiated. */
template = most_general_template (CLASSTYPE_TI_TEMPLATE (type));
my_friendly_assert (TREE_CODE (template) == TEMPLATE_DECL, 279);
/* Figure out which arguments are being used to do the
instantiation. */
args = CLASSTYPE_TI_ARGS (type);
PARTIAL_INSTANTIATION_P (type) = uses_template_parms (args);
if (pedantic && PARTIAL_INSTANTIATION_P (type))
/* If this is a partial instantiation, then we can't instantiate
the type; there's no telling whether or not one of the
template parameters might eventually be instantiated to some
value that results in a specialization being used. For
example, consider:
template <class T>
struct S {};
template <class U>
void f(S<U>);
template <>
struct S<int> {};
Now, the `S<U>' in `f<int>' is the specialization, not an
instantiation of the original template. */
goto end;
/* Determine what specialization of the original template to
instantiate. */
if (PARTIAL_INSTANTIATION_P (type))
/* There's no telling which specialization is appropriate at this
point. Since all peeking at the innards of this partial
instantiation are extensions (like the "implicit typename"
extension, which allows users to omit the keyword `typename' on
names that are declared as types in template base classes), we
are free to do what we please.
Trying to figure out which partial instantiation to use can
cause a crash. (Some of the template arguments don't even have
types.) So, we just use the most general version. */
t = NULL_TREE;
else
{
t = most_specialized_class (template, args);
if (t == error_mark_node)
{
const char *str = "candidates are:";
cp_error ("ambiguous class template instantiation for `%#T'", type);
for (t = DECL_TEMPLATE_SPECIALIZATIONS (template); t;
t = TREE_CHAIN (t))
{
if (get_class_bindings (TREE_VALUE (t), TREE_PURPOSE (t),
args))
{
cp_error_at ("%s %+#T", str, TREE_TYPE (t));
str = " ";
}
}
TYPE_BEING_DEFINED (type) = 1;
type = error_mark_node;
goto end;
}
}
if (t)
pattern = TREE_TYPE (t);
else
pattern = TREE_TYPE (template);
/* If the template we're instantiating is incomplete, then clearly
there's nothing we can do. */
if (TYPE_SIZE (pattern) == NULL_TREE)
goto end;
/* If this is a partial instantiation, don't tsubst anything. We will
only use this type for implicit typename, so the actual contents don't
matter. All that matters is whether a particular name is a type. */
if (PARTIAL_INSTANTIATION_P (type))
{
/* The fields set here must be kept in sync with those cleared
in begin_class_definition. */
TYPE_BINFO_BASETYPES (type) = TYPE_BINFO_BASETYPES (pattern);
TYPE_FIELDS (type) = TYPE_FIELDS (pattern);
TYPE_METHODS (type) = TYPE_METHODS (pattern);
CLASSTYPE_TAGS (type) = CLASSTYPE_TAGS (pattern);
/* Pretend that the type is complete, so that we will look
inside it during name lookup and such. */
TYPE_SIZE (type) = integer_zero_node;
goto end;
}
/* If we've recursively instantiated too many templates, stop. */
if (! push_tinst_level (type))
goto end;
/* Now we're really doing the instantiation. Mark the type as in
the process of being defined. */
TYPE_BEING_DEFINED (type) = 1;
maybe_push_to_top_level (uses_template_parms (type));
if (t)
{
/* This TYPE is actually a instantiation of of a partial
specialization. We replace the innermost set of ARGS with
the arguments appropriate for substitution. For example,
given:
template <class T> struct S {};
template <class T> struct S<T*> {};
and supposing that we are instantiating S<int*>, ARGS will
present be {int*} but we need {int}. */
tree inner_args
= get_class_bindings (TREE_VALUE (t), TREE_PURPOSE (t),
args);
/* If there were multiple levels in ARGS, replacing the
innermost level would alter CLASSTYPE_TI_ARGS, which we don't
want, so we make a copy first. */
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
{
args = copy_node (args);
SET_TMPL_ARGS_LEVEL (args, TMPL_ARGS_DEPTH (args), inner_args);
}
else
args = inner_args;
}
if (flag_external_templates)
{
if (flag_alt_external_templates)
{
CLASSTYPE_INTERFACE_ONLY (type) = interface_only;
SET_CLASSTYPE_INTERFACE_UNKNOWN_X (type, interface_unknown);
CLASSTYPE_VTABLE_NEEDS_WRITING (type)
= (! CLASSTYPE_INTERFACE_ONLY (type)
&& CLASSTYPE_INTERFACE_KNOWN (type));
}
else
{
CLASSTYPE_INTERFACE_ONLY (type) = CLASSTYPE_INTERFACE_ONLY (pattern);
SET_CLASSTYPE_INTERFACE_UNKNOWN_X
(type, CLASSTYPE_INTERFACE_UNKNOWN (pattern));
CLASSTYPE_VTABLE_NEEDS_WRITING (type)
= (! CLASSTYPE_INTERFACE_ONLY (type)
&& CLASSTYPE_INTERFACE_KNOWN (type));
}
}
else
{
SET_CLASSTYPE_INTERFACE_UNKNOWN (type);
CLASSTYPE_VTABLE_NEEDS_WRITING (type) = 1;
}
TYPE_HAS_CONSTRUCTOR (type) = TYPE_HAS_CONSTRUCTOR (pattern);
TYPE_HAS_DESTRUCTOR (type) = TYPE_HAS_DESTRUCTOR (pattern);
TYPE_OVERLOADS_CALL_EXPR (type) = TYPE_OVERLOADS_CALL_EXPR (pattern);
TYPE_OVERLOADS_ARRAY_REF (type) = TYPE_OVERLOADS_ARRAY_REF (pattern);
TYPE_OVERLOADS_ARROW (type) = TYPE_OVERLOADS_ARROW (pattern);
TYPE_GETS_NEW (type) = TYPE_GETS_NEW (pattern);
TYPE_GETS_DELETE (type) = TYPE_GETS_DELETE (pattern);
TYPE_VEC_DELETE_TAKES_SIZE (type) = TYPE_VEC_DELETE_TAKES_SIZE (pattern);
TYPE_HAS_ASSIGN_REF (type) = TYPE_HAS_ASSIGN_REF (pattern);
TYPE_HAS_CONST_ASSIGN_REF (type) = TYPE_HAS_CONST_ASSIGN_REF (pattern);
TYPE_HAS_ABSTRACT_ASSIGN_REF (type) = TYPE_HAS_ABSTRACT_ASSIGN_REF (pattern);
TYPE_HAS_INIT_REF (type) = TYPE_HAS_INIT_REF (pattern);
TYPE_HAS_CONST_INIT_REF (type) = TYPE_HAS_CONST_INIT_REF (pattern);
TYPE_HAS_DEFAULT_CONSTRUCTOR (type) = TYPE_HAS_DEFAULT_CONSTRUCTOR (pattern);
TYPE_HAS_CONVERSION (type) = TYPE_HAS_CONVERSION (pattern);
TYPE_USES_COMPLEX_INHERITANCE (type)
= TYPE_USES_COMPLEX_INHERITANCE (pattern);
TYPE_USES_MULTIPLE_INHERITANCE (type)
= TYPE_USES_MULTIPLE_INHERITANCE (pattern);
TYPE_USES_VIRTUAL_BASECLASSES (type)
= TYPE_USES_VIRTUAL_BASECLASSES (pattern);
TYPE_PACKED (type) = TYPE_PACKED (pattern);
TYPE_ALIGN (type) = TYPE_ALIGN (pattern);
TYPE_FOR_JAVA (type) = TYPE_FOR_JAVA (pattern); /* For libjava's JArray<T> */
if (ANON_UNION_TYPE_P (pattern))
SET_ANON_UNION_TYPE_P (type);
/* We must copy the arguments to the permanent obstack since
during the tsubst'ing below they may wind up in the
DECL_TI_ARGS of some instantiated member template. */
args = copy_to_permanent (args);
if (TYPE_BINFO_BASETYPES (pattern))
{
tree base_list = NULL_TREE;
tree pbases = TYPE_BINFO_BASETYPES (pattern);
int i;
/* Substitute into each of the bases to determine the actual
basetypes. */
for (i = 0; i < TREE_VEC_LENGTH (pbases); ++i)
{
tree base;
tree access;
tree pbase;
pbase = TREE_VEC_ELT (pbases, i);
/* Substitue to figure out the base class. */
base = tsubst (BINFO_TYPE (pbase), args,
/*complain=*/1, NULL_TREE);
if (base == error_mark_node)
continue;
/* Calculate the correct access node. */
if (TREE_VIA_VIRTUAL (pbase))
{
if (TREE_VIA_PUBLIC (pbase))
access = access_public_virtual_node;
else if (TREE_VIA_PROTECTED (pbase))
access = access_protected_virtual_node;
else
access = access_private_virtual_node;
}
else
{
if (TREE_VIA_PUBLIC (pbase))
access = access_public_node;
else if (TREE_VIA_PROTECTED (pbase))
access = access_protected_node;
else
access = access_private_node;
}
base_list = tree_cons (access, base, base_list);
}
/* The list is now in reverse order; correct that. */
base_list = nreverse (base_list);
/* Now call xref_basetypes to set up all the base-class
information. */
xref_basetypes (TREE_CODE (pattern) == RECORD_TYPE
? (CLASSTYPE_DECLARED_CLASS (pattern)
? class_type_node : record_type_node)
: union_type_node,
DECL_NAME (TYPE_NAME (pattern)),
type,
base_list);
}
/* Now that our base classes are set up, enter the scope of the
class, so that name lookups into base classes, etc. will work
corectly. This is precisely analagous to what we do in
begin_class_definition when defining an ordinary non-template
class. */
pushclass (type, 1);
for (t = CLASSTYPE_TAGS (pattern); t; t = TREE_CHAIN (t))
{
tree tag = TREE_VALUE (t);
tree name = TYPE_IDENTIFIER (tag);
tree newtag;
newtag = tsubst (tag, args, /*complain=*/1, NULL_TREE);
if (TREE_CODE (newtag) != ENUMERAL_TYPE)
{
if (TYPE_LANG_SPECIFIC (tag) && CLASSTYPE_IS_TEMPLATE (tag))
/* Unfortunately, lookup_template_class sets
CLASSTYPE_IMPLICIT_INSTANTIATION for a partial
instantiation (i.e., for the type of a member template
class nested within a template class.) This behavior is
required for maybe_process_partial_specialization to work
correctly, but is not accurate in this case; the TAG is not
an instantiation of anything. (The corresponding
TEMPLATE_DECL is an instantiation, but the TYPE is not.) */
CLASSTYPE_USE_TEMPLATE (newtag) = 0;
/* Now, we call pushtag to put this NEWTAG into the scope of
TYPE. We first set up the IDENTIFIER_TYPE_VALUE to avoid
pushtag calling push_template_decl. We don't have to do
this for enums because it will already have been done in
tsubst_enum. */
if (name)
SET_IDENTIFIER_TYPE_VALUE (name, newtag);
pushtag (name, newtag, /*globalize=*/0);
}
}
/* Don't replace enum constants here. */
for (t = TYPE_FIELDS (pattern); t; t = TREE_CHAIN (t))
if (TREE_CODE (t) != CONST_DECL)
{
tree r;
/* The the file and line for this declaration, to assist in
error message reporting. Since we called push_tinst_level
above, we don't need to restore these. */
lineno = DECL_SOURCE_LINE (t);
input_filename = DECL_SOURCE_FILE (t);
r = tsubst (t, args, /*complain=*/1, NULL_TREE);
if (TREE_CODE (r) == VAR_DECL)
{
tree init;
if (DECL_DEFINED_IN_CLASS_P (r))
init = tsubst_expr (DECL_INITIAL (t), args,
/*complain=*/1, NULL_TREE);
else
init = NULL_TREE;
finish_static_data_member_decl (r, init,
/*asmspec_tree=*/NULL_TREE,
/*need_pop=*/0,
/*flags=*/0);
if (DECL_DEFINED_IN_CLASS_P (r))
check_static_variable_definition (r, TREE_TYPE (r));
}
/* R will have a TREE_CHAIN if and only if it has already been
processed by finish_member_declaration. This can happen
if, for example, it is a TYPE_DECL for a class-scoped
ENUMERAL_TYPE; such a thing will already have been added to
the field list by tsubst_enum above. */
if (!TREE_CHAIN (r))
{
set_current_access_from_decl (r);
finish_member_declaration (r);
}
}
/* Set up the list (TYPE_METHODS) and vector (CLASSTYPE_METHOD_VEC)
for this instantiation. */
for (t = TYPE_METHODS (pattern); t; t = TREE_CHAIN (t))
{
tree r = tsubst (t, args, /*complain=*/1, NULL_TREE);
set_current_access_from_decl (r);
finish_member_declaration (r);
}
/* Construct the DECL_FRIENDLIST for the new class type. */
typedecl = TYPE_MAIN_DECL (type);
for (t = DECL_FRIENDLIST (TYPE_MAIN_DECL (pattern));
t != NULL_TREE;
t = TREE_CHAIN (t))
{
tree friends;
for (friends = TREE_VALUE (t);
friends != NULL_TREE;
friends = TREE_CHAIN (friends))
if (TREE_PURPOSE (friends) == error_mark_node)
add_friend (type,
tsubst_friend_function (TREE_VALUE (friends),
args));
else
add_friends (type,
tsubst_copy (TREE_PURPOSE (t), args,
/*complain=*/1, NULL_TREE),
tsubst (TREE_PURPOSE (friends), args,
/*complain=*/1, NULL_TREE));
}
for (t = CLASSTYPE_FRIEND_CLASSES (pattern);
t != NULL_TREE;
t = TREE_CHAIN (t))
{
tree friend_type = TREE_VALUE (t);
tree new_friend_type;
if (TREE_CODE (friend_type) == TEMPLATE_DECL)
new_friend_type = tsubst_friend_class (friend_type, args);
else if (uses_template_parms (friend_type))
new_friend_type = tsubst (friend_type, args, /*complain=*/1,
NULL_TREE);
else
/* The call to xref_tag_from_type does injection for friend
classes. */
new_friend_type =
xref_tag_from_type (friend_type, NULL_TREE, 1);
if (TREE_CODE (friend_type) == TEMPLATE_DECL)
/* Trick make_friend_class into realizing that the friend
we're adding is a template, not an ordinary class. It's
important that we use make_friend_class since it will
perform some error-checking and output cross-reference
information. */
++processing_template_decl;
make_friend_class (type, new_friend_type);
if (TREE_CODE (friend_type) == TEMPLATE_DECL)
--processing_template_decl;
}
/* This does injection for friend functions. */
if (!processing_template_decl)
{
t = tsubst (DECL_TEMPLATE_INJECT (template), args,
/*complain=*/1, NULL_TREE);
for (; t; t = TREE_CHAIN (t))
{
tree d = TREE_VALUE (t);
if (TREE_CODE (d) == TYPE_DECL)
/* Already injected. */;
else
pushdecl (d);
}
}
for (t = TYPE_FIELDS (type); t; t = TREE_CHAIN (t))
if (TREE_CODE (t) == FIELD_DECL)
{
TREE_TYPE (t) = complete_type (TREE_TYPE (t));
require_complete_type (t);
}
/* Set the file and line number information to whatever is given for
the class itself. This puts error messages involving generated
implicit functions at a predictable point, and the same point
that would be used for non-template classes. */
lineno = DECL_SOURCE_LINE (typedecl);
input_filename = DECL_SOURCE_FILE (typedecl);
unreverse_member_declarations (type);
finish_struct_1 (type, 0);
CLASSTYPE_GOT_SEMICOLON (type) = 1;
/* Clear this now so repo_template_used is happy. */
TYPE_BEING_DEFINED (type) = 0;
repo_template_used (type);
popclass ();
pop_from_top_level ();
pop_tinst_level ();
end:
pop_momentary ();
return type;
}
static int
list_eq (t1, t2)
tree t1, t2;
{
if (t1 == NULL_TREE)
return t2 == NULL_TREE;
if (t2 == NULL_TREE)
return 0;
/* Don't care if one declares its arg const and the other doesn't -- the
main variant of the arg type is all that matters. */
if (TYPE_MAIN_VARIANT (TREE_VALUE (t1))
!= TYPE_MAIN_VARIANT (TREE_VALUE (t2)))
return 0;
return list_eq (TREE_CHAIN (t1), TREE_CHAIN (t2));
}
/* If arg is a non-type template parameter that does not depend on template
arguments, fold it like we weren't in the body of a template. */
static tree
maybe_fold_nontype_arg (arg)
tree arg;
{
if (TREE_CODE_CLASS (TREE_CODE (arg)) != 't'
&& !uses_template_parms (arg))
{
/* Sometimes, one of the args was an expression involving a
template constant parameter, like N - 1. Now that we've
tsubst'd, we might have something like 2 - 1. This will
confuse lookup_template_class, so we do constant folding
here. We have to unset processing_template_decl, to
fool build_expr_from_tree() into building an actual
tree. */
int saved_processing_template_decl = processing_template_decl;
processing_template_decl = 0;
arg = fold (build_expr_from_tree (arg));
processing_template_decl = saved_processing_template_decl;
}
return arg;
}
/* Return the TREE_VEC with the arguments for the innermost template header,
where ARGS is either that or the VEC of VECs for all the
arguments. */
tree
innermost_args (args)
tree args;
{
return TMPL_ARGS_LEVEL (args, TMPL_ARGS_DEPTH (args));
}
/* Substitute ARGS into the vector of template arguments T. */
static tree
tsubst_template_arg_vector (t, args, complain)
tree t;
tree args;
int complain;
{
int len = TREE_VEC_LENGTH (t), need_new = 0, i;
tree *elts = (tree *) alloca (len * sizeof (tree));
bzero ((char *) elts, len * sizeof (tree));
for (i = 0; i < len; i++)
{
if (TREE_VEC_ELT (t, i) != NULL_TREE
&& TREE_CODE (TREE_VEC_ELT (t, i)) == TREE_VEC)
elts[i] = tsubst_template_arg_vector (TREE_VEC_ELT (t, i),
args, complain);
else
elts[i] = maybe_fold_nontype_arg
(tsubst_expr (TREE_VEC_ELT (t, i), args, complain,
NULL_TREE));
if (elts[i] != TREE_VEC_ELT (t, i))
need_new = 1;
}
if (!need_new)
return t;
t = make_temp_vec (len);
for (i = 0; i < len; i++)
TREE_VEC_ELT (t, i) = elts[i];
return t;
}
/* Return the result of substituting ARGS into the template parameters
given by PARMS. If there are m levels of ARGS and m + n levels of
PARMS, then the result will contain n levels of PARMS. For
example, if PARMS is `template <class T> template <class U>
template <T*, U, class V>' and ARGS is {{int}, {double}} then the
result will be `template <int*, double, class V>'. */
static tree
tsubst_template_parms (parms, args, complain)
tree parms;
tree args;
int complain;
{
tree r;
tree* new_parms = &r;
for (new_parms = &r;
TMPL_PARMS_DEPTH (parms) > TMPL_ARGS_DEPTH (args);
new_parms = &(TREE_CHAIN (*new_parms)),
parms = TREE_CHAIN (parms))
{
tree new_vec =
make_tree_vec (TREE_VEC_LENGTH (TREE_VALUE (parms)));
int i;
for (i = 0; i < TREE_VEC_LENGTH (new_vec); ++i)
{
tree default_value =
TREE_PURPOSE (TREE_VEC_ELT (TREE_VALUE (parms), i));
tree parm_decl =
TREE_VALUE (TREE_VEC_ELT (TREE_VALUE (parms), i));
TREE_VEC_ELT (new_vec, i)
= build_tree_list (tsubst (default_value, args, complain,
NULL_TREE),
tsubst (parm_decl, args, complain,
NULL_TREE));
}
*new_parms =
tree_cons (build_int_2 (0, (TMPL_PARMS_DEPTH (parms)
- TMPL_ARGS_DEPTH (args))),
new_vec, NULL_TREE);
}
return r;
}
/* Substitute the ARGS into the indicated aggregate (or enumeration)
type T. If T is not an aggregate or enumeration type, it is
handled as if by tsubst. IN_DECL is as for tsubst. If
ENTERING_SCOPE is non-zero, T is the context for a template which
we are presently tsubst'ing. Return the subsituted value. */
static tree
tsubst_aggr_type (t, args, complain, in_decl, entering_scope)
tree t;
tree args;
int complain;
tree in_decl;
int entering_scope;
{
if (t == NULL_TREE)
return NULL_TREE;
switch (TREE_CODE (t))
{
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_P (t))
{
tree r = build_ptrmemfunc_type
(tsubst (TYPE_PTRMEMFUNC_FN_TYPE (t), args, complain, in_decl));
return cp_build_qualified_type (r, TYPE_QUALS (t));
}
/* else fall through */
case ENUMERAL_TYPE:
case UNION_TYPE:
if (TYPE_TEMPLATE_INFO (t))
{
tree argvec;
tree context;
tree r;
/* First, determine the context for the type we are looking
up. */
if (TYPE_CONTEXT (t) != NULL_TREE)
context = tsubst_aggr_type (TYPE_CONTEXT (t), args,
complain,
in_decl, /*entering_scope=*/1);
else
context = NULL_TREE;
/* Then, figure out what arguments are appropriate for the
type we are trying to find. For example, given:
template <class T> struct S;
template <class T, class U> void f(T, U) { S<U> su; }
and supposing that we are instantiating f<int, double>,
then our ARGS will be {int, double}, but, when looking up
S we only want {double}. */
push_momentary ();
argvec = tsubst_template_arg_vector (TYPE_TI_ARGS (t), args,
complain);
r = lookup_template_class (t, argvec, in_decl, context,
entering_scope);
pop_momentary ();
return cp_build_qualified_type (r, TYPE_QUALS (t));
}
else
/* This is not a template type, so there's nothing to do. */
return t;
default:
return tsubst (t, args, complain, in_decl);
}
}
/* Substitute the ARGS into the T, which is a _DECL. TYPE is the
(already computed) substitution of ARGS into TREE_TYPE (T), if
appropriate. Return the result of the substitution. IN_DECL is as
for tsubst. */
static tree
tsubst_decl (t, args, type, in_decl)
tree t;
tree args;
tree type;
tree in_decl;
{
int saved_lineno;
char* saved_filename;
tree r = NULL_TREE;
/* Set the filename and linenumber to improve error-reporting. */
saved_lineno = lineno;
saved_filename = input_filename;
lineno = DECL_SOURCE_LINE (t);
input_filename = DECL_SOURCE_FILE (t);
switch (TREE_CODE (t))
{
case TEMPLATE_DECL:
{
/* We can get here when processing a member template function
of a template class. */
tree decl = DECL_TEMPLATE_RESULT (t);
tree spec;
int is_template_template_parm = DECL_TEMPLATE_TEMPLATE_PARM_P (t);
if (!is_template_template_parm)
{
/* We might already have an instance of this template.
The ARGS are for the surrounding class type, so the
full args contain the tsubst'd args for the context,
plus the innermost args from the template decl. */
tree tmpl_args = DECL_CLASS_TEMPLATE_P (t)
? CLASSTYPE_TI_ARGS (TREE_TYPE (t))
: DECL_TI_ARGS (DECL_RESULT (t));
tree full_args;
push_momentary ();
full_args = tsubst_template_arg_vector (tmpl_args, args,
/*complain=*/1);
/* tsubst_template_arg_vector doesn't copy the vector if
nothing changed. But, *something* should have
changed. */
my_friendly_assert (full_args != tmpl_args, 0);
spec = retrieve_specialization (t, full_args);
pop_momentary ();
if (spec != NULL_TREE)
{
r = spec;
break;
}
}
/* Make a new template decl. It will be similar to the
original, but will record the current template arguments.
We also create a new function declaration, which is just
like the old one, but points to this new template, rather
than the old one. */
r = copy_node (t);
copy_lang_decl (r);
my_friendly_assert (DECL_LANG_SPECIFIC (r) != 0, 0);
TREE_CHAIN (r) = NULL_TREE;
if (is_template_template_parm)
{
tree new_decl = tsubst (decl, args, /*complain=*/1, in_decl);
DECL_RESULT (r) = new_decl;
TREE_TYPE (r) = TREE_TYPE (new_decl);
break;
}
DECL_CONTEXT (r)
= tsubst_aggr_type (DECL_CONTEXT (t), args, /*complain=*/1,
in_decl, /*entering_scope=*/1);
DECL_CLASS_CONTEXT (r)
= tsubst_aggr_type (DECL_CLASS_CONTEXT (t), args,
/*complain=*/1, in_decl,
/*entering_scope=*/1);
DECL_TEMPLATE_INFO (r) = build_tree_list (t, args);
if (TREE_CODE (decl) == TYPE_DECL)
{
tree new_type = tsubst (TREE_TYPE (t), args,
/*complain=*/1, in_decl);
TREE_TYPE (r) = new_type;
CLASSTYPE_TI_TEMPLATE (new_type) = r;
DECL_RESULT (r) = TYPE_MAIN_DECL (new_type);
DECL_TI_ARGS (r) = CLASSTYPE_TI_ARGS (new_type);
}
else
{
tree new_decl = tsubst (decl, args, /*complain=*/1, in_decl);
DECL_RESULT (r) = new_decl;
DECL_TI_TEMPLATE (new_decl) = r;
TREE_TYPE (r) = TREE_TYPE (new_decl);
DECL_TI_ARGS (r) = DECL_TI_ARGS (new_decl);
}
SET_DECL_IMPLICIT_INSTANTIATION (r);
DECL_TEMPLATE_INSTANTIATIONS (r) = NULL_TREE;
DECL_TEMPLATE_SPECIALIZATIONS (r) = NULL_TREE;
/* The template parameters for this new template are all the
template parameters for the old template, except the
outermost level of parameters. */
DECL_TEMPLATE_PARMS (r)
= tsubst_template_parms (DECL_TEMPLATE_PARMS (t), args,
/*complain=*/1);
if (PRIMARY_TEMPLATE_P (t))
DECL_PRIMARY_TEMPLATE (r) = r;
/* We don't partially instantiate partial specializations. */
if (TREE_CODE (decl) == TYPE_DECL)
break;
for (spec = DECL_TEMPLATE_SPECIALIZATIONS (t);
spec != NULL_TREE;
spec = TREE_CHAIN (spec))
{
/* It helps to consider example here. Consider:
template <class T>
struct S {
template <class U>
void f(U u);
template <>
void f(T* t) {}
};
Now, for example, we are instantiating S<int>::f(U u).
We want to make a template:
template <class U>
void S<int>::f(U);
It will have a specialization, for the case U = int*, of
the form:
template <>
void S<int>::f<int*>(int*);
This specialization will be an instantiation of
the specialization given in the declaration of S, with
argument list int*. */
tree fn = TREE_VALUE (spec);
tree spec_args;
tree new_fn;
if (!DECL_TEMPLATE_SPECIALIZATION (fn))
/* Instantiations are on the same list, but they're of
no concern to us. */
continue;
if (TREE_CODE (fn) != TEMPLATE_DECL)
/* A full specialization. There's no need to record
that here. */
continue;
spec_args = tsubst (DECL_TI_ARGS (fn), args,
/*complain=*/1, in_decl);
new_fn = tsubst (DECL_RESULT (most_general_template (fn)),
spec_args, /*complain=*/1, in_decl);
DECL_TI_TEMPLATE (new_fn) = fn;
register_specialization (new_fn, r,
innermost_args (spec_args));
}
/* Record this partial instantiation. */
register_specialization (r, t,
DECL_TI_ARGS (DECL_RESULT (r)));
}
break;
case FUNCTION_DECL:
{
tree ctx;
tree argvec = NULL_TREE;
tree *friends;
tree gen_tmpl;
int member;
int args_depth;
int parms_depth;
/* Nobody should be tsubst'ing into non-template functions. */
my_friendly_assert (DECL_TEMPLATE_INFO (t) != NULL_TREE, 0);
if (TREE_CODE (DECL_TI_TEMPLATE (t)) == TEMPLATE_DECL)
{
tree spec;
/* Allocate template arguments on the momentary obstack,
in case we don't need to keep them. */
push_momentary ();
/* Calculate the most general template of which R is a
specialization, and the complete set of arguments used to
specialize R. */
gen_tmpl = most_general_template (DECL_TI_TEMPLATE (t));
argvec
= tsubst_template_arg_vector (DECL_TI_ARGS
(DECL_TEMPLATE_RESULT (gen_tmpl)),
args, /*complain=*/1);
/* Check to see if we already have this specialization. */
spec = retrieve_specialization (gen_tmpl, argvec);
if (spec)
{
r = spec;
pop_momentary ();
break;
}
/* We're going to need to keep the ARGVEC, so we copy it
here. */
argvec = copy_to_permanent (argvec);
pop_momentary ();
/* Here, we deal with the peculiar case:
template <class T> struct S {
template <class U> friend void f();
};
template <class U> friend void f() {}
template S<int>;
template void f<double>();
Here, the ARGS for the instantiation of will be {int,
double}. But, we only need as many ARGS as there are
levels of template parameters in CODE_PATTERN. We are
careful not to get fooled into reducing the ARGS in
situations like:
template <class T> struct S { template <class U> void f(U); }
template <class T> template <> void S<T>::f(int) {}
which we can spot because the pattern will be a
specialization in this case. */
args_depth = TMPL_ARGS_DEPTH (args);
parms_depth =
TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (DECL_TI_TEMPLATE (t)));
if (args_depth > parms_depth
&& !DECL_TEMPLATE_SPECIALIZATION (t))
{
my_friendly_assert (DECL_FRIEND_P (t), 0);
if (parms_depth > 1)
{
int i;
args = make_temp_vec (parms_depth);
for (i = 0; i < parms_depth; ++i)
TREE_VEC_ELT (args, i) =
TREE_VEC_ELT (args, i + (args_depth - parms_depth));
}
else
args = TREE_VEC_ELT (args, args_depth - parms_depth);
}
}
else
{
/* This special case arises when we have something like this:
template <class T> struct S {
friend void f<int>(int, double);
};
Here, the DECL_TI_TEMPLATE for the friend declaration
will be a LOOKUP_EXPR or an IDENTIFIER_NODE. We are
being called from tsubst_friend_function, and we want
only to create a new decl (R) with appropriate types so
that we can call determine_specialization. */
my_friendly_assert ((TREE_CODE (DECL_TI_TEMPLATE (t))
== LOOKUP_EXPR)
|| (TREE_CODE (DECL_TI_TEMPLATE (t))
== IDENTIFIER_NODE), 0);
gen_tmpl = NULL_TREE;
}
if (DECL_CLASS_SCOPE_P (t))
{
if (DECL_NAME (t) == constructor_name (DECL_CONTEXT (t)))
member = 2;
else
member = 1;
ctx = tsubst_aggr_type (DECL_CLASS_CONTEXT (t), args,
/*complain=*/1, t,
/*entering_scope=*/1);
}
else
{
member = 0;
ctx = NULL_TREE;
}
type = tsubst (type, args, /*complain=*/1, in_decl);
/* We do NOT check for matching decls pushed separately at this
point, as they may not represent instantiations of this
template, and in any case are considered separate under the
discrete model. Instead, see add_maybe_template. */
r = copy_node (t);
copy_lang_decl (r);
DECL_USE_TEMPLATE (r) = 0;
TREE_TYPE (r) = type;
DECL_CONTEXT (r)
= tsubst_aggr_type (DECL_CONTEXT (t), args, /*complain=*/1, t,
/*entering_scope=*/1);
DECL_CLASS_CONTEXT (r) = ctx;
if (member && IDENTIFIER_TYPENAME_P (DECL_NAME (r)))
/* Type-conversion operator. Reconstruct the name, in
case it's the name of one of the template's parameters. */
DECL_NAME (r) = build_typename_overload (TREE_TYPE (type));
DECL_ARGUMENTS (r) = tsubst (DECL_ARGUMENTS (t), args,
/*complain=*/1, t);
DECL_MAIN_VARIANT (r) = r;
DECL_RESULT (r) = NULL_TREE;
TREE_STATIC (r) = 0;
TREE_PUBLIC (r) = TREE_PUBLIC (t);
DECL_EXTERNAL (r) = 1;
DECL_INTERFACE_KNOWN (r) = 0;
DECL_DEFER_OUTPUT (r) = 0;
TREE_CHAIN (r) = NULL_TREE;
DECL_PENDING_INLINE_INFO (r) = 0;
TREE_USED (r) = 0;
/* Set up the DECL_TEMPLATE_INFO for R and compute its mangled
name. There's no need to do this in the special friend
case mentioned above where GEN_TMPL is NULL. */
if (gen_tmpl)
{
/* The ARGVEC was built on the momentary obstack. Make it
permanent now. */
argvec = copy_to_permanent (argvec);
DECL_TEMPLATE_INFO (r)
= perm_tree_cons (gen_tmpl, argvec, NULL_TREE);
SET_DECL_IMPLICIT_INSTANTIATION (r);
register_specialization (r, gen_tmpl, argvec);
/* Set the mangled name for R. */
if (DECL_DESTRUCTOR_P (t))
DECL_ASSEMBLER_NAME (r) = build_destructor_name (ctx);
else
{
/* Instantiations of template functions must be mangled
specially, in order to conform to 14.5.5.1
[temp.over.link]. */
tree tmpl = DECL_TI_TEMPLATE (t);
/* TMPL will be NULL if this is a specialization of a
member function of a template class. */
if (name_mangling_version < 1
|| tmpl == NULL_TREE
|| (member && !is_member_template (tmpl)
&& !DECL_TEMPLATE_INFO (tmpl)))
set_mangled_name_for_decl (r);
else
set_mangled_name_for_template_decl (r);
}
DECL_RTL (r) = 0;
make_decl_rtl (r, NULL_PTR, 1);
/* Like grokfndecl. If we don't do this, pushdecl will
mess up our TREE_CHAIN because it doesn't find a
previous decl. Sigh. */
if (member
&& ! uses_template_parms (r)
&& (IDENTIFIER_GLOBAL_VALUE (DECL_ASSEMBLER_NAME (r))
== NULL_TREE))
SET_IDENTIFIER_GLOBAL_VALUE (DECL_ASSEMBLER_NAME (r), r);
}
/* Copy the list of befriending classes. */
for (friends = &DECL_BEFRIENDING_CLASSES (r);
*friends;
friends = &TREE_CHAIN (*friends))
{
*friends = copy_node (*friends);
TREE_VALUE (*friends) = tsubst (TREE_VALUE (*friends),
args, /*complain=*/1,
in_decl);
}
if (DECL_CONSTRUCTOR_P (r))
{
maybe_retrofit_in_chrg (r);
grok_ctor_properties (ctx, r);
}
if (IDENTIFIER_OPNAME_P (DECL_NAME (r)))
grok_op_properties (r, DECL_VIRTUAL_P (r), DECL_FRIEND_P (r));
}
break;
case PARM_DECL:
{
r = copy_node (t);
TREE_TYPE (r) = type;
c_apply_type_quals_to_decl (CP_TYPE_QUALS (type), r);
if (TREE_CODE (DECL_INITIAL (r)) != TEMPLATE_PARM_INDEX)
DECL_INITIAL (r) = TREE_TYPE (r);
else
DECL_INITIAL (r) = tsubst (DECL_INITIAL (r), args,
/*complain=*/1, in_decl);
DECL_CONTEXT (r) = NULL_TREE;
#ifdef PROMOTE_PROTOTYPES
if ((TREE_CODE (type) == INTEGER_TYPE
|| TREE_CODE (type) == ENUMERAL_TYPE)
&& TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))
DECL_ARG_TYPE (r) = integer_type_node;
#endif
if (TREE_CHAIN (t))
TREE_CHAIN (r) = tsubst (TREE_CHAIN (t), args,
/*complain=*/1, TREE_CHAIN (t));
}
break;
case FIELD_DECL:
{
r = copy_node (t);
copy_lang_decl (r);
TREE_TYPE (r) = type;
c_apply_type_quals_to_decl (CP_TYPE_QUALS (type), r);
/* We don't have to set DECL_CONTEXT here; it is set by
finish_member_declaration. */
DECL_INITIAL (r) = tsubst_expr (DECL_INITIAL (t), args,
/*complain=*/1, in_decl);
TREE_CHAIN (r) = NULL_TREE;
if (TREE_CODE (type) == VOID_TYPE)
cp_error_at ("instantiation of `%D' as type void", r);
}
break;
case USING_DECL:
{
r = copy_node (t);
DECL_INITIAL (r)
= tsubst_copy (DECL_INITIAL (t), args, /*complain=*/1, in_decl);
TREE_CHAIN (r) = NULL_TREE;
}
break;
case VAR_DECL:
{
tree argvec;
tree gen_tmpl;
tree spec;
tree tmpl;
tree ctx = tsubst_aggr_type (DECL_CONTEXT (t), args,
/*complain=*/1,
in_decl, /*entering_scope=*/1);
/* Nobody should be tsubst'ing into non-template variables. */
my_friendly_assert (DECL_LANG_SPECIFIC (t)
&& DECL_TEMPLATE_INFO (t) != NULL_TREE, 0);
/* Check to see if we already have this specialization. */
tmpl = DECL_TI_TEMPLATE (t);
gen_tmpl = most_general_template (tmpl);
argvec = tsubst (DECL_TI_ARGS (t), args, /*complain=*/1, in_decl);
spec = retrieve_specialization (gen_tmpl, argvec);
if (spec)
{
r = spec;
break;
}
r = copy_node (t);
TREE_TYPE (r) = type;
c_apply_type_quals_to_decl (CP_TYPE_QUALS (type), r);
DECL_CONTEXT (r) = ctx;
/* Don't try to expand the initializer until someone tries to use
this variable; otherwise we run into circular dependencies. */
DECL_INITIAL (r) = NULL_TREE;
DECL_RTL (r) = 0;
DECL_SIZE (r) = 0;
copy_lang_decl (r);
DECL_CLASS_CONTEXT (r) = DECL_CONTEXT (r);
/* A static data member declaration is always marked external
when it is declared in-class, even if an initializer is
present. We mimic the non-template processing here. */
DECL_EXTERNAL (r) = 1;
DECL_TEMPLATE_INFO (r) = perm_tree_cons (tmpl, argvec, NULL_TREE);
SET_DECL_IMPLICIT_INSTANTIATION (r);
register_specialization (r, gen_tmpl, argvec);
TREE_CHAIN (r) = NULL_TREE;
if (TREE_CODE (type) == VOID_TYPE)
cp_error_at ("instantiation of `%D' as type void", r);
}
break;
case TYPE_DECL:
if (t == TYPE_NAME (TREE_TYPE (t)))
r = TYPE_NAME (type);
else
{
r = copy_node (t);
TREE_TYPE (r) = type;
DECL_CONTEXT (r) = current_class_type;
TREE_CHAIN (r) = NULL_TREE;
}
break;
default:
my_friendly_abort (0);
}
/* Restore the file and line information. */
lineno = saved_lineno;
input_filename = saved_filename;
return r;
}
/* Substitue into the ARG_TYPES of a function type. */
static tree
tsubst_arg_types (arg_types, args, complain, in_decl)
tree arg_types;
tree args;
int complain;
tree in_decl;
{
tree remaining_arg_types;
tree type;
if (!arg_types || arg_types == void_list_node)
return arg_types;
remaining_arg_types = tsubst_arg_types (TREE_CHAIN (arg_types),
args, complain, in_decl);
if (remaining_arg_types == error_mark_node)
return error_mark_node;
type = tsubst (TREE_VALUE (arg_types), args, complain, in_decl);
if (type == error_mark_node)
return error_mark_node;
/* Do array-to-pointer, function-to-pointer conversion, and ignore
top-level qualifiers as required. */
type = TYPE_MAIN_VARIANT (type_decays_to (type));
/* Note that we do not substitute into default arguments here. The
standard mandates that they be instantiated only when needed,
which is done in build_over_call. */
return hash_tree_cons (TREE_PURPOSE (arg_types), type,
remaining_arg_types);
}
/* Substitute into a FUNCTION_TYPE or METHOD_TYPE. This routine does
*not* handle the exception-specification for FNTYPE, because the
initial substitution of explicitly provided template parameters
during argument deduction forbids substitution into the
exception-specification:
[temp.deduct]
All references in the function type of the function template to the
corresponding template parameters are replaced by the specified tem-
plate argument values. If a substitution in a template parameter or
in the function type of the function template results in an invalid
type, type deduction fails. [Note: The equivalent substitution in
exception specifications is done only when the function is instanti-
ated, at which point a program is ill-formed if the substitution
results in an invalid type.] */
static tree
tsubst_function_type (t, args, complain, in_decl)
tree t;
tree args;
int complain;
tree in_decl;
{
tree return_type;
tree arg_types;
tree fntype;
/* The TYPE_CONTEXT is not used for function/method types. */
my_friendly_assert (TYPE_CONTEXT (t) == NULL_TREE, 0);
/* Substitue the return type. */
return_type = tsubst (TREE_TYPE (t), args, complain, in_decl);
if (return_type == error_mark_node)
return error_mark_node;
/* Substitue the argument types. */
arg_types = tsubst_arg_types (TYPE_ARG_TYPES (t), args,
complain, in_decl);
if (arg_types == error_mark_node)
return error_mark_node;
/* Construct a new type node and return it. */
if (TREE_CODE (t) == FUNCTION_TYPE)
fntype = build_function_type (return_type, arg_types);
else
{
tree r = TREE_TYPE (TREE_VALUE (arg_types));
if (! IS_AGGR_TYPE (r))
{
/* [temp.deduct]
Type deduction may fail for any of the following
reasons:
-- Attempting to create "pointer to member of T" when T
is not a class type. */
if (complain)
cp_error ("creating pointer to member function of non-class type `%T'",
r);
return error_mark_node;
}
fntype = build_cplus_method_type (r, return_type, TREE_CHAIN
(arg_types));
}
fntype = build_qualified_type (fntype, TYPE_QUALS (t));
fntype = build_type_attribute_variant (fntype, TYPE_ATTRIBUTES (t));
return fntype;
}
/* Substitute into the PARMS of a call-declarator. */
static tree
tsubst_call_declarator_parms (parms, args, complain, in_decl)
tree parms;
tree args;
int complain;
tree in_decl;
{
tree new_parms;
tree type;
tree defarg;
if (!parms || parms == void_list_node)
return parms;
new_parms = tsubst_call_declarator_parms (TREE_CHAIN (parms),
args, complain, in_decl);
/* Figure out the type of this parameter. */
type = tsubst (TREE_VALUE (parms), args, complain, in_decl);
/* Figure out the default argument as well. Note that we use
tsubst_expr since the default argument is really an expression. */
defarg = tsubst_expr (TREE_PURPOSE (parms), args, complain, in_decl);
/* Chain this parameter on to the front of those we have already
processed. We don't use hash_tree_cons because that function
doesn't check TREE_PARMLIST. */
new_parms = tree_cons (defarg, type, new_parms);
/* And note that these are parameters. */
TREE_PARMLIST (new_parms) = 1;
return new_parms;
}
/* Take the tree structure T and replace template parameters used
therein with the argument vector ARGS. IN_DECL is an associated
decl for diagnostics. If an error occurs, returns ERROR_MARK_NODE.
An appropriate error message is issued only if COMPLAIN is
non-zero. Note that we must be relatively non-tolerant of
extensions here, in order to preserve conformance; if we allow
substitutions that should not be allowed, we may allow argument
deductions that should not succeed, and therefore report ambiguous
overload situations where there are none. In theory, we could
allow the substitution, but indicate that it should have failed,
and allow our caller to make sure that the right thing happens, but
we don't try to do this yet.
This function is used for dealing with types, decls and the like;
for expressions, use tsubst_expr or tsubst_copy. */
tree
tsubst (t, args, complain, in_decl)
tree t, args;
int complain;
tree in_decl;
{
tree type, r;
if (t == NULL_TREE || t == error_mark_node
|| t == integer_type_node
|| t == void_type_node
|| t == char_type_node
|| TREE_CODE (t) == NAMESPACE_DECL)
return t;
if (TREE_CODE (t) == IDENTIFIER_NODE)
type = IDENTIFIER_TYPE_VALUE (t);
else
type = TREE_TYPE (t);
if (type == unknown_type_node)
my_friendly_abort (42);
if (type && TREE_CODE (t) != FUNCTION_DECL
&& TREE_CODE (t) != TYPENAME_TYPE
&& TREE_CODE (t) != TEMPLATE_DECL
&& TREE_CODE (t) != IDENTIFIER_NODE
&& TREE_CODE (t) != FUNCTION_TYPE
&& TREE_CODE (t) != METHOD_TYPE)
type = tsubst (type, args, complain, in_decl);
if (type == error_mark_node)
return error_mark_node;
if (TREE_CODE_CLASS (TREE_CODE (t)) == 'd')
return tsubst_decl (t, args, type, in_decl);
switch (TREE_CODE (t))
{
case RECORD_TYPE:
case UNION_TYPE:
case ENUMERAL_TYPE:
return tsubst_aggr_type (t, args, complain, in_decl,
/*entering_scope=*/0);
case ERROR_MARK:
case IDENTIFIER_NODE:
case OP_IDENTIFIER:
case VOID_TYPE:
case REAL_TYPE:
case COMPLEX_TYPE:
case BOOLEAN_TYPE:
case INTEGER_CST:
case REAL_CST:
case STRING_CST:
return t;
case INTEGER_TYPE:
if (t == integer_type_node)
return t;
if (TREE_CODE (TYPE_MIN_VALUE (t)) == INTEGER_CST
&& TREE_CODE (TYPE_MAX_VALUE (t)) == INTEGER_CST)
return t;
{
tree max, omax = TREE_OPERAND (TYPE_MAX_VALUE (t), 0);
max = tsubst_expr (omax, args, complain, in_decl);
if (max == error_mark_node)
return error_mark_node;
+ /* See if we can reduce this expression to something simpler. */
+ max = maybe_fold_nontype_arg (max);
+ if (!processing_template_decl && TREE_READONLY_DECL_P (max))
+ max = decl_constant_value (max);
+
if (processing_template_decl
/* When providing explicit arguments to a template
function, but leaving some arguments for subsequent
deduction, MAX may be template-dependent even if we're
not PROCESSING_TEMPLATE_DECL. */
|| TREE_CODE (max) != INTEGER_CST)
{
- return build_index_type (build_min
- (MINUS_EXPR, sizetype, max, integer_one_node));
+ tree itype = make_node (INTEGER_TYPE);
+ TYPE_MIN_VALUE (itype) = size_zero_node;
+ TYPE_MAX_VALUE (itype) = build_min (MINUS_EXPR, sizetype, max,
+ integer_one_node);
+ return itype;
}
if (integer_zerop (omax))
{
/* Still allow an explicit array of size zero. */
if (pedantic)
pedwarn ("creating array with size zero");
}
else if (integer_zerop (max) || INT_CST_LT (max, integer_zero_node))
{
/* [temp.deduct]
Type deduction may fail for any of the following
reasons:
Attempting to create an array with a size that is
zero or negative. */
if (complain)
cp_error ("creating array with size `%E'", max);
return error_mark_node;
}
max = fold (build_binary_op (MINUS_EXPR, max, integer_one_node));
if (!TREE_PERMANENT (max) && !allocation_temporary_p ())
max = copy_to_permanent (max);
return build_index_type (max);
}
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
case TEMPLATE_PARM_INDEX:
{
int idx;
int level;
int levels;
r = NULL_TREE;
if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
|| TREE_CODE (t) == TEMPLATE_TEMPLATE_PARM)
{
idx = TEMPLATE_TYPE_IDX (t);
level = TEMPLATE_TYPE_LEVEL (t);
}
else
{
idx = TEMPLATE_PARM_IDX (t);
level = TEMPLATE_PARM_LEVEL (t);
}
if (TREE_VEC_LENGTH (args) > 0)
{
tree arg = NULL_TREE;
levels = TMPL_ARGS_DEPTH (args);
if (level <= levels)
arg = TMPL_ARG (args, level, idx);
if (arg == error_mark_node)
return error_mark_node;
else if (arg != NULL_TREE)
{
if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
{
my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (arg))
== 't', 0);
return cp_build_qualified_type
(arg, CP_TYPE_QUALS (arg) | CP_TYPE_QUALS (t));
}
else if (TREE_CODE (t) == TEMPLATE_TEMPLATE_PARM)
{
if (TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (t))
{
/* We are processing a type constructed from
a template template parameter */
tree argvec = tsubst (TYPE_TI_ARGS (t),
args, complain, in_decl);
if (argvec == error_mark_node)
return error_mark_node;
/* We can get a TEMPLATE_TEMPLATE_PARM here when
we are resolving nested-types in the signature of
a member function templates.
Otherwise ARG is a TEMPLATE_DECL and is the real
template to be instantiated. */
if (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM)
arg = TYPE_NAME (arg);
r = lookup_template_class (DECL_NAME (arg),
argvec, in_decl,
DECL_CONTEXT (arg),
/*entering_scope=*/0);
return cp_build_qualified_type (r, TYPE_QUALS (t));
}
else
/* We are processing a template argument list. */
return arg;
}
else
return arg;
}
}
else
my_friendly_abort (981018);
if (level == 1)
/* This can happen during the attempted tsubst'ing in
unify. This means that we don't yet have any information
about the template parameter in question. */
return t;
/* If we get here, we must have been looking at a parm for a
more deeply nested template. Make a new version of this
template parameter, but with a lower level. */
switch (TREE_CODE (t))
{
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
r = copy_node (t);
TEMPLATE_TYPE_PARM_INDEX (r)
= reduce_template_parm_level (TEMPLATE_TYPE_PARM_INDEX (t),
r, levels);
TYPE_STUB_DECL (r) = TYPE_NAME (r) = TEMPLATE_TYPE_DECL (r);
TYPE_MAIN_VARIANT (r) = r;
TYPE_POINTER_TO (r) = NULL_TREE;
TYPE_REFERENCE_TO (r) = NULL_TREE;
if (TREE_CODE (t) == TEMPLATE_TEMPLATE_PARM
&& TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (t))
{
tree argvec = tsubst (TYPE_TI_ARGS (t), args,
complain, in_decl);
if (argvec == error_mark_node)
return error_mark_node;
TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (r)
= perm_tree_cons (TYPE_NAME (t), argvec, NULL_TREE);
}
break;
case TEMPLATE_PARM_INDEX:
r = reduce_template_parm_level (t, type, levels);
break;
default:
my_friendly_abort (0);
}
return r;
}
case TREE_LIST:
{
tree purpose, value, chain, result;
if (t == void_list_node)
return t;
purpose = TREE_PURPOSE (t);
if (purpose)
{
purpose = tsubst (purpose, args, complain, in_decl);
if (purpose == error_mark_node)
return error_mark_node;
}
value = TREE_VALUE (t);
if (value)
{
value = tsubst (value, args, complain, in_decl);
if (value == error_mark_node)
return error_mark_node;
}
chain = TREE_CHAIN (t);
if (chain && chain != void_type_node)
{
chain = tsubst (chain, args, complain, in_decl);
if (chain == error_mark_node)
return error_mark_node;
}
if (purpose == TREE_PURPOSE (t)
&& value == TREE_VALUE (t)
&& chain == TREE_CHAIN (t))
return t;
result = hash_tree_cons (purpose, value, chain);
TREE_PARMLIST (result) = TREE_PARMLIST (t);
return result;
}
case TREE_VEC:
if (type != NULL_TREE)
{
/* A binfo node. We always need to make a copy, of the node
itself and of its BINFO_BASETYPES. */
t = copy_node (t);
/* Make sure type isn't a typedef copy. */
type = BINFO_TYPE (TYPE_BINFO (type));
TREE_TYPE (t) = complete_type (type);
if (IS_AGGR_TYPE (type))
{
BINFO_VTABLE (t) = TYPE_BINFO_VTABLE (type);
BINFO_VIRTUALS (t) = TYPE_BINFO_VIRTUALS (type);
if (TYPE_BINFO_BASETYPES (type) != NULL_TREE)
BINFO_BASETYPES (t) = copy_node (TYPE_BINFO_BASETYPES (type));
}
return t;
}
/* Otherwise, a vector of template arguments. */
return tsubst_template_arg_vector (t, args, complain);
case POINTER_TYPE:
case REFERENCE_TYPE:
{
enum tree_code code;
if (type == TREE_TYPE (t))
return t;
code = TREE_CODE (t);
/* [temp.deduct]
Type deduction may fail for any of the following
reasons:
-- Attempting to create a pointer to reference type.
-- Attempting to create a reference to a reference type or
a reference to void. */
if (TREE_CODE (type) == REFERENCE_TYPE
|| (code == REFERENCE_TYPE && TREE_CODE (type) == VOID_TYPE))
{
static int last_line = 0;
static char* last_file = 0;
/* We keep track of the last time we issued this error
message to avoid spewing a ton of messages during a
single bad template instantiation. */
if (complain && (last_line != lineno ||
last_file != input_filename))
{
if (TREE_CODE (type) == VOID_TYPE)
cp_error ("forming reference to void");
else
cp_error ("forming %s to reference type `%T'",
(code == POINTER_TYPE) ? "pointer" : "reference",
type);
last_line = lineno;
last_file = input_filename;
}
return error_mark_node;
}
else if (code == POINTER_TYPE)
r = build_pointer_type (type);
else
r = build_reference_type (type);
r = cp_build_qualified_type (r, TYPE_QUALS (t));
/* Will this ever be needed for TYPE_..._TO values? */
layout_type (r);
return r;
}
case OFFSET_TYPE:
{
r = tsubst (TYPE_OFFSET_BASETYPE (t), args, complain, in_decl);
if (r == error_mark_node || !IS_AGGR_TYPE (r))
{
/* [temp.deduct]
Type deduction may fail for any of the following
reasons:
-- Attempting to create "pointer to member of T" when T
is not a class type. */
if (complain)
cp_error ("creating pointer to member of non-class type `%T'",
r);
return error_mark_node;
}
return build_offset_type (r, type);
}
case FUNCTION_TYPE:
case METHOD_TYPE:
{
tree fntype;
tree raises;
fntype = tsubst_function_type (t, args, complain, in_decl);
if (fntype == error_mark_node)
return error_mark_node;
/* Substitue the exception specification. */
raises = TYPE_RAISES_EXCEPTIONS (t);
if (raises)
{
raises = tsubst (raises, args, complain, in_decl);
if (raises == error_mark_node)
return raises;
fntype = build_exception_variant (fntype, raises);
}
return fntype;
}
case ARRAY_TYPE:
{
tree domain = tsubst (TYPE_DOMAIN (t), args, complain, in_decl);
if (domain == error_mark_node)
return error_mark_node;
/* As an optimization, we avoid regenerating the array type if
it will obviously be the same as T. */
if (type == TREE_TYPE (t) && domain == TYPE_DOMAIN (t))
return t;
/* These checks should match the ones in grokdeclarator.
[temp.deduct]
The deduction may fail for any of the following reasons:
-- Attempting to create an array with an element type that
is void, a function type, or a reference type. */
if (TREE_CODE (type) == VOID_TYPE
|| TREE_CODE (type) == FUNCTION_TYPE
|| TREE_CODE (type) == REFERENCE_TYPE)
{
if (complain)
cp_error ("creating array of `%T'", type);
return error_mark_node;
}
r = build_cplus_array_type (type, domain);
return r;
}
case PLUS_EXPR:
case MINUS_EXPR:
{
tree e1 = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
tree e2 = tsubst (TREE_OPERAND (t, 1), args, complain,
in_decl);
if (e1 == error_mark_node || e2 == error_mark_node)
return error_mark_node;
return fold (build (TREE_CODE (t), TREE_TYPE (t), e1, e2));
}
case NEGATE_EXPR:
case NOP_EXPR:
{
tree e = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
if (e == error_mark_node)
return error_mark_node;
return fold (build (TREE_CODE (t), TREE_TYPE (t), e));
}
case TYPENAME_TYPE:
{
tree ctx = tsubst_aggr_type (TYPE_CONTEXT (t), args, complain,
in_decl, /*entering_scope=*/1);
tree f = tsubst_copy (TYPENAME_TYPE_FULLNAME (t), args,
complain, in_decl);
if (ctx == error_mark_node || f == error_mark_node)
return error_mark_node;
if (!IS_AGGR_TYPE (ctx))
{
if (complain)
cp_error ("`%T' is not a class, struct, or union type",
ctx);
return error_mark_node;
}
else if (!uses_template_parms (ctx) && !TYPE_BEING_DEFINED (ctx))
{
/* Normally, make_typename_type does not require that the CTX
have complete type in order to allow things like:
template <class T> struct S { typename S<T>::X Y; };
But, such constructs have already been resolved by this
point, so here CTX really should have complete type, unless
it's a partial instantiation. */
ctx = complete_type (ctx);
if (!TYPE_SIZE (ctx))
{
if (complain)
incomplete_type_error (NULL_TREE, ctx);
return error_mark_node;
}
}
f = make_typename_type (ctx, f);
if (f == error_mark_node)
return f;
return cp_build_qualified_type (f,
CP_TYPE_QUALS (f)
| CP_TYPE_QUALS (t));
}
case INDIRECT_REF:
{
tree e = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
if (e == error_mark_node)
return error_mark_node;
return make_pointer_declarator (type, e);
}
case ADDR_EXPR:
{
tree e = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
if (e == error_mark_node)
return error_mark_node;
return make_reference_declarator (type, e);
}
case ARRAY_REF:
{
tree e1 = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
tree e2 = tsubst_expr (TREE_OPERAND (t, 1), args, complain,
in_decl);
if (e1 == error_mark_node || e2 == error_mark_node)
return error_mark_node;
return build_parse_node (ARRAY_REF, e1, e2, tsubst_expr);
}
case CALL_EXPR:
{
tree e1 = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
tree e2 = tsubst_call_declarator_parms (TREE_OPERAND (t, 1), args,
complain, in_decl);
tree e3 = tsubst (TREE_TYPE (t), args, complain, in_decl);
if (e1 == error_mark_node || e2 == error_mark_node
|| e3 == error_mark_node)
return error_mark_node;
return make_call_declarator (e1, e2, TREE_OPERAND (t, 2), e3);
}
case SCOPE_REF:
{
tree e1 = tsubst (TREE_OPERAND (t, 0), args, complain,
in_decl);
tree e2 = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl);
if (e1 == error_mark_node || e2 == error_mark_node)
return error_mark_node;
return build_parse_node (TREE_CODE (t), e1, e2);
}
case TYPEOF_TYPE:
{
tree e1 = tsubst_expr (TYPE_FIELDS (t), args, complain,
in_decl);
if (e1 == error_mark_node)
return error_mark_node;
return TREE_TYPE (e1);
}
default:
sorry ("use of `%s' in template",
tree_code_name [(int) TREE_CODE (t)]);
return error_mark_node;
}
}
void
do_pushlevel ()
{
emit_line_note (input_filename, lineno);
pushlevel (0);
clear_last_expr ();
push_momentary ();
expand_start_bindings (0);
}
tree
do_poplevel ()
{
tree t;
int saved_warn_unused = 0;
if (processing_template_decl)
{
saved_warn_unused = warn_unused;
warn_unused = 0;
}
expand_end_bindings (getdecls (), kept_level_p (), 0);
if (processing_template_decl)
warn_unused = saved_warn_unused;
t = poplevel (kept_level_p (), 1, 0);
pop_momentary ();
return t;
}
/* Like tsubst, but deals with expressions. This function just replaces
template parms; to finish processing the resultant expression, use
tsubst_expr. */
tree
tsubst_copy (t, args, complain, in_decl)
tree t, args;
int complain;
tree in_decl;
{
enum tree_code code;
tree r;
if (t == NULL_TREE || t == error_mark_node)
return t;
code = TREE_CODE (t);
switch (code)
{
case PARM_DECL:
return do_identifier (DECL_NAME (t), 0, NULL_TREE);
case CONST_DECL:
{
tree enum_type;
tree v;
if (!DECL_CONTEXT (t))
/* This is a global enumeration constant. */
return t;
/* Unfortunately, we cannot just call lookup_name here.
Consider:
template <int I> int f() {
enum E { a = I };
struct S { void g() { E e = a; } };
};
When we instantiate f<7>::S::g(), say, lookup_name is not
clever enough to find f<7>::a. */
enum_type
= tsubst_aggr_type (TREE_TYPE (t), args, complain, in_decl,
/*entering_scope=*/0);
for (v = TYPE_VALUES (enum_type);
v != NULL_TREE;
v = TREE_CHAIN (v))
if (TREE_PURPOSE (v) == DECL_NAME (t))
return TREE_VALUE (v);
/* We didn't find the name. That should never happen; if
name-lookup found it during preliminary parsing, we
should find it again here during instantiation. */
my_friendly_abort (0);
}
return t;
case FIELD_DECL:
if (DECL_CONTEXT (t))
{
tree ctx;
ctx = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
/*entering_scope=*/1);
if (ctx != DECL_CONTEXT (t))
return lookup_field (ctx, DECL_NAME (t), 0, 0);
}
return t;
case VAR_DECL:
case FUNCTION_DECL:
if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t))
t = tsubst (t, args, complain, in_decl);
mark_used (t);
return t;
case TEMPLATE_DECL:
if (is_member_template (t))
return tsubst (t, args, complain, in_decl);
else
return t;
case LOOKUP_EXPR:
{
/* We must tsbust into a LOOKUP_EXPR in case the names to
which it refers is a conversion operator; in that case the
name will change. We avoid making unnecessary copies,
however. */
tree id = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
if (id != TREE_OPERAND (t, 0))
{
r = build_nt (LOOKUP_EXPR, id);
LOOKUP_EXPR_GLOBAL (r) = LOOKUP_EXPR_GLOBAL (t);
t = r;
}
return t;
}
case CAST_EXPR:
case REINTERPRET_CAST_EXPR:
case CONST_CAST_EXPR:
case STATIC_CAST_EXPR:
case DYNAMIC_CAST_EXPR:
case NOP_EXPR:
return build1
(code, tsubst (TREE_TYPE (t), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl));
case INDIRECT_REF:
case PREDECREMENT_EXPR:
case PREINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
case NEGATE_EXPR:
case TRUTH_NOT_EXPR:
case BIT_NOT_EXPR:
case ADDR_EXPR:
case CONVERT_EXPR: /* Unary + */
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
case ARROW_EXPR:
case THROW_EXPR:
case TYPEID_EXPR:
return build1
(code, tsubst (TREE_TYPE (t), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl));
case PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
case TRUNC_DIV_EXPR:
case CEIL_DIV_EXPR:
case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR:
case BIT_AND_EXPR:
case BIT_ANDTC_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
case TRUNC_MOD_EXPR:
case FLOOR_MOD_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
case TRUTH_AND_EXPR:
case TRUTH_OR_EXPR:
case RSHIFT_EXPR:
case LSHIFT_EXPR:
case RROTATE_EXPR:
case LROTATE_EXPR:
case EQ_EXPR:
case NE_EXPR:
case MAX_EXPR:
case MIN_EXPR:
case LE_EXPR:
case GE_EXPR:
case LT_EXPR:
case GT_EXPR:
case COMPONENT_REF:
case ARRAY_REF:
case COMPOUND_EXPR:
case SCOPE_REF:
case DOTSTAR_EXPR:
case MEMBER_REF:
return build_nt
(code, tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl));
case CALL_EXPR:
{
tree fn = TREE_OPERAND (t, 0);
if (is_overloaded_fn (fn))
fn = tsubst_copy (get_first_fn (fn), args, complain, in_decl);
else
/* Sometimes FN is a LOOKUP_EXPR. */
fn = tsubst_copy (fn, args, complain, in_decl);
return build_nt
(code, fn, tsubst_copy (TREE_OPERAND (t, 1), args, complain,
in_decl),
NULL_TREE);
}
case METHOD_CALL_EXPR:
{
tree name = TREE_OPERAND (t, 0);
if (TREE_CODE (name) == BIT_NOT_EXPR)
{
name = tsubst_copy (TREE_OPERAND (name, 0), args,
complain, in_decl);
name = build1 (BIT_NOT_EXPR, NULL_TREE, name);
}
else if (TREE_CODE (name) == SCOPE_REF
&& TREE_CODE (TREE_OPERAND (name, 1)) == BIT_NOT_EXPR)
{
tree base = tsubst_copy (TREE_OPERAND (name, 0), args,
complain, in_decl);
name = TREE_OPERAND (name, 1);
name = tsubst_copy (TREE_OPERAND (name, 0), args,
complain, in_decl);
name = build1 (BIT_NOT_EXPR, NULL_TREE, name);
name = build_nt (SCOPE_REF, base, name);
}
else
name = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
return build_nt
(code, name, tsubst_copy (TREE_OPERAND (t, 1), args,
complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl),
NULL_TREE);
}
case BIND_EXPR:
case COND_EXPR:
case MODOP_EXPR:
{
r = build_nt
(code, tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl));
if (code == BIND_EXPR && !processing_template_decl)
{
/* This processing should really occur in tsubst_expr,
However, tsubst_expr does not recurse into expressions,
since it assumes that there aren't any statements
inside them. Instead, it simply calls
build_expr_from_tree. So, we need to expand the
BIND_EXPR here. */
tree rtl_expr = begin_stmt_expr ();
tree block = tsubst_expr (TREE_OPERAND (r, 1), args,
complain, in_decl);
r = finish_stmt_expr (rtl_expr, block);
}
return r;
}
case NEW_EXPR:
{
r = build_nt
(code, tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl));
NEW_EXPR_USE_GLOBAL (r) = NEW_EXPR_USE_GLOBAL (t);
return r;
}
case DELETE_EXPR:
{
r = build_nt
(code, tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl));
DELETE_EXPR_USE_GLOBAL (r) = DELETE_EXPR_USE_GLOBAL (t);
DELETE_EXPR_USE_VEC (r) = DELETE_EXPR_USE_VEC (t);
return r;
}
case TEMPLATE_ID_EXPR:
{
/* Substituted template arguments */
tree targs = tsubst_copy (TREE_OPERAND (t, 1), args, complain,
in_decl);
if (targs && TREE_CODE (targs) == TREE_LIST)
{
tree chain;
for (chain = targs; chain; chain = TREE_CHAIN (chain))
TREE_VALUE (chain) = maybe_fold_nontype_arg (TREE_VALUE (chain));
}
else if (targs)
{
int i;
for (i = 0; i < TREE_VEC_LENGTH (targs); ++i)
TREE_VEC_ELT (targs, i)
= maybe_fold_nontype_arg (TREE_VEC_ELT (targs, i));
}
return lookup_template_function
(tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl), targs);
}
case TREE_LIST:
{
tree purpose, value, chain;
if (t == void_list_node)
return t;
purpose = TREE_PURPOSE (t);
if (purpose)
purpose = tsubst_copy (purpose, args, complain, in_decl);
value = TREE_VALUE (t);
if (value)
value = tsubst_copy (value, args, complain, in_decl);
chain = TREE_CHAIN (t);
if (chain && chain != void_type_node)
chain = tsubst_copy (chain, args, complain, in_decl);
if (purpose == TREE_PURPOSE (t)
&& value == TREE_VALUE (t)
&& chain == TREE_CHAIN (t))
return t;
return tree_cons (purpose, value, chain);
}
case RECORD_TYPE:
case UNION_TYPE:
case ENUMERAL_TYPE:
case INTEGER_TYPE:
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
case TEMPLATE_PARM_INDEX:
case POINTER_TYPE:
case REFERENCE_TYPE:
case OFFSET_TYPE:
case FUNCTION_TYPE:
case METHOD_TYPE:
case ARRAY_TYPE:
case TYPENAME_TYPE:
case TYPE_DECL:
return tsubst (t, args, complain, in_decl);
case IDENTIFIER_NODE:
if (IDENTIFIER_TYPENAME_P (t)
/* Make sure it's not just a variable named `__opr', for instance,
which can occur in some existing code. */
&& TREE_TYPE (t))
return build_typename_overload
(tsubst (TREE_TYPE (t), args, complain, in_decl));
else
return t;
case CONSTRUCTOR:
{
r = build
(CONSTRUCTOR, tsubst (TREE_TYPE (t), args, complain, in_decl),
NULL_TREE, tsubst_copy (CONSTRUCTOR_ELTS (t), args,
complain, in_decl));
TREE_HAS_CONSTRUCTOR (r) = TREE_HAS_CONSTRUCTOR (t);
return r;
}
default:
return t;
}
}
/* Like tsubst_copy, but also does semantic processing and RTL expansion. */
tree
tsubst_expr (t, args, complain, in_decl)
tree t, args;
int complain;
tree in_decl;
{
if (t == NULL_TREE || t == error_mark_node)
return t;
if (processing_template_decl)
return tsubst_copy (t, args, complain, in_decl);
switch (TREE_CODE (t))
{
case RETURN_STMT:
lineno = TREE_COMPLEXITY (t);
finish_return_stmt (tsubst_expr (RETURN_EXPR (t),
args, complain, in_decl));
break;
case EXPR_STMT:
lineno = TREE_COMPLEXITY (t);
finish_expr_stmt (tsubst_expr (EXPR_STMT_EXPR (t),
args, complain, in_decl));
break;
case DECL_STMT:
{
int i = suspend_momentary ();
tree dcl, init;
lineno = TREE_COMPLEXITY (t);
emit_line_note (input_filename, lineno);
dcl = start_decl
(tsubst (TREE_OPERAND (t, 0), args, complain, in_decl),
tsubst (TREE_OPERAND (t, 1), args, complain, in_decl),
TREE_OPERAND (t, 2) != 0, NULL_TREE, NULL_TREE);
init = tsubst_expr (TREE_OPERAND (t, 2), args, complain, in_decl);
cp_finish_decl
(dcl, init, NULL_TREE, 1, /*init ? LOOKUP_ONLYCONVERTING :*/ 0);
resume_momentary (i);
return dcl;
}
case FOR_STMT:
{
tree tmp;
lineno = TREE_COMPLEXITY (t);
begin_for_stmt ();
for (tmp = FOR_INIT_STMT (t); tmp; tmp = TREE_CHAIN (tmp))
tsubst_expr (tmp, args, complain, in_decl);
finish_for_init_stmt (NULL_TREE);
finish_for_cond (tsubst_expr (FOR_COND (t), args,
complain, in_decl),
NULL_TREE);
tmp = tsubst_expr (FOR_EXPR (t), args, complain, in_decl);
finish_for_expr (tmp, NULL_TREE);
tsubst_expr (FOR_BODY (t), args, complain, in_decl);
finish_for_stmt (tmp, NULL_TREE);
}
break;
case WHILE_STMT:
{
lineno = TREE_COMPLEXITY (t);
begin_while_stmt ();
finish_while_stmt_cond (tsubst_expr (WHILE_COND (t),
args, complain, in_decl),
NULL_TREE);
tsubst_expr (WHILE_BODY (t), args, complain, in_decl);
finish_while_stmt (NULL_TREE);
}
break;
case DO_STMT:
{
lineno = TREE_COMPLEXITY (t);
begin_do_stmt ();
tsubst_expr (DO_BODY (t), args, complain, in_decl);
finish_do_body (NULL_TREE);
finish_do_stmt (tsubst_expr (DO_COND (t), args,
complain, in_decl),
NULL_TREE);
}
break;
case IF_STMT:
{
tree tmp;
lineno = TREE_COMPLEXITY (t);
begin_if_stmt ();
finish_if_stmt_cond (tsubst_expr (IF_COND (t),
args, complain, in_decl),
NULL_TREE);
if (tmp = THEN_CLAUSE (t), tmp)
{
tsubst_expr (tmp, args, complain, in_decl);
finish_then_clause (NULL_TREE);
}
if (tmp = ELSE_CLAUSE (t), tmp)
{
begin_else_clause ();
tsubst_expr (tmp, args, complain, in_decl);
finish_else_clause (NULL_TREE);
}
finish_if_stmt ();
}
break;
case COMPOUND_STMT:
{
tree substmt;
lineno = TREE_COMPLEXITY (t);
begin_compound_stmt (COMPOUND_STMT_NO_SCOPE (t));
for (substmt = COMPOUND_BODY (t);
substmt != NULL_TREE;
substmt = TREE_CHAIN (substmt))
tsubst_expr (substmt, args, complain, in_decl);
return finish_compound_stmt (COMPOUND_STMT_NO_SCOPE (t),
NULL_TREE);
}
break;
case BREAK_STMT:
lineno = TREE_COMPLEXITY (t);
finish_break_stmt ();
break;
case CONTINUE_STMT:
lineno = TREE_COMPLEXITY (t);
finish_continue_stmt ();
break;
case SWITCH_STMT:
{
tree val, tmp;
lineno = TREE_COMPLEXITY (t);
begin_switch_stmt ();
val = tsubst_expr (SWITCH_COND (t), args, complain, in_decl);
finish_switch_cond (val);
if (tmp = TREE_OPERAND (t, 1), tmp)
tsubst_expr (tmp, args, complain, in_decl);
finish_switch_stmt (val, NULL_TREE);
}
break;
case CASE_LABEL:
finish_case_label (tsubst_expr (CASE_LOW (t), args, complain, in_decl),
tsubst_expr (CASE_HIGH (t), args, complain, in_decl));
break;
case LABEL_DECL:
t = define_label (DECL_SOURCE_FILE (t), DECL_SOURCE_LINE (t),
DECL_NAME (t));
if (t)
expand_label (t);
break;
case GOTO_STMT:
lineno = TREE_COMPLEXITY (t);
t = GOTO_DESTINATION (t);
if (TREE_CODE (t) != IDENTIFIER_NODE)
/* Computed goto's must be tsubst'd into. On the other hand,
non-computed gotos must not be; the identifier in question
will have no binding. */
t = tsubst_expr (t, args, complain, in_decl);
finish_goto_stmt (t);
break;
case ASM_STMT:
lineno = TREE_COMPLEXITY (t);
finish_asm_stmt (tsubst_expr (ASM_CV_QUAL (t), args, complain, in_decl),
tsubst_expr (ASM_STRING (t), args, complain, in_decl),
tsubst_expr (ASM_OUTPUTS (t), args, complain, in_decl),
tsubst_expr (ASM_INPUTS (t), args, complain, in_decl),
tsubst_expr (ASM_CLOBBERS (t), args, complain,
in_decl));
break;
case TRY_BLOCK:
lineno = TREE_COMPLEXITY (t);
begin_try_block ();
tsubst_expr (TRY_STMTS (t), args, complain, in_decl);
finish_try_block (NULL_TREE);
{
tree handler = TRY_HANDLERS (t);
for (; handler; handler = TREE_CHAIN (handler))
tsubst_expr (handler, args, complain, in_decl);
}
finish_handler_sequence (NULL_TREE);
break;
case HANDLER:
lineno = TREE_COMPLEXITY (t);
begin_handler ();
if (HANDLER_PARMS (t))
{
tree d = HANDLER_PARMS (t);
expand_start_catch_block
(tsubst (TREE_OPERAND (d, 1), args, complain, in_decl),
tsubst (TREE_OPERAND (d, 0), args, complain, in_decl));
}
else
expand_start_catch_block (NULL_TREE, NULL_TREE);
finish_handler_parms (NULL_TREE);
tsubst_expr (HANDLER_BODY (t), args, complain, in_decl);
finish_handler (NULL_TREE);
break;
case TAG_DEFN:
lineno = TREE_COMPLEXITY (t);
t = TREE_TYPE (t);
if (TREE_CODE (t) == ENUMERAL_TYPE)
tsubst (t, args, complain, NULL_TREE);
break;
default:
return build_expr_from_tree (tsubst_copy (t, args, complain, in_decl));
}
return NULL_TREE;
}
/* Instantiate the indicated variable or function template TMPL with
the template arguments in TARG_PTR. */
tree
instantiate_template (tmpl, targ_ptr)
tree tmpl, targ_ptr;
{
tree fndecl;
tree gen_tmpl;
tree spec;
int i, len;
struct obstack *old_fmp_obstack;
extern struct obstack *function_maybepermanent_obstack;
tree inner_args;
if (tmpl == error_mark_node)
return error_mark_node;
my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 283);
/* Check to see if we already have this specialization. */
spec = retrieve_specialization (tmpl, targ_ptr);
if (spec != NULL_TREE)
return spec;
if (DECL_TEMPLATE_INFO (tmpl))
{
/* The TMPL is a partial instantiation. To get a full set of
arguments we must add the arguments used to perform the
partial instantiation. */
targ_ptr = add_outermost_template_args (DECL_TI_ARGS (tmpl),
targ_ptr);
gen_tmpl = most_general_template (tmpl);
/* Check to see if we already have this specialization. */
spec = retrieve_specialization (gen_tmpl, targ_ptr);
if (spec != NULL_TREE)
return spec;
}
else
gen_tmpl = tmpl;
push_obstacks (&permanent_obstack, &permanent_obstack);
old_fmp_obstack = function_maybepermanent_obstack;
function_maybepermanent_obstack = &permanent_obstack;
len = DECL_NTPARMS (gen_tmpl);
inner_args = innermost_args (targ_ptr);
i = len;
while (i--)
{
tree t = TREE_VEC_ELT (inner_args, i);
if (TREE_CODE_CLASS (TREE_CODE (t)) == 't')
{
tree nt = target_type (t);
if (IS_AGGR_TYPE (nt) && decl_function_context (TYPE_MAIN_DECL (nt)))
{
cp_error ("type `%T' composed from a local class is not a valid template-argument", t);
cp_error (" trying to instantiate `%D'", gen_tmpl);
fndecl = error_mark_node;
goto out;
}
}
}
targ_ptr = copy_to_permanent (targ_ptr);
/* substitute template parameters */
fndecl = tsubst (DECL_RESULT (gen_tmpl), targ_ptr, /*complain=*/1, gen_tmpl);
/* The DECL_TI_TEMPLATE should always be the immediate parent
template, not the most general template. */
DECL_TI_TEMPLATE (fndecl) = tmpl;
if (flag_external_templates)
add_pending_template (fndecl);
out:
function_maybepermanent_obstack = old_fmp_obstack;
pop_obstacks ();
return fndecl;
}
/* Push the name of the class template into the scope of the instantiation. */
void
overload_template_name (type)
tree type;
{
tree id = DECL_NAME (CLASSTYPE_TI_TEMPLATE (type));
tree decl;
if (IDENTIFIER_CLASS_VALUE (id)
&& TREE_TYPE (IDENTIFIER_CLASS_VALUE (id)) == type)
return;
decl = build_decl (TYPE_DECL, id, type);
SET_DECL_ARTIFICIAL (decl);
pushdecl_class_level (decl);
}
/* The FN is a TEMPLATE_DECL for a function. The ARGS are the
arguments that are being used when calling it. TARGS is a vector
into which the deduced template arguments are placed.
Return zero for success, 2 for an incomplete match that doesn't resolve
all the types, and 1 for complete failure. An error message will be
printed only for an incomplete match.
If FN is a conversion operator, RETURN_TYPE is the type desired as
the result of the conversion operator.
TPARMS is a vector of template parameters.
The EXPLICIT_TARGS are explicit template arguments provided via a
template-id.
The parameter STRICT is one of:
DEDUCE_CALL:
We are deducing arguments for a function call, as in
[temp.deduct.call].
DEDUCE_CONV:
We are deducing arguments for a conversion function, as in
[temp.deduct.conv].
DEDUCE_EXACT:
We are deducing arguments when calculating the partial
ordering between specializations of function or class
templates, as in [temp.func.order] and [temp.class.order],
when doing an explicit instantiation as in [temp.explicit],
when determining an explicit specialization as in
[temp.expl.spec], or when taking the address of a function
template, as in [temp.deduct.funcaddr].
The other arguments are as for type_unification. */
int
fn_type_unification (fn, explicit_targs, targs, args, return_type,
strict)
tree fn, explicit_targs, targs, args, return_type;
unification_kind_t strict;
{
tree parms;
tree fntype;
my_friendly_assert (TREE_CODE (fn) == TEMPLATE_DECL, 0);
fntype = TREE_TYPE (fn);
if (explicit_targs)
{
/* [temp.deduct]
The specified template arguments must match the template
parameters in kind (i.e., type, nontype, template), and there
must not be more arguments than there are parameters;
otherwise type deduction fails.
Nontype arguments must match the types of the corresponding
nontype template parameters, or must be convertible to the
types of the corresponding nontype parameters as specified in
_temp.arg.nontype_, otherwise type deduction fails.
All references in the function type of the function template
to the corresponding template parameters are replaced by the
specified template argument values. If a substitution in a
template parameter or in the function type of the function
template results in an invalid type, type deduction fails. */
int i;
tree converted_args;
converted_args
= (coerce_template_parms (DECL_INNERMOST_TEMPLATE_PARMS (fn),
explicit_targs, NULL_TREE, /*complain=*/0,
/*require_all_arguments=*/0));
if (converted_args == error_mark_node)
return 1;
fntype = tsubst (fntype, converted_args, /*complain=*/0, NULL_TREE);
if (fntype == error_mark_node)
return 1;
/* Place the explicitly specified arguments in TARGS. */
for (i = 0; i < TREE_VEC_LENGTH (targs); i++)
TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (converted_args, i);
}
parms = TYPE_ARG_TYPES (fntype);
if (DECL_CONV_FN_P (fn))
{
/* This is a template conversion operator. Use the return types
as well as the argument types. We use it instead of 'this', since
we could be comparing conversions from different classes. */
parms = scratch_tree_cons (NULL_TREE, TREE_TYPE (fntype),
TREE_CHAIN (parms));
args = scratch_tree_cons (NULL_TREE, return_type, TREE_CHAIN (args));
}
/* We allow incomplete unification without an error message here
because the standard doesn't seem to explicitly prohibit it. Our
callers must be ready to deal with unification failures in any
event. */
return type_unification_real (DECL_INNERMOST_TEMPLATE_PARMS (fn),
targs, parms, args, /*subr=*/0,
strict, /*allow_incomplete*/1);
}
/* Adjust types before performing type deduction, as described in
[temp.deduct.call] and [temp.deduct.conv]. The rules in these two
sections are symmetric. PARM is the type of a function parameter
or the return type of the conversion function. ARG is the type of
the argument passed to the call, or the type of the value
intialized with the result of the conversion function. */
static void
maybe_adjust_types_for_deduction (strict, parm, arg)
unification_kind_t strict;
tree* parm;
tree* arg;
{
switch (strict)
{
case DEDUCE_CALL:
break;
case DEDUCE_CONV:
{
/* Swap PARM and ARG throughout the remainder of this
function; the handling is precisely symmetric since PARM
will initialize ARG rather than vice versa. */
tree* temp = parm;
parm = arg;
arg = temp;
break;
}
case DEDUCE_EXACT:
/* There is nothing to do in this case. */
return;
default:
my_friendly_abort (0);
}
if (TREE_CODE (*parm) != REFERENCE_TYPE)
{
/* [temp.deduct.call]
If P is not a reference type:
--If A is an array type, the pointer type produced by the
array-to-pointer standard conversion (_conv.array_) is
used in place of A for type deduction; otherwise,
--If A is a function type, the pointer type produced by
the function-to-pointer standard conversion
(_conv.func_) is used in place of A for type deduction;
otherwise,
--If A is a cv-qualified type, the top level
cv-qualifiers of A's type are ignored for type
deduction. */
if (TREE_CODE (*arg) == ARRAY_TYPE)
*arg = build_pointer_type (TREE_TYPE (*arg));
else if (TREE_CODE (*arg) == FUNCTION_TYPE)
*arg = build_pointer_type (*arg);
else
*arg = TYPE_MAIN_VARIANT (*arg);
}
/* [temp.deduct.call]
If P is a cv-qualified type, the top level cv-qualifiers
of P's type are ignored for type deduction. If P is a
reference type, the type referred to by P is used for
type deduction. */
*parm = TYPE_MAIN_VARIANT (*parm);
if (TREE_CODE (*parm) == REFERENCE_TYPE)
*parm = TREE_TYPE (*parm);
}
/* Like type_unfication.
If SUBR is 1, we're being called recursively (to unify the
arguments of a function or method parameter of a function
template). */
static int
type_unification_real (tparms, targs, parms, args, subr,
strict, allow_incomplete)
tree tparms, targs, parms, args;
int subr;
unification_kind_t strict;
int allow_incomplete;
{
tree parm, arg;
int i;
int ntparms = TREE_VEC_LENGTH (tparms);
int sub_strict;
my_friendly_assert (TREE_CODE (tparms) == TREE_VEC, 289);
my_friendly_assert (parms == NULL_TREE
|| TREE_CODE (parms) == TREE_LIST, 290);
/* ARGS could be NULL (via a call from parse.y to
build_x_function_call). */
if (args)
my_friendly_assert (TREE_CODE (args) == TREE_LIST, 291);
my_friendly_assert (ntparms > 0, 292);
switch (strict)
{
case DEDUCE_CALL:
sub_strict = UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_DERIVED;
break;
case DEDUCE_CONV:
sub_strict = UNIFY_ALLOW_LESS_CV_QUAL;
break;
case DEDUCE_EXACT:
sub_strict = UNIFY_ALLOW_NONE;
break;
default:
my_friendly_abort (0);
}
while (parms
&& parms != void_list_node
&& args
&& args != void_list_node)
{
parm = TREE_VALUE (parms);
parms = TREE_CHAIN (parms);
arg = TREE_VALUE (args);
args = TREE_CHAIN (args);
if (arg == error_mark_node)
return 1;
if (arg == unknown_type_node)
/* We can't deduce anything from this, but we might get all the
template args from other function args. */
continue;
/* Conversions will be performed on a function argument that
corresponds with a function parameter that contains only
non-deducible template parameters and explicitly specified
template parameters. */
if (! uses_template_parms (parm))
{
tree type;
if (TREE_CODE_CLASS (TREE_CODE (arg)) != 't')
type = TREE_TYPE (arg);
else
{
type = arg;
arg = NULL_TREE;
}
if (strict == DEDUCE_EXACT)
{
if (same_type_p (parm, type))
continue;
}
else
/* It might work; we shouldn't check now, because we might
get into infinite recursion. Overload resolution will
handle it. */
continue;
return 1;
}
if (TREE_CODE_CLASS (TREE_CODE (arg)) != 't')
{
my_friendly_assert (TREE_TYPE (arg) != NULL_TREE, 293);
if (type_unknown_p (arg))
{
/* [temp.deduct.type] A template-argument can be deduced from
a pointer to function or pointer to member function
argument if the set of overloaded functions does not
contain function templates and at most one of a set of
overloaded functions provides a unique match. */
if (resolve_overloaded_unification
(tparms, targs, parm, arg, strict, sub_strict)
!= 0)
return 1;
continue;
}
arg = TREE_TYPE (arg);
}
if (!subr)
maybe_adjust_types_for_deduction (strict, &parm, &arg);
switch (unify (tparms, targs, parm, arg, sub_strict))
{
case 0:
break;
case 1:
return 1;
}
}
/* Fail if we've reached the end of the parm list, and more args
are present, and the parm list isn't variadic. */
if (args && args != void_list_node && parms == void_list_node)
return 1;
/* Fail if parms are left and they don't have default values. */
if (parms
&& parms != void_list_node
&& TREE_PURPOSE (parms) == NULL_TREE)
return 1;
if (!subr)
for (i = 0; i < ntparms; i++)
if (TREE_VEC_ELT (targs, i) == NULL_TREE)
{
if (!allow_incomplete)
error ("incomplete type unification");
return 2;
}
return 0;
}
/* Subroutine of type_unification_real. Args are like the variables at the
call site. ARG is an overloaded function (or template-id); we try
deducing template args from each of the overloads, and if only one
succeeds, we go with that. Modifies TARGS and returns 0 on success. */
static int
resolve_overloaded_unification (tparms, targs, parm, arg, strict,
sub_strict)
tree tparms, targs, parm, arg;
unification_kind_t strict;
int sub_strict;
{
tree tempargs = copy_node (targs);
int good = 0;
if (TREE_CODE (arg) == ADDR_EXPR)
arg = TREE_OPERAND (arg, 0);
if (TREE_CODE (arg) == COMPONENT_REF)
/* Handle `&x' where `x' is some static or non-static member
function name. */
arg = TREE_OPERAND (arg, 1);
if (TREE_CODE (arg) == OFFSET_REF)
arg = TREE_OPERAND (arg, 1);
/* Strip baselink information. */
while (TREE_CODE (arg) == TREE_LIST)
arg = TREE_VALUE (arg);
if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
{
/* If we got some explicit template args, we need to plug them into
the affected templates before we try to unify, in case the
explicit args will completely resolve the templates in question. */
tree expl_subargs = TREE_OPERAND (arg, 1);
arg = TREE_OPERAND (arg, 0);
for (; arg; arg = OVL_NEXT (arg))
{
tree fn = OVL_CURRENT (arg);
tree subargs, elem;
if (TREE_CODE (fn) != TEMPLATE_DECL)
continue;
subargs = get_bindings_overload (fn, DECL_RESULT (fn), expl_subargs);
if (subargs)
{
elem = tsubst (TREE_TYPE (fn), subargs, /*complain=*/0,
NULL_TREE);
if (TREE_CODE (elem) == METHOD_TYPE)
elem = build_ptrmemfunc_type (build_pointer_type (elem));
good += try_one_overload (tparms, targs, tempargs, parm, elem,
strict, sub_strict);
}
}
}
else if (TREE_CODE (arg) == OVERLOAD)
{
for (; arg; arg = OVL_NEXT (arg))
{
tree type = TREE_TYPE (OVL_CURRENT (arg));
if (TREE_CODE (type) == METHOD_TYPE)
type = build_ptrmemfunc_type (build_pointer_type (type));
good += try_one_overload (tparms, targs, tempargs, parm,
type,
strict, sub_strict);
}
}
else
my_friendly_abort (981006);
/* [temp.deduct.type] A template-argument can be deduced from a pointer
to function or pointer to member function argument if the set of
overloaded functions does not contain function templates and at most
one of a set of overloaded functions provides a unique match.
So if we found multiple possibilities, we return success but don't
deduce anything. */
if (good == 1)
{
int i = TREE_VEC_LENGTH (targs);
for (; i--; )
if (TREE_VEC_ELT (tempargs, i))
TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (tempargs, i);
}
if (good)
return 0;
return 1;
}
/* Subroutine of resolve_overloaded_unification; does deduction for a single
overload. Fills TARGS with any deduced arguments, or error_mark_node if
different overloads deduce different arguments for a given parm.
Returns 1 on success. */
static int
try_one_overload (tparms, orig_targs, targs, parm, arg, strict,
sub_strict)
tree tparms, orig_targs, targs, parm, arg;
unification_kind_t strict;
int sub_strict;
{
int nargs;
tree tempargs;
int i;
/* [temp.deduct.type] A template-argument can be deduced from a pointer
to function or pointer to member function argument if the set of
overloaded functions does not contain function templates and at most
one of a set of overloaded functions provides a unique match.
So if this is a template, just return success. */
if (uses_template_parms (arg))
return 1;
maybe_adjust_types_for_deduction (strict, &parm, &arg);
/* We don't copy orig_targs for this because if we have already deduced
some template args from previous args, unify would complain when we
try to deduce a template parameter for the same argument, even though
there isn't really a conflict. */
nargs = TREE_VEC_LENGTH (targs);
tempargs = make_scratch_vec (nargs);
if (unify (tparms, tempargs, parm, arg, sub_strict) != 0)
return 0;
/* First make sure we didn't deduce anything that conflicts with
explicitly specified args. */
for (i = nargs; i--; )
{
tree elt = TREE_VEC_ELT (tempargs, i);
tree oldelt = TREE_VEC_ELT (orig_targs, i);
if (elt == NULL_TREE)
continue;
else if (uses_template_parms (elt))
{
/* Since we're unifying against ourselves, we will fill in template
args used in the function parm list with our own template parms.
Discard them. */
TREE_VEC_ELT (tempargs, i) = NULL_TREE;
continue;
}
else if (oldelt && ! template_args_equal (oldelt, elt))
return 0;
}
for (i = nargs; i--; )
{
tree elt = TREE_VEC_ELT (tempargs, i);
if (elt)
TREE_VEC_ELT (targs, i) = elt;
}
return 1;
}
/* PARM is a template class (perhaps with unbound template
parameters). ARG is a fully instantiated type. If ARG can be
bound to PARM, return ARG, otherwise return NULL_TREE. TPARMS and
TARGS are as for unify. */
static tree
try_class_unification (tparms, targs, parm, arg)
tree tparms;
tree targs;
tree parm;
tree arg;
{
int i;
tree copy_of_targs;
if (!CLASSTYPE_TEMPLATE_INFO (arg)
|| CLASSTYPE_TI_TEMPLATE (arg) != CLASSTYPE_TI_TEMPLATE (parm))
return NULL_TREE;
/* We need to make a new template argument vector for the call to
unify. If we used TARGS, we'd clutter it up with the result of
the attempted unification, even if this class didn't work out.
We also don't want to commit ourselves to all the unifications
we've already done, since unification is supposed to be done on
an argument-by-argument basis. In other words, consider the
following pathological case:
template <int I, int J, int K>
struct S {};
template <int I, int J>
struct S<I, J, 2> : public S<I, I, I>, S<J, J, J> {};
template <int I, int J, int K>
void f(S<I, J, K>, S<I, I, I>);
void g() {
S<0, 0, 0> s0;
S<0, 1, 2> s2;
f(s0, s2);
}
Now, by the time we consider the unification involving `s2', we
already know that we must have `f<0, 0, 0>'. But, even though
`S<0, 1, 2>' is derived from `S<0, 0, 0>', the code is not legal
because there are two ways to unify base classes of S<0, 1, 2>
with S<I, I, I>. If we kept the already deduced knowledge, we
would reject the possibility I=1. */
push_momentary ();
copy_of_targs = make_temp_vec (TREE_VEC_LENGTH (targs));
i = unify (tparms, copy_of_targs, CLASSTYPE_TI_ARGS (parm),
CLASSTYPE_TI_ARGS (arg), UNIFY_ALLOW_NONE);
pop_momentary ();
/* If unification failed, we're done. */
if (i != 0)
return NULL_TREE;
else
return arg;
}
/* Subroutine of get_template_base. RVAL, if non-NULL, is a base we
have alreay discovered to be satisfactory. ARG_BINFO is the binfo
for the base class of ARG that we are currently examining. */
static tree
get_template_base_recursive (tparms, targs, parm,
arg_binfo, rval, flags)
tree tparms;
tree targs;
tree arg_binfo;
tree rval;
tree parm;
int flags;
{
tree binfos;
int i, n_baselinks;
tree arg = BINFO_TYPE (arg_binfo);
if (!(flags & GTB_IGNORE_TYPE))
{
tree r = try_class_unification (tparms, targs,
parm, arg);
/* If there is more than one satisfactory baseclass, then:
[temp.deduct.call]
If they yield more than one possible deduced A, the type
deduction fails.
applies. */
if (r && rval && !same_type_p (r, rval))
return error_mark_node;
else if (r)
rval = r;
}
binfos = BINFO_BASETYPES (arg_binfo);
n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
/* Process base types. */
for (i = 0; i < n_baselinks; i++)
{
tree base_binfo = TREE_VEC_ELT (binfos, i);
int this_virtual;
/* Skip this base, if we've already seen it. */
if (BINFO_MARKED (base_binfo))
continue;
this_virtual =
(flags & GTB_VIA_VIRTUAL) || TREE_VIA_VIRTUAL (base_binfo);
/* When searching for a non-virtual, we cannot mark virtually
found binfos. */
if (! this_virtual)
SET_BINFO_MARKED (base_binfo);
rval = get_template_base_recursive (tparms, targs,
parm,
base_binfo,
rval,
GTB_VIA_VIRTUAL * this_virtual);
/* If we discovered more than one matching base class, we can
stop now. */
if (rval == error_mark_node)
return error_mark_node;
}
return rval;
}
/* Given a template type PARM and a class type ARG, find the unique
base type in ARG that is an instance of PARM. We do not examine
ARG itself; only its base-classes. If there is no appropriate base
class, return NULL_TREE. If there is more than one, return
error_mark_node. PARM may be the type of a partial specialization,
as well as a plain template type. Used by unify. */
static tree
get_template_base (tparms, targs, parm, arg)
tree tparms;
tree targs;
tree parm;
tree arg;
{
tree rval;
tree arg_binfo;
my_friendly_assert (IS_AGGR_TYPE_CODE (TREE_CODE (arg)), 92);
arg_binfo = TYPE_BINFO (complete_type (arg));
rval = get_template_base_recursive (tparms, targs,
parm, arg_binfo,
NULL_TREE,
GTB_IGNORE_TYPE);
/* Since get_template_base_recursive marks the bases classes, we
must unmark them here. */
dfs_walk (arg_binfo, dfs_unmark, markedp, 0);
return rval;
}
/* Returns the level of DECL, which declares a template parameter. */
static int
template_decl_level (decl)
tree decl;
{
switch (TREE_CODE (decl))
{
case TYPE_DECL:
case TEMPLATE_DECL:
return TEMPLATE_TYPE_LEVEL (TREE_TYPE (decl));
case PARM_DECL:
return TEMPLATE_PARM_LEVEL (DECL_INITIAL (decl));
default:
my_friendly_abort (0);
return 0;
}
}
/* Decide whether ARG can be unified with PARM, considering only the
cv-qualifiers of each type, given STRICT as documented for unify.
Returns non-zero iff the unification is OK on that basis.*/
static int
check_cv_quals_for_unify (strict, arg, parm)
int strict;
tree arg;
tree parm;
{
return !((!(strict & UNIFY_ALLOW_MORE_CV_QUAL)
&& !at_least_as_qualified_p (arg, parm))
|| (!(strict & UNIFY_ALLOW_LESS_CV_QUAL)
&& (!at_least_as_qualified_p (parm, arg))));
}
/* Takes parameters as for type_unification. Returns 0 if the
type deduction suceeds, 1 otherwise. The parameter STRICT is a
bitwise or of the following flags:
UNIFY_ALLOW_NONE:
Require an exact match between PARM and ARG.
UNIFY_ALLOW_MORE_CV_QUAL:
Allow the deduced ARG to be more cv-qualified than ARG.
UNIFY_ALLOW_LESS_CV_QUAL:
Allow the deduced ARG to be less cv-qualified than ARG.
UNIFY_ALLOW_DERIVED:
Allow the deduced ARG to be a template base class of ARG,
or a pointer to a template base class of the type pointed to by
ARG.
UNIFY_ALLOW_INTEGER:
Allow any integral type to be deduced. See the TEMPLATE_PARM_INDEX
case for more information. */
static int
unify (tparms, targs, parm, arg, strict)
tree tparms, targs, parm, arg;
int strict;
{
int idx;
tree targ;
tree tparm;
/* I don't think this will do the right thing with respect to types.
But the only case I've seen it in so far has been array bounds, where
signedness is the only information lost, and I think that will be
okay. */
while (TREE_CODE (parm) == NOP_EXPR)
parm = TREE_OPERAND (parm, 0);
if (arg == error_mark_node)
return 1;
if (arg == unknown_type_node)
/* We can't deduce anything from this, but we might get all the
template args from other function args. */
return 0;
/* If PARM uses template parameters, then we can't bail out here,
even if ARG == PARM, since we won't record unifications for the
template parameters. We might need them if we're trying to
figure out which of two things is more specialized. */
if (arg == parm && !uses_template_parms (parm))
return 0;
/* Immediately reject some pairs that won't unify because of
cv-qualification mismatches. */
if (TREE_CODE (arg) == TREE_CODE (parm)
&& TREE_CODE_CLASS (TREE_CODE (arg)) == 't'
/* We check the cv-qualifiers when unifying with template type
parameters below. We want to allow ARG `const T' to unify with
PARM `T' for example, when computing which of two templates
is more specialized, for example. */
&& TREE_CODE (arg) != TEMPLATE_TYPE_PARM
&& !check_cv_quals_for_unify (strict, arg, parm))
return 1;
switch (TREE_CODE (parm))
{
case TYPENAME_TYPE:
/* In a type which contains a nested-name-specifier, template
argument values cannot be deduced for template parameters used
within the nested-name-specifier. */
return 0;
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
tparm = TREE_VALUE (TREE_VEC_ELT (tparms, 0));
if (TEMPLATE_TYPE_LEVEL (parm)
!= template_decl_level (tparm))
/* The PARM is not one we're trying to unify. Just check
to see if it matches ARG. */
return (TREE_CODE (arg) == TREE_CODE (parm)
&& same_type_p (parm, arg)) ? 0 : 1;
idx = TEMPLATE_TYPE_IDX (parm);
targ = TREE_VEC_ELT (targs, idx);
tparm = TREE_VALUE (TREE_VEC_ELT (tparms, idx));
/* Check for mixed types and values. */
if ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& TREE_CODE (tparm) != TYPE_DECL)
|| (TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM
&& TREE_CODE (tparm) != TEMPLATE_DECL))
return 1;
if (TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM)
{
if (TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (parm))
{
/* We arrive here when PARM does not involve template
specialization. */
/* ARG must be constructed from a template class. */
if (TREE_CODE (arg) != RECORD_TYPE || !CLASSTYPE_TEMPLATE_INFO (arg))
return 1;
{
tree parmtmpl = TYPE_TI_TEMPLATE (parm);
tree parmvec = TYPE_TI_ARGS (parm);
tree argvec = CLASSTYPE_TI_ARGS (arg);
tree argtmplvec
= DECL_INNERMOST_TEMPLATE_PARMS (CLASSTYPE_TI_TEMPLATE (arg));
int i;
/* The parameter and argument roles have to be switched here
in order to handle default arguments properly. For example,
template<template <class> class TT> void f(TT<int>)
should be able to accept vector<int> which comes from
template <class T, class Allocator = allocator>
class vector. */
if (coerce_template_parms (argtmplvec, parmvec, parmtmpl, 0, 1)
== error_mark_node)
return 1;
/* Deduce arguments T, i from TT<T> or TT<i>.
We check each element of PARMVEC and ARGVEC individually
rather than the whole TREE_VEC since they can have
different number of elements. */
for (i = 0; i < TREE_VEC_LENGTH (parmvec); ++i)
{
tree t = TREE_VEC_ELT (parmvec, i);
if (unify (tparms, targs, t,
TREE_VEC_ELT (argvec, i),
UNIFY_ALLOW_NONE))
return 1;
}
}
arg = CLASSTYPE_TI_TEMPLATE (arg);
}
}
else
{
/* If PARM is `const T' and ARG is only `int', we don't have
a match unless we are allowing additional qualification.
If ARG is `const int' and PARM is just `T' that's OK;
that binds `const int' to `T'. */
if (!check_cv_quals_for_unify (strict | UNIFY_ALLOW_LESS_CV_QUAL,
arg, parm))
return 1;
/* Consider the case where ARG is `const volatile int' and
PARM is `const T'. Then, T should be `volatile int'. */
arg =
cp_build_qualified_type (arg,
CP_TYPE_QUALS (arg)
& ~CP_TYPE_QUALS (parm));
}
/* Simple cases: Value already set, does match or doesn't. */
if (targ != NULL_TREE && same_type_p (targ, arg))
return 0;
else if (targ)
return 1;
/* Make sure that ARG is not a variable-sized array. (Note that
were talking about variable-sized arrays (like `int[n]'),
rather than arrays of unknown size (like `int[]').) We'll
get very confused by such a type since the bound of the array
will not be computable in an instantiation. Besides, such
types are not allowed in ISO C++, so we can do as we please
here. */
if (TREE_CODE (arg) == ARRAY_TYPE
&& !uses_template_parms (arg)
&& (TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (arg)))
!= INTEGER_CST))
return 1;
TREE_VEC_ELT (targs, idx) = arg;
return 0;
case TEMPLATE_PARM_INDEX:
tparm = TREE_VALUE (TREE_VEC_ELT (tparms, 0));
if (TEMPLATE_PARM_LEVEL (parm)
!= template_decl_level (tparm))
/* The PARM is not one we're trying to unify. Just check
to see if it matches ARG. */
return (TREE_CODE (arg) == TREE_CODE (parm)
&& cp_tree_equal (parm, arg) > 0) ? 0 : 1;
idx = TEMPLATE_PARM_IDX (parm);
targ = TREE_VEC_ELT (targs, idx);
if (targ)
{
int i = (cp_tree_equal (targ, arg) > 0);
if (i == 1)
return 0;
else if (i == 0)
return 1;
else
my_friendly_abort (42);
}
/* [temp.deduct.type] If, in the declaration of a function template
with a non-type template-parameter, the non-type
template-parameter is used in an expression in the function
parameter-list and, if the corresponding template-argument is
deduced, the template-argument type shall match the type of the
template-parameter exactly, except that a template-argument
deduced from an array bound may be of any integral type. */
if (same_type_p (TREE_TYPE (arg), TREE_TYPE (parm)))
/* OK */;
else if ((strict & UNIFY_ALLOW_INTEGER)
&& (TREE_CODE (TREE_TYPE (parm)) == INTEGER_TYPE
|| TREE_CODE (TREE_TYPE (parm)) == BOOLEAN_TYPE))
/* OK */;
else
return 1;
TREE_VEC_ELT (targs, idx) = copy_to_permanent (arg);
return 0;
case POINTER_TYPE:
{
int sub_strict;
if (TREE_CODE (arg) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (arg))
return (unify (tparms, targs, parm,
TYPE_PTRMEMFUNC_FN_TYPE (arg), strict));
if (TREE_CODE (arg) != POINTER_TYPE)
return 1;
/* [temp.deduct.call]
A can be another pointer or pointer to member type that can
be converted to the deduced A via a qualification
conversion (_conv.qual_).
We pass down STRICT here rather than UNIFY_ALLOW_NONE.
This will allow for additional cv-qualification of the
pointed-to types if appropriate. In general, this is a bit
too generous; we are only supposed to allow qualification
conversions and this method will allow an ARG of char** and
a deduced ARG of const char**. However, overload
resolution will subsequently invalidate the candidate, so
this is probably OK. */
sub_strict = strict;
if (TREE_CODE (TREE_TYPE (arg)) != RECORD_TYPE
|| TYPE_PTRMEMFUNC_FLAG (TREE_TYPE (arg)))
/* The derived-to-base conversion only persists through one
level of pointers. */
sub_strict &= ~UNIFY_ALLOW_DERIVED;
return unify (tparms, targs, TREE_TYPE (parm), TREE_TYPE
(arg), sub_strict);
}
case REFERENCE_TYPE:
if (TREE_CODE (arg) != REFERENCE_TYPE)
return 1;
return unify (tparms, targs, TREE_TYPE (parm), TREE_TYPE (arg),
UNIFY_ALLOW_NONE);
case ARRAY_TYPE:
if (TREE_CODE (arg) != ARRAY_TYPE)
return 1;
if ((TYPE_DOMAIN (parm) == NULL_TREE)
!= (TYPE_DOMAIN (arg) == NULL_TREE))
return 1;
if (TYPE_DOMAIN (parm) != NULL_TREE
&& unify (tparms, targs, TYPE_DOMAIN (parm),
TYPE_DOMAIN (arg), UNIFY_ALLOW_NONE) != 0)
return 1;
return unify (tparms, targs, TREE_TYPE (parm), TREE_TYPE (arg),
UNIFY_ALLOW_NONE);
case REAL_TYPE:
case COMPLEX_TYPE:
case INTEGER_TYPE:
case BOOLEAN_TYPE:
case VOID_TYPE:
if (TREE_CODE (arg) != TREE_CODE (parm))
return 1;
if (TREE_CODE (parm) == INTEGER_TYPE
&& TREE_CODE (TYPE_MAX_VALUE (parm)) != INTEGER_CST)
{
if (TYPE_MIN_VALUE (parm) && TYPE_MIN_VALUE (arg)
&& unify (tparms, targs, TYPE_MIN_VALUE (parm),
TYPE_MIN_VALUE (arg), UNIFY_ALLOW_INTEGER))
return 1;
if (TYPE_MAX_VALUE (parm) && TYPE_MAX_VALUE (arg)
&& unify (tparms, targs, TYPE_MAX_VALUE (parm),
TYPE_MAX_VALUE (arg), UNIFY_ALLOW_INTEGER))
return 1;
}
/* We use the TYPE_MAIN_VARIANT since we have already
checked cv-qualification at the top of the
function. */
else if (!same_type_p (TYPE_MAIN_VARIANT (arg),
TYPE_MAIN_VARIANT (parm)))
return 1;
/* As far as unification is concerned, this wins. Later checks
will invalidate it if necessary. */
return 0;
/* Types INTEGER_CST and MINUS_EXPR can come from array bounds. */
/* Type INTEGER_CST can come from ordinary constant template args. */
case INTEGER_CST:
while (TREE_CODE (arg) == NOP_EXPR)
arg = TREE_OPERAND (arg, 0);
if (TREE_CODE (arg) != INTEGER_CST)
return 1;
return !tree_int_cst_equal (parm, arg);
case TREE_VEC:
{
int i;
if (TREE_CODE (arg) != TREE_VEC)
return 1;
if (TREE_VEC_LENGTH (parm) != TREE_VEC_LENGTH (arg))
return 1;
for (i = TREE_VEC_LENGTH (parm) - 1; i >= 0; i--)
if (unify (tparms, targs,
TREE_VEC_ELT (parm, i), TREE_VEC_ELT (arg, i),
UNIFY_ALLOW_NONE))
return 1;
return 0;
}
case RECORD_TYPE:
case UNION_TYPE:
if (TYPE_PTRMEMFUNC_FLAG (parm))
return unify (tparms, targs, TYPE_PTRMEMFUNC_FN_TYPE (parm),
arg, strict);
if (TREE_CODE (arg) != TREE_CODE (parm))
return 1;
if (CLASSTYPE_TEMPLATE_INFO (parm))
{
tree t = NULL_TREE;
if (strict & UNIFY_ALLOW_DERIVED)
{
/* First, we try to unify the PARM and ARG directly. */
t = try_class_unification (tparms, targs,
parm, arg);
if (!t)
{
/* Fallback to the special case allowed in
[temp.deduct.call]:
If P is a class, and P has the form
template-id, then A can be a derived class of
the deduced A. Likewise, if P is a pointer to
a class of the form template-id, A can be a
pointer to a derived class pointed to by the
deduced A. */
t = get_template_base (tparms, targs,
parm, arg);
if (! t || t == error_mark_node)
return 1;
}
}
else if (CLASSTYPE_TEMPLATE_INFO (arg)
&& (CLASSTYPE_TI_TEMPLATE (parm)
== CLASSTYPE_TI_TEMPLATE (arg)))
/* Perhaps PARM is something like S<U> and ARG is S<int>.
Then, we should unify `int' and `U'. */
t = arg;
else
/* There's no chance of unication succeeding. */
return 1;
return unify (tparms, targs, CLASSTYPE_TI_ARGS (parm),
CLASSTYPE_TI_ARGS (t), UNIFY_ALLOW_NONE);
}
else if (!same_type_p (TYPE_MAIN_VARIANT (parm),
TYPE_MAIN_VARIANT (arg)))
return 1;
return 0;
case METHOD_TYPE:
case FUNCTION_TYPE:
if (TREE_CODE (arg) != TREE_CODE (parm))
return 1;
if (unify (tparms, targs, TREE_TYPE (parm),
TREE_TYPE (arg), UNIFY_ALLOW_NONE))
return 1;
return type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm),
TYPE_ARG_TYPES (arg), 1,
DEDUCE_EXACT, 0);
case OFFSET_TYPE:
if (TREE_CODE (arg) != OFFSET_TYPE)
return 1;
if (unify (tparms, targs, TYPE_OFFSET_BASETYPE (parm),
TYPE_OFFSET_BASETYPE (arg), UNIFY_ALLOW_NONE))
return 1;
return unify (tparms, targs, TREE_TYPE (parm), TREE_TYPE (arg),
strict);
case CONST_DECL:
if (arg != decl_constant_value (parm))
return 1;
return 0;
case TEMPLATE_DECL:
/* Matched cases are handled by the ARG == PARM test above. */
return 1;
case MINUS_EXPR:
if (TREE_CODE (TREE_OPERAND (parm, 1)) == INTEGER_CST)
{
/* We handle this case specially, since it comes up with
arrays. In particular, something like:
template <int N> void f(int (&x)[N]);
Here, we are trying to unify the range type, which
looks like [0 ... (N - 1)]. */
tree t, t1, t2;
t1 = TREE_OPERAND (parm, 0);
t2 = TREE_OPERAND (parm, 1);
/* Should this be a regular fold? */
t = maybe_fold_nontype_arg (build (PLUS_EXPR,
integer_type_node,
arg, t2));
return unify (tparms, targs, t1, t, strict);
}
/* else fall through */
default:
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (parm))))
/* We're looking at an expression. This can happen with
something like:
template <int I>
void foo(S<I>, S<I + 2>);
This is a "nondeduced context":
[deduct.type]
The nondeduced contexts are:
--A type that is a template-id in which one or more of
the template-arguments is an expression that references
a template-parameter.
In these cases, we assume deduction succeeded, but don't
actually infer any unifications. */
return 0;
else
sorry ("use of `%s' in template type unification",
tree_code_name [(int) TREE_CODE (parm)]);
return 1;
}
}
/* Called if RESULT is explicitly instantiated, or is a member of an
explicitly instantiated class, or if using -frepo and the
instantiation of RESULT has been assigned to this file. */
void
mark_decl_instantiated (result, extern_p)
tree result;
int extern_p;
{
if (TREE_CODE (result) != FUNCTION_DECL)
/* The TREE_PUBLIC flag for function declarations will have been
set correctly by tsubst. */
TREE_PUBLIC (result) = 1;
if (! extern_p)
{
DECL_INTERFACE_KNOWN (result) = 1;
DECL_NOT_REALLY_EXTERN (result) = 1;
/* Always make artificials weak. */
if (DECL_ARTIFICIAL (result) && flag_weak)
comdat_linkage (result);
/* For WIN32 we also want to put explicit instantiations in
linkonce sections. */
else if (TREE_PUBLIC (result))
maybe_make_one_only (result);
}
else if (TREE_CODE (result) == FUNCTION_DECL)
mark_inline_for_output (result);
}
/* Given two function templates PAT1 and PAT2, and explicit template
arguments EXPLICIT_ARGS return:
1 if PAT1 is more specialized than PAT2 as described in [temp.func.order].
-1 if PAT2 is more specialized than PAT1.
0 if neither is more specialized. */
int
more_specialized (pat1, pat2, explicit_args)
tree pat1, pat2, explicit_args;
{
tree targs;
int winner = 0;
targs = get_bindings_overload (pat1, DECL_RESULT (pat2), explicit_args);
if (targs)
--winner;
targs = get_bindings_overload (pat2, DECL_RESULT (pat1), explicit_args);
if (targs)
++winner;
return winner;
}
/* Given two class template specialization list nodes PAT1 and PAT2, return:
1 if PAT1 is more specialized than PAT2 as described in [temp.class.order].
-1 if PAT2 is more specialized than PAT1.
0 if neither is more specialized. */
int
more_specialized_class (pat1, pat2)
tree pat1, pat2;
{
tree targs;
int winner = 0;
targs = get_class_bindings (TREE_VALUE (pat1), TREE_PURPOSE (pat1),
TREE_PURPOSE (pat2));
if (targs)
--winner;
targs = get_class_bindings (TREE_VALUE (pat2), TREE_PURPOSE (pat2),
TREE_PURPOSE (pat1));
if (targs)
++winner;
return winner;
}
/* Return the template arguments that will produce the function signature
DECL from the function template FN, with the explicit template
arguments EXPLICIT_ARGS. If CHECK_RETTYPE is 1, the return type must
also match. Return NULL_TREE if no satisfactory arguments could be
found. */
static tree
get_bindings_real (fn, decl, explicit_args, check_rettype)
tree fn, decl, explicit_args;
int check_rettype;
{
int ntparms = DECL_NTPARMS (fn);
tree targs = make_scratch_vec (ntparms);
tree decl_type;
tree decl_arg_types;
int i;
/* Substitute the explicit template arguments into the type of DECL.
The call to fn_type_unification will handle substitution into the
FN. */
decl_type = TREE_TYPE (decl);
if (explicit_args && uses_template_parms (decl_type))
{
tree tmpl;
tree converted_args;
if (DECL_TEMPLATE_INFO (decl))
tmpl = DECL_TI_TEMPLATE (decl);
else
/* We can get here for some illegal specializations. */
return NULL_TREE;
converted_args
= (coerce_template_parms (DECL_INNERMOST_TEMPLATE_PARMS (tmpl),
explicit_args, NULL_TREE,
/*complain=*/0,
/*require_all_arguments=*/0));
if (converted_args == error_mark_node)
return NULL_TREE;
decl_type = tsubst (decl_type, converted_args, /*complain=*/0,
NULL_TREE);
if (decl_type == error_mark_node)
return NULL_TREE;
}
/* If FN is a static member function, adjust the type of DECL
appropriately. */
decl_arg_types = TYPE_ARG_TYPES (decl_type);
if (DECL_STATIC_FUNCTION_P (fn)
&& DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
decl_arg_types = TREE_CHAIN (decl_arg_types);
i = fn_type_unification (fn, explicit_args, targs,
decl_arg_types,
TREE_TYPE (decl_type),
DEDUCE_EXACT);
if (i != 0)
return NULL_TREE;
if (check_rettype)
{
/* Check to see that the resulting return type is also OK. */
tree t = tsubst (TREE_TYPE (TREE_TYPE (fn)), targs,
/*complain=*/0, NULL_TREE);
if (!same_type_p (t, TREE_TYPE (TREE_TYPE (decl))))
return NULL_TREE;
}
return targs;
}
/* For most uses, we want to check the return type. */
tree
get_bindings (fn, decl, explicit_args)
tree fn, decl, explicit_args;
{
return get_bindings_real (fn, decl, explicit_args, 1);
}
/* But for more_specialized, we only care about the parameter types. */
static tree
get_bindings_overload (fn, decl, explicit_args)
tree fn, decl, explicit_args;
{
return get_bindings_real (fn, decl, explicit_args, 0);
}
/* Return the innermost template arguments that, when applied to a
template specialization whose innermost template parameters are
TPARMS, and whose specialization arguments are ARGS, yield the
ARGS.
For example, suppose we have:
template <class T, class U> struct S {};
template <class T> struct S<T*, int> {};
Then, suppose we want to get `S<double*, int>'. The TPARMS will be
{T}, the PARMS will be {T*, int} and the ARGS will be {double*,
int}. The resulting vector will be {double}, indicating that `T'
is bound to `double'. */
static tree
get_class_bindings (tparms, parms, args)
tree tparms, parms, args;
{
int i, ntparms = TREE_VEC_LENGTH (tparms);
tree vec = make_temp_vec (ntparms);
args = innermost_args (args);
if (unify (tparms, vec, parms, args, UNIFY_ALLOW_NONE))
return NULL_TREE;
for (i = 0; i < ntparms; ++i)
if (! TREE_VEC_ELT (vec, i))
return NULL_TREE;
return vec;
}
/* In INSTANTIATIONS is a list of <INSTANTIATION, TEMPLATE> pairs.
Pick the most specialized template, and return the corresponding
instantiation, or if there is no corresponding instantiation, the
template itself. EXPLICIT_ARGS is any template arguments explicity
mentioned in a template-id. If there is no most specialized
tempalte, error_mark_node is returned. If there are no templates
at all, NULL_TREE is returned. */
tree
most_specialized_instantiation (instantiations, explicit_args)
tree instantiations;
tree explicit_args;
{
tree fn, champ;
int fate;
if (!instantiations)
return NULL_TREE;
champ = instantiations;
for (fn = TREE_CHAIN (instantiations); fn; fn = TREE_CHAIN (fn))
{
fate = more_specialized (TREE_VALUE (champ),
TREE_VALUE (fn), explicit_args);
if (fate == 1)
;
else
{
if (fate == 0)
{
fn = TREE_CHAIN (fn);
if (! fn)
return error_mark_node;
}
champ = fn;
}
}
for (fn = instantiations; fn && fn != champ; fn = TREE_CHAIN (fn))
{
fate = more_specialized (TREE_VALUE (champ),
TREE_VALUE (fn), explicit_args);
if (fate != 1)
return error_mark_node;
}
return TREE_PURPOSE (champ) ? TREE_PURPOSE (champ) : TREE_VALUE (champ);
}
/* Return the most specialized of the list of templates in FNS that can
produce an instantiation matching DECL, given the explicit template
arguments EXPLICIT_ARGS. */
static tree
most_specialized (fns, decl, explicit_args)
tree fns, decl, explicit_args;
{
tree candidates = NULL_TREE;
tree fn, args;
for (fn = fns; fn; fn = TREE_CHAIN (fn))
{
tree candidate = TREE_VALUE (fn);
args = get_bindings (candidate, decl, explicit_args);
if (args)
candidates = scratch_tree_cons (NULL_TREE, candidate,
candidates);
}
return most_specialized_instantiation (candidates, explicit_args);
}
/* If DECL is a specialization of some template, return the most
general such template. For example, given:
template <class T> struct S { template <class U> void f(U); };
if TMPL is `template <class U> void S<int>::f(U)' this will return
the full template. This function will not trace past partial
specializations, however. For example, given in addition:
template <class T> struct S<T*> { template <class U> void f(U); };
if TMPL is `template <class U> void S<int*>::f(U)' this will return
`template <class T> template <class U> S<T*>::f(U)'. */
static tree
most_general_template (decl)
tree decl;
{
while (DECL_TEMPLATE_INFO (decl))
decl = DECL_TI_TEMPLATE (decl);
return decl;
}
/* Return the most specialized of the class template specializations
of TMPL which can produce an instantiation matching ARGS, or
error_mark_node if the choice is ambiguous. */
static tree
most_specialized_class (tmpl, args)
tree tmpl;
tree args;
{
tree list = NULL_TREE;
tree t;
tree champ;
int fate;
tmpl = most_general_template (tmpl);
for (t = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); t; t = TREE_CHAIN (t))
{
tree spec_args
= get_class_bindings (TREE_VALUE (t), TREE_PURPOSE (t), args);
if (spec_args)
{
list = decl_tree_cons (TREE_PURPOSE (t), TREE_VALUE (t), list);
TREE_TYPE (list) = TREE_TYPE (t);
}
}
if (! list)
return NULL_TREE;
t = list;
champ = t;
t = TREE_CHAIN (t);
for (; t; t = TREE_CHAIN (t))
{
fate = more_specialized_class (champ, t);
if (fate == 1)
;
else
{
if (fate == 0)
{
t = TREE_CHAIN (t);
if (! t)
return error_mark_node;
}
champ = t;
}
}
for (t = list; t && t != champ; t = TREE_CHAIN (t))
{
fate = more_specialized_class (champ, t);
if (fate != 1)
return error_mark_node;
}
return champ;
}
/* called from the parser. */
void
do_decl_instantiation (declspecs, declarator, storage)
tree declspecs, declarator, storage;
{
tree decl = grokdeclarator (declarator, declspecs, NORMAL, 0, NULL_TREE);
tree result = NULL_TREE;
int extern_p = 0;
if (! DECL_LANG_SPECIFIC (decl))
{
cp_error ("explicit instantiation of non-template `%#D'", decl);
return;
}
else if (TREE_CODE (decl) == VAR_DECL)
{
/* There is an asymmetry here in the way VAR_DECLs and
FUNCTION_DECLs are handled by grokdeclarator. In the case of
the latter, the DECL we get back will be marked as a
template instantiation, and the appropriate
DECL_TEMPLATE_INFO will be set up. This does not happen for
VAR_DECLs so we do the lookup here. Probably, grokdeclarator
should handle VAR_DECLs as it currently handles
FUNCTION_DECLs. */
result = lookup_field (DECL_CONTEXT (decl), DECL_NAME (decl), 0, 0);
if (result && TREE_CODE (result) != VAR_DECL)
{
cp_error ("no matching template for `%D' found", result);
return;
}
}
else if (TREE_CODE (decl) != FUNCTION_DECL)
{
cp_error ("explicit instantiation of `%#D'", decl);
return;
}
else
result = decl;
/* Check for various error cases. Note that if the explicit
instantiation is legal the RESULT will currently be marked as an
*implicit* instantiation; DECL_EXPLICIT_INSTANTIATION is not set
until we get here. */
if (DECL_TEMPLATE_SPECIALIZATION (result))
{
/* [temp.spec]
No program shall both explicitly instantiate and explicitly
specialize a template. */
cp_pedwarn ("explicit instantiation of `%#D' after", result);
cp_pedwarn_at ("explicit specialization here", result);
return;
}
else if (DECL_EXPLICIT_INSTANTIATION (result))
{
/* [temp.spec]
No program shall explicitly instantiate any template more
than once.
We check DECL_INTERFACE_KNOWN so as not to complain when the
first instantiation was `extern' and the second is not, and
EXTERN_P for the opposite case. */
- if (DECL_INTERFACE_KNOWN (result) && !extern_p)
+ if (DECL_INTERFACE_KNOWN (result) && !extern_p && !flag_use_repository)
cp_pedwarn ("duplicate explicit instantiation of `%#D'", result);
/* If we've already instantiated the template, just return now. */
if (DECL_INTERFACE_KNOWN (result))
return;
}
else if (!DECL_IMPLICIT_INSTANTIATION (result))
{
cp_error ("no matching template for `%D' found", result);
return;
}
else if (!DECL_TEMPLATE_INFO (result))
{
cp_pedwarn ("explicit instantiation of non-template `%#D'", result);
return;
}
if (flag_external_templates)
return;
if (storage == NULL_TREE)
;
else if (storage == ridpointers[(int) RID_EXTERN])
{
if (pedantic)
cp_pedwarn ("ANSI C++ forbids the use of `extern' on explicit instantiations");
extern_p = 1;
}
else
cp_error ("storage class `%D' applied to template instantiation",
storage);
SET_DECL_EXPLICIT_INSTANTIATION (result);
mark_decl_instantiated (result, extern_p);
repo_template_instantiated (result, extern_p);
if (! extern_p)
instantiate_decl (result);
}
void
mark_class_instantiated (t, extern_p)
tree t;
int extern_p;
{
SET_CLASSTYPE_EXPLICIT_INSTANTIATION (t);
SET_CLASSTYPE_INTERFACE_KNOWN (t);
CLASSTYPE_INTERFACE_ONLY (t) = extern_p;
CLASSTYPE_VTABLE_NEEDS_WRITING (t) = ! extern_p;
TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t)) = extern_p;
if (! extern_p)
{
CLASSTYPE_DEBUG_REQUESTED (t) = 1;
rest_of_type_compilation (t, 1);
}
}
void
do_type_instantiation (t, storage)
tree t, storage;
{
int extern_p = 0;
int nomem_p = 0;
int static_p = 0;
if (TREE_CODE (t) == TYPE_DECL)
t = TREE_TYPE (t);
if (! CLASS_TYPE_P (t) || ! CLASSTYPE_TEMPLATE_INFO (t))
{
cp_error ("explicit instantiation of non-template type `%T'", t);
return;
}
complete_type (t);
/* With -fexternal-templates, explicit instantiations are treated the same
as implicit ones. */
if (flag_external_templates)
return;
if (TYPE_SIZE (t) == NULL_TREE)
{
cp_error ("explicit instantiation of `%#T' before definition of template",
t);
return;
}
if (storage != NULL_TREE)
{
if (pedantic)
cp_pedwarn("ANSI C++ forbids the use of `%s' on explicit instantiations",
IDENTIFIER_POINTER (storage));
if (storage == ridpointers[(int) RID_INLINE])
nomem_p = 1;
else if (storage == ridpointers[(int) RID_EXTERN])
extern_p = 1;
else if (storage == ridpointers[(int) RID_STATIC])
static_p = 1;
else
{
cp_error ("storage class `%D' applied to template instantiation",
storage);
extern_p = 0;
}
}
if (CLASSTYPE_TEMPLATE_SPECIALIZATION (t))
{
/* [temp.spec]
No program shall both explicitly instantiate and explicitly
specialize a template. */
cp_error ("explicit instantiation of `%#T' after", t);
cp_error_at ("explicit specialization here", t);
return;
}
else if (CLASSTYPE_EXPLICIT_INSTANTIATION (t))
{
/* [temp.spec]
No program shall explicitly instantiate any template more
than once.
If CLASSTYPE_INTERFACE_ONLY, then the first explicit
instantiation was `extern', and if EXTERN_P then the second
is. Both cases are OK. */
- if (!CLASSTYPE_INTERFACE_ONLY (t) && !extern_p)
- cp_error ("duplicate explicit instantiation of `%#T'", t);
+ if (!CLASSTYPE_INTERFACE_ONLY (t) && !extern_p && !flag_use_repository)
+ cp_pedwarn ("duplicate explicit instantiation of `%#T'", t);
/* If we've already instantiated the template, just return now. */
if (!CLASSTYPE_INTERFACE_ONLY (t))
return;
}
mark_class_instantiated (t, extern_p);
repo_template_instantiated (t, extern_p);
if (nomem_p)
return;
{
tree tmp;
/* In contrast to implicit instantiation, where only the
declarations, and not the definitions, of members are
instantiated, we have here:
[temp.explicit]
The explicit instantiation of a class template specialization
implies the instantiation of all of its members not
previously explicitly specialized in the translation unit
containing the explicit instantiation.
Of course, we can't instantiate member template classes, since
we don't have any arguments for them. Note that the standard
is unclear on whether the instatiation of the members are
*explicit* instantiations or not. We choose to be generous,
and not set DECL_EXPLICIT_INSTANTIATION. Therefore, we allow
the explicit instantiation of a class where some of the members
have no definition in the current translation unit. */
if (! static_p)
for (tmp = TYPE_METHODS (t); tmp; tmp = TREE_CHAIN (tmp))
if (TREE_CODE (tmp) == FUNCTION_DECL
&& DECL_TEMPLATE_INSTANTIATION (tmp))
{
mark_decl_instantiated (tmp, extern_p);
repo_template_instantiated (tmp, extern_p);
if (! extern_p)
instantiate_decl (tmp);
}
for (tmp = TYPE_FIELDS (t); tmp; tmp = TREE_CHAIN (tmp))
if (TREE_CODE (tmp) == VAR_DECL && DECL_TEMPLATE_INSTANTIATION (tmp))
{
mark_decl_instantiated (tmp, extern_p);
repo_template_instantiated (tmp, extern_p);
if (! extern_p)
instantiate_decl (tmp);
}
for (tmp = CLASSTYPE_TAGS (t); tmp; tmp = TREE_CHAIN (tmp))
if (IS_AGGR_TYPE (TREE_VALUE (tmp))
&& !uses_template_parms (CLASSTYPE_TI_ARGS (TREE_VALUE (tmp))))
do_type_instantiation (TYPE_MAIN_DECL (TREE_VALUE (tmp)), storage);
}
}
/* Given a function DECL, which is a specialization of TMPL, modify
DECL to be a re-instantiation of TMPL with the same template
arguments. TMPL should be the template into which tsubst'ing
should occur for DECL, not the most general template.
One reason for doing this is a scenario like this:
template <class T>
void f(const T&, int i);
void g() { f(3, 7); }
template <class T>
void f(const T& t, const int i) { }
Note that when the template is first instantiated, with
instantiate_template, the resulting DECL will have no name for the
first parameter, and the wrong type for the second. So, when we go
to instantiate the DECL, we regenerate it. */
static void
regenerate_decl_from_template (decl, tmpl)
tree decl;
tree tmpl;
{
tree args;
tree code_pattern;
tree new_decl;
tree gen_tmpl;
int unregistered;
args = DECL_TI_ARGS (decl);
code_pattern = DECL_TEMPLATE_RESULT (tmpl);
/* Unregister the specialization so that when we tsubst we will not
just return DECL. We don't have to unregister DECL from TMPL
because if would only be registered there if it were a partial
instantiation of a specialization, which it isn't: it's a full
instantiation. */
gen_tmpl = most_general_template (tmpl);
unregistered = unregister_specialization (decl, gen_tmpl);
/* If the DECL was not unregistered then something peculiar is
happening: we created a specialization but did not call
register_specialization for it. */
my_friendly_assert (unregistered, 0);
if (TREE_CODE (decl) == VAR_DECL)
/* Make sure that we can see identifiers, and compute access
correctly, for the class members used in the declaration of
this static variable. */
pushclass (DECL_CONTEXT (decl), 2);
/* Do the substitution to get the new declaration. */
new_decl = tsubst (code_pattern, args, /*complain=*/1, NULL_TREE);
if (TREE_CODE (decl) == VAR_DECL)
{
/* Set up DECL_INITIAL, since tsubst doesn't. */
DECL_INITIAL (new_decl) =
tsubst_expr (DECL_INITIAL (code_pattern), args,
/*complain=*/1, DECL_TI_TEMPLATE (decl));
/* Pop the class context we pushed above. */
popclass ();
}
else if (TREE_CODE (decl) == FUNCTION_DECL)
{
/* Convince duplicate_decls to use the DECL_ARGUMENTS from the
new decl. */
DECL_INITIAL (new_decl) = error_mark_node;
/* And don't complain about a duplicate definition. */
DECL_INITIAL (decl) = NULL_TREE;
}
/* The immediate parent of the new template is still whatever it was
before, even though tsubst sets DECL_TI_TEMPLATE up as the most
general template. We also reset the DECL_ASSEMBLER_NAME since
tsubst always calculates the name as if the function in question
were really a template instance, and sometimes, with friend
functions, this is not so. See tsubst_friend_function for
details. */
DECL_TI_TEMPLATE (new_decl) = DECL_TI_TEMPLATE (decl);
DECL_ASSEMBLER_NAME (new_decl) = DECL_ASSEMBLER_NAME (decl);
DECL_RTL (new_decl) = DECL_RTL (decl);
/* Call duplicate decls to merge the old and new declarations. */
duplicate_decls (new_decl, decl);
/* Now, re-register the specialization. */
register_specialization (decl, gen_tmpl, args);
}
/* Produce the definition of D, a _DECL generated from a template. */
tree
instantiate_decl (d)
tree d;
{
tree tmpl = DECL_TI_TEMPLATE (d);
tree args = DECL_TI_ARGS (d);
tree td;
tree code_pattern;
tree spec;
tree gen_tmpl;
int nested = in_function_p ();
int pattern_defined;
int line = lineno;
char *file = input_filename;
/* This function should only be used to instantiate templates for
functions and static member variables. */
my_friendly_assert (TREE_CODE (d) == FUNCTION_DECL
|| TREE_CODE (d) == VAR_DECL, 0);
if (DECL_TEMPLATE_INSTANTIATED (d))
/* D has already been instantiated. It might seem reasonable to
check whether or not D is an explict instantiation, and, if so,
stop here. But when an explicit instantiation is deferred
until the end of the compilation, DECL_EXPLICIT_INSTANTIATION
is set, even though we still need to do the instantiation. */
return d;
/* If we already have a specialization of this declaration, then
there's no reason to instantiate it. Note that
retrieve_specialization gives us both instantiations and
specializations, so we must explicitly check
DECL_TEMPLATE_SPECIALIZATION. */
gen_tmpl = most_general_template (tmpl);
spec = retrieve_specialization (gen_tmpl, args);
if (spec != NULL_TREE && DECL_TEMPLATE_SPECIALIZATION (spec))
return spec;
/* This needs to happen before any tsubsting. */
if (! push_tinst_level (d))
return d;
/* Set TD to the template whose DECL_TEMPLATE_RESULT is the pattern
for the instantiation. This is not always the most general
template. Consider, for example:
template <class T>
struct S { template <class U> void f();
template <> void f<int>(); };
and an instantiation of S<double>::f<int>. We want TD to be the
specialization S<T>::f<int>, not the more general S<T>::f<U>. */
td = tmpl;
for (td = tmpl;
/* An instantiation cannot have a definition, so we need a
more general template. */
DECL_TEMPLATE_INSTANTIATION (td)
/* We must also deal with friend templates. Given:
template <class T> struct S {
template <class U> friend void f() {};
};
S<int>::f<U> say, is not an instantiation of S<T>::f<U>,
so far as the language is concerned, but that's still
where we get the pattern for the instantiation from. On
ther hand, if the definition comes outside the class, say:
template <class T> struct S {
template <class U> friend void f();
};
template <class U> friend void f() {}
we don't need to look any further. That's what the check for
DECL_INITIAL is for. */
|| (TREE_CODE (d) == FUNCTION_DECL
&& DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (td)
&& !DECL_INITIAL (DECL_TEMPLATE_RESULT (td)));
)
{
/* The present template, TD, should not be a definition. If it
were a definition, we should be using it! Note that we
cannot restructure the loop to just keep going until we find
a template with a definition, since that might go too far if
a specialization was declared, but not defined. */
my_friendly_assert (!(TREE_CODE (d) == VAR_DECL
&& !DECL_IN_AGGR_P (DECL_TEMPLATE_RESULT (td))),
0);
/* Fetch the more general template. */
td = DECL_TI_TEMPLATE (td);
}
code_pattern = DECL_TEMPLATE_RESULT (td);
if (TREE_CODE (d) == FUNCTION_DECL)
pattern_defined = (DECL_INITIAL (code_pattern) != NULL_TREE);
else
pattern_defined = ! DECL_IN_AGGR_P (code_pattern);
push_to_top_level ();
lineno = DECL_SOURCE_LINE (d);
input_filename = DECL_SOURCE_FILE (d);
if (pattern_defined)
{
repo_template_used (d);
if (flag_external_templates && ! DECL_INTERFACE_KNOWN (d))
{
if (flag_alt_external_templates)
{
if (interface_unknown)
warn_if_unknown_interface (d);
}
else if (DECL_INTERFACE_KNOWN (code_pattern))
{
DECL_INTERFACE_KNOWN (d) = 1;
DECL_NOT_REALLY_EXTERN (d) = ! DECL_EXTERNAL (code_pattern);
}
else
warn_if_unknown_interface (code_pattern);
}
if (at_eof)
import_export_decl (d);
}
/* Reject all external templates except inline functions. */
if (DECL_INTERFACE_KNOWN (d)
&& ! DECL_NOT_REALLY_EXTERN (d)
&& ! (TREE_CODE (d) == FUNCTION_DECL && DECL_INLINE (d)))
goto out;
if (TREE_CODE (d) == VAR_DECL
&& TREE_READONLY (d)
&& DECL_INITIAL (d) == NULL_TREE
&& DECL_INITIAL (code_pattern) != NULL_TREE)
/* We need to set up DECL_INITIAL regardless of pattern_defined if
the variable is a static const initialized in the class body. */;
else if (! pattern_defined
|| (! (TREE_CODE (d) == FUNCTION_DECL && DECL_INLINE (d) && nested)
&& ! at_eof))
{
/* Defer all templates except inline functions used in another
function. We restore the source position here because it's used
by add_pending_template. */
lineno = line;
input_filename = file;
if (at_eof && !pattern_defined
&& DECL_EXPLICIT_INSTANTIATION (d))
/* [temp.explicit]
The definition of a non-exported function template, a
non-exported member function template, or a non-exported
member function or static data member of a class template
shall be present in every translation unit in which it is
explicitly instantiated. */
cp_error ("explicit instantiation of `%D' but no definition available",
d);
add_pending_template (d);
goto out;
}
/* We're now committed to instantiating this template. Mark it as
instantiated so that recursive calls to instantiate_decl do not
try to instantiate it again. */
DECL_TEMPLATE_INSTANTIATED (d) = 1;
/* Regenerate the declaration in case the template has been modified
by a subsequent redeclaration. */
regenerate_decl_from_template (d, td);
/* We already set the file and line above. Reset them now in case
they changed as a result of calling regenerate_decl_from_template. */
lineno = DECL_SOURCE_LINE (d);
input_filename = DECL_SOURCE_FILE (d);
if (TREE_CODE (d) == VAR_DECL)
{
DECL_IN_AGGR_P (d) = 0;
if (DECL_INTERFACE_KNOWN (d))
DECL_EXTERNAL (d) = ! DECL_NOT_REALLY_EXTERN (d);
else
{
DECL_EXTERNAL (d) = 1;
DECL_NOT_REALLY_EXTERN (d) = 1;
}
cp_finish_decl (d, DECL_INITIAL (d), NULL_TREE, 0, 0);
}
else if (TREE_CODE (d) == FUNCTION_DECL)
{
tree t = DECL_SAVED_TREE (code_pattern);
start_function (NULL_TREE, d, NULL_TREE, 1);
store_parm_decls ();
if (t && TREE_CODE (t) == RETURN_INIT)
{
store_return_init
(TREE_OPERAND (t, 0),
tsubst_expr (TREE_OPERAND (t, 1), args, /*complain=*/1, tmpl));
t = TREE_CHAIN (t);
}
if (t && TREE_CODE (t) == CTOR_INITIALIZER)
{
current_member_init_list
= tsubst_expr_values (TREE_OPERAND (t, 0), args);
current_base_init_list
= tsubst_expr_values (TREE_OPERAND (t, 1), args);
t = TREE_CHAIN (t);
}
setup_vtbl_ptr ();
/* Always keep the BLOCK node associated with the outermost
pair of curly braces of a function. These are needed
for correct operation of dwarfout.c. */
keep_next_level ();
my_friendly_assert (TREE_CODE (t) == COMPOUND_STMT, 42);
tsubst_expr (t, args, /*complain=*/1, tmpl);
finish_function (lineno, 0, nested);
}
out:
lineno = line;
input_filename = file;
pop_from_top_level ();
pop_tinst_level ();
return d;
}
/* Run through the list of templates that we wish we could
instantiate, and instantiate any we can. */
int
instantiate_pending_templates ()
{
tree *t;
int instantiated_something = 0;
int reconsider;
do
{
reconsider = 0;
t = &pending_templates;
while (*t)
{
tree srcloc = TREE_PURPOSE (*t);
tree instantiation = TREE_VALUE (*t);
input_filename = SRCLOC_FILE (srcloc);
lineno = SRCLOC_LINE (srcloc);
if (TREE_CODE_CLASS (TREE_CODE (instantiation)) == 't')
{
tree fn;
if (!TYPE_SIZE (instantiation))
{
instantiate_class_template (instantiation);
if (CLASSTYPE_TEMPLATE_INSTANTIATION (instantiation))
for (fn = TYPE_METHODS (instantiation);
fn;
fn = TREE_CHAIN (fn))
if (! DECL_ARTIFICIAL (fn))
instantiate_decl (fn);
if (TYPE_SIZE (instantiation))
{
instantiated_something = 1;
reconsider = 1;
}
}
if (TYPE_SIZE (instantiation))
/* If INSTANTIATION has been instantiated, then we don't
need to consider it again in the future. */
*t = TREE_CHAIN (*t);
else
t = &TREE_CHAIN (*t);
}
else
{
if (DECL_TEMPLATE_INSTANTIATION (instantiation)
&& !DECL_TEMPLATE_INSTANTIATED (instantiation))
{
instantiation = instantiate_decl (instantiation);
if (DECL_TEMPLATE_INSTANTIATED (instantiation))
{
instantiated_something = 1;
reconsider = 1;
}
}
if (!DECL_TEMPLATE_INSTANTIATION (instantiation)
|| DECL_TEMPLATE_INSTANTIATED (instantiation))
/* If INSTANTIATION has been instantiated, then we don't
need to consider it again in the future. */
*t = TREE_CHAIN (*t);
else
t = &TREE_CHAIN (*t);
}
}
template_tail = t;
/* Go through the things that are template instantiations if we are
using guiding declarations. */
t = &maybe_templates;
while (*t)
{
tree template;
tree fn;
tree args;
fn = TREE_VALUE (*t);
if (DECL_INITIAL (fn))
/* If the FN is already defined, then it was either already
instantiated or, even though guiding declarations were
allowed, a non-template definition was provided. */
;
else
{
template = TREE_PURPOSE (*t);
args = get_bindings (template, fn, NULL_TREE);
fn = instantiate_template (template, args);
instantiate_decl (fn);
reconsider = 1;
}
/* Remove this entry from the chain. */
*t = TREE_CHAIN (*t);
}
maybe_template_tail = t;
}
while (reconsider);
return instantiated_something;
}
/* Substitute ARGVEC into T, which is a TREE_LIST. In particular, it
is an initializer list: the TREE_PURPOSEs are DECLs, and the
TREE_VALUEs are initializer values. Used by instantiate_decl. */
static tree
tsubst_expr_values (t, argvec)
tree t, argvec;
{
tree first = NULL_TREE;
tree *p = &first;
for (; t; t = TREE_CHAIN (t))
{
tree pur = tsubst_copy (TREE_PURPOSE (t), argvec,
/*complain=*/1, NULL_TREE);
tree val = tsubst_expr (TREE_VALUE (t), argvec, /*complain=*/1,
NULL_TREE);
*p = build_tree_list (pur, val);
p = &TREE_CHAIN (*p);
}
return first;
}
tree last_tree;
void
add_tree (t)
tree t;
{
last_tree = TREE_CHAIN (last_tree) = t;
}
void
begin_tree ()
{
saved_trees = tree_cons (NULL_TREE, last_tree, saved_trees);
last_tree = NULL_TREE;
}
void
end_tree ()
{
my_friendly_assert (saved_trees != NULL_TREE, 0);
last_tree = TREE_VALUE (saved_trees);
saved_trees = TREE_CHAIN (saved_trees);
}
/* D is an undefined function declaration in the presence of templates with
the same name, listed in FNS. If one of them can produce D as an
instantiation, remember this so we can instantiate it at EOF if D has
not been defined by that time. */
void
add_maybe_template (d, fns)
tree d, fns;
{
tree t;
if (DECL_MAYBE_TEMPLATE (d))
return;
t = most_specialized (fns, d, NULL_TREE);
if (! t)
return;
if (t == error_mark_node)
{
cp_error ("ambiguous template instantiation for `%D'", d);
return;
}
*maybe_template_tail = perm_tree_cons (t, d, NULL_TREE);
maybe_template_tail = &TREE_CHAIN (*maybe_template_tail);
DECL_MAYBE_TEMPLATE (d) = 1;
}
/* Set CURRENT_ACCESS_SPECIFIER based on the protection of DECL. */
static void
set_current_access_from_decl (decl)
tree decl;
{
if (TREE_PRIVATE (decl))
current_access_specifier = access_private_node;
else if (TREE_PROTECTED (decl))
current_access_specifier = access_protected_node;
else
current_access_specifier = access_public_node;
}
/* Instantiate an enumerated type. TAG is the template type, NEWTAG
is the instantiation (which should have been created with
start_enum) and ARGS are the template arguments to use. */
static void
tsubst_enum (tag, newtag, args)
tree tag;
tree newtag;
tree args;
{
tree e;
for (e = TYPE_VALUES (tag); e; e = TREE_CHAIN (e))
{
tree value;
tree elt;
/* Note that in a template enum, the TREE_VALUE is the
CONST_DECL, not the corresponding INTEGER_CST. */
value = tsubst_expr (DECL_INITIAL (TREE_VALUE (e)),
args, /*complain=*/1,
NULL_TREE);
/* Give this enumeration constant the correct access. */
set_current_access_from_decl (TREE_VALUE (e));
/* Actually build the enumerator itself. */
elt = build_enumerator (TREE_PURPOSE (e), value, newtag);
/* We save the enumerators we have built so far in the
TYPE_VALUES so that if the enumeration constants for
subsequent enumerators involve those for previous ones,
tsubst_copy will be able to find them. */
TREE_CHAIN (elt) = TYPE_VALUES (newtag);
TYPE_VALUES (newtag) = elt;
}
finish_enum (newtag);
}
/* Set the DECL_ASSEMBLER_NAME for DECL, which is a FUNCTION_DECL that
is either an instantiation or specialization of a template
function. */
static void
set_mangled_name_for_template_decl (decl)
tree decl;
{
tree saved_namespace;
tree context = NULL_TREE;
tree fn_type;
tree ret_type;
tree parm_types;
tree tparms;
tree targs;
tree tmpl;
int parm_depth;
my_friendly_assert (TREE_CODE (decl) == FUNCTION_DECL, 0);
my_friendly_assert (DECL_TEMPLATE_INFO (decl) != NULL_TREE, 0);
/* The names of template functions must be mangled so as to indicate
what template is being specialized with what template arguments.
For example, each of the following three functions must get
different mangled names:
void f(int);
template <> void f<7>(int);
template <> void f<8>(int); */
targs = DECL_TI_ARGS (decl);
if (uses_template_parms (targs))
/* This DECL is for a partial instantiation. There's no need to
mangle the name of such an entity. */
return;
tmpl = most_general_template (DECL_TI_TEMPLATE (decl));
tparms = DECL_TEMPLATE_PARMS (tmpl);
parm_depth = TMPL_PARMS_DEPTH (tparms);
/* There should be as many levels of arguments as there are levels
of parameters. */
my_friendly_assert (parm_depth == TMPL_ARGS_DEPTH (targs), 0);
/* We now compute the PARMS and RET_TYPE to give to
build_decl_overload_real. The PARMS and RET_TYPE are the
parameter and return types of the template, after all but the
innermost template arguments have been substituted, not the
parameter and return types of the function DECL. For example,
given:
template <class T> T f(T);
both PARMS and RET_TYPE should be `T' even if DECL is `int f(int)'.
A more subtle example is:
template <class T> struct S { template <class U> void f(T, U); }
Here, if DECL is `void S<int>::f(int, double)', PARMS should be
{int, U}. Thus, the args that we want to subsitute into the
return and parameter type for the function are those in TARGS,
with the innermost level omitted. */
fn_type = TREE_TYPE (tmpl);
if (DECL_STATIC_FUNCTION_P (decl))
context = DECL_CLASS_CONTEXT (decl);
if (parm_depth == 1)
/* No substitution is necessary. */
;
else
{
int i;
tree partial_args;
/* Replace the innermost level of the TARGS with NULL_TREEs to
let tsubst know not to subsitute for those parameters. */
partial_args = make_temp_vec (TREE_VEC_LENGTH (targs));
for (i = 1; i < TMPL_ARGS_DEPTH (targs); ++i)
SET_TMPL_ARGS_LEVEL (partial_args, i,
TMPL_ARGS_LEVEL (targs, i));
SET_TMPL_ARGS_LEVEL (partial_args,
TMPL_ARGS_DEPTH (targs),
make_temp_vec (DECL_NTPARMS (tmpl)));
/* Now, do the (partial) substitution to figure out the
appropriate function type. */
fn_type = tsubst (fn_type, partial_args, /*complain=*/1, NULL_TREE);
if (DECL_STATIC_FUNCTION_P (decl))
context = tsubst (context, partial_args, /*complain=*/1, NULL_TREE);
/* Substitute into the template parameters to obtain the real
innermost set of parameters. This step is important if the
innermost set of template parameters contains value
parameters whose types depend on outer template parameters. */
TREE_VEC_LENGTH (partial_args)--;
tparms = tsubst_template_parms (tparms, partial_args, /*complain=*/1);
}
/* Now, get the innermost parameters and arguments, and figure out
the parameter and return types. */
tparms = INNERMOST_TEMPLATE_PARMS (tparms);
targs = innermost_args (targs);
ret_type = TREE_TYPE (fn_type);
parm_types = TYPE_ARG_TYPES (fn_type);
/* For a static member function, we generate a fake `this' pointer,
for the purposes of mangling. This indicates of which class the
function is a member. Because of:
[class.static]
There shall not be a static and a nonstatic member function
with the same name and the same parameter types
we don't have to worry that this will result in a clash with a
non-static member function. */
if (DECL_STATIC_FUNCTION_P (decl))
parm_types = hash_tree_chain (build_pointer_type (context), parm_types);
/* There should be the same number of template parameters as
template arguments. */
my_friendly_assert (TREE_VEC_LENGTH (tparms) == TREE_VEC_LENGTH (targs),
0);
/* If the template is in a namespace, we need to put that into the
mangled name. Unfortunately, build_decl_overload_real does not
get the decl to mangle, so it relies on the current
namespace. Therefore, we set that here temporarily. */
my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd', 980702);
saved_namespace = current_namespace;
current_namespace = CP_DECL_CONTEXT (decl);
/* Actually set the DCL_ASSEMBLER_NAME. */
DECL_ASSEMBLER_NAME (decl)
= build_decl_overload_real (DECL_NAME (decl), parm_types, ret_type,
tparms, targs,
DECL_FUNCTION_MEMBER_P (decl)
+ DECL_CONSTRUCTOR_P (decl));
/* Restore the previously active namespace. */
current_namespace = saved_namespace;
}
Index: head/contrib/gcc/cppinit.c
===================================================================
--- head/contrib/gcc/cppinit.c (revision 52750)
+++ head/contrib/gcc/cppinit.c (revision 52751)
@@ -1,1778 +1,1779 @@
/* CPP Library.
Copyright (C) 1986, 87, 89, 92-98, 1999 Free Software Foundation, Inc.
Contributed by Per Bothner, 1994-95.
Based on CCCP program by Paul Rubin, June 1986
Adapted to ANSI C, Richard Stallman, Jan 1987
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#define FAKE_CONST
#include "cpplib.h"
#include "cpphash.h"
#include "output.h"
#include "prefix.h"
#include "intl.h"
/* XXX Should be in a header file. */
extern char *version_string;
/* Predefined symbols, built-in macros, and the default include path. */
#ifndef GET_ENV_PATH_LIST
#define GET_ENV_PATH_LIST(VAR,NAME) do { (VAR) = getenv (NAME); } while (0)
#endif
/* By default, colon separates directories in a path. */
#ifndef PATH_SEPARATOR
#define PATH_SEPARATOR ':'
#endif
#ifndef STANDARD_INCLUDE_DIR
#define STANDARD_INCLUDE_DIR "/usr/include"
#endif
/* We let tm.h override the types used here, to handle trivial differences
such as the choice of unsigned int or long unsigned int for size_t.
When machines start needing nontrivial differences in the size type,
it would be best to do something here to figure out automatically
from other information what type to use. */
/* The string value for __SIZE_TYPE__. */
#ifndef SIZE_TYPE
#define SIZE_TYPE "long unsigned int"
#endif
/* The string value for __PTRDIFF_TYPE__. */
#ifndef PTRDIFF_TYPE
#define PTRDIFF_TYPE "long int"
#endif
/* The string value for __WCHAR_TYPE__. */
#ifndef WCHAR_TYPE
#define WCHAR_TYPE "int"
#endif
#define CPP_WCHAR_TYPE(PFILE) \
(CPP_OPTIONS (PFILE)->cplusplus ? "__wchar_t" : WCHAR_TYPE)
/* The string value for __USER_LABEL_PREFIX__ */
#ifndef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX ""
#endif
/* The string value for __REGISTER_PREFIX__ */
#ifndef REGISTER_PREFIX
#define REGISTER_PREFIX ""
#endif
/* Suffix for object files, and known input-file extensions. */
static char *known_suffixes[] =
{
".c", ".C", ".s", ".S", ".m",
".cc", ".cxx", ".cpp", ".cp", ".c++",
NULL
};
#ifndef OBJECT_SUFFIX
# ifdef VMS
# define OBJECT_SUFFIX ".obj"
# else
# define OBJECT_SUFFIX ".o"
# endif
#endif
/* This is the default list of directories to search for include files.
It may be overridden by the various -I and -ixxx options.
#include "file" looks in the same directory as the current file,
then this list.
#include <file> just looks in this list.
All these directories are treated as `system' include directories
(they are not subject to pedantic warnings in some cases). */
static struct default_include
{
char *fname; /* The name of the directory. */
char *component; /* The component containing the directory
(see update_path in prefix.c) */
int cplusplus; /* Only look here if we're compiling C++. */
int cxx_aware; /* Includes in this directory don't need to
be wrapped in extern "C" when compiling
C++. This is not used anymore. */
}
include_defaults_array[]
#ifdef INCLUDE_DEFAULTS
= INCLUDE_DEFAULTS;
#else
= {
/* Pick up GNU C++ specific include files. */
{ GPLUSPLUS_INCLUDE_DIR, "G++", 1, 1 },
#ifdef CROSS_COMPILE
/* This is the dir for fixincludes. Put it just before
the files that we fix. */
{ GCC_INCLUDE_DIR, "GCC", 0, 0 },
/* For cross-compilation, this dir name is generated
automatically in Makefile.in. */
{ CROSS_INCLUDE_DIR, "GCC", 0, 0 },
#ifdef TOOL_INCLUDE_DIR
/* This is another place that the target system's headers might be. */
{ TOOL_INCLUDE_DIR, "BINUTILS", 0, 1 },
#endif
#else /* not CROSS_COMPILE */
#ifdef LOCAL_INCLUDE_DIR
/* This should be /usr/local/include and should come before
the fixincludes-fixed header files. */
{ LOCAL_INCLUDE_DIR, 0, 0, 1 },
#endif
#ifdef TOOL_INCLUDE_DIR
/* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here.
Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h. */
{ TOOL_INCLUDE_DIR, "BINUTILS", 0, 1 },
#endif
/* This is the dir for fixincludes. Put it just before
the files that we fix. */
{ GCC_INCLUDE_DIR, "GCC", 0, 0 },
/* Some systems have an extra dir of include files. */
#ifdef SYSTEM_INCLUDE_DIR
{ SYSTEM_INCLUDE_DIR, 0, 0, 0 },
#endif
#ifndef STANDARD_INCLUDE_COMPONENT
#define STANDARD_INCLUDE_COMPONENT 0
#endif
{ STANDARD_INCLUDE_DIR, STANDARD_INCLUDE_COMPONENT, 0, 0 },
#endif /* not CROSS_COMPILE */
{ 0, 0, 0, 0 }
};
#endif /* no INCLUDE_DEFAULTS */
/* Internal structures and prototypes. */
/* A `struct pending_option' remembers one -D, -A, -U, -include, or -imacros
switch. There are four lists: one for -D and -U, one for -A, one
for -include, one for -imacros. `undef' is set for -U, clear for
-D, ignored for the others.
(Future: add an equivalent of -U for -A) */
struct pending_option
{
struct pending_option *next;
char *arg;
int undef;
};
#ifdef __STDC__
#define APPEND(pend, list, elt) \
do { if (!(pend)->list##_head) (pend)->list##_head = (elt); \
else (pend)->list##_tail->next = (elt); \
(pend)->list##_tail = (elt); \
} while (0)
#else
#define APPEND(pend, list, elt) \
do { if (!(pend)->list/**/_head) (pend)->list/**/_head = (elt); \
else (pend)->list/**/_tail->next = (elt); \
(pend)->list/**/_tail = (elt); \
} while (0)
#endif
static void initialize_char_syntax PARAMS ((int));
static void print_help PARAMS ((void));
static void path_include PARAMS ((cpp_reader *,
struct cpp_pending *,
char *, int));
static void initialize_builtins PARAMS ((cpp_reader *));
static void append_include_chain PARAMS ((cpp_reader *,
struct cpp_pending *,
char *, int));
/* Last argument to append_include_chain: chain to use */
enum { QUOTE = 0, BRACKET, SYSTEM, AFTER };
/* If gcc is in use (stage2/stage3) we can make these tables initialized
data. */
-#if defined __GNUC__ && __GNUC__ >= 2
+#if defined __GNUC__ && (__GNUC__ > 2 \
+ || (__GNUC__ == 2 && __GNUC_MINOR__ > 6))
/* Table to tell if a character is legal as the second or later character
of a C identifier. */
U_CHAR is_idchar[256] =
{
['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,
['g'] = 1, ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1,
['m'] = 1, ['n'] = 1, ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1,
['s'] = 1, ['t'] = 1, ['u'] = 1, ['v'] = 1, ['w'] = 1, ['x'] = 1,
['y'] = 1, ['z'] = 1,
['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
['Y'] = 1, ['Z'] = 1,
['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1, ['5'] = 1, ['6'] = 1,
['7'] = 1, ['8'] = 1, ['9'] = 1, ['0'] = 1,
['_'] = 1,
};
/* Table to tell if a character is legal as the first character of
a C identifier. */
U_CHAR is_idstart[256] =
{
['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,
['g'] = 1, ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1,
['m'] = 1, ['n'] = 1, ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1,
['s'] = 1, ['t'] = 1, ['u'] = 1, ['v'] = 1, ['w'] = 1, ['x'] = 1,
['y'] = 1, ['z'] = 1,
['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
['Y'] = 1, ['Z'] = 1,
['_'] = 1,
};
/* Table to tell if a character is horizontal space.
\r is magical, so it is not in here. */
U_CHAR is_hor_space[256] =
{
[' '] = 1, ['\t'] = 1, ['\v'] = 1, ['\f'] = 1,
};
/* table to tell if a character is horizontal or vertical space. */
U_CHAR is_space[256] =
{
[' '] = 1, ['\t'] = 1, ['\v'] = 1, ['\f'] = 1, ['\n'] = 1,
};
/* Table to handle trigraph conversion, which occurs before all other
processing, everywhere in the file. (This is necessary since one
of the trigraphs encodes backslash.) Note it's off by default.
from to from to from to
?? = # ?? ) ] ?? ! |
?? ( [ ?? ' ^ ?? > }
?? / \ ?? < { ?? - ~
There is not a space between the ?? and the third char. I put spaces
there to avoid warnings when compiling this file. */
U_CHAR trigraph_table[256] =
{
['='] = '#', [')'] = ']', ['!'] = '|',
['('] = '[', ['\''] = '^', ['>'] = '}',
['/'] = '\\', ['<'] = '{', ['-'] = '~',
};
/* This function will be entirely removed soon. */
static inline void
initialize_char_syntax (dollar_in_ident)
int dollar_in_ident;
{
is_idchar['$'] = dollar_in_ident;
is_idstart['$'] = dollar_in_ident;
}
#else /* Not GCC. */
U_CHAR is_idchar[256] = { 0 };
U_CHAR is_idstart[256] = { 0 };
U_CHAR is_hor_space[256] = { 0 };
U_CHAR is_space[256] = { 0 };
U_CHAR trigraph_table[256] = { 0 };
/* Initialize syntactic classifications of characters. */
static void
initialize_char_syntax (dollar_in_ident)
int dollar_in_ident;
{
is_idstart['a'] = 1; is_idstart['b'] = 1; is_idstart['c'] = 1;
is_idstart['d'] = 1; is_idstart['e'] = 1; is_idstart['f'] = 1;
is_idstart['g'] = 1; is_idstart['h'] = 1; is_idstart['i'] = 1;
is_idstart['j'] = 1; is_idstart['k'] = 1; is_idstart['l'] = 1;
is_idstart['m'] = 1; is_idstart['n'] = 1; is_idstart['o'] = 1;
is_idstart['p'] = 1; is_idstart['q'] = 1; is_idstart['r'] = 1;
is_idstart['s'] = 1; is_idstart['t'] = 1; is_idstart['u'] = 1;
is_idstart['v'] = 1; is_idstart['w'] = 1; is_idstart['x'] = 1;
is_idstart['y'] = 1; is_idstart['z'] = 1;
is_idstart['A'] = 1; is_idstart['B'] = 1; is_idstart['C'] = 1;
is_idstart['D'] = 1; is_idstart['E'] = 1; is_idstart['F'] = 1;
is_idstart['G'] = 1; is_idstart['H'] = 1; is_idstart['I'] = 1;
is_idstart['J'] = 1; is_idstart['K'] = 1; is_idstart['L'] = 1;
is_idstart['M'] = 1; is_idstart['N'] = 1; is_idstart['O'] = 1;
is_idstart['P'] = 1; is_idstart['Q'] = 1; is_idstart['R'] = 1;
is_idstart['S'] = 1; is_idstart['T'] = 1; is_idstart['U'] = 1;
is_idstart['V'] = 1; is_idstart['W'] = 1; is_idstart['X'] = 1;
is_idstart['Y'] = 1; is_idstart['Z'] = 1;
is_idstart['_'] = 1;
is_idchar['a'] = 1; is_idchar['b'] = 1; is_idchar['c'] = 1;
is_idchar['d'] = 1; is_idchar['e'] = 1; is_idchar['f'] = 1;
is_idchar['g'] = 1; is_idchar['h'] = 1; is_idchar['i'] = 1;
is_idchar['j'] = 1; is_idchar['k'] = 1; is_idchar['l'] = 1;
is_idchar['m'] = 1; is_idchar['n'] = 1; is_idchar['o'] = 1;
is_idchar['p'] = 1; is_idchar['q'] = 1; is_idchar['r'] = 1;
is_idchar['s'] = 1; is_idchar['t'] = 1; is_idchar['u'] = 1;
is_idchar['v'] = 1; is_idchar['w'] = 1; is_idchar['x'] = 1;
is_idchar['y'] = 1; is_idchar['z'] = 1;
is_idchar['A'] = 1; is_idchar['B'] = 1; is_idchar['C'] = 1;
is_idchar['D'] = 1; is_idchar['E'] = 1; is_idchar['F'] = 1;
is_idchar['G'] = 1; is_idchar['H'] = 1; is_idchar['I'] = 1;
is_idchar['J'] = 1; is_idchar['K'] = 1; is_idchar['L'] = 1;
is_idchar['M'] = 1; is_idchar['N'] = 1; is_idchar['O'] = 1;
is_idchar['P'] = 1; is_idchar['Q'] = 1; is_idchar['R'] = 1;
is_idchar['S'] = 1; is_idchar['T'] = 1; is_idchar['U'] = 1;
is_idchar['V'] = 1; is_idchar['W'] = 1; is_idchar['X'] = 1;
is_idchar['Y'] = 1; is_idchar['Z'] = 1;
is_idchar['1'] = 1; is_idchar['2'] = 1; is_idchar['3'] = 1;
is_idchar['4'] = 1; is_idchar['5'] = 1; is_idchar['6'] = 1;
is_idchar['7'] = 1; is_idchar['8'] = 1; is_idchar['9'] = 1;
is_idchar['0'] = 1;
is_idchar['_'] = 1;
is_idchar['$'] = dollar_in_ident;
is_idstart['$'] = dollar_in_ident;
/* white space tables */
is_hor_space[' '] = 1;
is_hor_space['\t'] = 1;
is_hor_space['\v'] = 1;
is_hor_space['\f'] = 1;
is_space[' '] = 1;
is_space['\t'] = 1;
is_space['\v'] = 1;
is_space['\f'] = 1;
is_space['\n'] = 1;
/* trigraph conversion */
trigraph_table['='] = '#'; trigraph_table[')'] = ']';
trigraph_table['!'] = '|'; trigraph_table['('] = '[';
trigraph_table['\''] = '^'; trigraph_table['>'] = '}';
trigraph_table['/'] = '\\'; trigraph_table['<'] = '{';
trigraph_table['-'] = '~';
}
#endif /* Not GCC. */
/* Given a colon-separated list of file names PATH,
add all the names to the search path for include files. */
static void
path_include (pfile, pend, list, path)
cpp_reader *pfile;
struct cpp_pending *pend;
char *list;
int path;
{
char *p, *q, *name;
p = list;
do
{
/* Find the end of this name. */
q = p;
while (*q != 0 && *q != PATH_SEPARATOR) q++;
if (q == p)
{
/* An empty name in the path stands for the current directory. */
name = (char *) xmalloc (2);
name[0] = '.';
name[1] = 0;
}
else
{
/* Otherwise use the directory that is named. */
name = (char *) xmalloc (q - p + 1);
memcpy (name, p, q - p);
name[q - p] = 0;
}
append_include_chain (pfile, pend, name, path);
/* Advance past this name. */
if (*q == 0)
break;
p = q + 1;
}
while (1);
}
/* Find the base name of a (partial) pathname FNAME.
Returns a pointer into the string passed in.
Accepts Unix (/-separated) paths on all systems,
DOS and VMS paths on those systems. */
static char *
base_name (fname)
const char *fname;
{
char *s = (char *)fname;
char *p;
#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
if (ISALPHA (s[0]) && s[1] == ':') s += 2;
if ((p = rindex (s, '\\'))) s = p + 1;
#elif defined VMS
if ((p = rindex (s, ':'))) s = p + 1; /* Skip device. */
if ((p = rindex (s, ']'))) s = p + 1; /* Skip directory. */
if ((p = rindex (s, '>'))) s = p + 1; /* Skip alternate (int'n'l) dir. */
#endif
if ((p = rindex (s, '/'))) s = p + 1;
return s;
}
/* Append DIR to include path PATH. DIR must be permanently allocated
and writable. */
static void
append_include_chain (pfile, pend, dir, path)
cpp_reader *pfile;
struct cpp_pending *pend;
char *dir;
int path;
{
struct file_name_list *new;
struct stat st;
unsigned int len;
simplify_pathname (dir);
if (stat (dir, &st))
{
/* Dirs that don't exist are silently ignored. */
if (errno != ENOENT)
cpp_perror_with_name (pfile, dir);
else if (CPP_OPTIONS (pfile)->verbose)
cpp_notice ("ignoring nonexistent directory `%s'\n", dir);
return;
}
if (!S_ISDIR (st.st_mode))
{
cpp_message (pfile, 1, "%s: %s: Not a directory", progname, dir);
return;
}
len = strlen (dir);
if (len > pfile->max_include_len)
pfile->max_include_len = len;
new = (struct file_name_list *)xmalloc (sizeof (struct file_name_list));
new->name = dir;
new->nlen = len;
new->ino = st.st_ino;
new->dev = st.st_dev;
new->sysp = (path == SYSTEM);
new->name_map = NULL;
switch (path)
{
case QUOTE: APPEND (pend, quote, new); break;
case BRACKET: APPEND (pend, brack, new); break;
case SYSTEM: APPEND (pend, systm, new); break;
case AFTER: APPEND (pend, after, new); break;
}
}
/* Write out a #define command for the special named MACRO_NAME
to PFILE's token_buffer. */
static void
dump_special_to_buffer (pfile, macro_name)
cpp_reader *pfile;
char *macro_name;
{
static char define_directive[] = "#define ";
int macro_name_length = strlen (macro_name);
output_line_command (pfile, same_file);
CPP_RESERVE (pfile, sizeof(define_directive) + macro_name_length);
CPP_PUTS_Q (pfile, define_directive, sizeof(define_directive)-1);
CPP_PUTS_Q (pfile, macro_name, macro_name_length);
CPP_PUTC_Q (pfile, ' ');
cpp_expand_to_buffer (pfile, macro_name, macro_name_length);
CPP_PUTC (pfile, '\n');
}
/* Initialize a cpp_options structure. */
void
cpp_options_init (opts)
cpp_options *opts;
{
bzero ((char *) opts, sizeof (struct cpp_options));
opts->dollars_in_ident = 1;
opts->cplusplus_comments = 1;
opts->warn_import = 1;
opts->pending = (struct cpp_pending *) xmalloc (sizeof (struct cpp_pending));
bzero ((char *) opts->pending, sizeof (struct cpp_pending));
}
/* Initialize a cpp_reader structure. */
void
cpp_reader_init (pfile)
cpp_reader *pfile;
{
bzero ((char *) pfile, sizeof (cpp_reader));
#if 0
pfile->get_token = cpp_get_token;
#endif
pfile->token_buffer_size = 200;
pfile->token_buffer = (U_CHAR *) xmalloc (pfile->token_buffer_size);
CPP_SET_WRITTEN (pfile, 0);
pfile->hashtab = (HASHNODE **) xcalloc (HASHSIZE, sizeof (HASHNODE *));
}
/* Free resources used by PFILE.
This is the cpp_reader 'finalizer' or 'destructor' (in C++ terminology). */
void
cpp_cleanup (pfile)
cpp_reader *pfile;
{
int i;
while (CPP_BUFFER (pfile) != CPP_NULL_BUFFER (pfile))
cpp_pop_buffer (pfile);
if (pfile->token_buffer)
{
free (pfile->token_buffer);
pfile->token_buffer = NULL;
}
if (pfile->deps_buffer)
{
free (pfile->deps_buffer);
pfile->deps_buffer = NULL;
pfile->deps_allocated_size = 0;
}
if (pfile->input_buffer)
{
free (pfile->input_buffer);
free (pfile->input_speccase);
pfile->input_buffer = pfile->input_speccase = NULL;
pfile->input_buffer_len = 0;
}
while (pfile->if_stack)
{
IF_STACK_FRAME *temp = pfile->if_stack;
pfile->if_stack = temp->next;
free (temp);
}
for (i = ALL_INCLUDE_HASHSIZE; --i >= 0; )
{
struct include_hash *imp = pfile->all_include_files[i];
while (imp)
{
struct include_hash *next = imp->next;
#if 0
/* This gets freed elsewhere - I think. */
free (imp->name);
#endif
free (imp);
imp = next;
}
pfile->all_include_files[i] = 0;
}
for (i = HASHSIZE; --i >= 0;)
{
while (pfile->hashtab[i])
delete_macro (pfile->hashtab[i]);
}
free (pfile->hashtab);
}
/* Initialize the built-in macros. */
static void
initialize_builtins (pfile)
cpp_reader *pfile;
{
#define NAME(str) (U_CHAR *)str, sizeof str - 1
cpp_install (pfile, NAME("__TIME__"), T_TIME, 0, -1);
cpp_install (pfile, NAME("__DATE__"), T_DATE, 0, -1);
cpp_install (pfile, NAME("__FILE__"), T_FILE, 0, -1);
cpp_install (pfile, NAME("__BASE_FILE__"), T_BASE_FILE, 0, -1);
cpp_install (pfile, NAME("__LINE__"), T_SPECLINE, 0, -1);
cpp_install (pfile, NAME("__INCLUDE_LEVEL__"), T_INCLUDE_LEVEL, 0, -1);
cpp_install (pfile, NAME("__VERSION__"), T_VERSION, 0, -1);
#ifndef NO_BUILTIN_SIZE_TYPE
cpp_install (pfile, NAME("__SIZE_TYPE__"), T_CONST, SIZE_TYPE, -1);
#endif
#ifndef NO_BUILTIN_PTRDIFF_TYPE
cpp_install (pfile, NAME("__PTRDIFF_TYPE__ "), T_CONST, PTRDIFF_TYPE, -1);
#endif
cpp_install (pfile, NAME("__WCHAR_TYPE__"), T_CONST, WCHAR_TYPE, -1);
cpp_install (pfile, NAME("__USER_LABEL_PREFIX__"), T_CONST, user_label_prefix, -1);
cpp_install (pfile, NAME("__REGISTER_PREFIX__"), T_CONST, REGISTER_PREFIX, -1);
cpp_install (pfile, NAME("__HAVE_BUILTIN_SETJMP__"), T_CONST, "1", -1);
if (!CPP_TRADITIONAL (pfile))
{
cpp_install (pfile, NAME("__STDC__"), T_STDC, 0, -1);
#if 0
if (CPP_OPTIONS (pfile)->c9x)
cpp_install (pfile, NAME("__STDC_VERSION__"),T_CONST, "199909L", -1);
else
#endif
cpp_install (pfile, NAME("__STDC_VERSION__"),T_CONST, "199409L", -1);
}
#undef NAME
if (CPP_OPTIONS (pfile)->debug_output)
{
dump_special_to_buffer (pfile, "__BASE_FILE__");
dump_special_to_buffer (pfile, "__VERSION__");
#ifndef NO_BUILTIN_SIZE_TYPE
dump_special_to_buffer (pfile, "__SIZE_TYPE__");
#endif
#ifndef NO_BUILTIN_PTRDIFF_TYPE
dump_special_to_buffer (pfile, "__PTRDIFF_TYPE__");
#endif
dump_special_to_buffer (pfile, "__WCHAR_TYPE__");
dump_special_to_buffer (pfile, "__DATE__");
dump_special_to_buffer (pfile, "__TIME__");
if (!CPP_TRADITIONAL (pfile))
dump_special_to_buffer (pfile, "__STDC__");
}
}
/* Another subroutine of cpp_start_read. This one sets up to do
dependency-file output. */
static void
initialize_dependency_output (pfile)
cpp_reader *pfile;
{
cpp_options *opts = CPP_OPTIONS (pfile);
char *spec, *s, *output_file;
/* Either of two environment variables can specify output of deps.
Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
where OUTPUT_FILE is the file to write deps info to
and DEPS_TARGET is the target to mention in the deps. */
if (opts->print_deps == 0)
{
spec = getenv ("DEPENDENCIES_OUTPUT");
if (spec)
opts->print_deps = 1;
else
{
spec = getenv ("SUNPRO_DEPENDENCIES");
if (spec)
opts->print_deps = 2;
else
return;
}
/* Find the space before the DEPS_TARGET, if there is one. */
s = strchr (spec, ' ');
if (s)
{
opts->deps_target = s + 1;
output_file = (char *) xmalloc (s - spec + 1);
memcpy (output_file, spec, s - spec);
output_file[s - spec] = 0;
}
else
{
opts->deps_target = 0;
output_file = spec;
}
opts->deps_file = output_file;
opts->print_deps_append = 1;
}
/* Print the expected object file name as the target of this Make-rule. */
pfile->deps_allocated_size = 200;
pfile->deps_buffer = (char *) xmalloc (pfile->deps_allocated_size);
pfile->deps_buffer[0] = 0;
pfile->deps_size = 0;
pfile->deps_column = 0;
if (opts->deps_target)
deps_output (pfile, opts->deps_target, ':');
else if (*opts->in_fname == 0)
deps_output (pfile, "-", ':');
else
{
char *p, *q, *r;
int len, x;
/* Discard all directory prefixes from filename. */
q = base_name (opts->in_fname);
/* Copy remainder to mungable area. */
len = strlen (q);
p = (char *) alloca (len + 8);
strcpy (p, q);
/* Output P, but remove known suffixes. */
q = p + len;
/* Point to the filename suffix. */
r = rindex (p, '.');
/* Compare against the known suffixes. */
for (x = 0; known_suffixes[x]; x++)
{
if (strncmp (known_suffixes[x], r, q - r) == 0)
{
/* Make q point to the bit we're going to overwrite
with an object suffix. */
q = r;
break;
}
}
/* Supply our own suffix. */
strcpy (q, OBJECT_SUFFIX);
deps_output (pfile, p, ':');
deps_output (pfile, opts->in_fname, ' ');
}
}
/* This is called after options have been processed.
* Check options for consistency, and setup for processing input
* from the file named FNAME. (Use standard input if FNAME==NULL.)
* Return 1 on success, 0 on failure.
*/
int
cpp_start_read (pfile, fname)
cpp_reader *pfile;
char *fname;
{
struct cpp_options *opts = CPP_OPTIONS (pfile);
struct pending_option *p, *q;
int f;
cpp_buffer *fp;
struct include_hash *ih_fake;
/* -MG doesn't select the form of output and must be specified with one of
-M or -MM. -MG doesn't make sense with -MD or -MMD since they don't
inhibit compilation. */
if (opts->print_deps_missing_files
&& (opts->print_deps == 0 || !opts->no_output))
{
cpp_fatal (pfile, "-MG must be specified with one of -M or -MM");
return 0;
}
/* Chill should not be used with -trigraphs. */
if (opts->chill && opts->trigraphs)
{
cpp_warning (pfile, "-lang-chill and -trigraphs are mutually exclusive");
opts->trigraphs = 0;
}
/* Set this if it hasn't been set already. */
if (user_label_prefix == NULL)
user_label_prefix = USER_LABEL_PREFIX;
/* Now that we know dollars_in_ident, we can initialize the syntax
tables. */
initialize_char_syntax (opts->dollars_in_ident);
/* Do partial setup of input buffer for the sake of generating
early #line directives (when -g is in effect). */
fp = cpp_push_buffer (pfile, NULL, 0);
if (!fp)
return 0;
if (opts->in_fname == NULL || *opts->in_fname == 0)
{
opts->in_fname = fname;
if (opts->in_fname == NULL)
opts->in_fname = "";
}
fp->nominal_fname = fp->fname = opts->in_fname;
fp->lineno = 0;
/* Install __LINE__, etc. Must follow initialize_char_syntax
and option processing. */
initialize_builtins (pfile);
/* Do -U's, -D's and -A's in the order they were seen. */
p = opts->pending->define_head;
while (p)
{
if (opts->debug_output)
output_line_command (pfile, same_file);
if (p->undef)
cpp_undef (pfile, p->arg);
else
cpp_define (pfile, p->arg);
q = p->next;
free (p);
p = q;
}
p = opts->pending->assert_head;
while (p)
{
if (opts->debug_output)
output_line_command (pfile, same_file);
if (p->undef)
cpp_unassert (pfile, p->arg);
else
cpp_assert (pfile, p->arg);
q = p->next;
free (p);
p = q;
}
opts->done_initializing = 1;
/* Several environment variables may add to the include search path.
CPATH specifies an additional list of directories to be searched
as if specified with -I, while C_INCLUDE_PATH, CPLUS_INCLUDE_PATH,
etc. specify an additional list of directories to be searched as
if specified with -isystem, for the language indicated.
These variables are ignored if -nostdinc is on. */
if (! opts->no_standard_includes)
{
char *path;
GET_ENV_PATH_LIST (path, "CPATH");
if (path != 0 && *path != 0)
path_include (pfile, opts->pending, path, BRACKET);
switch ((opts->objc << 1) + opts->cplusplus)
{
case 0:
GET_ENV_PATH_LIST (path, "C_INCLUDE_PATH");
break;
case 1:
GET_ENV_PATH_LIST (path, "CPLUS_INCLUDE_PATH");
break;
case 2:
GET_ENV_PATH_LIST (path, "OBJC_INCLUDE_PATH");
break;
case 3:
GET_ENV_PATH_LIST (path, "OBJCPLUS_INCLUDE_PATH");
break;
}
if (path != 0 && *path != 0)
path_include (pfile, opts->pending, path, SYSTEM);
}
/* Unless -nostdinc, add the compiled-in include path to the list,
translating prefixes. */
if (!opts->no_standard_includes)
{
struct default_include *p = include_defaults_array;
char *specd_prefix = opts->include_prefix;
/* Search "translated" versions of GNU directories.
These have /usr/local/lib/gcc... replaced by specd_prefix. */
if (specd_prefix != 0)
{
char *default_prefix = alloca (sizeof GCC_INCLUDE_DIR - 7);
/* Remove the `include' from /usr/local/lib/gcc.../include.
GCC_INCLUDE_DIR will always end in /include. */
int default_len = sizeof GCC_INCLUDE_DIR - 8;
int specd_len = strlen (specd_prefix);
default_len = sizeof GCC_INCLUDE_DIR - 8;
memcpy (default_prefix, GCC_INCLUDE_DIR, default_len);
default_prefix[default_len] = '\0';
for (p = include_defaults_array; p->fname; p++)
{
/* Some standard dirs are only for C++. */
if (!p->cplusplus
|| (opts->cplusplus
&& !opts->no_standard_cplusplus_includes))
{
/* Does this dir start with the prefix? */
if (!strncmp (p->fname, default_prefix, default_len))
{
/* Yes; change prefix and add to search list. */
int flen = strlen (p->fname);
int this_len = specd_len + flen - default_len;
char *str = (char *) xmalloc (this_len + 1);
memcpy (str, specd_prefix, specd_len);
memcpy (str + specd_len,
p->fname + default_len,
flen - default_len + 1);
append_include_chain (pfile, opts->pending,
str, SYSTEM);
}
}
}
}
/* Search ordinary names for GNU include directories. */
for (p = include_defaults_array; p->fname; p++)
{
/* Some standard dirs are only for C++. */
if (!p->cplusplus
|| (opts->cplusplus
&& !opts->no_standard_cplusplus_includes))
{
char *str = (char *) update_path (p->fname, p->component);
str = xstrdup (str); /* XXX Potential memory leak! */
append_include_chain (pfile, opts->pending, str, SYSTEM);
}
}
}
merge_include_chains (opts);
/* With -v, print the list of dirs to search. */
if (opts->verbose)
{
struct file_name_list *p;
cpp_message (pfile, -1, "#include \"...\" search starts here:\n");
for (p = opts->quote_include; p; p = p->next)
{
if (p == opts->bracket_include)
cpp_message (pfile, -1, "#include <...> search starts here:\n");
fprintf (stderr, " %s\n", p->name);
}
cpp_message (pfile, -1, "End of search list.\n");
}
/* Open the main input file.
We do this in nonblocking mode so we don't get stuck here if
someone clever has asked cpp to process /dev/rmt0;
finclude() will check that we have a real file to work with. */
if (fname == NULL || *fname == 0)
{
fname = "";
f = 0;
}
else if ((f = open (fname, O_RDONLY|O_NONBLOCK|O_NOCTTY, 0666)) < 0)
cpp_pfatal_with_name (pfile, fname);
initialize_dependency_output (pfile);
/* Must call finclude() on the main input before processing
-include switches; otherwise the -included text winds up
after the main input. */
ih_fake = (struct include_hash *) xmalloc (sizeof (struct include_hash));
ih_fake->next = 0;
ih_fake->next_this_file = 0;
ih_fake->foundhere = ABSOLUTE_PATH; /* well sort of ... */
ih_fake->name = fname;
ih_fake->control_macro = 0;
ih_fake->buf = (char *)-1;
ih_fake->limit = 0;
if (!finclude (pfile, f, ih_fake))
return 0;
output_line_command (pfile, same_file);
pfile->only_seen_white = 2;
/* The -imacros files can be scanned now, but the -include files
have to be pushed onto the include stack and processed later,
in the main loop calling cpp_get_token. */
pfile->no_record_file++;
opts->no_output++;
p = opts->pending->imacros_head;
while (p)
{
int fd = open (p->arg, O_RDONLY|O_NONBLOCK|O_NOCTTY, 0666);
if (fd < 0)
{
cpp_perror_with_name (pfile, p->arg);
return 0;
}
if (!cpp_push_buffer (pfile, NULL, 0))
return 0;
ih_fake = (struct include_hash *)
xmalloc (sizeof (struct include_hash));
ih_fake->next = 0;
ih_fake->next_this_file = 0;
ih_fake->foundhere = ABSOLUTE_PATH; /* well sort of ... */
ih_fake->name = p->arg;
ih_fake->control_macro = 0;
ih_fake->buf = (char *)-1;
ih_fake->limit = 0;
if (!finclude (pfile, fd, ih_fake))
cpp_scan_buffer (pfile);
free (ih_fake);
q = p->next;
free (p);
p = q;
}
opts->no_output--;
p = opts->pending->include_head;
while (p)
{
int fd = open (p->arg, O_RDONLY|O_NONBLOCK|O_NOCTTY, 0666);
if (fd < 0)
{
cpp_perror_with_name (pfile, p->arg);
return 0;
}
if (!cpp_push_buffer (pfile, NULL, 0))
return 0;
ih_fake = (struct include_hash *)
xmalloc (sizeof (struct include_hash));
ih_fake->next = 0;
ih_fake->next_this_file = 0;
ih_fake->foundhere = ABSOLUTE_PATH; /* well sort of ... */
ih_fake->name = p->arg;
ih_fake->control_macro = 0;
ih_fake->buf = (char *)-1;
ih_fake->limit = 0;
if (finclude (pfile, fd, ih_fake))
output_line_command (pfile, enter_file);
q = p->next;
free (p);
p = q;
}
pfile->no_record_file--;
free (opts->pending);
opts->pending = NULL;
return 1;
}
/* This is called at the end of preprocessing. It pops the
last buffer and writes dependency output. It should also
clear macro definitions, such that you could call cpp_start_read
with a new filename to restart processing. */
void
cpp_finish (pfile)
cpp_reader *pfile;
{
struct cpp_options *opts = CPP_OPTIONS (pfile);
if (CPP_PREV_BUFFER (CPP_BUFFER (pfile)) != CPP_NULL_BUFFER (pfile))
cpp_fatal (pfile,
"cpplib internal error: buffers still stacked in cpp_finish");
cpp_pop_buffer (pfile);
if (opts->print_deps)
{
/* Stream on which to print the dependency information. */
FILE *deps_stream;
/* Don't actually write the deps file if compilation has failed. */
if (pfile->errors == 0)
{
char *deps_mode = opts->print_deps_append ? "a" : "w";
if (opts->deps_file == 0)
deps_stream = stdout;
else if ((deps_stream = fopen (opts->deps_file, deps_mode)) == 0)
cpp_pfatal_with_name (pfile, opts->deps_file);
fputs (pfile->deps_buffer, deps_stream);
putc ('\n', deps_stream);
if (opts->deps_file)
{
if (ferror (deps_stream) || fclose (deps_stream) != 0)
cpp_fatal (pfile, "I/O error on output");
}
}
}
if (opts->dump_macros == dump_only)
{
int i;
HASHNODE *h;
MACRODEF m;
for (i = HASHSIZE; --i >= 0;)
{
for (h = pfile->hashtab[i]; h; h = h->next)
if (h->type == T_MACRO)
{
m.defn = h->value.defn;
m.symnam = h->name;
m.symlen = h->length;
dump_definition (pfile, m);
CPP_PUTC (pfile, '\n');
}
}
}
}
/* Handle one command-line option in (argc, argv).
Can be called multiple times, to handle multiple sets of options.
Returns number of strings consumed. */
int
cpp_handle_option (pfile, argc, argv)
cpp_reader *pfile;
int argc;
char **argv;
{
struct cpp_options *opts = CPP_OPTIONS (pfile);
int i = 0;
if (argv[i][0] != '-')
{
if (opts->out_fname != NULL)
{
print_help ();
cpp_fatal (pfile, "Too many arguments");
}
else if (opts->in_fname != NULL)
opts->out_fname = argv[i];
else
opts->in_fname = argv[i];
}
else
switch (argv[i][1])
{
case 'f':
if (!strcmp (argv[i], "-fleading-underscore"))
user_label_prefix = "_";
else if (!strcmp (argv[i], "-fno-leading-underscore"))
user_label_prefix = "";
break;
case 'I': /* Add directory to path for includes. */
if (!strcmp (argv[i] + 2, "-"))
{
/* -I- means:
Use the preceding -I directories for #include "..."
but not #include <...>.
Don't search the directory of the present file
for #include "...". (Note that -I. -I- is not the same as
the default setup; -I. uses the compiler's working dir.) */
if (! opts->ignore_srcdir)
{
opts->ignore_srcdir = 1;
opts->pending->quote_head = opts->pending->brack_head;
opts->pending->quote_tail = opts->pending->brack_tail;
opts->pending->brack_head = 0;
opts->pending->brack_tail = 0;
}
else
{
cpp_fatal (pfile, "-I- specified twice");
return argc;
}
}
else
{
char *fname;
if (argv[i][2] != 0)
fname = argv[i] + 2;
else if (i + 1 == argc)
goto missing_dirname;
else
fname = argv[++i];
append_include_chain (pfile, opts->pending,
xstrdup (fname), BRACKET);
}
break;
case 'i':
/* Add directory to beginning of system include path, as a system
include directory. */
if (!strcmp (argv[i], "-isystem"))
{
if (i + 1 == argc)
goto missing_filename;
append_include_chain (pfile, opts->pending,
xstrdup (argv[++i]), SYSTEM);
}
else if (!strcmp (argv[i], "-include"))
{
if (i + 1 == argc)
goto missing_filename;
else
{
struct pending_option *o = (struct pending_option *)
xmalloc (sizeof (struct pending_option));
o->arg = argv[++i];
/* This list has to be built in reverse order so that
when cpp_start_read pushes all the -include files onto
the buffer stack, they will be scanned in forward order. */
o->next = opts->pending->include_head;
opts->pending->include_head = o;
}
}
else if (!strcmp (argv[i], "-imacros"))
{
if (i + 1 == argc)
goto missing_filename;
else
{
struct pending_option *o = (struct pending_option *)
xmalloc (sizeof (struct pending_option));
o->arg = argv[++i];
o->next = NULL;
APPEND (opts->pending, imacros, o);
}
}
/* Add directory to end of path for includes,
with the default prefix at the front of its name. */
else if (!strcmp (argv[i], "-iwithprefix"))
{
char *fname;
int len;
if (i + 1 == argc)
goto missing_dirname;
++i;
len = strlen (argv[i]);
if (opts->include_prefix != 0)
{
fname = xmalloc (opts->include_prefix_len + len + 1);
memcpy (fname, opts->include_prefix, opts->include_prefix_len);
memcpy (fname + opts->include_prefix_len, argv[i], len + 1);
}
else
{
fname = xmalloc (sizeof GCC_INCLUDE_DIR - 8 + len);
memcpy (fname, GCC_INCLUDE_DIR, sizeof GCC_INCLUDE_DIR - 9);
memcpy (fname + sizeof GCC_INCLUDE_DIR - 9, argv[i], len + 1);
}
append_include_chain (pfile, opts->pending, fname, SYSTEM);
}
/* Add directory to main path for includes,
with the default prefix at the front of its name. */
else if (!strcmp (argv[i], "-iwithprefixbefore"))
{
char *fname;
int len;
if (i + 1 == argc)
goto missing_dirname;
++i;
len = strlen (argv[i]);
if (opts->include_prefix != 0)
{
fname = xmalloc (opts->include_prefix_len + len + 1);
memcpy (fname, opts->include_prefix, opts->include_prefix_len);
memcpy (fname + opts->include_prefix_len, argv[i], len + 1);
}
else
{
fname = xmalloc (sizeof GCC_INCLUDE_DIR - 8 + len);
memcpy (fname, GCC_INCLUDE_DIR, sizeof GCC_INCLUDE_DIR - 9);
memcpy (fname + sizeof GCC_INCLUDE_DIR - 9, argv[i], len + 1);
}
append_include_chain (pfile, opts->pending, fname, BRACKET);
}
/* Add directory to end of path for includes. */
else if (!strcmp (argv[i], "-idirafter"))
{
if (i + 1 == argc)
goto missing_dirname;
append_include_chain (pfile, opts->pending,
xstrdup (argv[++i]), AFTER);
}
else if (!strcmp (argv[i], "-iprefix"))
{
if (i + 1 == argc)
goto missing_filename;
else
{
opts->include_prefix = argv[++i];
opts->include_prefix_len = strlen (argv[i]);
}
}
else if (!strcmp (argv[i], "-ifoutput"))
opts->output_conditionals = 1;
break;
case 'o':
if (opts->out_fname != NULL)
{
cpp_fatal (pfile, "Output filename specified twice");
return argc;
}
if (i + 1 == argc)
goto missing_filename;
opts->out_fname = argv[++i];
if (!strcmp (opts->out_fname, "-"))
opts->out_fname = "";
break;
case 'p':
if (!strcmp (argv[i], "-pedantic"))
CPP_PEDANTIC (pfile) = 1;
else if (!strcmp (argv[i], "-pedantic-errors"))
{
CPP_PEDANTIC (pfile) = 1;
opts->pedantic_errors = 1;
}
#if 0
else if (!strcmp (argv[i], "-pcp")) {
char *pcp_fname = argv[++i];
pcp_outfile = ((pcp_fname[0] != '-' || pcp_fname[1] != '\0')
? fopen (pcp_fname, "w")
: fdopen (dup (fileno (stdout)), "w"));
if (pcp_outfile == 0)
cpp_pfatal_with_name (pfile, pcp_fname);
no_precomp = 1;
}
#endif
break;
case 't':
if (!strcmp (argv[i], "-traditional"))
{
opts->traditional = 1;
opts->cplusplus_comments = 0;
}
else if (!strcmp (argv[i], "-trigraphs"))
opts->trigraphs = 1;
break;
case 'l':
if (! strcmp (argv[i], "-lang-c"))
opts->cplusplus = 0, opts->cplusplus_comments = 1, opts->c89 = 0,
opts->c9x = 1, opts->objc = 0;
if (! strcmp (argv[i], "-lang-c89"))
opts->cplusplus = 0, opts->cplusplus_comments = 0, opts->c89 = 1,
opts->c9x = 0, opts->objc = 0;
if (! strcmp (argv[i], "-lang-c++"))
opts->cplusplus = 1, opts->cplusplus_comments = 1, opts->c89 = 0,
opts->c9x = 0, opts->objc = 0;
if (! strcmp (argv[i], "-lang-objc"))
opts->cplusplus = 0, opts->cplusplus_comments = 1, opts->c89 = 0,
opts->c9x = 0, opts->objc = 1;
if (! strcmp (argv[i], "-lang-objc++"))
opts->cplusplus = 1, opts->cplusplus_comments = 1, opts->c89 = 0,
opts->c9x = 0, opts->objc = 1;
if (! strcmp (argv[i], "-lang-asm"))
opts->lang_asm = 1;
if (! strcmp (argv[i], "-lint"))
opts->for_lint = 1;
if (! strcmp (argv[i], "-lang-chill"))
opts->objc = 0, opts->cplusplus = 0, opts->chill = 1,
opts->traditional = 1;
break;
case '+':
opts->cplusplus = 1, opts->cplusplus_comments = 1;
break;
case 's':
if (!strcmp (argv[i], "-std=iso9899:1990")
|| !strcmp (argv[i], "-std=iso9899:199409")
|| !strcmp (argv[i], "-std=c89")
|| !strcmp (argv[i], "-std=gnu89"))
opts->cplusplus = 0, opts->cplusplus_comments = 0,
opts->c89 = 1, opts->c9x = 0, opts->objc = 0;
else if (!strcmp (argv[i], "-std=iso9899:199x")
|| !strcmp (argv[i], "-std=c9x")
|| !strcmp (argv[i], "-std=gnu9x"))
opts->cplusplus = 0, opts->cplusplus_comments = 1, opts->c89 = 0,
opts->c9x = 1, opts->objc = 0;
break;
case 'w':
opts->inhibit_warnings = 1;
break;
case 'W':
if (!strcmp (argv[i], "-Wtrigraphs"))
opts->warn_trigraphs = 1;
else if (!strcmp (argv[i], "-Wno-trigraphs"))
opts->warn_trigraphs = 0;
else if (!strcmp (argv[i], "-Wcomment"))
opts->warn_comments = 1;
else if (!strcmp (argv[i], "-Wno-comment"))
opts->warn_comments = 0;
else if (!strcmp (argv[i], "-Wcomments"))
opts->warn_comments = 1;
else if (!strcmp (argv[i], "-Wno-comments"))
opts->warn_comments = 0;
else if (!strcmp (argv[i], "-Wtraditional"))
opts->warn_stringify = 1;
else if (!strcmp (argv[i], "-Wno-traditional"))
opts->warn_stringify = 0;
else if (!strcmp (argv[i], "-Wundef"))
opts->warn_undef = 1;
else if (!strcmp (argv[i], "-Wno-undef"))
opts->warn_undef = 0;
else if (!strcmp (argv[i], "-Wimport"))
opts->warn_import = 1;
else if (!strcmp (argv[i], "-Wno-import"))
opts->warn_import = 0;
else if (!strcmp (argv[i], "-Werror"))
opts->warnings_are_errors = 1;
else if (!strcmp (argv[i], "-Wno-error"))
opts->warnings_are_errors = 0;
else if (!strcmp (argv[i], "-Wall"))
{
opts->warn_trigraphs = 1;
opts->warn_comments = 1;
}
break;
case 'M':
/* The style of the choices here is a bit mixed.
The chosen scheme is a hybrid of keeping all options in one string
and specifying each option in a separate argument:
-M|-MM|-MD file|-MMD file [-MG]. An alternative is:
-M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely:
-M[M][G][D file]. This is awkward to handle in specs, and is not
as extensible. */
/* ??? -MG must be specified in addition to one of -M or -MM.
This can be relaxed in the future without breaking anything.
The converse isn't true. */
/* -MG isn't valid with -MD or -MMD. This is checked for later. */
if (!strcmp (argv[i], "-MG"))
{
opts->print_deps_missing_files = 1;
break;
}
if (!strcmp (argv[i], "-M"))
opts->print_deps = 2;
else if (!strcmp (argv[i], "-MM"))
opts->print_deps = 1;
else if (!strcmp (argv[i], "-MD"))
opts->print_deps = 2;
else if (!strcmp (argv[i], "-MMD"))
opts->print_deps = 1;
/* For -MD and -MMD options, write deps on file named by next arg. */
if (!strcmp (argv[i], "-MD") || !strcmp (argv[i], "-MMD"))
{
if (i+1 == argc)
goto missing_filename;
opts->deps_file = argv[++i];
}
else
{
/* For -M and -MM, write deps on standard output
and suppress the usual output. */
opts->no_output = 1;
}
break;
case 'd':
{
char *p = argv[i] + 2;
char c;
while ((c = *p++) != 0)
{
/* Arg to -d specifies what parts of macros to dump */
switch (c)
{
case 'M':
opts->dump_macros = dump_only;
opts->no_output = 1;
break;
case 'N':
opts->dump_macros = dump_names;
break;
case 'D':
opts->dump_macros = dump_definitions;
break;
case 'I':
opts->dump_includes = 1;
break;
}
}
}
break;
case 'g':
if (argv[i][2] == '3')
opts->debug_output = 1;
break;
case '-':
if (!strcmp (argv[i], "--help"))
print_help ();
else if (!strcmp (argv[i], "--version"))
cpp_notice ("GNU CPP version %s\n", version_string);
exit (0); /* XXX */
break;
case 'v':
cpp_notice ("GNU CPP version %s", version_string);
#ifdef TARGET_VERSION
TARGET_VERSION;
#endif
fputc ('\n', stderr);
opts->verbose = 1;
break;
case 'H':
opts->print_include_names = 1;
break;
case 'D':
{
struct pending_option *o = (struct pending_option *)
xmalloc (sizeof (struct pending_option));
if (argv[i][2] != 0)
o->arg = argv[i] + 2;
else if (i + 1 == argc)
{
cpp_fatal (pfile, "Macro name missing after -D option");
return argc;
}
else
o->arg = argv[++i];
o->next = NULL;
o->undef = 0;
APPEND (opts->pending, define, o);
}
break;
case 'A':
{
char *p;
if (argv[i][2] != 0)
p = argv[i] + 2;
else if (i + 1 == argc)
{
cpp_fatal (pfile, "Assertion missing after -A option");
return argc;
}
else
p = argv[++i];
if (strcmp (p, "-"))
{
struct pending_option *o = (struct pending_option *)
xmalloc (sizeof (struct pending_option));
o->arg = p;
o->next = NULL;
o->undef = 0;
APPEND (opts->pending, assert, o);
}
else
{
/* -A- eliminates all predefined macros and assertions.
Let's include also any that were specified earlier
on the command line. That way we can get rid of any
that were passed automatically in from GCC. */
struct pending_option *o1, *o2;
o1 = opts->pending->define_head;
while (o1)
{
o2 = o1->next;
free (o1);
o1 = o2;
}
o1 = opts->pending->assert_head;
while (o1)
{
o2 = o1->next;
free (o1);
o1 = o2;
}
opts->pending->assert_head = NULL;
opts->pending->assert_tail = NULL;
opts->pending->define_head = NULL;
opts->pending->define_tail = NULL;
}
}
break;
case 'U':
{
struct pending_option *o = (struct pending_option *)
xmalloc (sizeof (struct pending_option));
if (argv[i][2] != 0)
o->arg = argv[i] + 2;
else if (i + 1 == argc)
{
cpp_fatal (pfile, "Macro name missing after -U option");
return argc;
}
else
o->arg = argv[++i];
o->next = NULL;
o->undef = 1;
APPEND (opts->pending, define, o);
}
break;
case 'C':
opts->put_out_comments = 1;
break;
case 'E': /* -E comes from cc -E; ignore it. */
break;
case 'P':
opts->no_line_commands = 1;
break;
case '$': /* Don't include $ in identifiers. */
opts->dollars_in_ident = 0;
break;
case 'n':
if (!strcmp (argv[i], "-nostdinc"))
/* -nostdinc causes no default include directories.
You must specify all include-file directories with -I. */
opts->no_standard_includes = 1;
else if (!strcmp (argv[i], "-nostdinc++"))
/* -nostdinc++ causes no default C++-specific include directories. */
opts->no_standard_cplusplus_includes = 1;
#if 0
else if (!strcmp (argv[i], "-noprecomp"))
no_precomp = 1;
#endif
break;
case 'r':
if (!strcmp (argv[i], "-remap"))
opts->remap = 1;
break;
case '\0': /* JF handle '-' as file name meaning stdin or stdout */
if (opts->in_fname == NULL)
opts->in_fname = "";
else if (opts->out_fname == NULL)
opts->out_fname = "";
else
return i; /* error */
break;
default:
return i;
}
return i + 1;
missing_filename:
cpp_fatal (pfile, "Filename missing after `%s' option", argv[i]);
return argc;
missing_dirname:
cpp_fatal (pfile, "Directory name missing after `%s' option", argv[i]);
return argc;
}
/* Handle command-line options in (argc, argv).
Can be called multiple times, to handle multiple sets of options.
Returns if an unrecognized option is seen.
Returns number of strings consumed. */
int
cpp_handle_options (pfile, argc, argv)
cpp_reader *pfile;
int argc;
char **argv;
{
int i;
int strings_processed;
for (i = 0; i < argc; i += strings_processed)
{
strings_processed = cpp_handle_option (pfile, argc - i, argv + i);
if (strings_processed == 0)
break;
}
return i;
}
static void
print_help ()
{
cpp_notice ("Usage: %s [switches] input output\n", progname);
fputs (_("\
Switches:\n\
-include <file> Include the contents of <file> before other files\n\
-imacros <file> Accept definition of macros in <file>\n\
-iprefix <path> Specify <path> as a prefix for next two options\n\
-iwithprefix <dir> Add <dir> to the end of the system include path\n\
-iwithprefixbefore <dir> Add <dir> to the end of the main include path\n\
-isystem <dir> Add <dir> to the start of the system include path\n\
-idirafter <dir> Add <dir> to the end of the system include path\n\
-I <dir> Add <dir> to the end of the main include path\n\
-nostdinc Do not search system include directories\n\
(dirs specified with -isystem will still be used)\n\
-nostdinc++ Do not search system include directories for C++\n\
-o <file> Put output into <file>\n\
-pedantic Issue all warnings demanded by strict ANSI C\n\
-traditional Follow K&R pre-processor behaviour\n\
-trigraphs Support ANSI C trigraphs\n\
-lang-c Assume that the input sources are in C\n\
-lang-c89 Assume that the input sources are in C89\n\
-lang-c++ Assume that the input sources are in C++\n\
-lang-objc Assume that the input sources are in ObjectiveC\n\
-lang-objc++ Assume that the input sources are in ObjectiveC++\n\
-lang-asm Assume that the input sources are in assembler\n\
-lang-chill Assume that the input sources are in Chill\n\
-std=<std name> Specify the conformance standard; one of:\n\
gnu89, gnu9x, c89, c9x, iso9899:1990,\n\
iso9899:199409, iso9899:199x\n\
-+ Allow parsing of C++ style features\n\
-w Inhibit warning messages\n\
-Wtrigraphs Warn if trigraphs are encountered\n\
-Wno-trigraphs Do not warn about trigraphs\n\
-Wcomment{s} Warn if one comment starts inside another\n\
-Wno-comment{s} Do not warn about comments\n\
-Wtraditional Warn if a macro argument is/would be turned into\n\
a string if -traditional is specified\n\
-Wno-traditional Do not warn about stringification\n\
-Wundef Warn if an undefined macro is used by #if\n\
-Wno-undef Do not warn about testing undefined macros\n\
-Wimport Warn about the use of the #import directive\n\
-Wno-import Do not warn about the use of #import\n\
-Werror Treat all warnings as errors\n\
-Wno-error Do not treat warnings as errors\n\
-Wall Enable all preprocessor warnings\n\
-M Generate make dependencies\n\
-MM As -M, but ignore system header files\n\
-MD As -M, but put output in a .d file\n\
-MMD As -MD, but ignore system header files\n\
-MG Treat missing header file as generated files\n\
-g Include #define and #undef directives in the output\n\
-D<macro> Define a <macro> with string '1' as its value\n\
-D<macro>=<val> Define a <macro> with <val> as its value\n\
-A<question> (<answer>) Assert the <answer> to <question>\n\
-U<macro> Undefine <macro> \n\
-v Display the version number\n\
-H Print the name of header files as they are used\n\
-C Do not discard comments\n\
-dM Display a list of macro definitions active at end\n\
-dD Preserve macro definitions in output\n\
-dN As -dD except that only the names are preserved\n\
-dI Include #include directives in the output\n\
-ifoutput Describe skipped code blocks in output \n\
-P Do not generate #line directives\n\
-$ Do not allow '$' in identifiers\n\
-remap Remap file names when including files.\n\
-h or --help Display this information\n\
"), stdout);
}
Index: head/contrib/gcc/cse.c
===================================================================
--- head/contrib/gcc/cse.c (revision 52750)
+++ head/contrib/gcc/cse.c (revision 52751)
@@ -1,9292 +1,9300 @@
/* Common subexpression elimination for GNU compiler.
Copyright (C) 1987, 88, 89, 92-7, 1998, 1999 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. */
#include "config.h"
/* stdio.h must precede rtl.h for FFS. */
#include "system.h"
#include <setjmp.h>
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "expr.h"
#include "toplev.h"
#include "output.h"
#include "splay-tree.h"
/* The basic idea of common subexpression elimination is to go
through the code, keeping a record of expressions that would
have the same value at the current scan point, and replacing
expressions encountered with the cheapest equivalent expression.
It is too complicated to keep track of the different possibilities
when control paths merge; so, at each label, we forget all that is
known and start fresh. This can be described as processing each
basic block separately. Note, however, that these are not quite
the same as the basic blocks found by a later pass and used for
data flow analysis and register packing. We do not need to start fresh
after a conditional jump instruction if there is no label there.
We use two data structures to record the equivalent expressions:
a hash table for most expressions, and several vectors together
with "quantity numbers" to record equivalent (pseudo) registers.
The use of the special data structure for registers is desirable
because it is faster. It is possible because registers references
contain a fairly small number, the register number, taken from
a contiguously allocated series, and two register references are
identical if they have the same number. General expressions
do not have any such thing, so the only way to retrieve the
information recorded on an expression other than a register
is to keep it in a hash table.
Registers and "quantity numbers":
At the start of each basic block, all of the (hardware and pseudo)
registers used in the function are given distinct quantity
numbers to indicate their contents. During scan, when the code
copies one register into another, we copy the quantity number.
When a register is loaded in any other way, we allocate a new
quantity number to describe the value generated by this operation.
`reg_qty' records what quantity a register is currently thought
of as containing.
All real quantity numbers are greater than or equal to `max_reg'.
If register N has not been assigned a quantity, reg_qty[N] will equal N.
Quantity numbers below `max_reg' do not exist and none of the `qty_...'
variables should be referenced with an index below `max_reg'.
We also maintain a bidirectional chain of registers for each
quantity number. `qty_first_reg', `qty_last_reg',
`reg_next_eqv' and `reg_prev_eqv' hold these chains.
The first register in a chain is the one whose lifespan is least local.
Among equals, it is the one that was seen first.
We replace any equivalent register with that one.
If two registers have the same quantity number, it must be true that
REG expressions with `qty_mode' must be in the hash table for both
registers and must be in the same class.
The converse is not true. Since hard registers may be referenced in
any mode, two REG expressions might be equivalent in the hash table
but not have the same quantity number if the quantity number of one
of the registers is not the same mode as those expressions.
Constants and quantity numbers
When a quantity has a known constant value, that value is stored
in the appropriate element of qty_const. This is in addition to
putting the constant in the hash table as is usual for non-regs.
Whether a reg or a constant is preferred is determined by the configuration
macro CONST_COSTS and will often depend on the constant value. In any
event, expressions containing constants can be simplified, by fold_rtx.
When a quantity has a known nearly constant value (such as an address
of a stack slot), that value is stored in the appropriate element
of qty_const.
Integer constants don't have a machine mode. However, cse
determines the intended machine mode from the destination
of the instruction that moves the constant. The machine mode
is recorded in the hash table along with the actual RTL
constant expression so that different modes are kept separate.
Other expressions:
To record known equivalences among expressions in general
we use a hash table called `table'. It has a fixed number of buckets
that contain chains of `struct table_elt' elements for expressions.
These chains connect the elements whose expressions have the same
hash codes.
Other chains through the same elements connect the elements which
currently have equivalent values.
Register references in an expression are canonicalized before hashing
the expression. This is done using `reg_qty' and `qty_first_reg'.
The hash code of a register reference is computed using the quantity
number, not the register number.
When the value of an expression changes, it is necessary to remove from the
hash table not just that expression but all expressions whose values
could be different as a result.
1. If the value changing is in memory, except in special cases
ANYTHING referring to memory could be changed. That is because
nobody knows where a pointer does not point.
The function `invalidate_memory' removes what is necessary.
The special cases are when the address is constant or is
a constant plus a fixed register such as the frame pointer
or a static chain pointer. When such addresses are stored in,
we can tell exactly which other such addresses must be invalidated
due to overlap. `invalidate' does this.
All expressions that refer to non-constant
memory addresses are also invalidated. `invalidate_memory' does this.
2. If the value changing is a register, all expressions
containing references to that register, and only those,
must be removed.
Because searching the entire hash table for expressions that contain
a register is very slow, we try to figure out when it isn't necessary.
Precisely, this is necessary only when expressions have been
entered in the hash table using this register, and then the value has
changed, and then another expression wants to be added to refer to
the register's new value. This sequence of circumstances is rare
within any one basic block.
The vectors `reg_tick' and `reg_in_table' are used to detect this case.
reg_tick[i] is incremented whenever a value is stored in register i.
reg_in_table[i] holds -1 if no references to register i have been
entered in the table; otherwise, it contains the value reg_tick[i] had
when the references were entered. If we want to enter a reference
and reg_in_table[i] != reg_tick[i], we must scan and remove old references.
Until we want to enter a new entry, the mere fact that the two vectors
don't match makes the entries be ignored if anyone tries to match them.
Registers themselves are entered in the hash table as well as in
the equivalent-register chains. However, the vectors `reg_tick'
and `reg_in_table' do not apply to expressions which are simple
register references. These expressions are removed from the table
immediately when they become invalid, and this can be done even if
we do not immediately search for all the expressions that refer to
the register.
A CLOBBER rtx in an instruction invalidates its operand for further
reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK
invalidates everything that resides in memory.
Related expressions:
Constant expressions that differ only by an additive integer
are called related. When a constant expression is put in
the table, the related expression with no constant term
is also entered. These are made to point at each other
so that it is possible to find out if there exists any
register equivalent to an expression related to a given expression. */
/* One plus largest register number used in this function. */
static int max_reg;
/* One plus largest instruction UID used in this function at time of
cse_main call. */
static int max_insn_uid;
/* Length of vectors indexed by quantity number.
We know in advance we will not need a quantity number this big. */
static int max_qty;
/* Next quantity number to be allocated.
This is 1 + the largest number needed so far. */
static int next_qty;
/* Indexed by quantity number, gives the first (or last) register
in the chain of registers that currently contain this quantity. */
static int *qty_first_reg;
static int *qty_last_reg;
/* Index by quantity number, gives the mode of the quantity. */
static enum machine_mode *qty_mode;
/* Indexed by quantity number, gives the rtx of the constant value of the
quantity, or zero if it does not have a known value.
A sum of the frame pointer (or arg pointer) plus a constant
can also be entered here. */
static rtx *qty_const;
/* Indexed by qty number, gives the insn that stored the constant value
recorded in `qty_const'. */
static rtx *qty_const_insn;
/* The next three variables are used to track when a comparison between a
quantity and some constant or register has been passed. In that case, we
know the results of the comparison in case we see it again. These variables
record a comparison that is known to be true. */
/* Indexed by qty number, gives the rtx code of a comparison with a known
result involving this quantity. If none, it is UNKNOWN. */
static enum rtx_code *qty_comparison_code;
/* Indexed by qty number, gives the constant being compared against in a
comparison of known result. If no such comparison, it is undefined.
If the comparison is not with a constant, it is zero. */
static rtx *qty_comparison_const;
/* Indexed by qty number, gives the quantity being compared against in a
comparison of known result. If no such comparison, if it undefined.
If the comparison is not with a register, it is -1. */
static int *qty_comparison_qty;
#ifdef HAVE_cc0
/* For machines that have a CC0, we do not record its value in the hash
table since its use is guaranteed to be the insn immediately following
its definition and any other insn is presumed to invalidate it.
Instead, we store below the value last assigned to CC0. If it should
happen to be a constant, it is stored in preference to the actual
assigned value. In case it is a constant, we store the mode in which
the constant should be interpreted. */
static rtx prev_insn_cc0;
static enum machine_mode prev_insn_cc0_mode;
#endif
/* Previous actual insn. 0 if at first insn of basic block. */
static rtx prev_insn;
/* Insn being scanned. */
static rtx this_insn;
/* Index by register number, gives the number of the next (or
previous) register in the chain of registers sharing the same
value.
Or -1 if this register is at the end of the chain.
If reg_qty[N] == N, reg_next_eqv[N] is undefined. */
static int *reg_next_eqv;
static int *reg_prev_eqv;
struct cse_reg_info {
union {
/* The number of times the register has been altered in the current
basic block. */
int reg_tick;
/* The next cse_reg_info structure in the free list. */
struct cse_reg_info* next;
} variant;
/* The REG_TICK value at which rtx's containing this register are
valid in the hash table. If this does not equal the current
reg_tick value, such expressions existing in the hash table are
invalid. */
int reg_in_table;
/* The quantity number of the register's current contents. */
int reg_qty;
};
/* A free list of cse_reg_info entries. */
static struct cse_reg_info *cse_reg_info_free_list;
/* A mapping from registers to cse_reg_info data structures. */
static splay_tree cse_reg_info_tree;
/* The last lookup we did into the cse_reg_info_tree. This allows us
to cache repeated lookups. */
static int cached_regno;
static struct cse_reg_info *cached_cse_reg_info;
/* A HARD_REG_SET containing all the hard registers for which there is
currently a REG expression in the hash table. Note the difference
from the above variables, which indicate if the REG is mentioned in some
expression in the table. */
static HARD_REG_SET hard_regs_in_table;
/* A HARD_REG_SET containing all the hard registers that are invalidated
by a CALL_INSN. */
static HARD_REG_SET regs_invalidated_by_call;
/* CUID of insn that starts the basic block currently being cse-processed. */
static int cse_basic_block_start;
/* CUID of insn that ends the basic block currently being cse-processed. */
static int cse_basic_block_end;
/* Vector mapping INSN_UIDs to cuids.
The cuids are like uids but increase monotonically always.
We use them to see whether a reg is used outside a given basic block. */
static int *uid_cuid;
/* Highest UID in UID_CUID. */
static int max_uid;
/* Get the cuid of an insn. */
#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
/* Nonzero if cse has altered conditional jump insns
in such a way that jump optimization should be redone. */
static int cse_jumps_altered;
/* Nonzero if we put a LABEL_REF into the hash table. Since we may have put
it into an INSN without a REG_LABEL, we have to rerun jump after CSE
to put in the note. */
static int recorded_label_ref;
/* canon_hash stores 1 in do_not_record
if it notices a reference to CC0, PC, or some other volatile
subexpression. */
static int do_not_record;
#ifdef LOAD_EXTEND_OP
/* Scratch rtl used when looking for load-extended copy of a MEM. */
static rtx memory_extend_rtx;
#endif
/* canon_hash stores 1 in hash_arg_in_memory
if it notices a reference to memory within the expression being hashed. */
static int hash_arg_in_memory;
/* canon_hash stores 1 in hash_arg_in_struct
if it notices a reference to memory that's part of a structure. */
static int hash_arg_in_struct;
/* The hash table contains buckets which are chains of `struct table_elt's,
each recording one expression's information.
That expression is in the `exp' field.
Those elements with the same hash code are chained in both directions
through the `next_same_hash' and `prev_same_hash' fields.
Each set of expressions with equivalent values
are on a two-way chain through the `next_same_value'
and `prev_same_value' fields, and all point with
the `first_same_value' field at the first element in
that chain. The chain is in order of increasing cost.
Each element's cost value is in its `cost' field.
The `in_memory' field is nonzero for elements that
involve any reference to memory. These elements are removed
whenever a write is done to an unidentified location in memory.
To be safe, we assume that a memory address is unidentified unless
the address is either a symbol constant or a constant plus
the frame pointer or argument pointer.
The `in_struct' field is nonzero for elements that
involve any reference to memory inside a structure or array.
The `related_value' field is used to connect related expressions
(that differ by adding an integer).
The related expressions are chained in a circular fashion.
`related_value' is zero for expressions for which this
chain is not useful.
The `cost' field stores the cost of this element's expression.
The `is_const' flag is set if the element is a constant (including
a fixed address).
The `flag' field is used as a temporary during some search routines.
The `mode' field is usually the same as GET_MODE (`exp'), but
if `exp' is a CONST_INT and has no machine mode then the `mode'
field is the mode it was being used as. Each constant is
recorded separately for each mode it is used with. */
struct table_elt
{
rtx exp;
struct table_elt *next_same_hash;
struct table_elt *prev_same_hash;
struct table_elt *next_same_value;
struct table_elt *prev_same_value;
struct table_elt *first_same_value;
struct table_elt *related_value;
int cost;
enum machine_mode mode;
char in_memory;
char in_struct;
char is_const;
char flag;
};
/* We don't want a lot of buckets, because we rarely have very many
things stored in the hash table, and a lot of buckets slows
down a lot of loops that happen frequently. */
#define NBUCKETS 31
/* Compute hash code of X in mode M. Special-case case where X is a pseudo
register (hard registers may require `do_not_record' to be set). */
#define HASH(X, M) \
(GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) % NBUCKETS \
: canon_hash (X, M) % NBUCKETS)
/* Determine whether register number N is considered a fixed register for CSE.
It is desirable to replace other regs with fixed regs, to reduce need for
non-fixed hard regs.
A reg wins if it is either the frame pointer or designated as fixed,
but not if it is an overlapping register. */
#ifdef OVERLAPPING_REGNO_P
#define FIXED_REGNO_P(N) \
(((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| fixed_regs[N] || global_regs[N]) \
&& ! OVERLAPPING_REGNO_P ((N)))
#else
#define FIXED_REGNO_P(N) \
((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| fixed_regs[N] || global_regs[N])
#endif
/* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed
hard registers and pointers into the frame are the cheapest with a cost
of 0. Next come pseudos with a cost of one and other hard registers with
a cost of 2. Aside from these special cases, call `rtx_cost'. */
#define CHEAP_REGNO(N) \
((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
|| (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM \
|| ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \
|| ((N) < FIRST_PSEUDO_REGISTER \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
/* A register is cheap if it is a user variable assigned to the register
or if its register number always corresponds to a cheap register. */
#define CHEAP_REG(N) \
((REG_USERVAR_P (N) && REGNO (N) < FIRST_PSEUDO_REGISTER) \
|| CHEAP_REGNO (REGNO (N)))
#define COST(X) \
(GET_CODE (X) == REG \
? (CHEAP_REG (X) ? 0 \
: REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1 \
: 2) \
: notreg_cost(X))
/* Get the info associated with register N. */
#define GET_CSE_REG_INFO(N) \
(((N) == cached_regno && cached_cse_reg_info) \
? cached_cse_reg_info : get_cse_reg_info ((N)))
/* Get the number of times this register has been updated in this
basic block. */
#define REG_TICK(N) ((GET_CSE_REG_INFO (N))->variant.reg_tick)
/* Get the point at which REG was recorded in the table. */
#define REG_IN_TABLE(N) ((GET_CSE_REG_INFO (N))->reg_in_table)
/* Get the quantity number for REG. */
#define REG_QTY(N) ((GET_CSE_REG_INFO (N))->reg_qty)
/* Determine if the quantity number for register X represents a valid index
into the `qty_...' variables. */
#define REGNO_QTY_VALID_P(N) (REG_QTY (N) != (N))
#ifdef ADDRESS_COST
/* The ADDRESS_COST macro does not deal with ADDRESSOF nodes. But,
during CSE, such nodes are present. Using an ADDRESSOF node which
refers to the address of a REG is a good thing because we can then
turn (MEM (ADDRESSSOF (REG))) into just plain REG. */
#define CSE_ADDRESS_COST(RTX) \
((GET_CODE (RTX) == ADDRESSOF && REG_P (XEXP ((RTX), 0))) \
? -1 : ADDRESS_COST(RTX))
#endif
static struct table_elt *table[NBUCKETS];
/* Chain of `struct table_elt's made so far for this function
but currently removed from the table. */
static struct table_elt *free_element_chain;
/* Number of `struct table_elt' structures made so far for this function. */
static int n_elements_made;
/* Maximum value `n_elements_made' has had so far in this compilation
for functions previously processed. */
static int max_elements_made;
/* Surviving equivalence class when two equivalence classes are merged
by recording the effects of a jump in the last insn. Zero if the
last insn was not a conditional jump. */
static struct table_elt *last_jump_equiv_class;
/* Set to the cost of a constant pool reference if one was found for a
symbolic constant. If this was found, it means we should try to
convert constants into constant pool entries if they don't fit in
the insn. */
static int constant_pool_entries_cost;
/* Define maximum length of a branch path. */
#define PATHLENGTH 10
/* This data describes a block that will be processed by cse_basic_block. */
struct cse_basic_block_data {
/* Lowest CUID value of insns in block. */
int low_cuid;
/* Highest CUID value of insns in block. */
int high_cuid;
/* Total number of SETs in block. */
int nsets;
/* Last insn in the block. */
rtx last;
/* Size of current branch path, if any. */
int path_size;
/* Current branch path, indicating which branches will be taken. */
struct branch_path {
/* The branch insn. */
rtx branch;
/* Whether it should be taken or not. AROUND is the same as taken
except that it is used when the destination label is not preceded
by a BARRIER. */
enum taken {TAKEN, NOT_TAKEN, AROUND} status;
} path[PATHLENGTH];
};
/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
virtual regs here because the simplify_*_operation routines are called
by integrate.c, which is called before virtual register instantiation. */
#define FIXED_BASE_PLUS_P(X) \
((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
|| (X) == arg_pointer_rtx \
|| (X) == virtual_stack_vars_rtx \
|| (X) == virtual_incoming_args_rtx \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (XEXP (X, 0) == frame_pointer_rtx \
|| XEXP (X, 0) == hard_frame_pointer_rtx \
|| XEXP (X, 0) == arg_pointer_rtx \
|| XEXP (X, 0) == virtual_stack_vars_rtx \
|| XEXP (X, 0) == virtual_incoming_args_rtx)) \
|| GET_CODE (X) == ADDRESSOF)
/* Similar, but also allows reference to the stack pointer.
This used to include FIXED_BASE_PLUS_P, however, we can't assume that
arg_pointer_rtx by itself is nonzero, because on at least one machine,
the i960, the arg pointer is zero when it is unused. */
#define NONZERO_BASE_PLUS_P(X) \
((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
|| (X) == virtual_stack_vars_rtx \
|| (X) == virtual_incoming_args_rtx \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (XEXP (X, 0) == frame_pointer_rtx \
|| XEXP (X, 0) == hard_frame_pointer_rtx \
|| XEXP (X, 0) == arg_pointer_rtx \
|| XEXP (X, 0) == virtual_stack_vars_rtx \
|| XEXP (X, 0) == virtual_incoming_args_rtx)) \
|| (X) == stack_pointer_rtx \
|| (X) == virtual_stack_dynamic_rtx \
|| (X) == virtual_outgoing_args_rtx \
|| (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
&& (XEXP (X, 0) == stack_pointer_rtx \
|| XEXP (X, 0) == virtual_stack_dynamic_rtx \
|| XEXP (X, 0) == virtual_outgoing_args_rtx)) \
|| GET_CODE (X) == ADDRESSOF)
static int notreg_cost PROTO((rtx));
static void new_basic_block PROTO((void));
static void make_new_qty PROTO((int));
static void make_regs_eqv PROTO((int, int));
static void delete_reg_equiv PROTO((int));
static int mention_regs PROTO((rtx));
static int insert_regs PROTO((rtx, struct table_elt *, int));
static void free_element PROTO((struct table_elt *));
static void remove_from_table PROTO((struct table_elt *, unsigned));
static struct table_elt *get_element PROTO((void));
static struct table_elt *lookup PROTO((rtx, unsigned, enum machine_mode)),
*lookup_for_remove PROTO((rtx, unsigned, enum machine_mode));
static rtx lookup_as_function PROTO((rtx, enum rtx_code));
static struct table_elt *insert PROTO((rtx, struct table_elt *, unsigned,
enum machine_mode));
static void merge_equiv_classes PROTO((struct table_elt *,
struct table_elt *));
static void invalidate PROTO((rtx, enum machine_mode));
static int cse_rtx_varies_p PROTO((rtx));
static void remove_invalid_refs PROTO((int));
static void remove_invalid_subreg_refs PROTO((int, int, enum machine_mode));
static void rehash_using_reg PROTO((rtx));
static void invalidate_memory PROTO((void));
static void invalidate_for_call PROTO((void));
static rtx use_related_value PROTO((rtx, struct table_elt *));
static unsigned canon_hash PROTO((rtx, enum machine_mode));
static unsigned safe_hash PROTO((rtx, enum machine_mode));
static int exp_equiv_p PROTO((rtx, rtx, int, int));
static void set_nonvarying_address_components PROTO((rtx, int, rtx *,
HOST_WIDE_INT *,
HOST_WIDE_INT *));
static int refers_to_p PROTO((rtx, rtx));
static rtx canon_reg PROTO((rtx, rtx));
static void find_best_addr PROTO((rtx, rtx *));
static enum rtx_code find_comparison_args PROTO((enum rtx_code, rtx *, rtx *,
enum machine_mode *,
enum machine_mode *));
static rtx cse_gen_binary PROTO((enum rtx_code, enum machine_mode,
rtx, rtx));
static rtx simplify_plus_minus PROTO((enum rtx_code, enum machine_mode,
rtx, rtx));
static rtx fold_rtx PROTO((rtx, rtx));
static rtx equiv_constant PROTO((rtx));
static void record_jump_equiv PROTO((rtx, int));
static void record_jump_cond PROTO((enum rtx_code, enum machine_mode,
rtx, rtx, int));
static void cse_insn PROTO((rtx, rtx));
static int note_mem_written PROTO((rtx));
static void invalidate_from_clobbers PROTO((rtx));
static rtx cse_process_notes PROTO((rtx, rtx));
static void cse_around_loop PROTO((rtx));
static void invalidate_skipped_set PROTO((rtx, rtx));
static void invalidate_skipped_block PROTO((rtx));
static void cse_check_loop_start PROTO((rtx, rtx));
static void cse_set_around_loop PROTO((rtx, rtx, rtx));
static rtx cse_basic_block PROTO((rtx, rtx, struct branch_path *, int));
static void count_reg_usage PROTO((rtx, int *, rtx, int));
extern void dump_class PROTO((struct table_elt*));
static void check_fold_consts PROTO((PTR));
static struct cse_reg_info* get_cse_reg_info PROTO((int));
static void free_cse_reg_info PROTO((splay_tree_value));
static void flush_hash_table PROTO((void));
extern int rtx_equal_function_value_matters;
/* Dump the expressions in the equivalence class indicated by CLASSP.
This function is used only for debugging. */
void
dump_class (classp)
struct table_elt *classp;
{
struct table_elt *elt;
fprintf (stderr, "Equivalence chain for ");
print_rtl (stderr, classp->exp);
fprintf (stderr, ": \n");
for (elt = classp->first_same_value; elt; elt = elt->next_same_value)
{
print_rtl (stderr, elt->exp);
fprintf (stderr, "\n");
}
}
/* Return an estimate of the cost of computing rtx X.
One use is in cse, to decide which expression to keep in the hash table.
Another is in rtl generation, to pick the cheapest way to multiply.
Other uses like the latter are expected in the future. */
/* Internal function, to compute cost when X is not a register; called
from COST macro to keep it simple. */
static int
notreg_cost (x)
rtx x;
{
return ((GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == REG
&& GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
&& GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
&& (GET_MODE_SIZE (GET_MODE (x))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
&& subreg_lowpart_p (x)
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (x)),
GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))))
? (CHEAP_REG (SUBREG_REG (x)) ? 0
: (REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER ? 1
: 2))
: rtx_cost (x, SET) * 2);
}
/* Return the right cost to give to an operation
to make the cost of the corresponding register-to-register instruction
N times that of a fast register-to-register instruction. */
#define COSTS_N_INSNS(N) ((N) * 4 - 2)
int
rtx_cost (x, outer_code)
rtx x;
enum rtx_code outer_code ATTRIBUTE_UNUSED;
{
register int i, j;
register enum rtx_code code;
register char *fmt;
register int total;
if (x == 0)
return 0;
/* Compute the default costs of certain things.
Note that RTX_COSTS can override the defaults. */
code = GET_CODE (x);
switch (code)
{
case MULT:
/* Count multiplication by 2**n as a shift,
because if we are considering it, we would output it as a shift. */
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
total = 2;
else
total = COSTS_N_INSNS (5);
break;
case DIV:
case UDIV:
case MOD:
case UMOD:
total = COSTS_N_INSNS (7);
break;
case USE:
/* Used in loop.c and combine.c as a marker. */
total = 0;
break;
case ASM_OPERANDS:
/* We don't want these to be used in substitutions because
we have no way of validating the resulting insn. So assign
anything containing an ASM_OPERANDS a very high cost. */
total = 1000;
break;
default:
total = 2;
}
switch (code)
{
case REG:
return ! CHEAP_REG (x);
case SUBREG:
/* If we can't tie these modes, make this expensive. The larger
the mode, the more expensive it is. */
if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x))))
return COSTS_N_INSNS (2
+ GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD);
return 2;
#ifdef RTX_COSTS
RTX_COSTS (x, code, outer_code);
#endif
#ifdef CONST_COSTS
CONST_COSTS (x, code, outer_code);
#endif
default:
#ifdef DEFAULT_RTX_COSTS
DEFAULT_RTX_COSTS(x, code, outer_code);
#endif
break;
}
/* Sum the costs of the sub-rtx's, plus cost of this operation,
which is already in total. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
total += rtx_cost (XEXP (x, i), code);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
total += rtx_cost (XVECEXP (x, i, j), code);
return total;
}
static struct cse_reg_info *
get_cse_reg_info (regno)
int regno;
{
struct cse_reg_info *cri;
splay_tree_node n;
/* See if we already have this entry. */
n = splay_tree_lookup (cse_reg_info_tree,
(splay_tree_key) regno);
if (n)
cri = (struct cse_reg_info *) (n->value);
else
{
/* Get a new cse_reg_info structure. */
if (cse_reg_info_free_list)
{
cri = cse_reg_info_free_list;
cse_reg_info_free_list = cri->variant.next;
}
else
cri = (struct cse_reg_info *) xmalloc (sizeof (struct cse_reg_info));
/* Initialize it. */
cri->variant.reg_tick = 0;
cri->reg_in_table = -1;
cri->reg_qty = regno;
splay_tree_insert (cse_reg_info_tree,
(splay_tree_key) regno,
(splay_tree_value) cri);
}
/* Cache this lookup; we tend to be looking up information about the
same register several times in a row. */
cached_regno = regno;
cached_cse_reg_info = cri;
return cri;
}
static void
free_cse_reg_info (v)
splay_tree_value v;
{
struct cse_reg_info *cri = (struct cse_reg_info *) v;
cri->variant.next = cse_reg_info_free_list;
cse_reg_info_free_list = cri;
}
/* Clear the hash table and initialize each register with its own quantity,
for a new basic block. */
static void
new_basic_block ()
{
register int i;
next_qty = max_reg;
if (cse_reg_info_tree)
{
splay_tree_delete (cse_reg_info_tree);
cached_cse_reg_info = 0;
}
cse_reg_info_tree = splay_tree_new (splay_tree_compare_ints, 0,
free_cse_reg_info);
CLEAR_HARD_REG_SET (hard_regs_in_table);
/* The per-quantity values used to be initialized here, but it is
much faster to initialize each as it is made in `make_new_qty'. */
for (i = 0; i < NBUCKETS; i++)
{
register struct table_elt *this, *next;
for (this = table[i]; this; this = next)
{
next = this->next_same_hash;
free_element (this);
}
}
bzero ((char *) table, sizeof table);
prev_insn = 0;
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
#endif
}
/* Say that register REG contains a quantity not in any register before
and initialize that quantity. */
static void
make_new_qty (reg)
register int reg;
{
register int q;
if (next_qty >= max_qty)
abort ();
q = REG_QTY (reg) = next_qty++;
qty_first_reg[q] = reg;
qty_last_reg[q] = reg;
qty_const[q] = qty_const_insn[q] = 0;
qty_comparison_code[q] = UNKNOWN;
reg_next_eqv[reg] = reg_prev_eqv[reg] = -1;
}
/* Make reg NEW equivalent to reg OLD.
OLD is not changing; NEW is. */
static void
make_regs_eqv (new, old)
register int new, old;
{
register int lastr, firstr;
register int q = REG_QTY (old);
/* Nothing should become eqv until it has a "non-invalid" qty number. */
if (! REGNO_QTY_VALID_P (old))
abort ();
REG_QTY (new) = q;
firstr = qty_first_reg[q];
lastr = qty_last_reg[q];
/* Prefer fixed hard registers to anything. Prefer pseudo regs to other
hard regs. Among pseudos, if NEW will live longer than any other reg
of the same qty, and that is beyond the current basic block,
make it the new canonical replacement for this qty. */
if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr))
/* Certain fixed registers might be of the class NO_REGS. This means
that not only can they not be allocated by the compiler, but
they cannot be used in substitutions or canonicalizations
either. */
&& (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS)
&& ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new))
|| (new >= FIRST_PSEUDO_REGISTER
&& (firstr < FIRST_PSEUDO_REGISTER
|| ((uid_cuid[REGNO_LAST_UID (new)] > cse_basic_block_end
|| (uid_cuid[REGNO_FIRST_UID (new)]
< cse_basic_block_start))
&& (uid_cuid[REGNO_LAST_UID (new)]
> uid_cuid[REGNO_LAST_UID (firstr)]))))))
{
reg_prev_eqv[firstr] = new;
reg_next_eqv[new] = firstr;
reg_prev_eqv[new] = -1;
qty_first_reg[q] = new;
}
else
{
/* If NEW is a hard reg (known to be non-fixed), insert at end.
Otherwise, insert before any non-fixed hard regs that are at the
end. Registers of class NO_REGS cannot be used as an
equivalent for anything. */
while (lastr < FIRST_PSEUDO_REGISTER && reg_prev_eqv[lastr] >= 0
&& (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr))
&& new >= FIRST_PSEUDO_REGISTER)
lastr = reg_prev_eqv[lastr];
reg_next_eqv[new] = reg_next_eqv[lastr];
if (reg_next_eqv[lastr] >= 0)
reg_prev_eqv[reg_next_eqv[lastr]] = new;
else
qty_last_reg[q] = new;
reg_next_eqv[lastr] = new;
reg_prev_eqv[new] = lastr;
}
}
/* Remove REG from its equivalence class. */
static void
delete_reg_equiv (reg)
register int reg;
{
register int q = REG_QTY (reg);
register int p, n;
/* If invalid, do nothing. */
if (q == reg)
return;
p = reg_prev_eqv[reg];
n = reg_next_eqv[reg];
if (n != -1)
reg_prev_eqv[n] = p;
else
qty_last_reg[q] = p;
if (p != -1)
reg_next_eqv[p] = n;
else
qty_first_reg[q] = n;
REG_QTY (reg) = reg;
}
/* Remove any invalid expressions from the hash table
that refer to any of the registers contained in expression X.
Make sure that newly inserted references to those registers
as subexpressions will be considered valid.
mention_regs is not called when a register itself
is being stored in the table.
Return 1 if we have done something that may have changed the hash code
of X. */
static int
mention_regs (x)
rtx x;
{
register enum rtx_code code;
register int i, j;
register char *fmt;
register int changed = 0;
if (x == 0)
return 0;
code = GET_CODE (x);
if (code == REG)
{
register int regno = REGNO (x);
register int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (x)));
int i;
for (i = regno; i < endregno; i++)
{
if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
remove_invalid_refs (i);
REG_IN_TABLE (i) = REG_TICK (i);
}
return 0;
}
/* If this is a SUBREG, we don't want to discard other SUBREGs of the same
pseudo if they don't use overlapping words. We handle only pseudos
here for simplicity. */
if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
{
int i = REGNO (SUBREG_REG (x));
if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
{
/* If reg_tick has been incremented more than once since
reg_in_table was last set, that means that the entire
register has been set before, so discard anything memorized
for the entrire register, including all SUBREG expressions. */
if (REG_IN_TABLE (i) != REG_TICK (i) - 1)
remove_invalid_refs (i);
else
remove_invalid_subreg_refs (i, SUBREG_WORD (x), GET_MODE (x));
}
REG_IN_TABLE (i) = REG_TICK (i);
return 0;
}
/* If X is a comparison or a COMPARE and either operand is a register
that does not have a quantity, give it one. This is so that a later
call to record_jump_equiv won't cause X to be assigned a different
hash code and not found in the table after that call.
It is not necessary to do this here, since rehash_using_reg can
fix up the table later, but doing this here eliminates the need to
call that expensive function in the most common case where the only
use of the register is in the comparison. */
if (code == COMPARE || GET_RTX_CLASS (code) == '<')
{
if (GET_CODE (XEXP (x, 0)) == REG
&& ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
if (insert_regs (XEXP (x, 0), NULL_PTR, 0))
{
rehash_using_reg (XEXP (x, 0));
changed = 1;
}
if (GET_CODE (XEXP (x, 1)) == REG
&& ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
if (insert_regs (XEXP (x, 1), NULL_PTR, 0))
{
rehash_using_reg (XEXP (x, 1));
changed = 1;
}
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
changed |= mention_regs (XEXP (x, i));
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
changed |= mention_regs (XVECEXP (x, i, j));
return changed;
}
/* Update the register quantities for inserting X into the hash table
with a value equivalent to CLASSP.
(If the class does not contain a REG, it is irrelevant.)
If MODIFIED is nonzero, X is a destination; it is being modified.
Note that delete_reg_equiv should be called on a register
before insert_regs is done on that register with MODIFIED != 0.
Nonzero value means that elements of reg_qty have changed
so X's hash code may be different. */
static int
insert_regs (x, classp, modified)
rtx x;
struct table_elt *classp;
int modified;
{
if (GET_CODE (x) == REG)
{
register int regno = REGNO (x);
/* If REGNO is in the equivalence table already but is of the
wrong mode for that equivalence, don't do anything here. */
if (REGNO_QTY_VALID_P (regno)
&& qty_mode[REG_QTY (regno)] != GET_MODE (x))
return 0;
if (modified || ! REGNO_QTY_VALID_P (regno))
{
if (classp)
for (classp = classp->first_same_value;
classp != 0;
classp = classp->next_same_value)
if (GET_CODE (classp->exp) == REG
&& GET_MODE (classp->exp) == GET_MODE (x))
{
make_regs_eqv (regno, REGNO (classp->exp));
return 1;
}
make_new_qty (regno);
qty_mode[REG_QTY (regno)] = GET_MODE (x);
return 1;
}
return 0;
}
/* If X is a SUBREG, we will likely be inserting the inner register in the
table. If that register doesn't have an assigned quantity number at
this point but does later, the insertion that we will be doing now will
not be accessible because its hash code will have changed. So assign
a quantity number now. */
else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
&& ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x))))
{
int regno = REGNO (SUBREG_REG (x));
insert_regs (SUBREG_REG (x), NULL_PTR, 0);
/* Mention_regs checks if REG_TICK is exactly one larger than
REG_IN_TABLE to find out if there was only a single preceding
invalidation - for the SUBREG - or another one, which would be
for the full register. Since we don't invalidate the SUBREG
here first, we might have to bump up REG_TICK so that mention_regs
will do the right thing. */
if (REG_IN_TABLE (regno) >= 0
&& REG_TICK (regno) == REG_IN_TABLE (regno) + 1)
REG_TICK (regno)++;
mention_regs (x);
return 1;
}
else
return mention_regs (x);
}
/* Look in or update the hash table. */
/* Put the element ELT on the list of free elements. */
static void
free_element (elt)
struct table_elt *elt;
{
elt->next_same_hash = free_element_chain;
free_element_chain = elt;
}
/* Return an element that is free for use. */
static struct table_elt *
get_element ()
{
struct table_elt *elt = free_element_chain;
if (elt)
{
free_element_chain = elt->next_same_hash;
return elt;
}
n_elements_made++;
return (struct table_elt *) oballoc (sizeof (struct table_elt));
}
/* Remove table element ELT from use in the table.
HASH is its hash code, made using the HASH macro.
It's an argument because often that is known in advance
and we save much time not recomputing it. */
static void
remove_from_table (elt, hash)
register struct table_elt *elt;
unsigned hash;
{
if (elt == 0)
return;
/* Mark this element as removed. See cse_insn. */
elt->first_same_value = 0;
/* Remove the table element from its equivalence class. */
{
register struct table_elt *prev = elt->prev_same_value;
register struct table_elt *next = elt->next_same_value;
if (next) next->prev_same_value = prev;
if (prev)
prev->next_same_value = next;
else
{
register struct table_elt *newfirst = next;
while (next)
{
next->first_same_value = newfirst;
next = next->next_same_value;
}
}
}
/* Remove the table element from its hash bucket. */
{
register struct table_elt *prev = elt->prev_same_hash;
register struct table_elt *next = elt->next_same_hash;
if (next) next->prev_same_hash = prev;
if (prev)
prev->next_same_hash = next;
else if (table[hash] == elt)
table[hash] = next;
else
{
/* This entry is not in the proper hash bucket. This can happen
when two classes were merged by `merge_equiv_classes'. Search
for the hash bucket that it heads. This happens only very
rarely, so the cost is acceptable. */
for (hash = 0; hash < NBUCKETS; hash++)
if (table[hash] == elt)
table[hash] = next;
}
}
/* Remove the table element from its related-value circular chain. */
if (elt->related_value != 0 && elt->related_value != elt)
{
register struct table_elt *p = elt->related_value;
while (p->related_value != elt)
p = p->related_value;
p->related_value = elt->related_value;
if (p->related_value == p)
p->related_value = 0;
}
free_element (elt);
}
/* Look up X in the hash table and return its table element,
or 0 if X is not in the table.
MODE is the machine-mode of X, or if X is an integer constant
with VOIDmode then MODE is the mode with which X will be used.
Here we are satisfied to find an expression whose tree structure
looks like X. */
static struct table_elt *
lookup (x, hash, mode)
rtx x;
unsigned hash;
enum machine_mode mode;
{
register struct table_elt *p;
for (p = table[hash]; p; p = p->next_same_hash)
if (mode == p->mode && ((x == p->exp && GET_CODE (x) == REG)
|| exp_equiv_p (x, p->exp, GET_CODE (x) != REG, 0)))
return p;
return 0;
}
/* Like `lookup' but don't care whether the table element uses invalid regs.
Also ignore discrepancies in the machine mode of a register. */
static struct table_elt *
lookup_for_remove (x, hash, mode)
rtx x;
unsigned hash;
enum machine_mode mode;
{
register struct table_elt *p;
if (GET_CODE (x) == REG)
{
int regno = REGNO (x);
/* Don't check the machine mode when comparing registers;
invalidating (REG:SI 0) also invalidates (REG:DF 0). */
for (p = table[hash]; p; p = p->next_same_hash)
if (GET_CODE (p->exp) == REG
&& REGNO (p->exp) == regno)
return p;
}
else
{
for (p = table[hash]; p; p = p->next_same_hash)
if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0, 0)))
return p;
}
return 0;
}
/* Look for an expression equivalent to X and with code CODE.
If one is found, return that expression. */
static rtx
lookup_as_function (x, code)
rtx x;
enum rtx_code code;
{
register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS,
GET_MODE (x));
/* If we are looking for a CONST_INT, the mode doesn't really matter, as
long as we are narrowing. So if we looked in vain for a mode narrower
than word_mode before, look for word_mode now. */
if (p == 0 && code == CONST_INT
&& GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode))
{
x = copy_rtx (x);
PUT_MODE (x, word_mode);
p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS, word_mode);
}
if (p == 0)
return 0;
for (p = p->first_same_value; p; p = p->next_same_value)
{
if (GET_CODE (p->exp) == code
/* Make sure this is a valid entry in the table. */
&& exp_equiv_p (p->exp, p->exp, 1, 0))
return p->exp;
}
return 0;
}
/* Insert X in the hash table, assuming HASH is its hash code
and CLASSP is an element of the class it should go in
(or 0 if a new class should be made).
It is inserted at the proper position to keep the class in
the order cheapest first.
MODE is the machine-mode of X, or if X is an integer constant
with VOIDmode then MODE is the mode with which X will be used.
For elements of equal cheapness, the most recent one
goes in front, except that the first element in the list
remains first unless a cheaper element is added. The order of
pseudo-registers does not matter, as canon_reg will be called to
find the cheapest when a register is retrieved from the table.
The in_memory field in the hash table element is set to 0.
The caller must set it nonzero if appropriate.
You should call insert_regs (X, CLASSP, MODIFY) before calling here,
and if insert_regs returns a nonzero value
you must then recompute its hash code before calling here.
If necessary, update table showing constant values of quantities. */
#define CHEAPER(X,Y) ((X)->cost < (Y)->cost)
static struct table_elt *
insert (x, classp, hash, mode)
register rtx x;
register struct table_elt *classp;
unsigned hash;
enum machine_mode mode;
{
register struct table_elt *elt;
/* If X is a register and we haven't made a quantity for it,
something is wrong. */
if (GET_CODE (x) == REG && ! REGNO_QTY_VALID_P (REGNO (x)))
abort ();
/* If X is a hard register, show it is being put in the table. */
if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
{
int regno = REGNO (x);
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
int i;
for (i = regno; i < endregno; i++)
SET_HARD_REG_BIT (hard_regs_in_table, i);
}
/* If X is a label, show we recorded it. */
if (GET_CODE (x) == LABEL_REF
|| (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF))
recorded_label_ref = 1;
/* Put an element for X into the right hash bucket. */
elt = get_element ();
elt->exp = x;
elt->cost = COST (x);
elt->next_same_value = 0;
elt->prev_same_value = 0;
elt->next_same_hash = table[hash];
elt->prev_same_hash = 0;
elt->related_value = 0;
elt->in_memory = 0;
elt->mode = mode;
elt->is_const = (CONSTANT_P (x)
/* GNU C++ takes advantage of this for `this'
(and other const values). */
|| (RTX_UNCHANGING_P (x)
&& GET_CODE (x) == REG
&& REGNO (x) >= FIRST_PSEUDO_REGISTER)
|| FIXED_BASE_PLUS_P (x));
if (table[hash])
table[hash]->prev_same_hash = elt;
table[hash] = elt;
/* Put it into the proper value-class. */
if (classp)
{
classp = classp->first_same_value;
if (CHEAPER (elt, classp))
/* Insert at the head of the class */
{
register struct table_elt *p;
elt->next_same_value = classp;
classp->prev_same_value = elt;
elt->first_same_value = elt;
for (p = classp; p; p = p->next_same_value)
p->first_same_value = elt;
}
else
{
/* Insert not at head of the class. */
/* Put it after the last element cheaper than X. */
register struct table_elt *p, *next;
for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt);
p = next);
/* Put it after P and before NEXT. */
elt->next_same_value = next;
if (next)
next->prev_same_value = elt;
elt->prev_same_value = p;
p->next_same_value = elt;
elt->first_same_value = classp;
}
}
else
elt->first_same_value = elt;
/* If this is a constant being set equivalent to a register or a register
being set equivalent to a constant, note the constant equivalence.
If this is a constant, it cannot be equivalent to a different constant,
and a constant is the only thing that can be cheaper than a register. So
we know the register is the head of the class (before the constant was
inserted).
If this is a register that is not already known equivalent to a
constant, we must check the entire class.
If this is a register that is already known equivalent to an insn,
update `qty_const_insn' to show that `this_insn' is the latest
insn making that quantity equivalent to the constant. */
if (elt->is_const && classp && GET_CODE (classp->exp) == REG
&& GET_CODE (x) != REG)
{
qty_const[REG_QTY (REGNO (classp->exp))]
= gen_lowpart_if_possible (qty_mode[REG_QTY (REGNO (classp->exp))], x);
qty_const_insn[REG_QTY (REGNO (classp->exp))] = this_insn;
}
else if (GET_CODE (x) == REG && classp && ! qty_const[REG_QTY (REGNO (x))]
&& ! elt->is_const)
{
register struct table_elt *p;
for (p = classp; p != 0; p = p->next_same_value)
{
if (p->is_const && GET_CODE (p->exp) != REG)
{
qty_const[REG_QTY (REGNO (x))]
= gen_lowpart_if_possible (GET_MODE (x), p->exp);
qty_const_insn[REG_QTY (REGNO (x))] = this_insn;
break;
}
}
}
else if (GET_CODE (x) == REG && qty_const[REG_QTY (REGNO (x))]
&& GET_MODE (x) == qty_mode[REG_QTY (REGNO (x))])
qty_const_insn[REG_QTY (REGNO (x))] = this_insn;
/* If this is a constant with symbolic value,
and it has a term with an explicit integer value,
link it up with related expressions. */
if (GET_CODE (x) == CONST)
{
rtx subexp = get_related_value (x);
unsigned subhash;
struct table_elt *subelt, *subelt_prev;
if (subexp != 0)
{
/* Get the integer-free subexpression in the hash table. */
subhash = safe_hash (subexp, mode) % NBUCKETS;
subelt = lookup (subexp, subhash, mode);
if (subelt == 0)
subelt = insert (subexp, NULL_PTR, subhash, mode);
/* Initialize SUBELT's circular chain if it has none. */
if (subelt->related_value == 0)
subelt->related_value = subelt;
/* Find the element in the circular chain that precedes SUBELT. */
subelt_prev = subelt;
while (subelt_prev->related_value != subelt)
subelt_prev = subelt_prev->related_value;
/* Put new ELT into SUBELT's circular chain just before SUBELT.
This way the element that follows SUBELT is the oldest one. */
elt->related_value = subelt_prev->related_value;
subelt_prev->related_value = elt;
}
}
return elt;
}
/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from
CLASS2 into CLASS1. This is done when we have reached an insn which makes
the two classes equivalent.
CLASS1 will be the surviving class; CLASS2 should not be used after this
call.
Any invalid entries in CLASS2 will not be copied. */
static void
merge_equiv_classes (class1, class2)
struct table_elt *class1, *class2;
{
struct table_elt *elt, *next, *new;
/* Ensure we start with the head of the classes. */
class1 = class1->first_same_value;
class2 = class2->first_same_value;
/* If they were already equal, forget it. */
if (class1 == class2)
return;
for (elt = class2; elt; elt = next)
{
unsigned hash;
rtx exp = elt->exp;
enum machine_mode mode = elt->mode;
next = elt->next_same_value;
/* Remove old entry, make a new one in CLASS1's class.
Don't do this for invalid entries as we cannot find their
hash code (it also isn't necessary). */
if (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0))
{
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
hash = HASH (exp, mode);
if (GET_CODE (exp) == REG)
delete_reg_equiv (REGNO (exp));
remove_from_table (elt, hash);
if (insert_regs (exp, class1, 0))
{
rehash_using_reg (exp);
hash = HASH (exp, mode);
}
new = insert (exp, class1, hash, mode);
new->in_memory = hash_arg_in_memory;
new->in_struct = hash_arg_in_struct;
}
}
}
/* Flush the entire hash table. */
static void
flush_hash_table ()
{
int i;
struct table_elt *p;
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = table[i])
{
/* Note that invalidate can remove elements
after P in the current hash chain. */
if (GET_CODE (p->exp) == REG)
invalidate (p->exp, p->mode);
else
remove_from_table (p, i);
}
}
/* Remove from the hash table, or mark as invalid,
all expressions whose values could be altered by storing in X.
X is a register, a subreg, or a memory reference with nonvarying address
(because, when a memory reference with a varying address is stored in,
all memory references are removed by invalidate_memory
so specific invalidation is superfluous).
FULL_MODE, if not VOIDmode, indicates that this much should be invalidated
instead of just the amount indicated by the mode of X. This is only used
for bitfield stores into memory.
A nonvarying address may be just a register or just
a symbol reference, or it may be either of those plus
a numeric offset. */
static void
invalidate (x, full_mode)
rtx x;
enum machine_mode full_mode;
{
register int i;
register struct table_elt *p;
/* If X is a register, dependencies on its contents
are recorded through the qty number mechanism.
Just change the qty number of the register,
mark it as invalid for expressions that refer to it,
and remove it itself. */
if (GET_CODE (x) == REG)
{
register int regno = REGNO (x);
register unsigned hash = HASH (x, GET_MODE (x));
/* Remove REGNO from any quantity list it might be on and indicate
that its value might have changed. If it is a pseudo, remove its
entry from the hash table.
For a hard register, we do the first two actions above for any
additional hard registers corresponding to X. Then, if any of these
registers are in the table, we must remove any REG entries that
overlap these registers. */
delete_reg_equiv (regno);
REG_TICK (regno)++;
if (regno >= FIRST_PSEUDO_REGISTER)
{
/* Because a register can be referenced in more than one mode,
we might have to remove more than one table entry. */
struct table_elt *elt;
while ((elt = lookup_for_remove (x, hash, GET_MODE (x))))
remove_from_table (elt, hash);
}
else
{
HOST_WIDE_INT in_table
= TEST_HARD_REG_BIT (hard_regs_in_table, regno);
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
int tregno, tendregno;
register struct table_elt *p, *next;
CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
for (i = regno + 1; i < endregno; i++)
{
in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, i);
CLEAR_HARD_REG_BIT (hard_regs_in_table, i);
delete_reg_equiv (i);
REG_TICK (i)++;
}
if (in_table)
for (hash = 0; hash < NBUCKETS; hash++)
for (p = table[hash]; p; p = next)
{
next = p->next_same_hash;
if (GET_CODE (p->exp) != REG
|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
continue;
tregno = REGNO (p->exp);
tendregno
= tregno + HARD_REGNO_NREGS (tregno, GET_MODE (p->exp));
if (tendregno > regno && tregno < endregno)
remove_from_table (p, hash);
}
}
return;
}
if (GET_CODE (x) == SUBREG)
{
if (GET_CODE (SUBREG_REG (x)) != REG)
abort ();
invalidate (SUBREG_REG (x), VOIDmode);
return;
}
/* If X is a parallel, invalidate all of its elements. */
if (GET_CODE (x) == PARALLEL)
{
for (i = XVECLEN (x, 0) - 1; i >= 0 ; --i)
invalidate (XVECEXP (x, 0, i), VOIDmode);
return;
}
/* If X is an expr_list, this is part of a disjoint return value;
extract the location in question ignoring the offset. */
if (GET_CODE (x) == EXPR_LIST)
{
invalidate (XEXP (x, 0), VOIDmode);
return;
}
/* X is not a register; it must be a memory reference with
a nonvarying address. Remove all hash table elements
that refer to overlapping pieces of memory. */
if (GET_CODE (x) != MEM)
abort ();
if (full_mode == VOIDmode)
full_mode = GET_MODE (x);
for (i = 0; i < NBUCKETS; i++)
{
register struct table_elt *next;
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
/* Invalidate ASM_OPERANDS which reference memory (this is easier
than checking all the aliases). */
if (p->in_memory
&& (GET_CODE (p->exp) != MEM
|| true_dependence (x, full_mode, p->exp, cse_rtx_varies_p)))
remove_from_table (p, i);
}
}
}
/* Remove all expressions that refer to register REGNO,
since they are already invalid, and we are about to
mark that register valid again and don't want the old
expressions to reappear as valid. */
static void
remove_invalid_refs (regno)
int regno;
{
register int i;
register struct table_elt *p, *next;
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (GET_CODE (p->exp) != REG
&& refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR))
remove_from_table (p, i);
}
}
/* Likewise for a subreg with subreg_reg WORD and mode MODE. */
static void
remove_invalid_subreg_refs (regno, word, mode)
int regno;
int word;
enum machine_mode mode;
{
register int i;
register struct table_elt *p, *next;
int end = word + (GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD;
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = next)
{
rtx exp;
next = p->next_same_hash;
exp = p->exp;
if (GET_CODE (p->exp) != REG
&& (GET_CODE (exp) != SUBREG
|| GET_CODE (SUBREG_REG (exp)) != REG
|| REGNO (SUBREG_REG (exp)) != regno
|| (((SUBREG_WORD (exp)
+ (GET_MODE_SIZE (GET_MODE (exp)) - 1) / UNITS_PER_WORD)
>= word)
&& SUBREG_WORD (exp) <= end))
&& refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR))
remove_from_table (p, i);
}
}
/* Recompute the hash codes of any valid entries in the hash table that
reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG.
This is called when we make a jump equivalence. */
static void
rehash_using_reg (x)
rtx x;
{
unsigned int i;
struct table_elt *p, *next;
unsigned hash;
if (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
/* If X is not a register or if the register is known not to be in any
valid entries in the table, we have no work to do. */
if (GET_CODE (x) != REG
|| REG_IN_TABLE (REGNO (x)) < 0
|| REG_IN_TABLE (REGNO (x)) != REG_TICK (REGNO (x)))
return;
/* Scan all hash chains looking for valid entries that mention X.
If we find one and it is in the wrong hash chain, move it. We can skip
objects that are registers, since they are handled specially. */
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp)
&& exp_equiv_p (p->exp, p->exp, 1, 0)
&& i != (hash = safe_hash (p->exp, p->mode) % NBUCKETS))
{
if (p->next_same_hash)
p->next_same_hash->prev_same_hash = p->prev_same_hash;
if (p->prev_same_hash)
p->prev_same_hash->next_same_hash = p->next_same_hash;
else
table[i] = p->next_same_hash;
p->next_same_hash = table[hash];
p->prev_same_hash = 0;
if (table[hash])
table[hash]->prev_same_hash = p;
table[hash] = p;
}
}
}
/* Remove from the hash table any expression that is a call-clobbered
register. Also update their TICK values. */
static void
invalidate_for_call ()
{
int regno, endregno;
int i;
unsigned hash;
struct table_elt *p, *next;
int in_table = 0;
/* Go through all the hard registers. For each that is clobbered in
a CALL_INSN, remove the register from quantity chains and update
reg_tick if defined. Also see if any of these registers is currently
in the table. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
{
delete_reg_equiv (regno);
if (REG_TICK (regno) >= 0)
REG_TICK (regno)++;
in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
}
/* In the case where we have no call-clobbered hard registers in the
table, we are done. Otherwise, scan the table and remove any
entry that overlaps a call-clobbered register. */
if (in_table)
for (hash = 0; hash < NBUCKETS; hash++)
for (p = table[hash]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory)
{
remove_from_table (p, hash);
continue;
}
if (GET_CODE (p->exp) != REG
|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
continue;
regno = REGNO (p->exp);
endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (p->exp));
for (i = regno; i < endregno; i++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
{
remove_from_table (p, hash);
break;
}
}
}
/* Given an expression X of type CONST,
and ELT which is its table entry (or 0 if it
is not in the hash table),
return an alternate expression for X as a register plus integer.
If none can be found, return 0. */
static rtx
use_related_value (x, elt)
rtx x;
struct table_elt *elt;
{
register struct table_elt *relt = 0;
register struct table_elt *p, *q;
HOST_WIDE_INT offset;
/* First, is there anything related known?
If we have a table element, we can tell from that.
Otherwise, must look it up. */
if (elt != 0 && elt->related_value != 0)
relt = elt;
else if (elt == 0 && GET_CODE (x) == CONST)
{
rtx subexp = get_related_value (x);
if (subexp != 0)
relt = lookup (subexp,
safe_hash (subexp, GET_MODE (subexp)) % NBUCKETS,
GET_MODE (subexp));
}
if (relt == 0)
return 0;
/* Search all related table entries for one that has an
equivalent register. */
p = relt;
while (1)
{
/* This loop is strange in that it is executed in two different cases.
The first is when X is already in the table. Then it is searching
the RELATED_VALUE list of X's class (RELT). The second case is when
X is not in the table. Then RELT points to a class for the related
value.
Ensure that, whatever case we are in, that we ignore classes that have
the same value as X. */
if (rtx_equal_p (x, p->exp))
q = 0;
else
for (q = p->first_same_value; q; q = q->next_same_value)
if (GET_CODE (q->exp) == REG)
break;
if (q)
break;
p = p->related_value;
/* We went all the way around, so there is nothing to be found.
Alternatively, perhaps RELT was in the table for some other reason
and it has no related values recorded. */
if (p == relt || p == 0)
break;
}
if (q == 0)
return 0;
offset = (get_integer_term (x) - get_integer_term (p->exp));
/* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */
return plus_constant (q->exp, offset);
}
/* Hash an rtx. We are careful to make sure the value is never negative.
Equivalent registers hash identically.
MODE is used in hashing for CONST_INTs only;
otherwise the mode of X is used.
Store 1 in do_not_record if any subexpression is volatile.
Store 1 in hash_arg_in_memory if X contains a MEM rtx
which does not have the RTX_UNCHANGING_P bit set.
In this case, also store 1 in hash_arg_in_struct
if there is a MEM rtx which has the MEM_IN_STRUCT_P bit set.
Note that cse_insn knows that the hash code of a MEM expression
is just (int) MEM plus the hash code of the address. */
static unsigned
canon_hash (x, mode)
rtx x;
enum machine_mode mode;
{
register int i, j;
register unsigned hash = 0;
register enum rtx_code code;
register char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return hash;
code = GET_CODE (x);
switch (code)
{
case REG:
{
register int regno = REGNO (x);
/* On some machines, we can't record any non-fixed hard register,
because extending its life will cause reload problems. We
consider ap, fp, and sp to be fixed for this purpose.
We also consider CCmode registers to be fixed for this purpose;
failure to do so leads to failure to simplify 0<100 type of
conditionals.
On all machines, we can't record any global registers. */
if (regno < FIRST_PSEUDO_REGISTER
&& (global_regs[regno]
|| (SMALL_REGISTER_CLASSES
&& ! fixed_regs[regno]
&& regno != FRAME_POINTER_REGNUM
&& regno != HARD_FRAME_POINTER_REGNUM
&& regno != ARG_POINTER_REGNUM
&& regno != STACK_POINTER_REGNUM
&& GET_MODE_CLASS (GET_MODE (x)) != MODE_CC)))
{
do_not_record = 1;
return 0;
}
hash += ((unsigned) REG << 7) + (unsigned) REG_QTY (regno);
return hash;
}
/* We handle SUBREG of a REG specially because the underlying
reg changes its hash value with every value change; we don't
want to have to forget unrelated subregs when one subreg changes. */
case SUBREG:
{
if (GET_CODE (SUBREG_REG (x)) == REG)
{
hash += (((unsigned) SUBREG << 7)
+ REGNO (SUBREG_REG (x)) + SUBREG_WORD (x));
return hash;
}
break;
}
case CONST_INT:
{
unsigned HOST_WIDE_INT tem = INTVAL (x);
hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
return hash;
}
case CONST_DOUBLE:
/* This is like the general case, except that it only counts
the integers representing the constant. */
hash += (unsigned) code + (unsigned) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
{
unsigned tem = XINT (x, i);
hash += tem;
}
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
return hash;
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
hash
+= ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
return hash;
case SYMBOL_REF:
hash
+= ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0);
return hash;
case MEM:
if (MEM_VOLATILE_P (x))
{
do_not_record = 1;
return 0;
}
if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
{
hash_arg_in_memory = 1;
if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1;
}
/* Now that we have already found this special case,
might as well speed it up as much as possible. */
hash += (unsigned) MEM;
x = XEXP (x, 0);
goto repeat;
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
case PC:
case CC0:
case CALL:
case UNSPEC_VOLATILE:
do_not_record = 1;
return 0;
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
{
do_not_record = 1;
return 0;
}
break;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
hash += (unsigned) code + (unsigned) GET_MODE (x);
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
goto repeat;
}
hash += canon_hash (tem, 0);
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
hash += canon_hash (XVECEXP (x, i, j), 0);
else if (fmt[i] == 's')
{
register unsigned char *p = (unsigned char *) XSTR (x, i);
if (p)
while (*p)
hash += *p++;
}
else if (fmt[i] == 'i')
{
register unsigned tem = XINT (x, i);
hash += tem;
}
else if (fmt[i] == '0')
/* unused */;
else
abort ();
}
return hash;
}
/* Like canon_hash but with no side effects. */
static unsigned
safe_hash (x, mode)
rtx x;
enum machine_mode mode;
{
int save_do_not_record = do_not_record;
int save_hash_arg_in_memory = hash_arg_in_memory;
int save_hash_arg_in_struct = hash_arg_in_struct;
unsigned hash = canon_hash (x, mode);
hash_arg_in_memory = save_hash_arg_in_memory;
hash_arg_in_struct = save_hash_arg_in_struct;
do_not_record = save_do_not_record;
return hash;
}
/* Return 1 iff X and Y would canonicalize into the same thing,
without actually constructing the canonicalization of either one.
If VALIDATE is nonzero,
we assume X is an expression being processed from the rtl
and Y was found in the hash table. We check register refs
in Y for being marked as valid.
If EQUAL_VALUES is nonzero, we allow a register to match a constant value
that is known to be in the register. Ordinarily, we don't allow them
to match, because letting them match would cause unpredictable results
in all the places that search a hash table chain for an equivalent
for a given value. A possible equivalent that has different structure
has its hash code computed from different data. Whether the hash code
is the same as that of the given value is pure luck. */
static int
exp_equiv_p (x, y, validate, equal_values)
rtx x, y;
int validate;
int equal_values;
{
register int i, j;
register enum rtx_code code;
register char *fmt;
/* Note: it is incorrect to assume an expression is equivalent to itself
if VALIDATE is nonzero. */
if (x == y && !validate)
return 1;
if (x == 0 || y == 0)
return x == y;
code = GET_CODE (x);
if (code != GET_CODE (y))
{
if (!equal_values)
return 0;
/* If X is a constant and Y is a register or vice versa, they may be
equivalent. We only have to validate if Y is a register. */
if (CONSTANT_P (x) && GET_CODE (y) == REG
&& REGNO_QTY_VALID_P (REGNO (y))
&& GET_MODE (y) == qty_mode[REG_QTY (REGNO (y))]
&& rtx_equal_p (x, qty_const[REG_QTY (REGNO (y))])
&& (! validate || REG_IN_TABLE (REGNO (y)) == REG_TICK (REGNO (y))))
return 1;
if (CONSTANT_P (y) && code == REG
&& REGNO_QTY_VALID_P (REGNO (x))
&& GET_MODE (x) == qty_mode[REG_QTY (REGNO (x))]
&& rtx_equal_p (y, qty_const[REG_QTY (REGNO (x))]))
return 1;
return 0;
}
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
if (GET_MODE (x) != GET_MODE (y))
return 0;
switch (code)
{
case PC:
case CC0:
return x == y;
case CONST_INT:
return INTVAL (x) == INTVAL (y);
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
case REG:
{
int regno = REGNO (y);
int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (y)));
int i;
/* If the quantities are not the same, the expressions are not
equivalent. If there are and we are not to validate, they
are equivalent. Otherwise, ensure all regs are up-to-date. */
if (REG_QTY (REGNO (x)) != REG_QTY (regno))
return 0;
if (! validate)
return 1;
for (i = regno; i < endregno; i++)
if (REG_IN_TABLE (i) != REG_TICK (i))
return 0;
return 1;
}
/* For commutative operations, check both orders. */
case PLUS:
case MULT:
case AND:
case IOR:
case XOR:
case NE:
case EQ:
return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
validate, equal_values))
|| (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
validate, equal_values)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
validate, equal_values)));
default:
break;
}
/* Compare the elements. If any pair of corresponding elements
fail to match, return 0 for the whole things. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'e':
if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate, equal_values))
return 0;
break;
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
for (j = 0; j < XVECLEN (x, i); j++)
if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j),
validate, equal_values))
return 0;
break;
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case '0':
break;
default:
abort ();
}
}
return 1;
}
/* Return 1 iff any subexpression of X matches Y.
Here we do not require that X or Y be valid (for registers referred to)
for being in the hash table. */
static int
refers_to_p (x, y)
rtx x, y;
{
register int i;
register enum rtx_code code;
register char *fmt;
repeat:
if (x == y)
return 1;
if (x == 0 || y == 0)
return 0;
code = GET_CODE (x);
/* If X as a whole has the same code as Y, they may match.
If so, return 1. */
if (code == GET_CODE (y))
{
if (exp_equiv_p (x, y, 0, 1))
return 1;
}
/* X does not match, so try its subexpressions. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
{
if (i == 0)
{
x = XEXP (x, 0);
goto repeat;
}
else
if (refers_to_p (XEXP (x, i), y))
return 1;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
if (refers_to_p (XVECEXP (x, i, j), y))
return 1;
}
return 0;
}
/* Given ADDR and SIZE (a memory address, and the size of the memory reference),
set PBASE, PSTART, and PEND which correspond to the base of the address,
the starting offset, and ending offset respectively.
ADDR is known to be a nonvarying address. */
/* ??? Despite what the comments say, this function is in fact frequently
passed varying addresses. This does not appear to cause any problems. */
static void
set_nonvarying_address_components (addr, size, pbase, pstart, pend)
rtx addr;
int size;
rtx *pbase;
HOST_WIDE_INT *pstart, *pend;
{
rtx base;
HOST_WIDE_INT start, end;
base = addr;
start = 0;
end = 0;
if (flag_pic && GET_CODE (base) == PLUS
&& XEXP (base, 0) == pic_offset_table_rtx)
base = XEXP (base, 1);
/* Registers with nonvarying addresses usually have constant equivalents;
but the frame pointer register is also possible. */
if (GET_CODE (base) == REG
&& qty_const != 0
&& REGNO_QTY_VALID_P (REGNO (base))
&& qty_mode[REG_QTY (REGNO (base))] == GET_MODE (base)
&& qty_const[REG_QTY (REGNO (base))] != 0)
base = qty_const[REG_QTY (REGNO (base))];
else if (GET_CODE (base) == PLUS
&& GET_CODE (XEXP (base, 1)) == CONST_INT
&& GET_CODE (XEXP (base, 0)) == REG
&& qty_const != 0
&& REGNO_QTY_VALID_P (REGNO (XEXP (base, 0)))
&& (qty_mode[REG_QTY (REGNO (XEXP (base, 0)))]
== GET_MODE (XEXP (base, 0)))
&& qty_const[REG_QTY (REGNO (XEXP (base, 0)))])
{
start = INTVAL (XEXP (base, 1));
base = qty_const[REG_QTY (REGNO (XEXP (base, 0)))];
}
/* This can happen as the result of virtual register instantiation,
if the initial offset is too large to be a valid address. */
else if (GET_CODE (base) == PLUS
&& GET_CODE (XEXP (base, 0)) == REG
&& GET_CODE (XEXP (base, 1)) == REG
&& qty_const != 0
&& REGNO_QTY_VALID_P (REGNO (XEXP (base, 0)))
&& (qty_mode[REG_QTY (REGNO (XEXP (base, 0)))]
== GET_MODE (XEXP (base, 0)))
&& qty_const[REG_QTY (REGNO (XEXP (base, 0)))]
&& REGNO_QTY_VALID_P (REGNO (XEXP (base, 1)))
&& (qty_mode[REG_QTY (REGNO (XEXP (base, 1)))]
== GET_MODE (XEXP (base, 1)))
&& qty_const[REG_QTY (REGNO (XEXP (base, 1)))])
{
rtx tem = qty_const[REG_QTY (REGNO (XEXP (base, 1)))];
base = qty_const[REG_QTY (REGNO (XEXP (base, 0)))];
/* One of the two values must be a constant. */
if (GET_CODE (base) != CONST_INT)
{
if (GET_CODE (tem) != CONST_INT)
abort ();
start = INTVAL (tem);
}
else
{
start = INTVAL (base);
base = tem;
}
}
/* Handle everything that we can find inside an address that has been
viewed as constant. */
while (1)
{
/* If no part of this switch does a "continue", the code outside
will exit this loop. */
switch (GET_CODE (base))
{
case LO_SUM:
/* By definition, operand1 of a LO_SUM is the associated constant
address. Use the associated constant address as the base
instead. */
base = XEXP (base, 1);
continue;
case CONST:
/* Strip off CONST. */
base = XEXP (base, 0);
continue;
case PLUS:
if (GET_CODE (XEXP (base, 1)) == CONST_INT)
{
start += INTVAL (XEXP (base, 1));
base = XEXP (base, 0);
continue;
}
break;
case AND:
/* Handle the case of an AND which is the negative of a power of
two. This is used to represent unaligned memory operations. */
if (GET_CODE (XEXP (base, 1)) == CONST_INT
&& exact_log2 (- INTVAL (XEXP (base, 1))) > 0)
{
set_nonvarying_address_components (XEXP (base, 0), size,
pbase, pstart, pend);
/* Assume the worst misalignment. START is affected, but not
END, so compensate but adjusting SIZE. Don't lose any
constant we already had. */
size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
start += *pstart + INTVAL (XEXP (base, 1)) + 1;
end += *pend;
base = *pbase;
}
break;
default:
break;
}
break;
}
if (GET_CODE (base) == CONST_INT)
{
start += INTVAL (base);
base = const0_rtx;
}
end = start + size;
/* Set the return values. */
*pbase = base;
*pstart = start;
*pend = end;
}
/* Return 1 if X has a value that can vary even between two
executions of the program. 0 means X can be compared reliably
against certain constants or near-constants. */
static int
cse_rtx_varies_p (x)
register rtx x;
{
/* We need not check for X and the equivalence class being of the same
mode because if X is equivalent to a constant in some mode, it
doesn't vary in any mode. */
if (GET_CODE (x) == REG
&& REGNO_QTY_VALID_P (REGNO (x))
&& GET_MODE (x) == qty_mode[REG_QTY (REGNO (x))]
&& qty_const[REG_QTY (REGNO (x))] != 0)
return 0;
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& GET_CODE (XEXP (x, 0)) == REG
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
&& (GET_MODE (XEXP (x, 0))
== qty_mode[REG_QTY (REGNO (XEXP (x, 0)))])
&& qty_const[REG_QTY (REGNO (XEXP (x, 0)))])
return 0;
/* This can happen as the result of virtual register instantiation, if
the initial constant is too large to be a valid address. This gives
us a three instruction sequence, load large offset into a register,
load fp minus a constant into a register, then a MEM which is the
sum of the two `constant' registers. */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == REG
&& GET_CODE (XEXP (x, 1)) == REG
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
&& (GET_MODE (XEXP (x, 0))
== qty_mode[REG_QTY (REGNO (XEXP (x, 0)))])
&& qty_const[REG_QTY (REGNO (XEXP (x, 0)))]
&& REGNO_QTY_VALID_P (REGNO (XEXP (x, 1)))
&& (GET_MODE (XEXP (x, 1))
== qty_mode[REG_QTY (REGNO (XEXP (x, 1)))])
&& qty_const[REG_QTY (REGNO (XEXP (x, 1)))])
return 0;
return rtx_varies_p (x);
}
/* Canonicalize an expression:
replace each register reference inside it
with the "oldest" equivalent register.
If INSN is non-zero and we are replacing a pseudo with a hard register
or vice versa, validate_change is used to ensure that INSN remains valid
after we make our substitution. The calls are made with IN_GROUP non-zero
so apply_change_group must be called upon the outermost return from this
function (unless INSN is zero). The result of apply_change_group can
generally be discarded since the changes we are making are optional. */
static rtx
canon_reg (x, insn)
rtx x;
rtx insn;
{
register int i;
register enum rtx_code code;
register char *fmt;
if (x == 0)
return x;
code = GET_CODE (x);
switch (code)
{
case PC:
case CC0:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return x;
case REG:
{
register int first;
/* Never replace a hard reg, because hard regs can appear
in more than one machine mode, and we must preserve the mode
of each occurrence. Also, some hard regs appear in
MEMs that are shared and mustn't be altered. Don't try to
replace any reg that maps to a reg of class NO_REGS. */
if (REGNO (x) < FIRST_PSEUDO_REGISTER
|| ! REGNO_QTY_VALID_P (REGNO (x)))
return x;
first = qty_first_reg[REG_QTY (REGNO (x))];
return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first]
: REGNO_REG_CLASS (first) == NO_REGS ? x
: gen_rtx_REG (qty_mode[REG_QTY (REGNO (x))], first));
}
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
register int j;
if (fmt[i] == 'e')
{
rtx new = canon_reg (XEXP (x, i), insn);
int insn_code;
/* If replacing pseudo with hard reg or vice versa, ensure the
insn remains valid. Likewise if the insn has MATCH_DUPs. */
if (insn != 0 && new != 0
&& GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG
&& (((REGNO (new) < FIRST_PSEUDO_REGISTER)
!= (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER))
|| (insn_code = recog_memoized (insn)) < 0
|| insn_n_dups[insn_code] > 0))
validate_change (insn, &XEXP (x, i), new, 1);
else
XEXP (x, i) = new;
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j), insn);
}
return x;
}
/* LOC is a location within INSN that is an operand address (the contents of
a MEM). Find the best equivalent address to use that is valid for this
insn.
On most CISC machines, complicated address modes are costly, and rtx_cost
is a good approximation for that cost. However, most RISC machines have
only a few (usually only one) memory reference formats. If an address is
valid at all, it is often just as cheap as any other address. Hence, for
RISC machines, we use the configuration macro `ADDRESS_COST' to compare the
costs of various addresses. For two addresses of equal cost, choose the one
with the highest `rtx_cost' value as that has the potential of eliminating
the most insns. For equal costs, we choose the first in the equivalence
class. Note that we ignore the fact that pseudo registers are cheaper
than hard registers here because we would also prefer the pseudo registers.
*/
static void
find_best_addr (insn, loc)
rtx insn;
rtx *loc;
{
struct table_elt *elt;
rtx addr = *loc;
#ifdef ADDRESS_COST
struct table_elt *p;
int found_better = 1;
#endif
int save_do_not_record = do_not_record;
int save_hash_arg_in_memory = hash_arg_in_memory;
int save_hash_arg_in_struct = hash_arg_in_struct;
int addr_volatile;
int regno;
unsigned hash;
/* Do not try to replace constant addresses or addresses of local and
argument slots. These MEM expressions are made only once and inserted
in many instructions, as well as being used to control symbol table
output. It is not safe to clobber them.
There are some uncommon cases where the address is already in a register
for some reason, but we cannot take advantage of that because we have
no easy way to unshare the MEM. In addition, looking up all stack
addresses is costly. */
if ((GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == REG
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
&& (regno = REGNO (XEXP (addr, 0)),
regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
|| (GET_CODE (addr) == REG
&& (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
|| regno == HARD_FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM))
|| GET_CODE (addr) == ADDRESSOF
|| CONSTANT_ADDRESS_P (addr))
return;
/* If this address is not simply a register, try to fold it. This will
sometimes simplify the expression. Many simplifications
will not be valid, but some, usually applying the associative rule, will
be valid and produce better code. */
if (GET_CODE (addr) != REG)
{
rtx folded = fold_rtx (copy_rtx (addr), NULL_RTX);
if (1
#ifdef ADDRESS_COST
&& (CSE_ADDRESS_COST (folded) < CSE_ADDRESS_COST (addr)
|| (CSE_ADDRESS_COST (folded) == CSE_ADDRESS_COST (addr)
&& rtx_cost (folded, MEM) > rtx_cost (addr, MEM)))
#else
&& rtx_cost (folded, MEM) < rtx_cost (addr, MEM)
#endif
&& validate_change (insn, loc, folded, 0))
addr = folded;
}
/* If this address is not in the hash table, we can't look for equivalences
of the whole address. Also, ignore if volatile. */
do_not_record = 0;
hash = HASH (addr, Pmode);
addr_volatile = do_not_record;
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
hash_arg_in_struct = save_hash_arg_in_struct;
if (addr_volatile)
return;
elt = lookup (addr, hash, Pmode);
#ifndef ADDRESS_COST
if (elt)
{
int our_cost = elt->cost;
/* Find the lowest cost below ours that works. */
for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
if (elt->cost < our_cost
&& (GET_CODE (elt->exp) == REG
|| exp_equiv_p (elt->exp, elt->exp, 1, 0))
&& validate_change (insn, loc,
canon_reg (copy_rtx (elt->exp), NULL_RTX), 0))
return;
}
#else
if (elt)
{
/* We need to find the best (under the criteria documented above) entry
in the class that is valid. We use the `flag' field to indicate
choices that were invalid and iterate until we can't find a better
one that hasn't already been tried. */
for (p = elt->first_same_value; p; p = p->next_same_value)
p->flag = 0;
while (found_better)
{
int best_addr_cost = CSE_ADDRESS_COST (*loc);
int best_rtx_cost = (elt->cost + 1) >> 1;
struct table_elt *best_elt = elt;
found_better = 0;
for (p = elt->first_same_value; p; p = p->next_same_value)
if (! p->flag)
{
if ((GET_CODE (p->exp) == REG
|| exp_equiv_p (p->exp, p->exp, 1, 0))
&& (CSE_ADDRESS_COST (p->exp) < best_addr_cost
|| (CSE_ADDRESS_COST (p->exp) == best_addr_cost
&& (p->cost + 1) >> 1 > best_rtx_cost)))
{
found_better = 1;
best_addr_cost = CSE_ADDRESS_COST (p->exp);
best_rtx_cost = (p->cost + 1) >> 1;
best_elt = p;
}
}
if (found_better)
{
if (validate_change (insn, loc,
canon_reg (copy_rtx (best_elt->exp),
NULL_RTX), 0))
return;
else
best_elt->flag = 1;
}
}
}
/* If the address is a binary operation with the first operand a register
and the second a constant, do the same as above, but looking for
equivalences of the register. Then try to simplify before checking for
the best address to use. This catches a few cases: First is when we
have REG+const and the register is another REG+const. We can often merge
the constants and eliminate one insn and one register. It may also be
that a machine has a cheap REG+REG+const. Finally, this improves the
code on the Alpha for unaligned byte stores. */
if (flag_expensive_optimizations
&& (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
|| GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
&& GET_CODE (XEXP (*loc, 0)) == REG
&& GET_CODE (XEXP (*loc, 1)) == CONST_INT)
{
rtx c = XEXP (*loc, 1);
do_not_record = 0;
hash = HASH (XEXP (*loc, 0), Pmode);
do_not_record = save_do_not_record;
hash_arg_in_memory = save_hash_arg_in_memory;
hash_arg_in_struct = save_hash_arg_in_struct;
elt = lookup (XEXP (*loc, 0), hash, Pmode);
if (elt == 0)
return;
/* We need to find the best (under the criteria documented above) entry
in the class that is valid. We use the `flag' field to indicate
choices that were invalid and iterate until we can't find a better
one that hasn't already been tried. */
for (p = elt->first_same_value; p; p = p->next_same_value)
p->flag = 0;
while (found_better)
{
int best_addr_cost = CSE_ADDRESS_COST (*loc);
int best_rtx_cost = (COST (*loc) + 1) >> 1;
struct table_elt *best_elt = elt;
rtx best_rtx = *loc;
int count;
/* This is at worst case an O(n^2) algorithm, so limit our search
to the first 32 elements on the list. This avoids trouble
compiling code with very long basic blocks that can easily
call cse_gen_binary so many times that we run out of memory. */
found_better = 0;
for (p = elt->first_same_value, count = 0;
p && count < 32;
p = p->next_same_value, count++)
if (! p->flag
&& (GET_CODE (p->exp) == REG
|| exp_equiv_p (p->exp, p->exp, 1, 0)))
{
rtx new = cse_gen_binary (GET_CODE (*loc), Pmode, p->exp, c);
if ((CSE_ADDRESS_COST (new) < best_addr_cost
|| (CSE_ADDRESS_COST (new) == best_addr_cost
&& (COST (new) + 1) >> 1 > best_rtx_cost)))
{
found_better = 1;
best_addr_cost = CSE_ADDRESS_COST (new);
best_rtx_cost = (COST (new) + 1) >> 1;
best_elt = p;
best_rtx = new;
}
}
if (found_better)
{
if (validate_change (insn, loc,
canon_reg (copy_rtx (best_rtx),
NULL_RTX), 0))
return;
else
best_elt->flag = 1;
}
}
}
#endif
}
/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison
operation (EQ, NE, GT, etc.), follow it back through the hash table and
what values are being compared.
*PARG1 and *PARG2 are updated to contain the rtx representing the values
actually being compared. For example, if *PARG1 was (cc0) and *PARG2
was (const_int 0), *PARG1 and *PARG2 will be set to the objects that were
compared to produce cc0.
The return value is the comparison operator and is either the code of
A or the code corresponding to the inverse of the comparison. */
static enum rtx_code
find_comparison_args (code, parg1, parg2, pmode1, pmode2)
enum rtx_code code;
rtx *parg1, *parg2;
enum machine_mode *pmode1, *pmode2;
{
rtx arg1, arg2;
arg1 = *parg1, arg2 = *parg2;
/* If ARG2 is const0_rtx, see what ARG1 is equivalent to. */
while (arg2 == CONST0_RTX (GET_MODE (arg1)))
{
/* Set non-zero when we find something of interest. */
rtx x = 0;
int reverse_code = 0;
struct table_elt *p = 0;
/* If arg1 is a COMPARE, extract the comparison arguments from it.
On machines with CC0, this is the only case that can occur, since
fold_rtx will return the COMPARE or item being compared with zero
when given CC0. */
if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx)
x = arg1;
/* If ARG1 is a comparison operator and CODE is testing for
STORE_FLAG_VALUE, get the inner arguments. */
else if (GET_RTX_CLASS (GET_CODE (arg1)) == '<')
{
if (code == NE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
&& code == LT && STORE_FLAG_VALUE == -1)
#ifdef FLOAT_STORE_FLAG_VALUE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
)
x = arg1;
else if (code == EQ
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
&& code == GE && STORE_FLAG_VALUE == -1)
#ifdef FLOAT_STORE_FLAG_VALUE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
)
x = arg1, reverse_code = 1;
}
/* ??? We could also check for
(ne (and (eq (...) (const_int 1))) (const_int 0))
and related forms, but let's wait until we see them occurring. */
if (x == 0)
/* Look up ARG1 in the hash table and see if it has an equivalence
that lets us see what is being compared. */
p = lookup (arg1, safe_hash (arg1, GET_MODE (arg1)) % NBUCKETS,
GET_MODE (arg1));
if (p) p = p->first_same_value;
for (; p; p = p->next_same_value)
{
enum machine_mode inner_mode = GET_MODE (p->exp);
/* If the entry isn't valid, skip it. */
if (! exp_equiv_p (p->exp, p->exp, 1, 0))
continue;
if (GET_CODE (p->exp) == COMPARE
/* Another possibility is that this machine has a compare insn
that includes the comparison code. In that case, ARG1 would
be equivalent to a comparison operation that would set ARG1 to
either STORE_FLAG_VALUE or zero. If this is an NE operation,
ORIG_CODE is the actual comparison being done; if it is an EQ,
we must reverse ORIG_CODE. On machine with a negative value
for STORE_FLAG_VALUE, also look at LT and GE operations. */
|| ((code == NE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_INT
&& (GET_MODE_BITSIZE (inner_mode)
<= HOST_BITS_PER_WIDE_INT)
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
)
&& GET_RTX_CLASS (GET_CODE (p->exp)) == '<'))
{
x = p->exp;
break;
}
else if ((code == EQ
|| (code == GE
&& GET_MODE_CLASS (inner_mode) == MODE_INT
&& (GET_MODE_BITSIZE (inner_mode)
<= HOST_BITS_PER_WIDE_INT)
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == GE
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
)
&& GET_RTX_CLASS (GET_CODE (p->exp)) == '<')
{
reverse_code = 1;
x = p->exp;
break;
}
/* If this is fp + constant, the equivalent is a better operand since
it may let us predict the value of the comparison. */
else if (NONZERO_BASE_PLUS_P (p->exp))
{
arg1 = p->exp;
continue;
}
}
/* If we didn't find a useful equivalence for ARG1, we are done.
Otherwise, set up for the next iteration. */
if (x == 0)
break;
arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
if (GET_RTX_CLASS (GET_CODE (x)) == '<')
code = GET_CODE (x);
if (reverse_code)
code = reverse_condition (code);
}
/* Return our results. Return the modes from before fold_rtx
because fold_rtx might produce const_int, and then it's too late. */
*pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
*parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
return code;
}
/* Try to simplify a unary operation CODE whose output mode is to be
MODE with input operand OP whose mode was originally OP_MODE.
Return zero if no simplification can be made. */
rtx
simplify_unary_operation (code, mode, op, op_mode)
enum rtx_code code;
enum machine_mode mode;
rtx op;
enum machine_mode op_mode;
{
register int width = GET_MODE_BITSIZE (mode);
/* The order of these tests is critical so that, for example, we don't
check the wrong mode (input vs. output) for a conversion operation,
such as FIX. At some point, this should be simplified. */
#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
if (code == FLOAT && GET_MODE (op) == VOIDmode
&& (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
{
HOST_WIDE_INT hv, lv;
REAL_VALUE_TYPE d;
if (GET_CODE (op) == CONST_INT)
lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
else
lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op);
#ifdef REAL_ARITHMETIC
REAL_VALUE_FROM_INT (d, lv, hv, mode);
#else
if (hv < 0)
{
d = (double) (~ hv);
d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
* (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
d += (double) (unsigned HOST_WIDE_INT) (~ lv);
d = (- d - 1.0);
}
else
{
d = (double) hv;
d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
* (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
d += (double) (unsigned HOST_WIDE_INT) lv;
}
#endif /* REAL_ARITHMETIC */
d = real_value_truncate (mode, d);
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode
&& (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
{
HOST_WIDE_INT hv, lv;
REAL_VALUE_TYPE d;
if (GET_CODE (op) == CONST_INT)
lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
else
lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op);
if (op_mode == VOIDmode)
{
/* We don't know how to interpret negative-looking numbers in
this case, so don't try to fold those. */
if (hv < 0)
return 0;
}
else if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
;
else
hv = 0, lv &= GET_MODE_MASK (op_mode);
#ifdef REAL_ARITHMETIC
REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
#else
d = (double) (unsigned HOST_WIDE_INT) hv;
d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
* (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
d += (double) (unsigned HOST_WIDE_INT) lv;
#endif /* REAL_ARITHMETIC */
d = real_value_truncate (mode, d);
return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
}
#endif
if (GET_CODE (op) == CONST_INT
&& width <= HOST_BITS_PER_WIDE_INT && width > 0)
{
register HOST_WIDE_INT arg0 = INTVAL (op);
register HOST_WIDE_INT val;
switch (code)
{
case NOT:
val = ~ arg0;
break;
case NEG:
val = - arg0;
break;
case ABS:
val = (arg0 >= 0 ? arg0 : - arg0);
break;
case FFS:
/* Don't use ffs here. Instead, get low order bit and then its
number. If arg0 is zero, this will return 0, as desired. */
arg0 &= GET_MODE_MASK (mode);
val = exact_log2 (arg0 & (- arg0)) + 1;
break;
case TRUNCATE:
val = arg0;
break;
case ZERO_EXTEND:
if (op_mode == VOIDmode)
op_mode = mode;
if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
{
/* If we were really extending the mode,
we would have to distinguish between zero-extension
and sign-extension. */
if (width != GET_MODE_BITSIZE (op_mode))
abort ();
val = arg0;
}
else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
else
return 0;
break;
case SIGN_EXTEND:
if (op_mode == VOIDmode)
op_mode = mode;
if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
{
/* If we were really extending the mode,
we would have to distinguish between zero-extension
and sign-extension. */
if (width != GET_MODE_BITSIZE (op_mode))
abort ();
val = arg0;
}
else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
{
val
= arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
if (val
& ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1)))
val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
}
else
return 0;
break;
case SQRT:
return 0;
default:
abort ();
}
/* Clear the bits that don't belong in our 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. */
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
/* 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
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= ((HOST_WIDE_INT) (-1) << width);
return GEN_INT (val);
}
/* We can do some operations on integer CONST_DOUBLEs. Also allow
for a DImode operation on a CONST_INT. */
else if (GET_MODE (op) == VOIDmode && width <= HOST_BITS_PER_INT * 2
&& (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
{
HOST_WIDE_INT l1, h1, lv, hv;
if (GET_CODE (op) == CONST_DOUBLE)
l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op);
else
l1 = INTVAL (op), h1 = l1 < 0 ? -1 : 0;
switch (code)
{
case NOT:
lv = ~ l1;
hv = ~ h1;
break;
case NEG:
neg_double (l1, h1, &lv, &hv);
break;
case ABS:
if (h1 < 0)
neg_double (l1, h1, &lv, &hv);
else
lv = l1, hv = h1;
break;
case FFS:
hv = 0;
if (l1 == 0)
lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & (-h1)) + 1;
else
lv = exact_log2 (l1 & (-l1)) + 1;
break;
case TRUNCATE:
/* This is just a change-of-mode, so do nothing. */
lv = l1, hv = h1;
break;
case ZERO_EXTEND:
if (op_mode == VOIDmode
|| GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
return 0;
hv = 0;
lv = l1 & GET_MODE_MASK (op_mode);
break;
case SIGN_EXTEND:
if (op_mode == VOIDmode
|| GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
return 0;
else
{
lv = l1 & GET_MODE_MASK (op_mode);
if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT
&& (lv & ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (op_mode) - 1))) != 0)
lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
hv = (lv < 0) ? ~ (HOST_WIDE_INT) 0 : 0;
}
break;
case SQRT:
return 0;
default:
return 0;
}
return immed_double_const (lv, hv, mode);
}
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE_CLASS (mode) == MODE_FLOAT)
{
REAL_VALUE_TYPE d;
jmp_buf handler;
rtx x;
if (setjmp (handler))
/* There used to be a warning here, but that is inadvisable.
People may want to cause traps, and the natural way
to do it should not get a warning. */
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
switch (code)
{
case NEG:
d = REAL_VALUE_NEGATE (d);
break;
case ABS:
if (REAL_VALUE_NEGATIVE (d))
d = REAL_VALUE_NEGATE (d);
break;
case FLOAT_TRUNCATE:
d = real_value_truncate (mode, d);
break;
case FLOAT_EXTEND:
/* All this does is change the mode. */
break;
case FIX:
d = REAL_VALUE_RNDZINT (d);
break;
case UNSIGNED_FIX:
d = REAL_VALUE_UNSIGNED_RNDZINT (d);
break;
case SQRT:
return 0;
default:
abort ();
}
x = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
set_float_handler (NULL_PTR);
return x;
}
else if (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& GET_MODE_CLASS (mode) == MODE_INT
&& width <= HOST_BITS_PER_WIDE_INT && width > 0)
{
REAL_VALUE_TYPE d;
jmp_buf handler;
HOST_WIDE_INT val;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
switch (code)
{
case FIX:
val = REAL_VALUE_FIX (d);
break;
case UNSIGNED_FIX:
val = REAL_VALUE_UNSIGNED_FIX (d);
break;
default:
abort ();
}
set_float_handler (NULL_PTR);
/* Clear the bits that don't belong in our 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. */
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
/* 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
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= ((HOST_WIDE_INT) (-1) << width);
return GEN_INT (val);
}
#endif
/* This was formerly used only for non-IEEE float.
eggert@twinsun.com says it is safe for IEEE also. */
else
{
/* There are some simplifications we can do even if the operands
aren't constant. */
switch (code)
{
case NEG:
case NOT:
/* (not (not X)) == X, similarly for NEG. */
if (GET_CODE (op) == code)
return XEXP (op, 0);
break;
case SIGN_EXTEND:
/* (sign_extend (truncate (minus (label_ref L1) (label_ref L2))))
becomes just the MINUS if its mode is MODE. This allows
folding switch statements on machines using casesi (such as
the Vax). */
if (GET_CODE (op) == TRUNCATE
&& GET_MODE (XEXP (op, 0)) == mode
&& GET_CODE (XEXP (op, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF
&& GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
return XEXP (op, 0);
#ifdef POINTERS_EXTEND_UNSIGNED
if (! POINTERS_EXTEND_UNSIGNED
&& mode == Pmode && GET_MODE (op) == ptr_mode
&& CONSTANT_P (op))
return convert_memory_address (Pmode, op);
#endif
break;
#ifdef POINTERS_EXTEND_UNSIGNED
case ZERO_EXTEND:
if (POINTERS_EXTEND_UNSIGNED
&& mode == Pmode && GET_MODE (op) == ptr_mode
&& CONSTANT_P (op))
return convert_memory_address (Pmode, op);
break;
#endif
default:
break;
}
return 0;
}
}
/* Simplify a binary operation CODE with result mode MODE, operating on OP0
and OP1. Return 0 if no simplification is possible.
Don't use this for relational operations such as EQ or LT.
Use simplify_relational_operation instead. */
rtx
simplify_binary_operation (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
register HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
HOST_WIDE_INT val;
int width = GET_MODE_BITSIZE (mode);
rtx tem;
/* Relational operations don't work here. We must know the mode
of the operands in order to do the comparison correctly.
Assuming a full word can give incorrect results.
Consider comparing 128 with -128 in QImode. */
if (GET_RTX_CLASS (code) == '<')
abort ();
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
if (GET_MODE_CLASS (mode) == MODE_FLOAT
&& GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
&& mode == GET_MODE (op0) && mode == GET_MODE (op1))
{
REAL_VALUE_TYPE f0, f1, value;
jmp_buf handler;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (f0, op0);
REAL_VALUE_FROM_CONST_DOUBLE (f1, op1);
f0 = real_value_truncate (mode, f0);
f1 = real_value_truncate (mode, f1);
#ifdef REAL_ARITHMETIC
#ifndef REAL_INFINITY
if (code == DIV && REAL_VALUES_EQUAL (f1, dconst0))
return 0;
#endif
REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
#else
switch (code)
{
case PLUS:
value = f0 + f1;
break;
case MINUS:
value = f0 - f1;
break;
case MULT:
value = f0 * f1;
break;
case DIV:
#ifndef REAL_INFINITY
if (f1 == 0)
return 0;
#endif
value = f0 / f1;
break;
case SMIN:
value = MIN (f0, f1);
break;
case SMAX:
value = MAX (f0, f1);
break;
default:
abort ();
}
#endif
value = real_value_truncate (mode, value);
set_float_handler (NULL_PTR);
return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
}
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
/* We can fold some multi-word operations. */
if (GET_MODE_CLASS (mode) == MODE_INT
&& width == HOST_BITS_PER_WIDE_INT * 2
&& (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
&& (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
{
HOST_WIDE_INT l1, l2, h1, h2, lv, hv;
if (GET_CODE (op0) == CONST_DOUBLE)
l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
else
l1 = INTVAL (op0), h1 = l1 < 0 ? -1 : 0;
if (GET_CODE (op1) == CONST_DOUBLE)
l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1);
else
l2 = INTVAL (op1), h2 = l2 < 0 ? -1 : 0;
switch (code)
{
case MINUS:
/* A - B == A + (-B). */
neg_double (l2, h2, &lv, &hv);
l2 = lv, h2 = hv;
/* .. fall through ... */
case PLUS:
add_double (l1, h1, l2, h2, &lv, &hv);
break;
case MULT:
mul_double (l1, h1, l2, h2, &lv, &hv);
break;
case DIV: case MOD: case UDIV: case UMOD:
/* We'd need to include tree.h to do this and it doesn't seem worth
it. */
return 0;
case AND:
lv = l1 & l2, hv = h1 & h2;
break;
case IOR:
lv = l1 | l2, hv = h1 | h2;
break;
case XOR:
lv = l1 ^ l2, hv = h1 ^ h2;
break;
case SMIN:
if (h1 < h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
< (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case SMAX:
if (h1 > h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
> (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case UMIN:
if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
< (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case UMAX:
if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2
|| (h1 == h2
&& ((unsigned HOST_WIDE_INT) l1
> (unsigned HOST_WIDE_INT) l2)))
lv = l1, hv = h1;
else
lv = l2, hv = h2;
break;
case LSHIFTRT: case ASHIFTRT:
case ASHIFT:
case ROTATE: case ROTATERT:
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0;
#endif
if (h2 != 0 || l2 < 0 || l2 >= GET_MODE_BITSIZE (mode))
return 0;
if (code == LSHIFTRT || code == ASHIFTRT)
rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
code == ASHIFTRT);
else if (code == ASHIFT)
lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, 1);
else if (code == ROTATE)
lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
else /* code == ROTATERT */
rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
break;
default:
return 0;
}
return immed_double_const (lv, hv, mode);
}
if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT
|| width > HOST_BITS_PER_WIDE_INT || width == 0)
{
/* Even if we can't compute a constant result,
there are some cases worth simplifying. */
switch (code)
{
case PLUS:
/* In IEEE floating point, x+0 is not the same as x. Similarly
for the other optimizations below. */
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& FLOAT_MODE_P (mode) && ! flag_fast_math)
break;
if (op1 == CONST0_RTX (mode))
return op0;
/* ((-a) + b) -> (b - a) and similarly for (a + (-b)) */
if (GET_CODE (op0) == NEG)
return cse_gen_binary (MINUS, mode, op1, XEXP (op0, 0));
else if (GET_CODE (op1) == NEG)
return cse_gen_binary (MINUS, mode, op0, XEXP (op1, 0));
/* Handle both-operands-constant cases. We can only add
CONST_INTs to constants since the sum of relocatable symbols
can't be handled by most assemblers. Don't add CONST_INT
to CONST_INT since overflow won't be computed properly if wider
than HOST_BITS_PER_WIDE_INT. */
if (CONSTANT_P (op0) && GET_MODE (op0) != VOIDmode
&& GET_CODE (op1) == CONST_INT)
return plus_constant (op0, INTVAL (op1));
else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode
&& GET_CODE (op0) == CONST_INT)
return plus_constant (op1, INTVAL (op0));
/* See if this is something like X * C - X or vice versa or
if the multiplication is written as a shift. If so, we can
distribute and make a new multiply, shift, or maybe just
have X (if C is 2 in the example above). But don't make
real multiply if we didn't have one before. */
if (! FLOAT_MODE_P (mode))
{
HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
rtx lhs = op0, rhs = op1;
int had_mult = 0;
if (GET_CODE (lhs) == NEG)
coeff0 = -1, lhs = XEXP (lhs, 0);
else if (GET_CODE (lhs) == MULT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT)
{
coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
had_mult = 1;
}
else if (GET_CODE (lhs) == ASHIFT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT
&& INTVAL (XEXP (lhs, 1)) >= 0
&& INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
lhs = XEXP (lhs, 0);
}
if (GET_CODE (rhs) == NEG)
coeff1 = -1, rhs = XEXP (rhs, 0);
else if (GET_CODE (rhs) == MULT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT)
{
coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
had_mult = 1;
}
else if (GET_CODE (rhs) == ASHIFT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT
&& INTVAL (XEXP (rhs, 1)) >= 0
&& INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
rhs = XEXP (rhs, 0);
}
if (rtx_equal_p (lhs, rhs))
{
tem = cse_gen_binary (MULT, mode, lhs,
GEN_INT (coeff0 + coeff1));
return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
}
}
/* If one of the operands is a PLUS or a MINUS, see if we can
simplify this by the associative law.
Don't use the associative law for floating point.
The inaccuracy makes it nonassociative,
and subtle programs can break if operations are associated. */
if (INTEGRAL_MODE_P (mode)
&& (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|| GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
&& (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
return tem;
break;
case COMPARE:
#ifdef HAVE_cc0
/* Convert (compare FOO (const_int 0)) to FOO unless we aren't
using cc0, in which case we want to leave it as a COMPARE
so we can distinguish it from a register-register-copy.
In IEEE floating point, x-0 is not the same as x. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op1 == CONST0_RTX (mode))
return op0;
#else
/* Do nothing here. */
#endif
break;
case MINUS:
/* None of these optimizations can be done for IEEE
floating point. */
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& FLOAT_MODE_P (mode) && ! flag_fast_math)
break;
/* We can't assume x-x is 0 even with non-IEEE floating point,
but since it is zero except in very strange circumstances, we
will treat it as zero with -ffast-math. */
if (rtx_equal_p (op0, op1)
&& ! side_effects_p (op0)
&& (! FLOAT_MODE_P (mode) || flag_fast_math))
return CONST0_RTX (mode);
/* Change subtraction from zero into negation. */
if (op0 == CONST0_RTX (mode))
return gen_rtx_NEG (mode, op1);
/* (-1 - a) is ~a. */
if (op0 == constm1_rtx)
return gen_rtx_NOT (mode, op1);
/* Subtracting 0 has no effect. */
if (op1 == CONST0_RTX (mode))
return op0;
/* See if this is something like X * C - X or vice versa or
if the multiplication is written as a shift. If so, we can
distribute and make a new multiply, shift, or maybe just
have X (if C is 2 in the example above). But don't make
real multiply if we didn't have one before. */
if (! FLOAT_MODE_P (mode))
{
HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
rtx lhs = op0, rhs = op1;
int had_mult = 0;
if (GET_CODE (lhs) == NEG)
coeff0 = -1, lhs = XEXP (lhs, 0);
else if (GET_CODE (lhs) == MULT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT)
{
coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
had_mult = 1;
}
else if (GET_CODE (lhs) == ASHIFT
&& GET_CODE (XEXP (lhs, 1)) == CONST_INT
&& INTVAL (XEXP (lhs, 1)) >= 0
&& INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
lhs = XEXP (lhs, 0);
}
if (GET_CODE (rhs) == NEG)
coeff1 = - 1, rhs = XEXP (rhs, 0);
else if (GET_CODE (rhs) == MULT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT)
{
coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
had_mult = 1;
}
else if (GET_CODE (rhs) == ASHIFT
&& GET_CODE (XEXP (rhs, 1)) == CONST_INT
&& INTVAL (XEXP (rhs, 1)) >= 0
&& INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
{
coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
rhs = XEXP (rhs, 0);
}
if (rtx_equal_p (lhs, rhs))
{
tem = cse_gen_binary (MULT, mode, lhs,
GEN_INT (coeff0 - coeff1));
return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
}
}
/* (a - (-b)) -> (a + b). */
if (GET_CODE (op1) == NEG)
return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
/* If one of the operands is a PLUS or a MINUS, see if we can
simplify this by the associative law.
Don't use the associative law for floating point.
The inaccuracy makes it nonassociative,
and subtle programs can break if operations are associated. */
if (INTEGRAL_MODE_P (mode)
&& (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
|| GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
&& (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
return tem;
/* Don't let a relocatable value get a negative coeff. */
if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
return plus_constant (op0, - INTVAL (op1));
/* (x - (x & y)) -> (x & ~y) */
if (GET_CODE (op1) == AND)
{
if (rtx_equal_p (op0, XEXP (op1, 0)))
return cse_gen_binary (AND, mode, op0, gen_rtx_NOT (mode, XEXP (op1, 1)));
if (rtx_equal_p (op0, XEXP (op1, 1)))
return cse_gen_binary (AND, mode, op0, gen_rtx_NOT (mode, XEXP (op1, 0)));
}
break;
case MULT:
if (op1 == constm1_rtx)
{
tem = simplify_unary_operation (NEG, mode, op0, mode);
return tem ? tem : gen_rtx_NEG (mode, op0);
}
/* In IEEE floating point, x*0 is not always 0. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op1 == CONST0_RTX (mode)
&& ! side_effects_p (op0))
return op1;
/* In IEEE floating point, x*1 is not equivalent to x for nans.
However, ANSI says we can drop signals,
so we can do this anyway. */
if (op1 == CONST1_RTX (mode))
return op0;
/* Convert multiply by constant power of two into shift unless
we are still generating RTL. This test is a kludge. */
if (GET_CODE (op1) == CONST_INT
&& (val = exact_log2 (INTVAL (op1))) >= 0
/* If the mode is larger than the host word size, and the
uppermost bit is set, then this isn't a power of two due
to implicit sign extension. */
&& (width <= HOST_BITS_PER_WIDE_INT
|| val != HOST_BITS_PER_WIDE_INT - 1)
&& ! rtx_equal_function_value_matters)
return gen_rtx_ASHIFT (mode, op0, GEN_INT (val));
if (GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT)
{
REAL_VALUE_TYPE d;
jmp_buf handler;
int op1is2, op1ism1;
if (setjmp (handler))
return 0;
set_float_handler (handler);
REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
op1is2 = REAL_VALUES_EQUAL (d, dconst2);
op1ism1 = REAL_VALUES_EQUAL (d, dconstm1);
set_float_handler (NULL_PTR);
/* x*2 is x+x and x*(-1) is -x */
if (op1is2 && GET_MODE (op0) == mode)
return gen_rtx_PLUS (mode, op0, copy_rtx (op0));
else if (op1ism1 && GET_MODE (op0) == mode)
return gen_rtx_NEG (mode, op0);
}
break;
case IOR:
if (op1 == const0_rtx)
return op0;
if (GET_CODE (op1) == CONST_INT
&& (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
return op1;
if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
/* A | (~A) -> -1 */
if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
|| (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0)))
&& ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return constm1_rtx;
break;
case XOR:
if (op1 == const0_rtx)
return op0;
if (GET_CODE (op1) == CONST_INT
&& (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
return gen_rtx_NOT (mode, op0);
if (op0 == op1 && ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return const0_rtx;
break;
case AND:
if (op1 == const0_rtx && ! side_effects_p (op0))
return const0_rtx;
if (GET_CODE (op1) == CONST_INT
&& (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
return op0;
if (op0 == op1 && ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return op0;
/* A & (~A) -> 0 */
if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
|| (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0)))
&& ! side_effects_p (op0)
&& GET_MODE_CLASS (mode) != MODE_CC)
return const0_rtx;
break;
case UDIV:
/* Convert divide by power of two into shift (divide by 1 handled
below). */
if (GET_CODE (op1) == CONST_INT
&& (arg1 = exact_log2 (INTVAL (op1))) > 0)
return gen_rtx_LSHIFTRT (mode, op0, GEN_INT (arg1));
/* ... fall through ... */
case DIV:
if (op1 == CONST1_RTX (mode))
return op0;
/* In IEEE floating point, 0/x is not always 0. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode) || flag_fast_math)
&& op0 == CONST0_RTX (mode)
&& ! side_effects_p (op1))
return op0;
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
/* Change division by a constant into multiplication. Only do
this with -ffast-math until an expert says it is safe in
general. */
else if (GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT
&& op1 != CONST0_RTX (mode)
&& flag_fast_math)
{
REAL_VALUE_TYPE d;
REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
if (! REAL_VALUES_EQUAL (d, dconst0))
{
#if defined (REAL_ARITHMETIC)
REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
return gen_rtx_MULT (mode, op0,
CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
#else
return gen_rtx_MULT (mode, op0,
CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
#endif
}
}
#endif
break;
case UMOD:
/* Handle modulus by power of two (mod with 1 handled below). */
if (GET_CODE (op1) == CONST_INT
&& exact_log2 (INTVAL (op1)) > 0)
return gen_rtx_AND (mode, op0, GEN_INT (INTVAL (op1) - 1));
/* ... fall through ... */
case MOD:
if ((op0 == const0_rtx || op1 == const1_rtx)
&& ! side_effects_p (op0) && ! side_effects_p (op1))
return const0_rtx;
break;
case ROTATERT:
case ROTATE:
/* Rotating ~0 always results in ~0. */
if (GET_CODE (op0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT
&& INTVAL (op0) == GET_MODE_MASK (mode)
&& ! side_effects_p (op1))
return op0;
/* ... fall through ... */
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (op1 == const0_rtx)
return op0;
if (op0 == const0_rtx && ! side_effects_p (op1))
return op0;
break;
case SMIN:
if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == (HOST_WIDE_INT) 1 << (width -1)
&& ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
case SMAX:
if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
&& (INTVAL (op1)
== (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1)
&& ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
case UMIN:
if (op1 == const0_rtx && ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
case UMAX:
if (op1 == constm1_rtx && ! side_effects_p (op0))
return op1;
else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
return op0;
break;
default:
abort ();
}
return 0;
}
/* Get the integer argument values in two forms:
zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */
arg0 = INTVAL (op0);
arg1 = INTVAL (op1);
if (width < HOST_BITS_PER_WIDE_INT)
{
arg0 &= ((HOST_WIDE_INT) 1 << width) - 1;
arg1 &= ((HOST_WIDE_INT) 1 << width) - 1;
arg0s = arg0;
if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1)))
arg0s |= ((HOST_WIDE_INT) (-1) << width);
arg1s = arg1;
if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1)))
arg1s |= ((HOST_WIDE_INT) (-1) << width);
}
else
{
arg0s = arg0;
arg1s = arg1;
}
/* Compute the value of the arithmetic. */
switch (code)
{
case PLUS:
val = arg0s + arg1s;
break;
case MINUS:
val = arg0s - arg1s;
break;
case MULT:
val = arg0s * arg1s;
break;
case DIV:
if (arg1s == 0)
return 0;
val = arg0s / arg1s;
break;
case MOD:
if (arg1s == 0)
return 0;
val = arg0s % arg1s;
break;
case UDIV:
if (arg1 == 0)
return 0;
val = (unsigned HOST_WIDE_INT) arg0 / arg1;
break;
case UMOD:
if (arg1 == 0)
return 0;
val = (unsigned HOST_WIDE_INT) arg0 % arg1;
break;
case AND:
val = arg0 & arg1;
break;
case IOR:
val = arg0 | arg1;
break;
case XOR:
val = arg0 ^ arg1;
break;
case LSHIFTRT:
/* If shift count is undefined, don't fold it; let the machine do
what it wants. But truncate it if the machine will do that. */
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
#endif
val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
break;
case ASHIFT:
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
#endif
val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
break;
case ASHIFTRT:
if (arg1 < 0)
return 0;
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
arg1 %= width;
#endif
val = arg0s >> arg1;
/* Bootstrap compiler may not have sign extended the right shift.
Manually extend the sign to insure bootstrap cc matches gcc. */
if (arg0s < 0 && arg1 > 0)
val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1);
break;
case ROTATERT:
if (arg1 < 0)
return 0;
arg1 %= width;
val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1))
| (((unsigned HOST_WIDE_INT) arg0) >> arg1));
break;
case ROTATE:
if (arg1 < 0)
return 0;
arg1 %= width;
val = ((((unsigned HOST_WIDE_INT) arg0) << arg1)
| (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1)));
break;
case COMPARE:
/* Do nothing here. */
return 0;
case SMIN:
val = arg0s <= arg1s ? arg0s : arg1s;
break;
case UMIN:
val = ((unsigned HOST_WIDE_INT) arg0
<= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
break;
case SMAX:
val = arg0s > arg1s ? arg0s : arg1s;
break;
case UMAX:
val = ((unsigned HOST_WIDE_INT) arg0
> (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
break;
default:
abort ();
}
/* Clear the bits that don't belong in our 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. */
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
/* 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
&& (val & ((HOST_WIDE_INT) 1 << (width - 1))))
val |= ((HOST_WIDE_INT) (-1) << width);
return GEN_INT (val);
}
/* Simplify a PLUS or MINUS, at least one of whose operands may be another
PLUS or MINUS.
Rather than test for specific case, we do this by a brute-force method
and do all possible simplifications until no more changes occur. Then
we rebuild the operation. */
static rtx
simplify_plus_minus (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
rtx ops[8];
int negs[8];
rtx result, tem;
int n_ops = 2, input_ops = 2, input_consts = 0, n_consts = 0;
int first = 1, negate = 0, changed;
int i, j;
bzero ((char *) ops, sizeof ops);
/* Set up the two operands and then expand them until nothing has been
changed. If we run out of room in our array, give up; this should
almost never happen. */
ops[0] = op0, ops[1] = op1, negs[0] = 0, negs[1] = (code == MINUS);
changed = 1;
while (changed)
{
changed = 0;
for (i = 0; i < n_ops; i++)
switch (GET_CODE (ops[i]))
{
case PLUS:
case MINUS:
if (n_ops == 7)
return 0;
ops[n_ops] = XEXP (ops[i], 1);
negs[n_ops++] = GET_CODE (ops[i]) == MINUS ? !negs[i] : negs[i];
ops[i] = XEXP (ops[i], 0);
input_ops++;
changed = 1;
break;
case NEG:
ops[i] = XEXP (ops[i], 0);
negs[i] = ! negs[i];
changed = 1;
break;
case CONST:
ops[i] = XEXP (ops[i], 0);
input_consts++;
changed = 1;
break;
case NOT:
/* ~a -> (-a - 1) */
if (n_ops != 7)
{
ops[n_ops] = constm1_rtx;
negs[n_ops++] = negs[i];
ops[i] = XEXP (ops[i], 0);
negs[i] = ! negs[i];
changed = 1;
}
break;
case CONST_INT:
if (negs[i])
ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0, changed = 1;
break;
default:
break;
}
}
/* If we only have two operands, we can't do anything. */
if (n_ops <= 2)
return 0;
/* Now simplify each pair of operands until nothing changes. The first
time through just simplify constants against each other. */
changed = 1;
while (changed)
{
changed = first;
for (i = 0; i < n_ops - 1; i++)
for (j = i + 1; j < n_ops; j++)
if (ops[i] != 0 && ops[j] != 0
&& (! first || (CONSTANT_P (ops[i]) && CONSTANT_P (ops[j]))))
{
rtx lhs = ops[i], rhs = ops[j];
enum rtx_code ncode = PLUS;
if (negs[i] && ! negs[j])
lhs = ops[j], rhs = ops[i], ncode = MINUS;
else if (! negs[i] && negs[j])
ncode = MINUS;
tem = simplify_binary_operation (ncode, mode, lhs, rhs);
if (tem)
{
ops[i] = tem, ops[j] = 0;
negs[i] = negs[i] && negs[j];
if (GET_CODE (tem) == NEG)
ops[i] = XEXP (tem, 0), negs[i] = ! negs[i];
if (GET_CODE (ops[i]) == CONST_INT && negs[i])
ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0;
changed = 1;
}
}
first = 0;
}
/* Pack all the operands to the lower-numbered entries and give up if
we didn't reduce the number of operands we had. Make sure we
count a CONST as two operands. If we have the same number of
operands, but have made more CONSTs than we had, this is also
an improvement, so accept it. */
for (i = 0, j = 0; j < n_ops; j++)
if (ops[j] != 0)
{
ops[i] = ops[j], negs[i++] = negs[j];
if (GET_CODE (ops[j]) == CONST)
n_consts++;
}
if (i + n_consts > input_ops
|| (i + n_consts == input_ops && n_consts <= input_consts))
return 0;
n_ops = i;
/* If we have a CONST_INT, put it last. */
for (i = 0; i < n_ops - 1; i++)
if (GET_CODE (ops[i]) == CONST_INT)
{
tem = ops[n_ops - 1], ops[n_ops - 1] = ops[i] , ops[i] = tem;
j = negs[n_ops - 1], negs[n_ops - 1] = negs[i], negs[i] = j;
}
/* Put a non-negated operand first. If there aren't any, make all
operands positive and negate the whole thing later. */
for (i = 0; i < n_ops && negs[i]; i++)
;
if (i == n_ops)
{
for (i = 0; i < n_ops; i++)
negs[i] = 0;
negate = 1;
}
else if (i != 0)
{
tem = ops[0], ops[0] = ops[i], ops[i] = tem;
j = negs[0], negs[0] = negs[i], negs[i] = j;
}
/* Now make the result by performing the requested operations. */
result = ops[0];
for (i = 1; i < n_ops; i++)
result = cse_gen_binary (negs[i] ? MINUS : PLUS, mode, result, ops[i]);
return negate ? gen_rtx_NEG (mode, result) : result;
}
/* Make a binary operation by properly ordering the operands and
seeing if the expression folds. */
static rtx
cse_gen_binary (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
rtx tem;
/* Put complex operands first and constants second if commutative. */
if (GET_RTX_CLASS (code) == 'c'
&& ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
|| (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
&& GET_RTX_CLASS (GET_CODE (op1)) != 'o')
|| (GET_CODE (op0) == SUBREG
&& GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
&& GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
tem = op0, op0 = op1, op1 = tem;
/* If this simplifies, do it. */
tem = simplify_binary_operation (code, mode, op0, op1);
if (tem)
return tem;
/* Handle addition and subtraction of CONST_INT specially. Otherwise,
just form the operation. */
if (code == PLUS && GET_CODE (op1) == CONST_INT
&& GET_MODE (op0) != VOIDmode)
return plus_constant (op0, INTVAL (op1));
else if (code == MINUS && GET_CODE (op1) == CONST_INT
&& GET_MODE (op0) != VOIDmode)
return plus_constant (op0, - INTVAL (op1));
else
return gen_rtx_fmt_ee (code, mode, op0, op1);
}
struct cfc_args
{
/* Input */
rtx op0, op1;
/* Output */
int equal, op0lt, op1lt;
};
static void
check_fold_consts (data)
PTR data;
{
struct cfc_args * args = (struct cfc_args *) data;
REAL_VALUE_TYPE d0, d1;
REAL_VALUE_FROM_CONST_DOUBLE (d0, args->op0);
REAL_VALUE_FROM_CONST_DOUBLE (d1, args->op1);
args->equal = REAL_VALUES_EQUAL (d0, d1);
args->op0lt = REAL_VALUES_LESS (d0, d1);
args->op1lt = REAL_VALUES_LESS (d1, d0);
}
/* Like simplify_binary_operation except used for relational operators.
MODE is the mode of the operands, not that of the result. If MODE
is VOIDmode, both operands must also be VOIDmode and we compare the
operands in "infinite precision".
If no simplification is possible, this function returns zero. Otherwise,
it returns either const_true_rtx or const0_rtx. */
rtx
simplify_relational_operation (code, mode, op0, op1)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
{
int equal, op0lt, op0ltu, op1lt, op1ltu;
rtx tem;
/* If op0 is a compare, extract the comparison arguments from it. */
if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
/* We can't simplify MODE_CC values since we don't know what the
actual comparison is. */
if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC
#ifdef HAVE_cc0
|| op0 == cc0_rtx
#endif
)
return 0;
/* For integer comparisons of A and B maybe we can simplify A - B and can
then simplify a comparison of that with zero. If A and B are both either
a register or a CONST_INT, this can't help; testing for these cases will
prevent infinite recursion here and speed things up.
If CODE is an unsigned comparison, then we can never do this optimization,
because it gives an incorrect result if the subtraction wraps around zero.
ANSI C defines unsigned operations such that they never overflow, and
thus such cases can not be ignored. */
if (INTEGRAL_MODE_P (mode) && op1 != const0_rtx
&& ! ((GET_CODE (op0) == REG || GET_CODE (op0) == CONST_INT)
&& (GET_CODE (op1) == REG || GET_CODE (op1) == CONST_INT))
&& 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
&& code != GTU && code != GEU && code != LTU && code != LEU)
return simplify_relational_operation (signed_condition (code),
mode, tem, const0_rtx);
/* For non-IEEE floating-point, if the two operands are equal, we know the
result. */
if (rtx_equal_p (op0, op1)
&& (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (GET_MODE (op0)) || flag_fast_math))
equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
/* If the operands are floating-point constants, see if we can fold
the result. */
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
else if (GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
{
struct cfc_args args;
/* Setup input for check_fold_consts() */
args.op0 = op0;
args.op1 = op1;
if (do_float_handler(check_fold_consts, (PTR) &args) == 0)
/* We got an exception from check_fold_consts() */
return 0;
/* Receive output from check_fold_consts() */
equal = args.equal;
op0lt = op0ltu = args.op0lt;
op1lt = op1ltu = args.op1lt;
}
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
/* Otherwise, see if the operands are both integers. */
else if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
&& (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
&& (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
{
int width = GET_MODE_BITSIZE (mode);
HOST_WIDE_INT l0s, h0s, l1s, h1s;
unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
/* Get the two words comprising each integer constant. */
if (GET_CODE (op0) == CONST_DOUBLE)
{
l0u = l0s = CONST_DOUBLE_LOW (op0);
h0u = h0s = CONST_DOUBLE_HIGH (op0);
}
else
{
l0u = l0s = INTVAL (op0);
h0u = h0s = l0s < 0 ? -1 : 0;
}
if (GET_CODE (op1) == CONST_DOUBLE)
{
l1u = l1s = CONST_DOUBLE_LOW (op1);
h1u = h1s = CONST_DOUBLE_HIGH (op1);
}
else
{
l1u = l1s = INTVAL (op1);
h1u = h1s = l1s < 0 ? -1 : 0;
}
/* If WIDTH is nonzero and smaller than HOST_BITS_PER_WIDE_INT,
we have to sign or zero-extend the values. */
if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
h0u = h1u = 0, h0s = l0s < 0 ? -1 : 0, h1s = l1s < 0 ? -1 : 0;
if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
{
l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
if (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
l0s |= ((HOST_WIDE_INT) (-1) << width);
if (l1s & ((HOST_WIDE_INT) 1 << (width - 1)))
l1s |= ((HOST_WIDE_INT) (-1) << width);
}
equal = (h0u == h1u && l0u == l1u);
op0lt = (h0s < h1s || (h0s == h1s && l0s < l1s));
op1lt = (h1s < h0s || (h1s == h0s && l1s < l0s));
op0ltu = (h0u < h1u || (h0u == h1u && l0u < l1u));
op1ltu = (h1u < h0u || (h1u == h0u && l1u < l0u));
}
/* Otherwise, there are some code-specific tests we can make. */
else
{
switch (code)
{
case EQ:
/* References to the frame plus a constant or labels cannot
be zero, but a SYMBOL_REF can due to #pragma weak. */
if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
|| GET_CODE (op0) == LABEL_REF)
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
/* On some machines, the ap reg can be 0 sometimes. */
&& op0 != arg_pointer_rtx
#endif
)
return const0_rtx;
break;
case NE:
if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
|| GET_CODE (op0) == LABEL_REF)
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& op0 != arg_pointer_rtx
#endif
)
return const_true_rtx;
break;
case GEU:
/* Unsigned values are never negative. */
if (op1 == const0_rtx)
return const_true_rtx;
break;
case LTU:
if (op1 == const0_rtx)
return const0_rtx;
break;
case LEU:
/* Unsigned values are never greater than the largest
unsigned value. */
if (GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == GET_MODE_MASK (mode)
&& INTEGRAL_MODE_P (mode))
return const_true_rtx;
break;
case GTU:
if (GET_CODE (op1) == CONST_INT
&& INTVAL (op1) == GET_MODE_MASK (mode)
&& INTEGRAL_MODE_P (mode))
return const0_rtx;
break;
default:
break;
}
return 0;
}
/* If we reach here, EQUAL, OP0LT, OP0LTU, OP1LT, and OP1LTU are set
as appropriate. */
switch (code)
{
case EQ:
return equal ? const_true_rtx : const0_rtx;
case NE:
return ! equal ? const_true_rtx : const0_rtx;
case LT:
return op0lt ? const_true_rtx : const0_rtx;
case GT:
return op1lt ? const_true_rtx : const0_rtx;
case LTU:
return op0ltu ? const_true_rtx : const0_rtx;
case GTU:
return op1ltu ? const_true_rtx : const0_rtx;
case LE:
return equal || op0lt ? const_true_rtx : const0_rtx;
case GE:
return equal || op1lt ? const_true_rtx : const0_rtx;
case LEU:
return equal || op0ltu ? const_true_rtx : const0_rtx;
case GEU:
return equal || op1ltu ? const_true_rtx : const0_rtx;
default:
abort ();
}
}
/* Simplify CODE, an operation with result mode MODE and three operands,
OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became
a constant. Return 0 if no simplifications is possible. */
rtx
simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
enum rtx_code code;
enum machine_mode mode, op0_mode;
rtx op0, op1, op2;
{
int width = GET_MODE_BITSIZE (mode);
/* VOIDmode means "infinite" precision. */
if (width == 0)
width = HOST_BITS_PER_WIDE_INT;
switch (code)
{
case SIGN_EXTRACT:
case ZERO_EXTRACT:
if (GET_CODE (op0) == CONST_INT
&& GET_CODE (op1) == CONST_INT
&& GET_CODE (op2) == CONST_INT
&& INTVAL (op1) + INTVAL (op2) <= GET_MODE_BITSIZE (op0_mode)
&& width <= HOST_BITS_PER_WIDE_INT)
{
/* Extracting a bit-field from a constant */
HOST_WIDE_INT val = INTVAL (op0);
if (BITS_BIG_ENDIAN)
val >>= (GET_MODE_BITSIZE (op0_mode)
- INTVAL (op2) - INTVAL (op1));
else
val >>= INTVAL (op2);
if (HOST_BITS_PER_WIDE_INT != INTVAL (op1))
{
/* First zero-extend. */
val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1;
/* If desired, propagate sign bit. */
if (code == SIGN_EXTRACT
&& (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1))))
val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1);
}
/* Clear the bits that don't belong in our 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. */
if (width < HOST_BITS_PER_WIDE_INT
&& ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
!= ((HOST_WIDE_INT) (-1) << (width - 1))))
val &= ((HOST_WIDE_INT) 1 << width) - 1;
return GEN_INT (val);
}
break;
case IF_THEN_ELSE:
if (GET_CODE (op0) == CONST_INT)
return op0 != const0_rtx ? op1 : op2;
/* Convert a == b ? b : a to "a". */
if (GET_CODE (op0) == NE && ! side_effects_p (op0)
&& rtx_equal_p (XEXP (op0, 0), op1)
&& rtx_equal_p (XEXP (op0, 1), op2))
return op1;
else if (GET_CODE (op0) == EQ && ! side_effects_p (op0)
&& rtx_equal_p (XEXP (op0, 1), op1)
&& rtx_equal_p (XEXP (op0, 0), op2))
return op2;
else if (GET_RTX_CLASS (GET_CODE (op0)) == '<' && ! side_effects_p (op0))
{
rtx temp;
temp = simplify_relational_operation (GET_CODE (op0), op0_mode,
XEXP (op0, 0), XEXP (op0, 1));
/* See if any simplifications were possible. */
if (temp == const0_rtx)
return op2;
else if (temp == const1_rtx)
return op1;
}
break;
default:
abort ();
}
return 0;
}
/* If X is a nontrivial arithmetic operation on an argument
for which a constant value can be determined, return
the result of operating on that value, as a constant.
Otherwise, return X, possibly with one or more operands
modified by recursive calls to this function.
If X is a register whose contents are known, we do NOT
return those contents here. equiv_constant is called to
perform that task.
INSN is the insn that we may be modifying. If it is 0, make a copy
of X before modifying it. */
static rtx
fold_rtx (x, insn)
rtx x;
rtx insn;
{
register enum rtx_code code;
register enum machine_mode mode;
register char *fmt;
register int i;
rtx new = 0;
int copied = 0;
int must_swap = 0;
/* Folded equivalents of first two operands of X. */
rtx folded_arg0;
rtx folded_arg1;
/* Constant equivalents of first three operands of X;
0 when no such equivalent is known. */
rtx const_arg0;
rtx const_arg1;
rtx const_arg2;
/* The mode of the first operand of X. We need this for sign and zero
extends. */
enum machine_mode mode_arg0;
if (x == 0)
return x;
mode = GET_MODE (x);
code = GET_CODE (x);
switch (code)
{
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case REG:
/* No use simplifying an EXPR_LIST
since they are used only for lists of args
in a function call's REG_EQUAL note. */
case EXPR_LIST:
/* Changing anything inside an ADDRESSOF is incorrect; we don't
want to (e.g.,) make (addressof (const_int 0)) just because
the location is known to be zero. */
case ADDRESSOF:
return x;
#ifdef HAVE_cc0
case CC0:
return prev_insn_cc0;
#endif
case PC:
/* If the next insn is a CODE_LABEL followed by a jump table,
PC's value is a LABEL_REF pointing to that label. That
lets us fold switch statements on the Vax. */
if (insn && GET_CODE (insn) == JUMP_INSN)
{
rtx next = next_nonnote_insn (insn);
if (next && GET_CODE (next) == CODE_LABEL
&& NEXT_INSN (next) != 0
&& GET_CODE (NEXT_INSN (next)) == JUMP_INSN
&& (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC
|| GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC))
return gen_rtx_LABEL_REF (Pmode, next);
}
break;
case SUBREG:
/* See if we previously assigned a constant value to this SUBREG. */
if ((new = lookup_as_function (x, CONST_INT)) != 0
|| (new = lookup_as_function (x, CONST_DOUBLE)) != 0)
return new;
/* If this is a paradoxical SUBREG, we have no idea what value the
extra bits would have. However, if the operand is equivalent
to a SUBREG whose operand is the same as our mode, and all the
modes are within a word, we can just use the inner operand
because these SUBREGs just say how to treat the register.
Similarly if we find an integer constant. */
if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
{
enum machine_mode imode = GET_MODE (SUBREG_REG (x));
struct table_elt *elt;
if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
&& GET_MODE_SIZE (imode) <= UNITS_PER_WORD
&& (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode),
imode)) != 0)
for (elt = elt->first_same_value;
elt; elt = elt->next_same_value)
{
if (CONSTANT_P (elt->exp)
&& GET_MODE (elt->exp) == VOIDmode)
return elt->exp;
if (GET_CODE (elt->exp) == SUBREG
&& GET_MODE (SUBREG_REG (elt->exp)) == mode
&& exp_equiv_p (elt->exp, elt->exp, 1, 0))
return copy_rtx (SUBREG_REG (elt->exp));
}
return x;
}
/* Fold SUBREG_REG. If it changed, see if we can simplify the SUBREG.
We might be able to if the SUBREG is extracting a single word in an
integral mode or extracting the low part. */
folded_arg0 = fold_rtx (SUBREG_REG (x), insn);
const_arg0 = equiv_constant (folded_arg0);
if (const_arg0)
folded_arg0 = const_arg0;
if (folded_arg0 != SUBREG_REG (x))
{
new = 0;
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) == UNITS_PER_WORD
&& GET_MODE (SUBREG_REG (x)) != VOIDmode)
new = operand_subword (folded_arg0, SUBREG_WORD (x), 0,
GET_MODE (SUBREG_REG (x)));
if (new == 0 && subreg_lowpart_p (x))
new = gen_lowpart_if_possible (mode, folded_arg0);
if (new)
return new;
}
/* If this is a narrowing SUBREG and our operand is a REG, see if
we can find an equivalence for REG that is an arithmetic operation
in a wider mode where both operands are paradoxical SUBREGs
from objects of our result mode. In that case, we couldn't report
an equivalent value for that operation, since we don't know what the
extra bits will be. But we can find an equivalence for this SUBREG
by folding that operation is the narrow mode. This allows us to
fold arithmetic in narrow modes when the machine only supports
word-sized arithmetic.
Also look for a case where we have a SUBREG whose operand is the
same as our result. If both modes are smaller than a word, we
are simply interpreting a register in different modes and we
can use the inner value. */
if (GET_CODE (folded_arg0) == REG
&& GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0))
&& subreg_lowpart_p (x))
{
struct table_elt *elt;
/* We can use HASH here since we know that canon_hash won't be
called. */
elt = lookup (folded_arg0,
HASH (folded_arg0, GET_MODE (folded_arg0)),
GET_MODE (folded_arg0));
if (elt)
elt = elt->first_same_value;
for (; elt; elt = elt->next_same_value)
{
enum rtx_code eltcode = GET_CODE (elt->exp);
/* Just check for unary and binary operations. */
if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1'
&& GET_CODE (elt->exp) != SIGN_EXTEND
&& GET_CODE (elt->exp) != ZERO_EXTEND
&& GET_CODE (XEXP (elt->exp, 0)) == SUBREG
&& GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode)
{
rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
if (GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
op0 = equiv_constant (op0);
if (op0)
new = simplify_unary_operation (GET_CODE (elt->exp), mode,
op0, mode);
}
else if ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2'
|| GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c')
&& eltcode != DIV && eltcode != MOD
&& eltcode != UDIV && eltcode != UMOD
&& eltcode != ASHIFTRT && eltcode != LSHIFTRT
&& eltcode != ROTATE && eltcode != ROTATERT
&& ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG
&& (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0)))
== mode))
|| CONSTANT_P (XEXP (elt->exp, 0)))
&& ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG
&& (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1)))
== mode))
|| CONSTANT_P (XEXP (elt->exp, 1))))
{
rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0))
op0 = fold_rtx (op0, NULL_RTX);
if (op0)
op0 = equiv_constant (op0);
if (op1 && GET_CODE (op1) != REG && ! CONSTANT_P (op1))
op1 = fold_rtx (op1, NULL_RTX);
if (op1)
op1 = equiv_constant (op1);
/* If we are looking for the low SImode part of
(ashift:DI c (const_int 32)), it doesn't work
to compute that in SImode, because a 32-bit shift
in SImode is unpredictable. We know the value is 0. */
if (op0 && op1
&& GET_CODE (elt->exp) == ASHIFT
&& GET_CODE (op1) == CONST_INT
&& INTVAL (op1) >= GET_MODE_BITSIZE (mode))
{
if (INTVAL (op1) < GET_MODE_BITSIZE (GET_MODE (elt->exp)))
/* If the count fits in the inner mode's width,
but exceeds the outer mode's width,
the value will get truncated to 0
by the subreg. */
new = const0_rtx;
else
/* If the count exceeds even the inner mode's width,
don't fold this expression. */
new = 0;
}
else if (op0 && op1)
new = simplify_binary_operation (GET_CODE (elt->exp), mode,
op0, op1);
}
else if (GET_CODE (elt->exp) == SUBREG
&& GET_MODE (SUBREG_REG (elt->exp)) == mode
&& (GET_MODE_SIZE (GET_MODE (folded_arg0))
<= UNITS_PER_WORD)
&& exp_equiv_p (elt->exp, elt->exp, 1, 0))
new = copy_rtx (SUBREG_REG (elt->exp));
if (new)
return new;
}
}
return x;
case NOT:
case NEG:
/* If we have (NOT Y), see if Y is known to be (NOT Z).
If so, (NOT Y) simplifies to Z. Similarly for NEG. */
new = lookup_as_function (XEXP (x, 0), code);
if (new)
return fold_rtx (copy_rtx (XEXP (new, 0)), insn);
break;
case MEM:
/* If we are not actually processing an insn, don't try to find the
best address. Not only don't we care, but we could modify the
MEM in an invalid way since we have no insn to validate against. */
if (insn != 0)
find_best_addr (insn, &XEXP (x, 0));
{
/* Even if we don't fold in the insn itself,
we can safely do so here, in hopes of getting a constant. */
rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
rtx base = 0;
HOST_WIDE_INT offset = 0;
if (GET_CODE (addr) == REG
&& REGNO_QTY_VALID_P (REGNO (addr))
&& GET_MODE (addr) == qty_mode[REG_QTY (REGNO (addr))]
&& qty_const[REG_QTY (REGNO (addr))] != 0)
addr = qty_const[REG_QTY (REGNO (addr))];
/* If address is constant, split it into a base and integer offset. */
if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
base = addr;
else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
{
base = XEXP (XEXP (addr, 0), 0);
offset = INTVAL (XEXP (XEXP (addr, 0), 1));
}
else if (GET_CODE (addr) == LO_SUM
&& GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
base = XEXP (addr, 1);
else if (GET_CODE (addr) == ADDRESSOF)
return change_address (x, VOIDmode, addr);
/* If this is a constant pool reference, we can fold it into its
constant to allow better value tracking. */
if (base && GET_CODE (base) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (base))
{
rtx constant = get_pool_constant (base);
enum machine_mode const_mode = get_pool_mode (base);
rtx new;
if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT)
constant_pool_entries_cost = COST (constant);
/* If we are loading the full constant, we have an equivalence. */
if (offset == 0 && mode == const_mode)
return constant;
/* If this actually isn't a constant (weird!), we can't do
anything. Otherwise, handle the two most common cases:
extracting a word from a multi-word constant, and extracting
the low-order bits. Other cases don't seem common enough to
worry about. */
if (! CONSTANT_P (constant))
return x;
if (GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_SIZE (mode) == UNITS_PER_WORD
&& offset % UNITS_PER_WORD == 0
&& (new = operand_subword (constant,
offset / UNITS_PER_WORD,
0, const_mode)) != 0)
return new;
if (((BYTES_BIG_ENDIAN
&& offset == GET_MODE_SIZE (GET_MODE (constant)) - 1)
|| (! BYTES_BIG_ENDIAN && offset == 0))
&& (new = gen_lowpart_if_possible (mode, constant)) != 0)
return new;
}
/* If this is a reference to a label at a known position in a jump
table, we also know its value. */
if (base && GET_CODE (base) == LABEL_REF)
{
rtx label = XEXP (base, 0);
rtx table_insn = NEXT_INSN (label);
if (table_insn && GET_CODE (table_insn) == JUMP_INSN
&& GET_CODE (PATTERN (table_insn)) == ADDR_VEC)
{
rtx table = PATTERN (table_insn);
if (offset >= 0
&& (offset / GET_MODE_SIZE (GET_MODE (table))
< XVECLEN (table, 0)))
return XVECEXP (table, 0,
offset / GET_MODE_SIZE (GET_MODE (table)));
}
if (table_insn && GET_CODE (table_insn) == JUMP_INSN
&& GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC)
{
rtx table = PATTERN (table_insn);
if (offset >= 0
&& (offset / GET_MODE_SIZE (GET_MODE (table))
< XVECLEN (table, 1)))
{
offset /= GET_MODE_SIZE (GET_MODE (table));
new = gen_rtx_MINUS (Pmode, XVECEXP (table, 1, offset),
XEXP (table, 0));
if (GET_MODE (table) != Pmode)
new = gen_rtx_TRUNCATE (GET_MODE (table), new);
/* Indicate this is a constant. This isn't a
valid form of CONST, but it will only be used
to fold the next insns and then discarded, so
it should be safe.
Note this expression must be explicitly discarded,
by cse_insn, else it may end up in a REG_EQUAL note
and "escape" to cause problems elsewhere. */
return gen_rtx_CONST (GET_MODE (new), new);
}
}
}
return x;
}
case ASM_OPERANDS:
for (i = XVECLEN (x, 3) - 1; i >= 0; i--)
validate_change (insn, &XVECEXP (x, 3, i),
fold_rtx (XVECEXP (x, 3, i), insn), 0);
break;
default:
break;
}
const_arg0 = 0;
const_arg1 = 0;
const_arg2 = 0;
mode_arg0 = VOIDmode;
/* Try folding our operands.
Then see which ones have constant values known. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
{
rtx arg = XEXP (x, i);
rtx folded_arg = arg, const_arg = 0;
enum machine_mode mode_arg = GET_MODE (arg);
rtx cheap_arg, expensive_arg;
rtx replacements[2];
int j;
/* Most arguments are cheap, so handle them specially. */
switch (GET_CODE (arg))
{
case REG:
/* This is the same as calling equiv_constant; it is duplicated
here for speed. */
if (REGNO_QTY_VALID_P (REGNO (arg))
&& qty_const[REG_QTY (REGNO (arg))] != 0
&& GET_CODE (qty_const[REG_QTY (REGNO (arg))]) != REG
&& GET_CODE (qty_const[REG_QTY (REGNO (arg))]) != PLUS)
const_arg
= gen_lowpart_if_possible (GET_MODE (arg),
qty_const[REG_QTY (REGNO (arg))]);
break;
case CONST:
case CONST_INT:
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
const_arg = arg;
break;
#ifdef HAVE_cc0
case CC0:
folded_arg = prev_insn_cc0;
mode_arg = prev_insn_cc0_mode;
const_arg = equiv_constant (folded_arg);
break;
#endif
default:
folded_arg = fold_rtx (arg, insn);
const_arg = equiv_constant (folded_arg);
}
/* For the first three operands, see if the operand
is constant or equivalent to a constant. */
switch (i)
{
case 0:
folded_arg0 = folded_arg;
const_arg0 = const_arg;
mode_arg0 = mode_arg;
break;
case 1:
folded_arg1 = folded_arg;
const_arg1 = const_arg;
break;
case 2:
const_arg2 = const_arg;
break;
}
/* Pick the least expensive of the folded argument and an
equivalent constant argument. */
if (const_arg == 0 || const_arg == folded_arg
|| COST (const_arg) > COST (folded_arg))
cheap_arg = folded_arg, expensive_arg = const_arg;
else
cheap_arg = const_arg, expensive_arg = folded_arg;
/* Try to replace the operand with the cheapest of the two
possibilities. If it doesn't work and this is either of the first
two operands of a commutative operation, try swapping them.
If THAT fails, try the more expensive, provided it is cheaper
than what is already there. */
if (cheap_arg == XEXP (x, i))
continue;
if (insn == 0 && ! copied)
{
x = copy_rtx (x);
copied = 1;
}
replacements[0] = cheap_arg, replacements[1] = expensive_arg;
for (j = 0;
j < 2 && replacements[j]
&& COST (replacements[j]) < COST (XEXP (x, i));
j++)
{
if (validate_change (insn, &XEXP (x, i), replacements[j], 0))
break;
if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c')
{
validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1);
validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1);
if (apply_change_group ())
{
/* Swap them back to be invalid so that this loop can
continue and flag them to be swapped back later. */
rtx tem;
tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1);
XEXP (x, 1) = tem;
must_swap = 1;
break;
}
}
}
}
else
{
if (fmt[i] == 'E')
/* Don't try to fold inside of a vector of expressions.
Doing nothing is harmless. */
{;}
}
/* If a commutative operation, place a constant integer as the second
operand unless the first operand is also a constant integer. Otherwise,
place any constant second unless the first operand is also a constant. */
if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
{
if (must_swap || (const_arg0
&& (const_arg1 == 0
|| (GET_CODE (const_arg0) == CONST_INT
&& GET_CODE (const_arg1) != CONST_INT))))
{
register rtx tem = XEXP (x, 0);
if (insn == 0 && ! copied)
{
x = copy_rtx (x);
copied = 1;
}
validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
validate_change (insn, &XEXP (x, 1), tem, 1);
if (apply_change_group ())
{
tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
}
}
}
/* If X is an arithmetic operation, see if we can simplify it. */
switch (GET_RTX_CLASS (code))
{
case '1':
{
int is_const = 0;
/* We can't simplify extension ops unless we know the
original mode. */
if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
&& mode_arg0 == VOIDmode)
break;
/* If we had a CONST, strip it off and put it back later if we
fold. */
if (const_arg0 != 0 && GET_CODE (const_arg0) == CONST)
is_const = 1, const_arg0 = XEXP (const_arg0, 0);
new = simplify_unary_operation (code, mode,
const_arg0 ? const_arg0 : folded_arg0,
mode_arg0);
if (new != 0 && is_const)
new = gen_rtx_CONST (mode, new);
}
break;
case '<':
/* See what items are actually being compared and set FOLDED_ARG[01]
to those values and CODE to the actual comparison code. If any are
constant, set CONST_ARG0 and CONST_ARG1 appropriately. We needn't
do anything if both operands are already known to be constant. */
if (const_arg0 == 0 || const_arg1 == 0)
{
struct table_elt *p0, *p1;
rtx true = const_true_rtx, false = const0_rtx;
enum machine_mode mode_arg1;
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
mode);
false = CONST0_RTX (mode);
}
#endif
code = find_comparison_args (code, &folded_arg0, &folded_arg1,
&mode_arg0, &mode_arg1);
const_arg0 = equiv_constant (folded_arg0);
const_arg1 = equiv_constant (folded_arg1);
/* If the mode is VOIDmode or a MODE_CC mode, we don't know
what kinds of things are being compared, so we can't do
anything with this comparison. */
if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC)
break;
/* If we do not now have two constants being compared, see
if we can nevertheless deduce some things about the
comparison. */
if (const_arg0 == 0 || const_arg1 == 0)
{
/* Is FOLDED_ARG0 frame-pointer plus a constant? Or
non-explicit constant? These aren't zero, but we
don't know their sign. */
if (const_arg1 == const0_rtx
&& (NONZERO_BASE_PLUS_P (folded_arg0)
#if 0 /* Sad to say, on sysvr4, #pragma weak can make a symbol address
come out as 0. */
|| GET_CODE (folded_arg0) == SYMBOL_REF
#endif
|| GET_CODE (folded_arg0) == LABEL_REF
|| GET_CODE (folded_arg0) == CONST))
{
if (code == EQ)
return false;
else if (code == NE)
return true;
}
/* See if the two operands are the same. We don't do this
for IEEE floating-point since we can't assume x == x
since x might be a NaN. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_MODE_P (mode_arg0) || flag_fast_math)
&& (folded_arg0 == folded_arg1
|| (GET_CODE (folded_arg0) == REG
&& GET_CODE (folded_arg1) == REG
&& (REG_QTY (REGNO (folded_arg0))
== REG_QTY (REGNO (folded_arg1))))
|| ((p0 = lookup (folded_arg0,
(safe_hash (folded_arg0, mode_arg0)
% NBUCKETS), mode_arg0))
&& (p1 = lookup (folded_arg1,
(safe_hash (folded_arg1, mode_arg0)
% NBUCKETS), mode_arg0))
&& p0->first_same_value == p1->first_same_value)))
return ((code == EQ || code == LE || code == GE
|| code == LEU || code == GEU)
? true : false);
/* If FOLDED_ARG0 is a register, see if the comparison we are
doing now is either the same as we did before or the reverse
(we only check the reverse if not floating-point). */
else if (GET_CODE (folded_arg0) == REG)
{
int qty = REG_QTY (REGNO (folded_arg0));
if (REGNO_QTY_VALID_P (REGNO (folded_arg0))
&& (comparison_dominates_p (qty_comparison_code[qty], code)
|| (comparison_dominates_p (qty_comparison_code[qty],
reverse_condition (code))
&& ! FLOAT_MODE_P (mode_arg0)))
&& (rtx_equal_p (qty_comparison_const[qty], folded_arg1)
|| (const_arg1
&& rtx_equal_p (qty_comparison_const[qty],
const_arg1))
|| (GET_CODE (folded_arg1) == REG
&& (REG_QTY (REGNO (folded_arg1))
== qty_comparison_qty[qty]))))
return (comparison_dominates_p (qty_comparison_code[qty],
code)
? true : false);
}
}
}
/* If we are comparing against zero, see if the first operand is
equivalent to an IOR with a constant. If so, we may be able to
determine the result of this comparison. */
if (const_arg1 == const0_rtx)
{
rtx y = lookup_as_function (folded_arg0, IOR);
rtx inner_const;
if (y != 0
&& (inner_const = equiv_constant (XEXP (y, 1))) != 0
&& GET_CODE (inner_const) == CONST_INT
&& INTVAL (inner_const) != 0)
{
int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1;
int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
&& (INTVAL (inner_const)
& ((HOST_WIDE_INT) 1 << sign_bitnum)));
rtx true = const_true_rtx, false = const0_rtx;
#ifdef FLOAT_STORE_FLAG_VALUE
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
mode);
false = CONST0_RTX (mode);
}
#endif
switch (code)
{
case EQ:
return false;
case NE:
return true;
case LT: case LE:
if (has_sign)
return true;
break;
case GT: case GE:
if (has_sign)
return false;
break;
default:
break;
}
}
}
new = simplify_relational_operation (code, mode_arg0,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1);
#ifdef FLOAT_STORE_FLAG_VALUE
if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
new = ((new == const0_rtx) ? CONST0_RTX (mode)
: CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE, mode));
#endif
break;
case '2':
case 'c':
switch (code)
{
case PLUS:
/* If the second operand is a LABEL_REF, see if the first is a MINUS
with that LABEL_REF as its second operand. If so, the result is
the first operand of that MINUS. This handles switches with an
ADDR_DIFF_VEC table. */
if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
{
rtx y
= GET_CODE (folded_arg0) == MINUS ? folded_arg0
: lookup_as_function (folded_arg0, MINUS);
if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
&& XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0))
return XEXP (y, 0);
/* Now try for a CONST of a MINUS like the above. */
if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0
: lookup_as_function (folded_arg0, CONST))) != 0
&& GET_CODE (XEXP (y, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
&& XEXP (XEXP (XEXP (y, 0),1), 0) == XEXP (const_arg1, 0))
return XEXP (XEXP (y, 0), 0);
}
/* Likewise if the operands are in the other order. */
if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF)
{
rtx y
= GET_CODE (folded_arg1) == MINUS ? folded_arg1
: lookup_as_function (folded_arg1, MINUS);
if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
&& XEXP (XEXP (y, 1), 0) == XEXP (const_arg0, 0))
return XEXP (y, 0);
/* Now try for a CONST of a MINUS like the above. */
if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1
: lookup_as_function (folded_arg1, CONST))) != 0
&& GET_CODE (XEXP (y, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
&& XEXP (XEXP (XEXP (y, 0),1), 0) == XEXP (const_arg0, 0))
return XEXP (XEXP (y, 0), 0);
}
/* If second operand is a register equivalent to a negative
CONST_INT, see if we can find a register equivalent to the
positive constant. Make a MINUS if so. Don't do this for
a non-negative constant since we might then alternate between
chosing positive and negative constants. Having the positive
constant previously-used is the more common case. Be sure
the resulting constant is non-negative; if const_arg1 were
the smallest negative number this would overflow: depending
on the mode, this would either just be the same value (and
hence not save anything) or be incorrect. */
if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT
&& INTVAL (const_arg1) < 0
- && - INTVAL (const_arg1) >= 0
+ /* This used to test
+
+ - INTVAL (const_arg1) >= 0
+
+ But The Sun V5.0 compilers mis-compiled that test. So
+ instead we test for the problematic value in a more direct
+ manner and hope the Sun compilers get it correct. */
+ && INTVAL (const_arg1) !=
+ ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
&& GET_CODE (folded_arg1) == REG)
{
rtx new_const = GEN_INT (- INTVAL (const_arg1));
struct table_elt *p
= lookup (new_const, safe_hash (new_const, mode) % NBUCKETS,
mode);
if (p)
for (p = p->first_same_value; p; p = p->next_same_value)
if (GET_CODE (p->exp) == REG)
return cse_gen_binary (MINUS, mode, folded_arg0,
canon_reg (p->exp, NULL_RTX));
}
goto from_plus;
case MINUS:
/* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2).
If so, produce (PLUS Z C2-C). */
if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
{
rtx y = lookup_as_function (XEXP (x, 0), PLUS);
if (y && GET_CODE (XEXP (y, 1)) == CONST_INT)
return fold_rtx (plus_constant (copy_rtx (y),
-INTVAL (const_arg1)),
NULL_RTX);
}
/* ... fall through ... */
from_plus:
case SMIN: case SMAX: case UMIN: case UMAX:
case IOR: case AND: case XOR:
case MULT: case DIV: case UDIV:
case ASHIFT: case LSHIFTRT: case ASHIFTRT:
/* If we have (<op> <reg> <const_int>) for an associative OP and REG
is known to be of similar form, we may be able to replace the
operation with a combined operation. This may eliminate the
intermediate operation if every use is simplified in this way.
Note that the similar optimization done by combine.c only works
if the intermediate operation's result has only one reference. */
if (GET_CODE (folded_arg0) == REG
&& const_arg1 && GET_CODE (const_arg1) == CONST_INT)
{
int is_shift
= (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT);
rtx y = lookup_as_function (folded_arg0, code);
rtx inner_const;
enum rtx_code associate_code;
rtx new_const;
if (y == 0
|| 0 == (inner_const
= equiv_constant (fold_rtx (XEXP (y, 1), 0)))
|| GET_CODE (inner_const) != CONST_INT
/* If we have compiled a statement like
"if (x == (x & mask1))", and now are looking at
"x & mask2", we will have a case where the first operand
of Y is the same as our first operand. Unless we detect
this case, an infinite loop will result. */
|| XEXP (y, 0) == folded_arg0)
break;
/* Don't associate these operations if they are a PLUS with the
same constant and it is a power of two. These might be doable
with a pre- or post-increment. Similarly for two subtracts of
identical powers of two with post decrement. */
if (code == PLUS && INTVAL (const_arg1) == INTVAL (inner_const)
&& ((HAVE_PRE_INCREMENT
&& exact_log2 (INTVAL (const_arg1)) >= 0)
|| (HAVE_POST_INCREMENT
&& exact_log2 (INTVAL (const_arg1)) >= 0)
|| (HAVE_PRE_DECREMENT
&& exact_log2 (- INTVAL (const_arg1)) >= 0)
|| (HAVE_POST_DECREMENT
&& exact_log2 (- INTVAL (const_arg1)) >= 0)))
break;
/* Compute the code used to compose the constants. For example,
A/C1/C2 is A/(C1 * C2), so if CODE == DIV, we want MULT. */
associate_code
= (code == MULT || code == DIV || code == UDIV ? MULT
: is_shift || code == PLUS || code == MINUS ? PLUS : code);
new_const = simplify_binary_operation (associate_code, mode,
const_arg1, inner_const);
if (new_const == 0)
break;
/* If we are associating shift operations, don't let this
produce a shift of the size of the object or larger.
This could occur when we follow a sign-extend by a right
shift on a machine that does a sign-extend as a pair
of shifts. */
if (is_shift && GET_CODE (new_const) == CONST_INT
&& INTVAL (new_const) >= GET_MODE_BITSIZE (mode))
{
/* As an exception, we can turn an ASHIFTRT of this
form into a shift of the number of bits - 1. */
if (code == ASHIFTRT)
new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
else
break;
}
y = copy_rtx (XEXP (y, 0));
/* If Y contains our first operand (the most common way this
can happen is if Y is a MEM), we would do into an infinite
loop if we tried to fold it. So don't in that case. */
if (! reg_mentioned_p (folded_arg0, y))
y = fold_rtx (y, insn);
return cse_gen_binary (code, mode, y, new_const);
}
break;
default:
break;
}
new = simplify_binary_operation (code, mode,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1);
break;
case 'o':
/* (lo_sum (high X) X) is simply X. */
if (code == LO_SUM && const_arg0 != 0
&& GET_CODE (const_arg0) == HIGH
&& rtx_equal_p (XEXP (const_arg0, 0), const_arg1))
return const_arg1;
break;
case '3':
case 'b':
new = simplify_ternary_operation (code, mode, mode_arg0,
const_arg0 ? const_arg0 : folded_arg0,
const_arg1 ? const_arg1 : folded_arg1,
const_arg2 ? const_arg2 : XEXP (x, 2));
break;
case 'x':
/* Always eliminate CONSTANT_P_RTX at this stage. */
if (code == CONSTANT_P_RTX)
return (const_arg0 ? const1_rtx : const0_rtx);
break;
}
return new ? new : x;
}
/* Return a constant value currently equivalent to X.
Return 0 if we don't know one. */
static rtx
equiv_constant (x)
rtx x;
{
if (GET_CODE (x) == REG
&& REGNO_QTY_VALID_P (REGNO (x))
&& qty_const[REG_QTY (REGNO (x))])
x = gen_lowpart_if_possible (GET_MODE (x), qty_const[REG_QTY (REGNO (x))]);
if (x == 0 || CONSTANT_P (x))
return x;
/* If X is a MEM, try to fold it outside the context of any insn to see if
it might be equivalent to a constant. That handles the case where it
is a constant-pool reference. Then try to look it up in the hash table
in case it is something whose value we have seen before. */
if (GET_CODE (x) == MEM)
{
struct table_elt *elt;
x = fold_rtx (x, NULL_RTX);
if (CONSTANT_P (x))
return x;
elt = lookup (x, safe_hash (x, GET_MODE (x)) % NBUCKETS, GET_MODE (x));
if (elt == 0)
return 0;
for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
if (elt->is_const && CONSTANT_P (elt->exp))
return elt->exp;
}
return 0;
}
/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a fixed-point
number, return an rtx (MEM, SUBREG, or CONST_INT) that refers to the
least-significant part of X.
MODE specifies how big a part of X to return.
If the requested operation cannot be done, 0 is returned.
This is similar to gen_lowpart in emit-rtl.c. */
rtx
gen_lowpart_if_possible (mode, x)
enum machine_mode mode;
register rtx x;
{
rtx result = gen_lowpart_common (mode, x);
if (result)
return result;
else if (GET_CODE (x) == MEM)
{
/* This is the only other case we handle. */
register int offset = 0;
rtx new;
if (WORDS_BIG_ENDIAN)
offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
- MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
if (BYTES_BIG_ENDIAN)
/* Adjust the address so that the address-after-the-data is
unchanged. */
offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
- MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
new = gen_rtx_MEM (mode, plus_constant (XEXP (x, 0), offset));
if (! memory_address_p (mode, XEXP (new, 0)))
return 0;
RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
MEM_COPY_ATTRIBUTES (new, x);
return new;
}
else
return 0;
}
/* Given INSN, a jump insn, TAKEN indicates if we are following the "taken"
branch. It will be zero if not.
In certain cases, this can cause us to add an equivalence. For example,
if we are following the taken case of
if (i == 2)
we can add the fact that `i' and '2' are now equivalent.
In any case, we can record that this comparison was passed. If the same
comparison is seen later, we will know its value. */
static void
record_jump_equiv (insn, taken)
rtx insn;
int taken;
{
int cond_known_true;
rtx op0, op1;
enum machine_mode mode, mode0, mode1;
int reversed_nonequality = 0;
enum rtx_code code;
/* Ensure this is the right kind of insn. */
if (! condjump_p (insn) || simplejump_p (insn))
return;
/* See if this jump condition is known true or false. */
if (taken)
cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 2) == pc_rtx);
else
cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx);
/* Get the type of comparison being done and the operands being compared.
If we had to reverse a non-equality condition, record that fact so we
know that it isn't valid for floating-point. */
code = GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0));
op0 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0), insn);
op1 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 1), insn);
code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
if (! cond_known_true)
{
reversed_nonequality = (code != EQ && code != NE);
code = reverse_condition (code);
}
/* The mode is the mode of the non-constant. */
mode = mode0;
if (mode1 != VOIDmode)
mode = mode1;
record_jump_cond (code, mode, op0, op1, reversed_nonequality);
}
/* We know that comparison CODE applied to OP0 and OP1 in MODE is true.
REVERSED_NONEQUALITY is nonzero if CODE had to be swapped.
Make any useful entries we can with that information. Called from
above function and called recursively. */
static void
record_jump_cond (code, mode, op0, op1, reversed_nonequality)
enum rtx_code code;
enum machine_mode mode;
rtx op0, op1;
int reversed_nonequality;
{
unsigned op0_hash, op1_hash;
int op0_in_memory, op0_in_struct, op1_in_memory, op1_in_struct;
struct table_elt *op0_elt, *op1_elt;
/* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG,
we know that they are also equal in the smaller mode (this is also
true for all smaller modes whether or not there is a SUBREG, but
is not worth testing for with no SUBREG). */
/* Note that GET_MODE (op0) may not equal MODE. */
if (code == EQ && GET_CODE (op0) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
rtx tem = gen_lowpart_if_possible (inner_mode, op1);
record_jump_cond (code, mode, SUBREG_REG (op0),
tem ? tem : gen_rtx_SUBREG (inner_mode, op1, 0),
reversed_nonequality);
}
if (code == EQ && GET_CODE (op1) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (op1))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
rtx tem = gen_lowpart_if_possible (inner_mode, op0);
record_jump_cond (code, mode, SUBREG_REG (op1),
tem ? tem : gen_rtx_SUBREG (inner_mode, op0, 0),
reversed_nonequality);
}
/* Similarly, if this is an NE comparison, and either is a SUBREG
making a smaller mode, we know the whole thing is also NE. */
/* Note that GET_MODE (op0) may not equal MODE;
if we test MODE instead, we can get an infinite recursion
alternating between two modes each wider than MODE. */
if (code == NE && GET_CODE (op0) == SUBREG
&& subreg_lowpart_p (op0)
&& (GET_MODE_SIZE (GET_MODE (op0))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
rtx tem = gen_lowpart_if_possible (inner_mode, op1);
record_jump_cond (code, mode, SUBREG_REG (op0),
tem ? tem : gen_rtx_SUBREG (inner_mode, op1, 0),
reversed_nonequality);
}
if (code == NE && GET_CODE (op1) == SUBREG
&& subreg_lowpart_p (op1)
&& (GET_MODE_SIZE (GET_MODE (op1))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
{
enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
rtx tem = gen_lowpart_if_possible (inner_mode, op0);
record_jump_cond (code, mode, SUBREG_REG (op1),
tem ? tem : gen_rtx_SUBREG (inner_mode, op0, 0),
reversed_nonequality);
}
/* Hash both operands. */
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
op0_hash = HASH (op0, mode);
op0_in_memory = hash_arg_in_memory;
op0_in_struct = hash_arg_in_struct;
if (do_not_record)
return;
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
op1_hash = HASH (op1, mode);
op1_in_memory = hash_arg_in_memory;
op1_in_struct = hash_arg_in_struct;
if (do_not_record)
return;
/* Look up both operands. */
op0_elt = lookup (op0, op0_hash, mode);
op1_elt = lookup (op1, op1_hash, mode);
/* If both operands are already equivalent or if they are not in the
table but are identical, do nothing. */
if ((op0_elt != 0 && op1_elt != 0
&& op0_elt->first_same_value == op1_elt->first_same_value)
|| op0 == op1 || rtx_equal_p (op0, op1))
return;
/* If we aren't setting two things equal all we can do is save this
comparison. Similarly if this is floating-point. In the latter
case, OP1 might be zero and both -0.0 and 0.0 are equal to it.
If we record the equality, we might inadvertently delete code
whose intent was to change -0 to +0. */
if (code != EQ || FLOAT_MODE_P (GET_MODE (op0)))
{
/* If we reversed a floating-point comparison, if OP0 is not a
register, or if OP1 is neither a register or constant, we can't
do anything. */
if (GET_CODE (op1) != REG)
op1 = equiv_constant (op1);
if ((reversed_nonequality && FLOAT_MODE_P (mode))
|| GET_CODE (op0) != REG || op1 == 0)
return;
/* Put OP0 in the hash table if it isn't already. This gives it a
new quantity number. */
if (op0_elt == 0)
{
if (insert_regs (op0, NULL_PTR, 0))
{
rehash_using_reg (op0);
op0_hash = HASH (op0, mode);
/* If OP0 is contained in OP1, this changes its hash code
as well. Faster to rehash than to check, except
for the simple case of a constant. */
if (! CONSTANT_P (op1))
op1_hash = HASH (op1,mode);
}
op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
op0_elt->in_struct = op0_in_struct;
}
qty_comparison_code[REG_QTY (REGNO (op0))] = code;
if (GET_CODE (op1) == REG)
{
/* Look it up again--in case op0 and op1 are the same. */
op1_elt = lookup (op1, op1_hash, mode);
/* Put OP1 in the hash table so it gets a new quantity number. */
if (op1_elt == 0)
{
if (insert_regs (op1, NULL_PTR, 0))
{
rehash_using_reg (op1);
op1_hash = HASH (op1, mode);
}
op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
op1_elt->in_struct = op1_in_struct;
}
qty_comparison_qty[REG_QTY (REGNO (op0))] = REG_QTY (REGNO (op1));
qty_comparison_const[REG_QTY (REGNO (op0))] = 0;
}
else
{
qty_comparison_qty[REG_QTY (REGNO (op0))] = -1;
qty_comparison_const[REG_QTY (REGNO (op0))] = op1;
}
return;
}
/* If either side is still missing an equivalence, make it now,
then merge the equivalences. */
if (op0_elt == 0)
{
if (insert_regs (op0, NULL_PTR, 0))
{
rehash_using_reg (op0);
op0_hash = HASH (op0, mode);
}
op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
op0_elt->in_memory = op0_in_memory;
op0_elt->in_struct = op0_in_struct;
}
if (op1_elt == 0)
{
if (insert_regs (op1, NULL_PTR, 0))
{
rehash_using_reg (op1);
op1_hash = HASH (op1, mode);
}
op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
op1_elt->in_memory = op1_in_memory;
op1_elt->in_struct = op1_in_struct;
}
merge_equiv_classes (op0_elt, op1_elt);
last_jump_equiv_class = op0_elt;
}
/* CSE processing for one instruction.
First simplify sources and addresses of all assignments
in the instruction, using previously-computed equivalents values.
Then install the new sources and destinations in the table
of available values.
If LIBCALL_INSN is nonzero, don't record any equivalence made in
the insn. It means that INSN is inside libcall block. In this
case LIBCALL_INSN is the corresponding insn with REG_LIBCALL. */
/* Data on one SET contained in the instruction. */
struct set
{
/* The SET rtx itself. */
rtx rtl;
/* The SET_SRC of the rtx (the original value, if it is changing). */
rtx src;
/* The hash-table element for the SET_SRC of the SET. */
struct table_elt *src_elt;
/* Hash value for the SET_SRC. */
unsigned src_hash;
/* Hash value for the SET_DEST. */
unsigned dest_hash;
/* The SET_DEST, with SUBREG, etc., stripped. */
rtx inner_dest;
/* Place where the pointer to the INNER_DEST was found. */
rtx *inner_dest_loc;
/* Nonzero if the SET_SRC is in memory. */
char src_in_memory;
/* Nonzero if the SET_SRC is in a structure. */
char src_in_struct;
/* Nonzero if the SET_SRC contains something
whose value cannot be predicted and understood. */
char src_volatile;
/* Original machine mode, in case it becomes a CONST_INT. */
enum machine_mode mode;
/* A constant equivalent for SET_SRC, if any. */
rtx src_const;
/* Hash value of constant equivalent for SET_SRC. */
unsigned src_const_hash;
/* Table entry for constant equivalent for SET_SRC, if any. */
struct table_elt *src_const_elt;
};
static void
cse_insn (insn, libcall_insn)
rtx insn;
rtx libcall_insn;
{
register rtx x = PATTERN (insn);
register int i;
rtx tem;
register int n_sets = 0;
#ifdef HAVE_cc0
/* Records what this insn does to set CC0. */
rtx this_insn_cc0 = 0;
enum machine_mode this_insn_cc0_mode = VOIDmode;
#endif
rtx src_eqv = 0;
struct table_elt *src_eqv_elt = 0;
int src_eqv_volatile;
int src_eqv_in_memory;
int src_eqv_in_struct;
unsigned src_eqv_hash;
struct set *sets;
this_insn = insn;
/* Find all the SETs and CLOBBERs in this instruction.
Record all the SETs in the array `set' and count them.
Also determine whether there is a CLOBBER that invalidates
all memory references, or all references at varying addresses. */
if (GET_CODE (insn) == CALL_INSN)
{
for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
}
if (GET_CODE (x) == SET)
{
sets = (struct set *) alloca (sizeof (struct set));
sets[0].rtl = x;
/* Ignore SETs that are unconditional jumps.
They never need cse processing, so this does not hurt.
The reason is not efficiency but rather
so that we can test at the end for instructions
that have been simplified to unconditional jumps
and not be misled by unchanged instructions
that were unconditional jumps to begin with. */
if (SET_DEST (x) == pc_rtx
&& GET_CODE (SET_SRC (x)) == LABEL_REF)
;
/* Don't count call-insns, (set (reg 0) (call ...)), as a set.
The hard function value register is used only once, to copy to
someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)!
Ensure we invalidate the destination register. On the 80386 no
other code would invalidate it since it is a fixed_reg.
We need not check the return of apply_change_group; see canon_reg. */
else if (GET_CODE (SET_SRC (x)) == CALL)
{
canon_reg (SET_SRC (x), insn);
apply_change_group ();
fold_rtx (SET_SRC (x), insn);
invalidate (SET_DEST (x), VOIDmode);
}
else
n_sets = 1;
}
else if (GET_CODE (x) == PARALLEL)
{
register int lim = XVECLEN (x, 0);
sets = (struct set *) alloca (lim * sizeof (struct set));
/* Find all regs explicitly clobbered in this insn,
and ensure they are not replaced with any other regs
elsewhere in this insn.
When a reg that is clobbered is also used for input,
we should presume that that is for a reason,
and we should not substitute some other register
which is not supposed to be clobbered.
Therefore, this loop cannot be merged into the one below
because a CALL may precede a CLOBBER and refer to the
value clobbered. We must not let a canonicalization do
anything in that case. */
for (i = 0; i < lim; i++)
{
register rtx y = XVECEXP (x, 0, i);
if (GET_CODE (y) == CLOBBER)
{
rtx clobbered = XEXP (y, 0);
if (GET_CODE (clobbered) == REG
|| GET_CODE (clobbered) == SUBREG)
invalidate (clobbered, VOIDmode);
else if (GET_CODE (clobbered) == STRICT_LOW_PART
|| GET_CODE (clobbered) == ZERO_EXTRACT)
invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
}
}
for (i = 0; i < lim; i++)
{
register rtx y = XVECEXP (x, 0, i);
if (GET_CODE (y) == SET)
{
/* As above, we ignore unconditional jumps and call-insns and
ignore the result of apply_change_group. */
if (GET_CODE (SET_SRC (y)) == CALL)
{
canon_reg (SET_SRC (y), insn);
apply_change_group ();
fold_rtx (SET_SRC (y), insn);
invalidate (SET_DEST (y), VOIDmode);
}
else if (SET_DEST (y) == pc_rtx
&& GET_CODE (SET_SRC (y)) == LABEL_REF)
;
else
sets[n_sets++].rtl = y;
}
else if (GET_CODE (y) == CLOBBER)
{
/* If we clobber memory, canon the address.
This does nothing when a register is clobbered
because we have already invalidated the reg. */
if (GET_CODE (XEXP (y, 0)) == MEM)
canon_reg (XEXP (y, 0), NULL_RTX);
}
else if (GET_CODE (y) == USE
&& ! (GET_CODE (XEXP (y, 0)) == REG
&& REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
canon_reg (y, NULL_RTX);
else if (GET_CODE (y) == CALL)
{
/* The result of apply_change_group can be ignored; see
canon_reg. */
canon_reg (y, insn);
apply_change_group ();
fold_rtx (y, insn);
}
}
}
else if (GET_CODE (x) == CLOBBER)
{
if (GET_CODE (XEXP (x, 0)) == MEM)
canon_reg (XEXP (x, 0), NULL_RTX);
}
/* Canonicalize a USE of a pseudo register or memory location. */
else if (GET_CODE (x) == USE
&& ! (GET_CODE (XEXP (x, 0)) == REG
&& REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
canon_reg (XEXP (x, 0), NULL_RTX);
else if (GET_CODE (x) == CALL)
{
/* The result of apply_change_group can be ignored; see canon_reg. */
canon_reg (x, insn);
apply_change_group ();
fold_rtx (x, insn);
}
/* Store the equivalent value in SRC_EQV, if different, or if the DEST
is a STRICT_LOW_PART. The latter condition is necessary because SRC_EQV
is handled specially for this case, and if it isn't set, then there will
be no equivalence for the destination. */
if (n_sets == 1 && REG_NOTES (insn) != 0
&& (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
&& (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
|| GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX);
/* Canonicalize sources and addresses of destinations.
We do this in a separate pass to avoid problems when a MATCH_DUP is
present in the insn pattern. In that case, we want to ensure that
we don't break the duplicate nature of the pattern. So we will replace
both operands at the same time. Otherwise, we would fail to find an
equivalent substitution in the loop calling validate_change below.
We used to suppress canonicalization of DEST if it appears in SRC,
but we don't do this any more. */
for (i = 0; i < n_sets; i++)
{
rtx dest = SET_DEST (sets[i].rtl);
rtx src = SET_SRC (sets[i].rtl);
rtx new = canon_reg (src, insn);
int insn_code;
if ((GET_CODE (new) == REG && GET_CODE (src) == REG
&& ((REGNO (new) < FIRST_PSEUDO_REGISTER)
!= (REGNO (src) < FIRST_PSEUDO_REGISTER)))
|| (insn_code = recog_memoized (insn)) < 0
|| insn_n_dups[insn_code] > 0)
validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
else
SET_SRC (sets[i].rtl) = new;
if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
validate_change (insn, &XEXP (dest, 1),
canon_reg (XEXP (dest, 1), insn), 1);
validate_change (insn, &XEXP (dest, 2),
canon_reg (XEXP (dest, 2), insn), 1);
}
while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SIGN_EXTRACT)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == MEM)
canon_reg (dest, insn);
}
/* Now that we have done all the replacements, we can apply the change
group and see if they all work. Note that this will cause some
canonicalizations that would have worked individually not to be applied
because some other canonicalization didn't work, but this should not
occur often.
The result of apply_change_group can be ignored; see canon_reg. */
apply_change_group ();
/* Set sets[i].src_elt to the class each source belongs to.
Detect assignments from or to volatile things
and set set[i] to zero so they will be ignored
in the rest of this function.
Nothing in this loop changes the hash table or the register chains. */
for (i = 0; i < n_sets; i++)
{
register rtx src, dest;
register rtx src_folded;
register struct table_elt *elt = 0, *p;
enum machine_mode mode;
rtx src_eqv_here;
rtx src_const = 0;
rtx src_related = 0;
struct table_elt *src_const_elt = 0;
int src_cost = 10000, src_eqv_cost = 10000, src_folded_cost = 10000;
int src_related_cost = 10000, src_elt_cost = 10000;
/* Set non-zero if we need to call force_const_mem on with the
contents of src_folded before using it. */
int src_folded_force_flag = 0;
dest = SET_DEST (sets[i].rtl);
src = SET_SRC (sets[i].rtl);
/* If SRC is a constant that has no machine mode,
hash it with the destination's machine mode.
This way we can keep different modes separate. */
mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
sets[i].mode = mode;
if (src_eqv)
{
enum machine_mode eqvmode = mode;
if (GET_CODE (dest) == STRICT_LOW_PART)
eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
src_eqv = fold_rtx (src_eqv, insn);
src_eqv_hash = HASH (src_eqv, eqvmode);
/* Find the equivalence class for the equivalent expression. */
if (!do_not_record)
src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode);
src_eqv_volatile = do_not_record;
src_eqv_in_memory = hash_arg_in_memory;
src_eqv_in_struct = hash_arg_in_struct;
}
/* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the
value of the INNER register, not the destination. So it is not
a valid substitution for the source. But save it for later. */
if (GET_CODE (dest) == STRICT_LOW_PART)
src_eqv_here = 0;
else
src_eqv_here = src_eqv;
/* Simplify and foldable subexpressions in SRC. Then get the fully-
simplified result, which may not necessarily be valid. */
src_folded = fold_rtx (src, insn);
#if 0
/* ??? This caused bad code to be generated for the m68k port with -O2.
Suppose src is (CONST_INT -1), and that after truncation src_folded
is (CONST_INT 3). Suppose src_folded is then used for src_const.
At the end we will add src and src_const to the same equivalence
class. We now have 3 and -1 on the same equivalence class. This
causes later instructions to be mis-optimized. */
/* If storing a constant in a bitfield, pre-truncate the constant
so we will be able to record it later. */
if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT
|| GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT)
{
rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
if (GET_CODE (src) == CONST_INT
&& GET_CODE (width) == CONST_INT
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
src_folded
= GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
<< INTVAL (width)) - 1));
}
#endif
/* Compute SRC's hash code, and also notice if it
should not be recorded at all. In that case,
prevent any further processing of this assignment. */
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
sets[i].src = src;
sets[i].src_hash = HASH (src, mode);
sets[i].src_volatile = do_not_record;
sets[i].src_in_memory = hash_arg_in_memory;
sets[i].src_in_struct = hash_arg_in_struct;
/* If SRC is a MEM, there is a REG_EQUIV note for SRC, and DEST is
a pseudo that is set more than once, do not record SRC. Using
SRC as a replacement for anything else will be incorrect in that
situation. Note that this usually occurs only for stack slots,
in which case all the RTL would be referring to SRC, so we don't
lose any optimization opportunities by not having SRC in the
hash table. */
if (GET_CODE (src) == MEM
&& find_reg_note (insn, REG_EQUIV, src) != 0
&& GET_CODE (dest) == REG
&& REGNO (dest) >= FIRST_PSEUDO_REGISTER
&& REG_N_SETS (REGNO (dest)) != 1)
sets[i].src_volatile = 1;
#if 0
/* It is no longer clear why we used to do this, but it doesn't
appear to still be needed. So let's try without it since this
code hurts cse'ing widened ops. */
/* If source is a perverse subreg (such as QI treated as an SI),
treat it as volatile. It may do the work of an SI in one context
where the extra bits are not being used, but cannot replace an SI
in general. */
if (GET_CODE (src) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (src))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))
sets[i].src_volatile = 1;
#endif
/* Locate all possible equivalent forms for SRC. Try to replace
SRC in the insn with each cheaper equivalent.
We have the following types of equivalents: SRC itself, a folded
version, a value given in a REG_EQUAL note, or a value related
to a constant.
Each of these equivalents may be part of an additional class
of equivalents (if more than one is in the table, they must be in
the same class; we check for this).
If the source is volatile, we don't do any table lookups.
We note any constant equivalent for possible later use in a
REG_NOTE. */
if (!sets[i].src_volatile)
elt = lookup (src, sets[i].src_hash, mode);
sets[i].src_elt = elt;
if (elt && src_eqv_here && src_eqv_elt)
{
if (elt->first_same_value != src_eqv_elt->first_same_value)
{
/* The REG_EQUAL is indicating that two formerly distinct
classes are now equivalent. So merge them. */
merge_equiv_classes (elt, src_eqv_elt);
src_eqv_hash = HASH (src_eqv, elt->mode);
src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode);
}
src_eqv_here = 0;
}
else if (src_eqv_elt)
elt = src_eqv_elt;
/* Try to find a constant somewhere and record it in `src_const'.
Record its table element, if any, in `src_const_elt'. Look in
any known equivalences first. (If the constant is not in the
table, also set `sets[i].src_const_hash'). */
if (elt)
for (p = elt->first_same_value; p; p = p->next_same_value)
if (p->is_const)
{
src_const = p->exp;
src_const_elt = elt;
break;
}
if (src_const == 0
&& (CONSTANT_P (src_folded)
/* Consider (minus (label_ref L1) (label_ref L2)) as
"constant" here so we will record it. This allows us
to fold switch statements when an ADDR_DIFF_VEC is used. */
|| (GET_CODE (src_folded) == MINUS
&& GET_CODE (XEXP (src_folded, 0)) == LABEL_REF
&& GET_CODE (XEXP (src_folded, 1)) == LABEL_REF)))
src_const = src_folded, src_const_elt = elt;
else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here))
src_const = src_eqv_here, src_const_elt = src_eqv_elt;
/* If we don't know if the constant is in the table, get its
hash code and look it up. */
if (src_const && src_const_elt == 0)
{
sets[i].src_const_hash = HASH (src_const, mode);
src_const_elt = lookup (src_const, sets[i].src_const_hash, mode);
}
sets[i].src_const = src_const;
sets[i].src_const_elt = src_const_elt;
/* If the constant and our source are both in the table, mark them as
equivalent. Otherwise, if a constant is in the table but the source
isn't, set ELT to it. */
if (src_const_elt && elt
&& src_const_elt->first_same_value != elt->first_same_value)
merge_equiv_classes (elt, src_const_elt);
else if (src_const_elt && elt == 0)
elt = src_const_elt;
/* See if there is a register linearly related to a constant
equivalent of SRC. */
if (src_const
&& (GET_CODE (src_const) == CONST
|| (src_const_elt && src_const_elt->related_value != 0)))
{
src_related = use_related_value (src_const, src_const_elt);
if (src_related)
{
struct table_elt *src_related_elt
= lookup (src_related, HASH (src_related, mode), mode);
if (src_related_elt && elt)
{
if (elt->first_same_value
!= src_related_elt->first_same_value)
/* This can occur when we previously saw a CONST
involving a SYMBOL_REF and then see the SYMBOL_REF
twice. Merge the involved classes. */
merge_equiv_classes (elt, src_related_elt);
src_related = 0;
src_related_elt = 0;
}
else if (src_related_elt && elt == 0)
elt = src_related_elt;
}
}
/* See if we have a CONST_INT that is already in a register in a
wider mode. */
if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT
&& GET_MODE_CLASS (mode) == MODE_INT
&& GET_MODE_BITSIZE (mode) < BITS_PER_WORD)
{
enum machine_mode wider_mode;
for (wider_mode = GET_MODE_WIDER_MODE (mode);
GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
&& src_related == 0;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
struct table_elt *const_elt
= lookup (src_const, HASH (src_const, wider_mode), wider_mode);
if (const_elt == 0)
continue;
for (const_elt = const_elt->first_same_value;
const_elt; const_elt = const_elt->next_same_value)
if (GET_CODE (const_elt->exp) == REG)
{
src_related = gen_lowpart_if_possible (mode,
const_elt->exp);
break;
}
}
}
/* Another possibility is that we have an AND with a constant in
a mode narrower than a word. If so, it might have been generated
as part of an "if" which would narrow the AND. If we already
have done the AND in a wider mode, we can use a SUBREG of that
value. */
if (flag_expensive_optimizations && ! src_related
&& GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
{
enum machine_mode tmode;
rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
for (tmode = GET_MODE_WIDER_MODE (mode);
GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
tmode = GET_MODE_WIDER_MODE (tmode))
{
rtx inner = gen_lowpart_if_possible (tmode, XEXP (src, 0));
struct table_elt *larger_elt;
if (inner)
{
PUT_MODE (new_and, tmode);
XEXP (new_and, 0) = inner;
larger_elt = lookup (new_and, HASH (new_and, tmode), tmode);
if (larger_elt == 0)
continue;
for (larger_elt = larger_elt->first_same_value;
larger_elt; larger_elt = larger_elt->next_same_value)
if (GET_CODE (larger_elt->exp) == REG)
{
src_related
= gen_lowpart_if_possible (mode, larger_elt->exp);
break;
}
if (src_related)
break;
}
}
}
#ifdef LOAD_EXTEND_OP
/* See if a MEM has already been loaded with a widening operation;
if it has, we can use a subreg of that. Many CISC machines
also have such operations, but this is only likely to be
beneficial these machines. */
if (flag_expensive_optimizations && src_related == 0
&& (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
&& GET_MODE_CLASS (mode) == MODE_INT
&& GET_CODE (src) == MEM && ! do_not_record
&& LOAD_EXTEND_OP (mode) != NIL)
{
enum machine_mode tmode;
/* Set what we are trying to extend and the operation it might
have been extended with. */
PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
XEXP (memory_extend_rtx, 0) = src;
for (tmode = GET_MODE_WIDER_MODE (mode);
GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
tmode = GET_MODE_WIDER_MODE (tmode))
{
struct table_elt *larger_elt;
PUT_MODE (memory_extend_rtx, tmode);
larger_elt = lookup (memory_extend_rtx,
HASH (memory_extend_rtx, tmode), tmode);
if (larger_elt == 0)
continue;
for (larger_elt = larger_elt->first_same_value;
larger_elt; larger_elt = larger_elt->next_same_value)
if (GET_CODE (larger_elt->exp) == REG)
{
src_related = gen_lowpart_if_possible (mode,
larger_elt->exp);
break;
}
if (src_related)
break;
}
}
#endif /* LOAD_EXTEND_OP */
if (src == src_folded)
src_folded = 0;
/* At this point, ELT, if non-zero, points to a class of expressions
equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED,
and SRC_RELATED, if non-zero, each contain additional equivalent
expressions. Prune these latter expressions by deleting expressions
already in the equivalence class.
Check for an equivalent identical to the destination. If found,
this is the preferred equivalent since it will likely lead to
elimination of the insn. Indicate this by placing it in
`src_related'. */
if (elt) elt = elt->first_same_value;
for (p = elt; p; p = p->next_same_value)
{
enum rtx_code code = GET_CODE (p->exp);
/* If the expression is not valid, ignore it. Then we do not
have to check for validity below. In most cases, we can use
`rtx_equal_p', since canonicalization has already been done. */
if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, 0))
continue;
/* Also skip paradoxical subregs, unless that's what we're
looking for. */
if (code == SUBREG
&& (GET_MODE_SIZE (GET_MODE (p->exp))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))
&& ! (src != 0
&& GET_CODE (src) == SUBREG
&& GET_MODE (src) == GET_MODE (p->exp)
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))))
continue;
if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
src = 0;
else if (src_folded && GET_CODE (src_folded) == code
&& rtx_equal_p (src_folded, p->exp))
src_folded = 0;
else if (src_eqv_here && GET_CODE (src_eqv_here) == code
&& rtx_equal_p (src_eqv_here, p->exp))
src_eqv_here = 0;
else if (src_related && GET_CODE (src_related) == code
&& rtx_equal_p (src_related, p->exp))
src_related = 0;
/* This is the same as the destination of the insns, we want
to prefer it. Copy it to src_related. The code below will
then give it a negative cost. */
if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest))
src_related = dest;
}
/* Find the cheapest valid equivalent, trying all the available
possibilities. Prefer items not in the hash table to ones
that are when they are equal cost. Note that we can never
worsen an insn as the current contents will also succeed.
If we find an equivalent identical to the destination, use it as best,
since this insn will probably be eliminated in that case. */
if (src)
{
if (rtx_equal_p (src, dest))
src_cost = -1;
else
src_cost = COST (src);
}
if (src_eqv_here)
{
if (rtx_equal_p (src_eqv_here, dest))
src_eqv_cost = -1;
else
src_eqv_cost = COST (src_eqv_here);
}
if (src_folded)
{
if (rtx_equal_p (src_folded, dest))
src_folded_cost = -1;
else
src_folded_cost = COST (src_folded);
}
if (src_related)
{
if (rtx_equal_p (src_related, dest))
src_related_cost = -1;
else
src_related_cost = COST (src_related);
}
/* If this was an indirect jump insn, a known label will really be
cheaper even though it looks more expensive. */
if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF)
src_folded = src_const, src_folded_cost = -1;
/* Terminate loop when replacement made. This must terminate since
the current contents will be tested and will always be valid. */
while (1)
{
rtx trial, old_src;
/* Skip invalid entries. */
while (elt && GET_CODE (elt->exp) != REG
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
elt = elt->next_same_value;
/* A paradoxical subreg would be bad here: it'll be the right
size, but later may be adjusted so that the upper bits aren't
what we want. So reject it. */
if (elt != 0
&& GET_CODE (elt->exp) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (elt->exp))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))
/* It is okay, though, if the rtx we're trying to match
will ignore any of the bits we can't predict. */
&& ! (src != 0
&& GET_CODE (src) == SUBREG
&& GET_MODE (src) == GET_MODE (elt->exp)
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
< GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))))
{
elt = elt->next_same_value;
continue;
}
if (elt) src_elt_cost = elt->cost;
/* Find cheapest and skip it for the next time. For items
of equal cost, use this order:
src_folded, src, src_eqv, src_related and hash table entry. */
if (src_folded_cost <= src_cost
&& src_folded_cost <= src_eqv_cost
&& src_folded_cost <= src_related_cost
&& src_folded_cost <= src_elt_cost)
{
trial = src_folded, src_folded_cost = 10000;
if (src_folded_force_flag)
trial = force_const_mem (mode, trial);
}
else if (src_cost <= src_eqv_cost
&& src_cost <= src_related_cost
&& src_cost <= src_elt_cost)
trial = src, src_cost = 10000;
else if (src_eqv_cost <= src_related_cost
&& src_eqv_cost <= src_elt_cost)
trial = copy_rtx (src_eqv_here), src_eqv_cost = 10000;
else if (src_related_cost <= src_elt_cost)
trial = copy_rtx (src_related), src_related_cost = 10000;
else
{
trial = copy_rtx (elt->exp);
elt = elt->next_same_value;
src_elt_cost = 10000;
}
/* We don't normally have an insn matching (set (pc) (pc)), so
check for this separately here. We will delete such an
insn below.
Tablejump insns contain a USE of the table, so simply replacing
the operand with the constant won't match. This is simply an
unconditional branch, however, and is therefore valid. Just
insert the substitution here and we will delete and re-emit
the insn later. */
/* Keep track of the original SET_SRC so that we can fix notes
on libcall instructions. */
old_src = SET_SRC (sets[i].rtl);
if (n_sets == 1 && dest == pc_rtx
&& (trial == pc_rtx
|| (GET_CODE (trial) == LABEL_REF
&& ! condjump_p (insn))))
{
/* If TRIAL is a label in front of a jump table, we are
really falling through the switch (this is how casesi
insns work), so we must branch around the table. */
if (GET_CODE (trial) == CODE_LABEL
&& NEXT_INSN (trial) != 0
&& GET_CODE (NEXT_INSN (trial)) == JUMP_INSN
&& (GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_DIFF_VEC
|| GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_VEC))
trial = gen_rtx_LABEL_REF (Pmode, get_label_after (trial));
SET_SRC (sets[i].rtl) = trial;
cse_jumps_altered = 1;
break;
}
/* Look for a substitution that makes a valid insn. */
else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0))
{
/* If we just made a substitution inside a libcall, then we
need to make the same substitution in any notes attached
to the RETVAL insn. */
if (libcall_insn
&& (GET_CODE (old_src) == REG
|| GET_CODE (old_src) == SUBREG
|| GET_CODE (old_src) == MEM))
replace_rtx (REG_NOTES (libcall_insn), old_src,
canon_reg (SET_SRC (sets[i].rtl), insn));
/* The result of apply_change_group can be ignored; see
canon_reg. */
validate_change (insn, &SET_SRC (sets[i].rtl),
canon_reg (SET_SRC (sets[i].rtl), insn),
1);
apply_change_group ();
break;
}
/* If we previously found constant pool entries for
constants and this is a constant, try making a
pool entry. Put it in src_folded unless we already have done
this since that is where it likely came from. */
else if (constant_pool_entries_cost
&& CONSTANT_P (trial)
&& ! (GET_CODE (trial) == CONST
&& GET_CODE (XEXP (trial, 0)) == TRUNCATE)
&& (src_folded == 0
|| (GET_CODE (src_folded) != MEM
&& ! src_folded_force_flag))
&& GET_MODE_CLASS (mode) != MODE_CC
&& mode != VOIDmode)
{
src_folded_force_flag = 1;
src_folded = trial;
src_folded_cost = constant_pool_entries_cost;
}
}
src = SET_SRC (sets[i].rtl);
/* In general, it is good to have a SET with SET_SRC == SET_DEST.
However, there is an important exception: If both are registers
that are not the head of their equivalence class, replace SET_SRC
with the head of the class. If we do not do this, we will have
both registers live over a portion of the basic block. This way,
their lifetimes will likely abut instead of overlapping. */
if (GET_CODE (dest) == REG
&& REGNO_QTY_VALID_P (REGNO (dest))
&& qty_mode[REG_QTY (REGNO (dest))] == GET_MODE (dest)
&& qty_first_reg[REG_QTY (REGNO (dest))] != REGNO (dest)
&& GET_CODE (src) == REG && REGNO (src) == REGNO (dest)
/* Don't do this if the original insn had a hard reg as
SET_SRC. */
&& (GET_CODE (sets[i].src) != REG
|| REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER))
/* We can't call canon_reg here because it won't do anything if
SRC is a hard register. */
{
int first = qty_first_reg[REG_QTY (REGNO (src))];
rtx new_src
= (first >= FIRST_PSEUDO_REGISTER
? regno_reg_rtx[first] : gen_rtx_REG (GET_MODE (src), first));
/* We must use validate-change even for this, because this
might be a special no-op instruction, suitable only to
tag notes onto. */
if (validate_change (insn, &SET_SRC (sets[i].rtl), new_src, 0))
{
src = new_src;
/* If we had a constant that is cheaper than what we are now
setting SRC to, use that constant. We ignored it when we
thought we could make this into a no-op. */
if (src_const && COST (src_const) < COST (src)
&& validate_change (insn, &SET_SRC (sets[i].rtl), src_const,
0))
src = src_const;
}
}
/* If we made a change, recompute SRC values. */
if (src != sets[i].src)
{
do_not_record = 0;
hash_arg_in_memory = 0;
hash_arg_in_struct = 0;
sets[i].src = src;
sets[i].src_hash = HASH (src, mode);
sets[i].src_volatile = do_not_record;
sets[i].src_in_memory = hash_arg_in_memory;
sets[i].src_in_struct = hash_arg_in_struct;
sets[i].src_elt = lookup (src, sets[i].src_hash, mode);
}
/* If this is a single SET, we are setting a register, and we have an
equivalent constant, we want to add a REG_NOTE. We don't want
to write a REG_EQUAL note for a constant pseudo since verifying that
that pseudo hasn't been eliminated is a pain. Such a note also
won't help anything.
Avoid a REG_EQUAL note for (CONST (MINUS (LABEL_REF) (LABEL_REF)))
which can be created for a reference to a compile time computable
entry in a jump table. */
if (n_sets == 1 && src_const && GET_CODE (dest) == REG
&& GET_CODE (src_const) != REG
&& ! (GET_CODE (src_const) == CONST
&& GET_CODE (XEXP (src_const, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
&& GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF))
{
tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
/* Make sure that the rtx is not shared with any other insn. */
src_const = copy_rtx (src_const);
/* Record the actual constant value in a REG_EQUAL note, making
a new one if one does not already exist. */
if (tem)
XEXP (tem, 0) = src_const;
else
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL,
src_const, REG_NOTES (insn));
/* If storing a constant value in a register that
previously held the constant value 0,
record this fact with a REG_WAS_0 note on this insn.
Note that the *register* is required to have previously held 0,
not just any register in the quantity and we must point to the
insn that set that register to zero.
Rather than track each register individually, we just see if
the last set for this quantity was for this register. */
if (REGNO_QTY_VALID_P (REGNO (dest))
&& qty_const[REG_QTY (REGNO (dest))] == const0_rtx)
{
/* See if we previously had a REG_WAS_0 note. */
rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
rtx const_insn = qty_const_insn[REG_QTY (REGNO (dest))];
if ((tem = single_set (const_insn)) != 0
&& rtx_equal_p (SET_DEST (tem), dest))
{
if (note)
XEXP (note, 0) = const_insn;
else
REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_WAS_0,
const_insn,
REG_NOTES (insn));
}
}
}
/* Now deal with the destination. */
do_not_record = 0;
sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl);
/* Look within any SIGN_EXTRACT or ZERO_EXTRACT
to the MEM or REG within it. */
while (GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART)
{
sets[i].inner_dest_loc = &XEXP (dest, 0);
dest = XEXP (dest, 0);
}
sets[i].inner_dest = dest;
if (GET_CODE (dest) == MEM)
{
#ifdef PUSH_ROUNDING
/* Stack pushes invalidate the stack pointer. */
rtx addr = XEXP (dest, 0);
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
&& XEXP (addr, 0) == stack_pointer_rtx)
invalidate (stack_pointer_rtx, Pmode);
#endif
dest = fold_rtx (dest, insn);
}
/* Compute the hash code of the destination now,
before the effects of this instruction are recorded,
since the register values used in the address computation
are those before this instruction. */
sets[i].dest_hash = HASH (dest, mode);
/* Don't enter a bit-field in the hash table
because the value in it after the store
may not equal what was stored, due to truncation. */
if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT
|| GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT)
{
rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
if (src_const != 0 && GET_CODE (src_const) == CONST_INT
&& GET_CODE (width) == CONST_INT
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& ! (INTVAL (src_const)
& ((HOST_WIDE_INT) (-1) << INTVAL (width))))
/* Exception: if the value is constant,
and it won't be truncated, record it. */
;
else
{
/* This is chosen so that the destination will be invalidated
but no new value will be recorded.
We must invalidate because sometimes constant
values can be recorded for bitfields. */
sets[i].src_elt = 0;
sets[i].src_volatile = 1;
src_eqv = 0;
src_eqv_elt = 0;
}
}
/* If only one set in a JUMP_INSN and it is now a no-op, we can delete
the insn. */
else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
cse_jumps_altered = 1;
/* One less use of the label this insn used to jump to. */
if (JUMP_LABEL (insn) != 0)
--LABEL_NUSES (JUMP_LABEL (insn));
/* No more processing for this set. */
sets[i].rtl = 0;
}
/* If this SET is now setting PC to a label, we know it used to
be a conditional or computed branch. So we see if we can follow
it. If it was a computed branch, delete it and re-emit. */
else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF)
{
rtx p;
/* If this is not in the format for a simple branch and
we are the only SET in it, re-emit it. */
if (! simplejump_p (insn) && n_sets == 1)
{
rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
JUMP_LABEL (new) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
delete_insn (insn);
insn = new;
}
else
/* Otherwise, force rerecognition, since it probably had
a different pattern before.
This shouldn't really be necessary, since whatever
changed the source value above should have done this.
Until the right place is found, might as well do this here. */
INSN_CODE (insn) = -1;
/* Now that we've converted this jump to an unconditional jump,
there is dead code after it. Delete the dead code until we
reach a BARRIER, the end of the function, or a label. Do
not delete NOTEs except for NOTE_INSN_DELETED since later
phases assume these notes are retained. */
p = insn;
while (NEXT_INSN (p) != 0
&& GET_CODE (NEXT_INSN (p)) != BARRIER
&& GET_CODE (NEXT_INSN (p)) != CODE_LABEL)
{
/* Note, we must update P with the return value from
delete_insn, otherwise we could get an infinite loop
if NEXT_INSN (p) had INSN_DELETED_P set. */
if (GET_CODE (NEXT_INSN (p)) != NOTE
|| NOTE_LINE_NUMBER (NEXT_INSN (p)) == NOTE_INSN_DELETED)
p = PREV_INSN (delete_insn (NEXT_INSN (p)));
else
p = NEXT_INSN (p);
}
/* If we don't have a BARRIER immediately after INSN, put one there.
Much code assumes that there are no NOTEs between a JUMP_INSN and
BARRIER. */
if (NEXT_INSN (insn) == 0
|| GET_CODE (NEXT_INSN (insn)) != BARRIER)
emit_barrier_before (NEXT_INSN (insn));
/* We might have two BARRIERs separated by notes. Delete the second
one if so. */
if (p != insn && NEXT_INSN (p) != 0
&& GET_CODE (NEXT_INSN (p)) == BARRIER)
delete_insn (NEXT_INSN (p));
cse_jumps_altered = 1;
sets[i].rtl = 0;
}
/* If destination is volatile, invalidate it and then do no further
processing for this assignment. */
else if (do_not_record)
{
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == MEM)
invalidate (dest, VOIDmode);
else if (GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
sets[i].rtl = 0;
}
if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
#ifdef HAVE_cc0
/* If setting CC0, record what it was set to, or a constant, if it
is equivalent to a constant. If it is being set to a floating-point
value, make a COMPARE with the appropriate constant of 0. If we
don't do this, later code can interpret this as a test against
const0_rtx, which can cause problems if we try to put it into an
insn as a floating-point operand. */
if (dest == cc0_rtx)
{
this_insn_cc0 = src_const && mode != VOIDmode ? src_const : src;
this_insn_cc0_mode = mode;
if (FLOAT_MODE_P (mode))
this_insn_cc0 = gen_rtx_COMPARE (VOIDmode, this_insn_cc0,
CONST0_RTX (mode));
}
#endif
}
/* Now enter all non-volatile source expressions in the hash table
if they are not already present.
Record their equivalence classes in src_elt.
This way we can insert the corresponding destinations into
the same classes even if the actual sources are no longer in them
(having been invalidated). */
if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile
&& ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl)))
{
register struct table_elt *elt;
register struct table_elt *classp = sets[0].src_elt;
rtx dest = SET_DEST (sets[0].rtl);
enum machine_mode eqvmode = GET_MODE (dest);
if (GET_CODE (dest) == STRICT_LOW_PART)
{
eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
classp = 0;
}
if (insert_regs (src_eqv, classp, 0))
{
rehash_using_reg (src_eqv);
src_eqv_hash = HASH (src_eqv, eqvmode);
}
elt = insert (src_eqv, classp, src_eqv_hash, eqvmode);
elt->in_memory = src_eqv_in_memory;
elt->in_struct = src_eqv_in_struct;
src_eqv_elt = elt;
/* Check to see if src_eqv_elt is the same as a set source which
does not yet have an elt, and if so set the elt of the set source
to src_eqv_elt. */
for (i = 0; i < n_sets; i++)
if (sets[i].rtl && sets[i].src_elt == 0
&& rtx_equal_p (SET_SRC (sets[i].rtl), src_eqv))
sets[i].src_elt = src_eqv_elt;
}
for (i = 0; i < n_sets; i++)
if (sets[i].rtl && ! sets[i].src_volatile
&& ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl)))
{
if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART)
{
/* REG_EQUAL in setting a STRICT_LOW_PART
gives an equivalent for the entire destination register,
not just for the subreg being stored in now.
This is a more interesting equivalence, so we arrange later
to treat the entire reg as the destination. */
sets[i].src_elt = src_eqv_elt;
sets[i].src_hash = src_eqv_hash;
}
else
{
/* Insert source and constant equivalent into hash table, if not
already present. */
register struct table_elt *classp = src_eqv_elt;
register rtx src = sets[i].src;
register rtx dest = SET_DEST (sets[i].rtl);
enum machine_mode mode
= GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
/* Don't put a hard register source into the table if this is
the last insn of a libcall. */
if (sets[i].src_elt == 0
&& (GET_CODE (src) != REG
|| REGNO (src) >= FIRST_PSEUDO_REGISTER
|| ! find_reg_note (insn, REG_RETVAL, NULL_RTX)))
{
register struct table_elt *elt;
/* Note that these insert_regs calls cannot remove
any of the src_elt's, because they would have failed to
match if not still valid. */
if (insert_regs (src, classp, 0))
{
rehash_using_reg (src);
sets[i].src_hash = HASH (src, mode);
}
elt = insert (src, classp, sets[i].src_hash, mode);
elt->in_memory = sets[i].src_in_memory;
elt->in_struct = sets[i].src_in_struct;
sets[i].src_elt = classp = elt;
}
if (sets[i].src_const && sets[i].src_const_elt == 0
&& src != sets[i].src_const
&& ! rtx_equal_p (sets[i].src_const, src))
sets[i].src_elt = insert (sets[i].src_const, classp,
sets[i].src_const_hash, mode);
}
}
else if (sets[i].src_elt == 0)
/* If we did not insert the source into the hash table (e.g., it was
volatile), note the equivalence class for the REG_EQUAL value, if any,
so that the destination goes into that class. */
sets[i].src_elt = src_eqv_elt;
invalidate_from_clobbers (x);
/* Some registers are invalidated by subroutine calls. Memory is
invalidated by non-constant calls. */
if (GET_CODE (insn) == CALL_INSN)
{
if (! CONST_CALL_P (insn))
invalidate_memory ();
invalidate_for_call ();
}
/* Now invalidate everything set by this instruction.
If a SUBREG or other funny destination is being set,
sets[i].rtl is still nonzero, so here we invalidate the reg
a part of which is being set. */
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
/* We can't use the inner dest, because the mode associated with
a ZERO_EXTRACT is significant. */
register rtx dest = SET_DEST (sets[i].rtl);
/* Needed for registers to remove the register from its
previous quantity's chain.
Needed for memory if this is a nonvarying address, unless
we have just done an invalidate_memory that covers even those. */
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == MEM)
invalidate (dest, VOIDmode);
else if (GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
}
/* A volatile ASM invalidates everything. */
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == ASM_OPERANDS
&& MEM_VOLATILE_P (PATTERN (insn)))
flush_hash_table ();
/* Make sure registers mentioned in destinations
are safe for use in an expression to be inserted.
This removes from the hash table
any invalid entry that refers to one of these registers.
We don't care about the return value from mention_regs because
we are going to hash the SET_DEST values unconditionally. */
for (i = 0; i < n_sets; i++)
{
if (sets[i].rtl)
{
rtx x = SET_DEST (sets[i].rtl);
if (GET_CODE (x) != REG)
mention_regs (x);
else
{
/* We used to rely on all references to a register becoming
inaccessible when a register changes to a new quantity,
since that changes the hash code. However, that is not
safe, since after NBUCKETS new quantities we get a
hash 'collision' of a register with its own invalid
entries. And since SUBREGs have been changed not to
change their hash code with the hash code of the register,
it wouldn't work any longer at all. So we have to check
for any invalid references lying around now.
This code is similar to the REG case in mention_regs,
but it knows that reg_tick has been incremented, and
it leaves reg_in_table as -1 . */
register int regno = REGNO (x);
register int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (regno, GET_MODE (x)));
int i;
for (i = regno; i < endregno; i++)
{
if (REG_IN_TABLE (i) >= 0)
{
remove_invalid_refs (i);
REG_IN_TABLE (i) = -1;
}
}
}
}
}
/* We may have just removed some of the src_elt's from the hash table.
So replace each one with the current head of the same class. */
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0)
/* If elt was removed, find current head of same class,
or 0 if nothing remains of that class. */
{
register struct table_elt *elt = sets[i].src_elt;
while (elt && elt->prev_same_value)
elt = elt->prev_same_value;
while (elt && elt->first_same_value == 0)
elt = elt->next_same_value;
sets[i].src_elt = elt ? elt->first_same_value : 0;
}
}
/* Now insert the destinations into their equivalence classes. */
for (i = 0; i < n_sets; i++)
if (sets[i].rtl)
{
register rtx dest = SET_DEST (sets[i].rtl);
rtx inner_dest = sets[i].inner_dest;
register struct table_elt *elt;
/* Don't record value if we are not supposed to risk allocating
floating-point values in registers that might be wider than
memory. */
if ((flag_float_store
&& GET_CODE (dest) == MEM
&& FLOAT_MODE_P (GET_MODE (dest)))
/* Don't record BLKmode values, because we don't know the
size of it, and can't be sure that other BLKmode values
have the same or smaller size. */
|| GET_MODE (dest) == BLKmode
/* Don't record values of destinations set inside a libcall block
since we might delete the libcall. Things should have been set
up so we won't want to reuse such a value, but we play it safe
here. */
|| libcall_insn
/* If we didn't put a REG_EQUAL value or a source into the hash
table, there is no point is recording DEST. */
|| sets[i].src_elt == 0
/* If DEST is a paradoxical SUBREG and SRC is a ZERO_EXTEND
or SIGN_EXTEND, don't record DEST since it can cause
some tracking to be wrong.
??? Think about this more later. */
|| (GET_CODE (dest) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (dest))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
&& (GET_CODE (sets[i].src) == SIGN_EXTEND
|| GET_CODE (sets[i].src) == ZERO_EXTEND)))
continue;
/* STRICT_LOW_PART isn't part of the value BEING set,
and neither is the SUBREG inside it.
Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */
if (GET_CODE (dest) == STRICT_LOW_PART)
dest = SUBREG_REG (XEXP (dest, 0));
if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
/* Registers must also be inserted into chains for quantities. */
if (insert_regs (dest, sets[i].src_elt, 1))
{
/* If `insert_regs' changes something, the hash code must be
recalculated. */
rehash_using_reg (dest);
sets[i].dest_hash = HASH (dest, GET_MODE (dest));
}
if (GET_CODE (inner_dest) == MEM
&& GET_CODE (XEXP (inner_dest, 0)) == ADDRESSOF)
/* Given (SET (MEM (ADDRESSOF (X))) Y) we don't want to say
that (MEM (ADDRESSOF (X))) is equivalent to Y.
Consider the case in which the address of the MEM is
passed to a function, which alters the MEM. Then, if we
later use Y instead of the MEM we'll miss the update. */
elt = insert (dest, 0, sets[i].dest_hash, GET_MODE (dest));
else
elt = insert (dest, sets[i].src_elt,
sets[i].dest_hash, GET_MODE (dest));
elt->in_memory = (GET_CODE (sets[i].inner_dest) == MEM
&& (! RTX_UNCHANGING_P (sets[i].inner_dest)
|| FIXED_BASE_PLUS_P (XEXP (sets[i].inner_dest,
0))));
if (elt->in_memory)
{
/* This implicitly assumes a whole struct
need not have MEM_IN_STRUCT_P.
But a whole struct is *supposed* to have MEM_IN_STRUCT_P. */
elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest)
|| sets[i].inner_dest != SET_DEST (sets[i].rtl));
}
/* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no
narrower than M2, and both M1 and M2 are the same number of words,
we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so
make that equivalence as well.
However, BAR may have equivalences for which gen_lowpart_if_possible
will produce a simpler value than gen_lowpart_if_possible applied to
BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all
BAR's equivalences. If we don't get a simplified form, make
the SUBREG. It will not be used in an equivalence, but will
cause two similar assignments to be detected.
Note the loop below will find SUBREG_REG (DEST) since we have
already entered SRC and DEST of the SET in the table. */
if (GET_CODE (dest) == SUBREG
&& (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
/ UNITS_PER_WORD)
== (GET_MODE_SIZE (GET_MODE (dest)) - 1)/ UNITS_PER_WORD)
&& (GET_MODE_SIZE (GET_MODE (dest))
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
&& sets[i].src_elt != 0)
{
enum machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
struct table_elt *elt, *classp = 0;
for (elt = sets[i].src_elt->first_same_value; elt;
elt = elt->next_same_value)
{
rtx new_src = 0;
unsigned src_hash;
struct table_elt *src_elt;
/* Ignore invalid entries. */
if (GET_CODE (elt->exp) != REG
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
continue;
new_src = gen_lowpart_if_possible (new_mode, elt->exp);
if (new_src == 0)
new_src = gen_rtx_SUBREG (new_mode, elt->exp, 0);
src_hash = HASH (new_src, new_mode);
src_elt = lookup (new_src, src_hash, new_mode);
/* Put the new source in the hash table is if isn't
already. */
if (src_elt == 0)
{
if (insert_regs (new_src, classp, 0))
{
rehash_using_reg (new_src);
src_hash = HASH (new_src, new_mode);
}
src_elt = insert (new_src, classp, src_hash, new_mode);
src_elt->in_memory = elt->in_memory;
src_elt->in_struct = elt->in_struct;
}
else if (classp && classp != src_elt->first_same_value)
/* Show that two things that we've seen before are
actually the same. */
merge_equiv_classes (src_elt, classp);
classp = src_elt->first_same_value;
/* Ignore invalid entries. */
while (classp
&& GET_CODE (classp->exp) != REG
&& ! exp_equiv_p (classp->exp, classp->exp, 1, 0))
classp = classp->next_same_value;
}
}
}
/* Special handling for (set REG0 REG1)
where REG0 is the "cheapest", cheaper than REG1.
After cse, REG1 will probably not be used in the sequel,
so (if easily done) change this insn to (set REG1 REG0) and
replace REG1 with REG0 in the previous insn that computed their value.
Then REG1 will become a dead store and won't cloud the situation
for later optimizations.
Do not make this change if REG1 is a hard register, because it will
then be used in the sequel and we may be changing a two-operand insn
into a three-operand insn.
Also do not do this if we are operating on a copy of INSN.
Also don't do this if INSN ends a libcall; this would cause an unrelated
register to be set in the middle of a libcall, and we then get bad code
if the libcall is deleted. */
if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG
&& NEXT_INSN (PREV_INSN (insn)) == insn
&& GET_CODE (SET_SRC (sets[0].rtl)) == REG
&& REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER
&& REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl)))
&& (qty_first_reg[REG_QTY (REGNO (SET_SRC (sets[0].rtl)))]
== REGNO (SET_DEST (sets[0].rtl)))
&& ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
rtx prev = PREV_INSN (insn);
while (prev && GET_CODE (prev) == NOTE)
prev = PREV_INSN (prev);
if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET
&& SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl))
{
rtx dest = SET_DEST (sets[0].rtl);
rtx note = find_reg_note (prev, REG_EQUIV, NULL_RTX);
validate_change (prev, & SET_DEST (PATTERN (prev)), dest, 1);
validate_change (insn, & SET_DEST (sets[0].rtl),
SET_SRC (sets[0].rtl), 1);
validate_change (insn, & SET_SRC (sets[0].rtl), dest, 1);
apply_change_group ();
/* If REG1 was equivalent to a constant, REG0 is not. */
if (note)
PUT_REG_NOTE_KIND (note, REG_EQUAL);
/* If there was a REG_WAS_0 note on PREV, remove it. Move
any REG_WAS_0 note on INSN to PREV. */
note = find_reg_note (prev, REG_WAS_0, NULL_RTX);
if (note)
remove_note (prev, note);
note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
if (note)
{
remove_note (insn, note);
XEXP (note, 1) = REG_NOTES (prev);
REG_NOTES (prev) = note;
}
/* If INSN has a REG_EQUAL note, and this note mentions REG0,
then we must delete it, because the value in REG0 has changed. */
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (note && reg_mentioned_p (dest, XEXP (note, 0)))
remove_note (insn, note);
}
}
/* If this is a conditional jump insn, record any known equivalences due to
the condition being tested. */
last_jump_equiv_class = 0;
if (GET_CODE (insn) == JUMP_INSN
&& n_sets == 1 && GET_CODE (x) == SET
&& GET_CODE (SET_SRC (x)) == IF_THEN_ELSE)
record_jump_equiv (insn, 0);
#ifdef HAVE_cc0
/* If the previous insn set CC0 and this insn no longer references CC0,
delete the previous insn. Here we use the fact that nothing expects CC0
to be valid over an insn, which is true until the final pass. */
if (prev_insn && GET_CODE (prev_insn) == INSN
&& (tem = single_set (prev_insn)) != 0
&& SET_DEST (tem) == cc0_rtx
&& ! reg_mentioned_p (cc0_rtx, x))
{
PUT_CODE (prev_insn, NOTE);
NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (prev_insn) = 0;
}
prev_insn_cc0 = this_insn_cc0;
prev_insn_cc0_mode = this_insn_cc0_mode;
#endif
prev_insn = insn;
}
/* Remove from the hash table all expressions that reference memory. */
static void
invalidate_memory ()
{
register int i;
register struct table_elt *p, *next;
for (i = 0; i < NBUCKETS; i++)
for (p = table[i]; p; p = next)
{
next = p->next_same_hash;
if (p->in_memory)
remove_from_table (p, i);
}
}
/* XXX ??? The name of this function bears little resemblance to
what this function actually does. FIXME. */
static int
note_mem_written (addr)
register rtx addr;
{
/* Pushing or popping the stack invalidates just the stack pointer. */
if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
|| GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
&& GET_CODE (XEXP (addr, 0)) == REG
&& REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
{
if (REG_TICK (STACK_POINTER_REGNUM) >= 0)
REG_TICK (STACK_POINTER_REGNUM)++;
/* This should be *very* rare. */
if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
invalidate (stack_pointer_rtx, VOIDmode);
return 1;
}
return 0;
}
/* Perform invalidation on the basis of everything about an insn
except for invalidating the actual places that are SET in it.
This includes the places CLOBBERed, and anything that might
alias with something that is SET or CLOBBERed.
X is the pattern of the insn. */
static void
invalidate_from_clobbers (x)
rtx x;
{
if (GET_CODE (x) == CLOBBER)
{
rtx ref = XEXP (x, 0);
if (ref)
{
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|| GET_CODE (ref) == MEM)
invalidate (ref, VOIDmode);
else if (GET_CODE (ref) == STRICT_LOW_PART
|| GET_CODE (ref) == ZERO_EXTRACT)
invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
else if (GET_CODE (x) == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
register rtx y = XVECEXP (x, 0, i);
if (GET_CODE (y) == CLOBBER)
{
rtx ref = XEXP (y, 0);
if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
|| GET_CODE (ref) == MEM)
invalidate (ref, VOIDmode);
else if (GET_CODE (ref) == STRICT_LOW_PART
|| GET_CODE (ref) == ZERO_EXTRACT)
invalidate (XEXP (ref, 0), GET_MODE (ref));
}
}
}
}
/* Process X, part of the REG_NOTES of an insn. Look at any REG_EQUAL notes
and replace any registers in them with either an equivalent constant
or the canonical form of the register. If we are inside an address,
only do this if the address remains valid.
OBJECT is 0 except when within a MEM in which case it is the MEM.
Return the replacement for X. */
static rtx
cse_process_notes (x, object)
rtx x;
rtx object;
{
enum rtx_code code = GET_CODE (x);
char *fmt = GET_RTX_FORMAT (code);
int i;
switch (code)
{
case CONST_INT:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
case PC:
case CC0:
case LO_SUM:
return x;
case MEM:
XEXP (x, 0) = cse_process_notes (XEXP (x, 0), x);
return x;
case EXPR_LIST:
case INSN_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL)
XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX);
if (XEXP (x, 1))
XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX);
return x;
case SIGN_EXTEND:
case ZERO_EXTEND:
case SUBREG:
{
rtx new = cse_process_notes (XEXP (x, 0), object);
/* We don't substitute VOIDmode constants into these rtx,
since they would impede folding. */
if (GET_MODE (new) != VOIDmode)
validate_change (object, &XEXP (x, 0), new, 0);
return x;
}
case REG:
i = REG_QTY (REGNO (x));
/* Return a constant or a constant register. */
if (REGNO_QTY_VALID_P (REGNO (x))
&& qty_const[i] != 0
&& (CONSTANT_P (qty_const[i])
|| GET_CODE (qty_const[i]) == REG))
{
rtx new = gen_lowpart_if_possible (GET_MODE (x), qty_const[i]);
if (new)
return new;
}
/* Otherwise, canonicalize this register. */
return canon_reg (x, NULL_RTX);
default:
break;
}
for (i = 0; i < GET_RTX_LENGTH (code); i++)
if (fmt[i] == 'e')
validate_change (object, &XEXP (x, i),
cse_process_notes (XEXP (x, i), object), 0);
return x;
}
/* Find common subexpressions between the end test of a loop and the beginning
of the loop. LOOP_START is the CODE_LABEL at the start of a loop.
Often we have a loop where an expression in the exit test is used
in the body of the loop. For example "while (*p) *q++ = *p++;".
Because of the way we duplicate the loop exit test in front of the loop,
however, we don't detect that common subexpression. This will be caught
when global cse is implemented, but this is a quite common case.
This function handles the most common cases of these common expressions.
It is called after we have processed the basic block ending with the
NOTE_INSN_LOOP_END note that ends a loop and the previous JUMP_INSN
jumps to a label used only once. */
static void
cse_around_loop (loop_start)
rtx loop_start;
{
rtx insn;
int i;
struct table_elt *p;
/* If the jump at the end of the loop doesn't go to the start, we don't
do anything. */
for (insn = PREV_INSN (loop_start);
insn && (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0);
insn = PREV_INSN (insn))
;
if (insn == 0
|| GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG)
return;
/* If the last insn of the loop (the end test) was an NE comparison,
we will interpret it as an EQ comparison, since we fell through
the loop. Any equivalences resulting from that comparison are
therefore not valid and must be invalidated. */
if (last_jump_equiv_class)
for (p = last_jump_equiv_class->first_same_value; p;
p = p->next_same_value)
{
if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG
|| (GET_CODE (p->exp) == SUBREG
&& GET_CODE (SUBREG_REG (p->exp)) == REG))
invalidate (p->exp, VOIDmode);
else if (GET_CODE (p->exp) == STRICT_LOW_PART
|| GET_CODE (p->exp) == ZERO_EXTRACT)
invalidate (XEXP (p->exp, 0), GET_MODE (p->exp));
}
/* Process insns starting after LOOP_START until we hit a CALL_INSN or
a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it).
The only thing we do with SET_DEST is invalidate entries, so we
can safely process each SET in order. It is slightly less efficient
to do so, but we only want to handle the most common cases.
The gen_move_insn call in cse_set_around_loop may create new pseudos.
These pseudos won't have valid entries in any of the tables indexed
by register number, such as reg_qty. We avoid out-of-range array
accesses by not processing any instructions created after cse started. */
for (insn = NEXT_INSN (loop_start);
GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != CODE_LABEL
&& INSN_UID (insn) < max_insn_uid
&& ! (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER))
cse_set_around_loop (PATTERN (insn), insn, loop_start);
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER)
cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn,
loop_start);
}
}
/* Process one SET of an insn that was skipped. We ignore CLOBBERs
since they are done elsewhere. This function is called via note_stores. */
static void
invalidate_skipped_set (dest, set)
rtx set;
rtx dest;
{
enum rtx_code code = GET_CODE (dest);
if (code == MEM
&& ! note_mem_written (dest) /* If this is not a stack push ... */
/* There are times when an address can appear varying and be a PLUS
during this scan when it would be a fixed address were we to know
the proper equivalences. So invalidate all memory if there is
a BLKmode or nonscalar memory reference or a reference to a
variable address. */
&& (MEM_IN_STRUCT_P (dest) || GET_MODE (dest) == BLKmode
|| cse_rtx_varies_p (XEXP (dest, 0))))
{
invalidate_memory ();
return;
}
if (GET_CODE (set) == CLOBBER
#ifdef HAVE_cc0
|| dest == cc0_rtx
#endif
|| dest == pc_rtx)
return;
if (code == STRICT_LOW_PART || code == ZERO_EXTRACT)
invalidate (XEXP (dest, 0), GET_MODE (dest));
else if (code == REG || code == SUBREG || code == MEM)
invalidate (dest, VOIDmode);
}
/* Invalidate all insns from START up to the end of the function or the
next label. This called when we wish to CSE around a block that is
conditionally executed. */
static void
invalidate_skipped_block (start)
rtx start;
{
rtx insn;
for (insn = start; insn && GET_CODE (insn) != CODE_LABEL;
insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (GET_CODE (insn) == CALL_INSN)
{
if (! CONST_CALL_P (insn))
invalidate_memory ();
invalidate_for_call ();
}
invalidate_from_clobbers (PATTERN (insn));
note_stores (PATTERN (insn), invalidate_skipped_set);
}
}
/* Used for communication between the following two routines; contains a
value to be checked for modification. */
static rtx cse_check_loop_start_value;
/* If modifying X will modify the value in CSE_CHECK_LOOP_START_VALUE,
indicate that fact by setting CSE_CHECK_LOOP_START_VALUE to 0. */
static void
cse_check_loop_start (x, set)
rtx x;
rtx set ATTRIBUTE_UNUSED;
{
if (cse_check_loop_start_value == 0
|| GET_CODE (x) == CC0 || GET_CODE (x) == PC)
return;
if ((GET_CODE (x) == MEM && GET_CODE (cse_check_loop_start_value) == MEM)
|| reg_overlap_mentioned_p (x, cse_check_loop_start_value))
cse_check_loop_start_value = 0;
}
/* X is a SET or CLOBBER contained in INSN that was found near the start of
a loop that starts with the label at LOOP_START.
If X is a SET, we see if its SET_SRC is currently in our hash table.
If so, we see if it has a value equal to some register used only in the
loop exit code (as marked by jump.c).
If those two conditions are true, we search backwards from the start of
the loop to see if that same value was loaded into a register that still
retains its value at the start of the loop.
If so, we insert an insn after the load to copy the destination of that
load into the equivalent register and (try to) replace our SET_SRC with that
register.
In any event, we invalidate whatever this SET or CLOBBER modifies. */
static void
cse_set_around_loop (x, insn, loop_start)
rtx x;
rtx insn;
rtx loop_start;
{
struct table_elt *src_elt;
/* If this is a SET, see if we can replace SET_SRC, but ignore SETs that
are setting PC or CC0 or whose SET_SRC is already a register. */
if (GET_CODE (x) == SET
&& GET_CODE (SET_DEST (x)) != PC && GET_CODE (SET_DEST (x)) != CC0
&& GET_CODE (SET_SRC (x)) != REG)
{
src_elt = lookup (SET_SRC (x),
HASH (SET_SRC (x), GET_MODE (SET_DEST (x))),
GET_MODE (SET_DEST (x)));
if (src_elt)
for (src_elt = src_elt->first_same_value; src_elt;
src_elt = src_elt->next_same_value)
if (GET_CODE (src_elt->exp) == REG && REG_LOOP_TEST_P (src_elt->exp)
&& COST (src_elt->exp) < COST (SET_SRC (x)))
{
rtx p, set;
/* Look for an insn in front of LOOP_START that sets
something in the desired mode to SET_SRC (x) before we hit
a label or CALL_INSN. */
for (p = prev_nonnote_insn (loop_start);
p && GET_CODE (p) != CALL_INSN
&& GET_CODE (p) != CODE_LABEL;
p = prev_nonnote_insn (p))
if ((set = single_set (p)) != 0
&& GET_CODE (SET_DEST (set)) == REG
&& GET_MODE (SET_DEST (set)) == src_elt->mode
&& rtx_equal_p (SET_SRC (set), SET_SRC (x)))
{
/* We now have to ensure that nothing between P
and LOOP_START modified anything referenced in
SET_SRC (x). We know that nothing within the loop
can modify it, or we would have invalidated it in
the hash table. */
rtx q;
cse_check_loop_start_value = SET_SRC (x);
for (q = p; q != loop_start; q = NEXT_INSN (q))
if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
note_stores (PATTERN (q), cse_check_loop_start);
/* If nothing was changed and we can replace our
SET_SRC, add an insn after P to copy its destination
to what we will be replacing SET_SRC with. */
if (cse_check_loop_start_value
&& validate_change (insn, &SET_SRC (x),
src_elt->exp, 0))
{
/* If this creates new pseudos, this is unsafe,
because the regno of new pseudo is unsuitable
to index into reg_qty when cse_insn processes
the new insn. Therefore, if a new pseudo was
created, discard this optimization. */
int nregs = max_reg_num ();
rtx move
= gen_move_insn (src_elt->exp, SET_DEST (set));
if (nregs != max_reg_num ())
{
if (! validate_change (insn, &SET_SRC (x),
SET_SRC (set), 0))
abort ();
}
else
emit_insn_after (move, p);
}
break;
}
}
}
/* Now invalidate anything modified by X. */
note_mem_written (SET_DEST (x));
/* See comment on similar code in cse_insn for explanation of these tests. */
if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
|| GET_CODE (SET_DEST (x)) == MEM)
invalidate (SET_DEST (x), VOIDmode);
else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
|| GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
invalidate (XEXP (SET_DEST (x), 0), GET_MODE (SET_DEST (x)));
}
/* Find the end of INSN's basic block and return its range,
the total number of SETs in all the insns of the block, the last insn of the
block, and the branch path.
The branch path indicates which branches should be followed. If a non-zero
path size is specified, the block should be rescanned and a different set
of branches will be taken. The branch path is only used if
FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is non-zero.
DATA is a pointer to a struct cse_basic_block_data, defined below, that is
used to describe the block. It is filled in with the information about
the current block. The incoming structure's branch path, if any, is used
to construct the output branch path. */
void
cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks)
rtx insn;
struct cse_basic_block_data *data;
int follow_jumps;
int after_loop;
int skip_blocks;
{
rtx p = insn, q;
int nsets = 0;
int low_cuid = INSN_CUID (insn), high_cuid = INSN_CUID (insn);
rtx next = GET_RTX_CLASS (GET_CODE (insn)) == 'i' ? insn : next_real_insn (insn);
int path_size = data->path_size;
int path_entry = 0;
int i;
/* Update the previous branch path, if any. If the last branch was
previously TAKEN, mark it NOT_TAKEN. If it was previously NOT_TAKEN,
shorten the path by one and look at the previous branch. We know that
at least one branch must have been taken if PATH_SIZE is non-zero. */
while (path_size > 0)
{
if (data->path[path_size - 1].status != NOT_TAKEN)
{
data->path[path_size - 1].status = NOT_TAKEN;
break;
}
else
path_size--;
}
/* Scan to end of this basic block. */
while (p && GET_CODE (p) != CODE_LABEL)
{
/* Don't cse out the end of a loop. This makes a difference
only for the unusual loops that always execute at least once;
all other loops have labels there so we will stop in any case.
Cse'ing out the end of the loop is dangerous because it
might cause an invariant expression inside the loop
to be reused after the end of the loop. This would make it
hard to move the expression out of the loop in loop.c,
especially if it is one of several equivalent expressions
and loop.c would like to eliminate it.
If we are running after loop.c has finished, we can ignore
the NOTE_INSN_LOOP_END. */
if (! after_loop && GET_CODE (p) == NOTE
&& NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
break;
/* Don't cse over a call to setjmp; on some machines (eg vax)
the regs restored by the longjmp come from
a later time than the setjmp. */
if (GET_CODE (p) == NOTE
&& NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP)
break;
/* A PARALLEL can have lots of SETs in it,
especially if it is really an ASM_OPERANDS. */
if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& GET_CODE (PATTERN (p)) == PARALLEL)
nsets += XVECLEN (PATTERN (p), 0);
else if (GET_CODE (p) != NOTE)
nsets += 1;
/* Ignore insns made by CSE; they cannot affect the boundaries of
the basic block. */
if (INSN_UID (p) <= max_uid && INSN_CUID (p) > high_cuid)
high_cuid = INSN_CUID (p);
if (INSN_UID (p) <= max_uid && INSN_CUID (p) < low_cuid)
low_cuid = INSN_CUID (p);
/* See if this insn is in our branch path. If it is and we are to
take it, do so. */
if (path_entry < path_size && data->path[path_entry].branch == p)
{
if (data->path[path_entry].status != NOT_TAKEN)
p = JUMP_LABEL (p);
/* Point to next entry in path, if any. */
path_entry++;
}
/* If this is a conditional jump, we can follow it if -fcse-follow-jumps
was specified, we haven't reached our maximum path length, there are
insns following the target of the jump, this is the only use of the
jump label, and the target label is preceded by a BARRIER.
Alternatively, we can follow the jump if it branches around a
block of code and there are no other branches into the block.
In this case invalidate_skipped_block will be called to invalidate any
registers set in the block when following the jump. */
else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1
&& GET_CODE (p) == JUMP_INSN
&& GET_CODE (PATTERN (p)) == SET
&& GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE
&& JUMP_LABEL (p) != 0
&& LABEL_NUSES (JUMP_LABEL (p)) == 1
&& NEXT_INSN (JUMP_LABEL (p)) != 0)
{
for (q = PREV_INSN (JUMP_LABEL (p)); q; q = PREV_INSN (q))
if ((GET_CODE (q) != NOTE
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_SETJMP)
&& (GET_CODE (q) != CODE_LABEL || LABEL_NUSES (q) != 0))
break;
/* If we ran into a BARRIER, this code is an extension of the
basic block when the branch is taken. */
if (follow_jumps && q != 0 && GET_CODE (q) == BARRIER)
{
/* Don't allow ourself to keep walking around an
always-executed loop. */
if (next_real_insn (q) == next)
{
p = NEXT_INSN (p);
continue;
}
/* Similarly, don't put a branch in our path more than once. */
for (i = 0; i < path_entry; i++)
if (data->path[i].branch == p)
break;
if (i != path_entry)
break;
data->path[path_entry].branch = p;
data->path[path_entry++].status = TAKEN;
/* This branch now ends our path. It was possible that we
didn't see this branch the last time around (when the
insn in front of the target was a JUMP_INSN that was
turned into a no-op). */
path_size = path_entry;
p = JUMP_LABEL (p);
/* Mark block so we won't scan it again later. */
PUT_MODE (NEXT_INSN (p), QImode);
}
/* Detect a branch around a block of code. */
else if (skip_blocks && q != 0 && GET_CODE (q) != CODE_LABEL)
{
register rtx tmp;
if (next_real_insn (q) == next)
{
p = NEXT_INSN (p);
continue;
}
for (i = 0; i < path_entry; i++)
if (data->path[i].branch == p)
break;
if (i != path_entry)
break;
/* This is no_labels_between_p (p, q) with an added check for
reaching the end of a function (in case Q precedes P). */
for (tmp = NEXT_INSN (p); tmp && tmp != q; tmp = NEXT_INSN (tmp))
if (GET_CODE (tmp) == CODE_LABEL)
break;
if (tmp == q)
{
data->path[path_entry].branch = p;
data->path[path_entry++].status = AROUND;
path_size = path_entry;
p = JUMP_LABEL (p);
/* Mark block so we won't scan it again later. */
PUT_MODE (NEXT_INSN (p), QImode);
}
}
}
p = NEXT_INSN (p);
}
data->low_cuid = low_cuid;
data->high_cuid = high_cuid;
data->nsets = nsets;
data->last = p;
/* If all jumps in the path are not taken, set our path length to zero
so a rescan won't be done. */
for (i = path_size - 1; i >= 0; i--)
if (data->path[i].status != NOT_TAKEN)
break;
if (i == -1)
data->path_size = 0;
else
data->path_size = path_size;
/* End the current branch path. */
data->path[path_size].branch = 0;
}
/* Perform cse on the instructions of a function.
F is the first instruction.
NREGS is one plus the highest pseudo-reg number used in the instruction.
AFTER_LOOP is 1 if this is the cse call done after loop optimization
(only if -frerun-cse-after-loop).
Returns 1 if jump_optimize should be redone due to simplifications
in conditional jump instructions. */
int
cse_main (f, nregs, after_loop, file)
rtx f;
int nregs;
int after_loop;
FILE *file;
{
struct cse_basic_block_data val;
register rtx insn = f;
register int i;
cse_jumps_altered = 0;
recorded_label_ref = 0;
constant_pool_entries_cost = 0;
val.path_size = 0;
init_recog ();
init_alias_analysis ();
max_reg = nregs;
max_insn_uid = get_max_uid ();
reg_next_eqv = (int *) alloca (nregs * sizeof (int));
reg_prev_eqv = (int *) alloca (nregs * sizeof (int));
#ifdef LOAD_EXTEND_OP
/* Allocate scratch rtl here. cse_insn will fill in the memory reference
and change the code and mode as appropriate. */
memory_extend_rtx = gen_rtx_ZERO_EXTEND (VOIDmode, NULL_RTX);
#endif
/* Discard all the free elements of the previous function
since they are allocated in the temporarily obstack. */
bzero ((char *) table, sizeof table);
free_element_chain = 0;
n_elements_made = 0;
/* Find the largest uid. */
max_uid = get_max_uid ();
uid_cuid = (int *) alloca ((max_uid + 1) * sizeof (int));
bzero ((char *) uid_cuid, (max_uid + 1) * sizeof (int));
/* Compute the mapping from uids to cuids.
CUIDs are numbers assigned to insns, like uids,
except that cuids increase monotonically through the code.
Don't assign cuids to line-number NOTEs, so that the distance in cuids
between two insns is not affected by -g. */
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) < 0)
INSN_CUID (insn) = ++i;
else
/* Give a line number note the same cuid as preceding insn. */
INSN_CUID (insn) = i;
}
/* Initialize which registers are clobbered by calls. */
CLEAR_HARD_REG_SET (regs_invalidated_by_call);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if ((call_used_regs[i]
/* Used to check !fixed_regs[i] here, but that isn't safe;
fixed regs are still call-clobbered, and sched can get
confused if they can "live across calls".
The frame pointer is always preserved across calls. The arg
pointer is if it is fixed. The stack pointer usually is, unless
RETURN_POPS_ARGS, in which case an explicit CLOBBER
will be present. If we are generating PIC code, the PIC offset
table register is preserved across calls. */
&& i != STACK_POINTER_REGNUM
&& i != FRAME_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& i != HARD_FRAME_POINTER_REGNUM
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
#endif
#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
&& ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
#endif
)
|| global_regs[i])
SET_HARD_REG_BIT (regs_invalidated_by_call, i);
/* Loop over basic blocks.
Compute the maximum number of qty's needed for each basic block
(which is 2 for each SET). */
insn = f;
while (insn)
{
cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps, after_loop,
flag_cse_skip_blocks);
/* If this basic block was already processed or has no sets, skip it. */
if (val.nsets == 0 || GET_MODE (insn) == QImode)
{
PUT_MODE (insn, VOIDmode);
insn = (val.last ? NEXT_INSN (val.last) : 0);
val.path_size = 0;
continue;
}
cse_basic_block_start = val.low_cuid;
cse_basic_block_end = val.high_cuid;
max_qty = val.nsets * 2;
if (file)
fnotice (file, ";; Processing block from %d to %d, %d sets.\n",
INSN_UID (insn), val.last ? INSN_UID (val.last) : 0,
val.nsets);
/* Make MAX_QTY bigger to give us room to optimize
past the end of this basic block, if that should prove useful. */
if (max_qty < 500)
max_qty = 500;
max_qty += max_reg;
/* If this basic block is being extended by following certain jumps,
(see `cse_end_of_basic_block'), we reprocess the code from the start.
Otherwise, we start after this basic block. */
if (val.path_size > 0)
cse_basic_block (insn, val.last, val.path, 0);
else
{
int old_cse_jumps_altered = cse_jumps_altered;
rtx temp;
/* When cse changes a conditional jump to an unconditional
jump, we want to reprocess the block, since it will give
us a new branch path to investigate. */
cse_jumps_altered = 0;
temp = cse_basic_block (insn, val.last, val.path, ! after_loop);
if (cse_jumps_altered == 0
|| (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
insn = temp;
cse_jumps_altered |= old_cse_jumps_altered;
}
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
/* Tell refers_to_mem_p that qty_const info is not available. */
qty_const = 0;
if (max_elements_made < n_elements_made)
max_elements_made = n_elements_made;
return cse_jumps_altered || recorded_label_ref;
}
/* Process a single basic block. FROM and TO and the limits of the basic
block. NEXT_BRANCH points to the branch path when following jumps or
a null path when not following jumps.
AROUND_LOOP is non-zero if we are to try to cse around to the start of a
loop. This is true when we are being called for the last time on a
block and this CSE pass is before loop.c. */
static rtx
cse_basic_block (from, to, next_branch, around_loop)
register rtx from, to;
struct branch_path *next_branch;
int around_loop;
{
register rtx insn;
int to_usage = 0;
rtx libcall_insn = NULL_RTX;
int num_insns = 0;
/* Each of these arrays is undefined before max_reg, so only allocate
the space actually needed and adjust the start below. */
qty_first_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int));
qty_last_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int));
qty_mode= (enum machine_mode *) alloca ((max_qty - max_reg) * sizeof (enum machine_mode));
qty_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
qty_const_insn = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
qty_comparison_code
= (enum rtx_code *) alloca ((max_qty - max_reg) * sizeof (enum rtx_code));
qty_comparison_qty = (int *) alloca ((max_qty - max_reg) * sizeof (int));
qty_comparison_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
qty_first_reg -= max_reg;
qty_last_reg -= max_reg;
qty_mode -= max_reg;
qty_const -= max_reg;
qty_const_insn -= max_reg;
qty_comparison_code -= max_reg;
qty_comparison_qty -= max_reg;
qty_comparison_const -= max_reg;
new_basic_block ();
/* TO might be a label. If so, protect it from being deleted. */
if (to != 0 && GET_CODE (to) == CODE_LABEL)
++LABEL_NUSES (to);
for (insn = from; insn != to; insn = NEXT_INSN (insn))
{
register enum rtx_code code = GET_CODE (insn);
/* If we have processed 1,000 insns, flush the hash table to
avoid extreme quadratic behavior. We must not include NOTEs
in the count since there may be more or them when generating
debugging information. If we clear the table at different
times, code generated with -g -O might be different than code
generated with -O but not -g.
??? This is a real kludge and needs to be done some other way.
Perhaps for 2.9. */
if (code != NOTE && num_insns++ > 1000)
{
flush_hash_table ();
num_insns = 0;
}
/* See if this is a branch that is part of the path. If so, and it is
to be taken, do so. */
if (next_branch->branch == insn)
{
enum taken status = next_branch++->status;
if (status != NOT_TAKEN)
{
if (status == TAKEN)
record_jump_equiv (insn, 1);
else
invalidate_skipped_block (NEXT_INSN (insn));
/* Set the last insn as the jump insn; it doesn't affect cc0.
Then follow this branch. */
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
#endif
prev_insn = insn;
insn = JUMP_LABEL (insn);
continue;
}
}
if (GET_MODE (insn) == QImode)
PUT_MODE (insn, VOIDmode);
if (GET_RTX_CLASS (code) == 'i')
{
rtx p;
/* Process notes first so we have all notes in canonical forms when
looking for duplicate operations. */
if (REG_NOTES (insn))
REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), NULL_RTX);
/* Track when we are inside in LIBCALL block. Inside such a block,
we do not want to record destinations. The last insn of a
LIBCALL block is not considered to be part of the block, since
its destination is the result of the block and hence should be
recorded. */
if ((p = find_reg_note (insn, REG_LIBCALL, NULL_RTX)))
libcall_insn = XEXP (p, 0);
else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
libcall_insn = NULL_RTX;
cse_insn (insn, libcall_insn);
}
/* If INSN is now an unconditional jump, skip to the end of our
basic block by pretending that we just did the last insn in the
basic block. If we are jumping to the end of our block, show
that we can have one usage of TO. */
if (simplejump_p (insn))
{
if (to == 0)
return 0;
if (JUMP_LABEL (insn) == to)
to_usage = 1;
/* Maybe TO was deleted because the jump is unconditional.
If so, there is nothing left in this basic block. */
/* ??? Perhaps it would be smarter to set TO
to whatever follows this insn,
and pretend the basic block had always ended here. */
if (INSN_DELETED_P (to))
break;
insn = PREV_INSN (to);
}
/* See if it is ok to keep on going past the label
which used to end our basic block. Remember that we incremented
the count of that label, so we decrement it here. If we made
a jump unconditional, TO_USAGE will be one; in that case, we don't
want to count the use in that jump. */
if (to != 0 && NEXT_INSN (insn) == to
&& GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage)
{
struct cse_basic_block_data val;
rtx prev;
insn = NEXT_INSN (to);
if (LABEL_NUSES (to) == 0)
insn = delete_insn (to);
/* If TO was the last insn in the function, we are done. */
if (insn == 0)
return 0;
/* If TO was preceded by a BARRIER we are done with this block
because it has no continuation. */
prev = prev_nonnote_insn (to);
if (prev && GET_CODE (prev) == BARRIER)
return insn;
/* Find the end of the following block. Note that we won't be
following branches in this case. */
to_usage = 0;
val.path_size = 0;
cse_end_of_basic_block (insn, &val, 0, 0, 0);
/* If the tables we allocated have enough space left
to handle all the SETs in the next basic block,
continue through it. Otherwise, return,
and that block will be scanned individually. */
if (val.nsets * 2 + next_qty > max_qty)
break;
cse_basic_block_start = val.low_cuid;
cse_basic_block_end = val.high_cuid;
to = val.last;
/* Prevent TO from being deleted if it is a label. */
if (to != 0 && GET_CODE (to) == CODE_LABEL)
++LABEL_NUSES (to);
/* Back up so we process the first insn in the extension. */
insn = PREV_INSN (insn);
}
}
if (next_qty > max_qty)
abort ();
/* If we are running before loop.c, we stopped on a NOTE_INSN_LOOP_END, and
the previous insn is the only insn that branches to the head of a loop,
we can cse into the loop. Don't do this if we changed the jump
structure of a loop unless we aren't going to be following jumps. */
if ((cse_jumps_altered == 0
|| (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
&& around_loop && to != 0
&& GET_CODE (to) == NOTE && NOTE_LINE_NUMBER (to) == NOTE_INSN_LOOP_END
&& GET_CODE (PREV_INSN (to)) == JUMP_INSN
&& JUMP_LABEL (PREV_INSN (to)) != 0
&& LABEL_NUSES (JUMP_LABEL (PREV_INSN (to))) == 1)
cse_around_loop (JUMP_LABEL (PREV_INSN (to)));
return to ? NEXT_INSN (to) : 0;
}
/* Count the number of times registers are used (not set) in X.
COUNTS is an array in which we accumulate the count, INCR is how much
we count each register usage.
Don't count a usage of DEST, which is the SET_DEST of a SET which
contains X in its SET_SRC. This is because such a SET does not
modify the liveness of DEST. */
static void
count_reg_usage (x, counts, dest, incr)
rtx x;
int *counts;
rtx dest;
int incr;
{
enum rtx_code code;
char *fmt;
int i, j;
if (x == 0)
return;
switch (code = GET_CODE (x))
{
case REG:
if (x != dest)
counts[REGNO (x)] += incr;
return;
case PC:
case CC0:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
return;
case CLOBBER:
/* If we are clobbering a MEM, mark any registers inside the address
as being used. */
if (GET_CODE (XEXP (x, 0)) == MEM)
count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
return;
case SET:
/* Unless we are setting a REG, count everything in SET_DEST. */
if (GET_CODE (SET_DEST (x)) != REG)
count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
/* If SRC has side-effects, then we can't delete this insn, so the
usage of SET_DEST inside SRC counts.
??? Strictly-speaking, we might be preserving this insn
because some other SET has side-effects, but that's hard
to do and can't happen now. */
count_reg_usage (SET_SRC (x), counts,
side_effects_p (SET_SRC (x)) ? NULL_RTX : SET_DEST (x),
incr);
return;
case CALL_INSN:
count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, NULL_RTX, incr);
/* ... falls through ... */
case INSN:
case JUMP_INSN:
count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
/* Things used in a REG_EQUAL note aren't dead since loop may try to
use them. */
count_reg_usage (REG_NOTES (x), counts, NULL_RTX, incr);
return;
case EXPR_LIST:
case INSN_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL
|| (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE))
count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
return;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
count_reg_usage (XEXP (x, i), counts, dest, incr);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
}
}
/* Scan all the insns and delete any that are dead; i.e., they store a register
that is never used or they copy a register to itself.
This is used to remove insns made obviously dead by cse, loop or other
optimizations. It improves the heuristics in loop since it won't try to
move dead invariants out of loops or make givs for dead quantities. The
remaining passes of the compilation are also sped up. */
void
delete_trivially_dead_insns (insns, nreg)
rtx insns;
int nreg;
{
int *counts = (int *) alloca (nreg * sizeof (int));
rtx insn, prev;
#ifdef HAVE_cc0
rtx tem;
#endif
int i;
int in_libcall = 0, dead_libcall = 0;
/* First count the number of times each register is used. */
bzero ((char *) counts, sizeof (int) * nreg);
for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
count_reg_usage (insn, counts, NULL_RTX, 1);
/* Go from the last insn to the first and delete insns that only set unused
registers or copy a register to itself. As we delete an insn, remove
usage counts for registers it uses. */
for (insn = prev_real_insn (get_last_insn ()); insn; insn = prev)
{
int live_insn = 0;
rtx note;
prev = prev_real_insn (insn);
/* Don't delete any insns that are part of a libcall block unless
we can delete the whole libcall block.
Flow or loop might get confused if we did that. Remember
that we are scanning backwards. */
if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
in_libcall = 1;
live_insn = 1;
dead_libcall = 0;
/* See if there's a REG_EQUAL note on this insn and try to
replace the source with the REG_EQUAL expression.
We assume that insns with REG_RETVALs can only be reg->reg
copies at this point. */
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (note)
{
rtx set = single_set (insn);
if (set
&& validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0))
{
remove_note (insn,
find_reg_note (insn, REG_RETVAL, NULL_RTX));
dead_libcall = 1;
}
}
}
else if (in_libcall)
live_insn = ! dead_libcall;
else if (GET_CODE (PATTERN (insn)) == SET)
{
if (GET_CODE (SET_DEST (PATTERN (insn))) == REG
&& SET_DEST (PATTERN (insn)) == SET_SRC (PATTERN (insn)))
;
#ifdef HAVE_cc0
else if (GET_CODE (SET_DEST (PATTERN (insn))) == CC0
&& ! side_effects_p (SET_SRC (PATTERN (insn)))
&& ((tem = next_nonnote_insn (insn)) == 0
|| GET_RTX_CLASS (GET_CODE (tem)) != 'i'
|| ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
;
#endif
else if (GET_CODE (SET_DEST (PATTERN (insn))) != REG
|| REGNO (SET_DEST (PATTERN (insn))) < FIRST_PSEUDO_REGISTER
|| counts[REGNO (SET_DEST (PATTERN (insn)))] != 0
|| side_effects_p (SET_SRC (PATTERN (insn))))
live_insn = 1;
}
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
{
rtx elt = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (elt) == SET)
{
if (GET_CODE (SET_DEST (elt)) == REG
&& SET_DEST (elt) == SET_SRC (elt))
;
#ifdef HAVE_cc0
else if (GET_CODE (SET_DEST (elt)) == CC0
&& ! side_effects_p (SET_SRC (elt))
&& ((tem = next_nonnote_insn (insn)) == 0
|| GET_RTX_CLASS (GET_CODE (tem)) != 'i'
|| ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
;
#endif
else if (GET_CODE (SET_DEST (elt)) != REG
|| REGNO (SET_DEST (elt)) < FIRST_PSEUDO_REGISTER
|| counts[REGNO (SET_DEST (elt))] != 0
|| side_effects_p (SET_SRC (elt)))
live_insn = 1;
}
else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
live_insn = 1;
}
else
live_insn = 1;
/* If this is a dead insn, delete it and show registers in it aren't
being used. */
if (! live_insn)
{
count_reg_usage (insn, counts, NULL_RTX, -1);
delete_insn (insn);
}
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
{
in_libcall = 0;
dead_libcall = 0;
}
}
}
Index: head/contrib/gcc/f/ChangeLog
===================================================================
--- head/contrib/gcc/f/ChangeLog (revision 52750)
+++ head/contrib/gcc/f/ChangeLog (revision 52751)
@@ -1,1266 +1,1270 @@
+Sun Oct 24 23:54:10 PDT 1999 Jeff Law (law@cygnus.com)
+
+ * gcc-2.95.2 Released.
+
Mon Aug 16 01:29:24 PDT 1999 Jeff Law (law@cygnus.com)
* gcc-2.95.1 Released.
Thu Aug 5 02:40:42 1999 Jeffrey A Law (law@cygnus.com)
* g77spec.c: Update URLS and mail addresses.
* root.texi: Update URLS and mail addresses.
Wed Jul 28 21:39:31 PDT 1999 Jeff Law (law@cygnus.com)
* gcc-2.95 Released.
* version.c: No longer a prerelease.
Sat Jul 17 21:57:07 1999 Jeffrey A Law (law@cygnus.com)
* root.texi: Clear DEVEOPMENT per Craig's instructions.
1999-07-17 Alexandre Oliva <oliva@dcc.unicamp.br>
* root.texi: Update e-mail addresses to gcc.gnu.org.
* g77spec.c (lang_specific_driver): Updated URL with bug reporting
instructions to gcc.gnu.org. Removed e-mail address.
Sat Jul 17 11:28:43 1999 Craig Burley <craig@jcb-sc.com>
* root.texi, g77install.texi: Switchover to GCC terminology.
Also, FSF-G77 had been mistakenly set at some point.
Thu Jul 8 15:38:50 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Describe DATE intrinsic fix.
Tue Jun 29 10:10:25 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi: Undo Friday's change, as there's now a fix
available from netlib.
* news.texi: Document the fix.
Mon Jun 28 10:43:11 1999 Craig Burley <craig@jcb-sc.com>
* com.c (ffecom_prepare_expr_): A COMPLEX intrinsic needs
a temp even if -fno-f2c.
* version.c: Bump version.
Fri Jun 25 11:06:32 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi: Describe K(5)=10*3 NAMELIST-read bug.
Mon Jun 21 12:40:17 1999 Gerald Pfeifer <pfeifer@dbai.tuwien.ac.at>
* g77.texi: Update links.
Wed Jun 16 11:43:02 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Mention BACKSPACE fix to libg2c.
Mon Jun 7 08:42:40 1999 Craig Burley <craig@jcb-sc.com>
* Make-lang.in: Any target using libsubdir must depend
on installdirs.
Sat Jun 5 23:50:36 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: Describe a few more missing features people
have emailed me about.
Sat Jun 5 17:03:23 1999 Craig Burley <craig@jcb-sc.com>
From Dave Love to egcs-patches on 20 May 1999 17:38:38 +0100:
* g77.texi: Clean up fossil text vis-a-vis Intel CPUs.
Fri Jun 4 13:56:56 1999 Craig Burley <craig@jcb-sc.com>
* Make-lang.in: Use libsubdir, not prefix, to store
temporary lang-f77 `flag' file.
Fri Jun 4 10:26:04 1999 Craig Burley <craig@jcb-sc.com>
* news.texi (News): Mention GCC 2.95 in favor of EGCS 1.2.
Mention that libg2c is multilibbed.
Fri Jun 4 10:09:50 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi (Missing Features): Add `Better Warnings'
item.
Fri May 28 16:51:41 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: Fix thinko.
Wed May 26 14:43:27 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Document Tue May 18 03:52:04 1999 patch.
Fix a grammo.
Wed May 26 14:25:07 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi, news.texi, root.texi, version.c: Start renaming
EGCS 1.2 to GCC 2.95, and start using 0.5.25 to designate
the version of g77 within GCC 2.95.
Wed May 26 11:45:21 1999 Craig Burley <craig@jcb-sc.com>
Rename -fsubscript-check to -fbounds-check and
-ff2c-subscript-check to -ffortran-bounds-check:
* g77.texi: Rename options in docs, clarify usage.
* lang-options.h: Rename options, clarify doclets.
* news.texi: Rename options, don't bother with fortran-specific
option.
* top.c (ffe_decode_option): Rename recognized strings.
Tue May 25 18:21:09 1999 Craig Burley <craig@jcb-sc.com>
* com.c (FFECOM_FASTER_ARRAY_REFS): Delete this vestige,
now that -fflatten-arrays exists.
Tue May 25 17:48:34 1999 Craig Burley <craig@jcb-sc.com>
Fix 19990525-0.f:
* com.c (ffecom_arg_ptr_to_expr): Strip off parens around
CHARACTER expression.
(ffecom_prepare_expr_): Ditto.
Tue May 18 03:52:04 1999 Craig Burley <craig@jcb-sc.com>
Support use of back end's improved open-coding of complex divide:
* com.c (ffecom_tree_divide_): Use RDIV_EXPR for complex divide,
instead of run-time call to [cz]_div, if `-Os' option specified.
(lang_init_options): Tell back end we want support for wide range
of inputs to complex divide.
* Bump version.
Tue May 18 00:21:34 1999 Zack Weinberg <zack@rabi.phys.columbia.edu>
* lang-specs.h: Define __GNUC__ and __GNUC_MINOR__ only if -no-gcc
was not given.
Thu May 13 12:23:20 1999 Craig Burley <craig@jcb-sc.com>
Fix INTEGER*8 subscripts in array references:
* com.c (ffecom_subscript_check_): Convert low, high, and
element as necessary to make comparison work.
(ffecom_arrayref_): Do more of the work.
Properly handle subscript expr that's wider than int,
if pointers are wider than int.
(ffecom_expr_): Leave more work to ffecom_arrayref_.
(ffecom_init_0): Record sizes of pointers and ints for
convenience.
Use set_sizetype etc. as done by gcc front end.
(ffecom_ptr_to_expr): Leave more work to ffecom_arrayref_.
* expr.c (ffeexpr_finished_): Don't convert INTEGER subscript
expressions in run-time contexts.
(ffeexpr_token_elements_, ffeexpr_token_substring_1_): Cope with
non-default INTEGER subscript expressions.
* news.texi: Announce.
Finish accepting -fflatten-arrays option:
* com.c (ffecom_arrayref_): Flatten references if requested.
* g77.texi: Describe.
* lang-options.h: Allow.
* news.texi: Announce.
* top.c, top.h: Recognize.
* version.c: Bump version.
Wed May 12 07:30:05 1999 Craig Burley <craig@jcb-sc.com>
* com.c (lang_init_options): Disable back end's maintenance
of errno.
* news.texi: Document dropping of errno.
1999-05-10 18:21 -0400 Zack Weinberg <zack@rabi.phys.columbia.edu>
* lang-specs.h: Pass -$ to the preprocessor.
Mon May 10 18:14:28 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: Fix various @xref's per proper style.
Go ahead and use nested braces in @xref's, with care.
* g77install.texi: Fix @xref per proper style.
Mon May 10 17:38:39 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Doc upgrade to netlib libf2c as of today.
Sun May 9 18:52:13 1999 Hans-Peter Nilsson <hp@bitrange.com>
* f/g77spec.c (lang_specific_driver): Correct bug-report address
and point to the FAQ.
Thu May 6 12:40:21 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi (Arbitrary Concatenation): Put this under
"Missing Features" instead of "Projects".
(Internals Documentation): Point to new "Front End" chapter.
Thu May 6 08:23:52 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi, news.texi: Automatic arrays reportedly working
on HP-UX systems.
Thu May 6 08:19:31 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi (Advantages Over f2c): Expand on this topic.
Mon May 3 19:41:48 1999 Craig Burley <craig@jcb-sc.com>
* com.c (ffecom_expr_intrinsic_): Fix test of CTIME_subr.
Mon May 3 18:11:48 1999 Craig Burley <craig@jcb-sc.com>
Reverse order of two arguments to CTIME_subr, DTIME_subr,
ETIME_subr, and TTYNAM_subr:
* com.c (ffecom_expr_intrinsic_): Reverse the arguments.
While at it, set TREE_SIDE_EFFECTS for CTIME_subr and
TTYNAM_subr.
* intdoc.in: Document the new calling sequences.
* intrin.def: Reverse the arguments.
* news.texi: Document the fact that they changed.
* version.c: Bump version.
Mon May 3 11:28:14 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Doc upgrade to netlib libf2c as of today.
Sun May 2 17:04:28 1999 Craig Burley <craig@jcb-sc.com>
* version.c: Bump version.
Sun May 2 16:53:01 1999 Craig Burley <craig@jcb-sc.com>
Fix compile/19990502-1.f:
* ste.c (ffeste_R819B): Don't overwrite tree for temp
variable when expanding the assignment into it.
Sun Apr 25 20:55:10 1999 Craig Burley <craig@jcb-sc.com>
Fix 19990325-0.f and 19990325-1.f:
* com.c (ffecom_possible_partial_overlap_): New function.
(ffecom_expand_let_stmt): Use it to determine whether to assign
to a COMPLEX operand through a temp.
* news.texi: Document fix.
* version.c: Bump version.
Sat Apr 24 12:19:53 1999 Craig Burley <craig@jcb-sc.com>
* expr.c (ffeexpr_finished_): Convert DATA implied-do
start/end/incr expressions to default INTEGER.
Fix some broken conditionals.
Clean up some code in the region.
* news.c: Document the fix.
* version.c: Bump version.
Fri Apr 23 02:08:32 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi (Compiler Prototypes): Replace "missing" subscript-
checking option with something else.
Fri Apr 23 01:48:28 1999 Craig Burley <craig@jcb-sc.com>
Support new -fsubscript-check and -ff2c-subscript-check options:
* com-rt.def (FFECOM_gfrtRANGE): Describe s_rnge, in libf2c/libF77.
* com.c (ffecom_subscript_check_, ffecom_arrayref_): New functions.
(ffecom_char_args_x_): Use new ffecom_arrayref_ function for
FFEBLD_opARRAYREF case.
Compute character name, array type, and use new
ffecom_subscript_check_ function for FFEBLD_opSUBSTRING case.
(ffecom_expr_): Use new ffecom_arrayref_ function.
(ffecom_ptr_to_expr): Use new ffecom_arrayref_ function.
* g77.texi, news.texi: Document new options.
* top.c, top.h: Support new options.
* news.texi: Fix up some items to not be in "User-Visible Changes".
* ste.c (ffeste_R819B): Fix type for loop variable, to avoid
warnings.
* version.c: Bump version.
Tue Apr 20 01:38:57 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi, news.texi: Clarify -malign-double situation.
Tue Apr 20 01:15:25 1999 Craig Burley <craig@jcb-sc.com>
* stb.c (ffestb_R5282_): Convert DATA repeat count
to default INTEGER, to avoid problems downstream.
* version.c: Bump version.
Mon Apr 19 21:36:48 1999 Craig Burley <craig@jcb-sc.com>
* ste.c (ffeste_R819B): Start the loop before expanding
the termination expression.
* version.c: Bump version.
Sun Apr 18 21:53:58 1999 Craig Burley <craig@jcb-sc.com>
* com.c (ffecom_sym_transform_): COMMON and EQUIVALENCE
variables have constant addresses (EQUIVALENCE only if
containing aggregate is static).
Sat Apr 17 16:55:59 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi, ffe.texi, g77.texi, g77install.texi, news.texi:
Clean up @code{} vs. @samp{}.
Clean up dashes (`--') vs. @minus{} vs. `---'.
* ffe.texi: Add copyright header.
* g77.texi, lang-options.h, news.texi, top.c (ffe_decode_option):
Remove support for -fugly option.
Clarify that -fugly-logint is needed instead of -fugly
to work around using .EQ./.NE. on LOGICAL operands.
Explain more about why -fugly-logint is bad juju.
* g77.texi (Missing Features): Describe READONLY as a missing
feature. Describe AUTOMATIC better.
* news.texi: Mention libf2c upgrade.
Sat Apr 17 14:05:53 1999 Craig Burley <craig@jcb-sc.com>
Make a place for front-end internals documentation:
* Make-lang.in (f/g77.info, f/g77.dvi): Depend on f/ffe.texi.
* ffe.texi: New file, containing docs on front-end internals.
* g77.texi: New chapter for, and inclusion of, ffe.texi.
* g77.texi: Fix an index entry.
Sat Apr 17 13:53:43 1999 Craig Burley <craig@jcb-sc.com>
Rewrite to use block/scope structure of GBE and to ensure
variables (especially those going on stack/reg) are declared
before executable code generated:
* bld.c (ffebld_new_item, ffebld_new_one, ffebld_new_two):
Support new hooks.
* bld.h (ffebld_item_hook, ffebld_item_set_hook,
ffebld_nonter_hook, ffebld_nonter_set_hook): Ditto.
* bld.h (ffebld_basictype, ffebld_kind, ffebld_kindtype,
ffebld_rank, ffebld_where): New convenience macros (used
by rest of this patch).
* com.c, com.h (ffecom_push_calltemps, ffecom_pop_calltemps,
ffecom_push_tempvar, ffecom_pop_tempvar): Remove temp-var-
handling mechanism.
* com.c (ffecom_call_, ffecom_call_binop_, ffecom_tree_divide_,
ffecom_call_gfrt): Support passing hooks for temp-var info.
(ffecom_expr_power_integer_): Takes opPOWER expression, instead
of its left and right operands, so it can get at the hook.
(ffecom_prepare_let_char_, ffecom_prepare_arg_ptr_to_expr,
ffecom_prepare_end, ffecom_prepare_expr_, ffecom_prepare_expr_rw,
ffecom_prepare_expr_w, ffecom_prepare_return_expr,
ffecom_prepare_ptr_to_expr): New functions supporting expression
pre-scanning.
(bison_rule_compstmt_): Return the tree, as in the CFE.
(delete_block): New function, from CFE.
(kept_level_p): New function, from CFE, modified.
(ffecom_start_compstmt, ffecom_end_compstmt): New functions,
replacing ffecom_start_compstmt_ and ffecom_end_compstmt_ macros,
and they do real work.
(struct binding_level): Add prep_state member. Initialize to 0.
(ffecom_get_invented_identifier): Now takes either or both a
string and an integer, using -1 to denote no integer.
(ffecom_do_entry_): Disallow temp-var generation via expressions
in body of function, since the exprs aren't prescanned.
(ffecom_expr_rw): Now takes destination tree.
(ffecom_expr_w): New function, now used in some places
ffecom_expr_rw had been used.
(ffecom_expr_intrinsic_): Move huge f2c-related comment to bottom
of source file, to avoid annoying problems editing com.c using
Emacs C-mode.
(ffecom_expr_power_integer_): Make a temp var for division, if
necessary.
Handle expanded statement expression as does CFE.
(ffecom_start_progunit_): Disallow temp-var generation in body
of function, since expressions are not prescanned at this level.
(ffecom_sym_transform_): Transform ASSIGN variables as well,
so these are all transformed up front, before code-generation
begins.
(ffecom_arg_ptr_to_const_expr, ffecom_const_expr,
ffecom_ptr_to_const_expr): New functions to transform expressions
only if the results will surely be constants.
(ffecom_arg_ptr_to_expr): Precompute size, for convenience
obtaining temp vars.
(ffecom_expand_let_stmt): Guess at usability of destination
pre-expansion, to provide better prescan preparation (fewer
spurious temp vars).
(ffecom_init_0): Disallow temp-var generation in global scope.
(ffecom_type_expr): New function, returns just the type tree
for the expression.
(start_function): Disallow temp-var generation in parm scope.
(incomplete_type_error): Fix introductory comment.
(poplevel): Update (somewhat) from CFE.
(pushlevel): Update (somewhat) from CFE.
* stc.c (ffestc_R838): Mark ASSIGNed variable as so.
* std.c (ffestd_stmt_pass_, ffestd_R803, ffestd_R804, ffestd_R805,
ffestd_R806): Remember and pass through the ffestw block info
for these (IFTHEN, ELSEIF, ELSE, and ENDIF) statements.
* ste.c (ffeste_end_iterdo_): Now takes ffestw block argument.
(ffeste_io_inlist_): Add prototype.
(ffeste_f2c_*): Macros rewritten, new ones added.
(ffeste_start_block_, ffeste_end_block_, ffeste_start_stmt_,
ffeste_end_stmt_): New macros/functions, depending on whether
checking is enabled, to keep track of symmetry of other ste.c code.
(ffeste_begin_iterdo_, ffeste_end_iterdo_, ffeste_io_impdo_,
ffeste_io_dofio_, ffeste_io_dolio_, ffeste_io_douio_,
ffeste_io_ialist_, ffeste_io_cilist_, ffeste_io_cllist_,
ffeste_icilist_, ffeste_io_inlist_, ffeste_io_olist_,
ffeste_subr_beru_, ffeste_do, ffeste_end_R807, ffeste_R737A,
ffeste_R803, ffeste_R804, ffeste_R805, ffeste_R806, ffeste_R807,
ffeste_R809, ffeste_R810, ffeste_R811, ffeste_R819A, ffeste_R819B,
ffeste_R837, ffeste_R838, ffeste_R839, ffeste_R840, ffeste_R904,
ffeste_R907, ffeste_R909_start, ffeste_R909_item, ffeste_R909_finish,
ffeste_R910_start, ffeste_R910_item, ffeste_R910_finish,
ffeste_R911_start, ffeste_R911_item, ffeste_R911_finish,
ffeste_R923A, ffeste_R1212, ffeste_R1227): Prescan/prepare
all pertinent expressions, update to new com.c interface, etc.
(ffeste_io_impdo_): Relocate.
(ffeste_R834, ffeste_R835, ffeste_R836, ffeste_R1226): Don't
bother calling clear_momentary, nothing was generated.
(ffeste_R842, ffeste_R843): Update to new com.c interface.
(ffeste_R1226): Don't try to stuff error_mark_node's DECL_INITIAL.
(ffeste_terminate_2): When checking enabled, make sure all blocks
and statements have been ended.
* ste.h (ffeste_R803, ffeste_R804, ffeste_R805, ffeste_R806):
These now take ffestw block argument.
(ffeste_terminate_2): When checking enabled, it's a function, not
a macro.
* stw.h (struct _ffestw_): New variable for IFTHEN.
(ffestw_ifthen_fake_else, ffestw_set_ifthen_fake_else): New
accessor macros.
* symbol.c, symbol.h: Support new ASSIGN'ed-to info.
* com.c: Clean up commentary per GNU coding standards.
* bld.h (ffebld_size, ffebld_size_known): Canonize.
* version.c: Bump version.
Sun Apr 11 21:33:33 1999 Mumit Khan <khan@xraylith.wisc.edu>
* g77spec.c (lang_specific_driver): Check whether MATH_LIBRARY is
null to decide whether to use it.
Wed Apr 7 09:47:09 1999 Kaveh R. Ghazi <ghazi@snafu.rutgers.edu>
* ansify.c (die): Specify void argument.
* intdoc.c (family_name, dumpgen, dumpspec, dumpimp,
argument_info_ptr, argument_info_string, argument_name_ptr,
argument_name_string, elaborate_if_complex,
elaborate_if_maybe_complex, elaborate_if_real, print_type_string):
Const-ify a char*.
(main): Mark parameter `argv' with ATTRIBUTE_UNUSED.
(_ffeintrin_name_, _ffeintrin_gen_, _ffeintrin_spec_,
_ffeintrin_imp_, cc_pair, descriptions, summaries): Const-ify a char*.
Mon Apr 5 11:57:54 1999 Donn Terry (donn@interix.com)
* Make-lang.in (HOST_CFLAGS): compute dynamically.
Mon Apr 5 02:11:23 1999 Craig Burley <craig@jcb-sc.com>
Fix bugs exposed by configuring with --enable-checking:
* com.c (ffecom_do_entry_, ffecom_expr_, ffecom_arg_ptr_to_expr,
ffecom_list_expr, ffecom_list_ptr_to_expr, finish_function,
pop_f_function_context, store_parm_decls, poplevel): Handle
error_mark_node properly.
* ste.c (ffeste_begin_iterdo_, ffeste_end_iterdo_): Ditto.
* version.c: Bump version.
Sat Apr 3 23:57:56 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: Fix up docs for -fset-g77-defaults, and
describe how internal consistency checking now happens.
(Should have been done for EGCS version 1.1.)
Sat Apr 3 23:29:33 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi, g77.texi, lang-options.h, news.texi, top.c:
Make -fno-emulate-complex the default, as COMPLEX support
in the back end is now believed to be working.
* version.c: Bump version.
Fri Apr 2 13:33:16 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: -malign-double now works.
Give URL for alignment-testing package.
* news.texi: -malign-double now works.
Fri Apr 2 12:49:12 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi (Funding GNU Fortran): Dude's got a web page.
* root.texi: Ditto.
Tue Mar 30 12:04:11 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* sta.c (ffesta_ffebad_1sp, ffesta_ffebad_1st, ffesta_ffebad_2st):
Const-ify a char*.
* sta.h (ffesta_ffebad_1sp, ffesta_ffebad_1st, ffesta_ffebad_2st):
Likewise.
* stb.c (ffestb_local_u_): Likewise.
(ffestb_do, ffestb_dowhile, ffestb_else, ffestb_elsexyz,
ffestb_else3_, ffestb_endxyz, ffestb_goto, ffestb_let,
ffestb_type, ffestb_type1_, ffestb_varlist, ffestb_R423B,
ffestb_R522, ffestb_R528, ffestb_R542, ffestb_R834, ffestb_R835,
ffestb_R838, ffestb_R841, ffestb_R1102, ffestb_blockdata,
ffestb_R1212, ffestb_R1228, ffestb_V009, ffestb_module,
ffestb_R809, ffestb_R810, ffestb_R10014_, ffestb_R10015_,
ffestb_R10018_, ffestb_R1107, ffestb_R1202, ffestb_R12026_,
ffestb_S3P4, ffestb_V012, ffestb_V014, ffestb_V025, ffestb_V0255_,
ffestb_V020, ffestb_dimlist, ffestb_dummy, ffestb_R524,
ffestb_R547, ffestb_decl_chartype, ffestb_decl_dbltype,
ffestb_decl_gentype, ffestb_decl_recursive, ffestb_decl_entsp_2_,
ffestb_decl_func_, ffestb_V003, ffestb_V016, ffestb_V027,
ffestb_decl_R539): Likewise.
* stb.h (_ffestb_args_): Likewise.
* stc.c (ffestc_subr_binsrch_, ffestc_subr_is_present_,
ffestc_subr_speccmp_, ffestc_R904, ffestc_R907): Likewise.
* std.c (ffestd_R1001dump_1005_1_, ffestd_R1001dump_1005_2_,
ffestd_R1001dump_1005_3_, ffestd_R1001dump_1005_4_,
ffestd_R1001dump_1005_5_, ffestd_R1001dump_1010_1_,
ffestd_R1001dump_1010_2_, ffestd_R1001dump_1010_3_,
ffestd_R1001dump_1010_4_, ffestd_R1001dump_1010_5_): Likewise.
* ste.c (ffeste_begin_iterdo_, ffeste_subr_file_): Likewise.
* sts.c (ffests_printf_1D, ffests_printf_1U, ffests_printf_1s,
ffests_printf_2Us, ffests_puts, ffests_puttext): Likewise.
* sts.h (ffests_printf_1D, ffests_printf_1U, ffests_printf_1s,
ffests_printf_2Us, ffests_puts, ffests_puttext): Likewise.
* stt.c (ffestt_exprlist_drive, ffestt_implist_drive,
ffestt_tokenlist_drive): Add prototype arguments.
* stt.h (ffestt_exprlist_drive, ffestt_implist_drive,
ffestt_tokenlist_drive): Likewise.
* stu.c (ffestu_dummies_transition_): Likewise.
(ffestu_sym_end_transition): Const-ify a char*.
* stw.c (ffestw_display_state, ffestw_new, ffestw_pop): Add
prototype arguments.
* stw.h (ffestw_display_state, ffestw_new, ffestw_pop): Likewise.
* version.c (ffe_version_string): Const-ify a char*.
* version.h (ffe_version_string): Likewise.
Sat Mar 27 13:00:43 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* bad.c (_ffebad_message_, ffebad_string_, ffebad_message_,
ffebad_bufputs_, ffebad_bufputs_, ffebad_start_, ffebad_string,
ffebad_finish): Const-ify a char*.
* bld.c (ffebld_op_string_, ffebld_op_string): Likewise.
* bld.h (ffebld_op_string): Likewise.
* com.c (ffecom_arglist_expr_, ffecom_build_f2c_string_,
ffecom_debug_kludge_, ffecom_f2c_make_type_,
ffecom_get_appended_identifier_, ffecom_get_identifier_,
ffecom_gfrt_args_): Likewise.
(ffecom_convert_narrow_, ffecom_convert_widen_): Add prototype.
(builtin_function, ffecom_gfrt_name_, ffecom_gfrt_argstring_,
ffecom_arglist_expr_, ffecom_build_f2c_string_,
ffecom_debug_kludge_, ffecom_f2c_make_type_,
ffecom_get_appended_identifier_, ffecom_get_external_identifier_,
ffecom_get_identifier_, ffecom_decl_field,
ffecom_get_invented_identifier, lang_print_error_function,
skip_redundant_dir_prefix, read_name_map, print_containing_files):
Const-ify a char*.
(savestring): Remove, use `xstrdup' instead.
* com.h (ffecom_decl_field, ffecom_get_invented_identifier):
Const-ify a char*.
* data.c (ffebld, ffedata_gather_): Make explicitly static.
* expr.c (ffeexpr_isdigits_, ffeexpr_percent_,
ffeexpr_reduced_concatenate_, ffeexpr_nil_real_,
ffeexpr_nil_number_, ffeexpr_nil_number_period_,
ffeexpr_nil_number_real_, ffeexpr_token_real_,
ffeexpr_token_number_, ffeexpr_token_number_period_,
ffeexpr_token_number_real_): Const-ify a char*.
* fini.c (xspaces): Likewise.
* global.c (ffeglobal_type_string_): Likewise.
(ffeglobal_drive): Protoize.
(ffeglobal_proc_def_arg): Const-ify a char*.
* global.h (ffeglobal_drive): Protoize.
(ffeglobal_proc_def_arg): Const-ify a char*.
* implic.c (ffeimplic_none, ffeimplic_peek_symbol_type):
Likewise.
* implic.h (ffeimplic_peek_symbol_type): Likewise.
* info.c (ffeinfo_basictype_string_, ffeinfo_kind_message_,
ffeinfo_kind_string_, ffeinfo_kindtype_string_,
ffeinfo_where_string_, ffeinfo_basictype_string,
ffeinfo_kind_message, ffeinfo_kind_string,
ffeinfo_kindtype_string, ffeinfo_where_string): Likewise.
* info.h (ffeinfo_basictype_string, ffeinfo_kind_message,
ffeinfo_kind_string, ffeinfo_kindtype_string,
ffeinfo_where_string): Likewise.
* intrin.c (_ffeintrin_name_, _ffeintrin_gen_, _ffeintrin_spec_,
_ffeintrin_imp_, ffeintrin_check_, ffeintrin_cmp_name_,
ffeintrin_fulfill_specific, ffeintrin_init_0,
ffeintrin_is_actualarg, ffeintrin_is_intrinsic,
ffeintrin_name_generic, ffeintrin_name_implementation,
ffeintrin_name_specific): Likewise.
* intrin.h (ffeintrin_is_intrinsic, ffeintrin_name_generic,
ffeintrin_name_implementation, ffeintrin_name_specific): Likewise.
* lex.c (ffelex_type_string_, ffelex_token_new_character,
ffelex_token_new_name, ffelex_token_new_names,
ffelex_token_new_number): Likewise.
* lex.h (ffelex_token_new_character, ffelex_token_new_name,
ffelex_token_new_names, ffelex_token_new_number): Likewise.
* malloc.c (malloc_types_, malloc_pool_new, malloc_new_inpool_,
malloc_new_zinpool_): Likewise.
* malloc.h (malloc_new_inpool_, malloc_new_zinpool_,
malloc_pool_new): Likewise.
* name.c (ffename_space_drive_global, ffename_space_drive_symbol):
Protoize.
* name.h (ffename_space_drive_global, ffename_space_drive_symbol):
Likewise.
* symbol.c (ffesymbol_state_name_, ffesymbol_attr_name_,
ffesymbol_attrs_string): Const-ify a char*.
(ffesymbol_drive, ffesymbol_drive_sfnames): Protoize.
(ffesymbol_state_string): Const-ify a char*.
* symbol.h (ffesymbol_attrs_string): Likewise.
(ffesymbol_drive, ffesymbol_drive_sfnames): Protoize.
(ffesymbol_state_string): Const-ify a char*.
* target.c (ffetarget_layout): Likewise.
* target.h (ffetarget_layout): Likewise.
1999-03-25 Zack Weinberg <zack@rabi.columbia.edu>
* Make-lang.in: Remove all references to g77.o/g77.c.
Link g77 from gcc.o.
1999-03-21 Manfred Hollstein <manfred@s-direktnet.de>
* Makefile.in (g77$(exeext)): Depend on intl.o. Link in intl.o.
Wed Mar 17 11:39:44 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Editorial fix.
Mon Mar 15 17:12:07 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi, g77.texi, news.texi: Editorial fixes.
Sat Mar 13 17:51:55 1999 Craig Burley <craig@jcb-sc.com>
Fix 19990313-0.f, 19990313-1.f, 19990313-2.f, 19990313-3.f:
* bad.def (FFEBAD_NOCANDO): New error code for internal use only.
* expr.c (ffeexpr_collapse_convert): If FFEBAD_NOCANDO returned
by convertor, just return original expr.
* target.h: Return FFEBAD_NOCANDO for (usually) 64-bit
conversions that aren't yet working properly.
* news.texi: Explain.
* version.c: Bump version.
Sat Mar 13 14:26:55 1999 Craig Burley <craig@jcb-sc.com>
* RELEASE-PREP: New file, lists things to do for a release.
* Make-lang.in, bugs.texi, bugs0.texi, g77.texi, g77install.texi,
install0.texi, news.texi, news0.texi: Accommodate new doc
architecture.
Consolidate news items. Don't describe old news items in
various generated docs.
Don't describe FSF-g77 installation stuff in various EGCS-g77
generated docs.
Move description of AUTOMATIC to more suitable location.
* root.texi: New file for new doc architecture.
Thu Mar 11 17:32:55 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: Add AUTOMATIC to list of unsupported extensions.
Sat Mar 6 02:28:35 1999 Craig Burley <craig@jcb-sc.com>
Warn about non-Y2K-compliant intrinsics:
* bad.def (FFEBAD_INTRINSIC_Y2KBAD): New diagnostic.
* intrin.def (FFEINTRIN_impDATE, FFEINTRIN_impIDATE_vxt):
Use new DEFIMPY macro to flag these as non-Y2K-compliant.
* intdoc.c (DEFIMPY): Support new Y2K macro.
* intrin.h (DEFIMPY): Ditto.
* intrin.c (DEFIMPY): Ditto.
(ffeintrin_fulfill_generic, ffeintrin_fulfill_specific):
Warn about invocation of non-Y2K-compliant intrinsic.
* com-rt.def (FFECOM_gfrtDATE, FFECOM_gfrtVXTIDATE):
Rename external procedure names, to keep previously-
compiled (sans-new-warnings) code from linking to
new library.
* g77.texi: Document all this stuff.
* news.texi: Spread the joy.
* version.c: Bump version.
Fri Mar 5 13:22:44 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Relocate IDATE (VXT) fix: we put it in 1.1.2
so describe it there, instead of under 1.2.
Wed Mar 3 00:57:56 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: IDATE (VXT) fixed to return year as 0..99.
Wed Mar 3 00:43:49 1999 Craig Burley <craig@jcb-sc.com>
* g77.texi: Add remaining changes pending from Dave Love.
Wed Mar 3 00:38:42 1999 Craig Burley <craig@jcb-sc.com>
* bugs.texi, news.texi: Conditionalize cross-references
on non-html processing, providing temporary HTML "links".
* g77.texi: Fix up a reference.
Wed Mar 3 00:12:31 1999 Craig Burley <craig@jcb-sc.com>
* news.texi, bugs.texi: Delete fixed bugs, make one
of them into the appropriate news item.
Wed Mar 3 00:05:52 1999 Craig Burley <craig@jcb-sc.com>
* news.texi: Copy over 1.1.2 news.
1999-03-02 Craig Burley <craig@jcb-sc.com>
* g77.texi (Bug Reporting): Clarify whether to use -E.
Clarify other instructions.
1999-02-27 Craig Burley <craig@jcb-sc.com>
* lang-specs.h: Fix specs to pass `-ax' as well as `-a' option.
1999-02-26 Craig Burley <craig@jcb-sc.com>
* intdoc.in (STAT_func, STAT_subr,
FSTAT_func, FSTAT_subr, LSTAT_func, LSTAT_subr):
Properly order array elements. Specify N/A return values.
1999-02-26 Craig Burley <craig@jcb-sc.com>
* intdoc.in (DATE_AND_TIME): Explain that VALUES(7) holds
seconds, and VALUES(8), therefore, milliseconds.
1999-02-26 Craig Burley <craig@jcb-sc.com>
* news.texi: Clarify IOSTAT= fix.
1999-02-25 Richard Henderson <rth@cygnus.com>
* lang-specs.h: Define __FAST_MATH__ when appropriate.
1999-02-25 Craig Burley <craig@jcb-sc.com>
* g77.texi: Clarify/index lack of run-time allocation for
concatenation.
1999-02-25 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* f/intdoc.in: Add missing `,' after cross references.
1999-02-20 Craig Burley <craig@jcb-sc.com>
* Make-lang.in (f77.install-common, f77.install-info,
f77.install-man, f77.uninstall): Use `$(prefix)/lang-f77'
instead of `lang-f77' for flag file, to be sure of a
writable directory, and remove the flag file after each
operation to keep things clean.
1999-02-20 Craig Burley <craig@jcb-sc.com>
* g77.texi: Properly attribute Priest document; clarify
that it is in the .ps version of the Goldberg document.
1999-02-19 Craig Burley <craig@jcb-sc.com>
* bugs0.texi, bugs.texi, install0.texi, g77install.texi,
news0.texi, news.texi: Update copyright dates.
Clarify which files are source, which are derived,
and remind maintainers where copyright dates are sourced.
* BUGS, INSTALL, NEWS: Regenerated.
1999-02-19 Craig Burley <craig@jcb-sc.com>
* global.c (ffeglobal_ref_progunit_): Warn about a function
definition that disagrees with the type of a previous reference.
Improve commentary. Fix a couple of minor bugs. Clean up
some code.
* news.texi: Spread the joy.
1999-02-18 Craig Burley <craig@jcb-sc.com>
* expr.c (ffeexpr_finished_): Disallow non-default INTEGER
as argument for FILEINT and FILEASSOC as lhs.
* news.texi: Document fix.
* version.c: Bump.
1999-02-18 Craig Burley <craig@jcb-sc.com>
* g77.texi: Clarify -fno-globals vs. -Wno-globals.
1999-02-18 Craig Burley <craig@jcb-sc.com>
* intdoc.in (LOG10): Fix typo.
1999-02-17 Ulrich Drepper <drepper@cygnus.com>
* intdoc.in: Fix typo.
1999-02-17 Craig Burley <craig@jcb-sc.com>
* g77.texi, intdoc.in: Document Y2K and some other known
limitations.
* intrin.def (DTIME, FDATE): Fix capitalization of
case-sensitive forms of these intrinsics' names.
1999-02-17 Dave Love <fx@gnu.org>
* intdoc.in: Say `common' logarithm for log10.
1999-02-16 Ulrich Drepper <drepper@cygnus.com>
* g77.texi: Add missing @ in email addresses.
1999-02-15 Craig Burley <craig@jcb-sc.com>
* *.*: Delete my (old) email address in most places, change it
in a few.
1999-02-14 Craig Burley <craig@jcb-sc.com>
* version.c: Bump.
1999-02-14 Craig Burley <craig@jcb-sc.com>
* version.c: Bump for 1998-10-02 change (forgot to do this
before).
1999-02-14 Craig Burley <craig@jcb-sc.com>
* lang-specs.h, g77.1, g77.texi, news.texi: Recognize `.FOR'
and `.FPP' as well as `.for' and `.fpp'.
1999-02-14 Craig Burley <craig@jcb-sc.com>
* intdoc.in (LOG10): Fix description.
1999-02-14 Craig Burley <craig@jcb-sc.com>
* news.texi: Mention fix for SIGNAL invocation circa egcs-1.1.
1999-02-14 Craig Burley <craig@jcb-sc.com>
* g77.texi, g77install.texi, bugs.texi, g77install.texi: Clean
up and improve indexing, and some other areas of docs.
1999-02-14 Craig Burley <craig@jcb-sc.com>
* intdoc.in (MCLOCK8, TIME8): Warn about lower range on
32-bit systems.
Sat Feb 6 18:02:17 1999 Jeffrey A Law (law@cygnus.com)
* g77.texi: Update email addresses.
Wed Feb 3 22:50:17 1999 Marc Espie <Marc.Espie@liafa.jussieu.fr>
* Make-lang.in (g77$(exeext)): Get choose-temp.o, pexecute.o and
mkstemp.o from libiberty.
1999-02-01 Zack Weinberg <zack@rabi.columbia.edu>
* top.c: Don't define ffe_is_ident_. Don't process
-f(no-)ident here.
* top.h: Remove declaration of ffe_is_ident_ and macros
ffe_is_ident() and ffe_set_is_ident().
* lex.c: Use flag_no_ident instead of ffe_is_ident().
Sun Jan 31 20:34:29 1999 Zack Weinberg <zack@rabi.columbia.edu>
* lang-specs.h: Map -Qn to -fno-ident.
Tue Jan 5 22:12:41 1999 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Make-lang.in (g77.o): Depend on prefix.h.
Fri Nov 27 13:10:32 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* fini.c: Rename variable `spaces' to `xspaces' to avoid
conflicting with function `spaces' from libiberty.
* g77spec.c: Don't prototype libiberty functions.
* malloc.c: Likewise.
1998-11-20 Dave Love <d.love@dl.ac.uk>
* g77.texi: Assorted minor changes.
1998-11-19 Dave Love <d.love@dl.ac.uk>
* bugs.texi: Formatting changes from Craig.
* intdoc.in: Terminate some @xrefs with `,'.
1998-11-19 Manfred Hollstein <manfred@s-direktnet.de>
* Make-lang.in (mandir): Replace all uses of $(mandir) by $(man1dir).
Mon Nov 9 23:15:39 1998 Jeffrey A Law (law@cygnus.com)
* g77.texi, news.texi: Updates from Craig.
Sun Nov 8 17:47:56 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* Makefile.in (INCLUDES): Add "-I$(srcdir)/../../include".
Sat Nov 7 15:58:54 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* g77spec.c: Don't include gansidecl.h.
* output.j: Likewise.
1998-11-04 Dave Love <d.love@dl.ac.uk>
* g77.texi: Small formatting/indexing fixes.
Mon Oct 12 20:41:59 1998 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* bad.c (ffebad_finish): Change type of variable `c' to unsigned
char, change type of variable `s' to unsigned char *.
* com.c (ffecom_symbol_null_): Add missing initializers.
* fini.c (MAXNAMELEN): Undef it before defining.
* implic.c (ffeimplic_lookup_): Change type of parameter `c' to
unsigned char.
* intrin.c (ffeintrin_init_0): Cast the argument of ctype macros
to (unsigned char).
* lex.c (ffelex_splice_tokens): Change type of variable `p' to
unsigned char *.
(ffelex_token_name_from_names): Cast the argument of
`ffelex_is_firstnamechar' to (unsigned char).
(ffelex_token_names_from_names): Likewise.
(ffelex_token_new_name): Likewise.
(ffelex_token_new_names): Likewise.
* malloc.c (malloc_root_): Add missing initializer.
* stb.c (ffestb_do): Change type of variable `p' to unsigned char *.
(ffestb_else) Likewise.
(ffestb_else3_) Likewise.
(ffestb_endxyz) Likewise.
(ffestb_goto) Likewise.
(ffestb_let) Likewise.
(ffestb_varlist) Likewise.
(ffestb_R522) Likewise.
(ffestb_R528) Likewise.
(ffestb_R834) Likewise.
(ffestb_R835) Likewise.
(ffestb_R838) Likewise.
(ffestb_R1102) Likewise.
(ffestb_blockdata) Likewise.
(ffestb_R1212) Likewise.
(ffestb_R810) Likewise.
(ffestb_R10014_): Cast the argument of `ffelex_is_firstnamechar'
to (unsigned char).
(ffestb_V014): Change type of variable `p' to unsigned char *.
(ffestb_dummy) Likewise.
(ffestb_R524) Likewise.
(ffestb_R547) Likewise.
(ffestb_decl_chartype) Likewise.
(ffestb_decl_dbltype) Likewise.
(ffestb_decl_gentype) Likewise.
(ffestb_decl_entsp_2_) Likewise.
(ffestb_V027) Likewise.
(ffestb_decl_R539) Likewise.
* top.c (ffe_decode_option): Mark parameter `argc' with
ATTRIBUTE_UNUSED.
* where.c (ffewhere_unknown_line_): Add missing initializers.
1998-10-02 Dave Love <d.love@dl.ac.uk>
* com.c (ffecom_expr_intrinsic_): Fix return type for RAND.
Thu Oct 1 10:43:45 1998 Nick Clifton <nickc@cygnus.com>
* lex.c: Replace occurances of HANDLE_SYSV_PRAGMA with
HANDLE_GENERIC_PRAGMAS.
Mon Sep 28 04:22:00 1998 Jeffrey A Law (law@cygnus.com)
* news.texi: Update from Craig.
1998-09-23 Dave Love <d.love@dl.ac.uk>
* g77.texi: Additions about `/*', trailing comments and cpp.
1998-09-18 Dave Love <d.love@dl.ac.uk>
* g77.texi: Various additions and some small fixes.
Thu Sep 10 14:55:44 1998 Kamil Iskra <iskra@student.uci.agh.edu.pl>
* Make-lang.in (f77.install-common): Add missing "else true;".
1998-09-07 Dave Love <d.love@dl.ac.uk>
* ChangeLog.egcs: Deleted. Entries merged here.
1998-09-05 Dave Love <d.love@dl.ac.uk>
* Makefile.in (LDFLAGS): Set from BOOT_LDFLAGS.
(F771_LDFLAGS): Variable dispensed with.
Fri Sep 4 19:53:34 1998 Craig Burley <burley@gnu.org>
* intdoc.in: Minor editorial tweaks.
Fri Sep 4 18:35:52 1998 Craig Burley <burley@gnu.org>
* lang-options.h: Convert to wrap option and doc string
in a new macro invocation, FTNOPT, so the nearly identical
list can be used in FSF-g77.
Fri Sep 4 18:35:52 1998 Craig Burley <burley@gnu.org>
* Makefile.in (fini.o): Don't define USE_HCONFIG here.
* fini.c: Define USE_HCONFIG here instead, so deps-kinda
picks up correct dependency.
* Makefile.in (proj-h.o): Fix dependencies list.
Wed Sep 02 09:25:29 1998 Nick Clifton <nickc@cygnus.com>
* lex.c (ffe_lex_hash): Change how HANDLE_PRAGMA and
HANDLE_SYSV_PRAGMA would be called if they pragma parsing was
enabled in this code.
Generate warning messages if unknown pragmas are encountered.
(pragma_getc): New function: retrieves characters from the
input stream. Defined when HANDLE_PRAGMA is defined.
(pragma_ungetc): New function: replaces characters back into the
input stream. Defined when HANDLE_PRAGMA is defined.
Tue Sep 1 10:00:21 1998 Craig Burley <burley@gnu.org>
* bugs.texi, g77.1, g77.texi, intdoc.in, news.texi: Doc updates
from Craig.
1998-08-23 Dave Love <d.love@dl.ac.uk>
* g77.texi: Increment `version-g77' and fix a few typos.
Tue Aug 18 21:41:31 1998 Jeffrey A Law (law@cygnus.com)
* Make-lang.in: Add several "else true" clauses to deal with lame
systems.
Tue Aug 11 08:12:14 1998 H.J. Lu (hjl@gnu.org)
* Make-lang.in (g77.o): Touch lang-f77 before checking it.
1998-08-09 Dave Love <d.love@dl.ac.uk>
* Make-lang.in (f/g77.dvi): Replace non-working use of texi2dvi
with explicit use of tex.
(f77.mostlyclean): Remove TeX index files.
* g77install.texi (Prerequisites): Kluge round TeX lossage with
hyphen in @value in @code.
Tue Aug 4 16:59:39 1998 Craig Burley <burley@gnu.org>
* com.c (ffecom_convert_narrow_, ffecom_convert_widen_):
Allow conversion from pointer to same-sized integer,
to fix invoking SIGNAL as a function.
1998-07-26 Dave Love <d.love@dl.ac.uk>
* BUGS, INSTALL, NEWS: Rebuilt.
Sat Jul 25 17:23:55 1998 Craig Burley <burley@gnu.org>
Fix 980615-0.f:
* stc.c (ffestc_R1229_start): Set info to ANY as well.
Tue Jul 21 04:33:37 1998 Craig Burley <burley@gnu.org>
* g77spec.c (lang_specific_driver): Return unmolested
command line when --help seen.
Comment out code that printed g77-specific --help info.
Sat Jul 18 19:16:48 1998 Craig Burley <burley@gnu.org>
* lang-options.h: Fix up doc strings.
Remove the unimplemented -fdcp-intrinsics-* options.
* str-1t.fin: Change mixed-case spelling of `GoTo' from
`Goto'.
Thu Jul 16 13:26:36 1998 Craig Burley <burley@gnu.org>
* com.c (ffecom_finish_symbol_transform_): Revert change
of 1998-05-23, as it was too aggressive, in that it
prevented transformation of (used) functions before
primary code generation.
1998-07-15 Dave Love <d.love@dl.ac.uk>
* intdoc.texi: Regenerated.
Mon Jul 13 18:45:06 1998 Craig Burley <burley@gnu.org>
* Make-lang.in (f77.rebuilt): Fix to depend on
build-dir-based, not source-based, g77.info.
* g77.texi: Merge docs with 0.5.24.
* g77install.texi: Ditto.
Mon Jul 13 18:02:29 1998 Craig Burley <burley@gnu.org>
Cleanups vis-a-vis g77-0.5.24:
* g77spec.c (lang_specific_driver): Tabify source.
* top.c (ffe_decode_option): Use fixed macro to set
internal-checking flag.
* top.h (ffe_set_is_do_internal_checks): Fix macro.
Mon Jul 13 17:33:44 1998 Craig Burley <burley@gnu.org>
Cleanups vis-a-vis system.h cutover and g77-0.5.24:
* Makefile.in (fini.o): Define USE_HCONFIG macro
so source code doesn't have to.
* fini.c: Don't define USE_HCONFIG here, since
source code usually shouldn't care about this.
* ansify.c: Include stddef.h only if we have it.
* intdoc.c: Ditto.
* proj.h: Ditto.
Mon Jul 13 17:30:29 1998 Nick Clifton <nickc@cygnus.com>
* lang-options.h: Format changed to work with --help support added
to gcc/toplev.c
Mon Jul 13 11:54:03 1998 Craig Burley <burley@gnu.org>
* com.c (ffecom_push_tempvar): Replace kludge that
munged back-end globals directly with proper calls
to push_topmost_sequence and pop_topmost_sequence.
1998-07-12 Dave Love <d.love@dl.ac.uk>
* version.c: Bump version.
Sat Jul 11 19:24:32 1998 Craig Burley <burley@gnu.org>
Fix 980616-0.f:
* equiv.c (ffeequiv_offset_): Don't crash on various
possible ANY operands.
Sat Jul 11 18:24:37 1998 Craig Burley <burley@gnu.org>
* com.c (ffecom_expr_) [FFEBLD_opCONTER]: Die if padding
for constant is non-zero.
* com.c (__eprintf): Delete this function, it is obsolete.
1998-07-09 Dave Love <d.love@dl.ac.uk>
* intdoc.in (HOSTNM_func, HOSTNM_subr): Update last change.
Thu Jul 9 00:45:59 1998 Craig Burley <burley@gnu.org>
Fix debugging of CHARACTER*(*), etc., which requires
emitting debug info on types like `ftnlen':
* com.c (ffecom_start_progunit_): Don't bother
resetting "invented" flag for identifier.
(ffecom_transform_equiv_): Don't bother zeroing
"ignored" flag for decl.
(pushdecl): No longer set "ignored", "used", or
"suppressed debug" flags for decls having "invented"
identifiers.
1998-07-06 Mike Stump <mrs@wrs.com>
* Make-lang.in (f77.stage?): Use mv -f instead of just mv so that
we can move g77.c.
1998-07-06 Dave Love <d.love@dl.ac.uk>
* intdoc.in (HOSTNM_func, HOSTNM_subr): Note possible need for
-lsocket.
1998-07-05 Dave Love <d.love@dl.ac.uk>
* intdoc.in: Add entry for DATE_AND_TIME.
* intrin.def: Add implementation for DATE_AND_TIME. Make second
and third args of SYSTEM_CLOCK optional.
* com.c (ffecom_expr_intrinsic_): New case for DATE_AND_TIME.
* com-rt.def (FFECOM_gfrtSYSTEM_CLOCK): Call G77_system_clock_0,
not system_clock_.
(FFECOM_gfrtDATE_AND_TIME): New DEFGFRT.
Wed Jul 1 11:19:13 1998 Craig Burley <burley@gnu.org>
Fix 980701-1.f (which was producing "unaligned trap"
on an Alpha running GNU/Linux, as predicted):
* equiv.c (ffeequiv_layout_local_): Don't bother
coping with pre-padding of entire area while building
it; do that instead after the building is done, and
do it by modifying only the modulo field. This covers
the case of alignment stringency being increased without
lowering the starting offset, unlike the previous changes,
and even more elegantly than those.
* target.c (ffetarget_align): Make sure alignments
are non-zero, just in case.
See ChangeLog.0 for earlier changes.
Local Variables:
add-log-time-format: current-time-string
End:
Index: head/contrib/gcc/f/version.c
===================================================================
--- head/contrib/gcc/f/version.c (revision 52750)
+++ head/contrib/gcc/f/version.c (revision 52751)
@@ -1 +1 @@
-const char *ffe_version_string = "0.5.25 19990816 (release)";
+const char *ffe_version_string = "0.5.25 19991024 (release)";
Index: head/contrib/gcc/fix-header.c
===================================================================
--- head/contrib/gcc/fix-header.c (revision 52750)
+++ head/contrib/gcc/fix-header.c (revision 52751)
@@ -1,1333 +1,1333 @@
/* fix-header.c - Make C header file suitable for C++.
Copyright (C) 1993, 94-98, 1999 Free Software Foundation, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* This program massages a system include file (such as stdio.h),
into a form that is compatible with GNU C and GNU C++.
* extern "C" { ... } braces are added (inside #ifndef __cplusplus),
if they seem to be needed. These prevent C++ compilers from name
mangling the functions inside the braces.
* If an old-style incomplete function declaration is seen (without
an argument list), and it is a "standard" function listed in
the file sys-protos.h (and with a non-empty argument list), then
the declaration is converted to a complete prototype by replacing
the empty parameter list with the argument list from sys-protos.h.
* The program can be given a list of (names of) required standard
functions (such as fclose for stdio.h). If a required function
is not seen in the input, then a prototype for it will be
written to the output.
* If all of the non-comment code of the original file is protected
against multiple inclusion:
#ifndef FOO
#define FOO
<body of include file>
#endif
then extra matter added to the include file is placed inside the <body>.
* If the input file is OK (nothing needs to be done);
the output file is not written (nor removed if it exists).
There are also some special actions that are done for certain
well-known standard include files:
* If argv[1] is "sys/stat.h", the Posix.1 macros
S_ISBLK, S_ISCHR, S_ISDIR, S_ISFIFO, S_ISLNK, S_ISREG are added if
they were missing, and the corresponding "traditional" S_IFxxx
macros were defined.
* If argv[1] is "errno.h", errno is declared if it was missing.
* TODO: The input file should be read complete into memory, because:
a) it needs to be scanned twice anyway, and
b) it would be nice to allow update in place.
Usage:
fix-header FOO.H INFILE.H OUTFILE.H [OPTIONS]
where:
* FOO.H is the relative file name of the include file,
as it would be #include'd by a C file. (E.g. stdio.h)
* INFILE.H is a full pathname for the input file (e.g. /usr/include/stdio.h)
* OUTFILE.H is the full pathname for where to write the output file,
if anything needs to be done. (e.g. ./include/stdio.h)
* OPTIONS are such as you would pass to cpp.
Written by Per Bothner <bothner@cygnus.com>, July 1993. */
#include "hconfig.h"
#include "system.h"
#include "obstack.h"
#include "scan.h"
#include "cpplib.h"
#include "cpphash.h"
static void v_fatal PROTO ((const char *, va_list)) ATTRIBUTE_NORETURN;
void fatal PVPROTO ((const char *, ...)) ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
sstring buf;
int verbose = 0;
int partial_count = 0;
int warnings = 0;
/* We no longer need to add extern "C", because cpp implicitly
forces the standard include files to be treated as C. */
/*#define ADD_MISSING_EXTERN_C 1 */
#if ADD_MISSING_EXTERN_C
int missing_extern_C_count = 0;
#endif
#include "xsys-protos.h"
#ifdef FIXPROTO_IGNORE_LIST
/* This is a currently unused feature. */
/* List of files and directories to ignore.
A directory name (ending in '/') means ignore anything in that
directory. (It might be more efficient to do directory pruning
earlier in fixproto, but this is simpler and easier to customize.) */
static char *files_to_ignore[] = {
"X11/",
FIXPROTO_IGNORE_LIST
0
};
#endif
char *inf_buffer;
char *inf_limit;
char *inf_ptr;
/* Certain standard files get extra treatment */
enum special_file
{
no_special,
#ifdef errno_h
#undef errno_h
#endif
errno_h,
#ifdef stdio_h
#undef stdio_h
#endif
stdio_h,
#ifdef stdlib_h
#undef stdlib_h
#endif
stdlib_h,
#ifdef sys_stat_h
#undef sys_stat_h
#endif
sys_stat_h
};
/* A NAMELIST is a sequence of names, separated by '\0', and terminated
by an empty name (i.e. by "\0\0"). */
typedef const char *namelist;
/* The following macros provide the bits for symbol_flags. */
typedef int symbol_flags;
/* Used to mark names defined in the ANSI/ISO C standard. */
#define ANSI_SYMBOL 1
/* We no longer massage include files for POSIX or XOPEN symbols,
as there are now several versions of the POSIX and XOPEN standards,
and it would be a maintenance nightmare for us to track them all.
Better to be compatible with the system include files. */
/*#define ADD_MISSING_POSIX 1 */
/*#define ADD_MISSING_XOPEN 1 */
#if ADD_MISSING_POSIX
/* Used to mark names defined in the Posix.1 or Posix.2 standard. */
#define POSIX1_SYMBOL 2
#define POSIX2_SYMBOL 4
#else
#define POSIX1_SYMBOL 0
#define POSIX2_SYMBOL 0
#endif
#if ADD_MISSING_XOPEN
/* Used to mark names defined in X/Open Portability Guide. */
#define XOPEN_SYMBOL 8
/* Used to mark names defined in X/Open UNIX Extensions. */
#define XOPEN_EXTENDED_SYMBOL 16
#else
#define XOPEN_SYMBOL 0
#define XOPEN_EXTENDED_SYMBOL 0
#endif
/* Used to indicate names that are not functions */
#define MACRO_SYMBOL 512
struct symbol_list {
symbol_flags flags;
namelist names;
};
#define SYMBOL_TABLE_SIZE 10
struct symbol_list symbol_table[SYMBOL_TABLE_SIZE];
int cur_symbol_table_size;
void
add_symbols (flags, names)
symbol_flags flags;
namelist names;
{
symbol_table[cur_symbol_table_size].flags = flags;
symbol_table[cur_symbol_table_size].names = names;
cur_symbol_table_size++;
if (cur_symbol_table_size >= SYMBOL_TABLE_SIZE)
fatal ("too many calls to add_symbols");
symbol_table[cur_symbol_table_size].names = NULL; /* Termination. */
}
struct std_include_entry {
const char *name;
symbol_flags flags;
namelist names;
};
const char NONE[] = ""; /* The empty namelist. */
/* Special name to indicate a continuation line in std_include_table. */
const char CONTINUED[] = "";
struct std_include_entry *include_entry;
struct std_include_entry std_include_table [] = {
{ "ctype.h", ANSI_SYMBOL,
"isalnum\0isalpha\0iscntrl\0isdigit\0isgraph\0islower\0\
isprint\0ispunct\0isspace\0isupper\0isxdigit\0tolower\0toupper\0" },
{ "dirent.h", POSIX1_SYMBOL, "closedir\0opendir\0readdir\0rewinddir\0"},
{ "errno.h", ANSI_SYMBOL|MACRO_SYMBOL, "errno\0" },
/* ANSI_SYMBOL is wrong, but ... */
{ "curses.h", ANSI_SYMBOL, "box\0delwin\0endwin\0getcurx\0getcury\0initscr\0\
mvcur\0mvwprintw\0mvwscanw\0newwin\0overlay\0overwrite\0\
scroll\0subwin\0touchwin\0waddstr\0wclear\0wclrtobot\0wclrtoeol\0\
waddch\0wdelch\0wdeleteln\0werase\0wgetch\0wgetstr\0winsch\0winsertln\0\
wmove\0wprintw\0wrefresh\0wscanw\0wstandend\0wstandout\0" },
{ "fcntl.h", POSIX1_SYMBOL, "creat\0fcntl\0open\0" },
/* Maybe also "getgrent fgetgrent setgrent endgrent" */
{ "grp.h", POSIX1_SYMBOL, "getgrgid\0getgrnam\0" },
/*{ "limit.h", ... provided by gcc }, */
{ "locale.h", ANSI_SYMBOL, "localeconv\0setlocale\0" },
{ "math.h", ANSI_SYMBOL,
"acos\0asin\0atan\0atan2\0ceil\0cos\0cosh\0exp\0\
fabs\0floor\0fmod\0frexp\0ldexp\0log10\0log\0modf\0pow\0sin\0sinh\0sqrt\0\
tan\0tanh\0" },
{ CONTINUED, ANSI_SYMBOL|MACRO_SYMBOL, "HUGE_VAL\0" },
{ "pwd.h", POSIX1_SYMBOL, "getpwnam\0getpwuid\0" },
/* Left out siglongjmp sigsetjmp - these depend on sigjmp_buf. */
{ "setjmp.h", ANSI_SYMBOL, "longjmp\0setjmp\0" },
/* Left out signal() - its prototype is too complex for us!
Also left out "sigaction sigaddset sigdelset sigemptyset
sigfillset sigismember sigpending sigprocmask sigsuspend"
because these need sigset_t or struct sigaction.
Most systems that provide them will also declare them. */
{ "signal.h", ANSI_SYMBOL, "kill\0raise\0" },
{ "stdio.h", ANSI_SYMBOL,
"clearerr\0fclose\0feof\0ferror\0fflush\0fgetc\0fgetpos\0\
fgets\0fopen\0fprintf\0fputc\0fputs\0fread\0freopen\0fscanf\0fseek\0\
fsetpos\0ftell\0fwrite\0getc\0getchar\0gets\0perror\0\
printf\0putc\0putchar\0puts\0remove\0rename\0rewind\0scanf\0setbuf\0\
setvbuf\0sprintf\0sscanf\0vprintf\0vsprintf\0vfprintf\0tmpfile\0\
tmpnam\0ungetc\0" },
{ CONTINUED, POSIX1_SYMBOL, "fdopen\0fileno\0" },
{ CONTINUED, POSIX2_SYMBOL, "pclose\0popen\0" }, /* I think ... */
/* Should perhaps also handle NULL, EOF, ... ? */
/* "div ldiv", - ignored because these depend on div_t, ldiv_t
ignore these: "mblen mbstowcs mbstowc wcstombs wctomb"
Left out getgroups, because SunOS4 has incompatible BSD and SVR4 versions.
Should perhaps also add NULL */
{ "stdlib.h", ANSI_SYMBOL,
"abort\0abs\0atexit\0atof\0atoi\0atol\0bsearch\0calloc\0\
exit\0free\0getenv\0labs\0malloc\0putenv\0qsort\0rand\0realloc\0\
srand\0strtod\0strtol\0strtoul\0system\0" },
{ CONTINUED, ANSI_SYMBOL|MACRO_SYMBOL, "EXIT_FAILURE\0EXIT_SUCCESS\0" },
{ "string.h", ANSI_SYMBOL, "memchr\0memcmp\0memcpy\0memmove\0memset\0\
strcat\0strchr\0strcmp\0strcoll\0strcpy\0strcspn\0strerror\0\
strlen\0strncat\0strncmp\0strncpy\0strpbrk\0strrchr\0strspn\0strstr\0\
strtok\0strxfrm\0" },
/* Should perhaps also add NULL and size_t */
{ "strings.h", XOPEN_EXTENDED_SYMBOL,
"bcmp\0bcopy\0bzero\0ffs\0index\0rindex\0strcasecmp\0strncasecmp\0" },
{ "strops.h", XOPEN_EXTENDED_SYMBOL, "ioctl\0" },
/* Actually, XPG4 does not seem to have <sys/ioctl.h>, but defines
ioctl in <strops.h>. However, many systems have it is sys/ioctl.h,
and many systems do have <sys/ioctl.h> but not <strops.h>. */
{ "sys/ioctl.h", XOPEN_EXTENDED_SYMBOL, "ioctl\0" },
{ "sys/socket.h", XOPEN_EXTENDED_SYMBOL, "socket\0" },
{ "sys/stat.h", POSIX1_SYMBOL,
"chmod\0fstat\0mkdir\0mkfifo\0stat\0lstat\0umask\0" },
{ CONTINUED, POSIX1_SYMBOL|MACRO_SYMBOL,
"S_ISDIR\0S_ISBLK\0S_ISCHR\0S_ISFIFO\0S_ISREG\0S_ISLNK\0S_IFDIR\0\
S_IFBLK\0S_IFCHR\0S_IFIFO\0S_IFREG\0S_IFLNK\0" },
{ CONTINUED, XOPEN_EXTENDED_SYMBOL, "fchmod\0" },
#if 0
/* How do we handle fd_set? */
{ "sys/time.h", XOPEN_EXTENDED_SYMBOL, "select\0" },
{ "sys/select.h", XOPEN_EXTENDED_SYMBOL /* fake */, "select\0" },
#endif
{ "sys/times.h", POSIX1_SYMBOL, "times\0" },
/* "sys/types.h" add types (not in old g++-include) */
{ "sys/utsname.h", POSIX1_SYMBOL, "uname\0" },
{ "sys/wait.h", POSIX1_SYMBOL, "wait\0waitpid\0" },
{ CONTINUED, POSIX1_SYMBOL|MACRO_SYMBOL,
"WEXITSTATUS\0WIFEXITED\0WIFSIGNALED\0WIFSTOPPED\0WSTOPSIG\0\
WTERMSIG\0WNOHANG\0WNOTRACED\0" },
{ "tar.h", POSIX1_SYMBOL, NONE },
{ "termios.h", POSIX1_SYMBOL,
"cfgetispeed\0cfgetospeed\0cfsetispeed\0cfsetospeed\0tcdrain\0tcflow\0tcflush\0tcgetattr\0tcsendbreak\0tcsetattr\0" },
{ "time.h", ANSI_SYMBOL,
"asctime\0clock\0ctime\0difftime\0gmtime\0localtime\0mktime\0strftime\0time\0tzset\0" },
{ "unistd.h", POSIX1_SYMBOL,
"_exit\0access\0alarm\0chdir\0chown\0close\0ctermid\0cuserid\0\
dup\0dup2\0execl\0execle\0execlp\0execv\0execve\0execvp\0fork\0fpathconf\0\
getcwd\0getegid\0geteuid\0getgid\0getlogin\0getpgrp\0getpid\0\
getppid\0getuid\0isatty\0link\0lseek\0pathconf\0pause\0pipe\0read\0rmdir\0\
setgid\0setpgid\0setsid\0setuid\0sleep\0sysconf\0tcgetpgrp\0tcsetpgrp\0\
ttyname\0unlink\0write\0" },
{ CONTINUED, POSIX2_SYMBOL, "getopt\0" },
{ CONTINUED, XOPEN_EXTENDED_SYMBOL,
"lockf\0gethostid\0gethostname\0readlink\0symlink\0" },
{ "utime.h", POSIX1_SYMBOL, "utime\0" },
{ NULL, 0, NONE }
};
enum special_file special_file_handling = no_special;
/* They are set if the corresponding macro has been seen. */
/* The following are only used when handling sys/stat.h */
int seen_S_IFBLK = 0, seen_S_ISBLK = 0;
int seen_S_IFCHR = 0, seen_S_ISCHR = 0;
int seen_S_IFDIR = 0, seen_S_ISDIR = 0;
int seen_S_IFIFO = 0, seen_S_ISFIFO = 0;
int seen_S_IFLNK = 0, seen_S_ISLNK = 0;
int seen_S_IFREG = 0, seen_S_ISREG = 0;
/* The following are only used when handling errno.h */
int seen_errno = 0;
/* The following are only used when handling stdlib.h */
int seen_EXIT_FAILURE = 0, seen_EXIT_SUCCESS = 0;
/* Wrapper around free, to avoid prototype clashes. */
void
xfree (ptr)
char *ptr;
{
free (ptr);
}
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free xfree
struct obstack scan_file_obstack;
/* NOTE: If you edit this, also edit gen-protos.c !! */
struct fn_decl *
lookup_std_proto (name, name_length)
const char *name;
int name_length;
{
int i = hashf (name, name_length, HASH_SIZE);
int i0 = i;
for (;;)
{
struct fn_decl *fn;
if (hash_tab[i] == 0)
return NULL;
fn = &std_protos[hash_tab[i]];
if ((int) strlen (fn->fname) == name_length
&& strncmp (fn->fname, name, name_length) == 0)
return fn;
i = (i+1) % HASH_SIZE;
if (i == i0)
abort ();
}
}
char *inc_filename;
int inc_filename_length;
char *progname = "fix-header";
FILE *outf;
sstring line;
int lbrac_line, rbrac_line;
int required_unseen_count = 0;
int required_other = 0;
void
write_lbrac ()
{
#if ADD_MISSING_EXTERN_C
if (missing_extern_C_count + required_unseen_count > 0)
fprintf (outf, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
#endif
if (partial_count)
{
fprintf (outf, "#ifndef _PARAMS\n");
fprintf (outf, "#if defined(__STDC__) || defined(__cplusplus)\n");
fprintf (outf, "#define _PARAMS(ARGS) ARGS\n");
fprintf (outf, "#else\n");
fprintf (outf, "#define _PARAMS(ARGS) ()\n");
fprintf (outf, "#endif\n#endif /* _PARAMS */\n");
}
}
struct partial_proto
{
struct partial_proto *next;
char *fname; /* name of function */
char *rtype; /* return type */
struct fn_decl *fn;
int line_seen;
};
struct partial_proto *partial_proto_list = NULL;
struct partial_proto required_dummy_proto, seen_dummy_proto;
#define REQUIRED(FN) ((FN)->partial == &required_dummy_proto)
#define SET_REQUIRED(FN) ((FN)->partial = &required_dummy_proto)
#define SET_SEEN(FN) ((FN)->partial = &seen_dummy_proto)
#define SEEN(FN) ((FN)->partial == &seen_dummy_proto)
void
recognized_macro (fname)
char *fname;
{
/* The original include file defines fname as a macro. */
struct fn_decl *fn = lookup_std_proto (fname, strlen (fname));
/* Since fname is a macro, don't require a prototype for it. */
if (fn)
{
if (REQUIRED (fn))
required_unseen_count--;
SET_SEEN (fn);
}
switch (special_file_handling)
{
case errno_h:
if (strcmp (fname, "errno") == 0 && !seen_errno)
seen_errno = 1, required_other--;
break;
case stdlib_h:
if (strcmp (fname, "EXIT_FAILURE") == 0 && !seen_EXIT_FAILURE)
seen_EXIT_FAILURE = 1, required_other--;
if (strcmp (fname, "EXIT_SUCCESS") == 0 && !seen_EXIT_SUCCESS)
seen_EXIT_SUCCESS = 1, required_other--;
break;
case sys_stat_h:
if (fname[0] == 'S' && fname[1] == '_')
{
if (strcmp (fname, "S_IFBLK") == 0) seen_S_IFBLK++;
else if (strcmp (fname, "S_ISBLK") == 0) seen_S_ISBLK++;
else if (strcmp (fname, "S_IFCHR") == 0) seen_S_IFCHR++;
else if (strcmp (fname, "S_ISCHR") == 0) seen_S_ISCHR++;
else if (strcmp (fname, "S_IFDIR") == 0) seen_S_IFDIR++;
else if (strcmp (fname, "S_ISDIR") == 0) seen_S_ISDIR++;
else if (strcmp (fname, "S_IFIFO") == 0) seen_S_IFIFO++;
else if (strcmp (fname, "S_ISFIFO") == 0) seen_S_ISFIFO++;
else if (strcmp (fname, "S_IFLNK") == 0) seen_S_IFLNK++;
else if (strcmp (fname, "S_ISLNK") == 0) seen_S_ISLNK++;
else if (strcmp (fname, "S_IFREG") == 0) seen_S_IFREG++;
else if (strcmp (fname, "S_ISREG") == 0) seen_S_ISREG++;
}
break;
default:
break;
}
}
void
recognized_extern (name, name_length, type, type_length)
char *name;
char *type;
int name_length, type_length;
{
switch (special_file_handling)
{
case errno_h:
if (name_length == 5 && strncmp (name, "errno", 5) == 0 && !seen_errno)
seen_errno = 1, required_other--;
break;
default:
break;
}
}
/* Called by scan_decls if it saw a function definition for a function
named FNAME, with return type RTYPE, and argument list ARGS,
in source file FILE_SEEN on line LINE_SEEN.
KIND is 'I' for an inline function;
'F' if a normal function declaration preceded by 'extern "C"'
(or nested inside 'extern "C"' braces); or
'f' for other function declarations. */
void
recognized_function (fname, fname_length,
kind, rtype, rtype_length,
have_arg_list, file_seen, line_seen)
char *fname;
int fname_length;
int kind; /* One of 'f' 'F' or 'I' */
char *rtype;
int rtype_length;
int have_arg_list;
char *file_seen;
int line_seen;
{
struct partial_proto *partial;
int i;
struct fn_decl *fn;
#if ADD_MISSING_EXTERN_C
if (kind == 'f')
missing_extern_C_count++;
#endif
fn = lookup_std_proto (fname, fname_length);
/* Remove the function from the list of required function. */
if (fn)
{
if (REQUIRED (fn))
required_unseen_count--;
SET_SEEN (fn);
}
/* If we have a full prototype, we're done. */
if (have_arg_list)
return;
if (kind == 'I') /* don't edit inline function */
return;
/* If the partial prototype was included from some other file,
we don't need to patch it up (in this run). */
i = strlen (file_seen);
if (i < inc_filename_length
|| strcmp (inc_filename, file_seen + (i - inc_filename_length)) != 0)
return;
if (fn == NULL)
return;
if (fn->params[0] == '\0' || strcmp (fn->params, "void") == 0)
return;
/* We only have a partial function declaration,
so remember that we have to add a complete prototype. */
partial_count++;
partial = (struct partial_proto *)
obstack_alloc (&scan_file_obstack, sizeof (struct partial_proto));
partial->fname = obstack_alloc (&scan_file_obstack, fname_length + 1);
bcopy (fname, partial->fname, fname_length);
partial->fname[fname_length] = 0;
partial->rtype = obstack_alloc (&scan_file_obstack, rtype_length + 1);
sprintf (partial->rtype, "%.*s", rtype_length, rtype);
partial->line_seen = line_seen;
partial->fn = fn;
fn->partial = partial;
partial->next = partial_proto_list;
partial_proto_list = partial;
if (verbose)
{
fprintf (stderr, "(%s: %s non-prototype function declaration.)\n",
inc_filename, partial->fname);
}
}
/* For any name in NAMES that is defined as a macro,
call recognized_macro on it. */
void
check_macro_names (pfile, names)
cpp_reader *pfile;
namelist names;
{
while (*names)
{
if (cpp_lookup (pfile, names, -1, -1))
recognized_macro (names);
names += strlen (names) + 1;
}
}
void
read_scan_file (in_fname, argc, argv)
char *in_fname;
int argc;
char **argv;
{
cpp_reader scan_in;
cpp_options scan_options;
struct fn_decl *fn;
int i;
register struct symbol_list *cur_symbols;
obstack_init (&scan_file_obstack);
cpp_reader_init (&scan_in);
scan_in.opts = &scan_options;
cpp_options_init (&scan_options);
i = cpp_handle_options (&scan_in, argc, argv);
if (i < argc && ! CPP_FATAL_ERRORS (&scan_in))
cpp_fatal (&scan_in, "Invalid option `%s'", argv[i]);
if (CPP_FATAL_ERRORS (&scan_in))
exit (FATAL_EXIT_CODE);
if (! cpp_start_read (&scan_in, in_fname))
exit (FATAL_EXIT_CODE);
CPP_OPTIONS (&scan_in)->no_line_commands = 1;
scan_decls (&scan_in, argc, argv);
for (cur_symbols = &symbol_table[0]; cur_symbols->names; cur_symbols++)
check_macro_names (&scan_in, cur_symbols->names);
if (verbose && (scan_in.errors + warnings) > 0)
fprintf (stderr, "(%s: %d errors and %d warnings from cpp)\n",
inc_filename, scan_in.errors, warnings);
if (scan_in.errors)
exit (SUCCESS_EXIT_CODE);
/* Traditionally, getc and putc are defined in terms of _filbuf and _flsbuf.
If so, those functions are also required. */
if (special_file_handling == stdio_h
&& (fn = lookup_std_proto ("_filbuf", 7)) != NULL)
{
static char getchar_call[] = "getchar();";
cpp_buffer *buf
= cpp_push_buffer (&scan_in, getchar_call, sizeof(getchar_call) - 1);
int old_written = CPP_WRITTEN (&scan_in);
int seen_filbuf = 0;
/* Scan the macro expansion of "getchar();". */
for (;;)
{
enum cpp_token token = cpp_get_token (&scan_in);
int length = CPP_WRITTEN (&scan_in) - old_written;
CPP_SET_WRITTEN (&scan_in, old_written);
if (token == CPP_EOF) /* Should not happen ... */
break;
if (token == CPP_POP && CPP_BUFFER (&scan_in) == buf)
{
cpp_pop_buffer (&scan_in);
break;
}
if (token == CPP_NAME && length == 7
&& strcmp ("_filbuf", scan_in.token_buffer + old_written) == 0)
seen_filbuf++;
}
if (seen_filbuf)
{
int need_filbuf = !SEEN (fn) && !REQUIRED (fn);
struct fn_decl *flsbuf_fn = lookup_std_proto ("_flsbuf", 7);
int need_flsbuf
= flsbuf_fn && !SEEN (flsbuf_fn) && !REQUIRED (flsbuf_fn);
/* Append "_filbuf" and/or "_flsbuf" to the required functions. */
if (need_filbuf + need_flsbuf)
{
const char *new_list;
if (need_filbuf)
SET_REQUIRED (fn);
if (need_flsbuf)
SET_REQUIRED (flsbuf_fn);
if (need_flsbuf + need_filbuf == 2)
new_list = "_filbuf\0_flsbuf\0";
else if (need_flsbuf)
new_list = "_flsbuf\0";
else /* if (need_flsbuf) */
new_list = "_filbuf\0";
add_symbols (ANSI_SYMBOL, new_list);
required_unseen_count += need_filbuf + need_flsbuf;
}
}
}
if (required_unseen_count + partial_count + required_other
#if ADD_MISSING_EXTERN_C
+ missing_extern_C_count
#endif
== 0)
{
if (verbose)
fprintf (stderr, "%s: OK, nothing needs to be done.\n", inc_filename);
exit (SUCCESS_EXIT_CODE);
}
if (!verbose)
fprintf (stderr, "%s: fixing %s\n", progname, inc_filename);
else
{
if (required_unseen_count)
fprintf (stderr, "%s: %d missing function declarations.\n",
inc_filename, required_unseen_count);
if (partial_count)
fprintf (stderr, "%s: %d non-prototype function declarations.\n",
inc_filename, partial_count);
#if ADD_MISSING_EXTERN_C
if (missing_extern_C_count)
fprintf (stderr,
"%s: %d declarations not protected by extern \"C\".\n",
inc_filename, missing_extern_C_count);
#endif
}
}
void
write_rbrac ()
{
struct fn_decl *fn;
const char *cptr;
register struct symbol_list *cur_symbols;
if (required_unseen_count)
{
#ifdef NO_IMPLICIT_EXTERN_C
fprintf (outf, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
#endif
}
/* Now we print out prototypes for those functions that we haven't seen. */
for (cur_symbols = &symbol_table[0]; cur_symbols->names; cur_symbols++)
{
int if_was_emitted = 0;
int name_len;
cptr = cur_symbols->names;
for ( ; (name_len = strlen (cptr)) != 0; cptr+= name_len + 1)
{
int macro_protect = 0;
if (cur_symbols->flags & MACRO_SYMBOL)
continue;
fn = lookup_std_proto (cptr, name_len);
if (fn == NULL || !REQUIRED (fn))
continue;
if (!if_was_emitted)
{
/* what about curses. ??? or _flsbuf/_filbuf ??? */
if (cur_symbols->flags & ANSI_SYMBOL)
fprintf (outf,
"#if defined(__USE_FIXED_PROTOTYPES__) || defined(__cplusplus) || defined (__STRICT_ANSI__)\n");
else if (cur_symbols->flags & (POSIX1_SYMBOL|POSIX2_SYMBOL))
fprintf (outf,
"#if defined(__USE_FIXED_PROTOTYPES__) || (defined(__cplusplus) \\\n\
? (!defined(__STRICT_ANSI__) || defined(_POSIX_SOURCE)) \\\n\
: (defined(__STRICT_ANSI__) && defined(_POSIX_SOURCE)))\n");
else if (cur_symbols->flags & XOPEN_SYMBOL)
{
fprintf (outf,
"#if defined(__USE_FIXED_PROTOTYPES__) \\\n\
|| (defined(__STRICT_ANSI__) && defined(_XOPEN_SOURCE))\n");
}
else if (cur_symbols->flags & XOPEN_EXTENDED_SYMBOL)
{
fprintf (outf,
"#if defined(__USE_FIXED_PROTOTYPES__) \\\n\
|| (defined(__STRICT_ANSI__) && defined(_XOPEN_EXTENDED_SOURCE))\n");
}
else
{
fatal ("internal error for function %s", fn->fname);
}
if_was_emitted = 1;
}
/* In the case of memmove, protect in case the application
defines it as a macro before including the header. */
if (!strcmp (fn->fname, "memmove")
|| !strcmp (fn->fname, "vprintf")
|| !strcmp (fn->fname, "vfprintf")
|| !strcmp (fn->fname, "vsprintf")
|| !strcmp (fn->fname, "rewinddir")
|| !strcmp (fn->fname, "abort"))
macro_protect = 1;
if (macro_protect)
fprintf (outf, "#ifndef %s\n", fn->fname);
fprintf (outf, "extern %s %s (%s);\n",
fn->rtype, fn->fname, fn->params);
if (macro_protect)
fprintf (outf, "#endif\n");
}
if (if_was_emitted)
fprintf (outf,
"#endif /* defined(__USE_FIXED_PROTOTYPES__) || ... */\n");
}
if (required_unseen_count)
{
#ifdef NO_IMPLICIT_EXTERN_C
fprintf (outf, "#ifdef __cplusplus\n}\n#endif\n");
#endif
}
switch (special_file_handling)
{
case errno_h:
if (!seen_errno)
fprintf (outf, "extern int errno;\n");
break;
case stdlib_h:
if (!seen_EXIT_FAILURE)
fprintf (outf, "#define EXIT_FAILURE 1\n");
if (!seen_EXIT_SUCCESS)
fprintf (outf, "#define EXIT_SUCCESS 0\n");
break;
case sys_stat_h:
if (!seen_S_ISBLK && seen_S_IFBLK)
fprintf (outf,
"#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)\n");
if (!seen_S_ISCHR && seen_S_IFCHR)
fprintf (outf,
"#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)\n");
if (!seen_S_ISDIR && seen_S_IFDIR)
fprintf (outf,
"#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)\n");
if (!seen_S_ISFIFO && seen_S_IFIFO)
fprintf (outf,
"#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)\n");
if (!seen_S_ISLNK && seen_S_IFLNK)
fprintf (outf,
"#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)\n");
if (!seen_S_ISREG && seen_S_IFREG)
fprintf (outf,
"#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)\n");
break;
default:
break;
}
#if ADD_MISSING_EXTERN_C
if (missing_extern_C_count + required_unseen_count > 0)
fprintf (outf, "#ifdef __cplusplus\n}\n#endif\n");
#endif
}
/* Returns 1 iff the file is properly protected from multiple inclusion:
#ifndef PROTECT_NAME
#define PROTECT_NAME
#endif
*/
#define INF_GET() (inf_ptr < inf_limit ? *(unsigned char *) inf_ptr++ : EOF)
#define INF_UNGET(c) ((c)!=EOF && inf_ptr--)
int
inf_skip_spaces (c)
int c;
{
for (;;)
{
if (c == ' ' || c == '\t')
c = INF_GET ();
else if (c == '/')
{
c = INF_GET ();
if (c != '*')
{
(void) INF_UNGET (c);
return '/';
}
c = INF_GET ();
for (;;)
{
if (c == EOF)
return EOF;
else if (c != '*')
{
if (c == '\n')
source_lineno++, lineno++;
c = INF_GET ();
}
else if ((c = INF_GET ()) == '/')
return INF_GET ();
}
}
else
break;
}
return c;
}
/* Read into STR from inf_buffer upto DELIM. */
int
inf_read_upto (str, delim)
sstring *str;
int delim;
{
int ch;
for (;;)
{
ch = INF_GET ();
if (ch == EOF || ch == delim)
break;
SSTRING_PUT (str, ch);
}
MAKE_SSTRING_SPACE (str, 1);
*str->ptr = 0;
return ch;
}
int
inf_scan_ident (s, c)
register sstring *s;
int c;
{
s->ptr = s->base;
if (ISALPHA (c) || c == '_')
{
for (;;)
{
SSTRING_PUT (s, c);
c = INF_GET ();
if (c == EOF || !(ISALNUM (c) || c == '_'))
break;
}
}
MAKE_SSTRING_SPACE (s, 1);
*s->ptr = 0;
return c;
}
/* Returns 1 if the file is correctly protected against multiple
inclusion, setting *ifndef_line to the line number of the initial #ifndef
and setting *endif_line to the final #endif.
Otherwise return 0. */
int
check_protection (ifndef_line, endif_line)
int *ifndef_line, *endif_line;
{
int c;
int if_nesting = 1; /* Level of nesting of #if's */
char *protect_name = NULL; /* Identifier following initial #ifndef */
int define_seen = 0;
/* Skip initial white space (including comments). */
for (;; lineno++)
{
c = inf_skip_spaces (' ');
if (c == EOF)
return 0;
if (c != '\n')
break;
}
if (c != '#')
return 0;
c = inf_scan_ident (&buf, inf_skip_spaces (' '));
if (SSTRING_LENGTH (&buf) == 0 || strcmp (buf.base, "ifndef") != 0)
return 0;
/* So far so good: We've seen an initial #ifndef. */
*ifndef_line = lineno;
c = inf_scan_ident (&buf, inf_skip_spaces (c));
if (SSTRING_LENGTH (&buf) == 0 || c == EOF)
return 0;
protect_name = xstrdup (buf.base);
(void) INF_UNGET (c);
c = inf_read_upto (&buf, '\n');
if (c == EOF)
return 0;
lineno++;
for (;;)
{
c = inf_skip_spaces (' ');
if (c == EOF)
return 0;
if (c == '\n')
{
lineno++;
continue;
}
if (c != '#')
goto skip_to_eol;
c = inf_scan_ident (&buf, inf_skip_spaces (' '));
if (SSTRING_LENGTH (&buf) == 0)
;
else if (!strcmp (buf.base, "ifndef")
|| !strcmp (buf.base, "ifdef") || !strcmp (buf.base, "if"))
{
if_nesting++;
}
else if (!strcmp (buf.base, "endif"))
{
if_nesting--;
if (if_nesting == 0)
break;
}
else if (!strcmp (buf.base, "else"))
{
if (if_nesting == 1)
return 0;
}
else if (!strcmp (buf.base, "define"))
{
if (if_nesting != 1)
goto skip_to_eol;
c = inf_skip_spaces (c);
c = inf_scan_ident (&buf, c);
if (buf.base[0] > 0 && strcmp (buf.base, protect_name) == 0)
define_seen = 1;
}
skip_to_eol:
for (;;)
{
if (c == '\n' || c == EOF)
break;
c = INF_GET ();
}
if (c == EOF)
return 0;
lineno++;
}
if (!define_seen)
return 0;
*endif_line = lineno;
/* Skip final white space (including comments). */
for (;;)
{
c = inf_skip_spaces (' ');
if (c == EOF)
break;
if (c != '\n')
return 0;
}
return 1;
}
int
main (argc, argv)
int argc;
char **argv;
{
int inf_fd;
struct stat sbuf;
int c;
#ifdef FIXPROTO_IGNORE_LIST
int i;
#endif
const char *cptr;
int ifndef_line;
int endif_line;
long to_read;
long int inf_size;
register struct symbol_list *cur_symbols;
if (argv[0] && argv[0][0])
{
register char *p;
progname = 0;
for (p = argv[0]; *p; p++)
if (*p == '/')
progname = p;
progname = progname ? progname+1 : argv[0];
}
if (argc < 4)
{
fprintf (stderr, "%s: Usage: foo.h infile.h outfile.h options\n",
progname);
exit (FATAL_EXIT_CODE);
}
inc_filename = argv[1];
inc_filename_length = strlen (inc_filename);
#ifdef FIXPROTO_IGNORE_LIST
for (i = 0; files_to_ignore[i] != NULL; i++)
{
char *ignore_name = files_to_ignore[i];
int ignore_len = strlen (ignore_name);
if (strncmp (inc_filename, ignore_name, ignore_len) == 0)
{
if (ignore_name[ignore_len-1] == '/'
|| inc_filename[ignore_len] == '\0')
{
if (verbose)
fprintf (stderr, "%s: ignoring %s\n", progname, inc_filename);
exit (SUCCESS_EXIT_CODE);
}
}
}
#endif
if (strcmp (inc_filename, "sys/stat.h") == 0)
special_file_handling = sys_stat_h;
else if (strcmp (inc_filename, "errno.h") == 0)
special_file_handling = errno_h, required_other++;
else if (strcmp (inc_filename, "stdlib.h") == 0)
special_file_handling = stdlib_h, required_other+=2;
else if (strcmp (inc_filename, "stdio.h") == 0)
special_file_handling = stdio_h;
include_entry = std_include_table;
while (include_entry->name != NULL
&& ((strcmp (include_entry->name, CONTINUED) == 0)
|| strcmp (inc_filename, include_entry->name) != 0))
include_entry++;
if (include_entry->name != NULL)
{
struct std_include_entry *entry;
cur_symbol_table_size = 0;
for (entry = include_entry; ;)
{
if (entry->flags)
add_symbols (entry->flags, entry->names);
entry++;
- if (strcmp (entry->name, CONTINUED) != 0)
+ if (!entry->name || strcmp (entry->name, CONTINUED) != 0)
break;
}
}
else
symbol_table[0].names = NULL;
/* Count and mark the prototypes required for this include file. */
for (cur_symbols = &symbol_table[0]; cur_symbols->names; cur_symbols++)
{
int name_len;
if (cur_symbols->flags & MACRO_SYMBOL)
continue;
cptr = cur_symbols->names;
for ( ; (name_len = strlen (cptr)) != 0; cptr+= name_len + 1)
{
struct fn_decl *fn = lookup_std_proto (cptr, name_len);
required_unseen_count++;
if (fn == NULL)
fprintf (stderr, "Internal error: No prototype for %s\n", cptr);
else
SET_REQUIRED (fn);
}
}
read_scan_file (argv[2], argc - 4, argv + 4);
inf_fd = open (argv[2], O_RDONLY, 0666);
if (inf_fd < 0)
{
fprintf (stderr, "%s: Cannot open '%s' for reading -",
progname, argv[2]);
perror (NULL);
exit (FATAL_EXIT_CODE);
}
if (fstat (inf_fd, &sbuf) < 0)
{
fprintf (stderr, "%s: Cannot get size of '%s' -", progname, argv[2]);
perror (NULL);
exit (FATAL_EXIT_CODE);
}
inf_size = sbuf.st_size;
inf_buffer = (char *) xmalloc (inf_size + 2);
inf_buffer[inf_size] = '\n';
inf_buffer[inf_size + 1] = '\0';
inf_limit = inf_buffer + inf_size;
inf_ptr = inf_buffer;
to_read = inf_size;
while (to_read > 0)
{
long i = read (inf_fd, inf_buffer + inf_size - to_read, to_read);
if (i < 0)
{
fprintf (stderr, "%s: Failed to read '%s' -", progname, argv[2]);
perror (NULL);
exit (FATAL_EXIT_CODE);
}
if (i == 0)
{
inf_size -= to_read;
break;
}
to_read -= i;
}
close (inf_fd);
/* If file doesn't end with '\n', add one. */
if (inf_limit > inf_buffer && inf_limit[-1] != '\n')
inf_limit++;
unlink (argv[3]);
outf = fopen (argv[3], "w");
if (outf == NULL)
{
fprintf (stderr, "%s: Cannot open '%s' for writing -",
progname, argv[3]);
perror (NULL);
exit (FATAL_EXIT_CODE);
}
lineno = 1;
if (check_protection (&ifndef_line, &endif_line))
{
lbrac_line = ifndef_line+1;
rbrac_line = endif_line;
}
else
{
lbrac_line = 1;
rbrac_line = -1;
}
/* Reset input file. */
inf_ptr = inf_buffer;
lineno = 1;
for (;;)
{
if (lineno == lbrac_line)
write_lbrac ();
if (lineno == rbrac_line)
write_rbrac ();
for (;;)
{
struct fn_decl *fn;
c = INF_GET ();
if (c == EOF)
break;
if (ISALPHA (c) || c == '_')
{
c = inf_scan_ident (&buf, c);
(void) INF_UNGET (c);
fputs (buf.base, outf);
fn = lookup_std_proto (buf.base, strlen (buf.base));
/* We only want to edit the declaration matching the one
seen by scan-decls, as there can be multiple
declarations, selected by #ifdef __STDC__ or whatever. */
if (fn && fn->partial && fn->partial->line_seen == lineno)
{
c = inf_skip_spaces (' ');
if (c == EOF)
break;
if (c == '(')
{
c = inf_skip_spaces (' ');
if (c == ')')
{
fprintf (outf, " _PARAMS((%s))", fn->params);
}
else
{
putc ('(', outf);
(void) INF_UNGET (c);
}
}
else
fprintf (outf, " %c", c);
}
}
else
{
putc (c, outf);
if (c == '\n')
break;
}
}
if (c == EOF)
break;
lineno++;
}
if (rbrac_line < 0)
write_rbrac ();
fclose (outf);
return 0;
}
static void
v_fatal (str, ap)
const char * str;
va_list ap;
{
fprintf (stderr, "%s: %s: ", progname, inc_filename);
vfprintf (stderr, str, ap);
fprintf (stderr, "\n");
exit (FATAL_EXIT_CODE);
}
void
fatal VPROTO ((const char *str, ...))
{
#ifndef ANSI_PROTOTYPES
const char *str;
#endif
va_list ap;
VA_START(ap, str);
#ifndef ANSI_PROTOTYPES
str = va_arg (ap, const char *);
#endif
v_fatal(str, ap);
va_end(ap);
}
Index: head/contrib/gcc/fold-const.c
===================================================================
--- head/contrib/gcc/fold-const.c (revision 52750)
+++ head/contrib/gcc/fold-const.c (revision 52751)
@@ -1,6613 +1,6619 @@
/* Fold a constant sub-tree into a single node for C-compiler
Copyright (C) 1987, 88, 92-98, 1999 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 should be rewritten to use an arbitrary precision
@@ representation for "struct tree_int_cst" and "struct tree_real_cst".
@@ Perhaps the routines could also be used for bc/dc, and made a lib.
@@ The routines that translate from the ap rep should
@@ warn if precision et. al. is lost.
@@ This would also make life easier when this technology is used
@@ for cross-compilers. */
/* The entry points in this file are fold, size_int_wide, size_binop
and force_fit_type.
fold takes a tree as argument and returns a simplified tree.
size_binop takes a tree code for an arithmetic operation
and two operands that are trees, and produces a tree for the
result, assuming the type comes from `sizetype'.
size_int takes an integer value, and creates a tree constant
with type from `sizetype'.
force_fit_type takes a constant and prior overflow indicator, and
forces the value to fit the type. It returns an overflow indicator. */
#include "config.h"
#include "system.h"
#include <setjmp.h>
#include "flags.h"
#include "tree.h"
#include "rtl.h"
#include "toplev.h"
static void encode PROTO((HOST_WIDE_INT *,
HOST_WIDE_INT, HOST_WIDE_INT));
static void decode PROTO((HOST_WIDE_INT *,
HOST_WIDE_INT *, HOST_WIDE_INT *));
int div_and_round_double PROTO((enum tree_code, int, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT *,
HOST_WIDE_INT *, HOST_WIDE_INT *,
HOST_WIDE_INT *));
static int split_tree PROTO((tree, enum tree_code, tree *,
tree *, int *));
static tree int_const_binop PROTO((enum tree_code, tree, tree, int, int));
static tree const_binop PROTO((enum tree_code, tree, tree, int));
static tree fold_convert PROTO((tree, tree));
static enum tree_code invert_tree_comparison PROTO((enum tree_code));
static enum tree_code swap_tree_comparison PROTO((enum tree_code));
static int truth_value_p PROTO((enum tree_code));
static int operand_equal_for_comparison_p PROTO((tree, tree, tree));
static int twoval_comparison_p PROTO((tree, tree *, tree *, int *));
static tree eval_subst PROTO((tree, tree, tree, tree, tree));
static tree omit_one_operand PROTO((tree, tree, tree));
static tree pedantic_omit_one_operand PROTO((tree, tree, tree));
static tree distribute_bit_expr PROTO((enum tree_code, tree, tree, tree));
static tree make_bit_field_ref PROTO((tree, tree, int, int, int));
static tree optimize_bit_field_compare PROTO((enum tree_code, tree,
tree, tree));
static tree decode_field_reference PROTO((tree, int *, int *,
enum machine_mode *, int *,
int *, tree *, tree *));
static int all_ones_mask_p PROTO((tree, int));
static int simple_operand_p PROTO((tree));
static tree range_binop PROTO((enum tree_code, tree, tree, int,
tree, int));
static tree make_range PROTO((tree, int *, tree *, tree *));
static tree build_range_check PROTO((tree, tree, int, tree, tree));
static int merge_ranges PROTO((int *, tree *, tree *, int, tree, tree,
int, tree, tree));
static tree fold_range_test PROTO((tree));
static tree unextend PROTO((tree, int, int, tree));
static tree fold_truthop PROTO((enum tree_code, tree, tree, tree));
static tree strip_compound_expr PROTO((tree, tree));
static int multiple_of_p PROTO((tree, tree, tree));
static tree constant_boolean_node PROTO((int, tree));
static int count_cond PROTO((tree, int));
static void const_binop_1 PROTO((PTR));
static void fold_convert_1 PROTO((PTR));
#ifndef BRANCH_COST
#define BRANCH_COST 1
#endif
/* Suppose A1 + B1 = SUM1, using 2's complement arithmetic ignoring overflow.
Suppose A, B and SUM have the same respective signs as A1, B1, and SUM1.
Then this yields nonzero if overflow occurred during the addition.
Overflow occurs if A and B have the same sign, but A and SUM differ in sign.
Use `^' to test whether signs differ, and `< 0' to isolate the sign. */
#define overflow_sum_sign(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)
/* To do constant folding on INTEGER_CST nodes requires two-word arithmetic.
We do that by representing the two-word integer in 4 words, with only
HOST_BITS_PER_WIDE_INT/2 bits stored in each word, as a positive number. */
#define LOWPART(x) \
((x) & (((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT/2)) - 1))
#define HIGHPART(x) \
((unsigned HOST_WIDE_INT) (x) >> HOST_BITS_PER_WIDE_INT/2)
#define BASE ((unsigned HOST_WIDE_INT) 1 << HOST_BITS_PER_WIDE_INT/2)
/* Unpack a two-word integer into 4 words.
LOW and HI are the integer, as two `HOST_WIDE_INT' pieces.
WORDS points to the array of HOST_WIDE_INTs. */
static void
encode (words, low, hi)
HOST_WIDE_INT *words;
HOST_WIDE_INT low, hi;
{
words[0] = LOWPART (low);
words[1] = HIGHPART (low);
words[2] = LOWPART (hi);
words[3] = HIGHPART (hi);
}
/* Pack an array of 4 words into a two-word integer.
WORDS points to the array of words.
The integer is stored into *LOW and *HI as two `HOST_WIDE_INT' pieces. */
static void
decode (words, low, hi)
HOST_WIDE_INT *words;
HOST_WIDE_INT *low, *hi;
{
*low = words[0] | words[1] * BASE;
*hi = words[2] | words[3] * BASE;
}
/* Make the integer constant T valid for its type
by setting to 0 or 1 all the bits in the constant
that don't belong in the type.
Yield 1 if a signed overflow occurs, 0 otherwise.
If OVERFLOW is nonzero, a signed overflow has already occurred
in calculating T, so propagate it.
Make the real constant T valid for its type by calling CHECK_FLOAT_VALUE,
if it exists. */
int
force_fit_type (t, overflow)
tree t;
int overflow;
{
HOST_WIDE_INT low, high;
register int prec;
if (TREE_CODE (t) == REAL_CST)
{
#ifdef CHECK_FLOAT_VALUE
CHECK_FLOAT_VALUE (TYPE_MODE (TREE_TYPE (t)), TREE_REAL_CST (t),
overflow);
#endif
return overflow;
}
else if (TREE_CODE (t) != INTEGER_CST)
return overflow;
low = TREE_INT_CST_LOW (t);
high = TREE_INT_CST_HIGH (t);
if (POINTER_TYPE_P (TREE_TYPE (t)))
prec = POINTER_SIZE;
else
prec = TYPE_PRECISION (TREE_TYPE (t));
/* First clear all bits that are beyond the type's precision. */
if (prec == 2 * HOST_BITS_PER_WIDE_INT)
;
else if (prec > HOST_BITS_PER_WIDE_INT)
{
TREE_INT_CST_HIGH (t)
&= ~((HOST_WIDE_INT) (-1) << (prec - HOST_BITS_PER_WIDE_INT));
}
else
{
TREE_INT_CST_HIGH (t) = 0;
if (prec < HOST_BITS_PER_WIDE_INT)
TREE_INT_CST_LOW (t) &= ~((HOST_WIDE_INT) (-1) << prec);
}
/* Unsigned types do not suffer sign extension or overflow. */
if (TREE_UNSIGNED (TREE_TYPE (t)))
return overflow;
/* If the value's sign bit is set, extend the sign. */
if (prec != 2 * HOST_BITS_PER_WIDE_INT
&& (prec > HOST_BITS_PER_WIDE_INT
? (TREE_INT_CST_HIGH (t)
& ((HOST_WIDE_INT) 1 << (prec - HOST_BITS_PER_WIDE_INT - 1)))
: TREE_INT_CST_LOW (t) & ((HOST_WIDE_INT) 1 << (prec - 1))))
{
/* Value is negative:
set to 1 all the bits that are outside this type's precision. */
if (prec > HOST_BITS_PER_WIDE_INT)
{
TREE_INT_CST_HIGH (t)
|= ((HOST_WIDE_INT) (-1) << (prec - HOST_BITS_PER_WIDE_INT));
}
else
{
TREE_INT_CST_HIGH (t) = -1;
if (prec < HOST_BITS_PER_WIDE_INT)
TREE_INT_CST_LOW (t) |= ((HOST_WIDE_INT) (-1) << prec);
}
}
/* Yield nonzero if signed overflow occurred. */
return
((overflow | (low ^ TREE_INT_CST_LOW (t)) | (high ^ TREE_INT_CST_HIGH (t)))
!= 0);
}
/* Add two doubleword integers with doubleword result.
Each argument is given as two `HOST_WIDE_INT' pieces.
One argument is L1 and H1; the other, L2 and H2.
The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
int
add_double (l1, h1, l2, h2, lv, hv)
HOST_WIDE_INT l1, h1, l2, h2;
HOST_WIDE_INT *lv, *hv;
{
HOST_WIDE_INT l, h;
l = l1 + l2;
h = h1 + h2 + ((unsigned HOST_WIDE_INT) l < l1);
*lv = l;
*hv = h;
return overflow_sum_sign (h1, h2, h);
}
/* Negate a doubleword integer with doubleword result.
Return nonzero if the operation overflows, assuming it's signed.
The argument is given as two `HOST_WIDE_INT' pieces in L1 and H1.
The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
int
neg_double (l1, h1, lv, hv)
HOST_WIDE_INT l1, h1;
HOST_WIDE_INT *lv, *hv;
{
if (l1 == 0)
{
*lv = 0;
*hv = - h1;
return (*hv & h1) < 0;
}
else
{
*lv = - l1;
*hv = ~ h1;
return 0;
}
}
/* Multiply two doubleword integers with doubleword result.
Return nonzero if the operation overflows, assuming it's signed.
Each argument is given as two `HOST_WIDE_INT' pieces.
One argument is L1 and H1; the other, L2 and H2.
The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
int
mul_double (l1, h1, l2, h2, lv, hv)
HOST_WIDE_INT l1, h1, l2, h2;
HOST_WIDE_INT *lv, *hv;
{
HOST_WIDE_INT arg1[4];
HOST_WIDE_INT arg2[4];
HOST_WIDE_INT prod[4 * 2];
register unsigned HOST_WIDE_INT carry;
register int i, j, k;
HOST_WIDE_INT toplow, tophigh, neglow, neghigh;
encode (arg1, l1, h1);
encode (arg2, l2, h2);
bzero ((char *) prod, sizeof prod);
for (i = 0; i < 4; i++)
{
carry = 0;
for (j = 0; j < 4; j++)
{
k = i + j;
/* This product is <= 0xFFFE0001, the sum <= 0xFFFF0000. */
carry += arg1[i] * arg2[j];
/* Since prod[p] < 0xFFFF, this sum <= 0xFFFFFFFF. */
carry += prod[k];
prod[k] = LOWPART (carry);
carry = HIGHPART (carry);
}
prod[i + 4] = carry;
}
decode (prod, lv, hv); /* This ignores prod[4] through prod[4*2-1] */
/* Check for overflow by calculating the top half of the answer in full;
it should agree with the low half's sign bit. */
decode (prod+4, &toplow, &tophigh);
if (h1 < 0)
{
neg_double (l2, h2, &neglow, &neghigh);
add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
}
if (h2 < 0)
{
neg_double (l1, h1, &neglow, &neghigh);
add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
}
return (*hv < 0 ? ~(toplow & tophigh) : toplow | tophigh) != 0;
}
/* Shift the doubleword integer in L1, H1 left by COUNT places
keeping only PREC bits of result.
Shift right if COUNT is negative.
ARITH nonzero specifies arithmetic shifting; otherwise use logical shift.
Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
void
lshift_double (l1, h1, count, prec, lv, hv, arith)
HOST_WIDE_INT l1, h1, count;
int prec;
HOST_WIDE_INT *lv, *hv;
int arith;
{
if (count < 0)
{
rshift_double (l1, h1, - count, prec, lv, hv, arith);
return;
}
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
count %= prec;
#endif
if (count >= HOST_BITS_PER_WIDE_INT)
{
*hv = (unsigned HOST_WIDE_INT) l1 << (count - HOST_BITS_PER_WIDE_INT);
*lv = 0;
}
else
{
*hv = (((unsigned HOST_WIDE_INT) h1 << count)
| ((unsigned HOST_WIDE_INT) l1 >> (HOST_BITS_PER_WIDE_INT - count - 1) >> 1));
*lv = (unsigned HOST_WIDE_INT) l1 << count;
}
}
/* Shift the doubleword integer in L1, H1 right by COUNT places
keeping only PREC bits of result. COUNT must be positive.
ARITH nonzero specifies arithmetic shifting; otherwise use logical shift.
Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
void
rshift_double (l1, h1, count, prec, lv, hv, arith)
HOST_WIDE_INT l1, h1, count;
int prec ATTRIBUTE_UNUSED;
HOST_WIDE_INT *lv, *hv;
int arith;
{
unsigned HOST_WIDE_INT signmask;
signmask = (arith
? -((unsigned HOST_WIDE_INT) h1 >> (HOST_BITS_PER_WIDE_INT - 1))
: 0);
#ifdef SHIFT_COUNT_TRUNCATED
if (SHIFT_COUNT_TRUNCATED)
count %= prec;
#endif
if (count >= HOST_BITS_PER_WIDE_INT)
{
*hv = signmask;
*lv = ((signmask << (2 * HOST_BITS_PER_WIDE_INT - count - 1) << 1)
| ((unsigned HOST_WIDE_INT) h1 >> (count - HOST_BITS_PER_WIDE_INT)));
}
else
{
*lv = (((unsigned HOST_WIDE_INT) l1 >> count)
| ((unsigned HOST_WIDE_INT) h1 << (HOST_BITS_PER_WIDE_INT - count - 1) << 1));
*hv = ((signmask << (HOST_BITS_PER_WIDE_INT - count))
| ((unsigned HOST_WIDE_INT) h1 >> count));
}
}
/* Rotate the doubleword integer in L1, H1 left by COUNT places
keeping only PREC bits of result.
Rotate right if COUNT is negative.
Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
void
lrotate_double (l1, h1, count, prec, lv, hv)
HOST_WIDE_INT l1, h1, count;
int prec;
HOST_WIDE_INT *lv, *hv;
{
HOST_WIDE_INT s1l, s1h, s2l, s2h;
count %= prec;
if (count < 0)
count += prec;
lshift_double (l1, h1, count, prec, &s1l, &s1h, 0);
rshift_double (l1, h1, prec - count, prec, &s2l, &s2h, 0);
*lv = s1l | s2l;
*hv = s1h | s2h;
}
/* Rotate the doubleword integer in L1, H1 left by COUNT places
keeping only PREC bits of result. COUNT must be positive.
Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
void
rrotate_double (l1, h1, count, prec, lv, hv)
HOST_WIDE_INT l1, h1, count;
int prec;
HOST_WIDE_INT *lv, *hv;
{
HOST_WIDE_INT s1l, s1h, s2l, s2h;
count %= prec;
if (count < 0)
count += prec;
rshift_double (l1, h1, count, prec, &s1l, &s1h, 0);
lshift_double (l1, h1, prec - count, prec, &s2l, &s2h, 0);
*lv = s1l | s2l;
*hv = s1h | s2h;
}
/* Divide doubleword integer LNUM, HNUM by doubleword integer LDEN, HDEN
for a quotient (stored in *LQUO, *HQUO) and remainder (in *LREM, *HREM).
CODE is a tree code for a kind of division, one of
TRUNC_DIV_EXPR, FLOOR_DIV_EXPR, CEIL_DIV_EXPR, ROUND_DIV_EXPR
or EXACT_DIV_EXPR
It controls how the quotient is rounded to a integer.
Return nonzero if the operation overflows.
UNS nonzero says do unsigned division. */
int
div_and_round_double (code, uns,
lnum_orig, hnum_orig, lden_orig, hden_orig,
lquo, hquo, lrem, hrem)
enum tree_code code;
int uns;
HOST_WIDE_INT lnum_orig, hnum_orig; /* num == numerator == dividend */
HOST_WIDE_INT lden_orig, hden_orig; /* den == denominator == divisor */
HOST_WIDE_INT *lquo, *hquo, *lrem, *hrem;
{
int quo_neg = 0;
HOST_WIDE_INT num[4 + 1]; /* extra element for scaling. */
HOST_WIDE_INT den[4], quo[4];
register int i, j;
unsigned HOST_WIDE_INT work;
register unsigned HOST_WIDE_INT carry = 0;
HOST_WIDE_INT lnum = lnum_orig;
HOST_WIDE_INT hnum = hnum_orig;
HOST_WIDE_INT lden = lden_orig;
HOST_WIDE_INT hden = hden_orig;
int overflow = 0;
if ((hden == 0) && (lden == 0))
overflow = 1, lden = 1;
/* calculate quotient sign and convert operands to unsigned. */
if (!uns)
{
if (hnum < 0)
{
quo_neg = ~ quo_neg;
/* (minimum integer) / (-1) is the only overflow case. */
if (neg_double (lnum, hnum, &lnum, &hnum) && (lden & hden) == -1)
overflow = 1;
}
if (hden < 0)
{
quo_neg = ~ quo_neg;
neg_double (lden, hden, &lden, &hden);
}
}
if (hnum == 0 && hden == 0)
{ /* single precision */
*hquo = *hrem = 0;
/* This unsigned division rounds toward zero. */
*lquo = lnum / (unsigned HOST_WIDE_INT) lden;
goto finish_up;
}
if (hnum == 0)
{ /* trivial case: dividend < divisor */
/* hden != 0 already checked. */
*hquo = *lquo = 0;
*hrem = hnum;
*lrem = lnum;
goto finish_up;
}
bzero ((char *) quo, sizeof quo);
bzero ((char *) num, sizeof num); /* to zero 9th element */
bzero ((char *) den, sizeof den);
encode (num, lnum, hnum);
encode (den, lden, hden);
/* Special code for when the divisor < BASE. */
if (hden == 0 && lden < (HOST_WIDE_INT) BASE)
{
/* hnum != 0 already checked. */
for (i = 4 - 1; i >= 0; i--)
{
work = num[i] + carry * BASE;
quo[i] = work / (unsigned HOST_WIDE_INT) lden;
carry = work % (unsigned HOST_WIDE_INT) lden;
}
}
else
{
/* Full double precision division,
with thanks to Don Knuth's "Seminumerical Algorithms". */
int num_hi_sig, den_hi_sig;
unsigned HOST_WIDE_INT quo_est, scale;
/* Find the highest non-zero divisor digit. */
for (i = 4 - 1; ; i--)
if (den[i] != 0) {
den_hi_sig = i;
break;
}
/* Insure that the first digit of the divisor is at least BASE/2.
This is required by the quotient digit estimation algorithm. */
scale = BASE / (den[den_hi_sig] + 1);
if (scale > 1) { /* scale divisor and dividend */
carry = 0;
for (i = 0; i <= 4 - 1; i++) {
work = (num[i] * scale) + carry;
num[i] = LOWPART (work);
carry = HIGHPART (work);
} num[4] = carry;
carry = 0;
for (i = 0; i <= 4 - 1; i++) {
work = (den[i] * scale) + carry;
den[i] = LOWPART (work);
carry = HIGHPART (work);
if (den[i] != 0) den_hi_sig = i;
}
}
num_hi_sig = 4;
/* Main loop */
for (i = num_hi_sig - den_hi_sig - 1; i >= 0; i--) {
/* guess the next quotient digit, quo_est, by dividing the first
two remaining dividend digits by the high order quotient digit.
quo_est is never low and is at most 2 high. */
unsigned HOST_WIDE_INT tmp;
num_hi_sig = i + den_hi_sig + 1;
work = num[num_hi_sig] * BASE + num[num_hi_sig - 1];
if (num[num_hi_sig] != den[den_hi_sig])
quo_est = work / den[den_hi_sig];
else
quo_est = BASE - 1;
/* refine quo_est so it's usually correct, and at most one high. */
tmp = work - quo_est * den[den_hi_sig];
if (tmp < BASE
&& den[den_hi_sig - 1] * quo_est > (tmp * BASE + num[num_hi_sig - 2]))
quo_est--;
/* Try QUO_EST as the quotient digit, by multiplying the
divisor by QUO_EST and subtracting from the remaining dividend.
Keep in mind that QUO_EST is the I - 1st digit. */
carry = 0;
for (j = 0; j <= den_hi_sig; j++)
{
work = quo_est * den[j] + carry;
carry = HIGHPART (work);
work = num[i + j] - LOWPART (work);
num[i + j] = LOWPART (work);
carry += HIGHPART (work) != 0;
}
/* if quo_est was high by one, then num[i] went negative and
we need to correct things. */
if (num[num_hi_sig] < carry)
{
quo_est--;
carry = 0; /* add divisor back in */
for (j = 0; j <= den_hi_sig; j++)
{
work = num[i + j] + den[j] + carry;
carry = HIGHPART (work);
num[i + j] = LOWPART (work);
}
num [num_hi_sig] += carry;
}
/* store the quotient digit. */
quo[i] = quo_est;
}
}
decode (quo, lquo, hquo);
finish_up:
/* if result is negative, make it so. */
if (quo_neg)
neg_double (*lquo, *hquo, lquo, hquo);
/* compute trial remainder: rem = num - (quo * den) */
mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem);
neg_double (*lrem, *hrem, lrem, hrem);
add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem);
switch (code)
{
case TRUNC_DIV_EXPR:
case TRUNC_MOD_EXPR: /* round toward zero */
case EXACT_DIV_EXPR: /* for this one, it shouldn't matter */
return overflow;
case FLOOR_DIV_EXPR:
case FLOOR_MOD_EXPR: /* round toward negative infinity */
if (quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio < 0 && rem != 0 */
{
/* quo = quo - 1; */
add_double (*lquo, *hquo, (HOST_WIDE_INT) -1, (HOST_WIDE_INT) -1,
lquo, hquo);
}
else return overflow;
break;
case CEIL_DIV_EXPR:
case CEIL_MOD_EXPR: /* round toward positive infinity */
if (!quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio > 0 && rem != 0 */
{
add_double (*lquo, *hquo, (HOST_WIDE_INT) 1, (HOST_WIDE_INT) 0,
lquo, hquo);
}
else return overflow;
break;
case ROUND_DIV_EXPR:
case ROUND_MOD_EXPR: /* round to closest integer */
{
HOST_WIDE_INT labs_rem = *lrem, habs_rem = *hrem;
HOST_WIDE_INT labs_den = lden, habs_den = hden, ltwice, htwice;
/* get absolute values */
if (*hrem < 0) neg_double (*lrem, *hrem, &labs_rem, &habs_rem);
if (hden < 0) neg_double (lden, hden, &labs_den, &habs_den);
/* if (2 * abs (lrem) >= abs (lden)) */
mul_double ((HOST_WIDE_INT) 2, (HOST_WIDE_INT) 0,
labs_rem, habs_rem, &ltwice, &htwice);
if (((unsigned HOST_WIDE_INT) habs_den
< (unsigned HOST_WIDE_INT) htwice)
|| (((unsigned HOST_WIDE_INT) habs_den
== (unsigned HOST_WIDE_INT) htwice)
&& ((HOST_WIDE_INT unsigned) labs_den
< (unsigned HOST_WIDE_INT) ltwice)))
{
if (*hquo < 0)
/* quo = quo - 1; */
add_double (*lquo, *hquo,
(HOST_WIDE_INT) -1, (HOST_WIDE_INT) -1, lquo, hquo);
else
/* quo = quo + 1; */
add_double (*lquo, *hquo, (HOST_WIDE_INT) 1, (HOST_WIDE_INT) 0,
lquo, hquo);
}
else return overflow;
}
break;
default:
abort ();
}
/* compute true remainder: rem = num - (quo * den) */
mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem);
neg_double (*lrem, *hrem, lrem, hrem);
add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem);
return overflow;
}
#ifndef REAL_ARITHMETIC
/* Effectively truncate a real value to represent the nearest possible value
in a narrower mode. The result is actually represented in the same data
type as the argument, but its value is usually different.
A trap may occur during the FP operations and it is the responsibility
of the calling function to have a handler established. */
REAL_VALUE_TYPE
real_value_truncate (mode, arg)
enum machine_mode mode;
REAL_VALUE_TYPE arg;
{
return REAL_VALUE_TRUNCATE (mode, arg);
}
#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
/* Check for infinity in an IEEE double precision number. */
int
target_isinf (x)
REAL_VALUE_TYPE x;
{
/* The IEEE 64-bit double format. */
union {
REAL_VALUE_TYPE d;
struct {
unsigned sign : 1;
unsigned exponent : 11;
unsigned mantissa1 : 20;
unsigned mantissa2;
} little_endian;
struct {
unsigned mantissa2;
unsigned mantissa1 : 20;
unsigned exponent : 11;
unsigned sign : 1;
} big_endian;
} u;
u.d = dconstm1;
if (u.big_endian.sign == 1)
{
u.d = x;
return (u.big_endian.exponent == 2047
&& u.big_endian.mantissa1 == 0
&& u.big_endian.mantissa2 == 0);
}
else
{
u.d = x;
return (u.little_endian.exponent == 2047
&& u.little_endian.mantissa1 == 0
&& u.little_endian.mantissa2 == 0);
}
}
/* Check whether an IEEE double precision number is a NaN. */
int
target_isnan (x)
REAL_VALUE_TYPE x;
{
/* The IEEE 64-bit double format. */
union {
REAL_VALUE_TYPE d;
struct {
unsigned sign : 1;
unsigned exponent : 11;
unsigned mantissa1 : 20;
unsigned mantissa2;
} little_endian;
struct {
unsigned mantissa2;
unsigned mantissa1 : 20;
unsigned exponent : 11;
unsigned sign : 1;
} big_endian;
} u;
u.d = dconstm1;
if (u.big_endian.sign == 1)
{
u.d = x;
return (u.big_endian.exponent == 2047
&& (u.big_endian.mantissa1 != 0
|| u.big_endian.mantissa2 != 0));
}
else
{
u.d = x;
return (u.little_endian.exponent == 2047
&& (u.little_endian.mantissa1 != 0
|| u.little_endian.mantissa2 != 0));
}
}
/* Check for a negative IEEE double precision number. */
int
target_negative (x)
REAL_VALUE_TYPE x;
{
/* The IEEE 64-bit double format. */
union {
REAL_VALUE_TYPE d;
struct {
unsigned sign : 1;
unsigned exponent : 11;
unsigned mantissa1 : 20;
unsigned mantissa2;
} little_endian;
struct {
unsigned mantissa2;
unsigned mantissa1 : 20;
unsigned exponent : 11;
unsigned sign : 1;
} big_endian;
} u;
u.d = dconstm1;
if (u.big_endian.sign == 1)
{
u.d = x;
return u.big_endian.sign;
}
else
{
u.d = x;
return u.little_endian.sign;
}
}
#else /* Target not IEEE */
/* Let's assume other float formats don't have infinity.
(This can be overridden by redefining REAL_VALUE_ISINF.) */
target_isinf (x)
REAL_VALUE_TYPE x;
{
return 0;
}
/* Let's assume other float formats don't have NaNs.
(This can be overridden by redefining REAL_VALUE_ISNAN.) */
target_isnan (x)
REAL_VALUE_TYPE x;
{
return 0;
}
/* Let's assume other float formats don't have minus zero.
(This can be overridden by redefining REAL_VALUE_NEGATIVE.) */
target_negative (x)
REAL_VALUE_TYPE x;
{
return x < 0;
}
#endif /* Target not IEEE */
/* Try to change R into its exact multiplicative inverse in machine mode
MODE. Return nonzero function value if successful. */
int
exact_real_inverse (mode, r)
enum machine_mode mode;
REAL_VALUE_TYPE *r;
{
jmp_buf float_error;
union
{
double d;
unsigned short i[4];
}x, t, y;
int i;
/* Usually disable if bounds checks are not reliable. */
if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT) && !flag_pretend_float)
return 0;
/* Set array index to the less significant bits in the unions, depending
on the endian-ness of the host doubles.
Disable if insufficient information on the data structure. */
#if HOST_FLOAT_FORMAT == UNKNOWN_FLOAT_FORMAT
return 0;
#else
#if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT
#define K 2
#else
#if HOST_FLOAT_FORMAT == IBM_FLOAT_FORMAT
#define K 2
#else
#define K (2 * HOST_FLOAT_WORDS_BIG_ENDIAN)
#endif
#endif
#endif
if (setjmp (float_error))
{
/* Don't do the optimization if there was an arithmetic error. */
fail:
set_float_handler (NULL_PTR);
return 0;
}
set_float_handler (float_error);
/* Domain check the argument. */
x.d = *r;
if (x.d == 0.0)
goto fail;
#ifdef REAL_INFINITY
if (REAL_VALUE_ISINF (x.d) || REAL_VALUE_ISNAN (x.d))
goto fail;
#endif
/* Compute the reciprocal and check for numerical exactness.
It is unnecessary to check all the significand bits to determine
whether X is a power of 2. If X is not, then it is impossible for
the bottom half significand of both X and 1/X to be all zero bits.
Hence we ignore the data structure of the top half and examine only
the low order bits of the two significands. */
t.d = 1.0 / x.d;
if (x.i[K] != 0 || x.i[K + 1] != 0 || t.i[K] != 0 || t.i[K + 1] != 0)
goto fail;
/* Truncate to the required mode and range-check the result. */
y.d = REAL_VALUE_TRUNCATE (mode, t.d);
#ifdef CHECK_FLOAT_VALUE
i = 0;
if (CHECK_FLOAT_VALUE (mode, y.d, i))
goto fail;
#endif
/* Fail if truncation changed the value. */
if (y.d != t.d || y.d == 0.0)
goto fail;
#ifdef REAL_INFINITY
if (REAL_VALUE_ISINF (y.d) || REAL_VALUE_ISNAN (y.d))
goto fail;
#endif
/* Output the reciprocal and return success flag. */
set_float_handler (NULL_PTR);
*r = y.d;
return 1;
}
/* Convert C9X hexadecimal floating point string constant S. Return
real value type in mode MODE. This function uses the host computer's
fp arithmetic when there is no REAL_ARITHMETIC. */
REAL_VALUE_TYPE
real_hex_to_f (s, mode)
char *s;
enum machine_mode mode;
{
REAL_VALUE_TYPE ip;
char *p = s;
unsigned HOST_WIDE_INT low, high;
int frexpon, expon, shcount, nrmcount, k;
int sign, expsign, decpt, isfloat, isldouble, gotp, lost;
char c;
isldouble = 0;
isfloat = 0;
frexpon = 0;
expon = 0;
expsign = 1;
ip = 0.0;
while (*p == ' ' || *p == '\t')
++p;
/* Sign, if any, comes first. */
sign = 1;
if (*p == '-')
{
sign = -1;
++p;
}
/* The string is supposed to start with 0x or 0X . */
if (*p == '0')
{
++p;
if (*p == 'x' || *p == 'X')
++p;
else
abort ();
}
else
abort ();
while (*p == '0')
++p;
high = 0;
low = 0;
lost = 0; /* Nonzero low order bits shifted out and discarded. */
frexpon = 0; /* Bits after the decimal point. */
expon = 0; /* Value of exponent. */
decpt = 0; /* How many decimal points. */
gotp = 0; /* How many P's. */
shcount = 0;
while ((c = *p) != '\0')
{
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')
|| (c >= 'a' && c <= 'f'))
{
k = c & 0x7f;
if (k >= 'a')
k = k - 'a' + 10;
else if (k >= 'A')
k = k - 'A' + 10;
else
k = k - '0';
if ((high & 0xf0000000) == 0)
{
high = (high << 4) + ((low >> 28) & 15);
low = (low << 4) + k;
shcount += 4;
if (decpt)
frexpon += 4;
}
else
{
/* Record nonzero lost bits. */
lost |= k;
if (!decpt)
frexpon -= 4;
}
++p;
}
else if ( c == '.')
{
++decpt;
++p;
}
else if (c == 'p' || c == 'P')
{
++gotp;
++p;
/* Sign of exponent. */
if (*p == '-')
{
expsign = -1;
++p;
}
/* Value of exponent.
The exponent field is a decimal integer. */
while (isdigit(*p))
{
k = (*p++ & 0x7f) - '0';
expon = 10 * expon + k;
}
expon *= expsign;
/* F suffix is ambiguous in the significand part
so it must appear after the decimal exponent field. */
if (*p == 'f' || *p == 'F')
{
isfloat = 1;
++p;
break;
}
}
else if (c == 'l' || c == 'L')
{
isldouble = 1;
++p;
break;
}
else
break;
}
/* Abort if last character read was not legitimate. */
c = *p;
if ((c != '\0' && c != ' ' && c != '\n' && c != '\r') || (decpt > 1))
abort ();
/* There must be either one decimal point or one p. */
if (decpt == 0 && gotp == 0)
abort ();
shcount -= 4;
if ((high == 0) && (low == 0))
{
return dconst0;
}
/* Normalize. */
nrmcount = 0;
if (high == 0)
{
high = low;
low = 0;
nrmcount += 32;
}
/* Leave a high guard bit for carry-out. */
if ((high & 0x80000000) != 0)
{
lost |= low & 1;
low = (low >> 1) | (high << 31);
high = high >> 1;
nrmcount -= 1;
}
if ((high & 0xffff8000) == 0)
{
high = (high << 16) + ((low >> 16) & 0xffff);
low = low << 16;
nrmcount += 16;
}
while ((high & 0xc0000000) == 0)
{
high = (high << 1) + ((low >> 31) & 1);
low = low << 1;
nrmcount += 1;
}
if (isfloat || GET_MODE_SIZE(mode) == UNITS_PER_WORD)
{
/* Keep 24 bits precision, bits 0x7fffff80.
Rounding bit is 0x40. */
lost = lost | low | (high & 0x3f);
low = 0;
if (high & 0x40)
{
if ((high & 0x80) || lost)
high += 0x40;
}
high &= 0xffffff80;
}
else
{
/* We need real.c to do long double formats, so here default
to double precision. */
#if HOST_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
/* IEEE double.
Keep 53 bits precision, bits 0x7fffffff fffffc00.
Rounding bit is low word 0x200. */
lost = lost | (low & 0x1ff);
if (low & 0x200)
{
if ((low & 0x400) || lost)
{
low = (low + 0x200) & 0xfffffc00;
if (low == 0)
high += 1;
}
}
low &= 0xfffffc00;
#else
/* Assume it's a VAX with 56-bit significand,
bits 0x7fffffff ffffff80. */
lost = lost | (low & 0x7f);
if (low & 0x40)
{
if ((low & 0x80) || lost)
{
low = (low + 0x40) & 0xffffff80;
if (low == 0)
high += 1;
}
}
low &= 0xffffff80;
#endif
}
ip = (double) high;
ip = REAL_VALUE_LDEXP (ip, 32) + (double) low;
/* Apply shifts and exponent value as power of 2. */
ip = REAL_VALUE_LDEXP (ip, expon - (nrmcount + frexpon));
if (sign < 0)
ip = -ip;
return ip;
}
#endif /* no REAL_ARITHMETIC */
/* Split a tree IN into a constant and a variable part
that could be combined with CODE to make IN.
CODE must be a commutative arithmetic operation.
Store the constant part into *CONP and the variable in &VARP.
Return 1 if this was done; zero means the tree IN did not decompose
this way.
If CODE is PLUS_EXPR we also split trees that use MINUS_EXPR.
Therefore, we must tell the caller whether the variable part
was subtracted. We do this by storing 1 or -1 into *VARSIGNP.
The value stored is the coefficient for the variable term.
The constant term we return should always be added;
we negate it if necessary. */
static int
split_tree (in, code, varp, conp, varsignp)
tree in;
enum tree_code code;
tree *varp, *conp;
int *varsignp;
{
register tree outtype = TREE_TYPE (in);
*varp = 0;
*conp = 0;
/* Strip any conversions that don't change the machine mode. */
while ((TREE_CODE (in) == NOP_EXPR
|| TREE_CODE (in) == CONVERT_EXPR)
&& (TYPE_MODE (TREE_TYPE (in))
== TYPE_MODE (TREE_TYPE (TREE_OPERAND (in, 0)))))
in = TREE_OPERAND (in, 0);
if (TREE_CODE (in) == code
|| (! FLOAT_TYPE_P (TREE_TYPE (in))
/* We can associate addition and subtraction together
(even though the C standard doesn't say so)
for integers because the value is not affected.
For reals, the value might be affected, so we can't. */
&& ((code == PLUS_EXPR && TREE_CODE (in) == MINUS_EXPR)
|| (code == MINUS_EXPR && TREE_CODE (in) == PLUS_EXPR))))
{
enum tree_code code = TREE_CODE (TREE_OPERAND (in, 0));
if (code == INTEGER_CST)
{
*conp = TREE_OPERAND (in, 0);
*varp = TREE_OPERAND (in, 1);
if (TYPE_MODE (TREE_TYPE (*varp)) != TYPE_MODE (outtype)
&& TREE_TYPE (*varp) != outtype)
*varp = convert (outtype, *varp);
*varsignp = (TREE_CODE (in) == MINUS_EXPR) ? -1 : 1;
return 1;
}
if (TREE_CONSTANT (TREE_OPERAND (in, 1)))
{
*conp = TREE_OPERAND (in, 1);
*varp = TREE_OPERAND (in, 0);
*varsignp = 1;
if (TYPE_MODE (TREE_TYPE (*varp)) != TYPE_MODE (outtype)
&& TREE_TYPE (*varp) != outtype)
*varp = convert (outtype, *varp);
if (TREE_CODE (in) == MINUS_EXPR)
{
/* If operation is subtraction and constant is second,
must negate it to get an additive constant.
And this cannot be done unless it is a manifest constant.
It could also be the address of a static variable.
We cannot negate that, so give up. */
if (TREE_CODE (*conp) == INTEGER_CST)
/* Subtracting from integer_zero_node loses for long long. */
*conp = fold (build1 (NEGATE_EXPR, TREE_TYPE (*conp), *conp));
else
return 0;
}
return 1;
}
if (TREE_CONSTANT (TREE_OPERAND (in, 0)))
{
*conp = TREE_OPERAND (in, 0);
*varp = TREE_OPERAND (in, 1);
if (TYPE_MODE (TREE_TYPE (*varp)) != TYPE_MODE (outtype)
&& TREE_TYPE (*varp) != outtype)
*varp = convert (outtype, *varp);
*varsignp = (TREE_CODE (in) == MINUS_EXPR) ? -1 : 1;
return 1;
}
}
return 0;
}
/* Combine two integer constants ARG1 and ARG2 under operation CODE
to produce a new constant.
If NOTRUNC is nonzero, do not truncate the result to fit the data type.
If FORSIZE is nonzero, compute overflow for unsigned types. */
static tree
int_const_binop (code, arg1, arg2, notrunc, forsize)
enum tree_code code;
register tree arg1, arg2;
int notrunc, forsize;
{
HOST_WIDE_INT int1l, int1h, int2l, int2h;
HOST_WIDE_INT low, hi;
HOST_WIDE_INT garbagel, garbageh;
register tree t;
int uns = TREE_UNSIGNED (TREE_TYPE (arg1));
int overflow = 0;
int no_overflow = 0;
int1l = TREE_INT_CST_LOW (arg1);
int1h = TREE_INT_CST_HIGH (arg1);
int2l = TREE_INT_CST_LOW (arg2);
int2h = TREE_INT_CST_HIGH (arg2);
switch (code)
{
case BIT_IOR_EXPR:
low = int1l | int2l, hi = int1h | int2h;
break;
case BIT_XOR_EXPR:
low = int1l ^ int2l, hi = int1h ^ int2h;
break;
case BIT_AND_EXPR:
low = int1l & int2l, hi = int1h & int2h;
break;
case BIT_ANDTC_EXPR:
low = int1l & ~int2l, hi = int1h & ~int2h;
break;
case RSHIFT_EXPR:
int2l = - int2l;
case LSHIFT_EXPR:
/* It's unclear from the C standard whether shifts can overflow.
The following code ignores overflow; perhaps a C standard
interpretation ruling is needed. */
lshift_double (int1l, int1h, int2l,
TYPE_PRECISION (TREE_TYPE (arg1)),
&low, &hi,
!uns);
no_overflow = 1;
break;
case RROTATE_EXPR:
int2l = - int2l;
case LROTATE_EXPR:
lrotate_double (int1l, int1h, int2l,
TYPE_PRECISION (TREE_TYPE (arg1)),
&low, &hi);
break;
case PLUS_EXPR:
overflow = add_double (int1l, int1h, int2l, int2h, &low, &hi);
break;
case MINUS_EXPR:
neg_double (int2l, int2h, &low, &hi);
add_double (int1l, int1h, low, hi, &low, &hi);
overflow = overflow_sum_sign (hi, int2h, int1h);
break;
case MULT_EXPR:
overflow = mul_double (int1l, int1h, int2l, int2h, &low, &hi);
break;
case TRUNC_DIV_EXPR:
case FLOOR_DIV_EXPR: case CEIL_DIV_EXPR:
case EXACT_DIV_EXPR:
/* This is a shortcut for a common special case. */
if (int2h == 0 && int2l > 0
&& ! TREE_CONSTANT_OVERFLOW (arg1)
&& ! TREE_CONSTANT_OVERFLOW (arg2)
&& int1h == 0 && int1l >= 0)
{
if (code == CEIL_DIV_EXPR)
int1l += int2l - 1;
low = int1l / int2l, hi = 0;
break;
}
/* ... fall through ... */
case ROUND_DIV_EXPR:
if (int2h == 0 && int2l == 1)
{
low = int1l, hi = int1h;
break;
}
if (int1l == int2l && int1h == int2h
&& ! (int1l == 0 && int1h == 0))
{
low = 1, hi = 0;
break;
}
overflow = div_and_round_double (code, uns,
int1l, int1h, int2l, int2h,
&low, &hi, &garbagel, &garbageh);
break;
case TRUNC_MOD_EXPR:
case FLOOR_MOD_EXPR: case CEIL_MOD_EXPR:
/* This is a shortcut for a common special case. */
if (int2h == 0 && int2l > 0
&& ! TREE_CONSTANT_OVERFLOW (arg1)
&& ! TREE_CONSTANT_OVERFLOW (arg2)
&& int1h == 0 && int1l >= 0)
{
if (code == CEIL_MOD_EXPR)
int1l += int2l - 1;
low = int1l % int2l, hi = 0;
break;
}
/* ... fall through ... */
case ROUND_MOD_EXPR:
overflow = div_and_round_double (code, uns,
int1l, int1h, int2l, int2h,
&garbagel, &garbageh, &low, &hi);
break;
case MIN_EXPR:
case MAX_EXPR:
if (uns)
{
low = (((unsigned HOST_WIDE_INT) int1h
< (unsigned HOST_WIDE_INT) int2h)
|| (((unsigned HOST_WIDE_INT) int1h
== (unsigned HOST_WIDE_INT) int2h)
&& ((unsigned HOST_WIDE_INT) int1l
< (unsigned HOST_WIDE_INT) int2l)));
}
else
{
low = ((int1h < int2h)
|| ((int1h == int2h)
&& ((unsigned HOST_WIDE_INT) int1l
< (unsigned HOST_WIDE_INT) int2l)));
}
if (low == (code == MIN_EXPR))
low = int1l, hi = int1h;
else
low = int2l, hi = int2h;
break;
default:
abort ();
}
if (TREE_TYPE (arg1) == sizetype && hi == 0
&& low >= 0
&& (TYPE_MAX_VALUE (sizetype) == NULL
|| low <= TREE_INT_CST_LOW (TYPE_MAX_VALUE (sizetype)))
&& ! overflow
&& ! TREE_OVERFLOW (arg1) && ! TREE_OVERFLOW (arg2))
t = size_int (low);
else
{
t = build_int_2 (low, hi);
TREE_TYPE (t) = TREE_TYPE (arg1);
}
TREE_OVERFLOW (t)
= ((notrunc ? (!uns || forsize) && overflow
: force_fit_type (t, (!uns || forsize) && overflow) && ! no_overflow)
| TREE_OVERFLOW (arg1)
| TREE_OVERFLOW (arg2));
/* If we're doing a size calculation, unsigned arithmetic does overflow.
So check if force_fit_type truncated the value. */
if (forsize
&& ! TREE_OVERFLOW (t)
&& (TREE_INT_CST_HIGH (t) != hi
|| TREE_INT_CST_LOW (t) != low))
TREE_OVERFLOW (t) = 1;
TREE_CONSTANT_OVERFLOW (t) = (TREE_OVERFLOW (t)
| TREE_CONSTANT_OVERFLOW (arg1)
| TREE_CONSTANT_OVERFLOW (arg2));
return t;
}
struct cb_args
{
/* Input */
tree arg1;
REAL_VALUE_TYPE d1, d2;
enum tree_code code;
/* Output */
tree t;
};
static void
const_binop_1 (data)
PTR data;
{
struct cb_args * args = (struct cb_args *) data;
REAL_VALUE_TYPE value;
#ifdef REAL_ARITHMETIC
REAL_ARITHMETIC (value, args->code, args->d1, args->d2);
#else
switch (args->code)
{
case PLUS_EXPR:
value = args->d1 + args->d2;
break;
case MINUS_EXPR:
value = args->d1 - args->d2;
break;
case MULT_EXPR:
value = args->d1 * args->d2;
break;
case RDIV_EXPR:
#ifndef REAL_INFINITY
if (args->d2 == 0)
abort ();
#endif
value = args->d1 / args->d2;
break;
case MIN_EXPR:
value = MIN (args->d1, args->d2);
break;
case MAX_EXPR:
value = MAX (args->d1, args->d2);
break;
default:
abort ();
}
#endif /* no REAL_ARITHMETIC */
args->t =
build_real (TREE_TYPE (args->arg1),
real_value_truncate (TYPE_MODE (TREE_TYPE (args->arg1)),
value));
}
/* Combine two constants ARG1 and ARG2 under operation CODE
to produce a new constant.
We assume ARG1 and ARG2 have the same data type,
or at least are the same kind of constant and the same machine mode.
If NOTRUNC is nonzero, do not truncate the result to fit the data type. */
static tree
const_binop (code, arg1, arg2, notrunc)
enum tree_code code;
register tree arg1, arg2;
int notrunc;
{
STRIP_NOPS (arg1); STRIP_NOPS (arg2);
if (TREE_CODE (arg1) == INTEGER_CST)
return int_const_binop (code, arg1, arg2, notrunc, 0);
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
if (TREE_CODE (arg1) == REAL_CST)
{
REAL_VALUE_TYPE d1;
REAL_VALUE_TYPE d2;
int overflow = 0;
tree t;
struct cb_args args;
d1 = TREE_REAL_CST (arg1);
d2 = TREE_REAL_CST (arg2);
/* If either operand is a NaN, just return it. Otherwise, set up
for floating-point trap; we return an overflow. */
if (REAL_VALUE_ISNAN (d1))
return arg1;
else if (REAL_VALUE_ISNAN (d2))
return arg2;
/* Setup input for const_binop_1() */
args.arg1 = arg1;
args.d1 = d1;
args.d2 = d2;
args.code = code;
if (do_float_handler (const_binop_1, (PTR) &args))
{
/* Receive output from const_binop_1() */
t = args.t;
}
else
{
/* We got an exception from const_binop_1() */
t = copy_node (arg1);
overflow = 1;
}
TREE_OVERFLOW (t)
= (force_fit_type (t, overflow)
| TREE_OVERFLOW (arg1) | TREE_OVERFLOW (arg2));
TREE_CONSTANT_OVERFLOW (t)
= TREE_OVERFLOW (t)
| TREE_CONSTANT_OVERFLOW (arg1)
| TREE_CONSTANT_OVERFLOW (arg2);
return t;
}
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
if (TREE_CODE (arg1) == COMPLEX_CST)
{
register tree type = TREE_TYPE (arg1);
register tree r1 = TREE_REALPART (arg1);
register tree i1 = TREE_IMAGPART (arg1);
register tree r2 = TREE_REALPART (arg2);
register tree i2 = TREE_IMAGPART (arg2);
register tree t;
switch (code)
{
case PLUS_EXPR:
t = build_complex (type,
const_binop (PLUS_EXPR, r1, r2, notrunc),
const_binop (PLUS_EXPR, i1, i2, notrunc));
break;
case MINUS_EXPR:
t = build_complex (type,
const_binop (MINUS_EXPR, r1, r2, notrunc),
const_binop (MINUS_EXPR, i1, i2, notrunc));
break;
case MULT_EXPR:
t = build_complex (type,
const_binop (MINUS_EXPR,
const_binop (MULT_EXPR,
r1, r2, notrunc),
const_binop (MULT_EXPR,
i1, i2, notrunc),
notrunc),
const_binop (PLUS_EXPR,
const_binop (MULT_EXPR,
r1, i2, notrunc),
const_binop (MULT_EXPR,
i1, r2, notrunc),
notrunc));
break;
case RDIV_EXPR:
{
register tree magsquared
= const_binop (PLUS_EXPR,
const_binop (MULT_EXPR, r2, r2, notrunc),
const_binop (MULT_EXPR, i2, i2, notrunc),
notrunc);
t = build_complex (type,
const_binop
(INTEGRAL_TYPE_P (TREE_TYPE (r1))
? TRUNC_DIV_EXPR : RDIV_EXPR,
const_binop (PLUS_EXPR,
const_binop (MULT_EXPR, r1, r2,
notrunc),
const_binop (MULT_EXPR, i1, i2,
notrunc),
notrunc),
magsquared, notrunc),
const_binop
(INTEGRAL_TYPE_P (TREE_TYPE (r1))
? TRUNC_DIV_EXPR : RDIV_EXPR,
const_binop (MINUS_EXPR,
const_binop (MULT_EXPR, i1, r2,
notrunc),
const_binop (MULT_EXPR, r1, i2,
notrunc),
notrunc),
magsquared, notrunc));
}
break;
default:
abort ();
}
return t;
}
return 0;
}
/* Return an INTEGER_CST with value V . The type is determined by bit_p:
if it is zero, the type is taken from sizetype; if it is one, the type
is taken from bitsizetype. */
tree
size_int_wide (number, high, bit_p)
unsigned HOST_WIDE_INT number, high;
int bit_p;
{
register tree t;
/* Type-size nodes already made for small sizes. */
static tree size_table[2*HOST_BITS_PER_WIDE_INT + 1][2];
if (number < 2*HOST_BITS_PER_WIDE_INT + 1 && ! high
&& size_table[number][bit_p] != 0)
return size_table[number][bit_p];
if (number < 2*HOST_BITS_PER_WIDE_INT + 1 && ! high)
{
push_obstacks_nochange ();
/* Make this a permanent node. */
end_temporary_allocation ();
t = build_int_2 (number, 0);
TREE_TYPE (t) = bit_p ? bitsizetype : sizetype;
size_table[number][bit_p] = t;
pop_obstacks ();
}
else
{
t = build_int_2 (number, high);
TREE_TYPE (t) = bit_p ? bitsizetype : sizetype;
TREE_OVERFLOW (t) = TREE_CONSTANT_OVERFLOW (t) = force_fit_type (t, 0);
}
return t;
}
/* Combine operands OP1 and OP2 with arithmetic operation CODE.
CODE is a tree code. Data type is taken from `sizetype',
If the operands are constant, so is the result. */
tree
size_binop (code, arg0, arg1)
enum tree_code code;
tree arg0, arg1;
{
/* Handle the special case of two integer constants faster. */
if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
{
/* And some specific cases even faster than that. */
if (code == PLUS_EXPR && integer_zerop (arg0))
return arg1;
else if ((code == MINUS_EXPR || code == PLUS_EXPR)
&& integer_zerop (arg1))
return arg0;
else if (code == MULT_EXPR && integer_onep (arg0))
return arg1;
/* Handle general case of two integer constants. */
return int_const_binop (code, arg0, arg1, 0, 1);
}
if (arg0 == error_mark_node || arg1 == error_mark_node)
return error_mark_node;
return fold (build (code, sizetype, arg0, arg1));
}
/* Combine operands OP1 and OP2 with arithmetic operation CODE.
CODE is a tree code. Data type is taken from `ssizetype',
If the operands are constant, so is the result. */
tree
ssize_binop (code, arg0, arg1)
enum tree_code code;
tree arg0, arg1;
{
/* Handle the special case of two integer constants faster. */
if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
{
/* And some specific cases even faster than that. */
if (code == PLUS_EXPR && integer_zerop (arg0))
return arg1;
else if ((code == MINUS_EXPR || code == PLUS_EXPR)
&& integer_zerop (arg1))
return arg0;
else if (code == MULT_EXPR && integer_onep (arg0))
return arg1;
/* Handle general case of two integer constants. We convert
arg0 to ssizetype because int_const_binop uses its type for the
return value. */
arg0 = convert (ssizetype, arg0);
return int_const_binop (code, arg0, arg1, 0, 0);
}
if (arg0 == error_mark_node || arg1 == error_mark_node)
return error_mark_node;
return fold (build (code, ssizetype, arg0, arg1));
}
struct fc_args
{
/* Input */
tree arg1, type;
/* Output */
tree t;
};
static void
fold_convert_1 (data)
PTR data;
{
struct fc_args * args = (struct fc_args *) data;
args->t = build_real (args->type,
real_value_truncate (TYPE_MODE (args->type),
TREE_REAL_CST (args->arg1)));
}
/* Given T, a tree representing type conversion of ARG1, a constant,
return a constant tree representing the result of conversion. */
static tree
fold_convert (t, arg1)
register tree t;
register tree arg1;
{
register tree type = TREE_TYPE (t);
int overflow = 0;
if (POINTER_TYPE_P (type) || INTEGRAL_TYPE_P (type))
{
if (TREE_CODE (arg1) == INTEGER_CST)
{
/* If we would build a constant wider than GCC supports,
leave the conversion unfolded. */
if (TYPE_PRECISION (type) > 2 * HOST_BITS_PER_WIDE_INT)
return t;
/* Given an integer constant, make new constant with new type,
appropriately sign-extended or truncated. */
t = build_int_2 (TREE_INT_CST_LOW (arg1),
TREE_INT_CST_HIGH (arg1));
TREE_TYPE (t) = type;
/* Indicate an overflow if (1) ARG1 already overflowed,
or (2) force_fit_type indicates an overflow.
Tell force_fit_type that an overflow has already occurred
if ARG1 is a too-large unsigned value and T is signed.
But don't indicate an overflow if converting a pointer. */
TREE_OVERFLOW (t)
= ((force_fit_type (t,
(TREE_INT_CST_HIGH (arg1) < 0
&& (TREE_UNSIGNED (type)
< TREE_UNSIGNED (TREE_TYPE (arg1)))))
&& ! POINTER_TYPE_P (TREE_TYPE (arg1)))
|| TREE_OVERFLOW (arg1));
TREE_CONSTANT_OVERFLOW (t)
= TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1);
}
#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
else if (TREE_CODE (arg1) == REAL_CST)
{
/* Don't initialize these, use assignments.
Initialized local aggregates don't work on old compilers. */
REAL_VALUE_TYPE x;
REAL_VALUE_TYPE l;
REAL_VALUE_TYPE u;
tree type1 = TREE_TYPE (arg1);
int no_upper_bound;
x = TREE_REAL_CST (arg1);
l = real_value_from_int_cst (type1, TYPE_MIN_VALUE (type));
no_upper_bound = (TYPE_MAX_VALUE (type) == NULL);
if (!no_upper_bound)
u = real_value_from_int_cst (type1, TYPE_MAX_VALUE (type));
/* See if X will be in range after truncation towards 0.
To compensate for truncation, move the bounds away from 0,
but reject if X exactly equals the adjusted bounds. */
#ifdef REAL_ARITHMETIC
REAL_ARITHMETIC (l, MINUS_EXPR, l, dconst1);
if (!no_upper_bound)
REAL_ARITHMETIC (u, PLUS_EXPR, u, dconst1);
#else
l--;
if (!no_upper_bound)
u++;
#endif
/* If X is a NaN, use zero instead and show we have an overflow.
Otherwise, range check. */
if (REAL_VALUE_ISNAN (x))
overflow = 1, x = dconst0;
else if (! (REAL_VALUES_LESS (l, x)
&& !no_upper_bound
&& REAL_VALUES_LESS (x, u)))
overflow = 1;
#ifndef REAL_ARITHMETIC
{
HOST_WIDE_INT low, high;
HOST_WIDE_INT half_word
= (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2);
if (x < 0)
x = -x;
high = (HOST_WIDE_INT) (x / half_word / half_word);
x -= (REAL_VALUE_TYPE) high * half_word * half_word;
if (x >= (REAL_VALUE_TYPE) half_word * half_word / 2)
{
low = x - (REAL_VALUE_TYPE) half_word * half_word / 2;
low |= (HOST_WIDE_INT) -1 << (HOST_BITS_PER_WIDE_INT - 1);
}
else
low = (HOST_WIDE_INT) x;
if (TREE_REAL_CST (arg1) < 0)
neg_double (low, high, &low, &high);
t = build_int_2 (low, high);
}
#else
{
HOST_WIDE_INT low, high;
REAL_VALUE_TO_INT (&low, &high, x);
t = build_int_2 (low, high);
}
#endif
TREE_TYPE (t) = type;
TREE_OVERFLOW (t)
= TREE_OVERFLOW (arg1) | force_fit_type (t, overflow);
TREE_CONSTANT_OVERFLOW (t)
= TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1);
}
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
TREE_TYPE (t) = type;
}
else if (TREE_CODE (type) == REAL_TYPE)
{
#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
if (TREE_CODE (arg1) == INTEGER_CST)
return build_real_from_int_cst (type, arg1);
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
if (TREE_CODE (arg1) == REAL_CST)
{
struct fc_args args;
if (REAL_VALUE_ISNAN (TREE_REAL_CST (arg1)))
{
t = arg1;
TREE_TYPE (arg1) = type;
return t;
}
/* Setup input for fold_convert_1() */
args.arg1 = arg1;
args.type = type;
if (do_float_handler (fold_convert_1, (PTR) &args))
{
/* Receive output from fold_convert_1() */
t = args.t;
}
else
{
/* We got an exception from fold_convert_1() */
overflow = 1;
t = copy_node (arg1);
}
TREE_OVERFLOW (t)
= TREE_OVERFLOW (arg1) | force_fit_type (t, overflow);
TREE_CONSTANT_OVERFLOW (t)
= TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1);
return t;
}
}
TREE_CONSTANT (t) = 1;
return t;
}
/* Return an expr equal to X but certainly not valid as an lvalue. */
tree
non_lvalue (x)
tree x;
{
tree result;
/* These things are certainly not lvalues. */
if (TREE_CODE (x) == NON_LVALUE_EXPR
|| TREE_CODE (x) == INTEGER_CST
|| TREE_CODE (x) == REAL_CST
|| TREE_CODE (x) == STRING_CST
|| TREE_CODE (x) == ADDR_EXPR)
return x;
result = build1 (NON_LVALUE_EXPR, TREE_TYPE (x), x);
TREE_CONSTANT (result) = TREE_CONSTANT (x);
return result;
}
/* Nonzero means lvalues are limited to those valid in pedantic ANSI C.
Zero means allow extended lvalues. */
int pedantic_lvalues;
/* When pedantic, return an expr equal to X but certainly not valid as a
pedantic lvalue. Otherwise, return X. */
tree
pedantic_non_lvalue (x)
tree x;
{
if (pedantic_lvalues)
return non_lvalue (x);
else
return x;
}
/* Given a tree comparison code, return the code that is the logical inverse
of the given code. It is not safe to do this for floating-point
comparisons, except for NE_EXPR and EQ_EXPR. */
static enum tree_code
invert_tree_comparison (code)
enum tree_code code;
{
switch (code)
{
case EQ_EXPR:
return NE_EXPR;
case NE_EXPR:
return EQ_EXPR;
case GT_EXPR:
return LE_EXPR;
case GE_EXPR:
return LT_EXPR;
case LT_EXPR:
return GE_EXPR;
case LE_EXPR:
return GT_EXPR;
default:
abort ();
}
}
/* Similar, but return the comparison that results if the operands are
swapped. This is safe for floating-point. */
static enum tree_code
swap_tree_comparison (code)
enum tree_code code;
{
switch (code)
{
case EQ_EXPR:
case NE_EXPR:
return code;
case GT_EXPR:
return LT_EXPR;
case GE_EXPR:
return LE_EXPR;
case LT_EXPR:
return GT_EXPR;
case LE_EXPR:
return GE_EXPR;
default:
abort ();
}
}
/* Return nonzero if CODE is a tree code that represents a truth value. */
static int
truth_value_p (code)
enum tree_code code;
{
return (TREE_CODE_CLASS (code) == '<'
|| code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR
|| code == TRUTH_OR_EXPR || code == TRUTH_ORIF_EXPR
|| code == TRUTH_XOR_EXPR || code == TRUTH_NOT_EXPR);
}
/* Return nonzero if two operands are necessarily equal.
If ONLY_CONST is non-zero, only return non-zero for constants.
This function tests whether the operands are indistinguishable;
it does not test whether they are equal using C's == operation.
The distinction is important for IEEE floating point, because
(1) -0.0 and 0.0 are distinguishable, but -0.0==0.0, and
(2) two NaNs may be indistinguishable, but NaN!=NaN. */
int
operand_equal_p (arg0, arg1, only_const)
tree arg0, arg1;
int only_const;
{
/* If both types don't have the same signedness, then we can't consider
them equal. We must check this before the STRIP_NOPS calls
because they may change the signedness of the arguments. */
if (TREE_UNSIGNED (TREE_TYPE (arg0)) != TREE_UNSIGNED (TREE_TYPE (arg1)))
return 0;
STRIP_NOPS (arg0);
STRIP_NOPS (arg1);
if (TREE_CODE (arg0) != TREE_CODE (arg1)
/* This is needed for conversions and for COMPONENT_REF.
Might as well play it safe and always test this. */
|| TYPE_MODE (TREE_TYPE (arg0)) != TYPE_MODE (TREE_TYPE (arg1)))
return 0;
/* If ARG0 and ARG1 are the same SAVE_EXPR, they are necessarily equal.
We don't care about side effects in that case because the SAVE_EXPR
takes care of that for us. In all other cases, two expressions are
equal if they have no side effects. If we have two identical
expressions with side effects that should be treated the same due
to the only side effects being identical SAVE_EXPR's, that will
be detected in the recursive calls below. */
if (arg0 == arg1 && ! only_const
&& (TREE_CODE (arg0) == SAVE_EXPR
|| (! TREE_SIDE_EFFECTS (arg0) && ! TREE_SIDE_EFFECTS (arg1))))
return 1;
/* Next handle constant cases, those for which we can return 1 even
if ONLY_CONST is set. */
if (TREE_CONSTANT (arg0) && TREE_CONSTANT (arg1))
switch (TREE_CODE (arg0))
{
case INTEGER_CST:
return (! TREE_CONSTANT_OVERFLOW (arg0)
&& ! TREE_CONSTANT_OVERFLOW (arg1)
&& TREE_INT_CST_LOW (arg0) == TREE_INT_CST_LOW (arg1)
&& TREE_INT_CST_HIGH (arg0) == TREE_INT_CST_HIGH (arg1));
case REAL_CST:
return (! TREE_CONSTANT_OVERFLOW (arg0)
&& ! TREE_CONSTANT_OVERFLOW (arg1)
&& REAL_VALUES_IDENTICAL (TREE_REAL_CST (arg0),
TREE_REAL_CST (arg1)));
case COMPLEX_CST:
return (operand_equal_p (TREE_REALPART (arg0), TREE_REALPART (arg1),
only_const)
&& operand_equal_p (TREE_IMAGPART (arg0), TREE_IMAGPART (arg1),
only_const));
case STRING_CST:
return (TREE_STRING_LENGTH (arg0) == TREE_STRING_LENGTH (arg1)
&& ! strncmp (TREE_STRING_POINTER (arg0),
TREE_STRING_POINTER (arg1),
TREE_STRING_LENGTH (arg0)));
case ADDR_EXPR:
return operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1, 0),
0);
default:
break;
}
if (only_const)
return 0;
switch (TREE_CODE_CLASS (TREE_CODE (arg0)))
{
case '1':
/* Two conversions are equal only if signedness and modes match. */
if ((TREE_CODE (arg0) == NOP_EXPR || TREE_CODE (arg0) == CONVERT_EXPR)
&& (TREE_UNSIGNED (TREE_TYPE (arg0))
!= TREE_UNSIGNED (TREE_TYPE (arg1))))
return 0;
return operand_equal_p (TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg1, 0), 0);
case '<':
case '2':
if (operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1, 0), 0)
&& operand_equal_p (TREE_OPERAND (arg0, 1), TREE_OPERAND (arg1, 1),
0))
return 1;
/* For commutative ops, allow the other order. */
return ((TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MULT_EXPR
|| TREE_CODE (arg0) == MIN_EXPR || TREE_CODE (arg0) == MAX_EXPR
|| TREE_CODE (arg0) == BIT_IOR_EXPR
|| TREE_CODE (arg0) == BIT_XOR_EXPR
|| TREE_CODE (arg0) == BIT_AND_EXPR
|| TREE_CODE (arg0) == NE_EXPR || TREE_CODE (arg0) == EQ_EXPR)
&& operand_equal_p (TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg1, 1), 0)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg1, 0), 0));
case 'r':
+ /* If either of the pointer (or reference) expressions we are dereferencing
+ contain a side effect, these cannot be equal. */
+ if (TREE_SIDE_EFFECTS (arg0)
+ || TREE_SIDE_EFFECTS (arg1))
+ return 0;
+
switch (TREE_CODE (arg0))
{
case INDIRECT_REF:
return operand_equal_p (TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg1, 0), 0);
case COMPONENT_REF:
case ARRAY_REF:
return (operand_equal_p (TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg1, 0), 0)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg1, 1), 0));
case BIT_FIELD_REF:
return (operand_equal_p (TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg1, 0), 0)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg1, 1), 0)
&& operand_equal_p (TREE_OPERAND (arg0, 2),
TREE_OPERAND (arg1, 2), 0));
default:
return 0;
}
case 'e':
if (TREE_CODE (arg0) == RTL_EXPR)
return rtx_equal_p (RTL_EXPR_RTL (arg0), RTL_EXPR_RTL (arg1));
return 0;
default:
return 0;
}
}
/* Similar to operand_equal_p, but see if ARG0 might have been made by
shorten_compare from ARG1 when ARG1 was being compared with OTHER.
When in doubt, return 0. */
static int
operand_equal_for_comparison_p (arg0, arg1, other)
tree arg0, arg1;
tree other;
{
int unsignedp1, unsignedpo;
tree primarg0, primarg1, primother;
unsigned correct_width;
if (operand_equal_p (arg0, arg1, 0))
return 1;
if (! INTEGRAL_TYPE_P (TREE_TYPE (arg0))
|| ! INTEGRAL_TYPE_P (TREE_TYPE (arg1)))
return 0;
/* Discard any conversions that don't change the modes of ARG0 and ARG1
and see if the inner values are the same. This removes any
signedness comparison, which doesn't matter here. */
primarg0 = arg0, primarg1 = arg1;
STRIP_NOPS (primarg0); STRIP_NOPS (primarg1);
if (operand_equal_p (primarg0, primarg1, 0))
return 1;
/* Duplicate what shorten_compare does to ARG1 and see if that gives the
actual comparison operand, ARG0.
First throw away any conversions to wider types
already present in the operands. */
primarg1 = get_narrower (arg1, &unsignedp1);
primother = get_narrower (other, &unsignedpo);
correct_width = TYPE_PRECISION (TREE_TYPE (arg1));
if (unsignedp1 == unsignedpo
&& TYPE_PRECISION (TREE_TYPE (primarg1)) < correct_width
&& TYPE_PRECISION (TREE_TYPE (primother)) < correct_width)
{
tree type = TREE_TYPE (arg0);
/* Make sure shorter operand is extended the right way
to match the longer operand. */
primarg1 = convert (signed_or_unsigned_type (unsignedp1,
TREE_TYPE (primarg1)),
primarg1);
if (operand_equal_p (arg0, convert (type, primarg1), 0))
return 1;
}
return 0;
}
/* See if ARG is an expression that is either a comparison or is performing
arithmetic on comparisons. The comparisons must only be comparing
two different values, which will be stored in *CVAL1 and *CVAL2; if
they are non-zero it means that some operands have already been found.
No variables may be used anywhere else in the expression except in the
comparisons. If SAVE_P is true it means we removed a SAVE_EXPR around
the expression and save_expr needs to be called with CVAL1 and CVAL2.
If this is true, return 1. Otherwise, return zero. */
static int
twoval_comparison_p (arg, cval1, cval2, save_p)
tree arg;
tree *cval1, *cval2;
int *save_p;
{
enum tree_code code = TREE_CODE (arg);
char class = TREE_CODE_CLASS (code);
/* We can handle some of the 'e' cases here. */
if (class == 'e' && code == TRUTH_NOT_EXPR)
class = '1';
else if (class == 'e'
&& (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR
|| code == COMPOUND_EXPR))
class = '2';
/* ??? Disable this since the SAVE_EXPR might already be in use outside
the expression. There may be no way to make this work, but it needs
to be looked at again for 2.6. */
#if 0
else if (class == 'e' && code == SAVE_EXPR && SAVE_EXPR_RTL (arg) == 0)
{
/* If we've already found a CVAL1 or CVAL2, this expression is
two complex to handle. */
if (*cval1 || *cval2)
return 0;
class = '1';
*save_p = 1;
}
#endif
switch (class)
{
case '1':
return twoval_comparison_p (TREE_OPERAND (arg, 0), cval1, cval2, save_p);
case '2':
return (twoval_comparison_p (TREE_OPERAND (arg, 0), cval1, cval2, save_p)
&& twoval_comparison_p (TREE_OPERAND (arg, 1),
cval1, cval2, save_p));
case 'c':
return 1;
case 'e':
if (code == COND_EXPR)
return (twoval_comparison_p (TREE_OPERAND (arg, 0),
cval1, cval2, save_p)
&& twoval_comparison_p (TREE_OPERAND (arg, 1),
cval1, cval2, save_p)
&& twoval_comparison_p (TREE_OPERAND (arg, 2),
cval1, cval2, save_p));
return 0;
case '<':
/* First see if we can handle the first operand, then the second. For
the second operand, we know *CVAL1 can't be zero. It must be that
one side of the comparison is each of the values; test for the
case where this isn't true by failing if the two operands
are the same. */
if (operand_equal_p (TREE_OPERAND (arg, 0),
TREE_OPERAND (arg, 1), 0))
return 0;
if (*cval1 == 0)
*cval1 = TREE_OPERAND (arg, 0);
else if (operand_equal_p (*cval1, TREE_OPERAND (arg, 0), 0))
;
else if (*cval2 == 0)
*cval2 = TREE_OPERAND (arg, 0);
else if (operand_equal_p (*cval2, TREE_OPERAND (arg, 0), 0))
;
else
return 0;
if (operand_equal_p (*cval1, TREE_OPERAND (arg, 1), 0))
;
else if (*cval2 == 0)
*cval2 = TREE_OPERAND (arg, 1);
else if (operand_equal_p (*cval2, TREE_OPERAND (arg, 1), 0))
;
else
return 0;
return 1;
default:
return 0;
}
}
/* ARG is a tree that is known to contain just arithmetic operations and
comparisons. Evaluate the operations in the tree substituting NEW0 for
any occurrence of OLD0 as an operand of a comparison and likewise for
NEW1 and OLD1. */
static tree
eval_subst (arg, old0, new0, old1, new1)
tree arg;
tree old0, new0, old1, new1;
{
tree type = TREE_TYPE (arg);
enum tree_code code = TREE_CODE (arg);
char class = TREE_CODE_CLASS (code);
/* We can handle some of the 'e' cases here. */
if (class == 'e' && code == TRUTH_NOT_EXPR)
class = '1';
else if (class == 'e'
&& (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR))
class = '2';
switch (class)
{
case '1':
return fold (build1 (code, type,
eval_subst (TREE_OPERAND (arg, 0),
old0, new0, old1, new1)));
case '2':
return fold (build (code, type,
eval_subst (TREE_OPERAND (arg, 0),
old0, new0, old1, new1),
eval_subst (TREE_OPERAND (arg, 1),
old0, new0, old1, new1)));
case 'e':
switch (code)
{
case SAVE_EXPR:
return eval_subst (TREE_OPERAND (arg, 0), old0, new0, old1, new1);
case COMPOUND_EXPR:
return eval_subst (TREE_OPERAND (arg, 1), old0, new0, old1, new1);
case COND_EXPR:
return fold (build (code, type,
eval_subst (TREE_OPERAND (arg, 0),
old0, new0, old1, new1),
eval_subst (TREE_OPERAND (arg, 1),
old0, new0, old1, new1),
eval_subst (TREE_OPERAND (arg, 2),
old0, new0, old1, new1)));
default:
break;
}
/* fall through - ??? */
case '<':
{
tree arg0 = TREE_OPERAND (arg, 0);
tree arg1 = TREE_OPERAND (arg, 1);
/* We need to check both for exact equality and tree equality. The
former will be true if the operand has a side-effect. In that
case, we know the operand occurred exactly once. */
if (arg0 == old0 || operand_equal_p (arg0, old0, 0))
arg0 = new0;
else if (arg0 == old1 || operand_equal_p (arg0, old1, 0))
arg0 = new1;
if (arg1 == old0 || operand_equal_p (arg1, old0, 0))
arg1 = new0;
else if (arg1 == old1 || operand_equal_p (arg1, old1, 0))
arg1 = new1;
return fold (build (code, type, arg0, arg1));
}
default:
return arg;
}
}
/* Return a tree for the case when the result of an expression is RESULT
converted to TYPE and OMITTED was previously an operand of the expression
but is now not needed (e.g., we folded OMITTED * 0).
If OMITTED has side effects, we must evaluate it. Otherwise, just do
the conversion of RESULT to TYPE. */
static tree
omit_one_operand (type, result, omitted)
tree type, result, omitted;
{
tree t = convert (type, result);
if (TREE_SIDE_EFFECTS (omitted))
return build (COMPOUND_EXPR, type, omitted, t);
return non_lvalue (t);
}
/* Similar, but call pedantic_non_lvalue instead of non_lvalue. */
static tree
pedantic_omit_one_operand (type, result, omitted)
tree type, result, omitted;
{
tree t = convert (type, result);
if (TREE_SIDE_EFFECTS (omitted))
return build (COMPOUND_EXPR, type, omitted, t);
return pedantic_non_lvalue (t);
}
/* Return a simplified tree node for the truth-negation of ARG. This
never alters ARG itself. We assume that ARG is an operation that
returns a truth value (0 or 1). */
tree
invert_truthvalue (arg)
tree arg;
{
tree type = TREE_TYPE (arg);
enum tree_code code = TREE_CODE (arg);
if (code == ERROR_MARK)
return arg;
/* If this is a comparison, we can simply invert it, except for
floating-point non-equality comparisons, in which case we just
enclose a TRUTH_NOT_EXPR around what we have. */
if (TREE_CODE_CLASS (code) == '<')
{
if (FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
&& !flag_fast_math && code != NE_EXPR && code != EQ_EXPR)
return build1 (TRUTH_NOT_EXPR, type, arg);
else
return build (invert_tree_comparison (code), type,
TREE_OPERAND (arg, 0), TREE_OPERAND (arg, 1));
}
switch (code)
{
case INTEGER_CST:
return convert (type, build_int_2 (TREE_INT_CST_LOW (arg) == 0
&& TREE_INT_CST_HIGH (arg) == 0, 0));
case TRUTH_AND_EXPR:
return build (TRUTH_OR_EXPR, type,
invert_truthvalue (TREE_OPERAND (arg, 0)),
invert_truthvalue (TREE_OPERAND (arg, 1)));
case TRUTH_OR_EXPR:
return build (TRUTH_AND_EXPR, type,
invert_truthvalue (TREE_OPERAND (arg, 0)),
invert_truthvalue (TREE_OPERAND (arg, 1)));
case TRUTH_XOR_EXPR:
/* Here we can invert either operand. We invert the first operand
unless the second operand is a TRUTH_NOT_EXPR in which case our
result is the XOR of the first operand with the inside of the
negation of the second operand. */
if (TREE_CODE (TREE_OPERAND (arg, 1)) == TRUTH_NOT_EXPR)
return build (TRUTH_XOR_EXPR, type, TREE_OPERAND (arg, 0),
TREE_OPERAND (TREE_OPERAND (arg, 1), 0));
else
return build (TRUTH_XOR_EXPR, type,
invert_truthvalue (TREE_OPERAND (arg, 0)),
TREE_OPERAND (arg, 1));
case TRUTH_ANDIF_EXPR:
return build (TRUTH_ORIF_EXPR, type,
invert_truthvalue (TREE_OPERAND (arg, 0)),
invert_truthvalue (TREE_OPERAND (arg, 1)));
case TRUTH_ORIF_EXPR:
return build (TRUTH_ANDIF_EXPR, type,
invert_truthvalue (TREE_OPERAND (arg, 0)),
invert_truthvalue (TREE_OPERAND (arg, 1)));
case TRUTH_NOT_EXPR:
return TREE_OPERAND (arg, 0);
case COND_EXPR:
return build (COND_EXPR, type, TREE_OPERAND (arg, 0),
invert_truthvalue (TREE_OPERAND (arg, 1)),
invert_truthvalue (TREE_OPERAND (arg, 2)));
case COMPOUND_EXPR:
return build (COMPOUND_EXPR, type, TREE_OPERAND (arg, 0),
invert_truthvalue (TREE_OPERAND (arg, 1)));
case NON_LVALUE_EXPR:
return invert_truthvalue (TREE_OPERAND (arg, 0));
case NOP_EXPR:
case CONVERT_EXPR:
case FLOAT_EXPR:
return build1 (TREE_CODE (arg), type,
invert_truthvalue (TREE_OPERAND (arg, 0)));
case BIT_AND_EXPR:
if (!integer_onep (TREE_OPERAND (arg, 1)))
break;
return build (EQ_EXPR, type, arg, convert (type, integer_zero_node));
case SAVE_EXPR:
return build1 (TRUTH_NOT_EXPR, type, arg);
case CLEANUP_POINT_EXPR:
return build1 (CLEANUP_POINT_EXPR, type,
invert_truthvalue (TREE_OPERAND (arg, 0)));
default:
break;
}
if (TREE_CODE (TREE_TYPE (arg)) != BOOLEAN_TYPE)
abort ();
return build1 (TRUTH_NOT_EXPR, type, arg);
}
/* Given a bit-wise operation CODE applied to ARG0 and ARG1, see if both
operands are another bit-wise operation with a common input. If so,
distribute the bit operations to save an operation and possibly two if
constants are involved. For example, convert
(A | B) & (A | C) into A | (B & C)
Further simplification will occur if B and C are constants.
If this optimization cannot be done, 0 will be returned. */
static tree
distribute_bit_expr (code, type, arg0, arg1)
enum tree_code code;
tree type;
tree arg0, arg1;
{
tree common;
tree left, right;
if (TREE_CODE (arg0) != TREE_CODE (arg1)
|| TREE_CODE (arg0) == code
|| (TREE_CODE (arg0) != BIT_AND_EXPR
&& TREE_CODE (arg0) != BIT_IOR_EXPR))
return 0;
if (operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1, 0), 0))
{
common = TREE_OPERAND (arg0, 0);
left = TREE_OPERAND (arg0, 1);
right = TREE_OPERAND (arg1, 1);
}
else if (operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1, 1), 0))
{
common = TREE_OPERAND (arg0, 0);
left = TREE_OPERAND (arg0, 1);
right = TREE_OPERAND (arg1, 0);
}
else if (operand_equal_p (TREE_OPERAND (arg0, 1), TREE_OPERAND (arg1, 0), 0))
{
common = TREE_OPERAND (arg0, 1);
left = TREE_OPERAND (arg0, 0);
right = TREE_OPERAND (arg1, 1);
}
else if (operand_equal_p (TREE_OPERAND (arg0, 1), TREE_OPERAND (arg1, 1), 0))
{
common = TREE_OPERAND (arg0, 1);
left = TREE_OPERAND (arg0, 0);
right = TREE_OPERAND (arg1, 0);
}
else
return 0;
return fold (build (TREE_CODE (arg0), type, common,
fold (build (code, type, left, right))));
}
/* Return a BIT_FIELD_REF of type TYPE to refer to BITSIZE bits of INNER
starting at BITPOS. The field is unsigned if UNSIGNEDP is non-zero. */
static tree
make_bit_field_ref (inner, type, bitsize, bitpos, unsignedp)
tree inner;
tree type;
int bitsize, bitpos;
int unsignedp;
{
tree result = build (BIT_FIELD_REF, type, inner,
size_int (bitsize), bitsize_int (bitpos, 0L));
TREE_UNSIGNED (result) = unsignedp;
return result;
}
/* Optimize a bit-field compare.
There are two cases: First is a compare against a constant and the
second is a comparison of two items where the fields are at the same
bit position relative to the start of a chunk (byte, halfword, word)
large enough to contain it. In these cases we can avoid the shift
implicit in bitfield extractions.
For constants, we emit a compare of the shifted constant with the
BIT_AND_EXPR of a mask and a byte, halfword, or word of the operand being
compared. For two fields at the same position, we do the ANDs with the
similar mask and compare the result of the ANDs.
CODE is the comparison code, known to be either NE_EXPR or EQ_EXPR.
COMPARE_TYPE is the type of the comparison, and LHS and RHS
are the left and right operands of the comparison, respectively.
If the optimization described above can be done, we return the resulting
tree. Otherwise we return zero. */
static tree
optimize_bit_field_compare (code, compare_type, lhs, rhs)
enum tree_code code;
tree compare_type;
tree lhs, rhs;
{
int lbitpos, lbitsize, rbitpos, rbitsize;
int lnbitpos, lnbitsize, rnbitpos = 0, rnbitsize = 0;
tree type = TREE_TYPE (lhs);
tree signed_type, unsigned_type;
int const_p = TREE_CODE (rhs) == INTEGER_CST;
enum machine_mode lmode, rmode, lnmode, rnmode = VOIDmode;
int lunsignedp, runsignedp;
int lvolatilep = 0, rvolatilep = 0;
int alignment;
tree linner, rinner = NULL_TREE;
tree mask;
tree offset;
/* Get all the information about the extractions being done. If the bit size
if the same as the size of the underlying object, we aren't doing an
extraction at all and so can do nothing. */
linner = get_inner_reference (lhs, &lbitsize, &lbitpos, &offset, &lmode,
&lunsignedp, &lvolatilep, &alignment);
if (linner == lhs || lbitsize == GET_MODE_BITSIZE (lmode) || lbitsize < 0
|| offset != 0)
return 0;
if (!const_p)
{
/* If this is not a constant, we can only do something if bit positions,
sizes, and signedness are the same. */
rinner = get_inner_reference (rhs, &rbitsize, &rbitpos, &offset, &rmode,
&runsignedp, &rvolatilep, &alignment);
if (rinner == rhs || lbitpos != rbitpos || lbitsize != rbitsize
|| lunsignedp != runsignedp || offset != 0)
return 0;
}
/* See if we can find a mode to refer to this field. We should be able to,
but fail if we can't. */
lnmode = get_best_mode (lbitsize, lbitpos,
TYPE_ALIGN (TREE_TYPE (linner)), word_mode,
lvolatilep);
if (lnmode == VOIDmode)
return 0;
/* Set signed and unsigned types of the precision of this mode for the
shifts below. */
signed_type = type_for_mode (lnmode, 0);
unsigned_type = type_for_mode (lnmode, 1);
if (! const_p)
{
rnmode = get_best_mode (rbitsize, rbitpos,
TYPE_ALIGN (TREE_TYPE (rinner)), word_mode,
rvolatilep);
if (rnmode == VOIDmode)
return 0;
}
/* Compute the bit position and size for the new reference and our offset
within it. If the new reference is the same size as the original, we
won't optimize anything, so return zero. */
lnbitsize = GET_MODE_BITSIZE (lnmode);
lnbitpos = lbitpos & ~ (lnbitsize - 1);
lbitpos -= lnbitpos;
if (lnbitsize == lbitsize)
return 0;
if (! const_p)
{
rnbitsize = GET_MODE_BITSIZE (rnmode);
rnbitpos = rbitpos & ~ (rnbitsize - 1);
rbitpos -= rnbitpos;
if (rnbitsize == rbitsize)
return 0;
}
if (BYTES_BIG_ENDIAN)
lbitpos = lnbitsize - lbitsize - lbitpos;
/* Make the mask to be used against the extracted field. */
mask = build_int_2 (~0, ~0);
TREE_TYPE (mask) = unsigned_type;
force_fit_type (mask, 0);
mask = convert (unsigned_type, mask);
mask = const_binop (LSHIFT_EXPR, mask, size_int (lnbitsize - lbitsize), 0);
mask = const_binop (RSHIFT_EXPR, mask,
size_int (lnbitsize - lbitsize - lbitpos), 0);
if (! const_p)
/* If not comparing with constant, just rework the comparison
and return. */
return build (code, compare_type,
build (BIT_AND_EXPR, unsigned_type,
make_bit_field_ref (linner, unsigned_type,
lnbitsize, lnbitpos, 1),
mask),
build (BIT_AND_EXPR, unsigned_type,
make_bit_field_ref (rinner, unsigned_type,
rnbitsize, rnbitpos, 1),
mask));
/* Otherwise, we are handling the constant case. See if the constant is too
big for the field. Warn and return a tree of for 0 (false) if so. We do
this not only for its own sake, but to avoid having to test for this
error case below. If we didn't, we might generate wrong code.
For unsigned fields, the constant shifted right by the field length should
be all zero. For signed fields, the high-order bits should agree with
the sign bit. */
if (lunsignedp)
{
if (! integer_zerop (const_binop (RSHIFT_EXPR,
convert (unsigned_type, rhs),
size_int (lbitsize), 0)))
{
warning ("comparison is always %d due to width of bitfield",
code == NE_EXPR);
return convert (compare_type,
(code == NE_EXPR
? integer_one_node : integer_zero_node));
}
}
else
{
tree tem = const_binop (RSHIFT_EXPR, convert (signed_type, rhs),
size_int (lbitsize - 1), 0);
if (! integer_zerop (tem) && ! integer_all_onesp (tem))
{
warning ("comparison is always %d due to width of bitfield",
code == NE_EXPR);
return convert (compare_type,
(code == NE_EXPR
? integer_one_node : integer_zero_node));
}
}
/* Single-bit compares should always be against zero. */
if (lbitsize == 1 && ! integer_zerop (rhs))
{
code = code == EQ_EXPR ? NE_EXPR : EQ_EXPR;
rhs = convert (type, integer_zero_node);
}
/* Make a new bitfield reference, shift the constant over the
appropriate number of bits and mask it with the computed mask
(in case this was a signed field). If we changed it, make a new one. */
lhs = make_bit_field_ref (linner, unsigned_type, lnbitsize, lnbitpos, 1);
if (lvolatilep)
{
TREE_SIDE_EFFECTS (lhs) = 1;
TREE_THIS_VOLATILE (lhs) = 1;
}
rhs = fold (const_binop (BIT_AND_EXPR,
const_binop (LSHIFT_EXPR,
convert (unsigned_type, rhs),
size_int (lbitpos), 0),
mask, 0));
return build (code, compare_type,
build (BIT_AND_EXPR, unsigned_type, lhs, mask),
rhs);
}
/* Subroutine for fold_truthop: decode a field reference.
If EXP is a comparison reference, we return the innermost reference.
*PBITSIZE is set to the number of bits in the reference, *PBITPOS is
set to the starting bit number.
If the innermost field can be completely contained in a mode-sized
unit, *PMODE is set to that mode. Otherwise, it is set to VOIDmode.
*PVOLATILEP is set to 1 if the any expression encountered is volatile;
otherwise it is not changed.
*PUNSIGNEDP is set to the signedness of the field.
*PMASK is set to the mask used. This is either contained in a
BIT_AND_EXPR or derived from the width of the field.
*PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
Return 0 if this is not a component reference or is one that we can't
do anything with. */
static tree
decode_field_reference (exp, pbitsize, pbitpos, pmode, punsignedp,
pvolatilep, pmask, pand_mask)
tree exp;
int *pbitsize, *pbitpos;
enum machine_mode *pmode;
int *punsignedp, *pvolatilep;
tree *pmask;
tree *pand_mask;
{
tree and_mask = 0;
tree mask, inner, offset;
tree unsigned_type;
int precision;
int alignment;
/* All the optimizations using this function assume integer fields.
There are problems with FP fields since the type_for_size call
below can fail for, e.g., XFmode. */
if (! INTEGRAL_TYPE_P (TREE_TYPE (exp)))
return 0;
STRIP_NOPS (exp);
if (TREE_CODE (exp) == BIT_AND_EXPR)
{
and_mask = TREE_OPERAND (exp, 1);
exp = TREE_OPERAND (exp, 0);
STRIP_NOPS (exp); STRIP_NOPS (and_mask);
if (TREE_CODE (and_mask) != INTEGER_CST)
return 0;
}
inner = get_inner_reference (exp, pbitsize, pbitpos, &offset, pmode,
punsignedp, pvolatilep, &alignment);
if ((inner == exp && and_mask == 0)
|| *pbitsize < 0 || offset != 0)
return 0;
/* Compute the mask to access the bitfield. */
unsigned_type = type_for_size (*pbitsize, 1);
precision = TYPE_PRECISION (unsigned_type);
mask = build_int_2 (~0, ~0);
TREE_TYPE (mask) = unsigned_type;
force_fit_type (mask, 0);
mask = const_binop (LSHIFT_EXPR, mask, size_int (precision - *pbitsize), 0);
mask = const_binop (RSHIFT_EXPR, mask, size_int (precision - *pbitsize), 0);
/* Merge it with the mask we found in the BIT_AND_EXPR, if any. */
if (and_mask != 0)
mask = fold (build (BIT_AND_EXPR, unsigned_type,
convert (unsigned_type, and_mask), mask));
*pmask = mask;
*pand_mask = and_mask;
return inner;
}
/* Return non-zero if MASK represents a mask of SIZE ones in the low-order
bit positions. */
static int
all_ones_mask_p (mask, size)
tree mask;
int size;
{
tree type = TREE_TYPE (mask);
int precision = TYPE_PRECISION (type);
tree tmask;
tmask = build_int_2 (~0, ~0);
TREE_TYPE (tmask) = signed_type (type);
force_fit_type (tmask, 0);
return
tree_int_cst_equal (mask,
const_binop (RSHIFT_EXPR,
const_binop (LSHIFT_EXPR, tmask,
size_int (precision - size),
0),
size_int (precision - size), 0));
}
/* Subroutine for fold_truthop: determine if an operand is simple enough
to be evaluated unconditionally. */
static int
simple_operand_p (exp)
tree exp;
{
/* Strip any conversions that don't change the machine mode. */
while ((TREE_CODE (exp) == NOP_EXPR
|| TREE_CODE (exp) == CONVERT_EXPR)
&& (TYPE_MODE (TREE_TYPE (exp))
== TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
exp = TREE_OPERAND (exp, 0);
return (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c'
|| (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd'
&& ! TREE_ADDRESSABLE (exp)
&& ! TREE_THIS_VOLATILE (exp)
&& ! DECL_NONLOCAL (exp)
/* Don't regard global variables as simple. They may be
allocated in ways unknown to the compiler (shared memory,
#pragma weak, etc). */
&& ! TREE_PUBLIC (exp)
&& ! DECL_EXTERNAL (exp)
/* Loading a static variable is unduly expensive, but global
registers aren't expensive. */
&& (! TREE_STATIC (exp) || DECL_REGISTER (exp))));
}
/* The following functions are subroutines to fold_range_test and allow it to
try to change a logical combination of comparisons into a range test.
For example, both
X == 2 && X == 3 && X == 4 && X == 5
and
X >= 2 && X <= 5
are converted to
(unsigned) (X - 2) <= 3
We describe each set of comparisons as being either inside or outside
a range, using a variable named like IN_P, and then describe the
range with a lower and upper bound. If one of the bounds is omitted,
it represents either the highest or lowest value of the type.
In the comments below, we represent a range by two numbers in brackets
preceded by a "+" to designate being inside that range, or a "-" to
designate being outside that range, so the condition can be inverted by
flipping the prefix. An omitted bound is represented by a "-". For
example, "- [-, 10]" means being outside the range starting at the lowest
possible value and ending at 10, in other words, being greater than 10.
The range "+ [-, -]" is always true and hence the range "- [-, -]" is
always false.
We set up things so that the missing bounds are handled in a consistent
manner so neither a missing bound nor "true" and "false" need to be
handled using a special case. */
/* Return the result of applying CODE to ARG0 and ARG1, but handle the case
of ARG0 and/or ARG1 being omitted, meaning an unlimited range. UPPER0_P
and UPPER1_P are nonzero if the respective argument is an upper bound
and zero for a lower. TYPE, if nonzero, is the type of the result; it
must be specified for a comparison. ARG1 will be converted to ARG0's
type if both are specified. */
static tree
range_binop (code, type, arg0, upper0_p, arg1, upper1_p)
enum tree_code code;
tree type;
tree arg0, arg1;
int upper0_p, upper1_p;
{
tree tem;
int result;
int sgn0, sgn1;
/* If neither arg represents infinity, do the normal operation.
Else, if not a comparison, return infinity. Else handle the special
comparison rules. Note that most of the cases below won't occur, but
are handled for consistency. */
if (arg0 != 0 && arg1 != 0)
{
tem = fold (build (code, type != 0 ? type : TREE_TYPE (arg0),
arg0, convert (TREE_TYPE (arg0), arg1)));
STRIP_NOPS (tem);
return TREE_CODE (tem) == INTEGER_CST ? tem : 0;
}
if (TREE_CODE_CLASS (code) != '<')
return 0;
/* Set SGN[01] to -1 if ARG[01] is a lower bound, 1 for upper, and 0
for neither. In real maths, we cannot assume open ended ranges are
the same. But, this is computer arithmetic, where numbers are finite.
We can therefore make the transformation of any unbounded range with
the value Z, Z being greater than any representable number. This permits
us to treat unbounded ranges as equal. */
sgn0 = arg0 != 0 ? 0 : (upper0_p ? 1 : -1);
sgn1 = arg1 != 0 ? 0 : (upper1_p ? 1 : -1);
switch (code)
{
case EQ_EXPR:
result = sgn0 == sgn1;
break;
case NE_EXPR:
result = sgn0 != sgn1;
break;
case LT_EXPR:
result = sgn0 < sgn1;
break;
case LE_EXPR:
result = sgn0 <= sgn1;
break;
case GT_EXPR:
result = sgn0 > sgn1;
break;
case GE_EXPR:
result = sgn0 >= sgn1;
break;
default:
abort ();
}
return convert (type, result ? integer_one_node : integer_zero_node);
}
/* Given EXP, a logical expression, set the range it is testing into
variables denoted by PIN_P, PLOW, and PHIGH. Return the expression
actually being tested. *PLOW and *PHIGH will have be made the same type
as the returned expression. If EXP is not a comparison, we will most
likely not be returning a useful value and range. */
static tree
make_range (exp, pin_p, plow, phigh)
tree exp;
int *pin_p;
tree *plow, *phigh;
{
enum tree_code code;
tree arg0 = NULL_TREE, arg1 = NULL_TREE, type = NULL_TREE;
tree orig_type = NULL_TREE;
int in_p, n_in_p;
tree low, high, n_low, n_high;
/* Start with simply saying "EXP != 0" and then look at the code of EXP
and see if we can refine the range. Some of the cases below may not
happen, but it doesn't seem worth worrying about this. We "continue"
the outer loop when we've changed something; otherwise we "break"
the switch, which will "break" the while. */
in_p = 0, low = high = convert (TREE_TYPE (exp), integer_zero_node);
while (1)
{
code = TREE_CODE (exp);
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
{
arg0 = TREE_OPERAND (exp, 0);
if (TREE_CODE_CLASS (code) == '<'
|| TREE_CODE_CLASS (code) == '1'
|| TREE_CODE_CLASS (code) == '2')
type = TREE_TYPE (arg0);
if (TREE_CODE_CLASS (code) == '2'
|| TREE_CODE_CLASS (code) == '<'
|| (TREE_CODE_CLASS (code) == 'e'
&& tree_code_length[(int) code] > 1))
arg1 = TREE_OPERAND (exp, 1);
}
/* Set ORIG_TYPE as soon as TYPE is non-null so that we do not
lose a cast by accident. */
if (type != NULL_TREE && orig_type == NULL_TREE)
orig_type = type;
switch (code)
{
case TRUTH_NOT_EXPR:
in_p = ! in_p, exp = arg0;
continue;
case EQ_EXPR: case NE_EXPR:
case LT_EXPR: case LE_EXPR: case GE_EXPR: case GT_EXPR:
/* We can only do something if the range is testing for zero
and if the second operand is an integer constant. Note that
saying something is "in" the range we make is done by
complementing IN_P since it will set in the initial case of
being not equal to zero; "out" is leaving it alone. */
if (low == 0 || high == 0
|| ! integer_zerop (low) || ! integer_zerop (high)
|| TREE_CODE (arg1) != INTEGER_CST)
break;
switch (code)
{
case NE_EXPR: /* - [c, c] */
low = high = arg1;
break;
case EQ_EXPR: /* + [c, c] */
in_p = ! in_p, low = high = arg1;
break;
case GT_EXPR: /* - [-, c] */
low = 0, high = arg1;
break;
case GE_EXPR: /* + [c, -] */
in_p = ! in_p, low = arg1, high = 0;
break;
case LT_EXPR: /* - [c, -] */
low = arg1, high = 0;
break;
case LE_EXPR: /* + [-, c] */
in_p = ! in_p, low = 0, high = arg1;
break;
default:
abort ();
}
exp = arg0;
/* If this is an unsigned comparison, we also know that EXP is
greater than or equal to zero. We base the range tests we make
on that fact, so we record it here so we can parse existing
range tests. */
if (TREE_UNSIGNED (type) && (low == 0 || high == 0))
{
if (! merge_ranges (&n_in_p, &n_low, &n_high, in_p, low, high,
1, convert (type, integer_zero_node),
NULL_TREE))
break;
in_p = n_in_p, low = n_low, high = n_high;
/* If the high bound is missing, reverse the range so it
goes from zero to the low bound minus 1. */
if (high == 0)
{
in_p = ! in_p;
high = range_binop (MINUS_EXPR, NULL_TREE, low, 0,
integer_one_node, 0);
low = convert (type, integer_zero_node);
}
}
continue;
case NEGATE_EXPR:
/* (-x) IN [a,b] -> x in [-b, -a] */
n_low = range_binop (MINUS_EXPR, type,
convert (type, integer_zero_node), 0, high, 1);
n_high = range_binop (MINUS_EXPR, type,
convert (type, integer_zero_node), 0, low, 0);
low = n_low, high = n_high;
exp = arg0;
continue;
case BIT_NOT_EXPR:
/* ~ X -> -X - 1 */
exp = build (MINUS_EXPR, type, build1 (NEGATE_EXPR, type, arg0),
convert (type, integer_one_node));
continue;
case PLUS_EXPR: case MINUS_EXPR:
if (TREE_CODE (arg1) != INTEGER_CST)
break;
/* If EXP is signed, any overflow in the computation is undefined,
so we don't worry about it so long as our computations on
the bounds don't overflow. For unsigned, overflow is defined
and this is exactly the right thing. */
n_low = range_binop (code == MINUS_EXPR ? PLUS_EXPR : MINUS_EXPR,
type, low, 0, arg1, 0);
n_high = range_binop (code == MINUS_EXPR ? PLUS_EXPR : MINUS_EXPR,
type, high, 1, arg1, 0);
if ((n_low != 0 && TREE_OVERFLOW (n_low))
|| (n_high != 0 && TREE_OVERFLOW (n_high)))
break;
/* Check for an unsigned range which has wrapped around the maximum
value thus making n_high < n_low, and normalize it. */
if (n_low && n_high && tree_int_cst_lt (n_high, n_low))
{
low = range_binop (PLUS_EXPR, type, n_high, 0,
integer_one_node, 0);
high = range_binop (MINUS_EXPR, type, n_low, 0,
integer_one_node, 0);
in_p = ! in_p;
}
else
low = n_low, high = n_high;
exp = arg0;
continue;
case NOP_EXPR: case NON_LVALUE_EXPR: case CONVERT_EXPR:
if (TYPE_PRECISION (type) > TYPE_PRECISION (orig_type))
break;
if (! INTEGRAL_TYPE_P (type)
|| (low != 0 && ! int_fits_type_p (low, type))
|| (high != 0 && ! int_fits_type_p (high, type)))
break;
n_low = low, n_high = high;
if (n_low != 0)
n_low = convert (type, n_low);
if (n_high != 0)
n_high = convert (type, n_high);
/* If we're converting from an unsigned to a signed type,
we will be doing the comparison as unsigned. The tests above
have already verified that LOW and HIGH are both positive.
So we have to make sure that the original unsigned value will
be interpreted as positive. */
if (TREE_UNSIGNED (type) && ! TREE_UNSIGNED (TREE_TYPE (exp)))
{
tree equiv_type = type_for_mode (TYPE_MODE (type), 1);
tree high_positive;
/* A range without an upper bound is, naturally, unbounded.
Since convert would have cropped a very large value, use
the max value for the destination type. */
high_positive = TYPE_MAX_VALUE (equiv_type);
if (!high_positive)
{
high_positive = TYPE_MAX_VALUE (type);
if (!high_positive)
abort();
}
high_positive = fold (build (RSHIFT_EXPR, type,
convert (type, high_positive),
convert (type, integer_one_node)));
/* If the low bound is specified, "and" the range with the
range for which the original unsigned value will be
positive. */
if (low != 0)
{
if (! merge_ranges (&n_in_p, &n_low, &n_high,
1, n_low, n_high,
1, convert (type, integer_zero_node),
high_positive))
break;
in_p = (n_in_p == in_p);
}
else
{
/* Otherwise, "or" the range with the range of the input
that will be interpreted as negative. */
if (! merge_ranges (&n_in_p, &n_low, &n_high,
0, n_low, n_high,
1, convert (type, integer_zero_node),
high_positive))
break;
in_p = (in_p != n_in_p);
}
}
exp = arg0;
low = n_low, high = n_high;
continue;
default:
break;
}
break;
}
/* If EXP is a constant, we can evaluate whether this is true or false. */
if (TREE_CODE (exp) == INTEGER_CST)
{
in_p = in_p == (integer_onep (range_binop (GE_EXPR, integer_type_node,
exp, 0, low, 0))
&& integer_onep (range_binop (LE_EXPR, integer_type_node,
exp, 1, high, 1)));
low = high = 0;
exp = 0;
}
*pin_p = in_p, *plow = low, *phigh = high;
return exp;
}
/* Given a range, LOW, HIGH, and IN_P, an expression, EXP, and a result
type, TYPE, return an expression to test if EXP is in (or out of, depending
on IN_P) the range. */
static tree
build_range_check (type, exp, in_p, low, high)
tree type;
tree exp;
int in_p;
tree low, high;
{
tree etype = TREE_TYPE (exp);
tree utype, value;
if (! in_p
&& (0 != (value = build_range_check (type, exp, 1, low, high))))
return invert_truthvalue (value);
else if (low == 0 && high == 0)
return convert (type, integer_one_node);
else if (low == 0)
return fold (build (LE_EXPR, type, exp, high));
else if (high == 0)
return fold (build (GE_EXPR, type, exp, low));
else if (operand_equal_p (low, high, 0))
return fold (build (EQ_EXPR, type, exp, low));
else if (TREE_UNSIGNED (etype) && integer_zerop (low))
return build_range_check (type, exp, 1, 0, high);
else if (integer_zerop (low))
{
utype = unsigned_type (etype);
return build_range_check (type, convert (utype, exp), 1, 0,
convert (utype, high));
}
else if (0 != (value = const_binop (MINUS_EXPR, high, low, 0))
&& ! TREE_OVERFLOW (value))
return build_range_check (type,
fold (build (MINUS_EXPR, etype, exp, low)),
1, convert (etype, integer_zero_node), value);
else
return 0;
}
/* Given two ranges, see if we can merge them into one. Return 1 if we
can, 0 if we can't. Set the output range into the specified parameters. */
static int
merge_ranges (pin_p, plow, phigh, in0_p, low0, high0, in1_p, low1, high1)
int *pin_p;
tree *plow, *phigh;
int in0_p, in1_p;
tree low0, high0, low1, high1;
{
int no_overlap;
int subset;
int temp;
tree tem;
int in_p;
tree low, high;
int lowequal = ((low0 == 0 && low1 == 0)
|| integer_onep (range_binop (EQ_EXPR, integer_type_node,
low0, 0, low1, 0)));
int highequal = ((high0 == 0 && high1 == 0)
|| integer_onep (range_binop (EQ_EXPR, integer_type_node,
high0, 1, high1, 1)));
/* Make range 0 be the range that starts first, or ends last if they
start at the same value. Swap them if it isn't. */
if (integer_onep (range_binop (GT_EXPR, integer_type_node,
low0, 0, low1, 0))
|| (lowequal
&& integer_onep (range_binop (GT_EXPR, integer_type_node,
high1, 1, high0, 1))))
{
temp = in0_p, in0_p = in1_p, in1_p = temp;
tem = low0, low0 = low1, low1 = tem;
tem = high0, high0 = high1, high1 = tem;
}
/* Now flag two cases, whether the ranges are disjoint or whether the
second range is totally subsumed in the first. Note that the tests
below are simplified by the ones above. */
no_overlap = integer_onep (range_binop (LT_EXPR, integer_type_node,
high0, 1, low1, 0));
subset = integer_onep (range_binop (LE_EXPR, integer_type_node,
high1, 1, high0, 1));
/* We now have four cases, depending on whether we are including or
excluding the two ranges. */
if (in0_p && in1_p)
{
/* If they don't overlap, the result is false. If the second range
is a subset it is the result. Otherwise, the range is from the start
of the second to the end of the first. */
if (no_overlap)
in_p = 0, low = high = 0;
else if (subset)
in_p = 1, low = low1, high = high1;
else
in_p = 1, low = low1, high = high0;
}
else if (in0_p && ! in1_p)
{
/* If they don't overlap, the result is the first range. If they are
equal, the result is false. If the second range is a subset of the
first, and the ranges begin at the same place, we go from just after
the end of the first range to the end of the second. If the second
range is not a subset of the first, or if it is a subset and both
ranges end at the same place, the range starts at the start of the
first range and ends just before the second range.
Otherwise, we can't describe this as a single range. */
if (no_overlap)
in_p = 1, low = low0, high = high0;
else if (lowequal && highequal)
in_p = 0, low = high = 0;
else if (subset && lowequal)
{
in_p = 1, high = high0;
low = range_binop (PLUS_EXPR, NULL_TREE, high1, 0,
integer_one_node, 0);
}
else if (! subset || highequal)
{
in_p = 1, low = low0;
high = range_binop (MINUS_EXPR, NULL_TREE, low1, 0,
integer_one_node, 0);
}
else
return 0;
}
else if (! in0_p && in1_p)
{
/* If they don't overlap, the result is the second range. If the second
is a subset of the first, the result is false. Otherwise,
the range starts just after the first range and ends at the
end of the second. */
if (no_overlap)
in_p = 1, low = low1, high = high1;
else if (subset)
in_p = 0, low = high = 0;
else
{
in_p = 1, high = high1;
low = range_binop (PLUS_EXPR, NULL_TREE, high0, 1,
integer_one_node, 0);
}
}
else
{
/* The case where we are excluding both ranges. Here the complex case
is if they don't overlap. In that case, the only time we have a
range is if they are adjacent. If the second is a subset of the
first, the result is the first. Otherwise, the range to exclude
starts at the beginning of the first range and ends at the end of the
second. */
if (no_overlap)
{
if (integer_onep (range_binop (EQ_EXPR, integer_type_node,
range_binop (PLUS_EXPR, NULL_TREE,
high0, 1,
integer_one_node, 1),
1, low1, 0)))
in_p = 0, low = low0, high = high1;
else
return 0;
}
else if (subset)
in_p = 0, low = low0, high = high0;
else
in_p = 0, low = low0, high = high1;
}
*pin_p = in_p, *plow = low, *phigh = high;
return 1;
}
/* EXP is some logical combination of boolean tests. See if we can
merge it into some range test. Return the new tree if so. */
static tree
fold_range_test (exp)
tree exp;
{
int or_op = (TREE_CODE (exp) == TRUTH_ORIF_EXPR
|| TREE_CODE (exp) == TRUTH_OR_EXPR);
int in0_p, in1_p, in_p;
tree low0, low1, low, high0, high1, high;
tree lhs = make_range (TREE_OPERAND (exp, 0), &in0_p, &low0, &high0);
tree rhs = make_range (TREE_OPERAND (exp, 1), &in1_p, &low1, &high1);
tree tem;
/* If this is an OR operation, invert both sides; we will invert
again at the end. */
if (or_op)
in0_p = ! in0_p, in1_p = ! in1_p;
/* If both expressions are the same, if we can merge the ranges, and we
can build the range test, return it or it inverted. If one of the
ranges is always true or always false, consider it to be the same
expression as the other. */
if ((lhs == 0 || rhs == 0 || operand_equal_p (lhs, rhs, 0))
&& merge_ranges (&in_p, &low, &high, in0_p, low0, high0,
in1_p, low1, high1)
&& 0 != (tem = (build_range_check (TREE_TYPE (exp),
lhs != 0 ? lhs
: rhs != 0 ? rhs : integer_zero_node,
in_p, low, high))))
return or_op ? invert_truthvalue (tem) : tem;
/* On machines where the branch cost is expensive, if this is a
short-circuited branch and the underlying object on both sides
is the same, make a non-short-circuit operation. */
else if (BRANCH_COST >= 2
&& (TREE_CODE (exp) == TRUTH_ANDIF_EXPR
|| TREE_CODE (exp) == TRUTH_ORIF_EXPR)
&& operand_equal_p (lhs, rhs, 0))
{
/* If simple enough, just rewrite. Otherwise, make a SAVE_EXPR
unless we are at top level or LHS contains a PLACEHOLDER_EXPR, in
which cases we can't do this. */
if (simple_operand_p (lhs))
return build (TREE_CODE (exp) == TRUTH_ANDIF_EXPR
? TRUTH_AND_EXPR : TRUTH_OR_EXPR,
TREE_TYPE (exp), TREE_OPERAND (exp, 0),
TREE_OPERAND (exp, 1));
else if (current_function_decl != 0
&& ! contains_placeholder_p (lhs))
{
tree common = save_expr (lhs);
if (0 != (lhs = build_range_check (TREE_TYPE (exp), common,
or_op ? ! in0_p : in0_p,
low0, high0))
&& (0 != (rhs = build_range_check (TREE_TYPE (exp), common,
or_op ? ! in1_p : in1_p,
low1, high1))))
return build (TREE_CODE (exp) == TRUTH_ANDIF_EXPR
? TRUTH_AND_EXPR : TRUTH_OR_EXPR,
TREE_TYPE (exp), lhs, rhs);
}
}
return 0;
}
/* Subroutine for fold_truthop: C is an INTEGER_CST interpreted as a P
bit value. Arrange things so the extra bits will be set to zero if and
only if C is signed-extended to its full width. If MASK is nonzero,
it is an INTEGER_CST that should be AND'ed with the extra bits. */
static tree
unextend (c, p, unsignedp, mask)
tree c;
int p;
int unsignedp;
tree mask;
{
tree type = TREE_TYPE (c);
int modesize = GET_MODE_BITSIZE (TYPE_MODE (type));
tree temp;
if (p == modesize || unsignedp)
return c;
/* We work by getting just the sign bit into the low-order bit, then
into the high-order bit, then sign-extend. We then XOR that value
with C. */
temp = const_binop (RSHIFT_EXPR, c, size_int (p - 1), 0);
temp = const_binop (BIT_AND_EXPR, temp, size_int (1), 0);
/* We must use a signed type in order to get an arithmetic right shift.
However, we must also avoid introducing accidental overflows, so that
a subsequent call to integer_zerop will work. Hence we must
do the type conversion here. At this point, the constant is either
zero or one, and the conversion to a signed type can never overflow.
We could get an overflow if this conversion is done anywhere else. */
if (TREE_UNSIGNED (type))
temp = convert (signed_type (type), temp);
temp = const_binop (LSHIFT_EXPR, temp, size_int (modesize - 1), 0);
temp = const_binop (RSHIFT_EXPR, temp, size_int (modesize - p - 1), 0);
if (mask != 0)
temp = const_binop (BIT_AND_EXPR, temp, convert (TREE_TYPE (c), mask), 0);
/* If necessary, convert the type back to match the type of C. */
if (TREE_UNSIGNED (type))
temp = convert (type, temp);
return convert (type, const_binop (BIT_XOR_EXPR, c, temp, 0));
}
/* Find ways of folding logical expressions of LHS and RHS:
Try to merge two comparisons to the same innermost item.
Look for range tests like "ch >= '0' && ch <= '9'".
Look for combinations of simple terms on machines with expensive branches
and evaluate the RHS unconditionally.
For example, if we have p->a == 2 && p->b == 4 and we can make an
object large enough to span both A and B, we can do this with a comparison
against the object ANDed with the a mask.
If we have p->a == q->a && p->b == q->b, we may be able to use bit masking
operations to do this with one comparison.
We check for both normal comparisons and the BIT_AND_EXPRs made this by
function and the one above.
CODE is the logical operation being done. It can be TRUTH_ANDIF_EXPR,
TRUTH_AND_EXPR, TRUTH_ORIF_EXPR, or TRUTH_OR_EXPR.
TRUTH_TYPE is the type of the logical operand and LHS and RHS are its
two operands.
We return the simplified tree or 0 if no optimization is possible. */
static tree
fold_truthop (code, truth_type, lhs, rhs)
enum tree_code code;
tree truth_type, lhs, rhs;
{
/* If this is the "or" of two comparisons, we can do something if we
the comparisons are NE_EXPR. If this is the "and", we can do something
if the comparisons are EQ_EXPR. I.e.,
(a->b == 2 && a->c == 4) can become (a->new == NEW).
WANTED_CODE is this operation code. For single bit fields, we can
convert EQ_EXPR to NE_EXPR so we need not reject the "wrong"
comparison for one-bit fields. */
enum tree_code wanted_code;
enum tree_code lcode, rcode;
tree ll_arg, lr_arg, rl_arg, rr_arg;
tree ll_inner, lr_inner, rl_inner, rr_inner;
int ll_bitsize, ll_bitpos, lr_bitsize, lr_bitpos;
int rl_bitsize, rl_bitpos, rr_bitsize, rr_bitpos;
int xll_bitpos, xlr_bitpos, xrl_bitpos, xrr_bitpos;
int lnbitsize, lnbitpos, rnbitsize, rnbitpos;
int ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
enum machine_mode ll_mode, lr_mode, rl_mode, rr_mode;
enum machine_mode lnmode, rnmode;
tree ll_mask, lr_mask, rl_mask, rr_mask;
tree ll_and_mask, lr_and_mask, rl_and_mask, rr_and_mask;
tree l_const, r_const;
tree lntype, rntype, result;
int first_bit, end_bit;
int volatilep;
/* Start by getting the comparison codes. Fail if anything is volatile.
If one operand is a BIT_AND_EXPR with the constant one, treat it as if
it were surrounded with a NE_EXPR. */
if (TREE_SIDE_EFFECTS (lhs) || TREE_SIDE_EFFECTS (rhs))
return 0;
lcode = TREE_CODE (lhs);
rcode = TREE_CODE (rhs);
if (lcode == BIT_AND_EXPR && integer_onep (TREE_OPERAND (lhs, 1)))
lcode = NE_EXPR, lhs = build (NE_EXPR, truth_type, lhs, integer_zero_node);
if (rcode == BIT_AND_EXPR && integer_onep (TREE_OPERAND (rhs, 1)))
rcode = NE_EXPR, rhs = build (NE_EXPR, truth_type, rhs, integer_zero_node);
if (TREE_CODE_CLASS (lcode) != '<' || TREE_CODE_CLASS (rcode) != '<')
return 0;
code = ((code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR)
? TRUTH_AND_EXPR : TRUTH_OR_EXPR);
ll_arg = TREE_OPERAND (lhs, 0);
lr_arg = TREE_OPERAND (lhs, 1);
rl_arg = TREE_OPERAND (rhs, 0);
rr_arg = TREE_OPERAND (rhs, 1);
/* If the RHS can be evaluated unconditionally and its operands are
simple, it wins to evaluate the RHS unconditionally on machines
with expensive branches. In this case, this isn't a comparison
that can be merged. */
/* @@ I'm not sure it wins on the m88110 to do this if the comparisons
are with zero (tmw). */
if (BRANCH_COST >= 2
&& INTEGRAL_TYPE_P (TREE_TYPE (rhs))
&& simple_operand_p (rl_arg)
&& simple_operand_p (rr_arg))
return build (code, truth_type, lhs, rhs);
/* See if the comparisons can be merged. Then get all the parameters for
each side. */
if ((lcode != EQ_EXPR && lcode != NE_EXPR)
|| (rcode != EQ_EXPR && rcode != NE_EXPR))
return 0;
volatilep = 0;
ll_inner = decode_field_reference (ll_arg,
&ll_bitsize, &ll_bitpos, &ll_mode,
&ll_unsignedp, &volatilep, &ll_mask,
&ll_and_mask);
lr_inner = decode_field_reference (lr_arg,
&lr_bitsize, &lr_bitpos, &lr_mode,
&lr_unsignedp, &volatilep, &lr_mask,
&lr_and_mask);
rl_inner = decode_field_reference (rl_arg,
&rl_bitsize, &rl_bitpos, &rl_mode,
&rl_unsignedp, &volatilep, &rl_mask,
&rl_and_mask);
rr_inner = decode_field_reference (rr_arg,
&rr_bitsize, &rr_bitpos, &rr_mode,
&rr_unsignedp, &volatilep, &rr_mask,
&rr_and_mask);
/* It must be true that the inner operation on the lhs of each
comparison must be the same if we are to be able to do anything.
Then see if we have constants. If not, the same must be true for
the rhs's. */
if (volatilep || ll_inner == 0 || rl_inner == 0
|| ! operand_equal_p (ll_inner, rl_inner, 0))
return 0;
if (TREE_CODE (lr_arg) == INTEGER_CST
&& TREE_CODE (rr_arg) == INTEGER_CST)
l_const = lr_arg, r_const = rr_arg;
else if (lr_inner == 0 || rr_inner == 0
|| ! operand_equal_p (lr_inner, rr_inner, 0))
return 0;
else
l_const = r_const = 0;
/* If either comparison code is not correct for our logical operation,
fail. However, we can convert a one-bit comparison against zero into
the opposite comparison against that bit being set in the field. */
wanted_code = (code == TRUTH_AND_EXPR ? EQ_EXPR : NE_EXPR);
if (lcode != wanted_code)
{
if (l_const && integer_zerop (l_const) && integer_pow2p (ll_mask))
{
/* Make the left operand unsigned, since we are only interested
in the value of one bit. Otherwise we are doing the wrong
thing below. */
ll_unsignedp = 1;
l_const = ll_mask;
}
else
return 0;
}
/* This is analogous to the code for l_const above. */
if (rcode != wanted_code)
{
if (r_const && integer_zerop (r_const) && integer_pow2p (rl_mask))
{
rl_unsignedp = 1;
r_const = rl_mask;
}
else
return 0;
}
/* See if we can find a mode that contains both fields being compared on
the left. If we can't, fail. Otherwise, update all constants and masks
to be relative to a field of that size. */
first_bit = MIN (ll_bitpos, rl_bitpos);
end_bit = MAX (ll_bitpos + ll_bitsize, rl_bitpos + rl_bitsize);
lnmode = get_best_mode (end_bit - first_bit, first_bit,
TYPE_ALIGN (TREE_TYPE (ll_inner)), word_mode,
volatilep);
if (lnmode == VOIDmode)
return 0;
lnbitsize = GET_MODE_BITSIZE (lnmode);
lnbitpos = first_bit & ~ (lnbitsize - 1);
lntype = type_for_size (lnbitsize, 1);
xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
if (BYTES_BIG_ENDIAN)
{
xll_bitpos = lnbitsize - xll_bitpos - ll_bitsize;
xrl_bitpos = lnbitsize - xrl_bitpos - rl_bitsize;
}
ll_mask = const_binop (LSHIFT_EXPR, convert (lntype, ll_mask),
size_int (xll_bitpos), 0);
rl_mask = const_binop (LSHIFT_EXPR, convert (lntype, rl_mask),
size_int (xrl_bitpos), 0);
if (l_const)
{
l_const = convert (lntype, l_const);
l_const = unextend (l_const, ll_bitsize, ll_unsignedp, ll_and_mask);
l_const = const_binop (LSHIFT_EXPR, l_const, size_int (xll_bitpos), 0);
if (! integer_zerop (const_binop (BIT_AND_EXPR, l_const,
fold (build1 (BIT_NOT_EXPR,
lntype, ll_mask)),
0)))
{
warning ("comparison is always %d", wanted_code == NE_EXPR);
return convert (truth_type,
wanted_code == NE_EXPR
? integer_one_node : integer_zero_node);
}
}
if (r_const)
{
r_const = convert (lntype, r_const);
r_const = unextend (r_const, rl_bitsize, rl_unsignedp, rl_and_mask);
r_const = const_binop (LSHIFT_EXPR, r_const, size_int (xrl_bitpos), 0);
if (! integer_zerop (const_binop (BIT_AND_EXPR, r_const,
fold (build1 (BIT_NOT_EXPR,
lntype, rl_mask)),
0)))
{
warning ("comparison is always %d", wanted_code == NE_EXPR);
return convert (truth_type,
wanted_code == NE_EXPR
? integer_one_node : integer_zero_node);
}
}
/* If the right sides are not constant, do the same for it. Also,
disallow this optimization if a size or signedness mismatch occurs
between the left and right sides. */
if (l_const == 0)
{
if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
|| ll_unsignedp != lr_unsignedp || rl_unsignedp != rr_unsignedp
/* Make sure the two fields on the right
correspond to the left without being swapped. */
|| ll_bitpos - rl_bitpos != lr_bitpos - rr_bitpos)
return 0;
first_bit = MIN (lr_bitpos, rr_bitpos);
end_bit = MAX (lr_bitpos + lr_bitsize, rr_bitpos + rr_bitsize);
rnmode = get_best_mode (end_bit - first_bit, first_bit,
TYPE_ALIGN (TREE_TYPE (lr_inner)), word_mode,
volatilep);
if (rnmode == VOIDmode)
return 0;
rnbitsize = GET_MODE_BITSIZE (rnmode);
rnbitpos = first_bit & ~ (rnbitsize - 1);
rntype = type_for_size (rnbitsize, 1);
xlr_bitpos = lr_bitpos - rnbitpos, xrr_bitpos = rr_bitpos - rnbitpos;
if (BYTES_BIG_ENDIAN)
{
xlr_bitpos = rnbitsize - xlr_bitpos - lr_bitsize;
xrr_bitpos = rnbitsize - xrr_bitpos - rr_bitsize;
}
lr_mask = const_binop (LSHIFT_EXPR, convert (rntype, lr_mask),
size_int (xlr_bitpos), 0);
rr_mask = const_binop (LSHIFT_EXPR, convert (rntype, rr_mask),
size_int (xrr_bitpos), 0);
/* Make a mask that corresponds to both fields being compared.
Do this for both items being compared. If the operands are the
same size and the bits being compared are in the same position
then we can do this by masking both and comparing the masked
results. */
ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask, 0);
lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask, 0);
if (lnbitsize == rnbitsize && xll_bitpos == xlr_bitpos)
{
lhs = make_bit_field_ref (ll_inner, lntype, lnbitsize, lnbitpos,
ll_unsignedp || rl_unsignedp);
if (! all_ones_mask_p (ll_mask, lnbitsize))
lhs = build (BIT_AND_EXPR, lntype, lhs, ll_mask);
rhs = make_bit_field_ref (lr_inner, rntype, rnbitsize, rnbitpos,
lr_unsignedp || rr_unsignedp);
if (! all_ones_mask_p (lr_mask, rnbitsize))
rhs = build (BIT_AND_EXPR, rntype, rhs, lr_mask);
return build (wanted_code, truth_type, lhs, rhs);
}
/* There is still another way we can do something: If both pairs of
fields being compared are adjacent, we may be able to make a wider
field containing them both.
Note that we still must mask the lhs/rhs expressions. Furthermore,
the mask must be shifted to account for the shift done by
make_bit_field_ref. */
if ((ll_bitsize + ll_bitpos == rl_bitpos
&& lr_bitsize + lr_bitpos == rr_bitpos)
|| (ll_bitpos == rl_bitpos + rl_bitsize
&& lr_bitpos == rr_bitpos + rr_bitsize))
{
tree type;
lhs = make_bit_field_ref (ll_inner, lntype, ll_bitsize + rl_bitsize,
MIN (ll_bitpos, rl_bitpos), ll_unsignedp);
rhs = make_bit_field_ref (lr_inner, rntype, lr_bitsize + rr_bitsize,
MIN (lr_bitpos, rr_bitpos), lr_unsignedp);
ll_mask = const_binop (RSHIFT_EXPR, ll_mask,
size_int (MIN (xll_bitpos, xrl_bitpos)), 0);
lr_mask = const_binop (RSHIFT_EXPR, lr_mask,
size_int (MIN (xlr_bitpos, xrr_bitpos)), 0);
/* Convert to the smaller type before masking out unwanted bits. */
type = lntype;
if (lntype != rntype)
{
if (lnbitsize > rnbitsize)
{
lhs = convert (rntype, lhs);
ll_mask = convert (rntype, ll_mask);
type = rntype;
}
else if (lnbitsize < rnbitsize)
{
rhs = convert (lntype, rhs);
lr_mask = convert (lntype, lr_mask);
type = lntype;
}
}
if (! all_ones_mask_p (ll_mask, ll_bitsize + rl_bitsize))
lhs = build (BIT_AND_EXPR, type, lhs, ll_mask);
if (! all_ones_mask_p (lr_mask, lr_bitsize + rr_bitsize))
rhs = build (BIT_AND_EXPR, type, rhs, lr_mask);
return build (wanted_code, truth_type, lhs, rhs);
}
return 0;
}
/* Handle the case of comparisons with constants. If there is something in
common between the masks, those bits of the constants must be the same.
If not, the condition is always false. Test for this to avoid generating
incorrect code below. */
result = const_binop (BIT_AND_EXPR, ll_mask, rl_mask, 0);
if (! integer_zerop (result)
&& simple_cst_equal (const_binop (BIT_AND_EXPR, result, l_const, 0),
const_binop (BIT_AND_EXPR, result, r_const, 0)) != 1)
{
if (wanted_code == NE_EXPR)
{
warning ("`or' of unmatched not-equal tests is always 1");
return convert (truth_type, integer_one_node);
}
else
{
warning ("`and' of mutually exclusive equal-tests is always 0");
return convert (truth_type, integer_zero_node);
}
}
/* Construct the expression we will return. First get the component
reference we will make. Unless the mask is all ones the width of
that field, perform the mask operation. Then compare with the
merged constant. */
result = make_bit_field_ref (ll_inner, lntype, lnbitsize, lnbitpos,
ll_unsignedp || rl_unsignedp);
ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask, 0);
if (! all_ones_mask_p (ll_mask, lnbitsize))
result = build (BIT_AND_EXPR, lntype, result, ll_mask);
return build (wanted_code, truth_type, result,
const_binop (BIT_IOR_EXPR, l_const, r_const, 0));
}
/* If T contains a COMPOUND_EXPR which was inserted merely to evaluate
S, a SAVE_EXPR, return the expression actually being evaluated. Note
that we may sometimes modify the tree. */
static tree
strip_compound_expr (t, s)
tree t;
tree s;
{
enum tree_code code = TREE_CODE (t);
/* See if this is the COMPOUND_EXPR we want to eliminate. */
if (code == COMPOUND_EXPR && TREE_CODE (TREE_OPERAND (t, 0)) == CONVERT_EXPR
&& TREE_OPERAND (TREE_OPERAND (t, 0), 0) == s)
return TREE_OPERAND (t, 1);
/* See if this is a COND_EXPR or a simple arithmetic operator. We
don't bother handling any other types. */
else if (code == COND_EXPR)
{
TREE_OPERAND (t, 0) = strip_compound_expr (TREE_OPERAND (t, 0), s);
TREE_OPERAND (t, 1) = strip_compound_expr (TREE_OPERAND (t, 1), s);
TREE_OPERAND (t, 2) = strip_compound_expr (TREE_OPERAND (t, 2), s);
}
else if (TREE_CODE_CLASS (code) == '1')
TREE_OPERAND (t, 0) = strip_compound_expr (TREE_OPERAND (t, 0), s);
else if (TREE_CODE_CLASS (code) == '<'
|| TREE_CODE_CLASS (code) == '2')
{
TREE_OPERAND (t, 0) = strip_compound_expr (TREE_OPERAND (t, 0), s);
TREE_OPERAND (t, 1) = strip_compound_expr (TREE_OPERAND (t, 1), s);
}
return t;
}
/* Return a node which has the indicated constant VALUE (either 0 or
1), and is of the indicated TYPE. */
static tree
constant_boolean_node (value, type)
int value;
tree type;
{
if (type == integer_type_node)
return value ? integer_one_node : integer_zero_node;
else if (TREE_CODE (type) == BOOLEAN_TYPE)
return truthvalue_conversion (value ? integer_one_node :
integer_zero_node);
else
{
tree t = build_int_2 (value, 0);
TREE_TYPE (t) = type;
return t;
}
}
/* Utility function for the following routine, to see how complex a nesting of
COND_EXPRs can be. EXPR is the expression and LIMIT is a count beyond which
we don't care (to avoid spending too much time on complex expressions.). */
static int
count_cond (expr, lim)
tree expr;
int lim;
{
int true, false;
if (TREE_CODE (expr) != COND_EXPR)
return 0;
else if (lim <= 0)
return 0;
true = count_cond (TREE_OPERAND (expr, 1), lim - 1);
false = count_cond (TREE_OPERAND (expr, 2), lim - 1 - true);
return MIN (lim, 1 + true + false);
}
/* Perform constant folding and related simplification of EXPR.
The related simplifications include x*1 => x, x*0 => 0, etc.,
and application of the associative law.
NOP_EXPR conversions may be removed freely (as long as we
are careful not to change the C type of the overall expression)
We cannot simplify through a CONVERT_EXPR, FIX_EXPR or FLOAT_EXPR,
but we can constant-fold them if they have constant operands. */
tree
fold (expr)
tree expr;
{
register tree t = expr;
tree t1 = NULL_TREE;
tree tem;
tree type = TREE_TYPE (expr);
register tree arg0 = NULL_TREE, arg1 = NULL_TREE;
register enum tree_code code = TREE_CODE (t);
register int kind;
int invert;
/* WINS will be nonzero when the switch is done
if all operands are constant. */
int wins = 1;
/* Don't try to process an RTL_EXPR since its operands aren't trees.
Likewise for a SAVE_EXPR that's already been evaluated. */
if (code == RTL_EXPR || (code == SAVE_EXPR && SAVE_EXPR_RTL (t)) != 0)
return t;
/* Return right away if already constant. */
if (TREE_CONSTANT (t))
{
if (code == CONST_DECL)
return DECL_INITIAL (t);
return t;
}
#ifdef MAX_INTEGER_COMPUTATION_MODE
check_max_integer_computation_mode (expr);
#endif
kind = TREE_CODE_CLASS (code);
if (code == NOP_EXPR || code == FLOAT_EXPR || code == CONVERT_EXPR)
{
tree subop;
/* Special case for conversion ops that can have fixed point args. */
arg0 = TREE_OPERAND (t, 0);
/* Don't use STRIP_NOPS, because signedness of argument type matters. */
if (arg0 != 0)
STRIP_TYPE_NOPS (arg0);
if (arg0 != 0 && TREE_CODE (arg0) == COMPLEX_CST)
subop = TREE_REALPART (arg0);
else
subop = arg0;
if (subop != 0 && TREE_CODE (subop) != INTEGER_CST
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
&& TREE_CODE (subop) != REAL_CST
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
)
/* Note that TREE_CONSTANT isn't enough:
static var addresses are constant but we can't
do arithmetic on them. */
wins = 0;
}
else if (kind == 'e' || kind == '<'
|| kind == '1' || kind == '2' || kind == 'r')
{
register int len = tree_code_length[(int) code];
register int i;
for (i = 0; i < len; i++)
{
tree op = TREE_OPERAND (t, i);
tree subop;
if (op == 0)
continue; /* Valid for CALL_EXPR, at least. */
if (kind == '<' || code == RSHIFT_EXPR)
{
/* Signedness matters here. Perhaps we can refine this
later. */
STRIP_TYPE_NOPS (op);
}
else
{
/* Strip any conversions that don't change the mode. */
STRIP_NOPS (op);
}
if (TREE_CODE (op) == COMPLEX_CST)
subop = TREE_REALPART (op);
else
subop = op;
if (TREE_CODE (subop) != INTEGER_CST
#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
&& TREE_CODE (subop) != REAL_CST
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
)
/* Note that TREE_CONSTANT isn't enough:
static var addresses are constant but we can't
do arithmetic on them. */
wins = 0;
if (i == 0)
arg0 = op;
else if (i == 1)
arg1 = op;
}
}
/* If this is a commutative operation, and ARG0 is a constant, move it
to ARG1 to reduce the number of tests below. */
if ((code == PLUS_EXPR || code == MULT_EXPR || code == MIN_EXPR
|| code == MAX_EXPR || code == BIT_IOR_EXPR || code == BIT_XOR_EXPR
|| code == BIT_AND_EXPR)
&& (TREE_CODE (arg0) == INTEGER_CST || TREE_CODE (arg0) == REAL_CST))
{
tem = arg0; arg0 = arg1; arg1 = tem;
tem = TREE_OPERAND (t, 0); TREE_OPERAND (t, 0) = TREE_OPERAND (t, 1);
TREE_OPERAND (t, 1) = tem;
}
/* Now WINS is set as described above,
ARG0 is the first operand of EXPR,
and ARG1 is the second operand (if it has more than one operand).
First check for cases where an arithmetic operation is applied to a
compound, conditional, or comparison operation. Push the arithmetic
operation inside the compound or conditional to see if any folding
can then be done. Convert comparison to conditional for this purpose.
The also optimizes non-constant cases that used to be done in
expand_expr.
Before we do that, see if this is a BIT_AND_EXPR or a BIT_OR_EXPR,
one of the operands is a comparison and the other is a comparison, a
BIT_AND_EXPR with the constant 1, or a truth value. In that case, the
code below would make the expression more complex. Change it to a
TRUTH_{AND,OR}_EXPR. Likewise, convert a similar NE_EXPR to
TRUTH_XOR_EXPR and an EQ_EXPR to the inversion of a TRUTH_XOR_EXPR. */
if ((code == BIT_AND_EXPR || code == BIT_IOR_EXPR
|| code == EQ_EXPR || code == NE_EXPR)
&& ((truth_value_p (TREE_CODE (arg0))
&& (truth_value_p (TREE_CODE (arg1))
|| (TREE_CODE (arg1) == BIT_AND_EXPR
&& integer_onep (TREE_OPERAND (arg1, 1)))))
|| (truth_value_p (TREE_CODE (arg1))
&& (truth_value_p (TREE_CODE (arg0))
|| (TREE_CODE (arg0) == BIT_AND_EXPR
&& integer_onep (TREE_OPERAND (arg0, 1)))))))
{
t = fold (build (code == BIT_AND_EXPR ? TRUTH_AND_EXPR
: code == BIT_IOR_EXPR ? TRUTH_OR_EXPR
: TRUTH_XOR_EXPR,
type, arg0, arg1));
if (code == EQ_EXPR)
t = invert_truthvalue (t);
return t;
}
if (TREE_CODE_CLASS (code) == '1')
{
if (TREE_CODE (arg0) == COMPOUND_EXPR)
return build (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
fold (build1 (code, type, TREE_OPERAND (arg0, 1))));
else if (TREE_CODE (arg0) == COND_EXPR)
{
t = fold (build (COND_EXPR, type, TREE_OPERAND (arg0, 0),
fold (build1 (code, type, TREE_OPERAND (arg0, 1))),
fold (build1 (code, type, TREE_OPERAND (arg0, 2)))));
/* If this was a conversion, and all we did was to move into
inside the COND_EXPR, bring it back out. But leave it if
it is a conversion from integer to integer and the
result precision is no wider than a word since such a
conversion is cheap and may be optimized away by combine,
while it couldn't if it were outside the COND_EXPR. Then return
so we don't get into an infinite recursion loop taking the
conversion out and then back in. */
if ((code == NOP_EXPR || code == CONVERT_EXPR
|| code == NON_LVALUE_EXPR)
&& TREE_CODE (t) == COND_EXPR
&& TREE_CODE (TREE_OPERAND (t, 1)) == code
&& TREE_CODE (TREE_OPERAND (t, 2)) == code
&& (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 1), 0))
== TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 2), 0)))
&& ! (INTEGRAL_TYPE_P (TREE_TYPE (t))
&& INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 1), 0)))
&& TYPE_PRECISION (TREE_TYPE (t)) <= BITS_PER_WORD))
t = build1 (code, type,
build (COND_EXPR,
TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 1), 0)),
TREE_OPERAND (t, 0),
TREE_OPERAND (TREE_OPERAND (t, 1), 0),
TREE_OPERAND (TREE_OPERAND (t, 2), 0)));
return t;
}
else if (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<')
return fold (build (COND_EXPR, type, arg0,
fold (build1 (code, type, integer_one_node)),
fold (build1 (code, type, integer_zero_node))));
}
else if (TREE_CODE_CLASS (code) == '2'
|| TREE_CODE_CLASS (code) == '<')
{
if (TREE_CODE (arg1) == COMPOUND_EXPR)
return build (COMPOUND_EXPR, type, TREE_OPERAND (arg1, 0),
fold (build (code, type,
arg0, TREE_OPERAND (arg1, 1))));
else if ((TREE_CODE (arg1) == COND_EXPR
|| (TREE_CODE_CLASS (TREE_CODE (arg1)) == '<'
&& TREE_CODE_CLASS (code) != '<'))
&& (TREE_CODE (arg0) != COND_EXPR
|| count_cond (arg0, 25) + count_cond (arg1, 25) <= 25)
&& (! TREE_SIDE_EFFECTS (arg0)
|| (current_function_decl != 0
&& ! contains_placeholder_p (arg0))))
{
tree test, true_value, false_value;
tree lhs = 0, rhs = 0;
if (TREE_CODE (arg1) == COND_EXPR)
{
test = TREE_OPERAND (arg1, 0);
true_value = TREE_OPERAND (arg1, 1);
false_value = TREE_OPERAND (arg1, 2);
}
else
{
tree testtype = TREE_TYPE (arg1);
test = arg1;
true_value = convert (testtype, integer_one_node);
false_value = convert (testtype, integer_zero_node);
}
/* If ARG0 is complex we want to make sure we only evaluate
it once. Though this is only required if it is volatile, it
might be more efficient even if it is not. However, if we
succeed in folding one part to a constant, we do not need
to make this SAVE_EXPR. Since we do this optimization
primarily to see if we do end up with constant and this
SAVE_EXPR interferes with later optimizations, suppressing
it when we can is important.
If we are not in a function, we can't make a SAVE_EXPR, so don't
try to do so. Don't try to see if the result is a constant
if an arm is a COND_EXPR since we get exponential behavior
in that case. */
if (TREE_CODE (arg0) != SAVE_EXPR && ! TREE_CONSTANT (arg0)
&& current_function_decl != 0
&& ((TREE_CODE (arg0) != VAR_DECL
&& TREE_CODE (arg0) != PARM_DECL)
|| TREE_SIDE_EFFECTS (arg0)))
{
if (TREE_CODE (true_value) != COND_EXPR)
lhs = fold (build (code, type, arg0, true_value));
if (TREE_CODE (false_value) != COND_EXPR)
rhs = fold (build (code, type, arg0, false_value));
if ((lhs == 0 || ! TREE_CONSTANT (lhs))
&& (rhs == 0 || !TREE_CONSTANT (rhs)))
arg0 = save_expr (arg0), lhs = rhs = 0;
}
if (lhs == 0)
lhs = fold (build (code, type, arg0, true_value));
if (rhs == 0)
rhs = fold (build (code, type, arg0, false_value));
test = fold (build (COND_EXPR, type, test, lhs, rhs));
if (TREE_CODE (arg0) == SAVE_EXPR)
return build (COMPOUND_EXPR, type,
convert (void_type_node, arg0),
strip_compound_expr (test, arg0));
else
return convert (type, test);
}
else if (TREE_CODE (arg0) == COMPOUND_EXPR)
return build (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
fold (build (code, type, TREE_OPERAND (arg0, 1), arg1)));
else if ((TREE_CODE (arg0) == COND_EXPR
|| (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<'
&& TREE_CODE_CLASS (code) != '<'))
&& (TREE_CODE (arg1) != COND_EXPR
|| count_cond (arg0, 25) + count_cond (arg1, 25) <= 25)
&& (! TREE_SIDE_EFFECTS (arg1)
|| (current_function_decl != 0
&& ! contains_placeholder_p (arg1))))
{
tree test, true_value, false_value;
tree lhs = 0, rhs = 0;
if (TREE_CODE (arg0) == COND_EXPR)
{
test = TREE_OPERAND (arg0, 0);
true_value = TREE_OPERAND (arg0, 1);
false_value = TREE_OPERAND (arg0, 2);
}
else
{
tree testtype = TREE_TYPE (arg0);
test = arg0;
true_value = convert (testtype, integer_one_node);
false_value = convert (testtype, integer_zero_node);
}
if (TREE_CODE (arg1) != SAVE_EXPR && ! TREE_CONSTANT (arg0)
&& current_function_decl != 0
&& ((TREE_CODE (arg1) != VAR_DECL
&& TREE_CODE (arg1) != PARM_DECL)
|| TREE_SIDE_EFFECTS (arg1)))
{
if (TREE_CODE (true_value) != COND_EXPR)
lhs = fold (build (code, type, true_value, arg1));
if (TREE_CODE (false_value) != COND_EXPR)
rhs = fold (build (code, type, false_value, arg1));
if ((lhs == 0 || ! TREE_CONSTANT (lhs))
&& (rhs == 0 || !TREE_CONSTANT (rhs)))
arg1 = save_expr (arg1), lhs = rhs = 0;
}
if (lhs == 0)
lhs = fold (build (code, type, true_value, arg1));
if (rhs == 0)
rhs = fold (build (code, type, false_value, arg1));
test = fold (build (COND_EXPR, type, test, lhs, rhs));
if (TREE_CODE (arg1) == SAVE_EXPR)
return build (COMPOUND_EXPR, type,
convert (void_type_node, arg1),
strip_compound_expr (test, arg1));
else
return convert (type, test);
}
}
else if (TREE_CODE_CLASS (code) == '<'
&& TREE_CODE (arg0) == COMPOUND_EXPR)
return build (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
fold (build (code, type, TREE_OPERAND (arg0, 1), arg1)));
else if (TREE_CODE_CLASS (code) == '<'
&& TREE_CODE (arg1) == COMPOUND_EXPR)
return build (COMPOUND_EXPR, type, TREE_OPERAND (arg1, 0),
fold (build (code, type, arg0, TREE_OPERAND (arg1, 1))));
switch (code)
{
case INTEGER_CST:
case REAL_CST:
case STRING_CST:
case COMPLEX_CST:
case CONSTRUCTOR:
return t;
case CONST_DECL:
return fold (DECL_INITIAL (t));
case NOP_EXPR:
case FLOAT_EXPR:
case CONVERT_EXPR:
case FIX_TRUNC_EXPR:
/* Other kinds of FIX are not handled properly by fold_convert. */
if (TREE_TYPE (TREE_OPERAND (t, 0)) == TREE_TYPE (t))
return TREE_OPERAND (t, 0);
/* Handle cases of two conversions in a row. */
if (TREE_CODE (TREE_OPERAND (t, 0)) == NOP_EXPR
|| TREE_CODE (TREE_OPERAND (t, 0)) == CONVERT_EXPR)
{
tree inside_type = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0));
tree inter_type = TREE_TYPE (TREE_OPERAND (t, 0));
tree final_type = TREE_TYPE (t);
int inside_int = INTEGRAL_TYPE_P (inside_type);
int inside_ptr = POINTER_TYPE_P (inside_type);
int inside_float = FLOAT_TYPE_P (inside_type);
int inside_prec = TYPE_PRECISION (inside_type);
int inside_unsignedp = TREE_UNSIGNED (inside_type);
int inter_int = INTEGRAL_TYPE_P (inter_type);
int inter_ptr = POINTER_TYPE_P (inter_type);
int inter_float = FLOAT_TYPE_P (inter_type);
int inter_prec = TYPE_PRECISION (inter_type);
int inter_unsignedp = TREE_UNSIGNED (inter_type);
int final_int = INTEGRAL_TYPE_P (final_type);
int final_ptr = POINTER_TYPE_P (final_type);
int final_float = FLOAT_TYPE_P (final_type);
int final_prec = TYPE_PRECISION (final_type);
int final_unsignedp = TREE_UNSIGNED (final_type);
/* In addition to the cases of two conversions in a row
handled below, if we are converting something to its own
type via an object of identical or wider precision, neither
conversion is needed. */
if (inside_type == final_type
&& ((inter_int && final_int) || (inter_float && final_float))
&& inter_prec >= final_prec)
return TREE_OPERAND (TREE_OPERAND (t, 0), 0);
/* Likewise, if the intermediate and final types are either both
float or both integer, we don't need the middle conversion if
it is wider than the final type and doesn't change the signedness
(for integers). Avoid this if the final type is a pointer
since then we sometimes need the inner conversion. Likewise if
the outer has a precision not equal to the size of its mode. */
if ((((inter_int || inter_ptr) && (inside_int || inside_ptr))
|| (inter_float && inside_float))
&& inter_prec >= inside_prec
&& (inter_float || inter_unsignedp == inside_unsignedp)
&& ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (final_type))
&& TYPE_MODE (final_type) == TYPE_MODE (inter_type))
&& ! final_ptr)
return convert (final_type, TREE_OPERAND (TREE_OPERAND (t, 0), 0));
/* If we have a sign-extension of a zero-extended value, we can
replace that by a single zero-extension. */
if (inside_int && inter_int && final_int
&& inside_prec < inter_prec && inter_prec < final_prec
&& inside_unsignedp && !inter_unsignedp)
return convert (final_type, TREE_OPERAND (TREE_OPERAND (t, 0), 0));
/* Two conversions in a row are not needed unless:
- some conversion is floating-point (overstrict for now), or
- the intermediate type is narrower than both initial and
final, or
- the intermediate type and innermost type differ in signedness,
and the outermost type is wider than the intermediate, or
- the initial type is a pointer type and the precisions of the
intermediate and final types differ, or
- the final type is a pointer type and the precisions of the
initial and intermediate types differ. */
if (! inside_float && ! inter_float && ! final_float
&& (inter_prec > inside_prec || inter_prec > final_prec)
&& ! (inside_int && inter_int
&& inter_unsignedp != inside_unsignedp
&& inter_prec < final_prec)
&& ((inter_unsignedp && inter_prec > inside_prec)
== (final_unsignedp && final_prec > inter_prec))
&& ! (inside_ptr && inter_prec != final_prec)
&& ! (final_ptr && inside_prec != inter_prec)
&& ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (final_type))
&& TYPE_MODE (final_type) == TYPE_MODE (inter_type))
&& ! final_ptr)
return convert (final_type, TREE_OPERAND (TREE_OPERAND (t, 0), 0));
}
if (TREE_CODE (TREE_OPERAND (t, 0)) == MODIFY_EXPR
&& TREE_CONSTANT (TREE_OPERAND (TREE_OPERAND (t, 0), 1))
/* Detect assigning a bitfield. */
&& !(TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) == COMPONENT_REF
&& DECL_BIT_FIELD (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 1))))
{
/* Don't leave an assignment inside a conversion
unless assigning a bitfield. */
tree prev = TREE_OPERAND (t, 0);
TREE_OPERAND (t, 0) = TREE_OPERAND (prev, 1);
/* First do the assignment, then return converted constant. */
t = build (COMPOUND_EXPR, TREE_TYPE (t), prev, fold (t));
TREE_USED (t) = 1;
return t;
}
if (!wins)
{
TREE_CONSTANT (t) = TREE_CONSTANT (arg0);
return t;
}
return fold_convert (t, arg0);
#if 0 /* This loses on &"foo"[0]. */
case ARRAY_REF:
{
int i;
/* Fold an expression like: "foo"[2] */
if (TREE_CODE (arg0) == STRING_CST
&& TREE_CODE (arg1) == INTEGER_CST
&& !TREE_INT_CST_HIGH (arg1)
&& (i = TREE_INT_CST_LOW (arg1)) < TREE_STRING_LENGTH (arg0))
{
t = build_int_2 (TREE_STRING_POINTER (arg0)[i], 0);
TREE_TYPE (t) = TREE_TYPE (TREE_TYPE (arg0));
force_fit_type (t, 0);
}
}
return t;
#endif /* 0 */
case COMPONENT_REF:
if (TREE_CODE (arg0) == CONSTRUCTOR)
{
tree m = purpose_member (arg1, CONSTRUCTOR_ELTS (arg0));
if (m)
t = TREE_VALUE (m);
}
return t;
case RANGE_EXPR:
TREE_CONSTANT (t) = wins;
return t;
case NEGATE_EXPR:
if (wins)
{
if (TREE_CODE (arg0) == INTEGER_CST)
{
HOST_WIDE_INT low, high;
int overflow = neg_double (TREE_INT_CST_LOW (arg0),
TREE_INT_CST_HIGH (arg0),
&low, &high);
t = build_int_2 (low, high);
TREE_TYPE (t) = type;
TREE_OVERFLOW (t)
= (TREE_OVERFLOW (arg0)
| force_fit_type (t, overflow && !TREE_UNSIGNED (type)));
TREE_CONSTANT_OVERFLOW (t)
= TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg0);
}
else if (TREE_CODE (arg0) == REAL_CST)
t = build_real (type, REAL_VALUE_NEGATE (TREE_REAL_CST (arg0)));
}
else if (TREE_CODE (arg0) == NEGATE_EXPR)
return TREE_OPERAND (arg0, 0);
/* Convert - (a - b) to (b - a) for non-floating-point. */
else if (TREE_CODE (arg0) == MINUS_EXPR && ! FLOAT_TYPE_P (type))
return build (MINUS_EXPR, type, TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg0, 0));
return t;
case ABS_EXPR:
if (wins)
{
if (TREE_CODE (arg0) == INTEGER_CST)
{
if (! TREE_UNSIGNED (type)
&& TREE_INT_CST_HIGH (arg0) < 0)
{
HOST_WIDE_INT low, high;
int overflow = neg_double (TREE_INT_CST_LOW (arg0),
TREE_INT_CST_HIGH (arg0),
&low, &high);
t = build_int_2 (low, high);
TREE_TYPE (t) = type;
TREE_OVERFLOW (t)
= (TREE_OVERFLOW (arg0)
| force_fit_type (t, overflow));
TREE_CONSTANT_OVERFLOW (t)
= TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg0);
}
}
else if (TREE_CODE (arg0) == REAL_CST)
{
if (REAL_VALUE_NEGATIVE (TREE_REAL_CST (arg0)))
t = build_real (type,
REAL_VALUE_NEGATE (TREE_REAL_CST (arg0)));
}
}
else if (TREE_CODE (arg0) == ABS_EXPR || TREE_CODE (arg0) == NEGATE_EXPR)
return build1 (ABS_EXPR, type, TREE_OPERAND (arg0, 0));
return t;
case CONJ_EXPR:
if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
return arg0;
else if (TREE_CODE (arg0) == COMPLEX_EXPR)
return build (COMPLEX_EXPR, TREE_TYPE (arg0),
TREE_OPERAND (arg0, 0),
fold (build1 (NEGATE_EXPR,
TREE_TYPE (TREE_TYPE (arg0)),
TREE_OPERAND (arg0, 1))));
else if (TREE_CODE (arg0) == COMPLEX_CST)
return build_complex (type, TREE_OPERAND (arg0, 0),
fold (build1 (NEGATE_EXPR,
TREE_TYPE (TREE_TYPE (arg0)),
TREE_OPERAND (arg0, 1))));
else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
return fold (build (TREE_CODE (arg0), type,
fold (build1 (CONJ_EXPR, type,
TREE_OPERAND (arg0, 0))),
fold (build1 (CONJ_EXPR,
type, TREE_OPERAND (arg0, 1)))));
else if (TREE_CODE (arg0) == CONJ_EXPR)
return TREE_OPERAND (arg0, 0);
return t;
case BIT_NOT_EXPR:
if (wins)
{
t = build_int_2 (~ TREE_INT_CST_LOW (arg0),
~ TREE_INT_CST_HIGH (arg0));
TREE_TYPE (t) = type;
force_fit_type (t, 0);
TREE_OVERFLOW (t) = TREE_OVERFLOW (arg0);
TREE_CONSTANT_OVERFLOW (t) = TREE_CONSTANT_OVERFLOW (arg0);
}
else if (TREE_CODE (arg0) == BIT_NOT_EXPR)
return TREE_OPERAND (arg0, 0);
return t;
case PLUS_EXPR:
/* A + (-B) -> A - B */
if (TREE_CODE (arg1) == NEGATE_EXPR)
return fold (build (MINUS_EXPR, type, arg0, TREE_OPERAND (arg1, 0)));
else if (! FLOAT_TYPE_P (type))
{
if (integer_zerop (arg1))
return non_lvalue (convert (type, arg0));
/* If we are adding two BIT_AND_EXPR's, both of which are and'ing
with a constant, and the two constants have no bits in common,
we should treat this as a BIT_IOR_EXPR since this may produce more
simplifications. */
if (TREE_CODE (arg0) == BIT_AND_EXPR
&& TREE_CODE (arg1) == BIT_AND_EXPR
&& TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
&& TREE_CODE (TREE_OPERAND (arg1, 1)) == INTEGER_CST
&& integer_zerop (const_binop (BIT_AND_EXPR,
TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg1, 1), 0)))
{
code = BIT_IOR_EXPR;
goto bit_ior;
}
if (TREE_CODE (arg0) == MULT_EXPR && TREE_CODE (arg1) == MULT_EXPR)
{
tree arg00, arg01, arg10, arg11;
tree alt0 = NULL_TREE, alt1 = NULL_TREE, same;
/* (A * C) + (B * C) -> (A+B) * C.
We are most concerned about the case where C is a constant,
but other combinations show up during loop reduction. Since
it is not difficult, try all four possibilities. */
arg00 = TREE_OPERAND (arg0, 0);
arg01 = TREE_OPERAND (arg0, 1);
arg10 = TREE_OPERAND (arg1, 0);
arg11 = TREE_OPERAND (arg1, 1);
same = NULL_TREE;
if (operand_equal_p (arg01, arg11, 0))
same = arg01, alt0 = arg00, alt1 = arg10;
else if (operand_equal_p (arg00, arg10, 0))
same = arg00, alt0 = arg01, alt1 = arg11;
else if (operand_equal_p (arg00, arg11, 0))
same = arg00, alt0 = arg01, alt1 = arg10;
else if (operand_equal_p (arg01, arg10, 0))
same = arg01, alt0 = arg00, alt1 = arg11;
if (same)
return fold (build (MULT_EXPR, type,
fold (build (PLUS_EXPR, type, alt0, alt1)),
same));
}
}
/* In IEEE floating point, x+0 may not equal x. */
else if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| flag_fast_math)
&& real_zerop (arg1))
return non_lvalue (convert (type, arg0));
associate:
/* In most languages, can't associate operations on floats
through parentheses. Rather than remember where the parentheses
were, we don't associate floats at all. It shouldn't matter much.
However, associating multiplications is only very slightly
inaccurate, so do that if -ffast-math is specified. */
if (FLOAT_TYPE_P (type)
&& ! (flag_fast_math && code == MULT_EXPR))
goto binary;
/* The varsign == -1 cases happen only for addition and subtraction.
It says that the arg that was split was really CON minus VAR.
The rest of the code applies to all associative operations. */
if (!wins)
{
tree var, con;
int varsign;
if (split_tree (arg0, code, &var, &con, &varsign))
{
if (varsign == -1)
{
/* EXPR is (CON-VAR) +- ARG1. */
/* If it is + and VAR==ARG1, return just CONST. */
if (code == PLUS_EXPR && operand_equal_p (var, arg1, 0))
return convert (TREE_TYPE (t), con);
/* If ARG0 is a constant, don't change things around;
instead keep all the constant computations together. */
if (TREE_CONSTANT (arg0))
return t;
/* Otherwise return (CON +- ARG1) - VAR. */
t = build (MINUS_EXPR, type,
fold (build (code, type, con, arg1)), var);
}
else
{
/* EXPR is (VAR+CON) +- ARG1. */
/* If it is - and VAR==ARG1, return just CONST. */
if (code == MINUS_EXPR && operand_equal_p (var, arg1, 0))
return convert (TREE_TYPE (t), con);
/* If ARG0 is a constant, don't change things around;
instead keep all the constant computations together. */
if (TREE_CONSTANT (arg0))
return t;
/* Otherwise return VAR +- (ARG1 +- CON). */
tem = fold (build (code, type, arg1, con));
t = build (code, type, var, tem);
if (integer_zerop (tem)
&& (code == PLUS_EXPR || code == MINUS_EXPR))
return convert (type, var);
/* If we have x +/- (c - d) [c an explicit integer]
change it to x -/+ (d - c) since if d is relocatable
then the latter can be a single immediate insn
and the former cannot. */
if (TREE_CODE (tem) == MINUS_EXPR
&& TREE_CODE (TREE_OPERAND (tem, 0)) == INTEGER_CST)
{
tree tem1 = TREE_OPERAND (tem, 1);
TREE_OPERAND (tem, 1) = TREE_OPERAND (tem, 0);
TREE_OPERAND (tem, 0) = tem1;
TREE_SET_CODE (t,
(code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR));
}
}
return t;
}
if (split_tree (arg1, code, &var, &con, &varsign))
{
if (TREE_CONSTANT (arg1))
return t;
if (varsign == -1)
TREE_SET_CODE (t,
(code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR));
/* EXPR is ARG0 +- (CON +- VAR). */
if (TREE_CODE (t) == MINUS_EXPR
&& operand_equal_p (var, arg0, 0))
{
/* If VAR and ARG0 cancel, return just CON or -CON. */
if (code == PLUS_EXPR)
return convert (TREE_TYPE (t), con);
return fold (build1 (NEGATE_EXPR, TREE_TYPE (t),
convert (TREE_TYPE (t), con)));
}
t = build (TREE_CODE (t), type,
fold (build (code, TREE_TYPE (t), arg0, con)), var);
if (integer_zerop (TREE_OPERAND (t, 0))
&& TREE_CODE (t) == PLUS_EXPR)
return convert (TREE_TYPE (t), var);
return t;
}
}
binary:
#if defined (REAL_IS_NOT_DOUBLE) && ! defined (REAL_ARITHMETIC)
if (TREE_CODE (arg1) == REAL_CST)
return t;
#endif /* REAL_IS_NOT_DOUBLE, and no REAL_ARITHMETIC */
if (wins)
t1 = const_binop (code, arg0, arg1, 0);
if (t1 != NULL_TREE)
{
/* The return value should always have
the same type as the original expression. */
if (TREE_TYPE (t1) != TREE_TYPE (t))
t1 = convert (TREE_TYPE (t), t1);
return t1;
}
return t;
case MINUS_EXPR:
if (! FLOAT_TYPE_P (type))
{
if (! wins && integer_zerop (arg0))
return build1 (NEGATE_EXPR, type, arg1);
if (integer_zerop (arg1))
return non_lvalue (convert (type, arg0));
/* (A * C) - (B * C) -> (A-B) * C. Since we are most concerned
about the case where C is a constant, just try one of the
four possibilities. */
if (TREE_CODE (arg0) == MULT_EXPR && TREE_CODE (arg1) == MULT_EXPR
&& operand_equal_p (TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg1, 1), 0))
return fold (build (MULT_EXPR, type,
fold (build (MINUS_EXPR, type,
TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg1, 0))),
TREE_OPERAND (arg0, 1)));
}
/* Convert A - (-B) to A + B. */
else if (TREE_CODE (arg1) == NEGATE_EXPR)
return fold (build (PLUS_EXPR, type, arg0, TREE_OPERAND (arg1, 0)));
else if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| flag_fast_math)
{
/* Except with IEEE floating point, 0-x equals -x. */
if (! wins && real_zerop (arg0))
return build1 (NEGATE_EXPR, type, arg1);
/* Except with IEEE floating point, x-0 equals x. */
if (real_zerop (arg1))
return non_lvalue (convert (type, arg0));
}
/* Fold &x - &x. This can happen from &x.foo - &x.
This is unsafe for certain floats even in non-IEEE formats.
In IEEE, it is unsafe because it does wrong for NaNs.
Also note that operand_equal_p is always false if an operand
is volatile. */
if ((! FLOAT_TYPE_P (type) || flag_fast_math)
&& operand_equal_p (arg0, arg1, 0))
return convert (type, integer_zero_node);
goto associate;
case MULT_EXPR:
if (! FLOAT_TYPE_P (type))
{
if (integer_zerop (arg1))
return omit_one_operand (type, arg1, arg0);
if (integer_onep (arg1))
return non_lvalue (convert (type, arg0));
/* ((A / C) * C) is A if the division is an
EXACT_DIV_EXPR. Since C is normally a constant,
just check for one of the four possibilities. */
if (TREE_CODE (arg0) == EXACT_DIV_EXPR
&& operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
return TREE_OPERAND (arg0, 0);
/* (a * (1 << b)) is (a << b) */
if (TREE_CODE (arg1) == LSHIFT_EXPR
&& integer_onep (TREE_OPERAND (arg1, 0)))
return fold (build (LSHIFT_EXPR, type, arg0,
TREE_OPERAND (arg1, 1)));
if (TREE_CODE (arg0) == LSHIFT_EXPR
&& integer_onep (TREE_OPERAND (arg0, 0)))
return fold (build (LSHIFT_EXPR, type, arg1,
TREE_OPERAND (arg0, 1)));
}
else
{
/* x*0 is 0, except for IEEE floating point. */
if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| flag_fast_math)
&& real_zerop (arg1))
return omit_one_operand (type, arg1, arg0);
/* In IEEE floating point, x*1 is not equivalent to x for snans.
However, ANSI says we can drop signals,
so we can do this anyway. */
if (real_onep (arg1))
return non_lvalue (convert (type, arg0));
/* x*2 is x+x */
if (! wins && real_twop (arg1) && current_function_decl != 0
&& ! contains_placeholder_p (arg0))
{
tree arg = save_expr (arg0);
return build (PLUS_EXPR, type, arg, arg);
}
}
goto associate;
case BIT_IOR_EXPR:
bit_ior:
{
register enum tree_code code0, code1;
if (integer_all_onesp (arg1))
return omit_one_operand (type, arg1, arg0);
if (integer_zerop (arg1))
return non_lvalue (convert (type, arg0));
t1 = distribute_bit_expr (code, type, arg0, arg1);
if (t1 != NULL_TREE)
return t1;
/* (A << C1) | (A >> C2) if A is unsigned and C1+C2 is the size of A
is a rotate of A by C1 bits. */
/* (A << B) | (A >> (Z - B)) if A is unsigned and Z is the size of A
is a rotate of A by B bits. */
code0 = TREE_CODE (arg0);
code1 = TREE_CODE (arg1);
if (((code0 == RSHIFT_EXPR && code1 == LSHIFT_EXPR)
|| (code1 == RSHIFT_EXPR && code0 == LSHIFT_EXPR))
&& operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1,0), 0)
&& TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 0))))
{
register tree tree01, tree11;
register enum tree_code code01, code11;
tree01 = TREE_OPERAND (arg0, 1);
tree11 = TREE_OPERAND (arg1, 1);
STRIP_NOPS (tree01);
STRIP_NOPS (tree11);
code01 = TREE_CODE (tree01);
code11 = TREE_CODE (tree11);
if (code01 == INTEGER_CST
&& code11 == INTEGER_CST
&& TREE_INT_CST_HIGH (tree01) == 0
&& TREE_INT_CST_HIGH (tree11) == 0
&& ((TREE_INT_CST_LOW (tree01) + TREE_INT_CST_LOW (tree11))
== TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0)))))
return build (LROTATE_EXPR, type, TREE_OPERAND (arg0, 0),
code0 == LSHIFT_EXPR ? tree01 : tree11);
else if (code11 == MINUS_EXPR)
{
tree tree110, tree111;
tree110 = TREE_OPERAND (tree11, 0);
tree111 = TREE_OPERAND (tree11, 1);
STRIP_NOPS (tree110);
STRIP_NOPS (tree111);
if (TREE_CODE (tree110) == INTEGER_CST
&& TREE_INT_CST_HIGH (tree110) == 0
&& (TREE_INT_CST_LOW (tree110)
== TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0))))
&& operand_equal_p (tree01, tree111, 0))
return build ((code0 == LSHIFT_EXPR
? LROTATE_EXPR
: RROTATE_EXPR),
type, TREE_OPERAND (arg0, 0), tree01);
}
else if (code01 == MINUS_EXPR)
{
tree tree010, tree011;
tree010 = TREE_OPERAND (tree01, 0);
tree011 = TREE_OPERAND (tree01, 1);
STRIP_NOPS (tree010);
STRIP_NOPS (tree011);
if (TREE_CODE (tree010) == INTEGER_CST
&& TREE_INT_CST_HIGH (tree010) == 0
&& (TREE_INT_CST_LOW (tree010)
== TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0))))
&& operand_equal_p (tree11, tree011, 0))
return build ((code0 != LSHIFT_EXPR
? LROTATE_EXPR
: RROTATE_EXPR),
type, TREE_OPERAND (arg0, 0), tree11);
}
}
goto associate;
}
case BIT_XOR_EXPR:
if (integer_zerop (arg1))
return non_lvalue (convert (type, arg0));
if (integer_all_onesp (arg1))
return fold (build1 (BIT_NOT_EXPR, type, arg0));
goto associate;
case BIT_AND_EXPR:
bit_and:
if (integer_all_onesp (arg1))
return non_lvalue (convert (type, arg0));
if (integer_zerop (arg1))
return omit_one_operand (type, arg1, arg0);
t1 = distribute_bit_expr (code, type, arg0, arg1);
if (t1 != NULL_TREE)
return t1;
/* Simplify ((int)c & 0x377) into (int)c, if c is unsigned char. */
if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == NOP_EXPR
&& TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg1, 0))))
{
int prec = TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg1, 0)));
if (prec < BITS_PER_WORD && prec < HOST_BITS_PER_WIDE_INT
&& (~TREE_INT_CST_LOW (arg0)
& (((HOST_WIDE_INT) 1 << prec) - 1)) == 0)
return build1 (NOP_EXPR, type, TREE_OPERAND (arg1, 0));
}
if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg0) == NOP_EXPR
&& TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 0))))
{
int prec = TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0)));
if (prec < BITS_PER_WORD && prec < HOST_BITS_PER_WIDE_INT
&& (~TREE_INT_CST_LOW (arg1)
& (((HOST_WIDE_INT) 1 << prec) - 1)) == 0)
return build1 (NOP_EXPR, type, TREE_OPERAND (arg0, 0));
}
goto associate;
case BIT_ANDTC_EXPR:
if (integer_all_onesp (arg0))
return non_lvalue (convert (type, arg1));
if (integer_zerop (arg0))
return omit_one_operand (type, arg0, arg1);
if (TREE_CODE (arg1) == INTEGER_CST)
{
arg1 = fold (build1 (BIT_NOT_EXPR, type, arg1));
code = BIT_AND_EXPR;
goto bit_and;
}
goto binary;
case RDIV_EXPR:
/* In most cases, do nothing with a divide by zero. */
#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
#ifndef REAL_INFINITY
if (TREE_CODE (arg1) == REAL_CST && real_zerop (arg1))
return t;
#endif
#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
/* In IEEE floating point, x/1 is not equivalent to x for snans.
However, ANSI says we can drop signals, so we can do this anyway. */
if (real_onep (arg1))
return non_lvalue (convert (type, arg0));
/* If ARG1 is a constant, we can convert this to a multiply by the
reciprocal. This does not have the same rounding properties,
so only do this if -ffast-math. We can actually always safely
do it if ARG1 is a power of two, but it's hard to tell if it is
or not in a portable manner. */
if (TREE_CODE (arg1) == REAL_CST)
{
if (flag_fast_math
&& 0 != (tem = const_binop (code, build_real (type, dconst1),
arg1, 0)))
return fold (build (MULT_EXPR, type, arg0, tem));
/* Find the reciprocal if optimizing and the result is exact. */
else if (optimize)
{
REAL_VALUE_TYPE r;
r = TREE_REAL_CST (arg1);
if (exact_real_inverse (TYPE_MODE(TREE_TYPE(arg0)), &r))
{
tem = build_real (type, r);
return fold (build (MULT_EXPR, type, arg0, tem));
}
}
}
goto binary;
case TRUNC_DIV_EXPR:
case ROUND_DIV_EXPR:
case FLOOR_DIV_EXPR:
case CEIL_DIV_EXPR:
case EXACT_DIV_EXPR:
if (integer_onep (arg1))
return non_lvalue (convert (type, arg0));
if (integer_zerop (arg1))
return t;
/* If arg0 is a multiple of arg1, then rewrite to the fastest div
operation, EXACT_DIV_EXPR.
Note that only CEIL_DIV_EXPR and FLOOR_DIV_EXPR are rewritten now.
At one time others generated faster code, it's not clear if they do
after the last round to changes to the DIV code in expmed.c. */
if ((code == CEIL_DIV_EXPR || code == FLOOR_DIV_EXPR)
&& multiple_of_p (type, arg0, arg1))
return fold (build (EXACT_DIV_EXPR, type, arg0, arg1));
/* If we have ((a / C1) / C2) where both division are the same type, try
to simplify. First see if C1 * C2 overflows or not. */
if (TREE_CODE (arg0) == code && TREE_CODE (arg1) == INTEGER_CST
&& TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST)
{
tree new_divisor;
new_divisor = const_binop (MULT_EXPR, TREE_OPERAND (arg0, 1), arg1, 0);
tem = const_binop (FLOOR_DIV_EXPR, new_divisor, arg1, 0);
if (TREE_INT_CST_LOW (TREE_OPERAND (arg0, 1)) == TREE_INT_CST_LOW (tem)
&& TREE_INT_CST_HIGH (TREE_OPERAND (arg0, 1)) == TREE_INT_CST_HIGH (tem))
{
/* If no overflow, divide by C1*C2. */
return fold (build (code, type, TREE_OPERAND (arg0, 0), new_divisor));
}
}
/* Look for ((a * C1) / C3) or (((a * C1) + C2) / C3),
where C1 % C3 == 0 or C3 % C1 == 0. We can simplify these
expressions, which often appear in the offsets or sizes of
objects with a varying size. Only deal with positive divisors
and multiplicands. If C2 is negative, we must have C2 % C3 == 0.
Look for NOPs and SAVE_EXPRs inside. */
if (TREE_CODE (arg1) == INTEGER_CST
&& tree_int_cst_sgn (arg1) >= 0)
{
int have_save_expr = 0;
tree c2 = integer_zero_node;
tree xarg0 = arg0;
if (TREE_CODE (xarg0) == SAVE_EXPR && SAVE_EXPR_RTL (xarg0) == 0)
have_save_expr = 1, xarg0 = TREE_OPERAND (xarg0, 0);
STRIP_NOPS (xarg0);
/* Look inside the dividend and simplify using EXACT_DIV_EXPR
if possible. */
if (TREE_CODE (xarg0) == MULT_EXPR
&& multiple_of_p (type, TREE_OPERAND (xarg0, 0), arg1))
{
tree t;
t = fold (build (MULT_EXPR, type,
fold (build (EXACT_DIV_EXPR, type,
TREE_OPERAND (xarg0, 0), arg1)),
TREE_OPERAND (xarg0, 1)));
if (have_save_expr)
t = save_expr (t);
return t;
}
if (TREE_CODE (xarg0) == MULT_EXPR
&& multiple_of_p (type, TREE_OPERAND (xarg0, 1), arg1))
{
tree t;
t = fold (build (MULT_EXPR, type,
fold (build (EXACT_DIV_EXPR, type,
TREE_OPERAND (xarg0, 1), arg1)),
TREE_OPERAND (xarg0, 0)));
if (have_save_expr)
t = save_expr (t);
return t;
}
if (TREE_CODE (xarg0) == PLUS_EXPR
&& TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST)
c2 = TREE_OPERAND (xarg0, 1), xarg0 = TREE_OPERAND (xarg0, 0);
else if (TREE_CODE (xarg0) == MINUS_EXPR
&& TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
/* If we are doing this computation unsigned, the negate
is incorrect. */
&& ! TREE_UNSIGNED (type))
{
c2 = fold (build1 (NEGATE_EXPR, type, TREE_OPERAND (xarg0, 1)));
xarg0 = TREE_OPERAND (xarg0, 0);
}
if (TREE_CODE (xarg0) == SAVE_EXPR && SAVE_EXPR_RTL (xarg0) == 0)
have_save_expr = 1, xarg0 = TREE_OPERAND (xarg0, 0);
STRIP_NOPS (xarg0);
if (TREE_CODE (xarg0) == MULT_EXPR
&& TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
&& tree_int_cst_sgn (TREE_OPERAND (xarg0, 1)) >= 0
&& (integer_zerop (const_binop (TRUNC_MOD_EXPR,
TREE_OPERAND (xarg0, 1), arg1, 1))
|| integer_zerop (const_binop (TRUNC_MOD_EXPR, arg1,
TREE_OPERAND (xarg0, 1), 1)))
&& (tree_int_cst_sgn (c2) >= 0
|| integer_zerop (const_binop (TRUNC_MOD_EXPR, c2,
arg1, 1))))
{
tree outer_div = integer_one_node;
tree c1 = TREE_OPERAND (xarg0, 1);
tree c3 = arg1;
/* If C3 > C1, set them equal and do a divide by
C3/C1 at the end of the operation. */
if (tree_int_cst_lt (c1, c3))
outer_div = const_binop (code, c3, c1, 0), c3 = c1;
/* The result is A * (C1/C3) + (C2/C3). */
t = fold (build (PLUS_EXPR, type,
fold (build (MULT_EXPR, type,
TREE_OPERAND (xarg0, 0),
const_binop (code, c1, c3, 1))),
const_binop (code, c2, c3, 1)));
if (! integer_onep (outer_div))
t = fold (build (code, type, t, convert (type, outer_div)));
if (have_save_expr)
t = save_expr (t);
return t;
}
}
goto binary;
case CEIL_MOD_EXPR:
case FLOOR_MOD_EXPR:
case ROUND_MOD_EXPR:
case TRUNC_MOD_EXPR:
if (integer_onep (arg1))
return omit_one_operand (type, integer_zero_node, arg0);
if (integer_zerop (arg1))
return t;
/* Look for ((a * C1) % C3) or (((a * C1) + C2) % C3),
where C1 % C3 == 0. Handle similarly to the division case,
but don't bother with SAVE_EXPRs. */
if (TREE_CODE (arg1) == INTEGER_CST
&& ! integer_zerop (arg1))
{
tree c2 = integer_zero_node;
tree xarg0 = arg0;
if (TREE_CODE (xarg0) == PLUS_EXPR
&& TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST)
c2 = TREE_OPERAND (xarg0, 1), xarg0 = TREE_OPERAND (xarg0, 0);
else if (TREE_CODE (xarg0) == MINUS_EXPR
&& TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
&& ! TREE_UNSIGNED (type))
{
c2 = fold (build1 (NEGATE_EXPR, type, TREE_OPERAND (xarg0, 1)));
xarg0 = TREE_OPERAND (xarg0, 0);
}
STRIP_NOPS (xarg0);
if (TREE_CODE (xarg0) == MULT_EXPR
&& TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
&& integer_zerop (const_binop (TRUNC_MOD_EXPR,
TREE_OPERAND (xarg0, 1),
arg1, 1))
&& tree_int_cst_sgn (c2) >= 0)
/* The result is (C2%C3). */
return omit_one_operand (type, const_binop (code, c2, arg1, 1),
TREE_OPERAND (xarg0, 0));
}
goto binary;
case LSHIFT_EXPR:
case RSHIFT_EXPR:
case LROTATE_EXPR:
case RROTATE_EXPR:
if (integer_zerop (arg1))
return non_lvalue (convert (type, arg0));
/* Since negative shift count is not well-defined,
don't try to compute it in the compiler. */
if (TREE_CODE (arg1) == INTEGER_CST && tree_int_cst_sgn (arg1) < 0)
return t;
/* Rewrite an LROTATE_EXPR by a constant into an
RROTATE_EXPR by a new constant. */
if (code == LROTATE_EXPR && TREE_CODE (arg1) == INTEGER_CST)
{
TREE_SET_CODE (t, RROTATE_EXPR);
code = RROTATE_EXPR;
TREE_OPERAND (t, 1) = arg1
= const_binop
(MINUS_EXPR,
convert (TREE_TYPE (arg1),
build_int_2 (GET_MODE_BITSIZE (TYPE_MODE (type)), 0)),
arg1, 0);
if (tree_int_cst_sgn (arg1) < 0)
return t;
}
/* If we have a rotate of a bit operation with the rotate count and
the second operand of the bit operation both constant,
permute the two operations. */
if (code == RROTATE_EXPR && TREE_CODE (arg1) == INTEGER_CST
&& (TREE_CODE (arg0) == BIT_AND_EXPR
|| TREE_CODE (arg0) == BIT_ANDTC_EXPR
|| TREE_CODE (arg0) == BIT_IOR_EXPR
|| TREE_CODE (arg0) == BIT_XOR_EXPR)
&& TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST)
return fold (build (TREE_CODE (arg0), type,
fold (build (code, type,
TREE_OPERAND (arg0, 0), arg1)),
fold (build (code, type,
TREE_OPERAND (arg0, 1), arg1))));
/* Two consecutive rotates adding up to the width of the mode can
be ignored. */
if (code == RROTATE_EXPR && TREE_CODE (arg1) == INTEGER_CST
&& TREE_CODE (arg0) == RROTATE_EXPR
&& TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
&& TREE_INT_CST_HIGH (arg1) == 0
&& TREE_INT_CST_HIGH (TREE_OPERAND (arg0, 1)) == 0
&& ((TREE_INT_CST_LOW (arg1)
+ TREE_INT_CST_LOW (TREE_OPERAND (arg0, 1)))
== GET_MODE_BITSIZE (TYPE_MODE (type))))
return TREE_OPERAND (arg0, 0);
goto binary;
case MIN_EXPR:
if (operand_equal_p (arg0, arg1, 0))
return arg0;
if (INTEGRAL_TYPE_P (type)
&& operand_equal_p (arg1, TYPE_MIN_VALUE (type), 1))
return omit_one_operand (type, arg1, arg0);
goto associate;
case MAX_EXPR:
if (operand_equal_p (arg0, arg1, 0))
return arg0;
if (INTEGRAL_TYPE_P (type)
&& TYPE_MAX_VALUE (type)
&& operand_equal_p (arg1, TYPE_MAX_VALUE (type), 1))
return omit_one_operand (type, arg1, arg0);
goto associate;
case TRUTH_NOT_EXPR:
/* Note that the operand of this must be an int
and its values must be 0 or 1.
("true" is a fixed value perhaps depending on the language,
but we don't handle values other than 1 correctly yet.) */
tem = invert_truthvalue (arg0);
/* Avoid infinite recursion. */
if (TREE_CODE (tem) == TRUTH_NOT_EXPR)
return t;
return convert (type, tem);
case TRUTH_ANDIF_EXPR:
/* Note that the operands of this must be ints
and their values must be 0 or 1.
("true" is a fixed value perhaps depending on the language.) */
/* If first arg is constant zero, return it. */
if (integer_zerop (arg0))
return arg0;
case TRUTH_AND_EXPR:
/* If either arg is constant true, drop it. */
if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0))
return non_lvalue (arg1);
if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1))
return non_lvalue (arg0);
/* If second arg is constant zero, result is zero, but first arg
must be evaluated. */
if (integer_zerop (arg1))
return omit_one_operand (type, arg1, arg0);
/* Likewise for first arg, but note that only the TRUTH_AND_EXPR
case will be handled here. */
if (integer_zerop (arg0))
return omit_one_operand (type, arg0, arg1);
truth_andor:
/* We only do these simplifications if we are optimizing. */
if (!optimize)
return t;
/* Check for things like (A || B) && (A || C). We can convert this
to A || (B && C). Note that either operator can be any of the four
truth and/or operations and the transformation will still be
valid. Also note that we only care about order for the
ANDIF and ORIF operators. If B contains side effects, this
might change the truth-value of A. */
if (TREE_CODE (arg0) == TREE_CODE (arg1)
&& (TREE_CODE (arg0) == TRUTH_ANDIF_EXPR
|| TREE_CODE (arg0) == TRUTH_ORIF_EXPR
|| TREE_CODE (arg0) == TRUTH_AND_EXPR
|| TREE_CODE (arg0) == TRUTH_OR_EXPR)
&& ! TREE_SIDE_EFFECTS (TREE_OPERAND (arg0, 1)))
{
tree a00 = TREE_OPERAND (arg0, 0);
tree a01 = TREE_OPERAND (arg0, 1);
tree a10 = TREE_OPERAND (arg1, 0);
tree a11 = TREE_OPERAND (arg1, 1);
int commutative = ((TREE_CODE (arg0) == TRUTH_OR_EXPR
|| TREE_CODE (arg0) == TRUTH_AND_EXPR)
&& (code == TRUTH_AND_EXPR
|| code == TRUTH_OR_EXPR));
if (operand_equal_p (a00, a10, 0))
return fold (build (TREE_CODE (arg0), type, a00,
fold (build (code, type, a01, a11))));
else if (commutative && operand_equal_p (a00, a11, 0))
return fold (build (TREE_CODE (arg0), type, a00,
fold (build (code, type, a01, a10))));
else if (commutative && operand_equal_p (a01, a10, 0))
return fold (build (TREE_CODE (arg0), type, a01,
fold (build (code, type, a00, a11))));
/* This case if tricky because we must either have commutative
operators or else A10 must not have side-effects. */
else if ((commutative || ! TREE_SIDE_EFFECTS (a10))
&& operand_equal_p (a01, a11, 0))
return fold (build (TREE_CODE (arg0), type,
fold (build (code, type, a00, a10)),
a01));
}
/* See if we can build a range comparison. */
if (0 != (tem = fold_range_test (t)))
return tem;
/* Check for the possibility of merging component references. If our
lhs is another similar operation, try to merge its rhs with our
rhs. Then try to merge our lhs and rhs. */
if (TREE_CODE (arg0) == code
&& 0 != (tem = fold_truthop (code, type,
TREE_OPERAND (arg0, 1), arg1)))
return fold (build (code, type, TREE_OPERAND (arg0, 0), tem));
if ((tem = fold_truthop (code, type, arg0, arg1)) != 0)
return tem;
return t;
case TRUTH_ORIF_EXPR:
/* Note that the operands of this must be ints
and their values must be 0 or true.
("true" is a fixed value perhaps depending on the language.) */
/* If first arg is constant true, return it. */
if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0))
return arg0;
case TRUTH_OR_EXPR:
/* If either arg is constant zero, drop it. */
if (TREE_CODE (arg0) == INTEGER_CST && integer_zerop (arg0))
return non_lvalue (arg1);
if (TREE_CODE (arg1) == INTEGER_CST && integer_zerop (arg1))
return non_lvalue (arg0);
/* If second arg is constant true, result is true, but we must
evaluate first arg. */
if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1))
return omit_one_operand (type, arg1, arg0);
/* Likewise for first arg, but note this only occurs here for
TRUTH_OR_EXPR. */
if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0))
return omit_one_operand (type, arg0, arg1);
goto truth_andor;
case TRUTH_XOR_EXPR:
/* If either arg is constant zero, drop it. */
if (integer_zerop (arg0))
return non_lvalue (arg1);
if (integer_zerop (arg1))
return non_lvalue (arg0);
/* If either arg is constant true, this is a logical inversion. */
if (integer_onep (arg0))
return non_lvalue (invert_truthvalue (arg1));
if (integer_onep (arg1))
return non_lvalue (invert_truthvalue (arg0));
return t;
case EQ_EXPR:
case NE_EXPR:
case LT_EXPR:
case GT_EXPR:
case LE_EXPR:
case GE_EXPR:
/* If one arg is a constant integer, put it last. */
if (TREE_CODE (arg0) == INTEGER_CST
&& TREE_CODE (arg1) != INTEGER_CST)
{
TREE_OPERAND (t, 0) = arg1;
TREE_OPERAND (t, 1) = arg0;
arg0 = TREE_OPERAND (t, 0);
arg1 = TREE_OPERAND (t, 1);
code = swap_tree_comparison (code);
TREE_SET_CODE (t, code);
}
/* Convert foo++ == CONST into ++foo == CONST + INCR.
First, see if one arg is constant; find the constant arg
and the other one. */
{
tree constop = 0, varop = NULL_TREE;
int constopnum = -1;
if (TREE_CONSTANT (arg1))
constopnum = 1, constop = arg1, varop = arg0;
if (TREE_CONSTANT (arg0))
constopnum = 0, constop = arg0, varop = arg1;
if (constop && TREE_CODE (varop) == POSTINCREMENT_EXPR)
{
/* This optimization is invalid for ordered comparisons
if CONST+INCR overflows or if foo+incr might overflow.
This optimization is invalid for floating point due to rounding.
For pointer types we assume overflow doesn't happen. */
if (POINTER_TYPE_P (TREE_TYPE (varop))
|| (! FLOAT_TYPE_P (TREE_TYPE (varop))
&& (code == EQ_EXPR || code == NE_EXPR)))
{
tree newconst
= fold (build (PLUS_EXPR, TREE_TYPE (varop),
constop, TREE_OPERAND (varop, 1)));
TREE_SET_CODE (varop, PREINCREMENT_EXPR);
/* If VAROP is a reference to a bitfield, we must mask
the constant by the width of the field. */
if (TREE_CODE (TREE_OPERAND (varop, 0)) == COMPONENT_REF
&& DECL_BIT_FIELD(TREE_OPERAND
(TREE_OPERAND (varop, 0), 1)))
{
int size
= TREE_INT_CST_LOW (DECL_SIZE
(TREE_OPERAND
(TREE_OPERAND (varop, 0), 1)));
tree mask, unsigned_type;
int precision;
tree folded_compare;
/* First check whether the comparison would come out
always the same. If we don't do that we would
change the meaning with the masking. */
if (constopnum == 0)
folded_compare = fold (build (code, type, constop,
TREE_OPERAND (varop, 0)));
else
folded_compare = fold (build (code, type,
TREE_OPERAND (varop, 0),
constop));
if (integer_zerop (folded_compare)
|| integer_onep (folded_compare))
return omit_one_operand (type, folded_compare, varop);
unsigned_type = type_for_size (size, 1);
precision = TYPE_PRECISION (unsigned_type);
mask = build_int_2 (~0, ~0);
TREE_TYPE (mask) = unsigned_type;
force_fit_type (mask, 0);
mask = const_binop (RSHIFT_EXPR, mask,
size_int (precision - size), 0);
newconst = fold (build (BIT_AND_EXPR,
TREE_TYPE (varop), newconst,
convert (TREE_TYPE (varop),
mask)));
}
t = build (code, type, TREE_OPERAND (t, 0),
TREE_OPERAND (t, 1));
TREE_OPERAND (t, constopnum) = newconst;
return t;
}
}
else if (constop && TREE_CODE (varop) == POSTDECREMENT_EXPR)
{
if (POINTER_TYPE_P (TREE_TYPE (varop))
|| (! FLOAT_TYPE_P (TREE_TYPE (varop))
&& (code == EQ_EXPR || code == NE_EXPR)))
{
tree newconst
= fold (build (MINUS_EXPR, TREE_TYPE (varop),
constop, TREE_OPERAND (varop, 1)));
TREE_SET_CODE (varop, PREDECREMENT_EXPR);
if (TREE_CODE (TREE_OPERAND (varop, 0)) == COMPONENT_REF
&& DECL_BIT_FIELD(TREE_OPERAND
(TREE_OPERAND (varop, 0), 1)))
{
int size
= TREE_INT_CST_LOW (DECL_SIZE
(TREE_OPERAND
(TREE_OPERAND (varop, 0), 1)));
tree mask, unsigned_type;
int precision;
tree folded_compare;
if (constopnum == 0)
folded_compare = fold (build (code, type, constop,
TREE_OPERAND (varop, 0)));
else
folded_compare = fold (build (code, type,
TREE_OPERAND (varop, 0),
constop));
if (integer_zerop (folded_compare)
|| integer_onep (folded_compare))
return omit_one_operand (type, folded_compare, varop);
unsigned_type = type_for_size (size, 1);
precision = TYPE_PRECISION (unsigned_type);
mask = build_int_2 (~0, ~0);
TREE_TYPE (mask) = TREE_TYPE (varop);
force_fit_type (mask, 0);
mask = const_binop (RSHIFT_EXPR, mask,
size_int (precision - size), 0);
newconst = fold (build (BIT_AND_EXPR,
TREE_TYPE (varop), newconst,
convert (TREE_TYPE (varop),
mask)));
}
t = build (code, type, TREE_OPERAND (t, 0),
TREE_OPERAND (t, 1));
TREE_OPERAND (t, constopnum) = newconst;
return t;
}
}
}
/* Change X >= CST to X > (CST - 1) if CST is positive. */
if (TREE_CODE (arg1) == INTEGER_CST
&& TREE_CODE (arg0) != INTEGER_CST
&& tree_int_cst_sgn (arg1) > 0)
{
switch (TREE_CODE (t))
{
case GE_EXPR:
code = GT_EXPR;
arg1 = const_binop (MINUS_EXPR, arg1, integer_one_node, 0);
t = build (code, type, TREE_OPERAND (t, 0), arg1);
break;
case LT_EXPR:
code = LE_EXPR;
arg1 = const_binop (MINUS_EXPR, arg1, integer_one_node, 0);
t = build (code, type, TREE_OPERAND (t, 0), arg1);
break;
default:
break;
}
}
/* If this is an EQ or NE comparison with zero and ARG0 is
(1 << foo) & bar, convert it to (bar >> foo) & 1. Both require
two operations, but the latter can be done in one less insn
on machines that have only two-operand insns or on which a
constant cannot be the first operand. */
if (integer_zerop (arg1) && (code == EQ_EXPR || code == NE_EXPR)
&& TREE_CODE (arg0) == BIT_AND_EXPR)
{
if (TREE_CODE (TREE_OPERAND (arg0, 0)) == LSHIFT_EXPR
&& integer_onep (TREE_OPERAND (TREE_OPERAND (arg0, 0), 0)))
return
fold (build (code, type,
build (BIT_AND_EXPR, TREE_TYPE (arg0),
build (RSHIFT_EXPR,
TREE_TYPE (TREE_OPERAND (arg0, 0)),
TREE_OPERAND (arg0, 1),
TREE_OPERAND (TREE_OPERAND (arg0, 0), 1)),
convert (TREE_TYPE (arg0),
integer_one_node)),
arg1));
else if (TREE_CODE (TREE_OPERAND (arg0, 1)) == LSHIFT_EXPR
&& integer_onep (TREE_OPERAND (TREE_OPERAND (arg0, 1), 0)))
return
fold (build (code, type,
build (BIT_AND_EXPR, TREE_TYPE (arg0),
build (RSHIFT_EXPR,
TREE_TYPE (TREE_OPERAND (arg0, 1)),
TREE_OPERAND (arg0, 0),
TREE_OPERAND (TREE_OPERAND (arg0, 1), 1)),
convert (TREE_TYPE (arg0),
integer_one_node)),
arg1));
}
/* If this is an NE or EQ comparison of zero against the result of a
signed MOD operation whose second operand is a power of 2, make
the MOD operation unsigned since it is simpler and equivalent. */
if ((code == NE_EXPR || code == EQ_EXPR)
&& integer_zerop (arg1)
&& ! TREE_UNSIGNED (TREE_TYPE (arg0))
&& (TREE_CODE (arg0) == TRUNC_MOD_EXPR
|| TREE_CODE (arg0) == CEIL_MOD_EXPR
|| TREE_CODE (arg0) == FLOOR_MOD_EXPR
|| TREE_CODE (arg0) == ROUND_MOD_EXPR)
&& integer_pow2p (TREE_OPERAND (arg0, 1)))
{
tree newtype = unsigned_type (TREE_TYPE (arg0));
tree newmod = build (TREE_CODE (arg0), newtype,
convert (newtype, TREE_OPERAND (arg0, 0)),
convert (newtype, TREE_OPERAND (arg0, 1)));
return build (code, type, newmod, convert (newtype, arg1));
}
/* If this is an NE comparison of zero with an AND of one, remove the
comparison since the AND will give the correct value. */
if (code == NE_EXPR && integer_zerop (arg1)
&& TREE_CODE (arg0) == BIT_AND_EXPR
&& integer_onep (TREE_OPERAND (arg0, 1)))
return convert (type, arg0);
/* If we have (A & C) == C where C is a power of 2, convert this into
(A & C) != 0. Similarly for NE_EXPR. */
if ((code == EQ_EXPR || code == NE_EXPR)
&& TREE_CODE (arg0) == BIT_AND_EXPR
&& integer_pow2p (TREE_OPERAND (arg0, 1))
&& operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
return build (code == EQ_EXPR ? NE_EXPR : EQ_EXPR, type,
arg0, integer_zero_node);
/* If X is unsigned, convert X < (1 << Y) into X >> Y == 0
and similarly for >= into !=. */
if ((code == LT_EXPR || code == GE_EXPR)
&& TREE_UNSIGNED (TREE_TYPE (arg0))
&& TREE_CODE (arg1) == LSHIFT_EXPR
&& integer_onep (TREE_OPERAND (arg1, 0)))
return build (code == LT_EXPR ? EQ_EXPR : NE_EXPR, type,
build (RSHIFT_EXPR, TREE_TYPE (arg0), arg0,
TREE_OPERAND (arg1, 1)),
convert (TREE_TYPE (arg0), integer_zero_node));
else if ((code == LT_EXPR || code == GE_EXPR)
&& TREE_UNSIGNED (TREE_TYPE (arg0))
&& (TREE_CODE (arg1) == NOP_EXPR
|| TREE_CODE (arg1) == CONVERT_EXPR)
&& TREE_CODE (TREE_OPERAND (arg1, 0)) == LSHIFT_EXPR
&& integer_onep (TREE_OPERAND (TREE_OPERAND (arg1, 0), 0)))
return
build (code == LT_EXPR ? EQ_EXPR : NE_EXPR, type,
convert (TREE_TYPE (arg0),
build (RSHIFT_EXPR, TREE_TYPE (arg0), arg0,
TREE_OPERAND (TREE_OPERAND (arg1, 0), 1))),
convert (TREE_TYPE (arg0), integer_zero_node));
/* Simplify comparison of something with itself. (For IEEE
floating-point, we can only do some of these simplifications.) */
if (operand_equal_p (arg0, arg1, 0))
{
switch (code)
{
case EQ_EXPR:
case GE_EXPR:
case LE_EXPR:
if (INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
return constant_boolean_node (1, type);
code = EQ_EXPR;
TREE_SET_CODE (t, code);
break;
case NE_EXPR:
/* For NE, we can only do this simplification if integer. */
if (! INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
break;
/* ... fall through ... */
case GT_EXPR:
case LT_EXPR:
return constant_boolean_node (0, type);
default:
abort ();
}
}
/* An unsigned comparison against 0 can be simplified. */
if (integer_zerop (arg1)
&& (INTEGRAL_TYPE_P (TREE_TYPE (arg1))
|| POINTER_TYPE_P (TREE_TYPE (arg1)))
&& TREE_UNSIGNED (TREE_TYPE (arg1)))
{
switch (TREE_CODE (t))
{
case GT_EXPR:
code = NE_EXPR;
TREE_SET_CODE (t, NE_EXPR);
break;
case LE_EXPR:
code = EQ_EXPR;
TREE_SET_CODE (t, EQ_EXPR);
break;
case GE_EXPR:
return omit_one_operand (type,
convert (type, integer_one_node),
arg0);
case LT_EXPR:
return omit_one_operand (type,
convert (type, integer_zero_node),
arg0);
default:
break;
}
}
/* An unsigned <= 0x7fffffff can be simplified. */
{
int width = TYPE_PRECISION (TREE_TYPE (arg1));
if (TREE_CODE (arg1) == INTEGER_CST
&& ! TREE_CONSTANT_OVERFLOW (arg1)
&& width <= HOST_BITS_PER_WIDE_INT
&& TREE_INT_CST_LOW (arg1) == ((HOST_WIDE_INT) 1 << (width - 1)) - 1
&& TREE_INT_CST_HIGH (arg1) == 0
&& (INTEGRAL_TYPE_P (TREE_TYPE (arg1))
|| POINTER_TYPE_P (TREE_TYPE (arg1)))
&& TREE_UNSIGNED (TREE_TYPE (arg1)))
{
switch (TREE_CODE (t))
{
case LE_EXPR:
return fold (build (GE_EXPR, type,
convert (signed_type (TREE_TYPE (arg0)),
arg0),
convert (signed_type (TREE_TYPE (arg1)),
integer_zero_node)));
case GT_EXPR:
return fold (build (LT_EXPR, type,
convert (signed_type (TREE_TYPE (arg0)),
arg0),
convert (signed_type (TREE_TYPE (arg1)),
integer_zero_node)));
default:
break;
}
}
}
/* If we are comparing an expression that just has comparisons
of two integer values, arithmetic expressions of those comparisons,
and constants, we can simplify it. There are only three cases
to check: the two values can either be equal, the first can be
greater, or the second can be greater. Fold the expression for
those three values. Since each value must be 0 or 1, we have
eight possibilities, each of which corresponds to the constant 0
or 1 or one of the six possible comparisons.
This handles common cases like (a > b) == 0 but also handles
expressions like ((x > y) - (y > x)) > 0, which supposedly
occur in macroized code. */
if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg0) != INTEGER_CST)
{
tree cval1 = 0, cval2 = 0;
int save_p = 0;
if (twoval_comparison_p (arg0, &cval1, &cval2, &save_p)
/* Don't handle degenerate cases here; they should already
have been handled anyway. */
&& cval1 != 0 && cval2 != 0
&& ! (TREE_CONSTANT (cval1) && TREE_CONSTANT (cval2))
&& TREE_TYPE (cval1) == TREE_TYPE (cval2)
&& INTEGRAL_TYPE_P (TREE_TYPE (cval1))
&& TYPE_MAX_VALUE (TREE_TYPE (cval1))
&& TYPE_MAX_VALUE (TREE_TYPE (cval2))
&& ! operand_equal_p (TYPE_MIN_VALUE (TREE_TYPE (cval1)),
TYPE_MAX_VALUE (TREE_TYPE (cval2)), 0))
{
tree maxval = TYPE_MAX_VALUE (TREE_TYPE (cval1));
tree minval = TYPE_MIN_VALUE (TREE_TYPE (cval1));
/* We can't just pass T to eval_subst in case cval1 or cval2
was the same as ARG1. */
tree high_result
= fold (build (code, type,
eval_subst (arg0, cval1, maxval, cval2, minval),
arg1));
tree equal_result
= fold (build (code, type,
eval_subst (arg0, cval1, maxval, cval2, maxval),
arg1));
tree low_result
= fold (build (code, type,
eval_subst (arg0, cval1, minval, cval2, maxval),
arg1));
/* All three of these results should be 0 or 1. Confirm they
are. Then use those values to select the proper code
to use. */
if ((integer_zerop (high_result)
|| integer_onep (high_result))
&& (integer_zerop (equal_result)
|| integer_onep (equal_result))
&& (integer_zerop (low_result)
|| integer_onep (low_result)))
{
/* Make a 3-bit mask with the high-order bit being the
value for `>', the next for '=', and the low for '<'. */
switch ((integer_onep (high_result) * 4)
+ (integer_onep (equal_result) * 2)
+ integer_onep (low_result))
{
case 0:
/* Always false. */
return omit_one_operand (type, integer_zero_node, arg0);
case 1:
code = LT_EXPR;
break;
case 2:
code = EQ_EXPR;
break;
case 3:
code = LE_EXPR;
break;
case 4:
code = GT_EXPR;
break;
case 5:
code = NE_EXPR;
break;
case 6:
code = GE_EXPR;
break;
case 7:
/* Always true. */
return omit_one_operand (type, integer_one_node, arg0);
}
t = build (code, type, cval1, cval2);
if (save_p)
return save_expr (t);
else
return fold (t);
}
}
}
/* If this is a comparison of a field, we may be able to simplify it. */
if ((TREE_CODE (arg0) == COMPONENT_REF
|| TREE_CODE (arg0) == BIT_FIELD_REF)
&& (code == EQ_EXPR || code == NE_EXPR)
/* Handle the constant case even without -O
to make sure the warnings are given. */
&& (optimize || TREE_CODE (arg1) == INTEGER_CST))
{
t1 = optimize_bit_field_compare (code, type, arg0, arg1);
return t1 ? t1 : t;
}
/* If this is a comparison of complex values and either or both sides
are a COMPLEX_EXPR or COMPLEX_CST, it is best to split up the
comparisons and join them with a TRUTH_ANDIF_EXPR or TRUTH_ORIF_EXPR.
This may prevent needless evaluations. */
if ((code == EQ_EXPR || code == NE_EXPR)
&& TREE_CODE (TREE_TYPE (arg0)) == COMPLEX_TYPE
&& (TREE_CODE (arg0) == COMPLEX_EXPR
|| TREE_CODE (arg1) == COMPLEX_EXPR
|| TREE_CODE (arg0) == COMPLEX_CST
|| TREE_CODE (arg1) == COMPLEX_CST))
{
tree subtype = TREE_TYPE (TREE_TYPE (arg0));
tree real0, imag0, real1, imag1;
arg0 = save_expr (arg0);
arg1 = save_expr (arg1);
real0 = fold (build1 (REALPART_EXPR, subtype, arg0));
imag0 = fold (build1 (IMAGPART_EXPR, subtype, arg0));
real1 = fold (build1 (REALPART_EXPR, subtype, arg1));
imag1 = fold (build1 (IMAGPART_EXPR, subtype, arg1));
return fold (build ((code == EQ_EXPR ? TRUTH_ANDIF_EXPR
: TRUTH_ORIF_EXPR),
type,
fold (build (code, type, real0, real1)),
fold (build (code, type, imag0, imag1))));
}
/* From here on, the only cases we handle are when the result is
known to be a constant.
To compute GT, swap the arguments and do LT.
To compute GE, do LT and invert the result.
To compute LE, swap the arguments, do LT and invert the result.
To compute NE, do EQ and invert the result.
Therefore, the code below must handle only EQ and LT. */
if (code == LE_EXPR || code == GT_EXPR)
{
tem = arg0, arg0 = arg1, arg1 = tem;
code = swap_tree_comparison (code);
}
/* Note that it is safe to invert for real values here because we
will check below in the one case that it matters. */
invert = 0;
if (code == NE_EXPR || code == GE_EXPR)
{
invert = 1;
code = invert_tree_comparison (code);
}
/* Compute a result for LT or EQ if args permit;
otherwise return T. */
if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
{
if (code == EQ_EXPR)
t1 = build_int_2 ((TREE_INT_CST_LOW (arg0)
== TREE_INT_CST_LOW (arg1))
&& (TREE_INT_CST_HIGH (arg0)
== TREE_INT_CST_HIGH (arg1)),
0);
else
t1 = build_int_2 ((TREE_UNSIGNED (TREE_TYPE (arg0))
? INT_CST_LT_UNSIGNED (arg0, arg1)
: INT_CST_LT (arg0, arg1)),
0);
}
#if 0 /* This is no longer useful, but breaks some real code. */
/* Assume a nonexplicit constant cannot equal an explicit one,
since such code would be undefined anyway.
Exception: on sysvr4, using #pragma weak,
a label can come out as 0. */
else if (TREE_CODE (arg1) == INTEGER_CST
&& !integer_zerop (arg1)
&& TREE_CONSTANT (arg0)
&& TREE_CODE (arg0) == ADDR_EXPR
&& code == EQ_EXPR)
t1 = build_int_2 (0, 0);
#endif
/* Two real constants can be compared explicitly. */
else if (TREE_CODE (arg0) == REAL_CST && TREE_CODE (arg1) == REAL_CST)
{
/* If either operand is a NaN, the result is false with two
exceptions: First, an NE_EXPR is true on NaNs, but that case
is already handled correctly since we will be inverting the
result for NE_EXPR. Second, if we had inverted a LE_EXPR
or a GE_EXPR into a LT_EXPR, we must return true so that it
will be inverted into false. */
if (REAL_VALUE_ISNAN (TREE_REAL_CST (arg0))
|| REAL_VALUE_ISNAN (TREE_REAL_CST (arg1)))
t1 = build_int_2 (invert && code == LT_EXPR, 0);
else if (code == EQ_EXPR)
t1 = build_int_2 (REAL_VALUES_EQUAL (TREE_REAL_CST (arg0),
TREE_REAL_CST (arg1)),
0);
else
t1 = build_int_2 (REAL_VALUES_LESS (TREE_REAL_CST (arg0),
TREE_REAL_CST (arg1)),
0);
}
if (t1 == NULL_TREE)
return t;
if (invert)
TREE_INT_CST_LOW (t1) ^= 1;
TREE_TYPE (t1) = type;
if (TREE_CODE (type) == BOOLEAN_TYPE)
return truthvalue_conversion (t1);
return t1;
case COND_EXPR:
/* Pedantic ANSI C says that a conditional expression is never an lvalue,
so all simple results must be passed through pedantic_non_lvalue. */
if (TREE_CODE (arg0) == INTEGER_CST)
return pedantic_non_lvalue
(TREE_OPERAND (t, (integer_zerop (arg0) ? 2 : 1)));
else if (operand_equal_p (arg1, TREE_OPERAND (expr, 2), 0))
return pedantic_omit_one_operand (type, arg1, arg0);
/* If the second operand is zero, invert the comparison and swap
the second and third operands. Likewise if the second operand
is constant and the third is not or if the third operand is
equivalent to the first operand of the comparison. */
if (integer_zerop (arg1)
|| (TREE_CONSTANT (arg1) && ! TREE_CONSTANT (TREE_OPERAND (t, 2)))
|| (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<'
&& operand_equal_for_comparison_p (TREE_OPERAND (arg0, 0),
TREE_OPERAND (t, 2),
TREE_OPERAND (arg0, 1))))
{
/* See if this can be inverted. If it can't, possibly because
it was a floating-point inequality comparison, don't do
anything. */
tem = invert_truthvalue (arg0);
if (TREE_CODE (tem) != TRUTH_NOT_EXPR)
{
t = build (code, type, tem,
TREE_OPERAND (t, 2), TREE_OPERAND (t, 1));
arg0 = tem;
/* arg1 should be the first argument of the new T. */
arg1 = TREE_OPERAND (t, 1);
STRIP_NOPS (arg1);
}
}
/* If we have A op B ? A : C, we may be able to convert this to a
simpler expression, depending on the operation and the values
of B and C. IEEE floating point prevents this though,
because A or B might be -0.0 or a NaN. */
if (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<'
&& (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
|| ! FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (arg0, 0)))
|| flag_fast_math)
&& operand_equal_for_comparison_p (TREE_OPERAND (arg0, 0),
arg1, TREE_OPERAND (arg0, 1)))
{
tree arg2 = TREE_OPERAND (t, 2);
enum tree_code comp_code = TREE_CODE (arg0);
STRIP_NOPS (arg2);
/* If we have A op 0 ? A : -A, this is A, -A, abs (A), or abs (-A),
depending on the comparison operation. */
if ((FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (arg0, 1)))
? real_zerop (TREE_OPERAND (arg0, 1))
: integer_zerop (TREE_OPERAND (arg0, 1)))
&& TREE_CODE (arg2) == NEGATE_EXPR
&& operand_equal_p (TREE_OPERAND (arg2, 0), arg1, 0))
switch (comp_code)
{
case EQ_EXPR:
return pedantic_non_lvalue
(fold (build1 (NEGATE_EXPR, type, arg1)));
case NE_EXPR:
return pedantic_non_lvalue (convert (type, arg1));
case GE_EXPR:
case GT_EXPR:
if (TREE_UNSIGNED (TREE_TYPE (arg1)))
arg1 = convert (signed_type (TREE_TYPE (arg1)), arg1);
return pedantic_non_lvalue
(convert (type, fold (build1 (ABS_EXPR,
TREE_TYPE (arg1), arg1))));
case LE_EXPR:
case LT_EXPR:
if (TREE_UNSIGNED (TREE_TYPE (arg1)))
arg1 = convert (signed_type (TREE_TYPE (arg1)), arg1);
return pedantic_non_lvalue
(fold (build1 (NEGATE_EXPR, type,
convert (type,
fold (build1 (ABS_EXPR,
TREE_TYPE (arg1),
arg1))))));
default:
abort ();
}
/* If this is A != 0 ? A : 0, this is simply A. For ==, it is
always zero. */
if (integer_zerop (TREE_OPERAND (arg0, 1)) && integer_zerop (arg2))
{
if (comp_code == NE_EXPR)
return pedantic_non_lvalue (convert (type, arg1));
else if (comp_code == EQ_EXPR)
return pedantic_non_lvalue (convert (type, integer_zero_node));
}
/* If this is A op B ? A : B, this is either A, B, min (A, B),
or max (A, B), depending on the operation. */
if (operand_equal_for_comparison_p (TREE_OPERAND (arg0, 1),
arg2, TREE_OPERAND (arg0, 0)))
{
tree comp_op0 = TREE_OPERAND (arg0, 0);
tree comp_op1 = TREE_OPERAND (arg0, 1);
tree comp_type = TREE_TYPE (comp_op0);
switch (comp_code)
{
case EQ_EXPR:
return pedantic_non_lvalue (convert (type, arg2));
case NE_EXPR:
return pedantic_non_lvalue (convert (type, arg1));
case LE_EXPR:
case LT_EXPR:
/* In C++ a ?: expression can be an lvalue, so put the
operand which will be used if they are equal first
so that we can convert this back to the
corresponding COND_EXPR. */
return pedantic_non_lvalue
(convert (type, (fold (build (MIN_EXPR, comp_type,
(comp_code == LE_EXPR
? comp_op0 : comp_op1),
(comp_code == LE_EXPR
? comp_op1 : comp_op0))))));
break;
case GE_EXPR:
case GT_EXPR:
return pedantic_non_lvalue
(convert (type, fold (build (MAX_EXPR, comp_type,
(comp_code == GE_EXPR
? comp_op0 : comp_op1),
(comp_code == GE_EXPR
? comp_op1 : comp_op0)))));
break;
default:
abort ();
}
}
/* If this is A op C1 ? A : C2 with C1 and C2 constant integers,
we might still be able to simplify this. For example,
if C1 is one less or one more than C2, this might have started
out as a MIN or MAX and been transformed by this function.
Only good for INTEGER_TYPEs, because we need TYPE_MAX_VALUE. */
if (INTEGRAL_TYPE_P (type)
&& TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
&& TREE_CODE (arg2) == INTEGER_CST)
switch (comp_code)
{
case EQ_EXPR:
/* We can replace A with C1 in this case. */
arg1 = convert (type, TREE_OPERAND (arg0, 1));
t = build (code, type, TREE_OPERAND (t, 0), arg1,
TREE_OPERAND (t, 2));
break;
case LT_EXPR:
/* If C1 is C2 + 1, this is min(A, C2). */
if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type), 1)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
const_binop (PLUS_EXPR, arg2,
integer_one_node, 0), 1))
return pedantic_non_lvalue
(fold (build (MIN_EXPR, type, arg1, arg2)));
break;
case LE_EXPR:
/* If C1 is C2 - 1, this is min(A, C2). */
if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type), 1)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
const_binop (MINUS_EXPR, arg2,
integer_one_node, 0), 1))
return pedantic_non_lvalue
(fold (build (MIN_EXPR, type, arg1, arg2)));
break;
case GT_EXPR:
/* If C1 is C2 - 1, this is max(A, C2). */
if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type), 1)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
const_binop (MINUS_EXPR, arg2,
integer_one_node, 0), 1))
return pedantic_non_lvalue
(fold (build (MAX_EXPR, type, arg1, arg2)));
break;
case GE_EXPR:
/* If C1 is C2 + 1, this is max(A, C2). */
if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type), 1)
&& operand_equal_p (TREE_OPERAND (arg0, 1),
const_binop (PLUS_EXPR, arg2,
integer_one_node, 0), 1))
return pedantic_non_lvalue
(fold (build (MAX_EXPR, type, arg1, arg2)));
break;
case NE_EXPR:
break;
default:
abort ();
}
}
/* If the second operand is simpler than the third, swap them
since that produces better jump optimization results. */
if ((TREE_CONSTANT (arg1) || TREE_CODE_CLASS (TREE_CODE (arg1)) == 'd'
|| TREE_CODE (arg1) == SAVE_EXPR)
&& ! (TREE_CONSTANT (TREE_OPERAND (t, 2))
|| TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (t, 2))) == 'd'
|| TREE_CODE (TREE_OPERAND (t, 2)) == SAVE_EXPR))
{
/* See if this can be inverted. If it can't, possibly because
it was a floating-point inequality comparison, don't do
anything. */
tem = invert_truthvalue (arg0);
if (TREE_CODE (tem) != TRUTH_NOT_EXPR)
{
t = build (code, type, tem,
TREE_OPERAND (t, 2), TREE_OPERAND (t, 1));
arg0 = tem;
/* arg1 should be the first argument of the new T. */
arg1 = TREE_OPERAND (t, 1);
STRIP_NOPS (arg1);
}
}
/* Convert A ? 1 : 0 to simply A. */
if (integer_onep (TREE_OPERAND (t, 1))
&& integer_zerop (TREE_OPERAND (t, 2))
/* If we try to convert TREE_OPERAND (t, 0) to our type, the
call to fold will try to move the conversion inside
a COND, which will recurse. In that case, the COND_EXPR
is probably the best choice, so leave it alone. */
&& type == TREE_TYPE (arg0))
return pedantic_non_lvalue (arg0);
/* Look for expressions of the form A & 2 ? 2 : 0. The result of this
operation is simply A & 2. */
if (integer_zerop (TREE_OPERAND (t, 2))
&& TREE_CODE (arg0) == NE_EXPR
&& integer_zerop (TREE_OPERAND (arg0, 1))
&& integer_pow2p (arg1)
&& TREE_CODE (TREE_OPERAND (arg0, 0)) == BIT_AND_EXPR
&& operand_equal_p (TREE_OPERAND (TREE_OPERAND (arg0, 0), 1),
arg1, 1))
return pedantic_non_lvalue (convert (type, TREE_OPERAND (arg0, 0)));
return t;
case COMPOUND_EXPR:
/* When pedantic, a compound expression can be neither an lvalue
nor an integer constant expression. */
if (TREE_SIDE_EFFECTS (arg0) || pedantic)
return t;
/* Don't let (0, 0) be null pointer constant. */
if (integer_zerop (arg1))
return build1 (NOP_EXPR, TREE_TYPE (arg1), arg1);
return arg1;
case COMPLEX_EXPR:
if (wins)
return build_complex (type, arg0, arg1);
return t;
case REALPART_EXPR:
if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
return t;
else if (TREE_CODE (arg0) == COMPLEX_EXPR)
return omit_one_operand (type, TREE_OPERAND (arg0, 0),
TREE_OPERAND (arg0, 1));
else if (TREE_CODE (arg0) == COMPLEX_CST)
return TREE_REALPART (arg0);
else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
return fold (build (TREE_CODE (arg0), type,
fold (build1 (REALPART_EXPR, type,
TREE_OPERAND (arg0, 0))),
fold (build1 (REALPART_EXPR,
type, TREE_OPERAND (arg0, 1)))));
return t;
case IMAGPART_EXPR:
if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
return convert (type, integer_zero_node);
else if (TREE_CODE (arg0) == COMPLEX_EXPR)
return omit_one_operand (type, TREE_OPERAND (arg0, 1),
TREE_OPERAND (arg0, 0));
else if (TREE_CODE (arg0) == COMPLEX_CST)
return TREE_IMAGPART (arg0);
else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
return fold (build (TREE_CODE (arg0), type,
fold (build1 (IMAGPART_EXPR, type,
TREE_OPERAND (arg0, 0))),
fold (build1 (IMAGPART_EXPR, type,
TREE_OPERAND (arg0, 1)))));
return t;
/* Pull arithmetic ops out of the CLEANUP_POINT_EXPR where
appropriate. */
case CLEANUP_POINT_EXPR:
if (! has_cleanups (arg0))
return TREE_OPERAND (t, 0);
{
enum tree_code code0 = TREE_CODE (arg0);
int kind0 = TREE_CODE_CLASS (code0);
tree arg00 = TREE_OPERAND (arg0, 0);
tree arg01;
if (kind0 == '1' || code0 == TRUTH_NOT_EXPR)
return fold (build1 (code0, type,
fold (build1 (CLEANUP_POINT_EXPR,
TREE_TYPE (arg00), arg00))));
if (kind0 == '<' || kind0 == '2'
|| code0 == TRUTH_ANDIF_EXPR || code0 == TRUTH_ORIF_EXPR
|| code0 == TRUTH_AND_EXPR || code0 == TRUTH_OR_EXPR
|| code0 == TRUTH_XOR_EXPR)
{
arg01 = TREE_OPERAND (arg0, 1);
if (TREE_CONSTANT (arg00)
|| ((code0 == TRUTH_ANDIF_EXPR || code0 == TRUTH_ORIF_EXPR)
&& ! has_cleanups (arg00)))
return fold (build (code0, type, arg00,
fold (build1 (CLEANUP_POINT_EXPR,
TREE_TYPE (arg01), arg01))));
if (TREE_CONSTANT (arg01))
return fold (build (code0, type,
fold (build1 (CLEANUP_POINT_EXPR,
TREE_TYPE (arg00), arg00)),
arg01));
}
return t;
}
default:
return t;
} /* switch (code) */
}
/* Determine if first argument is a multiple of second argument.
Return 0 if it is not, or is not easily determined to so be.
An example of the sort of thing we care about (at this point --
this routine could surely be made more general, and expanded
to do what the *_DIV_EXPR's fold() cases do now) is discovering
that
SAVE_EXPR (I) * SAVE_EXPR (J * 8)
is a multiple of
SAVE_EXPR (J * 8)
when we know that the two `SAVE_EXPR (J * 8)' nodes are the
same node (which means they will have the same value at run
time, even though we don't know when they'll be assigned).
This code also handles discovering that
SAVE_EXPR (I) * SAVE_EXPR (J * 8)
is a multiple of
8
(of course) so we don't have to worry about dealing with a
possible remainder.
Note that we _look_ inside a SAVE_EXPR only to determine
how it was calculated; it is not safe for fold() to do much
of anything else with the internals of a SAVE_EXPR, since
fold() cannot know when it will be evaluated at run time.
For example, the latter example above _cannot_ be implemented
as
SAVE_EXPR (I) * J
or any variant thereof, since the value of J at evaluation time
of the original SAVE_EXPR is not necessarily the same at the time
the new expression is evaluated. The only optimization of this
sort that would be valid is changing
SAVE_EXPR (I) * SAVE_EXPR (SAVE_EXPR (J) * 8)
divided by
8
to
SAVE_EXPR (I) * SAVE_EXPR (J)
(where the same SAVE_EXPR (J) is used in the original and the
transformed version). */
static int
multiple_of_p (type, top, bottom)
tree type;
tree top;
tree bottom;
{
if (operand_equal_p (top, bottom, 0))
return 1;
if (TREE_CODE (type) != INTEGER_TYPE)
return 0;
switch (TREE_CODE (top))
{
case MULT_EXPR:
return (multiple_of_p (type, TREE_OPERAND (top, 0), bottom)
|| multiple_of_p (type, TREE_OPERAND (top, 1), bottom));
case PLUS_EXPR:
case MINUS_EXPR:
return (multiple_of_p (type, TREE_OPERAND (top, 0), bottom)
&& multiple_of_p (type, TREE_OPERAND (top, 1), bottom));
case NOP_EXPR:
/* Punt if conversion from non-integral or wider integral type. */
if ((TREE_CODE (TREE_TYPE (TREE_OPERAND (top, 0))) != INTEGER_TYPE)
|| (TYPE_PRECISION (type)
< TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (top, 0)))))
return 0;
/* Fall through. */
case SAVE_EXPR:
return multiple_of_p (type, TREE_OPERAND (top, 0), bottom);
case INTEGER_CST:
if ((TREE_CODE (bottom) != INTEGER_CST)
|| (tree_int_cst_sgn (top) < 0)
|| (tree_int_cst_sgn (bottom) < 0))
return 0;
return integer_zerop (const_binop (TRUNC_MOD_EXPR,
top, bottom, 0));
default:
return 0;
}
}
Index: head/contrib/gcc/gcse.c
===================================================================
--- head/contrib/gcc/gcse.c (revision 52750)
+++ head/contrib/gcc/gcse.c (revision 52751)
@@ -1,4695 +1,4704 @@
/* Global common subexpression elimination/Partial redundancy elimination
and global constant/copy propagation for GNU compiler.
Copyright (C) 1997, 1998, 1999 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. */
/* TODO
- reordering of memory allocation and freeing to be more space efficient
- do rough calc of how many regs are needed in each block, and a rough
calc of how many regs are available in each class and use that to
throttle back the code in cases where RTX_COST is minimal.
- dead store elimination
- a store to the same address as a load does not kill the load if the
source of the store is also the destination of the load. Handling this
allows more load motion, particularly out of loops.
- ability to realloc sbitmap vectors would allow one initial computation
of reg_set_in_block with only subsequent additions, rather than
recomputing it for each pass
*/
/* References searched while implementing this.
Compilers Principles, Techniques and Tools
Aho, Sethi, Ullman
Addison-Wesley, 1988
Global Optimization by Suppression of Partial Redundancies
E. Morel, C. Renvoise
communications of the acm, Vol. 22, Num. 2, Feb. 1979
A Portable Machine-Independent Global Optimizer - Design and Measurements
Frederick Chow
Stanford Ph.D. thesis, Dec. 1983
A Fast Algorithm for Code Movement Optimization
D.M. Dhamdhere
SIGPLAN Notices, Vol. 23, Num. 10, Oct. 1988
A Solution to a Problem with Morel and Renvoise's
Global Optimization by Suppression of Partial Redundancies
K-H Drechsler, M.P. Stadel
ACM TOPLAS, Vol. 10, Num. 4, Oct. 1988
Practical Adaptation of the Global Optimization
Algorithm of Morel and Renvoise
D.M. Dhamdhere
ACM TOPLAS, Vol. 13, Num. 2. Apr. 1991
Efficiently Computing Static Single Assignment Form and the Control
Dependence Graph
R. Cytron, J. Ferrante, B.K. Rosen, M.N. Wegman, and F.K. Zadeck
ACM TOPLAS, Vol. 13, Num. 4, Oct. 1991
Lazy Code Motion
J. Knoop, O. Ruthing, B. Steffen
ACM SIGPLAN Notices Vol. 27, Num. 7, Jul. 1992, '92 Conference on PLDI
What's In a Region? Or Computing Control Dependence Regions in Near-Linear
Time for Reducible Flow Control
Thomas Ball
ACM Letters on Programming Languages and Systems,
Vol. 2, Num. 1-4, Mar-Dec 1993
An Efficient Representation for Sparse Sets
Preston Briggs, Linda Torczon
ACM Letters on Programming Languages and Systems,
Vol. 2, Num. 1-4, Mar-Dec 1993
A Variation of Knoop, Ruthing, and Steffen's Lazy Code Motion
K-H Drechsler, M.P. Stadel
ACM SIGPLAN Notices, Vol. 28, Num. 5, May 1993
Partial Dead Code Elimination
J. Knoop, O. Ruthing, B. Steffen
ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
Effective Partial Redundancy Elimination
P. Briggs, K.D. Cooper
ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
The Program Structure Tree: Computing Control Regions in Linear Time
R. Johnson, D. Pearson, K. Pingali
ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
Optimal Code Motion: Theory and Practice
J. Knoop, O. Ruthing, B. Steffen
ACM TOPLAS, Vol. 16, Num. 4, Jul. 1994
The power of assignment motion
J. Knoop, O. Ruthing, B. Steffen
ACM SIGPLAN Notices Vol. 30, Num. 6, Jun. 1995, '95 Conference on PLDI
Global code motion / global value numbering
C. Click
ACM SIGPLAN Notices Vol. 30, Num. 6, Jun. 1995, '95 Conference on PLDI
Value Driven Redundancy Elimination
L.T. Simpson
Rice University Ph.D. thesis, Apr. 1996
Value Numbering
L.T. Simpson
Massively Scalar Compiler Project, Rice University, Sep. 1996
High Performance Compilers for Parallel Computing
Michael Wolfe
Addison-Wesley, 1996
Advanced Compiler Design and Implementation
Steven Muchnick
Morgan Kaufmann, 1997
People wishing to speed up the code here should read:
Elimination Algorithms for Data Flow Analysis
B.G. Ryder, M.C. Paull
ACM Computing Surveys, Vol. 18, Num. 3, Sep. 1986
How to Analyze Large Programs Efficiently and Informatively
D.M. Dhamdhere, B.K. Rosen, F.K. Zadeck
ACM SIGPLAN Notices Vol. 27, Num. 7, Jul. 1992, '92 Conference on PLDI
People wishing to do something different can find various possibilities
in the above papers and elsewhere.
*/
#include "config.h"
#include "system.h"
#include "toplev.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "basic-block.h"
#include "output.h"
#include "expr.h"
#include "obstack.h"
#define obstack_chunk_alloc gmalloc
#define obstack_chunk_free free
/* Maximum number of passes to perform. */
#define MAX_PASSES 1
/* Propagate flow information through back edges and thus enable PRE's
moving loop invariant calculations out of loops.
Originally this tended to create worse overall code, but several
improvements during the development of PRE seem to have made following
back edges generally a win.
Note much of the loop invariant code motion done here would normally
be done by loop.c, which has more heuristics for when to move invariants
out of loops. At some point we might need to move some of those
heuristics into gcse.c. */
#define FOLLOW_BACK_EDGES 1
/* We support GCSE via Partial Redundancy Elimination. PRE optimizations
are a superset of those done by GCSE.
We perform the following steps:
1) Compute basic block information.
2) Compute table of places where registers are set.
3) Perform copy/constant propagation.
4) Perform global cse.
5) Perform another pass of copy/constant propagation.
Two passes of copy/constant propagation are done because the first one
enables more GCSE and the second one helps to clean up the copies that
GCSE creates. This is needed more for PRE than for Classic because Classic
GCSE will try to use an existing register containing the common
subexpression rather than create a new one. This is harder to do for PRE
because of the code motion (which Classic GCSE doesn't do).
Expressions we are interested in GCSE-ing are of the form
(set (pseudo-reg) (expression)).
Function want_to_gcse_p says what these are.
PRE handles moving invariant expressions out of loops (by treating them as
partially redundant).
Eventually it would be nice to replace cse.c/gcse.c with SSA (static single
assignment) based GVN (global value numbering). L. T. Simpson's paper
(Rice University) on value numbering is a useful reference for this.
**********************
We used to support multiple passes but there are diminishing returns in
doing so. The first pass usually makes 90% of the changes that are doable.
A second pass can make a few more changes made possible by the first pass.
Experiments show any further passes don't make enough changes to justify
the expense.
A study of spec92 using an unlimited number of passes:
[1 pass] = 1208 substitutions, [2] = 577, [3] = 202, [4] = 192, [5] = 83,
[6] = 34, [7] = 17, [8] = 9, [9] = 4, [10] = 4, [11] = 2,
[12] = 2, [13] = 1, [15] = 1, [16] = 2, [41] = 1
It was found doing copy propagation between each pass enables further
substitutions.
PRE is quite expensive in complicated functions because the DFA can take
awhile to converge. Hence we only perform one pass. Macro MAX_PASSES can
be modified if one wants to experiment.
**********************
The steps for PRE are:
1) Build the hash table of expressions we wish to GCSE (expr_hash_table).
2) Perform the data flow analysis for PRE.
3) Delete the redundant instructions
4) Insert the required copies [if any] that make the partially
redundant instructions fully redundant.
5) For other reaching expressions, insert an instruction to copy the value
to a newly created pseudo that will reach the redundant instruction.
The deletion is done first so that when we do insertions we
know which pseudo reg to use.
Various papers have argued that PRE DFA is expensive (O(n^2)) and others
argue it is not. The number of iterations for the algorithm to converge
is typically 2-4 so I don't view it as that expensive (relatively speaking).
PRE GCSE depends heavily on the second CSE pass to clean up the copies
we create. To make an expression reach the place where it's redundant,
the result of the expression is copied to a new register, and the redundant
expression is deleted by replacing it with this new register. Classic GCSE
doesn't have this problem as much as it computes the reaching defs of
each register in each block and thus can try to use an existing register.
**********************
A fair bit of simplicity is created by creating small functions for simple
tasks, even when the function is only called in one place. This may
measurably slow things down [or may not] by creating more function call
overhead than is necessary. The source is laid out so that it's trivial
to make the affected functions inline so that one can measure what speed
up, if any, can be achieved, and maybe later when things settle things can
be rearranged.
Help stamp out big monolithic functions! */
/* GCSE global vars. */
/* -dG dump file. */
static FILE *gcse_file;
/* Note whether or not we should run jump optimization after gcse. We
want to do this for two cases.
* If we changed any jumps via cprop.
* If we added any labels via edge splitting. */
static int run_jump_opt_after_gcse;
/* Element I is a list of I's predecessors/successors. */
static int_list_ptr *s_preds;
static int_list_ptr *s_succs;
/* Element I is the number of predecessors/successors of basic block I. */
static int *num_preds;
static int *num_succs;
/* Bitmaps are normally not included in debugging dumps.
However it's useful to be able to print them from GDB.
We could create special functions for this, but it's simpler to
just allow passing stderr to the dump_foo fns. Since stderr can
be a macro, we store a copy here. */
static FILE *debug_stderr;
/* An obstack for our working variables. */
static struct obstack gcse_obstack;
/* Non-zero for each mode that supports (set (reg) (reg)).
This is trivially true for integer and floating point values.
It may or may not be true for condition codes. */
static char can_copy_p[(int) NUM_MACHINE_MODES];
/* Non-zero if can_copy_p has been initialized. */
static int can_copy_init_p;
/* Hash table of expressions. */
struct expr
{
/* The expression (SET_SRC for expressions, PATTERN for assignments). */
rtx expr;
/* Index in the available expression bitmaps. */
int bitmap_index;
/* Next entry with the same hash. */
struct expr *next_same_hash;
/* List of anticipatable occurrences in basic blocks in the function.
An "anticipatable occurrence" is one that is the first occurrence in the
basic block, the operands are not modified in the basic block prior
to the occurrence and the output is not used between the start of
the block and the occurrence. */
struct occr *antic_occr;
/* List of available occurrence in basic blocks in the function.
An "available occurrence" is one that is the last occurrence in the
basic block and the operands are not modified by following statements in
the basic block [including this insn]. */
struct occr *avail_occr;
/* Non-null if the computation is PRE redundant.
The value is the newly created pseudo-reg to record a copy of the
expression in all the places that reach the redundant copy. */
rtx reaching_reg;
};
/* Occurrence of an expression.
There is one per basic block. If a pattern appears more than once the
last appearance is used [or first for anticipatable expressions]. */
struct occr
{
/* Next occurrence of this expression. */
struct occr *next;
/* The insn that computes the expression. */
rtx insn;
/* Non-zero if this [anticipatable] occurrence has been deleted. */
char deleted_p;
/* Non-zero if this [available] occurrence has been copied to
reaching_reg. */
/* ??? This is mutually exclusive with deleted_p, so they could share
the same byte. */
char copied_p;
};
/* Expression and copy propagation hash tables.
Each hash table is an array of buckets.
??? It is known that if it were an array of entries, structure elements
`next_same_hash' and `bitmap_index' wouldn't be necessary. However, it is
not clear whether in the final analysis a sufficient amount of memory would
be saved as the size of the available expression bitmaps would be larger
[one could build a mapping table without holes afterwards though].
Someday I'll perform the computation and figure it out.
*/
/* Total size of the expression hash table, in elements. */
static int expr_hash_table_size;
/* The table itself.
This is an array of `expr_hash_table_size' elements. */
static struct expr **expr_hash_table;
/* Total size of the copy propagation hash table, in elements. */
static int set_hash_table_size;
/* The table itself.
This is an array of `set_hash_table_size' elements. */
static struct expr **set_hash_table;
/* Mapping of uids to cuids.
Only real insns get cuids. */
static int *uid_cuid;
/* Highest UID in UID_CUID. */
static int max_uid;
/* Get the cuid of an insn. */
#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
/* Number of cuids. */
static int max_cuid;
/* Mapping of cuids to insns. */
static rtx *cuid_insn;
/* Get insn from cuid. */
#define CUID_INSN(CUID) (cuid_insn[CUID])
/* Maximum register number in function prior to doing gcse + 1.
Registers created during this pass have regno >= max_gcse_regno.
This is named with "gcse" to not collide with global of same name. */
static int max_gcse_regno;
/* Maximum number of cse-able expressions found. */
static int n_exprs;
/* Maximum number of assignments for copy propagation found. */
static int n_sets;
/* Table of registers that are modified.
For each register, each element is a list of places where the pseudo-reg
is set.
For simplicity, GCSE is done on sets of pseudo-regs only. PRE GCSE only
requires knowledge of which blocks kill which regs [and thus could use
a bitmap instead of the lists `reg_set_table' uses].
`reg_set_table' and could be turned into an array of bitmaps
(num-bbs x num-regs)
[however perhaps it may be useful to keep the data as is].
One advantage of recording things this way is that `reg_set_table' is
fairly sparse with respect to pseudo regs but for hard regs could be
fairly dense [relatively speaking].
And recording sets of pseudo-regs in lists speeds
up functions like compute_transp since in the case of pseudo-regs we only
need to iterate over the number of times a pseudo-reg is set, not over the
number of basic blocks [clearly there is a bit of a slow down in the cases
where a pseudo is set more than once in a block, however it is believed
that the net effect is to speed things up]. This isn't done for hard-regs
because recording call-clobbered hard-regs in `reg_set_table' at each
function call can consume a fair bit of memory, and iterating over hard-regs
stored this way in compute_transp will be more expensive. */
typedef struct reg_set {
/* The next setting of this register. */
struct reg_set *next;
/* The insn where it was set. */
rtx insn;
} reg_set;
static reg_set **reg_set_table;
/* Size of `reg_set_table'.
The table starts out at max_gcse_regno + slop, and is enlarged as
necessary. */
static int reg_set_table_size;
/* Amount to grow `reg_set_table' by when it's full. */
#define REG_SET_TABLE_SLOP 100
/* Bitmap containing one bit for each register in the program.
Used when performing GCSE to track which registers have been set since
the start of the basic block. */
static sbitmap reg_set_bitmap;
/* For each block, a bitmap of registers set in the block.
This is used by expr_killed_p and compute_transp.
It is computed during hash table computation and not by compute_sets
as it includes registers added since the last pass (or between cprop and
gcse) and it's currently not easy to realloc sbitmap vectors. */
static sbitmap *reg_set_in_block;
/* For each block, non-zero if memory is set in that block.
This is computed during hash table computation and is used by
expr_killed_p and compute_transp.
??? Handling of memory is very simple, we don't make any attempt
to optimize things (later).
??? This can be computed by compute_sets since the information
doesn't change. */
static char *mem_set_in_block;
/* Various variables for statistics gathering. */
/* Memory used in a pass.
This isn't intended to be absolutely precise. Its intent is only
to keep an eye on memory usage. */
static int bytes_used;
/* GCSE substitutions made. */
static int gcse_subst_count;
/* Number of copy instructions created. */
static int gcse_create_count;
/* Number of constants propagated. */
static int const_prop_count;
/* Number of copys propagated. */
static int copy_prop_count;
extern char *current_function_name;
extern int current_function_calls_setjmp;
/* These variables are used by classic GCSE.
Normally they'd be defined a bit later, but `rd_gen' needs to
be declared sooner. */
/* A bitmap of all ones for implementing the algorithm for available
expressions and reaching definitions. */
/* ??? Available expression bitmaps have a different size than reaching
definition bitmaps. This should be the larger of the two, however, it
is not currently used for reaching definitions. */
static sbitmap u_bitmap;
/* Each block has a bitmap of each type.
The length of each blocks bitmap is:
max_cuid - for reaching definitions
n_exprs - for available expressions
Thus we view the bitmaps as 2 dimensional arrays. i.e.
rd_kill[block_num][cuid_num]
ae_kill[block_num][expr_num]
*/
/* For reaching defs */
static sbitmap *rd_kill, *rd_gen, *reaching_defs, *rd_out;
/* for available exprs */
static sbitmap *ae_kill, *ae_gen, *ae_in, *ae_out;
static void compute_can_copy PROTO ((void));
static char *gmalloc PROTO ((unsigned int));
static char *grealloc PROTO ((char *, unsigned int));
static char *gcse_alloc PROTO ((unsigned long));
static void alloc_gcse_mem PROTO ((rtx));
static void free_gcse_mem PROTO ((void));
static void alloc_reg_set_mem PROTO ((int));
static void free_reg_set_mem PROTO ((void));
static void record_one_set PROTO ((int, rtx));
static void record_set_info PROTO ((rtx, rtx));
static void compute_sets PROTO ((rtx));
static void hash_scan_insn PROTO ((rtx, int, int));
static void hash_scan_set PROTO ((rtx, rtx, int));
static void hash_scan_clobber PROTO ((rtx, rtx));
static void hash_scan_call PROTO ((rtx, rtx));
static int want_to_gcse_p PROTO ((rtx));
static int oprs_unchanged_p PROTO ((rtx, rtx, int));
static int oprs_anticipatable_p PROTO ((rtx, rtx));
static int oprs_available_p PROTO ((rtx, rtx));
static void insert_expr_in_table PROTO ((rtx, enum machine_mode,
rtx, int, int));
static void insert_set_in_table PROTO ((rtx, rtx));
static unsigned int hash_expr PROTO ((rtx, enum machine_mode,
int *, int));
static unsigned int hash_expr_1 PROTO ((rtx, enum machine_mode, int *));
static unsigned int hash_set PROTO ((int, int));
static int expr_equiv_p PROTO ((rtx, rtx));
static void record_last_reg_set_info PROTO ((rtx, int));
static void record_last_mem_set_info PROTO ((rtx));
static void record_last_set_info PROTO ((rtx, rtx));
static void compute_hash_table PROTO ((int));
static void alloc_set_hash_table PROTO ((int));
static void free_set_hash_table PROTO ((void));
static void compute_set_hash_table PROTO ((void));
static void alloc_expr_hash_table PROTO ((int));
static void free_expr_hash_table PROTO ((void));
static void compute_expr_hash_table PROTO ((void));
static void dump_hash_table PROTO ((FILE *, const char *, struct expr **,
int, int));
static struct expr *lookup_expr PROTO ((rtx));
static struct expr *lookup_set PROTO ((int, rtx));
static struct expr *next_set PROTO ((int, struct expr *));
static void reset_opr_set_tables PROTO ((void));
static int oprs_not_set_p PROTO ((rtx, rtx));
static void mark_call PROTO ((rtx));
static void mark_set PROTO ((rtx, rtx));
static void mark_clobber PROTO ((rtx, rtx));
static void mark_oprs_set PROTO ((rtx));
static void alloc_cprop_mem PROTO ((int, int));
static void free_cprop_mem PROTO ((void));
static void compute_transp PROTO ((rtx, int, sbitmap *, int));
static void compute_transpout PROTO ((void));
static void compute_local_properties PROTO ((sbitmap *, sbitmap *,
sbitmap *, int));
static void compute_cprop_avinout PROTO ((void));
static void compute_cprop_data PROTO ((void));
static void find_used_regs PROTO ((rtx));
static int try_replace_reg PROTO ((rtx, rtx, rtx));
static struct expr *find_avail_set PROTO ((int, rtx));
static int cprop_insn PROTO ((rtx, int));
static int cprop PROTO ((int));
static int one_cprop_pass PROTO ((int, int));
static void alloc_pre_mem PROTO ((int, int));
static void free_pre_mem PROTO ((void));
static void compute_pre_data PROTO ((void));
static int pre_expr_reaches_here_p PROTO ((int, struct expr *,
int, int, char *));
static void insert_insn_end_bb PROTO ((struct expr *, int, int));
static void pre_insert PROTO ((struct expr **));
static void pre_insert_copy_insn PROTO ((struct expr *, rtx));
static void pre_insert_copies PROTO ((void));
static int pre_delete PROTO ((void));
static int pre_gcse PROTO ((void));
static int one_pre_gcse_pass PROTO ((int));
static void add_label_notes PROTO ((rtx, rtx));
static void alloc_rd_mem PROTO ((int, int));
static void free_rd_mem PROTO ((void));
static void handle_rd_kill_set PROTO ((rtx, int, int));
static void compute_kill_rd PROTO ((void));
static void compute_rd PROTO ((void));
static void alloc_avail_expr_mem PROTO ((int, int));
static void free_avail_expr_mem PROTO ((void));
static void compute_ae_gen PROTO ((void));
static int expr_killed_p PROTO ((rtx, int));
static void compute_ae_kill PROTO ((void));
static void compute_available PROTO ((void));
static int expr_reaches_here_p PROTO ((struct occr *, struct expr *,
int, int, char *));
static rtx computing_insn PROTO ((struct expr *, rtx));
static int def_reaches_here_p PROTO ((rtx, rtx));
static int can_disregard_other_sets PROTO ((struct reg_set **, rtx, int));
static int handle_avail_expr PROTO ((rtx, struct expr *));
static int classic_gcse PROTO ((void));
static int one_classic_gcse_pass PROTO ((int));
/* Entry point for global common subexpression elimination.
F is the first instruction in the function. */
int
gcse_main (f, file)
rtx f;
FILE *file;
{
int changed, pass;
/* Bytes used at start of pass. */
int initial_bytes_used;
/* Maximum number of bytes used by a pass. */
int max_pass_bytes;
/* Point to release obstack data from for each pass. */
char *gcse_obstack_bottom;
/* We do not construct an accurate cfg in functions which call
setjmp, so just punt to be safe. */
if (current_function_calls_setjmp)
return 0;
/* Assume that we do not need to run jump optimizations after gcse. */
run_jump_opt_after_gcse = 0;
/* For calling dump_foo fns from gdb. */
debug_stderr = stderr;
gcse_file = file;
/* Identify the basic block information for this function, including
successors and predecessors. */
max_gcse_regno = max_reg_num ();
find_basic_blocks (f, max_gcse_regno, file, 1);
/* Return if there's nothing to do. */
if (n_basic_blocks <= 1)
{
/* Free storage allocated by find_basic_blocks. */
free_basic_block_vars (0);
return 0;
}
/* See what modes support reg/reg copy operations. */
if (! can_copy_init_p)
{
compute_can_copy ();
can_copy_init_p = 1;
}
gcc_obstack_init (&gcse_obstack);
/* Allocate and compute predecessors/successors. */
s_preds = (int_list_ptr *) alloca (n_basic_blocks * sizeof (int_list_ptr));
s_succs = (int_list_ptr *) alloca (n_basic_blocks * sizeof (int_list_ptr));
num_preds = (int *) alloca (n_basic_blocks * sizeof (int));
num_succs = (int *) alloca (n_basic_blocks * sizeof (int));
bytes_used = 4 * n_basic_blocks * sizeof (int_list_ptr);
compute_preds_succs (s_preds, s_succs, num_preds, num_succs);
if (file)
dump_bb_data (file, s_preds, s_succs, 0);
/* Record where pseudo-registers are set.
This data is kept accurate during each pass.
??? We could also record hard-reg information here
[since it's unchanging], however it is currently done during
hash table computation.
It may be tempting to compute MEM set information here too, but MEM
sets will be subject to code motion one day and thus we need to compute
information about memory sets when we build the hash tables. */
alloc_reg_set_mem (max_gcse_regno);
compute_sets (f);
pass = 0;
initial_bytes_used = bytes_used;
max_pass_bytes = 0;
gcse_obstack_bottom = gcse_alloc (1);
changed = 1;
while (changed && pass < MAX_PASSES)
{
changed = 0;
if (file)
fprintf (file, "GCSE pass %d\n\n", pass + 1);
/* Initialize bytes_used to the space for the pred/succ lists,
and the reg_set_table data. */
bytes_used = initial_bytes_used;
/* Each pass may create new registers, so recalculate each time. */
max_gcse_regno = max_reg_num ();
alloc_gcse_mem (f);
/* Don't allow constant propagation to modify jumps
during this pass. */
changed = one_cprop_pass (pass + 1, 0);
if (optimize_size)
changed |= one_classic_gcse_pass (pass + 1);
else
changed |= one_pre_gcse_pass (pass + 1);
if (max_pass_bytes < bytes_used)
max_pass_bytes = bytes_used;
free_gcse_mem ();
if (file)
{
fprintf (file, "\n");
fflush (file);
}
obstack_free (&gcse_obstack, gcse_obstack_bottom);
pass++;
}
/* Do one last pass of copy propagation, including cprop into
conditional jumps. */
max_gcse_regno = max_reg_num ();
alloc_gcse_mem (f);
/* This time, go ahead and allow cprop to alter jumps. */
one_cprop_pass (pass + 1, 1);
free_gcse_mem ();
if (file)
{
fprintf (file, "GCSE of %s: %d basic blocks, ",
current_function_name, n_basic_blocks);
fprintf (file, "%d pass%s, %d bytes\n\n",
pass, pass > 1 ? "es" : "", max_pass_bytes);
}
/* Free our obstack. */
obstack_free (&gcse_obstack, NULL_PTR);
/* Free reg_set_table. */
free_reg_set_mem ();
/* Free storage used to record predecessor/successor data. */
free_bb_mem ();
/* Free storage allocated by find_basic_blocks. */
free_basic_block_vars (0);
return run_jump_opt_after_gcse;
}
/* Misc. utilities. */
/* Compute which modes support reg/reg copy operations. */
static void
compute_can_copy ()
{
int i;
#ifndef AVOID_CCMODE_COPIES
rtx reg,insn;
#endif
char *free_point = (char *) oballoc (1);
bzero (can_copy_p, NUM_MACHINE_MODES);
start_sequence ();
for (i = 0; i < NUM_MACHINE_MODES; i++)
{
switch (GET_MODE_CLASS (i))
{
case MODE_CC :
#ifdef AVOID_CCMODE_COPIES
can_copy_p[i] = 0;
#else
reg = gen_rtx_REG ((enum machine_mode) i, LAST_VIRTUAL_REGISTER + 1);
insn = emit_insn (gen_rtx_SET (VOIDmode, reg, reg));
if (recog (PATTERN (insn), insn, NULL_PTR) >= 0)
can_copy_p[i] = 1;
#endif
break;
default :
can_copy_p[i] = 1;
break;
}
}
end_sequence ();
/* Free the objects we just allocated. */
obfree (free_point);
}
/* Cover function to xmalloc to record bytes allocated. */
static char *
gmalloc (size)
unsigned int size;
{
bytes_used += size;
return xmalloc (size);
}
/* Cover function to xrealloc.
We don't record the additional size since we don't know it.
It won't affect memory usage stats much anyway. */
static char *
grealloc (ptr, size)
char *ptr;
unsigned int size;
{
return xrealloc (ptr, size);
}
/* Cover function to obstack_alloc.
We don't need to record the bytes allocated here since
obstack_chunk_alloc is set to gmalloc. */
static char *
gcse_alloc (size)
unsigned long size;
{
return (char *) obstack_alloc (&gcse_obstack, size);
}
/* Allocate memory for the cuid mapping array,
and reg/memory set tracking tables.
This is called at the start of each pass. */
static void
alloc_gcse_mem (f)
rtx f;
{
int i,n;
rtx insn;
/* Find the largest UID and create a mapping from UIDs to CUIDs.
CUIDs are like UIDs except they increase monotonically, have no gaps,
and only apply to real insns. */
max_uid = get_max_uid ();
n = (max_uid + 1) * sizeof (int);
uid_cuid = (int *) gmalloc (n);
bzero ((char *) uid_cuid, n);
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
INSN_CUID (insn) = i++;
else
INSN_CUID (insn) = i;
}
/* Create a table mapping cuids to insns. */
max_cuid = i;
n = (max_cuid + 1) * sizeof (rtx);
cuid_insn = (rtx *) gmalloc (n);
bzero ((char *) cuid_insn, n);
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
CUID_INSN (i) = insn;
i++;
}
}
/* Allocate vars to track sets of regs. */
reg_set_bitmap = (sbitmap) sbitmap_alloc (max_gcse_regno);
/* Allocate vars to track sets of regs, memory per block. */
reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks,
max_gcse_regno);
mem_set_in_block = (char *) gmalloc (n_basic_blocks);
}
/* Free memory allocated by alloc_gcse_mem. */
static void
free_gcse_mem ()
{
free (uid_cuid);
free (cuid_insn);
free (reg_set_bitmap);
free (reg_set_in_block);
free (mem_set_in_block);
}
/* Compute the local properties of each recorded expression.
Local properties are those that are defined by the block, irrespective
of other blocks.
An expression is transparent in a block if its operands are not modified
in the block.
An expression is computed (locally available) in a block if it is computed
at least once and expression would contain the same value if the
computation was moved to the end of the block.
An expression is locally anticipatable in a block if it is computed at
least once and expression would contain the same value if the computation
was moved to the beginning of the block.
We call this routine for cprop, pre and code hoisting. They all
compute basically the same information and thus can easily share
this code.
TRANSP, COMP, and ANTLOC are destination sbitmaps for recording
local properties. If NULL, then it is not necessary to compute
or record that particular property.
SETP controls which hash table to look at. If zero, this routine
looks at the expr hash table; if nonzero this routine looks at
the set hash table. Additionally, TRANSP is computed as ~TRANSP,
since this is really cprop's ABSALTERED. */
static void
compute_local_properties (transp, comp, antloc, setp)
sbitmap *transp;
sbitmap *comp;
sbitmap *antloc;
int setp;
{
int i, hash_table_size;
struct expr **hash_table;
/* Initialize any bitmaps that were passed in. */
if (transp)
{
if (setp)
sbitmap_vector_zero (transp, n_basic_blocks);
else
sbitmap_vector_ones (transp, n_basic_blocks);
}
if (comp)
sbitmap_vector_zero (comp, n_basic_blocks);
if (antloc)
sbitmap_vector_zero (antloc, n_basic_blocks);
/* We use the same code for cprop, pre and hoisting. For cprop
we care about the set hash table, for pre and hoisting we
care about the expr hash table. */
hash_table_size = setp ? set_hash_table_size : expr_hash_table_size;
hash_table = setp ? set_hash_table : expr_hash_table;
for (i = 0; i < hash_table_size; i++)
{
struct expr *expr;
for (expr = hash_table[i]; expr != NULL; expr = expr->next_same_hash)
{
struct occr *occr;
int indx = expr->bitmap_index;
/* The expression is transparent in this block if it is not killed.
We start by assuming all are transparent [none are killed], and
then reset the bits for those that are. */
if (transp)
compute_transp (expr->expr, indx, transp, setp);
/* The occurrences recorded in antic_occr are exactly those that
we want to set to non-zero in ANTLOC. */
if (antloc)
{
for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
{
int bb = BLOCK_NUM (occr->insn);
SET_BIT (antloc[bb], indx);
/* While we're scanning the table, this is a good place to
initialize this. */
occr->deleted_p = 0;
}
}
/* The occurrences recorded in avail_occr are exactly those that
we want to set to non-zero in COMP. */
if (comp)
{
for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
{
int bb = BLOCK_NUM (occr->insn);
SET_BIT (comp[bb], indx);
/* While we're scanning the table, this is a good place to
initialize this. */
occr->copied_p = 0;
}
}
/* While we're scanning the table, this is a good place to
initialize this. */
expr->reaching_reg = 0;
}
}
}
/* Register set information.
`reg_set_table' records where each register is set or otherwise
modified. */
static struct obstack reg_set_obstack;
static void
alloc_reg_set_mem (n_regs)
int n_regs;
{
int n;
reg_set_table_size = n_regs + REG_SET_TABLE_SLOP;
n = reg_set_table_size * sizeof (struct reg_set *);
reg_set_table = (struct reg_set **) gmalloc (n);
bzero ((char *) reg_set_table, n);
gcc_obstack_init (&reg_set_obstack);
}
static void
free_reg_set_mem ()
{
free (reg_set_table);
obstack_free (&reg_set_obstack, NULL_PTR);
}
/* Record REGNO in the reg_set table. */
static void
record_one_set (regno, insn)
int regno;
rtx insn;
{
/* allocate a new reg_set element and link it onto the list */
struct reg_set *new_reg_info, *reg_info_ptr1, *reg_info_ptr2;
/* If the table isn't big enough, enlarge it. */
if (regno >= reg_set_table_size)
{
int new_size = regno + REG_SET_TABLE_SLOP;
reg_set_table = (struct reg_set **)
grealloc ((char *) reg_set_table,
new_size * sizeof (struct reg_set *));
bzero ((char *) (reg_set_table + reg_set_table_size),
(new_size - reg_set_table_size) * sizeof (struct reg_set *));
reg_set_table_size = new_size;
}
new_reg_info = (struct reg_set *) obstack_alloc (&reg_set_obstack,
sizeof (struct reg_set));
bytes_used += sizeof (struct reg_set);
new_reg_info->insn = insn;
new_reg_info->next = NULL;
if (reg_set_table[regno] == NULL)
reg_set_table[regno] = new_reg_info;
else
{
reg_info_ptr1 = reg_info_ptr2 = reg_set_table[regno];
/* ??? One could keep a "last" pointer to speed this up. */
while (reg_info_ptr1 != NULL)
{
reg_info_ptr2 = reg_info_ptr1;
reg_info_ptr1 = reg_info_ptr1->next;
}
reg_info_ptr2->next = new_reg_info;
}
}
/* For communication between next two functions (via note_stores). */
static rtx record_set_insn;
/* Called from compute_sets via note_stores to handle one
SET or CLOBBER in an insn. */
static void
record_set_info (dest, setter)
rtx dest, setter ATTRIBUTE_UNUSED;
{
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
{
if (REGNO (dest) >= FIRST_PSEUDO_REGISTER)
record_one_set (REGNO (dest), record_set_insn);
}
}
/* Scan the function and record each set of each pseudo-register.
This is called once, at the start of the gcse pass.
See the comments for `reg_set_table' for further docs. */
static void
compute_sets (f)
rtx f;
{
rtx insn = f;
while (insn)
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
record_set_insn = insn;
note_stores (PATTERN (insn), record_set_info);
}
insn = NEXT_INSN (insn);
}
}
/* Hash table support. */
#define NEVER_SET -1
/* For each register, the cuid of the first/last insn in the block to set it,
or -1 if not set. */
static int *reg_first_set;
static int *reg_last_set;
/* While computing "first/last set" info, this is the CUID of first/last insn
to set memory or -1 if not set. `mem_last_set' is also used when
performing GCSE to record whether memory has been set since the beginning
of the block.
Note that handling of memory is very simple, we don't make any attempt
to optimize things (later). */
static int mem_first_set;
static int mem_last_set;
/* Perform a quick check whether X, the source of a set, is something
we want to consider for GCSE. */
static int
want_to_gcse_p (x)
rtx x;
{
enum rtx_code code = GET_CODE (x);
switch (code)
{
case REG:
case SUBREG:
case CONST_INT:
case CONST_DOUBLE:
case CALL:
return 0;
default:
break;
}
return 1;
}
/* Return non-zero if the operands of expression X are unchanged from the
start of INSN's basic block up to but not including INSN (if AVAIL_P == 0),
or from INSN to the end of INSN's basic block (if AVAIL_P != 0). */
static int
oprs_unchanged_p (x, insn, avail_p)
rtx x, insn;
int avail_p;
{
int i;
enum rtx_code code;
char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return 1;
code = GET_CODE (x);
switch (code)
{
case REG:
if (avail_p)
return (reg_last_set[REGNO (x)] == NEVER_SET
|| reg_last_set[REGNO (x)] < INSN_CUID (insn));
else
return (reg_first_set[REGNO (x)] == NEVER_SET
|| reg_first_set[REGNO (x)] >= INSN_CUID (insn));
case MEM:
if (avail_p)
{
if (mem_last_set != NEVER_SET
&& mem_last_set >= INSN_CUID (insn))
return 0;
}
else
{
if (mem_first_set != NEVER_SET
&& mem_first_set < INSN_CUID (insn))
return 0;
}
x = XEXP (x, 0);
goto repeat;
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
return 0;
case PC:
case CC0: /*FIXME*/
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return 1;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
goto repeat;
}
if (! oprs_unchanged_p (tem, insn, avail_p))
return 0;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
{
if (! oprs_unchanged_p (XVECEXP (x, i, j), insn, avail_p))
return 0;
}
}
}
return 1;
}
/* Return non-zero if the operands of expression X are unchanged from
the start of INSN's basic block up to but not including INSN. */
static int
oprs_anticipatable_p (x, insn)
rtx x, insn;
{
return oprs_unchanged_p (x, insn, 0);
}
/* Return non-zero if the operands of expression X are unchanged from
INSN to the end of INSN's basic block. */
static int
oprs_available_p (x, insn)
rtx x, insn;
{
return oprs_unchanged_p (x, insn, 1);
}
/* Hash expression X.
MODE is only used if X is a CONST_INT.
A boolean indicating if a volatile operand is found or if the expression
contains something we don't want to insert in the table is stored in
DO_NOT_RECORD_P.
??? One might want to merge this with canon_hash. Later. */
static unsigned int
hash_expr (x, mode, do_not_record_p, hash_table_size)
rtx x;
enum machine_mode mode;
int *do_not_record_p;
int hash_table_size;
{
unsigned int hash;
*do_not_record_p = 0;
hash = hash_expr_1 (x, mode, do_not_record_p);
return hash % hash_table_size;
}
/* Subroutine of hash_expr to do the actual work. */
static unsigned int
hash_expr_1 (x, mode, do_not_record_p)
rtx x;
enum machine_mode mode;
int *do_not_record_p;
{
int i, j;
unsigned hash = 0;
enum rtx_code code;
char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return hash;
code = GET_CODE (x);
switch (code)
{
case REG:
{
register int regno = REGNO (x);
hash += ((unsigned) REG << 7) + regno;
return hash;
}
case CONST_INT:
{
unsigned HOST_WIDE_INT tem = INTVAL (x);
hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
return hash;
}
case CONST_DOUBLE:
/* This is like the general case, except that it only counts
the integers representing the constant. */
hash += (unsigned) code + (unsigned) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
{
unsigned tem = XINT (x, i);
hash += tem;
}
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
return hash;
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
/* We don't hash on the address of the CODE_LABEL to avoid bootstrap
differences and differences between each stage's debugging dumps. */
hash += ((unsigned) LABEL_REF << 7) + CODE_LABEL_NUMBER (XEXP (x, 0));
return hash;
case SYMBOL_REF:
{
/* Don't hash on the symbol's address to avoid bootstrap differences.
Different hash values may cause expressions to be recorded in
different orders and thus different registers to be used in the
final assembler. This also avoids differences in the dump files
between various stages. */
unsigned int h = 0;
unsigned char *p = (unsigned char *) XSTR (x, 0);
while (*p)
h += (h << 7) + *p++; /* ??? revisit */
hash += ((unsigned) SYMBOL_REF << 7) + h;
return hash;
}
case MEM:
if (MEM_VOLATILE_P (x))
{
*do_not_record_p = 1;
return 0;
}
hash += (unsigned) MEM;
+ hash += MEM_ALIAS_SET (x);
x = XEXP (x, 0);
goto repeat;
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
case PC:
case CC0:
case CALL:
case UNSPEC_VOLATILE:
*do_not_record_p = 1;
return 0;
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
{
*do_not_record_p = 1;
return 0;
}
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
hash += (unsigned) code + (unsigned) GET_MODE (x);
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
goto repeat;
}
hash += hash_expr_1 (tem, 0, do_not_record_p);
if (*do_not_record_p)
return 0;
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
{
hash += hash_expr_1 (XVECEXP (x, i, j), 0, do_not_record_p);
if (*do_not_record_p)
return 0;
}
else if (fmt[i] == 's')
{
register unsigned char *p = (unsigned char *) XSTR (x, i);
if (p)
while (*p)
hash += *p++;
}
else if (fmt[i] == 'i')
{
register unsigned tem = XINT (x, i);
hash += tem;
}
else
abort ();
}
return hash;
}
/* Hash a set of register REGNO.
Sets are hashed on the register that is set.
This simplifies the PRE copy propagation code.
??? May need to make things more elaborate. Later, as necessary. */
static unsigned int
hash_set (regno, hash_table_size)
int regno;
int hash_table_size;
{
unsigned int hash;
hash = regno;
return hash % hash_table_size;
}
/* Return non-zero if exp1 is equivalent to exp2.
??? Borrowed from cse.c. Might want to remerge with cse.c. Later. */
static int
expr_equiv_p (x, y)
rtx x, y;
{
register int i, j;
register enum rtx_code code;
register char *fmt;
if (x == y)
return 1;
if (x == 0 || y == 0)
return x == y;
code = GET_CODE (x);
if (code != GET_CODE (y))
return 0;
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
if (GET_MODE (x) != GET_MODE (y))
return 0;
switch (code)
{
case PC:
case CC0:
return x == y;
case CONST_INT:
return INTVAL (x) == INTVAL (y);
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
case REG:
return REGNO (x) == REGNO (y);
+
+ case MEM:
+ /* Can't merge two expressions in different alias sets, since we can
+ decide that the expression is transparent in a block when it isn't,
+ due to it being set with the different alias set. */
+ if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
+ return 0;
+ break;
/* For commutative operations, check both orders. */
case PLUS:
case MULT:
case AND:
case IOR:
case XOR:
case NE:
case EQ:
return ((expr_equiv_p (XEXP (x, 0), XEXP (y, 0))
&& expr_equiv_p (XEXP (x, 1), XEXP (y, 1)))
|| (expr_equiv_p (XEXP (x, 0), XEXP (y, 1))
&& expr_equiv_p (XEXP (x, 1), XEXP (y, 0))));
default:
break;
}
/* Compare the elements. If any pair of corresponding elements
fail to match, return 0 for the whole thing. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'e':
if (! expr_equiv_p (XEXP (x, i), XEXP (y, i)))
return 0;
break;
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
for (j = 0; j < XVECLEN (x, i); j++)
if (! expr_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j)))
return 0;
break;
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case '0':
break;
default:
abort ();
}
}
return 1;
}
/* Insert expression X in INSN in the hash table.
If it is already present, record it as the last occurrence in INSN's
basic block.
MODE is the mode of the value X is being stored into.
It is only used if X is a CONST_INT.
ANTIC_P is non-zero if X is an anticipatable expression.
AVAIL_P is non-zero if X is an available expression. */
static void
insert_expr_in_table (x, mode, insn, antic_p, avail_p)
rtx x;
enum machine_mode mode;
rtx insn;
int antic_p, avail_p;
{
int found, do_not_record_p;
unsigned int hash;
struct expr *cur_expr, *last_expr = NULL;
struct occr *antic_occr, *avail_occr;
struct occr *last_occr = NULL;
hash = hash_expr (x, mode, &do_not_record_p, expr_hash_table_size);
/* Do not insert expression in table if it contains volatile operands,
or if hash_expr determines the expression is something we don't want
to or can't handle. */
if (do_not_record_p)
return;
cur_expr = expr_hash_table[hash];
found = 0;
while (cur_expr && ! (found = expr_equiv_p (cur_expr->expr, x)))
{
/* If the expression isn't found, save a pointer to the end of
the list. */
last_expr = cur_expr;
cur_expr = cur_expr->next_same_hash;
}
if (! found)
{
cur_expr = (struct expr *) gcse_alloc (sizeof (struct expr));
bytes_used += sizeof (struct expr);
if (expr_hash_table[hash] == NULL)
{
/* This is the first pattern that hashed to this index. */
expr_hash_table[hash] = cur_expr;
}
else
{
/* Add EXPR to end of this hash chain. */
last_expr->next_same_hash = cur_expr;
}
/* Set the fields of the expr element. */
cur_expr->expr = x;
cur_expr->bitmap_index = n_exprs++;
cur_expr->next_same_hash = NULL;
cur_expr->antic_occr = NULL;
cur_expr->avail_occr = NULL;
}
/* Now record the occurrence(s). */
if (antic_p)
{
antic_occr = cur_expr->antic_occr;
/* Search for another occurrence in the same basic block. */
while (antic_occr && BLOCK_NUM (antic_occr->insn) != BLOCK_NUM (insn))
{
/* If an occurrence isn't found, save a pointer to the end of
the list. */
last_occr = antic_occr;
antic_occr = antic_occr->next;
}
if (antic_occr)
{
/* Found another instance of the expression in the same basic block.
Prefer the currently recorded one. We want the first one in the
block and the block is scanned from start to end. */
; /* nothing to do */
}
else
{
/* First occurrence of this expression in this basic block. */
antic_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
bytes_used += sizeof (struct occr);
/* First occurrence of this expression in any block? */
if (cur_expr->antic_occr == NULL)
cur_expr->antic_occr = antic_occr;
else
last_occr->next = antic_occr;
antic_occr->insn = insn;
antic_occr->next = NULL;
}
}
if (avail_p)
{
avail_occr = cur_expr->avail_occr;
/* Search for another occurrence in the same basic block. */
while (avail_occr && BLOCK_NUM (avail_occr->insn) != BLOCK_NUM (insn))
{
/* If an occurrence isn't found, save a pointer to the end of
the list. */
last_occr = avail_occr;
avail_occr = avail_occr->next;
}
if (avail_occr)
{
/* Found another instance of the expression in the same basic block.
Prefer this occurrence to the currently recorded one. We want
the last one in the block and the block is scanned from start
to end. */
avail_occr->insn = insn;
}
else
{
/* First occurrence of this expression in this basic block. */
avail_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
bytes_used += sizeof (struct occr);
/* First occurrence of this expression in any block? */
if (cur_expr->avail_occr == NULL)
cur_expr->avail_occr = avail_occr;
else
last_occr->next = avail_occr;
avail_occr->insn = insn;
avail_occr->next = NULL;
}
}
}
/* Insert pattern X in INSN in the hash table.
X is a SET of a reg to either another reg or a constant.
If it is already present, record it as the last occurrence in INSN's
basic block. */
static void
insert_set_in_table (x, insn)
rtx x;
rtx insn;
{
int found;
unsigned int hash;
struct expr *cur_expr, *last_expr = NULL;
struct occr *cur_occr, *last_occr = NULL;
if (GET_CODE (x) != SET
|| GET_CODE (SET_DEST (x)) != REG)
abort ();
hash = hash_set (REGNO (SET_DEST (x)), set_hash_table_size);
cur_expr = set_hash_table[hash];
found = 0;
while (cur_expr && ! (found = expr_equiv_p (cur_expr->expr, x)))
{
/* If the expression isn't found, save a pointer to the end of
the list. */
last_expr = cur_expr;
cur_expr = cur_expr->next_same_hash;
}
if (! found)
{
cur_expr = (struct expr *) gcse_alloc (sizeof (struct expr));
bytes_used += sizeof (struct expr);
if (set_hash_table[hash] == NULL)
{
/* This is the first pattern that hashed to this index. */
set_hash_table[hash] = cur_expr;
}
else
{
/* Add EXPR to end of this hash chain. */
last_expr->next_same_hash = cur_expr;
}
/* Set the fields of the expr element.
We must copy X because it can be modified when copy propagation is
performed on its operands. */
/* ??? Should this go in a different obstack? */
cur_expr->expr = copy_rtx (x);
cur_expr->bitmap_index = n_sets++;
cur_expr->next_same_hash = NULL;
cur_expr->antic_occr = NULL;
cur_expr->avail_occr = NULL;
}
/* Now record the occurrence. */
cur_occr = cur_expr->avail_occr;
/* Search for another occurrence in the same basic block. */
while (cur_occr && BLOCK_NUM (cur_occr->insn) != BLOCK_NUM (insn))
{
/* If an occurrence isn't found, save a pointer to the end of
the list. */
last_occr = cur_occr;
cur_occr = cur_occr->next;
}
if (cur_occr)
{
/* Found another instance of the expression in the same basic block.
Prefer this occurrence to the currently recorded one. We want
the last one in the block and the block is scanned from start
to end. */
cur_occr->insn = insn;
}
else
{
/* First occurrence of this expression in this basic block. */
cur_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
bytes_used += sizeof (struct occr);
/* First occurrence of this expression in any block? */
if (cur_expr->avail_occr == NULL)
cur_expr->avail_occr = cur_occr;
else
last_occr->next = cur_occr;
cur_occr->insn = insn;
cur_occr->next = NULL;
}
}
/* Scan pattern PAT of INSN and add an entry to the hash table.
If SET_P is non-zero, this is for the assignment hash table,
otherwise it is for the expression hash table. */
static void
hash_scan_set (pat, insn, set_p)
rtx pat, insn;
int set_p;
{
rtx src = SET_SRC (pat);
rtx dest = SET_DEST (pat);
if (GET_CODE (src) == CALL)
hash_scan_call (src, insn);
if (GET_CODE (dest) == REG)
{
int regno = REGNO (dest);
rtx tmp;
/* Only record sets of pseudo-regs in the hash table. */
if (! set_p
&& regno >= FIRST_PSEUDO_REGISTER
/* Don't GCSE something if we can't do a reg/reg copy. */
&& can_copy_p [GET_MODE (dest)]
/* Is SET_SRC something we want to gcse? */
&& want_to_gcse_p (src))
{
/* An expression is not anticipatable if its operands are
modified before this insn. */
int antic_p = ! optimize_size && oprs_anticipatable_p (src, insn);
/* An expression is not available if its operands are
subsequently modified, including this insn. */
int avail_p = oprs_available_p (src, insn);
insert_expr_in_table (src, GET_MODE (dest), insn, antic_p, avail_p);
}
/* Record sets for constant/copy propagation. */
else if (set_p
&& regno >= FIRST_PSEUDO_REGISTER
&& ((GET_CODE (src) == REG
&& REGNO (src) >= FIRST_PSEUDO_REGISTER
&& can_copy_p [GET_MODE (dest)])
/* ??? CONST_INT:wip */
|| GET_CODE (src) == CONST_INT
|| GET_CODE (src) == CONST_DOUBLE)
/* A copy is not available if its src or dest is subsequently
modified. Here we want to search from INSN+1 on, but
oprs_available_p searches from INSN on. */
&& (insn == BLOCK_END (BLOCK_NUM (insn))
|| ((tmp = next_nonnote_insn (insn)) != NULL_RTX
&& oprs_available_p (pat, tmp))))
insert_set_in_table (pat, insn);
}
}
static void
hash_scan_clobber (x, insn)
rtx x ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
{
/* Currently nothing to do. */
}
static void
hash_scan_call (x, insn)
rtx x ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
{
/* Currently nothing to do. */
}
/* Process INSN and add hash table entries as appropriate.
Only available expressions that set a single pseudo-reg are recorded.
Single sets in a PARALLEL could be handled, but it's an extra complication
that isn't dealt with right now. The trick is handling the CLOBBERs that
are also in the PARALLEL. Later.
If SET_P is non-zero, this is for the assignment hash table,
otherwise it is for the expression hash table.
If IN_LIBCALL_BLOCK nonzero, we are in a libcall block, and should
not record any expressions. */
static void
hash_scan_insn (insn, set_p, in_libcall_block)
rtx insn;
int set_p;
int in_libcall_block;
{
rtx pat = PATTERN (insn);
/* Pick out the sets of INSN and for other forms of instructions record
what's been modified. */
if (GET_CODE (pat) == SET && ! in_libcall_block)
hash_scan_set (pat, insn, set_p);
else if (GET_CODE (pat) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx x = XVECEXP (pat, 0, i);
if (GET_CODE (x) == SET)
{
if (GET_CODE (SET_SRC (x)) == CALL)
hash_scan_call (SET_SRC (x), insn);
}
else if (GET_CODE (x) == CLOBBER)
hash_scan_clobber (x, insn);
else if (GET_CODE (x) == CALL)
hash_scan_call (x, insn);
}
}
else if (GET_CODE (pat) == CLOBBER)
hash_scan_clobber (pat, insn);
else if (GET_CODE (pat) == CALL)
hash_scan_call (pat, insn);
}
static void
dump_hash_table (file, name, table, table_size, total_size)
FILE *file;
const char *name;
struct expr **table;
int table_size, total_size;
{
int i;
/* Flattened out table, so it's printed in proper order. */
struct expr **flat_table = (struct expr **) alloca (total_size * sizeof (struct expr *));
unsigned int *hash_val = (unsigned int *) alloca (total_size * sizeof (unsigned int));
bzero ((char *) flat_table, total_size * sizeof (struct expr *));
for (i = 0; i < table_size; i++)
{
struct expr *expr;
for (expr = table[i]; expr != NULL; expr = expr->next_same_hash)
{
flat_table[expr->bitmap_index] = expr;
hash_val[expr->bitmap_index] = i;
}
}
fprintf (file, "%s hash table (%d buckets, %d entries)\n",
name, table_size, total_size);
for (i = 0; i < total_size; i++)
{
struct expr *expr = flat_table[i];
fprintf (file, "Index %d (hash value %d)\n ",
expr->bitmap_index, hash_val[i]);
print_rtl (file, expr->expr);
fprintf (file, "\n");
}
fprintf (file, "\n");
}
/* Record register first/last/block set information for REGNO in INSN.
reg_first_set records the first place in the block where the register
is set and is used to compute "anticipatability".
reg_last_set records the last place in the block where the register
is set and is used to compute "availability".
reg_set_in_block records whether the register is set in the block
and is used to compute "transparency". */
static void
record_last_reg_set_info (insn, regno)
rtx insn;
int regno;
{
if (reg_first_set[regno] == NEVER_SET)
reg_first_set[regno] = INSN_CUID (insn);
reg_last_set[regno] = INSN_CUID (insn);
SET_BIT (reg_set_in_block[BLOCK_NUM (insn)], regno);
}
/* Record memory first/last/block set information for INSN. */
static void
record_last_mem_set_info (insn)
rtx insn;
{
if (mem_first_set == NEVER_SET)
mem_first_set = INSN_CUID (insn);
mem_last_set = INSN_CUID (insn);
mem_set_in_block[BLOCK_NUM (insn)] = 1;
}
/* Used for communicating between next two routines. */
static rtx last_set_insn;
/* Called from compute_hash_table via note_stores to handle one
SET or CLOBBER in an insn. */
static void
record_last_set_info (dest, setter)
rtx dest, setter ATTRIBUTE_UNUSED;
{
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
record_last_reg_set_info (last_set_insn, REGNO (dest));
else if (GET_CODE (dest) == MEM
/* Ignore pushes, they clobber nothing. */
&& ! push_operand (dest, GET_MODE (dest)))
record_last_mem_set_info (last_set_insn);
}
/* Top level function to create an expression or assignment hash table.
Expression entries are placed in the hash table if
- they are of the form (set (pseudo-reg) src),
- src is something we want to perform GCSE on,
- none of the operands are subsequently modified in the block
Assignment entries are placed in the hash table if
- they are of the form (set (pseudo-reg) src),
- src is something we want to perform const/copy propagation on,
- none of the operands or target are subsequently modified in the block
Currently src must be a pseudo-reg or a const_int.
F is the first insn.
SET_P is non-zero for computing the assignment hash table. */
static void
compute_hash_table (set_p)
int set_p;
{
int bb;
/* While we compute the hash table we also compute a bit array of which
registers are set in which blocks.
We also compute which blocks set memory, in the absence of aliasing
support [which is TODO].
??? This isn't needed during const/copy propagation, but it's cheap to
compute. Later. */
sbitmap_vector_zero (reg_set_in_block, n_basic_blocks);
bzero ((char *) mem_set_in_block, n_basic_blocks);
/* Some working arrays used to track first and last set in each block. */
/* ??? One could use alloca here, but at some size a threshold is crossed
beyond which one should use malloc. Are we at that threshold here? */
reg_first_set = (int *) gmalloc (max_gcse_regno * sizeof (int));
reg_last_set = (int *) gmalloc (max_gcse_regno * sizeof (int));
for (bb = 0; bb < n_basic_blocks; bb++)
{
rtx insn;
int regno;
int in_libcall_block;
int i;
/* First pass over the instructions records information used to
determine when registers and memory are first and last set.
??? The mem_set_in_block and hard-reg reg_set_in_block computation
could be moved to compute_sets since they currently don't change. */
for (i = 0; i < max_gcse_regno; i++)
reg_first_set[i] = reg_last_set[i] = NEVER_SET;
mem_first_set = NEVER_SET;
mem_last_set = NEVER_SET;
for (insn = BLOCK_HEAD (bb);
insn && insn != NEXT_INSN (BLOCK_END (bb));
insn = NEXT_INSN (insn))
{
#ifdef NON_SAVING_SETJMP
if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
record_last_reg_set_info (insn, regno);
continue;
}
#endif
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (GET_CODE (insn) == CALL_INSN)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if ((call_used_regs[regno]
&& regno != STACK_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& regno != HARD_FRAME_POINTER_REGNUM
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
&& ! (regno == PIC_OFFSET_TABLE_REGNUM && flag_pic)
#endif
&& regno != FRAME_POINTER_REGNUM)
|| global_regs[regno])
record_last_reg_set_info (insn, regno);
if (! CONST_CALL_P (insn))
record_last_mem_set_info (insn);
}
last_set_insn = insn;
note_stores (PATTERN (insn), record_last_set_info);
}
/* The next pass builds the hash table. */
for (insn = BLOCK_HEAD (bb), in_libcall_block = 0;
insn && insn != NEXT_INSN (BLOCK_END (bb));
insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
in_libcall_block = 1;
else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
in_libcall_block = 0;
hash_scan_insn (insn, set_p, in_libcall_block);
}
}
}
free (reg_first_set);
free (reg_last_set);
/* Catch bugs early. */
reg_first_set = reg_last_set = 0;
}
/* Allocate space for the set hash table.
N_INSNS is the number of instructions in the function.
It is used to determine the number of buckets to use. */
static void
alloc_set_hash_table (n_insns)
int n_insns;
{
int n;
set_hash_table_size = n_insns / 4;
if (set_hash_table_size < 11)
set_hash_table_size = 11;
/* Attempt to maintain efficient use of hash table.
Making it an odd number is simplest for now.
??? Later take some measurements. */
set_hash_table_size |= 1;
n = set_hash_table_size * sizeof (struct expr *);
set_hash_table = (struct expr **) gmalloc (n);
}
/* Free things allocated by alloc_set_hash_table. */
static void
free_set_hash_table ()
{
free (set_hash_table);
}
/* Compute the hash table for doing copy/const propagation. */
static void
compute_set_hash_table ()
{
/* Initialize count of number of entries in hash table. */
n_sets = 0;
bzero ((char *) set_hash_table, set_hash_table_size * sizeof (struct expr *));
compute_hash_table (1);
}
/* Allocate space for the expression hash table.
N_INSNS is the number of instructions in the function.
It is used to determine the number of buckets to use. */
static void
alloc_expr_hash_table (n_insns)
int n_insns;
{
int n;
expr_hash_table_size = n_insns / 2;
/* Make sure the amount is usable. */
if (expr_hash_table_size < 11)
expr_hash_table_size = 11;
/* Attempt to maintain efficient use of hash table.
Making it an odd number is simplest for now.
??? Later take some measurements. */
expr_hash_table_size |= 1;
n = expr_hash_table_size * sizeof (struct expr *);
expr_hash_table = (struct expr **) gmalloc (n);
}
/* Free things allocated by alloc_expr_hash_table. */
static void
free_expr_hash_table ()
{
free (expr_hash_table);
}
/* Compute the hash table for doing GCSE. */
static void
compute_expr_hash_table ()
{
/* Initialize count of number of entries in hash table. */
n_exprs = 0;
bzero ((char *) expr_hash_table, expr_hash_table_size * sizeof (struct expr *));
compute_hash_table (0);
}
/* Expression tracking support. */
/* Lookup pattern PAT in the expression table.
The result is a pointer to the table entry, or NULL if not found. */
static struct expr *
lookup_expr (pat)
rtx pat;
{
int do_not_record_p;
unsigned int hash = hash_expr (pat, GET_MODE (pat), &do_not_record_p,
expr_hash_table_size);
struct expr *expr;
if (do_not_record_p)
return NULL;
expr = expr_hash_table[hash];
while (expr && ! expr_equiv_p (expr->expr, pat))
expr = expr->next_same_hash;
return expr;
}
/* Lookup REGNO in the set table.
If PAT is non-NULL look for the entry that matches it, otherwise return
the first entry for REGNO.
The result is a pointer to the table entry, or NULL if not found. */
static struct expr *
lookup_set (regno, pat)
int regno;
rtx pat;
{
unsigned int hash = hash_set (regno, set_hash_table_size);
struct expr *expr;
expr = set_hash_table[hash];
if (pat)
{
while (expr && ! expr_equiv_p (expr->expr, pat))
expr = expr->next_same_hash;
}
else
{
while (expr && REGNO (SET_DEST (expr->expr)) != regno)
expr = expr->next_same_hash;
}
return expr;
}
/* Return the next entry for REGNO in list EXPR. */
static struct expr *
next_set (regno, expr)
int regno;
struct expr *expr;
{
do
expr = expr->next_same_hash;
while (expr && REGNO (SET_DEST (expr->expr)) != regno);
return expr;
}
/* Reset tables used to keep track of what's still available [since the
start of the block]. */
static void
reset_opr_set_tables ()
{
/* Maintain a bitmap of which regs have been set since beginning of
the block. */
sbitmap_zero (reg_set_bitmap);
/* Also keep a record of the last instruction to modify memory.
For now this is very trivial, we only record whether any memory
location has been modified. */
mem_last_set = 0;
}
/* Return non-zero if the operands of X are not set before INSN in
INSN's basic block. */
static int
oprs_not_set_p (x, insn)
rtx x, insn;
{
int i;
enum rtx_code code;
char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return 1;
code = GET_CODE (x);
switch (code)
{
case PC:
case CC0:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return 1;
case MEM:
if (mem_last_set != 0)
return 0;
x = XEXP (x, 0);
goto repeat;
case REG:
return ! TEST_BIT (reg_set_bitmap, REGNO (x));
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
int not_set_p;
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = XEXP (x, 0);
goto repeat;
}
not_set_p = oprs_not_set_p (XEXP (x, i), insn);
if (! not_set_p)
return 0;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
{
int not_set_p = oprs_not_set_p (XVECEXP (x, i, j), insn);
if (! not_set_p)
return 0;
}
}
}
return 1;
}
/* Mark things set by a CALL. */
static void
mark_call (insn)
rtx insn;
{
mem_last_set = INSN_CUID (insn);
}
/* Mark things set by a SET. */
static void
mark_set (pat, insn)
rtx pat, insn;
{
rtx dest = SET_DEST (pat);
while (GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG)
SET_BIT (reg_set_bitmap, REGNO (dest));
else if (GET_CODE (dest) == MEM)
mem_last_set = INSN_CUID (insn);
if (GET_CODE (SET_SRC (pat)) == CALL)
mark_call (insn);
}
/* Record things set by a CLOBBER. */
static void
mark_clobber (pat, insn)
rtx pat, insn;
{
rtx clob = XEXP (pat, 0);
while (GET_CODE (clob) == SUBREG || GET_CODE (clob) == STRICT_LOW_PART)
clob = XEXP (clob, 0);
if (GET_CODE (clob) == REG)
SET_BIT (reg_set_bitmap, REGNO (clob));
else
mem_last_set = INSN_CUID (insn);
}
/* Record things set by INSN.
This data is used by oprs_not_set_p. */
static void
mark_oprs_set (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) == SET)
mark_set (pat, insn);
else if (GET_CODE (pat) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx x = XVECEXP (pat, 0, i);
if (GET_CODE (x) == SET)
mark_set (x, insn);
else if (GET_CODE (x) == CLOBBER)
mark_clobber (x, insn);
else if (GET_CODE (x) == CALL)
mark_call (insn);
}
}
else if (GET_CODE (pat) == CLOBBER)
mark_clobber (pat, insn);
else if (GET_CODE (pat) == CALL)
mark_call (insn);
}
/* Classic GCSE reaching definition support. */
/* Allocate reaching def variables. */
static void
alloc_rd_mem (n_blocks, n_insns)
int n_blocks, n_insns;
{
rd_kill = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
sbitmap_vector_zero (rd_kill, n_basic_blocks);
rd_gen = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
sbitmap_vector_zero (rd_gen, n_basic_blocks);
reaching_defs = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
sbitmap_vector_zero (reaching_defs, n_basic_blocks);
rd_out = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
sbitmap_vector_zero (rd_out, n_basic_blocks);
}
/* Free reaching def variables. */
static void
free_rd_mem ()
{
free (rd_kill);
free (rd_gen);
free (reaching_defs);
free (rd_out);
}
/* Add INSN to the kills of BB.
REGNO, set in BB, is killed by INSN. */
static void
handle_rd_kill_set (insn, regno, bb)
rtx insn;
int regno, bb;
{
struct reg_set *this_reg = reg_set_table[regno];
while (this_reg)
{
if (BLOCK_NUM (this_reg->insn) != BLOCK_NUM (insn))
SET_BIT (rd_kill[bb], INSN_CUID (this_reg->insn));
this_reg = this_reg->next;
}
}
/* Compute the set of kill's for reaching definitions. */
static void
compute_kill_rd ()
{
int bb,cuid;
/* For each block
For each set bit in `gen' of the block (i.e each insn which
generates a definition in the block)
Call the reg set by the insn corresponding to that bit regx
Look at the linked list starting at reg_set_table[regx]
For each setting of regx in the linked list, which is not in
this block
Set the bit in `kill' corresponding to that insn
*/
for (bb = 0; bb < n_basic_blocks; bb++)
{
for (cuid = 0; cuid < max_cuid; cuid++)
{
if (TEST_BIT (rd_gen[bb], cuid))
{
rtx insn = CUID_INSN (cuid);
rtx pat = PATTERN (insn);
if (GET_CODE (insn) == CALL_INSN)
{
int regno;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if ((call_used_regs[regno]
&& regno != STACK_POINTER_REGNUM
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& regno != HARD_FRAME_POINTER_REGNUM
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == ARG_POINTER_REGNUM
&& fixed_regs[regno])
#endif
#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
&& ! (regno == PIC_OFFSET_TABLE_REGNUM && flag_pic)
#endif
&& regno != FRAME_POINTER_REGNUM)
|| global_regs[regno])
handle_rd_kill_set (insn, regno, bb);
}
}
if (GET_CODE (pat) == PARALLEL)
{
int i;
/* We work backwards because ... */
for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
{
enum rtx_code code = GET_CODE (XVECEXP (pat, 0, i));
if ((code == SET || code == CLOBBER)
&& GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == REG)
handle_rd_kill_set (insn,
REGNO (XEXP (XVECEXP (pat, 0, i), 0)),
bb);
}
}
else if (GET_CODE (pat) == SET)
{
if (GET_CODE (SET_DEST (pat)) == REG)
{
/* Each setting of this register outside of this block
must be marked in the set of kills in this block. */
handle_rd_kill_set (insn, REGNO (SET_DEST (pat)), bb);
}
}
/* FIXME: CLOBBER? */
}
}
}
}
/* Compute the reaching definitions as in
Compilers Principles, Techniques, and Tools. Aho, Sethi, Ullman,
Chapter 10. It is the same algorithm as used for computing available
expressions but applied to the gens and kills of reaching definitions. */
static void
compute_rd ()
{
int bb, changed, passes;
for (bb = 0; bb < n_basic_blocks; bb++)
sbitmap_copy (rd_out[bb] /*dst*/, rd_gen[bb] /*src*/);
passes = 0;
changed = 1;
while (changed)
{
changed = 0;
for (bb = 0; bb < n_basic_blocks; bb++)
{
sbitmap_union_of_predecessors (reaching_defs[bb], rd_out,
bb, s_preds);
changed |= sbitmap_union_of_diff (rd_out[bb], rd_gen[bb],
reaching_defs[bb], rd_kill[bb]);
}
passes++;
}
if (gcse_file)
fprintf (gcse_file, "reaching def computation: %d passes\n", passes);
}
/* Classic GCSE available expression support. */
/* Allocate memory for available expression computation. */
static void
alloc_avail_expr_mem (n_blocks, n_exprs)
int n_blocks, n_exprs;
{
ae_kill = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
sbitmap_vector_zero (ae_kill, n_basic_blocks);
ae_gen = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
sbitmap_vector_zero (ae_gen, n_basic_blocks);
ae_in = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
sbitmap_vector_zero (ae_in, n_basic_blocks);
ae_out = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
sbitmap_vector_zero (ae_out, n_basic_blocks);
u_bitmap = (sbitmap) sbitmap_alloc (n_exprs);
sbitmap_ones (u_bitmap);
}
static void
free_avail_expr_mem ()
{
free (ae_kill);
free (ae_gen);
free (ae_in);
free (ae_out);
free (u_bitmap);
}
/* Compute the set of available expressions generated in each basic block. */
static void
compute_ae_gen ()
{
int i;
/* For each recorded occurrence of each expression, set ae_gen[bb][expr].
This is all we have to do because an expression is not recorded if it
is not available, and the only expressions we want to work with are the
ones that are recorded. */
for (i = 0; i < expr_hash_table_size; i++)
{
struct expr *expr = expr_hash_table[i];
while (expr != NULL)
{
struct occr *occr = expr->avail_occr;
while (occr != NULL)
{
SET_BIT (ae_gen[BLOCK_NUM (occr->insn)], expr->bitmap_index);
occr = occr->next;
}
expr = expr->next_same_hash;
}
}
}
/* Return non-zero if expression X is killed in BB. */
static int
expr_killed_p (x, bb)
rtx x;
int bb;
{
int i;
enum rtx_code code;
char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return 1;
code = GET_CODE (x);
switch (code)
{
case REG:
return TEST_BIT (reg_set_in_block[bb], REGNO (x));
case MEM:
if (mem_set_in_block[bb])
return 1;
x = XEXP (x, 0);
goto repeat;
case PC:
case CC0: /*FIXME*/
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return 0;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
goto repeat;
}
if (expr_killed_p (tem, bb))
return 1;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
{
if (expr_killed_p (XVECEXP (x, i, j), bb))
return 1;
}
}
}
return 0;
}
/* Compute the set of available expressions killed in each basic block. */
static void
compute_ae_kill ()
{
int bb,i;
for (bb = 0; bb < n_basic_blocks; bb++)
{
for (i = 0; i < expr_hash_table_size; i++)
{
struct expr *expr = expr_hash_table[i];
for ( ; expr != NULL; expr = expr->next_same_hash)
{
/* Skip EXPR if generated in this block. */
if (TEST_BIT (ae_gen[bb], expr->bitmap_index))
continue;
if (expr_killed_p (expr->expr, bb))
SET_BIT (ae_kill[bb], expr->bitmap_index);
}
}
}
}
/* Compute available expressions.
Implement the algorithm to find available expressions
as given in the Aho Sethi Ullman book, pages 627-631. */
static void
compute_available ()
{
int bb, changed, passes;
sbitmap_zero (ae_in[0]);
sbitmap_copy (ae_out[0] /*dst*/, ae_gen[0] /*src*/);
for (bb = 1; bb < n_basic_blocks; bb++)
sbitmap_difference (ae_out[bb], u_bitmap, ae_kill[bb]);
passes = 0;
changed = 1;
while (changed)
{
changed = 0;
for (bb = 1; bb < n_basic_blocks; bb++)
{
sbitmap_intersect_of_predecessors (ae_in[bb], ae_out, bb, s_preds);
changed |= sbitmap_union_of_diff (ae_out[bb], ae_gen[bb],
ae_in[bb], ae_kill[bb]);
}
passes++;
}
if (gcse_file)
fprintf (gcse_file, "avail expr computation: %d passes\n", passes);
}
/* Actually perform the Classic GCSE optimizations. */
/* Return non-zero if occurrence OCCR of expression EXPR reaches block BB.
CHECK_SELF_LOOP is non-zero if we should consider a block reaching itself
as a positive reach. We want to do this when there are two computations
of the expression in the block.
VISITED is a pointer to a working buffer for tracking which BB's have
been visited. It is NULL for the top-level call.
We treat reaching expressions that go through blocks containing the same
reaching expression as "not reaching". E.g. if EXPR is generated in blocks
2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
2 as not reaching. The intent is to improve the probability of finding
only one reaching expression and to reduce register lifetimes by picking
the closest such expression. */
static int
expr_reaches_here_p (occr, expr, bb, check_self_loop, visited)
struct occr *occr;
struct expr *expr;
int bb;
int check_self_loop;
char *visited;
{
int_list_ptr pred;
if (visited == NULL)
{
visited = (char *) alloca (n_basic_blocks);
bzero (visited, n_basic_blocks);
}
for (pred = s_preds[bb]; pred != NULL; pred = pred->next)
{
int pred_bb = INT_LIST_VAL (pred);
if (visited[pred_bb])
{
/* This predecessor has already been visited.
Nothing to do. */
;
}
else if (pred_bb == bb)
{
/* BB loops on itself. */
if (check_self_loop
&& TEST_BIT (ae_gen[pred_bb], expr->bitmap_index)
&& BLOCK_NUM (occr->insn) == pred_bb)
return 1;
visited[pred_bb] = 1;
}
/* Ignore this predecessor if it kills the expression. */
else if (TEST_BIT (ae_kill[pred_bb], expr->bitmap_index))
visited[pred_bb] = 1;
/* Does this predecessor generate this expression? */
else if (TEST_BIT (ae_gen[pred_bb], expr->bitmap_index))
{
/* Is this the occurrence we're looking for?
Note that there's only one generating occurrence per block
so we just need to check the block number. */
if (BLOCK_NUM (occr->insn) == pred_bb)
return 1;
visited[pred_bb] = 1;
}
/* Neither gen nor kill. */
else
{
visited[pred_bb] = 1;
if (expr_reaches_here_p (occr, expr, pred_bb, check_self_loop, visited))
return 1;
}
}
/* All paths have been checked. */
return 0;
}
/* Return the instruction that computes EXPR that reaches INSN's basic block.
If there is more than one such instruction, return NULL.
Called only by handle_avail_expr. */
static rtx
computing_insn (expr, insn)
struct expr *expr;
rtx insn;
{
int bb = BLOCK_NUM (insn);
if (expr->avail_occr->next == NULL)
{
if (BLOCK_NUM (expr->avail_occr->insn) == bb)
{
/* The available expression is actually itself
(i.e. a loop in the flow graph) so do nothing. */
return NULL;
}
/* (FIXME) Case that we found a pattern that was created by
a substitution that took place. */
return expr->avail_occr->insn;
}
else
{
/* Pattern is computed more than once.
Search backwards from this insn to see how many of these
computations actually reach this insn. */
struct occr *occr;
rtx insn_computes_expr = NULL;
int can_reach = 0;
for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
{
if (BLOCK_NUM (occr->insn) == bb)
{
/* The expression is generated in this block.
The only time we care about this is when the expression
is generated later in the block [and thus there's a loop].
We let the normal cse pass handle the other cases. */
if (INSN_CUID (insn) < INSN_CUID (occr->insn))
{
if (expr_reaches_here_p (occr, expr, bb, 1, NULL))
{
can_reach++;
if (can_reach > 1)
return NULL;
insn_computes_expr = occr->insn;
}
}
}
else /* Computation of the pattern outside this block. */
{
if (expr_reaches_here_p (occr, expr, bb, 0, NULL))
{
can_reach++;
if (can_reach > 1)
return NULL;
insn_computes_expr = occr->insn;
}
}
}
if (insn_computes_expr == NULL)
abort ();
return insn_computes_expr;
}
}
/* Return non-zero if the definition in DEF_INSN can reach INSN.
Only called by can_disregard_other_sets. */
static int
def_reaches_here_p (insn, def_insn)
rtx insn, def_insn;
{
rtx reg;
if (TEST_BIT (reaching_defs[BLOCK_NUM (insn)], INSN_CUID (def_insn)))
return 1;
if (BLOCK_NUM (insn) == BLOCK_NUM (def_insn))
{
if (INSN_CUID (def_insn) < INSN_CUID (insn))
{
if (GET_CODE (PATTERN (def_insn)) == PARALLEL)
return 1;
if (GET_CODE (PATTERN (def_insn)) == CLOBBER)
reg = XEXP (PATTERN (def_insn), 0);
else if (GET_CODE (PATTERN (def_insn)) == SET)
reg = SET_DEST (PATTERN (def_insn));
else
abort ();
return ! reg_set_between_p (reg, NEXT_INSN (def_insn), insn);
}
else
return 0;
}
return 0;
}
/* Return non-zero if *ADDR_THIS_REG can only have one value at INSN.
The value returned is the number of definitions that reach INSN.
Returning a value of zero means that [maybe] more than one definition
reaches INSN and the caller can't perform whatever optimization it is
trying. i.e. it is always safe to return zero. */
static int
can_disregard_other_sets (addr_this_reg, insn, for_combine)
struct reg_set **addr_this_reg;
rtx insn;
int for_combine;
{
int number_of_reaching_defs = 0;
struct reg_set *this_reg = *addr_this_reg;
while (this_reg)
{
if (def_reaches_here_p (insn, this_reg->insn))
{
number_of_reaching_defs++;
/* Ignore parallels for now. */
if (GET_CODE (PATTERN (this_reg->insn)) == PARALLEL)
return 0;
if (!for_combine
&& (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER
|| ! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)),
SET_SRC (PATTERN (insn)))))
{
/* A setting of the reg to a different value reaches INSN. */
return 0;
}
if (number_of_reaching_defs > 1)
{
/* If in this setting the value the register is being
set to is equal to the previous value the register
was set to and this setting reaches the insn we are
trying to do the substitution on then we are ok. */
if (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER)
return 0;
if (! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)),
SET_SRC (PATTERN (insn))))
return 0;
}
*addr_this_reg = this_reg;
}
/* prev_this_reg = this_reg; */
this_reg = this_reg->next;
}
return number_of_reaching_defs;
}
/* Expression computed by insn is available and the substitution is legal,
so try to perform the substitution.
The result is non-zero if any changes were made. */
static int
handle_avail_expr (insn, expr)
rtx insn;
struct expr *expr;
{
rtx pat, insn_computes_expr;
rtx to;
struct reg_set *this_reg;
int found_setting, use_src;
int changed = 0;
/* We only handle the case where one computation of the expression
reaches this instruction. */
insn_computes_expr = computing_insn (expr, insn);
if (insn_computes_expr == NULL)
return 0;
found_setting = 0;
use_src = 0;
/* At this point we know only one computation of EXPR outside of this
block reaches this insn. Now try to find a register that the
expression is computed into. */
if (GET_CODE (SET_SRC (PATTERN (insn_computes_expr))) == REG)
{
/* This is the case when the available expression that reaches
here has already been handled as an available expression. */
int regnum_for_replacing = REGNO (SET_SRC (PATTERN (insn_computes_expr)));
/* If the register was created by GCSE we can't use `reg_set_table',
however we know it's set only once. */
if (regnum_for_replacing >= max_gcse_regno
/* If the register the expression is computed into is set only once,
or only one set reaches this insn, we can use it. */
|| (((this_reg = reg_set_table[regnum_for_replacing]),
this_reg->next == NULL)
|| can_disregard_other_sets (&this_reg, insn, 0)))
{
use_src = 1;
found_setting = 1;
}
}
if (!found_setting)
{
int regnum_for_replacing = REGNO (SET_DEST (PATTERN (insn_computes_expr)));
/* This shouldn't happen. */
if (regnum_for_replacing >= max_gcse_regno)
abort ();
this_reg = reg_set_table[regnum_for_replacing];
/* If the register the expression is computed into is set only once,
or only one set reaches this insn, use it. */
if (this_reg->next == NULL
|| can_disregard_other_sets (&this_reg, insn, 0))
found_setting = 1;
}
if (found_setting)
{
pat = PATTERN (insn);
if (use_src)
to = SET_SRC (PATTERN (insn_computes_expr));
else
to = SET_DEST (PATTERN (insn_computes_expr));
changed = validate_change (insn, &SET_SRC (pat), to, 0);
/* We should be able to ignore the return code from validate_change but
to play it safe we check. */
if (changed)
{
gcse_subst_count++;
if (gcse_file != NULL)
{
fprintf (gcse_file, "GCSE: Replacing the source in insn %d with reg %d %s insn %d\n",
INSN_UID (insn), REGNO (to),
use_src ? "from" : "set in",
INSN_UID (insn_computes_expr));
}
}
}
/* The register that the expr is computed into is set more than once. */
else if (1 /*expensive_op(this_pattrn->op) && do_expensive_gcse)*/)
{
/* Insert an insn after insnx that copies the reg set in insnx
into a new pseudo register call this new register REGN.
From insnb until end of basic block or until REGB is set
replace all uses of REGB with REGN. */
rtx new_insn;
to = gen_reg_rtx (GET_MODE (SET_DEST (PATTERN (insn_computes_expr))));
/* Generate the new insn. */
/* ??? If the change fails, we return 0, even though we created
an insn. I think this is ok. */
new_insn
= emit_insn_after (gen_rtx_SET (VOIDmode, to,
SET_DEST (PATTERN (insn_computes_expr))),
insn_computes_expr);
/* Keep block number table up to date. */
set_block_num (new_insn, BLOCK_NUM (insn_computes_expr));
/* Keep register set table up to date. */
record_one_set (REGNO (to), new_insn);
gcse_create_count++;
if (gcse_file != NULL)
{
fprintf (gcse_file, "GCSE: Creating insn %d to copy value of reg %d, computed in insn %d,\n",
INSN_UID (NEXT_INSN (insn_computes_expr)),
REGNO (SET_SRC (PATTERN (NEXT_INSN (insn_computes_expr)))),
INSN_UID (insn_computes_expr));
fprintf (gcse_file, " into newly allocated reg %d\n", REGNO (to));
}
pat = PATTERN (insn);
/* Do register replacement for INSN. */
changed = validate_change (insn, &SET_SRC (pat),
SET_DEST (PATTERN (NEXT_INSN (insn_computes_expr))),
0);
/* We should be able to ignore the return code from validate_change but
to play it safe we check. */
if (changed)
{
gcse_subst_count++;
if (gcse_file != NULL)
{
fprintf (gcse_file, "GCSE: Replacing the source in insn %d with reg %d set in insn %d\n",
INSN_UID (insn),
REGNO (SET_DEST (PATTERN (NEXT_INSN (insn_computes_expr)))),
INSN_UID (insn_computes_expr));
}
}
}
return changed;
}
/* Perform classic GCSE.
This is called by one_classic_gcse_pass after all the dataflow analysis
has been done.
The result is non-zero if a change was made. */
static int
classic_gcse ()
{
int bb, changed;
rtx insn;
/* Note we start at block 1. */
changed = 0;
for (bb = 1; bb < n_basic_blocks; bb++)
{
/* Reset tables used to keep track of what's still valid [since the
start of the block]. */
reset_opr_set_tables ();
for (insn = BLOCK_HEAD (bb);
insn != NULL && insn != NEXT_INSN (BLOCK_END (bb));
insn = NEXT_INSN (insn))
{
/* Is insn of form (set (pseudo-reg) ...)? */
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SET
&& GET_CODE (SET_DEST (PATTERN (insn))) == REG
&& REGNO (SET_DEST (PATTERN (insn))) >= FIRST_PSEUDO_REGISTER)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
struct expr *expr;
if (want_to_gcse_p (src)
/* Is the expression recorded? */
&& ((expr = lookup_expr (src)) != NULL)
/* Is the expression available [at the start of the
block]? */
&& TEST_BIT (ae_in[bb], expr->bitmap_index)
/* Are the operands unchanged since the start of the
block? */
&& oprs_not_set_p (src, insn))
changed |= handle_avail_expr (insn, expr);
}
/* Keep track of everything modified by this insn. */
/* ??? Need to be careful w.r.t. mods done to INSN. */
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
mark_oprs_set (insn);
}
}
return changed;
}
/* Top level routine to perform one classic GCSE pass.
Return non-zero if a change was made. */
static int
one_classic_gcse_pass (pass)
int pass;
{
int changed = 0;
gcse_subst_count = 0;
gcse_create_count = 0;
alloc_expr_hash_table (max_cuid);
alloc_rd_mem (n_basic_blocks, max_cuid);
compute_expr_hash_table ();
if (gcse_file)
dump_hash_table (gcse_file, "Expression", expr_hash_table,
expr_hash_table_size, n_exprs);
if (n_exprs > 0)
{
compute_kill_rd ();
compute_rd ();
alloc_avail_expr_mem (n_basic_blocks, n_exprs);
compute_ae_gen ();
compute_ae_kill ();
compute_available ();
changed = classic_gcse ();
free_avail_expr_mem ();
}
free_rd_mem ();
free_expr_hash_table ();
if (gcse_file)
{
fprintf (gcse_file, "\n");
fprintf (gcse_file, "GCSE of %s, pass %d: %d bytes needed, %d substs, %d insns created\n",
current_function_name, pass,
bytes_used, gcse_subst_count, gcse_create_count);
}
return changed;
}
/* Compute copy/constant propagation working variables. */
/* Local properties of assignments. */
static sbitmap *cprop_pavloc;
static sbitmap *cprop_absaltered;
/* Global properties of assignments (computed from the local properties). */
static sbitmap *cprop_avin;
static sbitmap *cprop_avout;
/* Allocate vars used for copy/const propagation.
N_BLOCKS is the number of basic blocks.
N_SETS is the number of sets. */
static void
alloc_cprop_mem (n_blocks, n_sets)
int n_blocks, n_sets;
{
cprop_pavloc = sbitmap_vector_alloc (n_blocks, n_sets);
cprop_absaltered = sbitmap_vector_alloc (n_blocks, n_sets);
cprop_avin = sbitmap_vector_alloc (n_blocks, n_sets);
cprop_avout = sbitmap_vector_alloc (n_blocks, n_sets);
}
/* Free vars used by copy/const propagation. */
static void
free_cprop_mem ()
{
free (cprop_pavloc);
free (cprop_absaltered);
free (cprop_avin);
free (cprop_avout);
}
/* For each block, compute whether X is transparent.
X is either an expression or an assignment [though we don't care which,
for this context an assignment is treated as an expression].
For each block where an element of X is modified, set (SET_P == 1) or reset
(SET_P == 0) the INDX bit in BMAP. */
static void
compute_transp (x, indx, bmap, set_p)
rtx x;
int indx;
sbitmap *bmap;
int set_p;
{
int bb,i;
enum rtx_code code;
char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case REG:
{
reg_set *r;
int regno = REGNO (x);
if (set_p)
{
if (regno < FIRST_PSEUDO_REGISTER)
{
for (bb = 0; bb < n_basic_blocks; bb++)
if (TEST_BIT (reg_set_in_block[bb], regno))
SET_BIT (bmap[bb], indx);
}
else
{
for (r = reg_set_table[regno]; r != NULL; r = r->next)
{
bb = BLOCK_NUM (r->insn);
SET_BIT (bmap[bb], indx);
}
}
}
else
{
if (regno < FIRST_PSEUDO_REGISTER)
{
for (bb = 0; bb < n_basic_blocks; bb++)
if (TEST_BIT (reg_set_in_block[bb], regno))
RESET_BIT (bmap[bb], indx);
}
else
{
for (r = reg_set_table[regno]; r != NULL; r = r->next)
{
bb = BLOCK_NUM (r->insn);
RESET_BIT (bmap[bb], indx);
}
}
}
return;
}
case MEM:
if (set_p)
{
for (bb = 0; bb < n_basic_blocks; bb++)
if (mem_set_in_block[bb])
SET_BIT (bmap[bb], indx);
}
else
{
for (bb = 0; bb < n_basic_blocks; bb++)
if (mem_set_in_block[bb])
RESET_BIT (bmap[bb], indx);
}
x = XEXP (x, 0);
goto repeat;
case PC:
case CC0: /*FIXME*/
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return;
default:
break;
}
i = GET_RTX_LENGTH (code) - 1;
fmt = GET_RTX_FORMAT (code);
for (; i >= 0; i--)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
goto repeat;
}
compute_transp (tem, indx, bmap, set_p);
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
compute_transp (XVECEXP (x, i, j), indx, bmap, set_p);
}
}
}
/* Compute the available expressions at the start and end of each
basic block for cprop. This particular dataflow equation is
used often enough that we might want to generalize it and make
as a subroutine for other global optimizations that need available
in/out information. */
static void
compute_cprop_avinout ()
{
int bb, changed, passes;
sbitmap_zero (cprop_avin[0]);
sbitmap_vector_ones (cprop_avout, n_basic_blocks);
passes = 0;
changed = 1;
while (changed)
{
changed = 0;
for (bb = 0; bb < n_basic_blocks; bb++)
{
if (bb != 0)
sbitmap_intersect_of_predecessors (cprop_avin[bb],
cprop_avout, bb, s_preds);
changed |= sbitmap_union_of_diff (cprop_avout[bb],
cprop_pavloc[bb],
cprop_avin[bb],
cprop_absaltered[bb]);
}
passes++;
}
if (gcse_file)
fprintf (gcse_file, "cprop avail expr computation: %d passes\n", passes);
}
/* Top level routine to do the dataflow analysis needed by copy/const
propagation. */
static void
compute_cprop_data ()
{
compute_local_properties (cprop_absaltered, cprop_pavloc, NULL, 1);
compute_cprop_avinout ();
}
/* Copy/constant propagation. */
struct reg_use {
rtx reg_rtx;
};
/* Maximum number of register uses in an insn that we handle. */
#define MAX_USES 8
/* Table of uses found in an insn.
Allocated statically to avoid alloc/free complexity and overhead. */
static struct reg_use reg_use_table[MAX_USES];
/* Index into `reg_use_table' while building it. */
static int reg_use_count;
/* Set up a list of register numbers used in INSN.
The found uses are stored in `reg_use_table'.
`reg_use_count' is initialized to zero before entry, and
contains the number of uses in the table upon exit.
??? If a register appears multiple times we will record it multiple
times. This doesn't hurt anything but it will slow things down. */
static void
find_used_regs (x)
rtx x;
{
int i;
enum rtx_code code;
char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
repeat:
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case REG:
if (reg_use_count == MAX_USES)
return;
reg_use_table[reg_use_count].reg_rtx = x;
reg_use_count++;
return;
case MEM:
x = XEXP (x, 0);
goto repeat;
case PC:
case CC0:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case CLOBBER:
case ADDR_VEC:
case ADDR_DIFF_VEC:
case ASM_INPUT: /*FIXME*/
return;
case SET:
if (GET_CODE (SET_DEST (x)) == MEM)
find_used_regs (SET_DEST (x));
x = SET_SRC (x);
goto repeat;
default:
break;
}
/* Recursively scan the operands of this expression. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = XEXP (x, 0);
goto repeat;
}
find_used_regs (XEXP (x, i));
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
find_used_regs (XVECEXP (x, i, j));
}
}
}
/* Try to replace all non-SET_DEST occurrences of FROM in INSN with TO.
Returns non-zero is successful. */
static int
try_replace_reg (from, to, insn)
rtx from, to, insn;
{
/* If this fails we could try to simplify the result of the
replacement and attempt to recognize the simplified insn.
But we need a general simplify_rtx that doesn't have pass
specific state variables. I'm not aware of one at the moment. */
return validate_replace_src (from, to, insn);
}
/* Find a set of REGNO that is available on entry to INSN's block.
Returns NULL if not found. */
static struct expr *
find_avail_set (regno, insn)
int regno;
rtx insn;
{
struct expr *set = lookup_set (regno, NULL_RTX);
while (set)
{
if (TEST_BIT (cprop_avin[BLOCK_NUM (insn)], set->bitmap_index))
break;
set = next_set (regno, set);
}
return set;
}
/* Perform constant and copy propagation on INSN.
The result is non-zero if a change was made. */
static int
cprop_insn (insn, alter_jumps)
rtx insn;
int alter_jumps;
{
struct reg_use *reg_used;
int changed = 0;
/* Only propagate into SETs. Note that a conditional jump is a
SET with pc_rtx as the destination. */
if ((GET_CODE (insn) != INSN
&& GET_CODE (insn) != JUMP_INSN)
|| GET_CODE (PATTERN (insn)) != SET)
return 0;
reg_use_count = 0;
find_used_regs (PATTERN (insn));
reg_used = &reg_use_table[0];
for ( ; reg_use_count > 0; reg_used++, reg_use_count--)
{
rtx pat, src;
struct expr *set;
int regno = REGNO (reg_used->reg_rtx);
/* Ignore registers created by GCSE.
We do this because ... */
if (regno >= max_gcse_regno)
continue;
/* If the register has already been set in this block, there's
nothing we can do. */
if (! oprs_not_set_p (reg_used->reg_rtx, insn))
continue;
/* Find an assignment that sets reg_used and is available
at the start of the block. */
set = find_avail_set (regno, insn);
if (! set)
continue;
pat = set->expr;
/* ??? We might be able to handle PARALLELs. Later. */
if (GET_CODE (pat) != SET)
abort ();
src = SET_SRC (pat);
/* Constant propagation. */
if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE)
{
/* Handle normal insns first. */
if (GET_CODE (insn) == INSN
&& try_replace_reg (reg_used->reg_rtx, src, insn))
{
changed = 1;
const_prop_count++;
if (gcse_file != NULL)
{
fprintf (gcse_file, "CONST-PROP: Replacing reg %d in insn %d with constant ",
regno, INSN_UID (insn));
print_rtl (gcse_file, src);
fprintf (gcse_file, "\n");
}
/* The original insn setting reg_used may or may not now be
deletable. We leave the deletion to flow. */
}
/* Try to propagate a CONST_INT into a conditional jump.
We're pretty specific about what we will handle in this
code, we can extend this as necessary over time.
Right now the insn in question must look like
(set (pc) (if_then_else ...))
Note this does not currently handle machines which use cc0. */
else if (alter_jumps
&& GET_CODE (insn) == JUMP_INSN
&& condjump_p (insn)
&& ! simplejump_p (insn))
{
/* We want a copy of the JUMP_INSN so we can modify it
in-place as needed without effecting the original. */
rtx copy = copy_rtx (insn);
rtx set = PATTERN (copy);
rtx temp;
/* Replace the register with the appropriate constant. */
replace_rtx (SET_SRC (set), reg_used->reg_rtx, src);
temp = simplify_ternary_operation (GET_CODE (SET_SRC (set)),
GET_MODE (SET_SRC (set)),
GET_MODE (XEXP (SET_SRC (set), 0)),
XEXP (SET_SRC (set), 0),
XEXP (SET_SRC (set), 1),
XEXP (SET_SRC (set), 2));
/* If no simplification can be made, then try the next
register. */
if (temp)
SET_SRC (set) = temp;
else
continue;
/* That may have changed the structure of TEMP, so
force it to be rerecognized if it has not turned
into a nop or unconditional jump. */
INSN_CODE (copy) = -1;
if ((SET_DEST (set) == pc_rtx
&& (SET_SRC (set) == pc_rtx
|| GET_CODE (SET_SRC (set)) == LABEL_REF))
|| recog (PATTERN (copy), copy, NULL) >= 0)
{
/* This has either become an unconditional jump
or a nop-jump. We'd like to delete nop jumps
here, but doing so confuses gcse. So we just
make the replacement and let later passes
sort things out. */
PATTERN (insn) = set;
INSN_CODE (insn) = -1;
/* One less use of the label this insn used to jump to
if we turned this into a NOP jump. */
if (SET_SRC (set) == pc_rtx && JUMP_LABEL (insn) != 0)
--LABEL_NUSES (JUMP_LABEL (insn));
/* If this has turned into an unconditional jump,
then put a barrier after it so that the unreachable
code will be deleted. */
if (GET_CODE (SET_SRC (set)) == LABEL_REF)
emit_barrier_after (insn);
run_jump_opt_after_gcse = 1;
changed = 1;
const_prop_count++;
if (gcse_file != NULL)
{
fprintf (gcse_file, "CONST-PROP: Replacing reg %d in insn %d with constant ",
regno, INSN_UID (insn));
print_rtl (gcse_file, src);
fprintf (gcse_file, "\n");
}
}
}
}
else if (GET_CODE (src) == REG
&& REGNO (src) >= FIRST_PSEUDO_REGISTER
&& REGNO (src) != regno)
{
/* We know the set is available.
Now check that SET_SRC is ANTLOC (i.e. none of the source operands
have changed since the start of the block). */
if (oprs_not_set_p (src, insn))
{
if (try_replace_reg (reg_used->reg_rtx, src, insn))
{
changed = 1;
copy_prop_count++;
if (gcse_file != NULL)
{
fprintf (gcse_file, "COPY-PROP: Replacing reg %d in insn %d with reg %d\n",
regno, INSN_UID (insn), REGNO (src));
}
/* The original insn setting reg_used may or may not now be
deletable. We leave the deletion to flow. */
/* FIXME: If it turns out that the insn isn't deletable,
then we may have unnecessarily extended register lifetimes
and made things worse. */
}
}
}
}
return changed;
}
/* Forward propagate copies.
This includes copies and constants.
Return non-zero if a change was made. */
static int
cprop (alter_jumps)
int alter_jumps;
{
int bb, changed;
rtx insn;
/* Note we start at block 1. */
changed = 0;
for (bb = 1; bb < n_basic_blocks; bb++)
{
/* Reset tables used to keep track of what's still valid [since the
start of the block]. */
reset_opr_set_tables ();
for (insn = BLOCK_HEAD (bb);
insn != NULL && insn != NEXT_INSN (BLOCK_END (bb));
insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
changed |= cprop_insn (insn, alter_jumps);
/* Keep track of everything modified by this insn. */
/* ??? Need to be careful w.r.t. mods done to INSN. */
mark_oprs_set (insn);
}
}
}
if (gcse_file != NULL)
fprintf (gcse_file, "\n");
return changed;
}
/* Perform one copy/constant propagation pass.
F is the first insn in the function.
PASS is the pass count. */
static int
one_cprop_pass (pass, alter_jumps)
int pass;
int alter_jumps;
{
int changed = 0;
const_prop_count = 0;
copy_prop_count = 0;
alloc_set_hash_table (max_cuid);
compute_set_hash_table ();
if (gcse_file)
dump_hash_table (gcse_file, "SET", set_hash_table, set_hash_table_size,
n_sets);
if (n_sets > 0)
{
alloc_cprop_mem (n_basic_blocks, n_sets);
compute_cprop_data ();
changed = cprop (alter_jumps);
free_cprop_mem ();
}
free_set_hash_table ();
if (gcse_file)
{
fprintf (gcse_file, "CPROP of %s, pass %d: %d bytes needed, %d const props, %d copy props\n",
current_function_name, pass,
bytes_used, const_prop_count, copy_prop_count);
fprintf (gcse_file, "\n");
}
return changed;
}
/* Compute PRE+LCM working variables. */
/* Local properties of expressions. */
/* Nonzero for expressions that are transparent in the block. */
static sbitmap *transp;
/* Nonzero for expressions that are transparent at the end of the block.
This is only zero for expressions killed by abnormal critical edge
created by a calls. */
static sbitmap *transpout;
/* Nonzero for expressions that are computed (available) in the block. */
static sbitmap *comp;
/* Nonzero for expressions that are locally anticipatable in the block. */
static sbitmap *antloc;
/* Nonzero for expressions where this block is an optimal computation
point. */
static sbitmap *pre_optimal;
/* Nonzero for expressions which are redundant in a particular block. */
static sbitmap *pre_redundant;
static sbitmap *temp_bitmap;
/* Redundant insns. */
static sbitmap pre_redundant_insns;
/* Allocate vars used for PRE analysis. */
static void
alloc_pre_mem (n_blocks, n_exprs)
int n_blocks, n_exprs;
{
transp = sbitmap_vector_alloc (n_blocks, n_exprs);
comp = sbitmap_vector_alloc (n_blocks, n_exprs);
antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
temp_bitmap = sbitmap_vector_alloc (n_blocks, n_exprs);
pre_optimal = sbitmap_vector_alloc (n_blocks, n_exprs);
pre_redundant = sbitmap_vector_alloc (n_blocks, n_exprs);
transpout = sbitmap_vector_alloc (n_blocks, n_exprs);
}
/* Free vars used for PRE analysis. */
static void
free_pre_mem ()
{
free (transp);
free (comp);
free (antloc);
free (pre_optimal);
free (pre_redundant);
free (transpout);
}
/* Top level routine to do the dataflow analysis needed by PRE. */
static void
compute_pre_data ()
{
compute_local_properties (transp, comp, antloc, 0);
compute_transpout ();
pre_lcm (n_basic_blocks, n_exprs, s_preds, s_succs, transp,
antloc, pre_redundant, pre_optimal);
}
/* PRE utilities */
/* Return non-zero if an occurrence of expression EXPR in OCCR_BB would reach
block BB.
VISITED is a pointer to a working buffer for tracking which BB's have
been visited. It is NULL for the top-level call.
CHECK_PRE_COMP controls whether or not we check for a computation of
EXPR in OCCR_BB.
We treat reaching expressions that go through blocks containing the same
reaching expression as "not reaching". E.g. if EXPR is generated in blocks
2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
2 as not reaching. The intent is to improve the probability of finding
only one reaching expression and to reduce register lifetimes by picking
the closest such expression. */
static int
pre_expr_reaches_here_p (occr_bb, expr, bb, check_pre_comp, visited)
int occr_bb;
struct expr *expr;
int bb;
int check_pre_comp;
char *visited;
{
int_list_ptr pred;
if (visited == NULL)
{
visited = (char *) alloca (n_basic_blocks);
bzero (visited, n_basic_blocks);
}
for (pred = s_preds[bb]; pred != NULL; pred = pred->next)
{
int pred_bb = INT_LIST_VAL (pred);
if (pred_bb == ENTRY_BLOCK
/* Has predecessor has already been visited? */
|| visited[pred_bb])
{
/* Nothing to do. */
}
/* Does this predecessor generate this expression? */
else if ((!check_pre_comp && occr_bb == pred_bb)
|| TEST_BIT (comp[pred_bb], expr->bitmap_index))
{
/* Is this the occurrence we're looking for?
Note that there's only one generating occurrence per block
so we just need to check the block number. */
if (occr_bb == pred_bb)
return 1;
visited[pred_bb] = 1;
}
/* Ignore this predecessor if it kills the expression. */
else if (! TEST_BIT (transp[pred_bb], expr->bitmap_index))
visited[pred_bb] = 1;
/* Neither gen nor kill. */
else
{
visited[pred_bb] = 1;
if (pre_expr_reaches_here_p (occr_bb, expr, pred_bb,
check_pre_comp, visited))
return 1;
}
}
/* All paths have been checked. */
return 0;
}
/* Add EXPR to the end of basic block BB.
This is used by both the PRE and code hoisting.
For PRE, we want to verify that the expr is either transparent
or locally anticipatable in the target block. This check makes
no sense for code hoisting. */
static void
insert_insn_end_bb (expr, bb, pre)
struct expr *expr;
int bb;
int pre;
{
rtx insn = BLOCK_END (bb);
rtx new_insn;
rtx reg = expr->reaching_reg;
int regno = REGNO (reg);
rtx pat, copied_expr;
rtx first_new_insn;
start_sequence ();
copied_expr = copy_rtx (expr->expr);
emit_move_insn (reg, copied_expr);
first_new_insn = get_insns ();
pat = gen_sequence ();
end_sequence ();
/* If the last insn is a jump, insert EXPR in front [taking care to
handle cc0, etc. properly]. */
if (GET_CODE (insn) == JUMP_INSN)
{
#ifdef HAVE_cc0
rtx note;
#endif
/* If this is a jump table, then we can't insert stuff here. Since
we know the previous real insn must be the tablejump, we insert
the new instruction just before the tablejump. */
if (GET_CODE (PATTERN (insn)) == ADDR_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
insn = prev_real_insn (insn);
#ifdef HAVE_cc0
/* FIXME: 'twould be nice to call prev_cc0_setter here but it aborts
if cc0 isn't set. */
note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
if (note)
insn = XEXP (note, 0);
else
{
rtx maybe_cc0_setter = prev_nonnote_insn (insn);
if (maybe_cc0_setter
&& GET_RTX_CLASS (GET_CODE (maybe_cc0_setter)) == 'i'
&& sets_cc0_p (PATTERN (maybe_cc0_setter)))
insn = maybe_cc0_setter;
}
#endif
/* FIXME: What if something in cc0/jump uses value set in new insn? */
new_insn = emit_insn_before (pat, insn);
if (BLOCK_HEAD (bb) == insn)
BLOCK_HEAD (bb) = new_insn;
}
/* Likewise if the last insn is a call, as will happen in the presence
of exception handling. */
else if (GET_CODE (insn) == CALL_INSN)
{
HARD_REG_SET parm_regs;
int nparm_regs;
rtx p;
/* Keeping in mind SMALL_REGISTER_CLASSES and parameters in registers,
we search backward and place the instructions before the first
parameter is loaded. Do this for everyone for consistency and a
presumtion that we'll get better code elsewhere as well. */
/* It should always be the case that we can put these instructions
anywhere in the basic block with performing PRE optimizations.
Check this. */
if (pre
&& !TEST_BIT (antloc[bb], expr->bitmap_index)
&& !TEST_BIT (transp[bb], expr->bitmap_index))
abort ();
/* Since different machines initialize their parameter registers
in different orders, assume nothing. Collect the set of all
parameter registers. */
CLEAR_HARD_REG_SET (parm_regs);
nparm_regs = 0;
for (p = CALL_INSN_FUNCTION_USAGE (insn); p ; p = XEXP (p, 1))
if (GET_CODE (XEXP (p, 0)) == USE
&& GET_CODE (XEXP (XEXP (p, 0), 0)) == REG)
{
int regno = REGNO (XEXP (XEXP (p, 0), 0));
if (regno >= FIRST_PSEUDO_REGISTER)
abort ();
SET_HARD_REG_BIT (parm_regs, regno);
nparm_regs++;
}
/* Search backward for the first set of a register in this set. */
while (nparm_regs && BLOCK_HEAD (bb) != insn)
{
insn = PREV_INSN (insn);
p = single_set (insn);
if (p && GET_CODE (SET_DEST (p)) == REG
&& REGNO (SET_DEST (p)) < FIRST_PSEUDO_REGISTER
&& TEST_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p))))
{
CLEAR_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p)));
nparm_regs--;
}
}
/* If we found all the parameter loads, then we want to insert
before the first parameter load.
If we did not find all the parameter loads, then we might have
stopped on the head of the block, which could be a CODE_LABEL.
If we inserted before the CODE_LABEL, then we would be putting
the insn in the wrong basic block. In that case, put the insn
after the CODE_LABEL.
?!? Do we need to account for NOTE_INSN_BASIC_BLOCK here? */
if (GET_CODE (insn) != CODE_LABEL)
{
new_insn = emit_insn_before (pat, insn);
if (BLOCK_HEAD (bb) == insn)
BLOCK_HEAD (bb) = new_insn;
}
else
{
new_insn = emit_insn_after (pat, insn);
}
}
else
{
new_insn = emit_insn_after (pat, insn);
BLOCK_END (bb) = new_insn;
}
/* Keep block number table up to date.
Note, PAT could be a multiple insn sequence, we have to make
sure that each insn in the sequence is handled. */
if (GET_CODE (pat) == SEQUENCE)
{
int i;
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx insn = XVECEXP (pat, 0, i);
set_block_num (insn, bb);
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
add_label_notes (PATTERN (insn), new_insn);
record_set_insn = insn;
note_stores (PATTERN (insn), record_set_info);
}
}
else
{
add_label_notes (SET_SRC (pat), new_insn);
set_block_num (new_insn, bb);
/* Keep register set table up to date. */
record_one_set (regno, new_insn);
}
gcse_create_count++;
if (gcse_file)
{
fprintf (gcse_file, "PRE/HOIST: end of bb %d, insn %d, copying expression %d to reg %d\n",
bb, INSN_UID (new_insn), expr->bitmap_index, regno);
}
}
/* Insert partially redundant expressions at the ends of appropriate basic
blocks making them fully redundant. */
static void
pre_insert (index_map)
struct expr **index_map;
{
int bb, i, set_size;
sbitmap *inserted;
/* Compute INSERT = PRE_OPTIMAL & ~PRE_REDUNDANT.
Where INSERT is nonzero, we add the expression at the end of the basic
block if it reaches any of the deleted expressions. */
set_size = pre_optimal[0]->size;
inserted = sbitmap_vector_alloc (n_basic_blocks, n_exprs);
sbitmap_vector_zero (inserted, n_basic_blocks);
for (bb = 0; bb < n_basic_blocks; bb++)
{
int indx;
/* This computes the number of potential insertions we need. */
sbitmap_not (temp_bitmap[bb], pre_redundant[bb]);
sbitmap_a_and_b (temp_bitmap[bb], temp_bitmap[bb], pre_optimal[bb]);
/* TEMP_BITMAP[bb] now contains a bitmap of the expressions that we need
to insert at the end of this basic block. */
for (i = indx = 0; i < set_size; i++, indx += SBITMAP_ELT_BITS)
{
SBITMAP_ELT_TYPE insert = temp_bitmap[bb]->elms[i];
int j;
for (j = indx; insert && j < n_exprs; j++, insert >>= 1)
{
if ((insert & 1) != 0 && index_map[j]->reaching_reg != NULL_RTX)
{
struct expr *expr = index_map[j];
struct occr *occr;
/* Now look at each deleted occurence of this expression. */
for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
{
if (! occr->deleted_p)
continue;
/* Insert this expression at the end of BB if it would
reach the deleted occurence. */
if (!TEST_BIT (inserted[bb], j)
&& pre_expr_reaches_here_p (bb, expr,
BLOCK_NUM (occr->insn), 0,
NULL))
{
SET_BIT (inserted[bb], j);
insert_insn_end_bb (index_map[j], bb, 1);
}
}
}
}
}
}
}
/* Copy the result of INSN to REG.
INDX is the expression number. */
static void
pre_insert_copy_insn (expr, insn)
struct expr *expr;
rtx insn;
{
rtx reg = expr->reaching_reg;
int regno = REGNO (reg);
int indx = expr->bitmap_index;
rtx set = single_set (insn);
rtx new_insn;
if (!set)
abort ();
new_insn = emit_insn_after (gen_rtx_SET (VOIDmode, reg, SET_DEST (set)),
insn);
/* Keep block number table up to date. */
set_block_num (new_insn, BLOCK_NUM (insn));
/* Keep register set table up to date. */
record_one_set (regno, new_insn);
gcse_create_count++;
if (gcse_file)
{
fprintf (gcse_file, "PRE: bb %d, insn %d, copying expression %d in insn %d to reg %d\n",
BLOCK_NUM (insn), INSN_UID (new_insn), indx, INSN_UID (insn), regno);
}
}
/* Copy available expressions that reach the redundant expression
to `reaching_reg'. */
static void
pre_insert_copies ()
{
int i, bb;
for (bb = 0; bb < n_basic_blocks; bb++)
{
sbitmap_a_and_b (temp_bitmap[bb], pre_optimal[bb], pre_redundant[bb]);
}
/* For each available expression in the table, copy the result to
`reaching_reg' if the expression reaches a deleted one.
??? The current algorithm is rather brute force.
Need to do some profiling. */
for (i = 0; i < expr_hash_table_size; i++)
{
struct expr *expr;
for (expr = expr_hash_table[i]; expr != NULL; expr = expr->next_same_hash)
{
struct occr *occr;
/* If the basic block isn't reachable, PPOUT will be TRUE.
However, we don't want to insert a copy here because the
expression may not really be redundant. So only insert
an insn if the expression was deleted.
This test also avoids further processing if the expression
wasn't deleted anywhere. */
if (expr->reaching_reg == NULL)
continue;
for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
{
struct occr *avail;
if (! occr->deleted_p)
continue;
for (avail = expr->avail_occr; avail != NULL; avail = avail->next)
{
rtx insn = avail->insn;
int bb = BLOCK_NUM (insn);
if (!TEST_BIT (temp_bitmap[bb], expr->bitmap_index))
continue;
/* No need to handle this one if handled already. */
if (avail->copied_p)
continue;
/* Don't handle this one if it's a redundant one. */
if (TEST_BIT (pre_redundant_insns, INSN_CUID (insn)))
continue;
/* Or if the expression doesn't reach the deleted one. */
if (! pre_expr_reaches_here_p (BLOCK_NUM (avail->insn), expr,
BLOCK_NUM (occr->insn),
1, NULL))
continue;
/* Copy the result of avail to reaching_reg. */
pre_insert_copy_insn (expr, insn);
avail->copied_p = 1;
}
}
}
}
}
/* Delete redundant computations.
Deletion is done by changing the insn to copy the `reaching_reg' of
the expression into the result of the SET. It is left to later passes
(cprop, cse2, flow, combine, regmove) to propagate the copy or eliminate it.
Returns non-zero if a change is made. */
static int
pre_delete ()
{
int i, bb, changed;
/* Compute the expressions which are redundant and need to be replaced by
copies from the reaching reg to the target reg. */
for (bb = 0; bb < n_basic_blocks; bb++)
{
sbitmap_not (temp_bitmap[bb], pre_optimal[bb]);
sbitmap_a_and_b (temp_bitmap[bb], temp_bitmap[bb], pre_redundant[bb]);
}
changed = 0;
for (i = 0; i < expr_hash_table_size; i++)
{
struct expr *expr;
for (expr = expr_hash_table[i]; expr != NULL; expr = expr->next_same_hash)
{
struct occr *occr;
int indx = expr->bitmap_index;
/* We only need to search antic_occr since we require
ANTLOC != 0. */
for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
{
rtx insn = occr->insn;
rtx set;
int bb = BLOCK_NUM (insn);
if (TEST_BIT (temp_bitmap[bb], indx))
{
set = single_set (insn);
if (! set)
abort ();
/* Create a pseudo-reg to store the result of reaching
expressions into. Get the mode for the new pseudo
from the mode of the original destination pseudo. */
if (expr->reaching_reg == NULL)
expr->reaching_reg
= gen_reg_rtx (GET_MODE (SET_DEST (set)));
/* In theory this should never fail since we're creating
a reg->reg copy.
However, on the x86 some of the movXX patterns actually
contain clobbers of scratch regs. This may cause the
insn created by validate_change to not match any pattern
and thus cause validate_change to fail. */
if (validate_change (insn, &SET_SRC (set),
expr->reaching_reg, 0))
{
occr->deleted_p = 1;
SET_BIT (pre_redundant_insns, INSN_CUID (insn));
changed = 1;
gcse_subst_count++;
}
if (gcse_file)
{
fprintf (gcse_file,
"PRE: redundant insn %d (expression %d) in bb %d, reaching reg is %d\n",
INSN_UID (insn), indx, bb, REGNO (expr->reaching_reg));
}
}
}
}
}
return changed;
}
/* Perform GCSE optimizations using PRE.
This is called by one_pre_gcse_pass after all the dataflow analysis
has been done.
This is based on the original Morel-Renvoise paper Fred Chow's thesis,
and lazy code motion from Knoop, Ruthing and Steffen as described in
Advanced Compiler Design and Implementation.
??? A new pseudo reg is created to hold the reaching expression.
The nice thing about the classical approach is that it would try to
use an existing reg. If the register can't be adequately optimized
[i.e. we introduce reload problems], one could add a pass here to
propagate the new register through the block.
??? We don't handle single sets in PARALLELs because we're [currently]
not able to copy the rest of the parallel when we insert copies to create
full redundancies from partial redundancies. However, there's no reason
why we can't handle PARALLELs in the cases where there are no partial
redundancies. */
static int
pre_gcse ()
{
int i;
int changed;
struct expr **index_map;
/* Compute a mapping from expression number (`bitmap_index') to
hash table entry. */
index_map = (struct expr **) alloca (n_exprs * sizeof (struct expr *));
bzero ((char *) index_map, n_exprs * sizeof (struct expr *));
for (i = 0; i < expr_hash_table_size; i++)
{
struct expr *expr;
for (expr = expr_hash_table[i]; expr != NULL; expr = expr->next_same_hash)
index_map[expr->bitmap_index] = expr;
}
/* Reset bitmap used to track which insns are redundant. */
pre_redundant_insns = sbitmap_alloc (max_cuid);
sbitmap_zero (pre_redundant_insns);
/* Delete the redundant insns first so that
- we know what register to use for the new insns and for the other
ones with reaching expressions
- we know which insns are redundant when we go to create copies */
changed = pre_delete ();
/* Insert insns in places that make partially redundant expressions
fully redundant. */
pre_insert (index_map);
/* In other places with reaching expressions, copy the expression to the
specially allocated pseudo-reg that reaches the redundant expression. */
pre_insert_copies ();
free (pre_redundant_insns);
return changed;
}
/* Top level routine to perform one PRE GCSE pass.
Return non-zero if a change was made. */
static int
one_pre_gcse_pass (pass)
int pass;
{
int changed = 0;
gcse_subst_count = 0;
gcse_create_count = 0;
alloc_expr_hash_table (max_cuid);
compute_expr_hash_table ();
if (gcse_file)
dump_hash_table (gcse_file, "Expression", expr_hash_table,
expr_hash_table_size, n_exprs);
if (n_exprs > 0)
{
alloc_pre_mem (n_basic_blocks, n_exprs);
compute_pre_data ();
changed |= pre_gcse ();
free_pre_mem ();
}
free_expr_hash_table ();
if (gcse_file)
{
fprintf (gcse_file, "\n");
fprintf (gcse_file, "PRE GCSE of %s, pass %d: %d bytes needed, %d substs, %d insns created\n",
current_function_name, pass,
bytes_used, gcse_subst_count, gcse_create_count);
}
return changed;
}
/* If X contains any LABEL_REF's, add REG_LABEL notes for them to INSN.
We have to add REG_LABEL notes, because the following loop optimization
pass requires them. */
/* ??? This is very similar to the loop.c add_label_notes function. We
could probably share code here. */
/* ??? If there was a jump optimization pass after gcse and before loop,
then we would not need to do this here, because jump would add the
necessary REG_LABEL notes. */
static void
add_label_notes (x, insn)
rtx x;
rtx insn;
{
enum rtx_code code = GET_CODE (x);
int i, j;
char *fmt;
if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x))
{
/* This code used to ignore labels that referred to dispatch tables to
avoid flow generating (slighly) worse code.
We no longer ignore such label references (see LABEL_REF handling in
mark_jump_label for additional information). */
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL, XEXP (x, 0),
REG_NOTES (insn));
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
add_label_notes (XEXP (x, i), insn);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
add_label_notes (XVECEXP (x, i, j), insn);
}
}
/* Compute transparent outgoing information for each block.
An expression is transparent to an edge unless it is killed by
the edge itself. This can only happen with abnormal control flow,
when the edge is traversed through a call. This happens with
non-local labels and exceptions.
This would not be necessary if we split the edge. While this is
normally impossible for abnormal critical edges, with some effort
it should be possible with exception handling, since we still have
control over which handler should be invoked. But due to increased
EH table sizes, this may not be worthwhile. */
static void
compute_transpout ()
{
int bb;
sbitmap_vector_ones (transpout, n_basic_blocks);
for (bb = 0; bb < n_basic_blocks; ++bb)
{
int i;
/* Note that flow inserted a nop a the end of basic blocks that
end in call instructions for reasons other than abnormal
control flow. */
if (GET_CODE (BLOCK_END (bb)) != CALL_INSN)
continue;
for (i = 0; i < expr_hash_table_size; i++)
{
struct expr *expr;
for (expr = expr_hash_table[i]; expr ; expr = expr->next_same_hash)
if (GET_CODE (expr->expr) == MEM)
{
rtx addr = XEXP (expr->expr, 0);
if (GET_CODE (addr) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (addr))
continue;
/* ??? Optimally, we would use interprocedural alias
analysis to determine if this mem is actually killed
by this call. */
RESET_BIT (transpout[bb], expr->bitmap_index);
}
}
}
}
Index: head/contrib/gcc/haifa-sched.c
===================================================================
--- head/contrib/gcc/haifa-sched.c (revision 52750)
+++ head/contrib/gcc/haifa-sched.c (revision 52751)
@@ -1,8828 +1,8828 @@
/* Instruction scheduling pass.
Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by,
and currently maintained by, Jim Wilson (wilson@cygnus.com)
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
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Instruction scheduling pass.
This pass implements list scheduling within basic blocks. It is
run twice: (1) after flow analysis, but before register allocation,
and (2) after register allocation.
The first run performs interblock scheduling, moving insns between
different blocks in the same "region", and the second runs only
basic block scheduling.
Interblock motions performed are useful motions and speculative
motions, including speculative loads. Motions requiring code
duplication are not supported. The identification of motion type
and the check for validity of speculative motions requires
construction and analysis of the function's control flow graph.
The scheduler works as follows:
We compute insn priorities based on data dependencies. Flow
analysis only creates a fraction of the data-dependencies we must
observe: namely, only those dependencies which the combiner can be
expected to use. For this pass, we must therefore create the
remaining dependencies we need to observe: register dependencies,
memory dependencies, dependencies to keep function calls in order,
and the dependence between a conditional branch and the setting of
condition codes are all dealt with here.
The scheduler first traverses the data flow graph, starting with
the last instruction, and proceeding to the first, assigning values
to insn_priority as it goes. This sorts the instructions
topologically by data dependence.
Once priorities have been established, we order the insns using
list scheduling. This works as follows: starting with a list of
all the ready insns, and sorted according to priority number, we
schedule the insn from the end of the list by placing its
predecessors in the list according to their priority order. We
consider this insn scheduled by setting the pointer to the "end" of
the list to point to the previous insn. When an insn has no
predecessors, we either queue it until sufficient time has elapsed
or add it to the ready list. As the instructions are scheduled or
when stalls are introduced, the queue advances and dumps insns into
the ready list. When all insns down to the lowest priority have
been scheduled, the critical path of the basic block has been made
as short as possible. The remaining insns are then scheduled in
remaining slots.
Function unit conflicts are resolved during forward list scheduling
by tracking the time when each insn is committed to the schedule
and from that, the time the function units it uses must be free.
As insns on the ready list are considered for scheduling, those
that would result in a blockage of the already committed insns are
queued until no blockage will result.
The following list shows the order in which we want to break ties
among insns in the ready list:
1. choose insn with the longest path to end of bb, ties
broken by
2. choose insn with least contribution to register pressure,
ties broken by
3. prefer in-block upon interblock motion, ties broken by
4. prefer useful upon speculative motion, ties broken by
5. choose insn with largest control flow probability, ties
broken by
6. choose insn with the least dependences upon the previously
scheduled insn, or finally
7 choose the insn which has the most insns dependent on it.
8. choose insn with lowest UID.
Memory references complicate matters. Only if we can be certain
that memory references are not part of the data dependency graph
(via true, anti, or output dependence), can we move operations past
memory references. To first approximation, reads can be done
independently, while writes introduce dependencies. Better
approximations will yield fewer dependencies.
Before reload, an extended analysis of interblock data dependences
is required for interblock scheduling. This is performed in
compute_block_backward_dependences ().
Dependencies set up by memory references are treated in exactly the
same way as other dependencies, by using LOG_LINKS backward
dependences. LOG_LINKS are translated into INSN_DEPEND forward
dependences for the purpose of forward list scheduling.
Having optimized the critical path, we may have also unduly
extended the lifetimes of some registers. If an operation requires
that constants be loaded into registers, it is certainly desirable
to load those constants as early as necessary, but no earlier.
I.e., it will not do to load up a bunch of registers at the
beginning of a basic block only to use them at the end, if they
could be loaded later, since this may result in excessive register
utilization.
Note that since branches are never in basic blocks, but only end
basic blocks, this pass will not move branches. But that is ok,
since we can use GNU's delayed branch scheduling pass to take care
of this case.
Also note that no further optimizations based on algebraic
identities are performed, so this pass would be a good one to
perform instruction splitting, such as breaking up a multiply
instruction into shifts and adds where that is profitable.
Given the memory aliasing analysis that this pass should perform,
it should be possible to remove redundant stores to memory, and to
load values from registers instead of hitting memory.
Before reload, speculative insns are moved only if a 'proof' exists
that no exception will be caused by this, and if no live registers
exist that inhibit the motion (live registers constraints are not
represented by data dependence edges).
This pass must update information that subsequent passes expect to
be correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths,
reg_n_calls_crossed, and reg_live_length. Also, BLOCK_HEAD,
BLOCK_END.
The information in the line number notes is carefully retained by
this pass. Notes that refer to the starting and ending of
exception regions are also carefully retained by this pass. All
other NOTE insns are grouped in their same relative order at the
beginning of basic blocks and regions that have been scheduled.
The main entry point for this pass is schedule_insns(), called for
each function. The work of the scheduler is organized in three
levels: (1) function level: insns are subject to splitting,
control-flow-graph is constructed, regions are computed (after
reload, each region is of one block), (2) region level: control
flow graph attributes required for interblock scheduling are
computed (dominators, reachability, etc.), data dependences and
priorities are computed, and (3) block level: insns in the block
are actually scheduled. */
#include "config.h"
#include "system.h"
#include "toplev.h"
#include "rtl.h"
#include "basic-block.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "except.h"
#include "toplev.h"
#include "recog.h"
extern char *reg_known_equiv_p;
extern rtx *reg_known_value;
#ifdef INSN_SCHEDULING
/* target_units bitmask has 1 for each unit in the cpu. It should be
possible to compute this variable from the machine description.
But currently it is computed by examinning the insn list. Since
this is only needed for visualization, it seems an acceptable
solution. (For understanding the mapping of bits to units, see
definition of function_units[] in "insn-attrtab.c") */
static int target_units = 0;
/* issue_rate is the number of insns that can be scheduled in the same
machine cycle. It can be defined in the config/mach/mach.h file,
otherwise we set it to 1. */
static int issue_rate;
#ifndef ISSUE_RATE
#define ISSUE_RATE 1
#endif
/* sched-verbose controls the amount of debugging output the
scheduler prints. It is controlled by -fsched-verbose-N:
N>0 and no -DSR : the output is directed to stderr.
N>=10 will direct the printouts to stderr (regardless of -dSR).
N=1: same as -dSR.
N=2: bb's probabilities, detailed ready list info, unit/insn info.
N=3: rtl at abort point, control-flow, regions info.
N=5: dependences info. */
#define MAX_RGN_BLOCKS 10
#define MAX_RGN_INSNS 100
static int sched_verbose_param = 0;
static int sched_verbose = 0;
/* nr_inter/spec counts interblock/speculative motion for the function */
static int nr_inter, nr_spec;
/* debugging file. all printouts are sent to dump, which is always set,
either to stderr, or to the dump listing file (-dRS). */
static FILE *dump = 0;
/* fix_sched_param() is called from toplev.c upon detection
of the -fsched-***-N options. */
void
fix_sched_param (param, val)
char *param, *val;
{
if (!strcmp (param, "verbose"))
sched_verbose_param = atoi (val);
else
warning ("fix_sched_param: unknown param: %s", param);
}
/* Arrays set up by scheduling for the same respective purposes as
similar-named arrays set up by flow analysis. We work with these
arrays during the scheduling pass so we can compare values against
unscheduled code.
Values of these arrays are copied at the end of this pass into the
arrays set up by flow analysis. */
static int *sched_reg_n_calls_crossed;
static int *sched_reg_live_length;
static int *sched_reg_basic_block;
/* We need to know the current block number during the post scheduling
update of live register information so that we can also update
REG_BASIC_BLOCK if a register changes blocks. */
static int current_block_num;
/* Element N is the next insn that sets (hard or pseudo) register
N within the current basic block; or zero, if there is no
such insn. Needed for new registers which may be introduced
by splitting insns. */
static rtx *reg_last_uses;
static rtx *reg_last_sets;
static rtx *reg_last_clobbers;
static regset reg_pending_sets;
static regset reg_pending_clobbers;
static int reg_pending_sets_all;
/* Vector indexed by INSN_UID giving the original ordering of the insns. */
static int *insn_luid;
#define INSN_LUID(INSN) (insn_luid[INSN_UID (INSN)])
/* Vector indexed by INSN_UID giving each instruction a priority. */
static int *insn_priority;
#define INSN_PRIORITY(INSN) (insn_priority[INSN_UID (INSN)])
static short *insn_costs;
#define INSN_COST(INSN) insn_costs[INSN_UID (INSN)]
/* Vector indexed by INSN_UID giving an encoding of the function units
used. */
static short *insn_units;
#define INSN_UNIT(INSN) insn_units[INSN_UID (INSN)]
/* Vector indexed by INSN_UID giving each instruction a register-weight.
This weight is an estimation of the insn contribution to registers pressure. */
static int *insn_reg_weight;
#define INSN_REG_WEIGHT(INSN) (insn_reg_weight[INSN_UID (INSN)])
/* Vector indexed by INSN_UID giving list of insns which
depend upon INSN. Unlike LOG_LINKS, it represents forward dependences. */
static rtx *insn_depend;
#define INSN_DEPEND(INSN) insn_depend[INSN_UID (INSN)]
/* Vector indexed by INSN_UID. Initialized to the number of incoming
edges in forward dependence graph (= number of LOG_LINKS). As
scheduling procedes, dependence counts are decreased. An
instruction moves to the ready list when its counter is zero. */
static int *insn_dep_count;
#define INSN_DEP_COUNT(INSN) (insn_dep_count[INSN_UID (INSN)])
/* Vector indexed by INSN_UID giving an encoding of the blockage range
function. The unit and the range are encoded. */
static unsigned int *insn_blockage;
#define INSN_BLOCKAGE(INSN) insn_blockage[INSN_UID (INSN)]
#define UNIT_BITS 5
#define BLOCKAGE_MASK ((1 << BLOCKAGE_BITS) - 1)
#define ENCODE_BLOCKAGE(U, R) \
(((U) << BLOCKAGE_BITS \
| MIN_BLOCKAGE_COST (R)) << BLOCKAGE_BITS \
| MAX_BLOCKAGE_COST (R))
#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS))
#define BLOCKAGE_RANGE(B) \
(((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \
| ((B) & BLOCKAGE_MASK))
/* Encodings of the `<name>_unit_blockage_range' function. */
#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2))
#define MAX_BLOCKAGE_COST(R) ((R) & ((1 << (HOST_BITS_PER_INT / 2)) - 1))
#define DONE_PRIORITY -1
#define MAX_PRIORITY 0x7fffffff
#define TAIL_PRIORITY 0x7ffffffe
#define LAUNCH_PRIORITY 0x7f000001
#define DONE_PRIORITY_P(INSN) (INSN_PRIORITY (INSN) < 0)
#define LOW_PRIORITY_P(INSN) ((INSN_PRIORITY (INSN) & 0x7f000000) == 0)
/* Vector indexed by INSN_UID giving number of insns referring to this insn. */
static int *insn_ref_count;
#define INSN_REF_COUNT(INSN) (insn_ref_count[INSN_UID (INSN)])
/* Vector indexed by INSN_UID giving line-number note in effect for each
insn. For line-number notes, this indicates whether the note may be
reused. */
static rtx *line_note;
#define LINE_NOTE(INSN) (line_note[INSN_UID (INSN)])
/* Vector indexed by basic block number giving the starting line-number
for each basic block. */
static rtx *line_note_head;
/* List of important notes we must keep around. This is a pointer to the
last element in the list. */
static rtx note_list;
/* Regsets telling whether a given register is live or dead before the last
scheduled insn. Must scan the instructions once before scheduling to
determine what registers are live or dead at the end of the block. */
static regset bb_live_regs;
/* Regset telling whether a given register is live after the insn currently
being scheduled. Before processing an insn, this is equal to bb_live_regs
above. This is used so that we can find registers that are newly born/dead
after processing an insn. */
static regset old_live_regs;
/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns
during the initial scan and reused later. If there are not exactly as
many REG_DEAD notes in the post scheduled code as there were in the
prescheduled code then we trigger an abort because this indicates a bug. */
static rtx dead_notes;
/* Queues, etc. */
/* An instruction is ready to be scheduled when all insns preceding it
have already been scheduled. It is important to ensure that all
insns which use its result will not be executed until its result
has been computed. An insn is maintained in one of four structures:
(P) the "Pending" set of insns which cannot be scheduled until
their dependencies have been satisfied.
(Q) the "Queued" set of insns that can be scheduled when sufficient
time has passed.
(R) the "Ready" list of unscheduled, uncommitted insns.
(S) the "Scheduled" list of insns.
Initially, all insns are either "Pending" or "Ready" depending on
whether their dependencies are satisfied.
Insns move from the "Ready" list to the "Scheduled" list as they
are committed to the schedule. As this occurs, the insns in the
"Pending" list have their dependencies satisfied and move to either
the "Ready" list or the "Queued" set depending on whether
sufficient time has passed to make them ready. As time passes,
insns move from the "Queued" set to the "Ready" list. Insns may
move from the "Ready" list to the "Queued" set if they are blocked
due to a function unit conflict.
The "Pending" list (P) are the insns in the INSN_DEPEND of the unscheduled
insns, i.e., those that are ready, queued, and pending.
The "Queued" set (Q) is implemented by the variable `insn_queue'.
The "Ready" list (R) is implemented by the variables `ready' and
`n_ready'.
The "Scheduled" list (S) is the new insn chain built by this pass.
The transition (R->S) is implemented in the scheduling loop in
`schedule_block' when the best insn to schedule is chosen.
The transition (R->Q) is implemented in `queue_insn' when an
insn is found to have a function unit conflict with the already
committed insns.
The transitions (P->R and P->Q) are implemented in `schedule_insn' as
insns move from the ready list to the scheduled list.
The transition (Q->R) is implemented in 'queue_to_insn' as time
passes or stalls are introduced. */
/* Implement a circular buffer to delay instructions until sufficient
time has passed. INSN_QUEUE_SIZE is a power of two larger than
MAX_BLOCKAGE and MAX_READY_COST computed by genattr.c. This is the
longest time an isnsn may be queued. */
static rtx insn_queue[INSN_QUEUE_SIZE];
static int q_ptr = 0;
static int q_size = 0;
#define NEXT_Q(X) (((X)+1) & (INSN_QUEUE_SIZE-1))
#define NEXT_Q_AFTER(X, C) (((X)+C) & (INSN_QUEUE_SIZE-1))
/* Vector indexed by INSN_UID giving the minimum clock tick at which
the insn becomes ready. This is used to note timing constraints for
insns in the pending list. */
static int *insn_tick;
#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)])
/* Data structure for keeping track of register information
during that register's life. */
struct sometimes
{
int regno;
int live_length;
int calls_crossed;
};
/* Forward declarations. */
static void add_dependence PROTO ((rtx, rtx, enum reg_note));
static void remove_dependence PROTO ((rtx, rtx));
static rtx find_insn_list PROTO ((rtx, rtx));
static int insn_unit PROTO ((rtx));
static unsigned int blockage_range PROTO ((int, rtx));
static void clear_units PROTO ((void));
static int actual_hazard_this_instance PROTO ((int, int, rtx, int, int));
static void schedule_unit PROTO ((int, rtx, int));
static int actual_hazard PROTO ((int, rtx, int, int));
static int potential_hazard PROTO ((int, rtx, int));
static int insn_cost PROTO ((rtx, rtx, rtx));
static int priority PROTO ((rtx));
static void free_pending_lists PROTO ((void));
static void add_insn_mem_dependence PROTO ((rtx *, rtx *, rtx, rtx));
static void flush_pending_lists PROTO ((rtx, int));
static void sched_analyze_1 PROTO ((rtx, rtx));
static void sched_analyze_2 PROTO ((rtx, rtx));
static void sched_analyze_insn PROTO ((rtx, rtx, rtx));
static void sched_analyze PROTO ((rtx, rtx));
static void sched_note_set PROTO ((rtx, int));
static int rank_for_schedule PROTO ((const GENERIC_PTR, const GENERIC_PTR));
static void swap_sort PROTO ((rtx *, int));
static void queue_insn PROTO ((rtx, int));
static int schedule_insn PROTO ((rtx, rtx *, int, int));
static void create_reg_dead_note PROTO ((rtx, rtx));
static void attach_deaths PROTO ((rtx, rtx, int));
static void attach_deaths_insn PROTO ((rtx));
static int new_sometimes_live PROTO ((struct sometimes *, int, int));
static void finish_sometimes_live PROTO ((struct sometimes *, int));
static int schedule_block PROTO ((int, int));
static void split_hard_reg_notes PROTO ((rtx, rtx, rtx));
static void new_insn_dead_notes PROTO ((rtx, rtx, rtx, rtx));
static void update_n_sets PROTO ((rtx, int));
static char *safe_concat PROTO ((char *, char *, char *));
static int insn_issue_delay PROTO ((rtx));
static int birthing_insn_p PROTO ((rtx));
static void adjust_priority PROTO ((rtx));
/* Mapping of insns to their original block prior to scheduling. */
static int *insn_orig_block;
#define INSN_BLOCK(insn) (insn_orig_block[INSN_UID (insn)])
/* Some insns (e.g. call) are not allowed to move across blocks. */
static char *cant_move;
#define CANT_MOVE(insn) (cant_move[INSN_UID (insn)])
/* Control flow graph edges are kept in circular lists. */
typedef struct
{
int from_block;
int to_block;
int next_in;
int next_out;
}
haifa_edge;
static haifa_edge *edge_table;
#define NEXT_IN(edge) (edge_table[edge].next_in)
#define NEXT_OUT(edge) (edge_table[edge].next_out)
#define FROM_BLOCK(edge) (edge_table[edge].from_block)
#define TO_BLOCK(edge) (edge_table[edge].to_block)
/* Number of edges in the control flow graph. (in fact larger than
that by 1, since edge 0 is unused.) */
static int nr_edges;
/* Circular list of incoming/outgoing edges of a block */
static int *in_edges;
static int *out_edges;
#define IN_EDGES(block) (in_edges[block])
#define OUT_EDGES(block) (out_edges[block])
/* List of labels which cannot be deleted, needed for control
flow graph construction. */
extern rtx forced_labels;
static int is_cfg_nonregular PROTO ((void));
static int build_control_flow PROTO ((int_list_ptr *, int_list_ptr *,
int *, int *));
static void new_edge PROTO ((int, int));
/* A region is the main entity for interblock scheduling: insns
are allowed to move between blocks in the same region, along
control flow graph edges, in the 'up' direction. */
typedef struct
{
int rgn_nr_blocks; /* number of blocks in region */
int rgn_blocks; /* blocks in the region (actually index in rgn_bb_table) */
}
region;
/* Number of regions in the procedure */
static int nr_regions;
/* Table of region descriptions */
static region *rgn_table;
/* Array of lists of regions' blocks */
static int *rgn_bb_table;
/* Topological order of blocks in the region (if b2 is reachable from
b1, block_to_bb[b2] > block_to_bb[b1]).
Note: A basic block is always referred to by either block or b,
while its topological order name (in the region) is refered to by
bb.
*/
static int *block_to_bb;
/* The number of the region containing a block. */
static int *containing_rgn;
#define RGN_NR_BLOCKS(rgn) (rgn_table[rgn].rgn_nr_blocks)
#define RGN_BLOCKS(rgn) (rgn_table[rgn].rgn_blocks)
#define BLOCK_TO_BB(block) (block_to_bb[block])
#define CONTAINING_RGN(block) (containing_rgn[block])
void debug_regions PROTO ((void));
static void find_single_block_region PROTO ((void));
static void find_rgns PROTO ((int_list_ptr *, int_list_ptr *,
int *, int *, sbitmap *));
static int too_large PROTO ((int, int *, int *));
extern void debug_live PROTO ((int, int));
/* Blocks of the current region being scheduled. */
static int current_nr_blocks;
static int current_blocks;
/* The mapping from bb to block */
#define BB_TO_BLOCK(bb) (rgn_bb_table[current_blocks + (bb)])
/* Bit vectors and bitset operations are needed for computations on
the control flow graph. */
typedef unsigned HOST_WIDE_INT *bitset;
typedef struct
{
int *first_member; /* pointer to the list start in bitlst_table. */
int nr_members; /* the number of members of the bit list. */
}
bitlst;
static int bitlst_table_last;
static int bitlst_table_size;
static int *bitlst_table;
static char bitset_member PROTO ((bitset, int, int));
static void extract_bitlst PROTO ((bitset, int, bitlst *));
/* target info declarations.
The block currently being scheduled is referred to as the "target" block,
while other blocks in the region from which insns can be moved to the
target are called "source" blocks. The candidate structure holds info
about such sources: are they valid? Speculative? Etc. */
typedef bitlst bblst;
typedef struct
{
char is_valid;
char is_speculative;
int src_prob;
bblst split_bbs;
bblst update_bbs;
}
candidate;
static candidate *candidate_table;
/* A speculative motion requires checking live information on the path
from 'source' to 'target'. The split blocks are those to be checked.
After a speculative motion, live information should be modified in
the 'update' blocks.
Lists of split and update blocks for each candidate of the current
target are in array bblst_table */
static int *bblst_table, bblst_size, bblst_last;
#define IS_VALID(src) ( candidate_table[src].is_valid )
#define IS_SPECULATIVE(src) ( candidate_table[src].is_speculative )
#define SRC_PROB(src) ( candidate_table[src].src_prob )
/* The bb being currently scheduled. */
static int target_bb;
/* List of edges. */
typedef bitlst edgelst;
/* target info functions */
static void split_edges PROTO ((int, int, edgelst *));
static void compute_trg_info PROTO ((int));
void debug_candidate PROTO ((int));
void debug_candidates PROTO ((int));
/* Bit-set of bbs, where bit 'i' stands for bb 'i'. */
typedef bitset bbset;
/* Number of words of the bbset. */
static int bbset_size;
/* Dominators array: dom[i] contains the bbset of dominators of
bb i in the region. */
static bbset *dom;
/* bb 0 is the only region entry */
#define IS_RGN_ENTRY(bb) (!bb)
/* Is bb_src dominated by bb_trg. */
#define IS_DOMINATED(bb_src, bb_trg) \
( bitset_member (dom[bb_src], bb_trg, bbset_size) )
/* Probability: Prob[i] is a float in [0, 1] which is the probability
of bb i relative to the region entry. */
static float *prob;
/* The probability of bb_src, relative to bb_trg. Note, that while the
'prob[bb]' is a float in [0, 1], this macro returns an integer
in [0, 100]. */
#define GET_SRC_PROB(bb_src, bb_trg) ((int) (100.0 * (prob[bb_src] / \
prob[bb_trg])))
/* Bit-set of edges, where bit i stands for edge i. */
typedef bitset edgeset;
/* Number of edges in the region. */
static int rgn_nr_edges;
/* Array of size rgn_nr_edges. */
static int *rgn_edges;
/* Number of words in an edgeset. */
static int edgeset_size;
/* Mapping from each edge in the graph to its number in the rgn. */
static int *edge_to_bit;
#define EDGE_TO_BIT(edge) (edge_to_bit[edge])
/* The split edges of a source bb is different for each target
bb. In order to compute this efficiently, the 'potential-split edges'
are computed for each bb prior to scheduling a region. This is actually
the split edges of each bb relative to the region entry.
pot_split[bb] is the set of potential split edges of bb. */
static edgeset *pot_split;
/* For every bb, a set of its ancestor edges. */
static edgeset *ancestor_edges;
static void compute_dom_prob_ps PROTO ((int));
#define ABS_VALUE(x) (((x)<0)?(-(x)):(x))
#define INSN_PROBABILITY(INSN) (SRC_PROB (BLOCK_TO_BB (INSN_BLOCK (INSN))))
#define IS_SPECULATIVE_INSN(INSN) (IS_SPECULATIVE (BLOCK_TO_BB (INSN_BLOCK (INSN))))
#define INSN_BB(INSN) (BLOCK_TO_BB (INSN_BLOCK (INSN)))
/* parameters affecting the decision of rank_for_schedule() */
#define MIN_DIFF_PRIORITY 2
#define MIN_PROBABILITY 40
#define MIN_PROB_DIFF 10
/* speculative scheduling functions */
static int check_live_1 PROTO ((int, rtx));
static void update_live_1 PROTO ((int, rtx));
static int check_live PROTO ((rtx, int));
static void update_live PROTO ((rtx, int));
static void set_spec_fed PROTO ((rtx));
static int is_pfree PROTO ((rtx, int, int));
static int find_conditional_protection PROTO ((rtx, int));
static int is_conditionally_protected PROTO ((rtx, int, int));
static int may_trap_exp PROTO ((rtx, int));
static int haifa_classify_insn PROTO ((rtx));
static int is_prisky PROTO ((rtx, int, int));
static int is_exception_free PROTO ((rtx, int, int));
static char find_insn_mem_list PROTO ((rtx, rtx, rtx, rtx));
static void compute_block_forward_dependences PROTO ((int));
static void init_rgn_data_dependences PROTO ((int));
static void add_branch_dependences PROTO ((rtx, rtx));
static void compute_block_backward_dependences PROTO ((int));
void debug_dependencies PROTO ((void));
/* Notes handling mechanism:
=========================
Generally, NOTES are saved before scheduling and restored after scheduling.
The scheduler distinguishes between three types of notes:
(1) LINE_NUMBER notes, generated and used for debugging. Here,
before scheduling a region, a pointer to the LINE_NUMBER note is
added to the insn following it (in save_line_notes()), and the note
is removed (in rm_line_notes() and unlink_line_notes()). After
scheduling the region, this pointer is used for regeneration of
the LINE_NUMBER note (in restore_line_notes()).
(2) LOOP_BEGIN, LOOP_END, SETJMP, EHREGION_BEG, EHREGION_END notes:
Before scheduling a region, a pointer to the note is added to the insn
that follows or precedes it. (This happens as part of the data dependence
computation). After scheduling an insn, the pointer contained in it is
used for regenerating the corresponding note (in reemit_notes).
(3) All other notes (e.g. INSN_DELETED): Before scheduling a block,
these notes are put in a list (in rm_other_notes() and
unlink_other_notes ()). After scheduling the block, these notes are
inserted at the beginning of the block (in schedule_block()). */
static rtx unlink_other_notes PROTO ((rtx, rtx));
static rtx unlink_line_notes PROTO ((rtx, rtx));
static void rm_line_notes PROTO ((int));
static void save_line_notes PROTO ((int));
static void restore_line_notes PROTO ((int));
static void rm_redundant_line_notes PROTO ((void));
static void rm_other_notes PROTO ((rtx, rtx));
static rtx reemit_notes PROTO ((rtx, rtx));
static void get_block_head_tail PROTO ((int, rtx *, rtx *));
static void find_pre_sched_live PROTO ((int));
static void find_post_sched_live PROTO ((int));
static void update_reg_usage PROTO ((void));
static int queue_to_ready PROTO ((rtx [], int));
static void debug_ready_list PROTO ((rtx[], int));
static void init_target_units PROTO ((void));
static void insn_print_units PROTO ((rtx));
static int get_visual_tbl_length PROTO ((void));
static void init_block_visualization PROTO ((void));
static void print_block_visualization PROTO ((int, char *));
static void visualize_scheduled_insns PROTO ((int, int));
static void visualize_no_unit PROTO ((rtx));
static void visualize_stall_cycles PROTO ((int, int));
static void print_exp PROTO ((char *, rtx, int));
static void print_value PROTO ((char *, rtx, int));
static void print_pattern PROTO ((char *, rtx, int));
static void print_insn PROTO ((char *, rtx, int));
void debug_reg_vector PROTO ((regset));
static rtx move_insn1 PROTO ((rtx, rtx));
static rtx move_insn PROTO ((rtx, rtx));
static rtx group_leader PROTO ((rtx));
static int set_priorities PROTO ((int));
static void init_rtx_vector PROTO ((rtx **, rtx *, int, int));
static void schedule_region PROTO ((int));
#endif /* INSN_SCHEDULING */
#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X)))
/* Helper functions for instruction scheduling. */
/* An INSN_LIST containing all INSN_LISTs allocated but currently unused. */
static rtx unused_insn_list;
/* An EXPR_LIST containing all EXPR_LISTs allocated but currently unused. */
static rtx unused_expr_list;
static void free_list PROTO ((rtx *, rtx *));
static rtx alloc_INSN_LIST PROTO ((rtx, rtx));
static rtx alloc_EXPR_LIST PROTO ((int, rtx, rtx));
static void
free_list (listp, unused_listp)
rtx *listp, *unused_listp;
{
register rtx link, prev_link;
if (*listp == 0)
return;
prev_link = *listp;
link = XEXP (prev_link, 1);
while (link)
{
prev_link = link;
link = XEXP (link, 1);
}
XEXP (prev_link, 1) = *unused_listp;
*unused_listp = *listp;
*listp = 0;
}
static rtx
alloc_INSN_LIST (val, next)
rtx val, next;
{
rtx r;
if (unused_insn_list)
{
r = unused_insn_list;
unused_insn_list = XEXP (r, 1);
XEXP (r, 0) = val;
XEXP (r, 1) = next;
PUT_REG_NOTE_KIND (r, VOIDmode);
}
else
r = gen_rtx_INSN_LIST (VOIDmode, val, next);
return r;
}
static rtx
alloc_EXPR_LIST (kind, val, next)
int kind;
rtx val, next;
{
rtx r;
if (unused_expr_list)
{
r = unused_expr_list;
unused_expr_list = XEXP (r, 1);
XEXP (r, 0) = val;
XEXP (r, 1) = next;
PUT_REG_NOTE_KIND (r, kind);
}
else
r = gen_rtx_EXPR_LIST (kind, val, next);
return r;
}
/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the
LOG_LINKS of INSN, if not already there. DEP_TYPE indicates the type
of dependence that this link represents. */
static void
add_dependence (insn, elem, dep_type)
rtx insn;
rtx elem;
enum reg_note dep_type;
{
rtx link, next;
/* Don't depend an insn on itself. */
if (insn == elem)
return;
/* We can get a dependency on deleted insns due to optimizations in
the register allocation and reloading or due to splitting. Any
such dependency is useless and can be ignored. */
if (GET_CODE (elem) == NOTE)
return;
/* If elem is part of a sequence that must be scheduled together, then
make the dependence point to the last insn of the sequence.
When HAVE_cc0, it is possible for NOTEs to exist between users and
setters of the condition codes, so we must skip past notes here.
Otherwise, NOTEs are impossible here. */
next = NEXT_INSN (elem);
#ifdef HAVE_cc0
while (next && GET_CODE (next) == NOTE)
next = NEXT_INSN (next);
#endif
if (next && SCHED_GROUP_P (next)
&& GET_CODE (next) != CODE_LABEL)
{
/* Notes will never intervene here though, so don't bother checking
for them. */
/* We must reject CODE_LABELs, so that we don't get confused by one
that has LABEL_PRESERVE_P set, which is represented by the same
bit in the rtl as SCHED_GROUP_P. A CODE_LABEL can never be
SCHED_GROUP_P. */
while (NEXT_INSN (next) && SCHED_GROUP_P (NEXT_INSN (next))
&& GET_CODE (NEXT_INSN (next)) != CODE_LABEL)
next = NEXT_INSN (next);
/* Again, don't depend an insn on itself. */
if (insn == next)
return;
/* Make the dependence to NEXT, the last insn of the group, instead
of the original ELEM. */
elem = next;
}
#ifdef INSN_SCHEDULING
/* (This code is guarded by INSN_SCHEDULING, otherwise INSN_BB is undefined.)
No need for interblock dependences with calls, since
calls are not moved between blocks. Note: the edge where
elem is a CALL is still required. */
if (GET_CODE (insn) == CALL_INSN
&& (INSN_BB (elem) != INSN_BB (insn)))
return;
#endif
/* Check that we don't already have this dependence. */
for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
if (XEXP (link, 0) == elem)
{
/* If this is a more restrictive type of dependence than the existing
one, then change the existing dependence to this type. */
if ((int) dep_type < (int) REG_NOTE_KIND (link))
PUT_REG_NOTE_KIND (link, dep_type);
return;
}
/* Might want to check one level of transitivity to save conses. */
link = alloc_INSN_LIST (elem, LOG_LINKS (insn));
LOG_LINKS (insn) = link;
/* Insn dependency, not data dependency. */
PUT_REG_NOTE_KIND (link, dep_type);
}
/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS
of INSN. Abort if not found. */
static void
remove_dependence (insn, elem)
rtx insn;
rtx elem;
{
rtx prev, link, next;
int found = 0;
for (prev = 0, link = LOG_LINKS (insn); link; link = next)
{
next = XEXP (link, 1);
if (XEXP (link, 0) == elem)
{
if (prev)
XEXP (prev, 1) = next;
else
LOG_LINKS (insn) = next;
XEXP (link, 1) = unused_insn_list;
unused_insn_list = link;
found = 1;
}
else
prev = link;
}
if (!found)
abort ();
return;
}
#ifndef INSN_SCHEDULING
void
schedule_insns (dump_file)
FILE *dump_file;
{
}
#else
#ifndef __GNUC__
#define __inline
#endif
#ifndef HAIFA_INLINE
#define HAIFA_INLINE __inline
#endif
/* Computation of memory dependencies. */
/* The *_insns and *_mems are paired lists. Each pending memory operation
will have a pointer to the MEM rtx on one list and a pointer to the
containing insn on the other list in the same place in the list. */
/* We can't use add_dependence like the old code did, because a single insn
may have multiple memory accesses, and hence needs to be on the list
once for each memory access. Add_dependence won't let you add an insn
to a list more than once. */
/* An INSN_LIST containing all insns with pending read operations. */
static rtx pending_read_insns;
/* An EXPR_LIST containing all MEM rtx's which are pending reads. */
static rtx pending_read_mems;
/* An INSN_LIST containing all insns with pending write operations. */
static rtx pending_write_insns;
/* An EXPR_LIST containing all MEM rtx's which are pending writes. */
static rtx pending_write_mems;
/* Indicates the combined length of the two pending lists. We must prevent
these lists from ever growing too large since the number of dependencies
produced is at least O(N*N), and execution time is at least O(4*N*N), as
a function of the length of these pending lists. */
static int pending_lists_length;
/* The last insn upon which all memory references must depend.
This is an insn which flushed the pending lists, creating a dependency
between it and all previously pending memory references. This creates
a barrier (or a checkpoint) which no memory reference is allowed to cross.
This includes all non constant CALL_INSNs. When we do interprocedural
alias analysis, this restriction can be relaxed.
This may also be an INSN that writes memory if the pending lists grow
too large. */
static rtx last_pending_memory_flush;
/* The last function call we have seen. All hard regs, and, of course,
the last function call, must depend on this. */
static rtx last_function_call;
/* The LOG_LINKS field of this is a list of insns which use a pseudo register
that does not already cross a call. We create dependencies between each
of those insn and the next call insn, to ensure that they won't cross a call
after scheduling is done. */
static rtx sched_before_next_call;
/* Pointer to the last instruction scheduled. Used by rank_for_schedule,
so that insns independent of the last scheduled insn will be preferred
over dependent instructions. */
static rtx last_scheduled_insn;
/* Data structures for the computation of data dependences in a regions. We
keep one copy of each of the declared above variables for each bb in the
region. Before analyzing the data dependences for a bb, its variables
are initialized as a function of the variables of its predecessors. When
the analysis for a bb completes, we save the contents of each variable X
to a corresponding bb_X[bb] variable. For example, pending_read_insns is
copied to bb_pending_read_insns[bb]. Another change is that few
variables are now a list of insns rather than a single insn:
last_pending_memory_flash, last_function_call, reg_last_sets. The
manipulation of these variables was changed appropriately. */
static rtx **bb_reg_last_uses;
static rtx **bb_reg_last_sets;
static rtx **bb_reg_last_clobbers;
static rtx *bb_pending_read_insns;
static rtx *bb_pending_read_mems;
static rtx *bb_pending_write_insns;
static rtx *bb_pending_write_mems;
static int *bb_pending_lists_length;
static rtx *bb_last_pending_memory_flush;
static rtx *bb_last_function_call;
static rtx *bb_sched_before_next_call;
/* functions for construction of the control flow graph. */
/* Return 1 if control flow graph should not be constructed, 0 otherwise.
We decide not to build the control flow graph if there is possibly more
than one entry to the function, if computed branches exist, of if we
have nonlocal gotos. */
static int
is_cfg_nonregular ()
{
int b;
rtx insn;
RTX_CODE code;
/* If we have a label that could be the target of a nonlocal goto, then
the cfg is not well structured. */
if (nonlocal_goto_handler_labels)
return 1;
/* If we have any forced labels, then the cfg is not well structured. */
if (forced_labels)
return 1;
/* If this function has a computed jump, then we consider the cfg
not well structured. */
if (current_function_has_computed_jump)
return 1;
/* If we have exception handlers, then we consider the cfg not well
structured. ?!? We should be able to handle this now that flow.c
computes an accurate cfg for EH. */
if (exception_handler_labels)
return 1;
/* If we have non-jumping insns which refer to labels, then we consider
the cfg not well structured. */
/* check for labels referred to other thn by jumps */
for (b = 0; b < n_basic_blocks; b++)
for (insn = BLOCK_HEAD (b);; insn = NEXT_INSN (insn))
{
code = GET_CODE (insn);
if (GET_RTX_CLASS (code) == 'i')
{
rtx note;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_LABEL)
return 1;
}
if (insn == BLOCK_END (b))
break;
}
/* All the tests passed. Consider the cfg well structured. */
return 0;
}
/* Build the control flow graph and set nr_edges.
Instead of trying to build a cfg ourselves, we rely on flow to
do it for us. Stamp out useless code (and bug) duplication.
Return nonzero if an irregularity in the cfg is found which would
prevent cross block scheduling. */
static int
build_control_flow (s_preds, s_succs, num_preds, num_succs)
int_list_ptr *s_preds;
int_list_ptr *s_succs;
int *num_preds;
int *num_succs;
{
int i;
int_list_ptr succ;
int unreachable;
/* Count the number of edges in the cfg. */
nr_edges = 0;
unreachable = 0;
for (i = 0; i < n_basic_blocks; i++)
{
nr_edges += num_succs[i];
/* Unreachable loops with more than one basic block are detected
during the DFS traversal in find_rgns.
Unreachable loops with a single block are detected here. This
test is redundant with the one in find_rgns, but it's much
cheaper to go ahead and catch the trivial case here. */
if (num_preds[i] == 0
|| (num_preds[i] == 1 && INT_LIST_VAL (s_preds[i]) == i))
unreachable = 1;
}
/* Account for entry/exit edges. */
nr_edges += 2;
in_edges = (int *) xmalloc (n_basic_blocks * sizeof (int));
out_edges = (int *) xmalloc (n_basic_blocks * sizeof (int));
bzero ((char *) in_edges, n_basic_blocks * sizeof (int));
bzero ((char *) out_edges, n_basic_blocks * sizeof (int));
edge_table = (haifa_edge *) xmalloc ((nr_edges) * sizeof (haifa_edge));
bzero ((char *) edge_table, ((nr_edges) * sizeof (haifa_edge)));
nr_edges = 0;
for (i = 0; i < n_basic_blocks; i++)
for (succ = s_succs[i]; succ; succ = succ->next)
{
if (INT_LIST_VAL (succ) != EXIT_BLOCK)
new_edge (i, INT_LIST_VAL (succ));
}
/* increment by 1, since edge 0 is unused. */
nr_edges++;
return unreachable;
}
/* Record an edge in the control flow graph from SOURCE to TARGET.
In theory, this is redundant with the s_succs computed above, but
we have not converted all of haifa to use information from the
integer lists. */
static void
new_edge (source, target)
int source, target;
{
int e, next_edge;
int curr_edge, fst_edge;
/* check for duplicates */
fst_edge = curr_edge = OUT_EDGES (source);
while (curr_edge)
{
if (FROM_BLOCK (curr_edge) == source
&& TO_BLOCK (curr_edge) == target)
{
return;
}
curr_edge = NEXT_OUT (curr_edge);
if (fst_edge == curr_edge)
break;
}
e = ++nr_edges;
FROM_BLOCK (e) = source;
TO_BLOCK (e) = target;
if (OUT_EDGES (source))
{
next_edge = NEXT_OUT (OUT_EDGES (source));
NEXT_OUT (OUT_EDGES (source)) = e;
NEXT_OUT (e) = next_edge;
}
else
{
OUT_EDGES (source) = e;
NEXT_OUT (e) = e;
}
if (IN_EDGES (target))
{
next_edge = NEXT_IN (IN_EDGES (target));
NEXT_IN (IN_EDGES (target)) = e;
NEXT_IN (e) = next_edge;
}
else
{
IN_EDGES (target) = e;
NEXT_IN (e) = e;
}
}
/* BITSET macros for operations on the control flow graph. */
/* Compute bitwise union of two bitsets. */
#define BITSET_UNION(set1, set2, len) \
do { register bitset tp = set1, sp = set2; \
register int i; \
for (i = 0; i < len; i++) \
*(tp++) |= *(sp++); } while (0)
/* Compute bitwise intersection of two bitsets. */
#define BITSET_INTER(set1, set2, len) \
do { register bitset tp = set1, sp = set2; \
register int i; \
for (i = 0; i < len; i++) \
*(tp++) &= *(sp++); } while (0)
/* Compute bitwise difference of two bitsets. */
#define BITSET_DIFFER(set1, set2, len) \
do { register bitset tp = set1, sp = set2; \
register int i; \
for (i = 0; i < len; i++) \
*(tp++) &= ~*(sp++); } while (0)
/* Inverts every bit of bitset 'set' */
#define BITSET_INVERT(set, len) \
do { register bitset tmpset = set; \
register int i; \
for (i = 0; i < len; i++, tmpset++) \
*tmpset = ~*tmpset; } while (0)
/* Turn on the index'th bit in bitset set. */
#define BITSET_ADD(set, index, len) \
{ \
if (index >= HOST_BITS_PER_WIDE_INT * len) \
abort (); \
else \
set[index/HOST_BITS_PER_WIDE_INT] |= \
1 << (index % HOST_BITS_PER_WIDE_INT); \
}
/* Turn off the index'th bit in set. */
#define BITSET_REMOVE(set, index, len) \
{ \
if (index >= HOST_BITS_PER_WIDE_INT * len) \
abort (); \
else \
set[index/HOST_BITS_PER_WIDE_INT] &= \
~(1 << (index%HOST_BITS_PER_WIDE_INT)); \
}
/* Check if the index'th bit in bitset set is on. */
static char
bitset_member (set, index, len)
bitset set;
int index, len;
{
if (index >= HOST_BITS_PER_WIDE_INT * len)
abort ();
return (set[index / HOST_BITS_PER_WIDE_INT] &
1 << (index % HOST_BITS_PER_WIDE_INT)) ? 1 : 0;
}
/* Translate a bit-set SET to a list BL of the bit-set members. */
static void
extract_bitlst (set, len, bl)
bitset set;
int len;
bitlst *bl;
{
int i, j, offset;
unsigned HOST_WIDE_INT word;
/* bblst table space is reused in each call to extract_bitlst */
bitlst_table_last = 0;
bl->first_member = &bitlst_table[bitlst_table_last];
bl->nr_members = 0;
for (i = 0; i < len; i++)
{
word = set[i];
offset = i * HOST_BITS_PER_WIDE_INT;
for (j = 0; word; j++)
{
if (word & 1)
{
bitlst_table[bitlst_table_last++] = offset;
(bl->nr_members)++;
}
word >>= 1;
++offset;
}
}
}
/* functions for the construction of regions */
/* Print the regions, for debugging purposes. Callable from debugger. */
void
debug_regions ()
{
int rgn, bb;
fprintf (dump, "\n;; ------------ REGIONS ----------\n\n");
for (rgn = 0; rgn < nr_regions; rgn++)
{
fprintf (dump, ";;\trgn %d nr_blocks %d:\n", rgn,
rgn_table[rgn].rgn_nr_blocks);
fprintf (dump, ";;\tbb/block: ");
for (bb = 0; bb < rgn_table[rgn].rgn_nr_blocks; bb++)
{
current_blocks = RGN_BLOCKS (rgn);
if (bb != BLOCK_TO_BB (BB_TO_BLOCK (bb)))
abort ();
fprintf (dump, " %d/%d ", bb, BB_TO_BLOCK (bb));
}
fprintf (dump, "\n\n");
}
}
/* Build a single block region for each basic block in the function.
This allows for using the same code for interblock and basic block
scheduling. */
static void
find_single_block_region ()
{
int i;
for (i = 0; i < n_basic_blocks; i++)
{
rgn_bb_table[i] = i;
RGN_NR_BLOCKS (i) = 1;
RGN_BLOCKS (i) = i;
CONTAINING_RGN (i) = i;
BLOCK_TO_BB (i) = 0;
}
nr_regions = n_basic_blocks;
}
/* Update number of blocks and the estimate for number of insns
in the region. Return 1 if the region is "too large" for interblock
scheduling (compile time considerations), otherwise return 0. */
static int
too_large (block, num_bbs, num_insns)
int block, *num_bbs, *num_insns;
{
(*num_bbs)++;
(*num_insns) += (INSN_LUID (BLOCK_END (block)) -
INSN_LUID (BLOCK_HEAD (block)));
if ((*num_bbs > MAX_RGN_BLOCKS) || (*num_insns > MAX_RGN_INSNS))
return 1;
else
return 0;
}
/* Update_loop_relations(blk, hdr): Check if the loop headed by max_hdr[blk]
is still an inner loop. Put in max_hdr[blk] the header of the most inner
loop containing blk. */
#define UPDATE_LOOP_RELATIONS(blk, hdr) \
{ \
if (max_hdr[blk] == -1) \
max_hdr[blk] = hdr; \
else if (dfs_nr[max_hdr[blk]] > dfs_nr[hdr]) \
RESET_BIT (inner, hdr); \
else if (dfs_nr[max_hdr[blk]] < dfs_nr[hdr]) \
{ \
RESET_BIT (inner,max_hdr[blk]); \
max_hdr[blk] = hdr; \
} \
}
/* Find regions for interblock scheduling.
A region for scheduling can be:
* A loop-free procedure, or
* A reducible inner loop, or
* A basic block not contained in any other region.
?!? In theory we could build other regions based on extended basic
blocks or reverse extended basic blocks. Is it worth the trouble?
Loop blocks that form a region are put into the region's block list
in topological order.
This procedure stores its results into the following global (ick) variables
* rgn_nr
* rgn_table
* rgn_bb_table
* block_to_bb
* containing region
We use dominator relationships to avoid making regions out of non-reducible
loops.
This procedure needs to be converted to work on pred/succ lists instead
of edge tables. That would simplify it somewhat. */
static void
find_rgns (s_preds, s_succs, num_preds, num_succs, dom)
int_list_ptr *s_preds;
int_list_ptr *s_succs;
int *num_preds;
int *num_succs;
sbitmap *dom;
{
int *max_hdr, *dfs_nr, *stack, *queue, *degree;
char no_loops = 1;
int node, child, loop_head, i, head, tail;
int count = 0, sp, idx = 0, current_edge = out_edges[0];
int num_bbs, num_insns, unreachable;
int too_large_failure;
/* Note if an edge has been passed. */
sbitmap passed;
/* Note if a block is a natural loop header. */
sbitmap header;
/* Note if a block is an natural inner loop header. */
sbitmap inner;
/* Note if a block is in the block queue. */
sbitmap in_queue;
/* Note if a block is in the block queue. */
sbitmap in_stack;
/* Perform a DFS traversal of the cfg. Identify loop headers, inner loops
and a mapping from block to its loop header (if the block is contained
in a loop, else -1).
Store results in HEADER, INNER, and MAX_HDR respectively, these will
be used as inputs to the second traversal.
STACK, SP and DFS_NR are only used during the first traversal. */
/* Allocate and initialize variables for the first traversal. */
max_hdr = (int *) alloca (n_basic_blocks * sizeof (int));
dfs_nr = (int *) alloca (n_basic_blocks * sizeof (int));
bzero ((char *) dfs_nr, n_basic_blocks * sizeof (int));
stack = (int *) alloca (nr_edges * sizeof (int));
inner = sbitmap_alloc (n_basic_blocks);
sbitmap_ones (inner);
header = sbitmap_alloc (n_basic_blocks);
sbitmap_zero (header);
passed = sbitmap_alloc (nr_edges);
sbitmap_zero (passed);
in_queue = sbitmap_alloc (n_basic_blocks);
sbitmap_zero (in_queue);
in_stack = sbitmap_alloc (n_basic_blocks);
sbitmap_zero (in_stack);
for (i = 0; i < n_basic_blocks; i++)
max_hdr[i] = -1;
/* DFS traversal to find inner loops in the cfg. */
sp = -1;
while (1)
{
if (current_edge == 0 || TEST_BIT (passed, current_edge))
{
/* We have reached a leaf node or a node that was already
processed. Pop edges off the stack until we find
an edge that has not yet been processed. */
while (sp >= 0
&& (current_edge == 0 || TEST_BIT (passed, current_edge)))
{
/* Pop entry off the stack. */
current_edge = stack[sp--];
node = FROM_BLOCK (current_edge);
child = TO_BLOCK (current_edge);
RESET_BIT (in_stack, child);
if (max_hdr[child] >= 0 && TEST_BIT (in_stack, max_hdr[child]))
UPDATE_LOOP_RELATIONS (node, max_hdr[child]);
current_edge = NEXT_OUT (current_edge);
}
/* See if have finished the DFS tree traversal. */
if (sp < 0 && TEST_BIT (passed, current_edge))
break;
/* Nope, continue the traversal with the popped node. */
continue;
}
/* Process a node. */
node = FROM_BLOCK (current_edge);
child = TO_BLOCK (current_edge);
SET_BIT (in_stack, node);
dfs_nr[node] = ++count;
/* If the successor is in the stack, then we've found a loop.
Mark the loop, if it is not a natural loop, then it will
be rejected during the second traversal. */
if (TEST_BIT (in_stack, child))
{
no_loops = 0;
SET_BIT (header, child);
UPDATE_LOOP_RELATIONS (node, child);
SET_BIT (passed, current_edge);
current_edge = NEXT_OUT (current_edge);
continue;
}
/* If the child was already visited, then there is no need to visit
it again. Just update the loop relationships and restart
with a new edge. */
if (dfs_nr[child])
{
if (max_hdr[child] >= 0 && TEST_BIT (in_stack, max_hdr[child]))
UPDATE_LOOP_RELATIONS (node, max_hdr[child]);
SET_BIT (passed, current_edge);
current_edge = NEXT_OUT (current_edge);
continue;
}
/* Push an entry on the stack and continue DFS traversal. */
stack[++sp] = current_edge;
SET_BIT (passed, current_edge);
current_edge = OUT_EDGES (child);
}
/* Another check for unreachable blocks. The earlier test in
is_cfg_nonregular only finds unreachable blocks that do not
form a loop.
The DFS traversal will mark every block that is reachable from
the entry node by placing a nonzero value in dfs_nr. Thus if
dfs_nr is zero for any block, then it must be unreachable. */
unreachable = 0;
for (i = 0; i < n_basic_blocks; i++)
if (dfs_nr[i] == 0)
{
unreachable = 1;
break;
}
/* Gross. To avoid wasting memory, the second pass uses the dfs_nr array
to hold degree counts. */
degree = dfs_nr;
/* Compute the in-degree of every block in the graph */
for (i = 0; i < n_basic_blocks; i++)
degree[i] = num_preds[i];
/* Do not perform region scheduling if there are any unreachable
blocks. */
if (!unreachable)
{
if (no_loops)
SET_BIT (header, 0);
/* Second travsersal:find reducible inner loops and topologically sort
block of each region. */
queue = (int *) alloca (n_basic_blocks * sizeof (int));
/* Find blocks which are inner loop headers. We still have non-reducible
loops to consider at this point. */
for (i = 0; i < n_basic_blocks; i++)
{
if (TEST_BIT (header, i) && TEST_BIT (inner, i))
{
int_list_ptr ps;
int j;
/* Now check that the loop is reducible. We do this separate
from finding inner loops so that we do not find a reducible
loop which contains an inner non-reducible loop.
A simple way to find reducible/natrual loops is to verify
that each block in the loop is dominated by the loop
header.
If there exists a block that is not dominated by the loop
header, then the block is reachable from outside the loop
and thus the loop is not a natural loop. */
for (j = 0; j < n_basic_blocks; j++)
{
/* First identify blocks in the loop, except for the loop
entry block. */
if (i == max_hdr[j] && i != j)
{
/* Now verify that the block is dominated by the loop
header. */
if (!TEST_BIT (dom[j], i))
break;
}
}
/* If we exited the loop early, then I is the header of a non
reducible loop and we should quit processing it now. */
if (j != n_basic_blocks)
continue;
/* I is a header of an inner loop, or block 0 in a subroutine
with no loops at all. */
head = tail = -1;
too_large_failure = 0;
loop_head = max_hdr[i];
/* Decrease degree of all I's successors for topological
ordering. */
for (ps = s_succs[i]; ps; ps = ps->next)
if (INT_LIST_VAL (ps) != EXIT_BLOCK
&& INT_LIST_VAL (ps) != ENTRY_BLOCK)
--degree[INT_LIST_VAL(ps)];
/* Estimate # insns, and count # blocks in the region. */
num_bbs = 1;
num_insns = (INSN_LUID (BLOCK_END (i))
- INSN_LUID (BLOCK_HEAD (i)));
/* Find all loop latches (blocks which back edges to the loop
header) or all the leaf blocks in the cfg has no loops.
Place those blocks into the queue. */
if (no_loops)
{
for (j = 0; j < n_basic_blocks; j++)
/* Leaf nodes have only a single successor which must
be EXIT_BLOCK. */
if (num_succs[j] == 1
&& INT_LIST_VAL (s_succs[j]) == EXIT_BLOCK)
{
queue[++tail] = j;
SET_BIT (in_queue, j);
if (too_large (j, &num_bbs, &num_insns))
{
too_large_failure = 1;
break;
}
}
}
else
{
int_list_ptr ps;
for (ps = s_preds[i]; ps; ps = ps->next)
{
node = INT_LIST_VAL (ps);
if (node == ENTRY_BLOCK || node == EXIT_BLOCK)
continue;
if (max_hdr[node] == loop_head && node != i)
{
/* This is a loop latch. */
queue[++tail] = node;
SET_BIT (in_queue, node);
if (too_large (node, &num_bbs, &num_insns))
{
too_large_failure = 1;
break;
}
}
}
}
/* Now add all the blocks in the loop to the queue.
We know the loop is a natural loop; however the algorithm
above will not always mark certain blocks as being in the
loop. Consider:
node children
a b,c
b c
c a,d
d b
The algorithm in the DFS traversal may not mark B & D as part
of the loop (ie they will not have max_hdr set to A).
We know they can not be loop latches (else they would have
had max_hdr set since they'd have a backedge to a dominator
block). So we don't need them on the initial queue.
We know they are part of the loop because they are dominated
by the loop header and can be reached by a backwards walk of
the edges starting with nodes on the initial queue.
It is safe and desirable to include those nodes in the
loop/scheduling region. To do so we would need to decrease
the degree of a node if it is the target of a backedge
within the loop itself as the node is placed in the queue.
We do not do this because I'm not sure that the actual
scheduling code will properly handle this case. ?!? */
while (head < tail && !too_large_failure)
{
int_list_ptr ps;
child = queue[++head];
for (ps = s_preds[child]; ps; ps = ps->next)
{
node = INT_LIST_VAL (ps);
/* See discussion above about nodes not marked as in
this loop during the initial DFS traversal. */
if (node == ENTRY_BLOCK || node == EXIT_BLOCK
|| max_hdr[node] != loop_head)
{
tail = -1;
break;
}
else if (!TEST_BIT (in_queue, node) && node != i)
{
queue[++tail] = node;
SET_BIT (in_queue, node);
if (too_large (node, &num_bbs, &num_insns))
{
too_large_failure = 1;
break;
}
}
}
}
if (tail >= 0 && !too_large_failure)
{
/* Place the loop header into list of region blocks. */
degree[i] = -1;
rgn_bb_table[idx] = i;
RGN_NR_BLOCKS (nr_regions) = num_bbs;
RGN_BLOCKS (nr_regions) = idx++;
CONTAINING_RGN (i) = nr_regions;
BLOCK_TO_BB (i) = count = 0;
/* Remove blocks from queue[] when their in degree becomes
zero. Repeat until no blocks are left on the list. This
produces a topological list of blocks in the region. */
while (tail >= 0)
{
int_list_ptr ps;
if (head < 0)
head = tail;
child = queue[head];
if (degree[child] == 0)
{
degree[child] = -1;
rgn_bb_table[idx++] = child;
BLOCK_TO_BB (child) = ++count;
CONTAINING_RGN (child) = nr_regions;
queue[head] = queue[tail--];
for (ps = s_succs[child]; ps; ps = ps->next)
if (INT_LIST_VAL (ps) != ENTRY_BLOCK
&& INT_LIST_VAL (ps) != EXIT_BLOCK)
--degree[INT_LIST_VAL (ps)];
}
else
--head;
}
++nr_regions;
}
}
}
}
/* Any block that did not end up in a region is placed into a region
by itself. */
for (i = 0; i < n_basic_blocks; i++)
if (degree[i] >= 0)
{
rgn_bb_table[idx] = i;
RGN_NR_BLOCKS (nr_regions) = 1;
RGN_BLOCKS (nr_regions) = idx++;
CONTAINING_RGN (i) = nr_regions++;
BLOCK_TO_BB (i) = 0;
}
free (passed);
free (header);
free (inner);
free (in_queue);
free (in_stack);
}
/* functions for regions scheduling information */
/* Compute dominators, probability, and potential-split-edges of bb.
Assume that these values were already computed for bb's predecessors. */
static void
compute_dom_prob_ps (bb)
int bb;
{
int nxt_in_edge, fst_in_edge, pred;
int fst_out_edge, nxt_out_edge, nr_out_edges, nr_rgn_out_edges;
prob[bb] = 0.0;
if (IS_RGN_ENTRY (bb))
{
BITSET_ADD (dom[bb], 0, bbset_size);
prob[bb] = 1.0;
return;
}
fst_in_edge = nxt_in_edge = IN_EDGES (BB_TO_BLOCK (bb));
/* intialize dom[bb] to '111..1' */
BITSET_INVERT (dom[bb], bbset_size);
do
{
pred = FROM_BLOCK (nxt_in_edge);
BITSET_INTER (dom[bb], dom[BLOCK_TO_BB (pred)], bbset_size);
BITSET_UNION (ancestor_edges[bb], ancestor_edges[BLOCK_TO_BB (pred)],
edgeset_size);
BITSET_ADD (ancestor_edges[bb], EDGE_TO_BIT (nxt_in_edge), edgeset_size);
nr_out_edges = 1;
nr_rgn_out_edges = 0;
fst_out_edge = OUT_EDGES (pred);
nxt_out_edge = NEXT_OUT (fst_out_edge);
BITSET_UNION (pot_split[bb], pot_split[BLOCK_TO_BB (pred)],
edgeset_size);
BITSET_ADD (pot_split[bb], EDGE_TO_BIT (fst_out_edge), edgeset_size);
/* the successor doesn't belong the region? */
if (CONTAINING_RGN (TO_BLOCK (fst_out_edge)) !=
CONTAINING_RGN (BB_TO_BLOCK (bb)))
++nr_rgn_out_edges;
while (fst_out_edge != nxt_out_edge)
{
++nr_out_edges;
/* the successor doesn't belong the region? */
if (CONTAINING_RGN (TO_BLOCK (nxt_out_edge)) !=
CONTAINING_RGN (BB_TO_BLOCK (bb)))
++nr_rgn_out_edges;
BITSET_ADD (pot_split[bb], EDGE_TO_BIT (nxt_out_edge), edgeset_size);
nxt_out_edge = NEXT_OUT (nxt_out_edge);
}
/* now nr_rgn_out_edges is the number of region-exit edges from pred,
and nr_out_edges will be the number of pred out edges not leaving
the region. */
nr_out_edges -= nr_rgn_out_edges;
if (nr_rgn_out_edges > 0)
prob[bb] += 0.9 * prob[BLOCK_TO_BB (pred)] / nr_out_edges;
else
prob[bb] += prob[BLOCK_TO_BB (pred)] / nr_out_edges;
nxt_in_edge = NEXT_IN (nxt_in_edge);
}
while (fst_in_edge != nxt_in_edge);
BITSET_ADD (dom[bb], bb, bbset_size);
BITSET_DIFFER (pot_split[bb], ancestor_edges[bb], edgeset_size);
if (sched_verbose >= 2)
fprintf (dump, ";; bb_prob(%d, %d) = %3d\n", bb, BB_TO_BLOCK (bb), (int) (100.0 * prob[bb]));
} /* compute_dom_prob_ps */
/* functions for target info */
/* Compute in BL the list of split-edges of bb_src relatively to bb_trg.
Note that bb_trg dominates bb_src. */
static void
split_edges (bb_src, bb_trg, bl)
int bb_src;
int bb_trg;
edgelst *bl;
{
int es = edgeset_size;
edgeset src = (edgeset) alloca (es * sizeof (HOST_WIDE_INT));
while (es--)
src[es] = (pot_split[bb_src])[es];
BITSET_DIFFER (src, pot_split[bb_trg], edgeset_size);
extract_bitlst (src, edgeset_size, bl);
}
/* Find the valid candidate-source-blocks for the target block TRG, compute
their probability, and check if they are speculative or not.
For speculative sources, compute their update-blocks and split-blocks. */
static void
compute_trg_info (trg)
int trg;
{
register candidate *sp;
edgelst el;
int check_block, update_idx;
int i, j, k, fst_edge, nxt_edge;
/* define some of the fields for the target bb as well */
sp = candidate_table + trg;
sp->is_valid = 1;
sp->is_speculative = 0;
sp->src_prob = 100;
for (i = trg + 1; i < current_nr_blocks; i++)
{
sp = candidate_table + i;
sp->is_valid = IS_DOMINATED (i, trg);
if (sp->is_valid)
{
sp->src_prob = GET_SRC_PROB (i, trg);
sp->is_valid = (sp->src_prob >= MIN_PROBABILITY);
}
if (sp->is_valid)
{
split_edges (i, trg, &el);
sp->is_speculative = (el.nr_members) ? 1 : 0;
if (sp->is_speculative && !flag_schedule_speculative)
sp->is_valid = 0;
}
if (sp->is_valid)
{
sp->split_bbs.first_member = &bblst_table[bblst_last];
sp->split_bbs.nr_members = el.nr_members;
for (j = 0; j < el.nr_members; bblst_last++, j++)
bblst_table[bblst_last] =
TO_BLOCK (rgn_edges[el.first_member[j]]);
sp->update_bbs.first_member = &bblst_table[bblst_last];
update_idx = 0;
for (j = 0; j < el.nr_members; j++)
{
check_block = FROM_BLOCK (rgn_edges[el.first_member[j]]);
fst_edge = nxt_edge = OUT_EDGES (check_block);
do
{
for (k = 0; k < el.nr_members; k++)
if (EDGE_TO_BIT (nxt_edge) == el.first_member[k])
break;
if (k >= el.nr_members)
{
bblst_table[bblst_last++] = TO_BLOCK (nxt_edge);
update_idx++;
}
nxt_edge = NEXT_OUT (nxt_edge);
}
while (fst_edge != nxt_edge);
}
sp->update_bbs.nr_members = update_idx;
}
else
{
sp->split_bbs.nr_members = sp->update_bbs.nr_members = 0;
sp->is_speculative = 0;
sp->src_prob = 0;
}
}
} /* compute_trg_info */
/* Print candidates info, for debugging purposes. Callable from debugger. */
void
debug_candidate (i)
int i;
{
if (!candidate_table[i].is_valid)
return;
if (candidate_table[i].is_speculative)
{
int j;
fprintf (dump, "src b %d bb %d speculative \n", BB_TO_BLOCK (i), i);
fprintf (dump, "split path: ");
for (j = 0; j < candidate_table[i].split_bbs.nr_members; j++)
{
int b = candidate_table[i].split_bbs.first_member[j];
fprintf (dump, " %d ", b);
}
fprintf (dump, "\n");
fprintf (dump, "update path: ");
for (j = 0; j < candidate_table[i].update_bbs.nr_members; j++)
{
int b = candidate_table[i].update_bbs.first_member[j];
fprintf (dump, " %d ", b);
}
fprintf (dump, "\n");
}
else
{
fprintf (dump, " src %d equivalent\n", BB_TO_BLOCK (i));
}
}
/* Print candidates info, for debugging purposes. Callable from debugger. */
void
debug_candidates (trg)
int trg;
{
int i;
fprintf (dump, "----------- candidate table: target: b=%d bb=%d ---\n",
BB_TO_BLOCK (trg), trg);
for (i = trg + 1; i < current_nr_blocks; i++)
debug_candidate (i);
}
/* functions for speculative scheduing */
/* Return 0 if x is a set of a register alive in the beginning of one
of the split-blocks of src, otherwise return 1. */
static int
check_live_1 (src, x)
int src;
rtx x;
{
register int i;
register int regno;
register rtx reg = SET_DEST (x);
if (reg == 0)
return 1;
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == SIGN_EXTRACT
|| GET_CODE (reg) == STRICT_LOW_PART)
reg = XEXP (reg, 0);
if (GET_CODE (reg) == PARALLEL
&& GET_MODE (reg) == BLKmode)
{
register int i;
for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
if (check_live_1 (src, XVECEXP (reg, 0, i)))
return 1;
return 0;
}
if (GET_CODE (reg) != REG)
return 1;
regno = REGNO (reg);
if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
{
/* Global registers are assumed live */
return 0;
}
else
{
if (regno < FIRST_PSEUDO_REGISTER)
{
/* check for hard registers */
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
for (i = 0; i < candidate_table[src].split_bbs.nr_members; i++)
{
int b = candidate_table[src].split_bbs.first_member[i];
if (REGNO_REG_SET_P (BASIC_BLOCK (b)->global_live_at_start,
regno + j))
{
return 0;
}
}
}
}
else
{
/* check for psuedo registers */
for (i = 0; i < candidate_table[src].split_bbs.nr_members; i++)
{
int b = candidate_table[src].split_bbs.first_member[i];
if (REGNO_REG_SET_P (BASIC_BLOCK (b)->global_live_at_start, regno))
{
return 0;
}
}
}
}
return 1;
}
/* If x is a set of a register R, mark that R is alive in the beginning
of every update-block of src. */
static void
update_live_1 (src, x)
int src;
rtx x;
{
register int i;
register int regno;
register rtx reg = SET_DEST (x);
if (reg == 0)
return;
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == SIGN_EXTRACT
|| GET_CODE (reg) == STRICT_LOW_PART)
reg = XEXP (reg, 0);
if (GET_CODE (reg) == PARALLEL
&& GET_MODE (reg) == BLKmode)
{
register int i;
for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
update_live_1 (src, XVECEXP (reg, 0, i));
return;
}
if (GET_CODE (reg) != REG)
return;
/* Global registers are always live, so the code below does not apply
to them. */
regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER || !global_regs[regno])
{
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
for (i = 0; i < candidate_table[src].update_bbs.nr_members; i++)
{
int b = candidate_table[src].update_bbs.first_member[i];
SET_REGNO_REG_SET (BASIC_BLOCK (b)->global_live_at_start,
regno + j);
}
}
}
else
{
for (i = 0; i < candidate_table[src].update_bbs.nr_members; i++)
{
int b = candidate_table[src].update_bbs.first_member[i];
SET_REGNO_REG_SET (BASIC_BLOCK (b)->global_live_at_start, regno);
}
}
}
}
/* Return 1 if insn can be speculatively moved from block src to trg,
otherwise return 0. Called before first insertion of insn to
ready-list or before the scheduling. */
static int
check_live (insn, src)
rtx insn;
int src;
{
/* find the registers set by instruction */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
return check_live_1 (src, PATTERN (insn));
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if ((GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
&& !check_live_1 (src, XVECEXP (PATTERN (insn), 0, j)))
return 0;
return 1;
}
return 1;
}
/* Update the live registers info after insn was moved speculatively from
block src to trg. */
static void
update_live (insn, src)
rtx insn;
int src;
{
/* find the registers set by instruction */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
update_live_1 (src, PATTERN (insn));
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
update_live_1 (src, XVECEXP (PATTERN (insn), 0, j));
}
}
/* Exception Free Loads:
We define five classes of speculative loads: IFREE, IRISKY,
PFREE, PRISKY, and MFREE.
IFREE loads are loads that are proved to be exception-free, just
by examining the load insn. Examples for such loads are loads
from TOC and loads of global data.
IRISKY loads are loads that are proved to be exception-risky,
just by examining the load insn. Examples for such loads are
volatile loads and loads from shared memory.
PFREE loads are loads for which we can prove, by examining other
insns, that they are exception-free. Currently, this class consists
of loads for which we are able to find a "similar load", either in
the target block, or, if only one split-block exists, in that split
block. Load2 is similar to load1 if both have same single base
register. We identify only part of the similar loads, by finding
an insn upon which both load1 and load2 have a DEF-USE dependence.
PRISKY loads are loads for which we can prove, by examining other
insns, that they are exception-risky. Currently we have two proofs for
such loads. The first proof detects loads that are probably guarded by a
test on the memory address. This proof is based on the
backward and forward data dependence information for the region.
Let load-insn be the examined load.
Load-insn is PRISKY iff ALL the following hold:
- insn1 is not in the same block as load-insn
- there is a DEF-USE dependence chain (insn1, ..., load-insn)
- test-insn is either a compare or a branch, not in the same block as load-insn
- load-insn is reachable from test-insn
- there is a DEF-USE dependence chain (insn1, ..., test-insn)
This proof might fail when the compare and the load are fed
by an insn not in the region. To solve this, we will add to this
group all loads that have no input DEF-USE dependence.
The second proof detects loads that are directly or indirectly
fed by a speculative load. This proof is affected by the
scheduling process. We will use the flag fed_by_spec_load.
Initially, all insns have this flag reset. After a speculative
motion of an insn, if insn is either a load, or marked as
fed_by_spec_load, we will also mark as fed_by_spec_load every
insn1 for which a DEF-USE dependence (insn, insn1) exists. A
load which is fed_by_spec_load is also PRISKY.
MFREE (maybe-free) loads are all the remaining loads. They may be
exception-free, but we cannot prove it.
Now, all loads in IFREE and PFREE classes are considered
exception-free, while all loads in IRISKY and PRISKY classes are
considered exception-risky. As for loads in the MFREE class,
these are considered either exception-free or exception-risky,
depending on whether we are pessimistic or optimistic. We have
to take the pessimistic approach to assure the safety of
speculative scheduling, but we can take the optimistic approach
by invoking the -fsched_spec_load_dangerous option. */
enum INSN_TRAP_CLASS
{
TRAP_FREE = 0, IFREE = 1, PFREE_CANDIDATE = 2,
PRISKY_CANDIDATE = 3, IRISKY = 4, TRAP_RISKY = 5
};
#define WORST_CLASS(class1, class2) \
((class1 > class2) ? class1 : class2)
/* Indexed by INSN_UID, and set if there's DEF-USE dependence between */
/* some speculatively moved load insn and this one. */
char *fed_by_spec_load;
char *is_load_insn;
/* Non-zero if block bb_to is equal to, or reachable from block bb_from. */
#define IS_REACHABLE(bb_from, bb_to) \
(bb_from == bb_to \
|| IS_RGN_ENTRY (bb_from) \
|| (bitset_member (ancestor_edges[bb_to], \
EDGE_TO_BIT (IN_EDGES (BB_TO_BLOCK (bb_from))), \
edgeset_size)))
#define FED_BY_SPEC_LOAD(insn) (fed_by_spec_load[INSN_UID (insn)])
#define IS_LOAD_INSN(insn) (is_load_insn[INSN_UID (insn)])
/* Non-zero iff the address is comprised from at most 1 register */
#define CONST_BASED_ADDRESS_P(x) \
(GET_CODE (x) == REG \
|| ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS \
|| (GET_CODE (x) == LO_SUM)) \
&& (GET_CODE (XEXP (x, 0)) == CONST_INT \
|| GET_CODE (XEXP (x, 1)) == CONST_INT)))
/* Turns on the fed_by_spec_load flag for insns fed by load_insn. */
static void
set_spec_fed (load_insn)
rtx load_insn;
{
rtx link;
for (link = INSN_DEPEND (load_insn); link; link = XEXP (link, 1))
if (GET_MODE (link) == VOIDmode)
FED_BY_SPEC_LOAD (XEXP (link, 0)) = 1;
} /* set_spec_fed */
/* On the path from the insn to load_insn_bb, find a conditional branch */
/* depending on insn, that guards the speculative load. */
static int
find_conditional_protection (insn, load_insn_bb)
rtx insn;
int load_insn_bb;
{
rtx link;
/* iterate through DEF-USE forward dependences */
for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
{
rtx next = XEXP (link, 0);
if ((CONTAINING_RGN (INSN_BLOCK (next)) ==
CONTAINING_RGN (BB_TO_BLOCK (load_insn_bb)))
&& IS_REACHABLE (INSN_BB (next), load_insn_bb)
&& load_insn_bb != INSN_BB (next)
&& GET_MODE (link) == VOIDmode
&& (GET_CODE (next) == JUMP_INSN
|| find_conditional_protection (next, load_insn_bb)))
return 1;
}
return 0;
} /* find_conditional_protection */
/* Returns 1 if the same insn1 that participates in the computation
of load_insn's address is feeding a conditional branch that is
guarding on load_insn. This is true if we find a the two DEF-USE
chains:
insn1 -> ... -> conditional-branch
insn1 -> ... -> load_insn,
and if a flow path exist:
insn1 -> ... -> conditional-branch -> ... -> load_insn,
and if insn1 is on the path
region-entry -> ... -> bb_trg -> ... load_insn.
Locate insn1 by climbing on LOG_LINKS from load_insn.
Locate the branch by following INSN_DEPEND from insn1. */
static int
is_conditionally_protected (load_insn, bb_src, bb_trg)
rtx load_insn;
int bb_src, bb_trg;
{
rtx link;
for (link = LOG_LINKS (load_insn); link; link = XEXP (link, 1))
{
rtx insn1 = XEXP (link, 0);
/* must be a DEF-USE dependence upon non-branch */
if (GET_MODE (link) != VOIDmode
|| GET_CODE (insn1) == JUMP_INSN)
continue;
/* must exist a path: region-entry -> ... -> bb_trg -> ... load_insn */
if (INSN_BB (insn1) == bb_src
|| (CONTAINING_RGN (INSN_BLOCK (insn1))
!= CONTAINING_RGN (BB_TO_BLOCK (bb_src)))
|| (!IS_REACHABLE (bb_trg, INSN_BB (insn1))
&& !IS_REACHABLE (INSN_BB (insn1), bb_trg)))
continue;
/* now search for the conditional-branch */
if (find_conditional_protection (insn1, bb_src))
return 1;
/* recursive step: search another insn1, "above" current insn1. */
return is_conditionally_protected (insn1, bb_src, bb_trg);
}
/* the chain does not exsist */
return 0;
} /* is_conditionally_protected */
/* Returns 1 if a clue for "similar load" 'insn2' is found, and hence
load_insn can move speculatively from bb_src to bb_trg. All the
following must hold:
(1) both loads have 1 base register (PFREE_CANDIDATEs).
(2) load_insn and load1 have a def-use dependence upon
the same insn 'insn1'.
(3) either load2 is in bb_trg, or:
- there's only one split-block, and
- load1 is on the escape path, and
From all these we can conclude that the two loads access memory
addresses that differ at most by a constant, and hence if moving
load_insn would cause an exception, it would have been caused by
load2 anyhow. */
static int
is_pfree (load_insn, bb_src, bb_trg)
rtx load_insn;
int bb_src, bb_trg;
{
rtx back_link;
register candidate *candp = candidate_table + bb_src;
if (candp->split_bbs.nr_members != 1)
/* must have exactly one escape block */
return 0;
for (back_link = LOG_LINKS (load_insn);
back_link; back_link = XEXP (back_link, 1))
{
rtx insn1 = XEXP (back_link, 0);
if (GET_MODE (back_link) == VOIDmode)
{
/* found a DEF-USE dependence (insn1, load_insn) */
rtx fore_link;
for (fore_link = INSN_DEPEND (insn1);
fore_link; fore_link = XEXP (fore_link, 1))
{
rtx insn2 = XEXP (fore_link, 0);
if (GET_MODE (fore_link) == VOIDmode)
{
/* found a DEF-USE dependence (insn1, insn2) */
if (haifa_classify_insn (insn2) != PFREE_CANDIDATE)
/* insn2 not guaranteed to be a 1 base reg load */
continue;
if (INSN_BB (insn2) == bb_trg)
/* insn2 is the similar load, in the target block */
return 1;
if (*(candp->split_bbs.first_member) == INSN_BLOCK (insn2))
/* insn2 is a similar load, in a split-block */
return 1;
}
}
}
}
/* couldn't find a similar load */
return 0;
} /* is_pfree */
/* Returns a class that insn with GET_DEST(insn)=x may belong to,
as found by analyzing insn's expression. */
static int
may_trap_exp (x, is_store)
rtx x;
int is_store;
{
enum rtx_code code;
if (x == 0)
return TRAP_FREE;
code = GET_CODE (x);
if (is_store)
{
if (code == MEM)
return TRAP_RISKY;
else
return TRAP_FREE;
}
if (code == MEM)
{
/* The insn uses memory */
/* a volatile load */
if (MEM_VOLATILE_P (x))
return IRISKY;
/* an exception-free load */
if (!may_trap_p (x))
return IFREE;
/* a load with 1 base register, to be further checked */
if (CONST_BASED_ADDRESS_P (XEXP (x, 0)))
return PFREE_CANDIDATE;
/* no info on the load, to be further checked */
return PRISKY_CANDIDATE;
}
else
{
char *fmt;
int i, insn_class = TRAP_FREE;
/* neither store nor load, check if it may cause a trap */
if (may_trap_p (x))
return TRAP_RISKY;
/* recursive step: walk the insn... */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
int tmp_class = may_trap_exp (XEXP (x, i), is_store);
insn_class = WORST_CLASS (insn_class, tmp_class);
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
{
int tmp_class = may_trap_exp (XVECEXP (x, i, j), is_store);
insn_class = WORST_CLASS (insn_class, tmp_class);
if (insn_class == TRAP_RISKY || insn_class == IRISKY)
break;
}
}
if (insn_class == TRAP_RISKY || insn_class == IRISKY)
break;
}
return insn_class;
}
} /* may_trap_exp */
/* Classifies insn for the purpose of verifying that it can be
moved speculatively, by examining it's patterns, returning:
TRAP_RISKY: store, or risky non-load insn (e.g. division by variable).
TRAP_FREE: non-load insn.
IFREE: load from a globaly safe location.
IRISKY: volatile load.
PFREE_CANDIDATE, PRISKY_CANDIDATE: load that need to be checked for
being either PFREE or PRISKY. */
static int
haifa_classify_insn (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
int tmp_class = TRAP_FREE;
int insn_class = TRAP_FREE;
enum rtx_code code;
if (GET_CODE (pat) == PARALLEL)
{
int i, len = XVECLEN (pat, 0);
for (i = len - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (pat, 0, i));
switch (code)
{
case CLOBBER:
/* test if it is a 'store' */
tmp_class = may_trap_exp (XEXP (XVECEXP (pat, 0, i), 0), 1);
break;
case SET:
/* test if it is a store */
tmp_class = may_trap_exp (SET_DEST (XVECEXP (pat, 0, i)), 1);
if (tmp_class == TRAP_RISKY)
break;
/* test if it is a load */
tmp_class =
WORST_CLASS (tmp_class,
may_trap_exp (SET_SRC (XVECEXP (pat, 0, i)), 0));
break;
case TRAP_IF:
tmp_class = TRAP_RISKY;
break;
default:;
}
insn_class = WORST_CLASS (insn_class, tmp_class);
if (insn_class == TRAP_RISKY || insn_class == IRISKY)
break;
}
}
else
{
code = GET_CODE (pat);
switch (code)
{
case CLOBBER:
/* test if it is a 'store' */
tmp_class = may_trap_exp (XEXP (pat, 0), 1);
break;
case SET:
/* test if it is a store */
tmp_class = may_trap_exp (SET_DEST (pat), 1);
if (tmp_class == TRAP_RISKY)
break;
/* test if it is a load */
tmp_class =
WORST_CLASS (tmp_class,
may_trap_exp (SET_SRC (pat), 0));
break;
case TRAP_IF:
tmp_class = TRAP_RISKY;
break;
default:;
}
insn_class = tmp_class;
}
return insn_class;
} /* haifa_classify_insn */
/* Return 1 if load_insn is prisky (i.e. if load_insn is fed by
a load moved speculatively, or if load_insn is protected by
a compare on load_insn's address). */
static int
is_prisky (load_insn, bb_src, bb_trg)
rtx load_insn;
int bb_src, bb_trg;
{
if (FED_BY_SPEC_LOAD (load_insn))
return 1;
if (LOG_LINKS (load_insn) == NULL)
/* dependence may 'hide' out of the region. */
return 1;
if (is_conditionally_protected (load_insn, bb_src, bb_trg))
return 1;
return 0;
} /* is_prisky */
/* Insn is a candidate to be moved speculatively from bb_src to bb_trg.
Return 1 if insn is exception-free (and the motion is valid)
and 0 otherwise. */
static int
is_exception_free (insn, bb_src, bb_trg)
rtx insn;
int bb_src, bb_trg;
{
int insn_class = haifa_classify_insn (insn);
/* handle non-load insns */
switch (insn_class)
{
case TRAP_FREE:
return 1;
case TRAP_RISKY:
return 0;
default:;
}
/* handle loads */
if (!flag_schedule_speculative_load)
return 0;
IS_LOAD_INSN (insn) = 1;
switch (insn_class)
{
case IFREE:
return (1);
case IRISKY:
return 0;
case PFREE_CANDIDATE:
if (is_pfree (insn, bb_src, bb_trg))
return 1;
/* don't 'break' here: PFREE-candidate is also PRISKY-candidate */
case PRISKY_CANDIDATE:
if (!flag_schedule_speculative_load_dangerous
|| is_prisky (insn, bb_src, bb_trg))
return 0;
break;
default:;
}
return flag_schedule_speculative_load_dangerous;
} /* is_exception_free */
/* Process an insn's memory dependencies. There are four kinds of
dependencies:
(0) read dependence: read follows read
(1) true dependence: read follows write
(2) anti dependence: write follows read
(3) output dependence: write follows write
We are careful to build only dependencies which actually exist, and
use transitivity to avoid building too many links. */
/* Return the INSN_LIST containing INSN in LIST, or NULL
if LIST does not contain INSN. */
HAIFA_INLINE static rtx
find_insn_list (insn, list)
rtx insn;
rtx list;
{
while (list)
{
if (XEXP (list, 0) == insn)
return list;
list = XEXP (list, 1);
}
return 0;
}
/* Return 1 if the pair (insn, x) is found in (LIST, LIST1), or 0 otherwise. */
HAIFA_INLINE static char
find_insn_mem_list (insn, x, list, list1)
rtx insn, x;
rtx list, list1;
{
while (list)
{
if (XEXP (list, 0) == insn
&& XEXP (list1, 0) == x)
return 1;
list = XEXP (list, 1);
list1 = XEXP (list1, 1);
}
return 0;
}
/* Compute the function units used by INSN. This caches the value
returned by function_units_used. A function unit is encoded as the
unit number if the value is non-negative and the compliment of a
mask if the value is negative. A function unit index is the
non-negative encoding. */
HAIFA_INLINE static int
insn_unit (insn)
rtx insn;
{
register int unit = INSN_UNIT (insn);
if (unit == 0)
{
recog_memoized (insn);
/* A USE insn, or something else we don't need to understand.
We can't pass these directly to function_units_used because it will
trigger a fatal error for unrecognizable insns. */
if (INSN_CODE (insn) < 0)
unit = -1;
else
{
unit = function_units_used (insn);
/* Increment non-negative values so we can cache zero. */
if (unit >= 0)
unit++;
}
/* We only cache 16 bits of the result, so if the value is out of
range, don't cache it. */
if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT
|| unit >= 0
- || (~unit & ((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
+ || (unit & ~((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
INSN_UNIT (insn) = unit;
}
return (unit > 0 ? unit - 1 : unit);
}
/* Compute the blockage range for executing INSN on UNIT. This caches
the value returned by the blockage_range_function for the unit.
These values are encoded in an int where the upper half gives the
minimum value and the lower half gives the maximum value. */
HAIFA_INLINE static unsigned int
blockage_range (unit, insn)
int unit;
rtx insn;
{
unsigned int blockage = INSN_BLOCKAGE (insn);
unsigned int range;
if ((int) UNIT_BLOCKED (blockage) != unit + 1)
{
range = function_units[unit].blockage_range_function (insn);
/* We only cache the blockage range for one unit and then only if
the values fit. */
if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS)
INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range);
}
else
range = BLOCKAGE_RANGE (blockage);
return range;
}
/* A vector indexed by function unit instance giving the last insn to use
the unit. The value of the function unit instance index for unit U
instance I is (U + I * FUNCTION_UNITS_SIZE). */
static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
/* A vector indexed by function unit instance giving the minimum time when
the unit will unblock based on the maximum blockage cost. */
static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
/* A vector indexed by function unit number giving the number of insns
that remain to use the unit. */
static int unit_n_insns[FUNCTION_UNITS_SIZE];
/* Reset the function unit state to the null state. */
static void
clear_units ()
{
bzero ((char *) unit_last_insn, sizeof (unit_last_insn));
bzero ((char *) unit_tick, sizeof (unit_tick));
bzero ((char *) unit_n_insns, sizeof (unit_n_insns));
}
/* Return the issue-delay of an insn */
HAIFA_INLINE static int
insn_issue_delay (insn)
rtx insn;
{
int i, delay = 0;
int unit = insn_unit (insn);
/* efficiency note: in fact, we are working 'hard' to compute a
value that was available in md file, and is not available in
function_units[] structure. It would be nice to have this
value there, too. */
if (unit >= 0)
{
if (function_units[unit].blockage_range_function &&
function_units[unit].blockage_function)
delay = function_units[unit].blockage_function (insn, insn);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0 && function_units[i].blockage_range_function
&& function_units[i].blockage_function)
delay = MAX (delay, function_units[i].blockage_function (insn, insn));
return delay;
}
/* Return the actual hazard cost of executing INSN on the unit UNIT,
instance INSTANCE at time CLOCK if the previous actual hazard cost
was COST. */
HAIFA_INLINE static int
actual_hazard_this_instance (unit, instance, insn, clock, cost)
int unit, instance, clock, cost;
rtx insn;
{
int tick = unit_tick[instance]; /* issue time of the last issued insn */
if (tick - clock > cost)
{
/* The scheduler is operating forward, so unit's last insn is the
executing insn and INSN is the candidate insn. We want a
more exact measure of the blockage if we execute INSN at CLOCK
given when we committed the execution of the unit's last insn.
The blockage value is given by either the unit's max blockage
constant, blockage range function, or blockage function. Use
the most exact form for the given unit. */
if (function_units[unit].blockage_range_function)
{
if (function_units[unit].blockage_function)
tick += (function_units[unit].blockage_function
(unit_last_insn[instance], insn)
- function_units[unit].max_blockage);
else
tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn))
- function_units[unit].max_blockage);
}
if (tick - clock > cost)
cost = tick - clock;
}
return cost;
}
/* Record INSN as having begun execution on the units encoded by UNIT at
time CLOCK. */
HAIFA_INLINE static void
schedule_unit (unit, insn, clock)
int unit, clock;
rtx insn;
{
int i;
if (unit >= 0)
{
int instance = unit;
#if MAX_MULTIPLICITY > 1
/* Find the first free instance of the function unit and use that
one. We assume that one is free. */
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
if (!actual_hazard_this_instance (unit, instance, insn, clock, 0))
break;
instance += FUNCTION_UNITS_SIZE;
}
#endif
unit_last_insn[instance] = insn;
unit_tick[instance] = (clock + function_units[unit].max_blockage);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
schedule_unit (i, insn, clock);
}
/* Return the actual hazard cost of executing INSN on the units encoded by
UNIT at time CLOCK if the previous actual hazard cost was COST. */
HAIFA_INLINE static int
actual_hazard (unit, insn, clock, cost)
int unit, clock, cost;
rtx insn;
{
int i;
if (unit >= 0)
{
/* Find the instance of the function unit with the minimum hazard. */
int instance = unit;
int best_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
int this_cost;
#if MAX_MULTIPLICITY > 1
if (best_cost > cost)
{
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
instance += FUNCTION_UNITS_SIZE;
this_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
if (this_cost < best_cost)
{
best_cost = this_cost;
if (this_cost <= cost)
break;
}
}
}
#endif
cost = MAX (cost, best_cost);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
cost = actual_hazard (i, insn, clock, cost);
return cost;
}
/* Return the potential hazard cost of executing an instruction on the
units encoded by UNIT if the previous potential hazard cost was COST.
An insn with a large blockage time is chosen in preference to one
with a smaller time; an insn that uses a unit that is more likely
to be used is chosen in preference to one with a unit that is less
used. We are trying to minimize a subsequent actual hazard. */
HAIFA_INLINE static int
potential_hazard (unit, insn, cost)
int unit, cost;
rtx insn;
{
int i, ncost;
unsigned int minb, maxb;
if (unit >= 0)
{
minb = maxb = function_units[unit].max_blockage;
if (maxb > 1)
{
if (function_units[unit].blockage_range_function)
{
maxb = minb = blockage_range (unit, insn);
maxb = MAX_BLOCKAGE_COST (maxb);
minb = MIN_BLOCKAGE_COST (minb);
}
if (maxb > 1)
{
/* Make the number of instructions left dominate. Make the
minimum delay dominate the maximum delay. If all these
are the same, use the unit number to add an arbitrary
ordering. Other terms can be added. */
ncost = minb * 0x40 + maxb;
ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit;
if (ncost > cost)
cost = ncost;
}
}
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
cost = potential_hazard (i, insn, cost);
return cost;
}
/* Compute cost of executing INSN given the dependence LINK on the insn USED.
This is the number of cycles between instruction issue and
instruction results. */
HAIFA_INLINE static int
insn_cost (insn, link, used)
rtx insn, link, used;
{
register int cost = INSN_COST (insn);
if (cost == 0)
{
recog_memoized (insn);
/* A USE insn, or something else we don't need to understand.
We can't pass these directly to result_ready_cost because it will
trigger a fatal error for unrecognizable insns. */
if (INSN_CODE (insn) < 0)
{
INSN_COST (insn) = 1;
return 1;
}
else
{
cost = result_ready_cost (insn);
if (cost < 1)
cost = 1;
INSN_COST (insn) = cost;
}
}
/* in this case estimate cost without caring how insn is used. */
if (link == 0 && used == 0)
return cost;
/* A USE insn should never require the value used to be computed. This
allows the computation of a function's result and parameter values to
overlap the return and call. */
recog_memoized (used);
if (INSN_CODE (used) < 0)
LINK_COST_FREE (link) = 1;
/* If some dependencies vary the cost, compute the adjustment. Most
commonly, the adjustment is complete: either the cost is ignored
(in the case of an output- or anti-dependence), or the cost is
unchanged. These values are cached in the link as LINK_COST_FREE
and LINK_COST_ZERO. */
if (LINK_COST_FREE (link))
cost = 1;
#ifdef ADJUST_COST
else if (!LINK_COST_ZERO (link))
{
int ncost = cost;
ADJUST_COST (used, link, insn, ncost);
if (ncost <= 1)
LINK_COST_FREE (link) = ncost = 1;
if (cost == ncost)
LINK_COST_ZERO (link) = 1;
cost = ncost;
}
#endif
return cost;
}
/* Compute the priority number for INSN. */
static int
priority (insn)
rtx insn;
{
int this_priority;
rtx link;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
return 0;
if ((this_priority = INSN_PRIORITY (insn)) == 0)
{
if (INSN_DEPEND (insn) == 0)
this_priority = insn_cost (insn, 0, 0);
else
for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
{
rtx next;
int next_priority;
if (RTX_INTEGRATED_P (link))
continue;
next = XEXP (link, 0);
/* critical path is meaningful in block boundaries only */
if (INSN_BLOCK (next) != INSN_BLOCK (insn))
continue;
next_priority = insn_cost (insn, link, next) + priority (next);
if (next_priority > this_priority)
this_priority = next_priority;
}
INSN_PRIORITY (insn) = this_priority;
}
return this_priority;
}
/* Remove all INSN_LISTs and EXPR_LISTs from the pending lists and add
them to the unused_*_list variables, so that they can be reused. */
static void
free_pending_lists ()
{
if (current_nr_blocks <= 1)
{
free_list (&pending_read_insns, &unused_insn_list);
free_list (&pending_write_insns, &unused_insn_list);
free_list (&pending_read_mems, &unused_expr_list);
free_list (&pending_write_mems, &unused_expr_list);
}
else
{
/* interblock scheduling */
int bb;
for (bb = 0; bb < current_nr_blocks; bb++)
{
free_list (&bb_pending_read_insns[bb], &unused_insn_list);
free_list (&bb_pending_write_insns[bb], &unused_insn_list);
free_list (&bb_pending_read_mems[bb], &unused_expr_list);
free_list (&bb_pending_write_mems[bb], &unused_expr_list);
}
}
}
/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST.
The MEM is a memory reference contained within INSN, which we are saving
so that we can do memory aliasing on it. */
static void
add_insn_mem_dependence (insn_list, mem_list, insn, mem)
rtx *insn_list, *mem_list, insn, mem;
{
register rtx link;
link = alloc_INSN_LIST (insn, *insn_list);
*insn_list = link;
link = alloc_EXPR_LIST (VOIDmode, mem, *mem_list);
*mem_list = link;
pending_lists_length++;
}
/* Make a dependency between every memory reference on the pending lists
and INSN, thus flushing the pending lists. If ONLY_WRITE, don't flush
the read list. */
static void
flush_pending_lists (insn, only_write)
rtx insn;
int only_write;
{
rtx u;
rtx link;
while (pending_read_insns && ! only_write)
{
add_dependence (insn, XEXP (pending_read_insns, 0), REG_DEP_ANTI);
link = pending_read_insns;
pending_read_insns = XEXP (pending_read_insns, 1);
XEXP (link, 1) = unused_insn_list;
unused_insn_list = link;
link = pending_read_mems;
pending_read_mems = XEXP (pending_read_mems, 1);
XEXP (link, 1) = unused_expr_list;
unused_expr_list = link;
}
while (pending_write_insns)
{
add_dependence (insn, XEXP (pending_write_insns, 0), REG_DEP_ANTI);
link = pending_write_insns;
pending_write_insns = XEXP (pending_write_insns, 1);
XEXP (link, 1) = unused_insn_list;
unused_insn_list = link;
link = pending_write_mems;
pending_write_mems = XEXP (pending_write_mems, 1);
XEXP (link, 1) = unused_expr_list;
unused_expr_list = link;
}
pending_lists_length = 0;
/* last_pending_memory_flush is now a list of insns */
for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
free_list (&last_pending_memory_flush, &unused_insn_list);
last_pending_memory_flush = alloc_INSN_LIST (insn, NULL_RTX);
}
/* Analyze a single SET or CLOBBER rtx, X, creating all dependencies generated
by the write to the destination of X, and reads of everything mentioned. */
static void
sched_analyze_1 (x, insn)
rtx x;
rtx insn;
{
register int regno;
register rtx dest = SET_DEST (x);
enum rtx_code code = GET_CODE (x);
if (dest == 0)
return;
if (GET_CODE (dest) == PARALLEL
&& GET_MODE (dest) == BLKmode)
{
register int i;
for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
sched_analyze_1 (XVECEXP (dest, 0, i), insn);
if (GET_CODE (x) == SET)
sched_analyze_2 (SET_SRC (x), insn);
return;
}
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
/* The second and third arguments are values read by this insn. */
sched_analyze_2 (XEXP (dest, 1), insn);
sched_analyze_2 (XEXP (dest, 2), insn);
}
dest = SUBREG_REG (dest);
}
if (GET_CODE (dest) == REG)
{
register int i;
regno = REGNO (dest);
/* A hard reg in a wide mode may really be multiple registers.
If so, mark all of them just like the first. */
if (regno < FIRST_PSEUDO_REGISTER)
{
i = HARD_REGNO_NREGS (regno, GET_MODE (dest));
while (--i >= 0)
{
rtx u;
for (u = reg_last_uses[regno + i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
for (u = reg_last_sets[regno + i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT);
/* Clobbers need not be ordered with respect to one another,
but sets must be ordered with respect to a pending clobber. */
if (code == SET)
{
reg_last_uses[regno + i] = 0;
for (u = reg_last_clobbers[regno + i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT);
SET_REGNO_REG_SET (reg_pending_sets, regno + i);
}
else
SET_REGNO_REG_SET (reg_pending_clobbers, regno + i);
/* Function calls clobber all call_used regs. */
if (global_regs[regno + i]
|| (code == SET && call_used_regs[regno + i]))
for (u = last_function_call; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
}
}
else
{
rtx u;
for (u = reg_last_uses[regno]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
for (u = reg_last_sets[regno]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT);
if (code == SET)
{
reg_last_uses[regno] = 0;
for (u = reg_last_clobbers[regno]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT);
SET_REGNO_REG_SET (reg_pending_sets, regno);
}
else
SET_REGNO_REG_SET (reg_pending_clobbers, regno);
/* Pseudos that are REG_EQUIV to something may be replaced
by that during reloading. We need only add dependencies for
the address in the REG_EQUIV note. */
if (!reload_completed
&& reg_known_equiv_p[regno]
&& GET_CODE (reg_known_value[regno]) == MEM)
sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
/* Don't let it cross a call after scheduling if it doesn't
already cross one. */
if (REG_N_CALLS_CROSSED (regno) == 0)
for (u = last_function_call; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
}
}
else if (GET_CODE (dest) == MEM)
{
/* Writing memory. */
if (pending_lists_length > 32)
{
/* Flush all pending reads and writes to prevent the pending lists
from getting any larger. Insn scheduling runs too slowly when
these lists get long. The number 32 was chosen because it
seems like a reasonable number. When compiling GCC with itself,
this flush occurs 8 times for sparc, and 10 times for m88k using
the number 32. */
flush_pending_lists (insn, 0);
}
else
{
rtx u;
rtx pending, pending_mem;
pending = pending_read_insns;
pending_mem = pending_read_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (anti_dependence (XEXP (pending_mem, 0), dest))
add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
pending = pending_write_insns;
pending_mem = pending_write_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (output_dependence (XEXP (pending_mem, 0), dest))
add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
add_insn_mem_dependence (&pending_write_insns, &pending_write_mems,
insn, dest);
}
sched_analyze_2 (XEXP (dest, 0), insn);
}
/* Analyze reads. */
if (GET_CODE (x) == SET)
sched_analyze_2 (SET_SRC (x), insn);
}
/* Analyze the uses of memory and registers in rtx X in INSN. */
static void
sched_analyze_2 (x, insn)
rtx x;
rtx insn;
{
register int i;
register int j;
register enum rtx_code code;
register char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
/* Ignore constants. Note that we must handle CONST_DOUBLE here
because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but
this does not mean that this insn is using cc0. */
return;
#ifdef HAVE_cc0
case CC0:
{
rtx link, prev;
/* User of CC0 depends on immediately preceding insn. */
SCHED_GROUP_P (insn) = 1;
/* There may be a note before this insn now, but all notes will
be removed before we actually try to schedule the insns, so
it won't cause a problem later. We must avoid it here though. */
prev = prev_nonnote_insn (insn);
/* Make a copy of all dependencies on the immediately previous insn,
and add to this insn. This is so that all the dependencies will
apply to the group. Remove an explicit dependence on this insn
as SCHED_GROUP_P now represents it. */
if (find_insn_list (prev, LOG_LINKS (insn)))
remove_dependence (insn, prev);
for (link = LOG_LINKS (prev); link; link = XEXP (link, 1))
add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
return;
}
#endif
case REG:
{
rtx u;
int regno = REGNO (x);
if (regno < FIRST_PSEUDO_REGISTER)
{
int i;
i = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--i >= 0)
{
reg_last_uses[regno + i]
= alloc_INSN_LIST (insn, reg_last_uses[regno + i]);
for (u = reg_last_sets[regno + i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
/* ??? This should never happen. */
for (u = reg_last_clobbers[regno + i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
if ((call_used_regs[regno + i] || global_regs[regno + i]))
/* Function calls clobber all call_used regs. */
for (u = last_function_call; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
}
}
else
{
reg_last_uses[regno] = alloc_INSN_LIST (insn, reg_last_uses[regno]);
for (u = reg_last_sets[regno]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
/* ??? This should never happen. */
for (u = reg_last_clobbers[regno]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
/* Pseudos that are REG_EQUIV to something may be replaced
by that during reloading. We need only add dependencies for
the address in the REG_EQUIV note. */
if (!reload_completed
&& reg_known_equiv_p[regno]
&& GET_CODE (reg_known_value[regno]) == MEM)
sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
/* If the register does not already cross any calls, then add this
insn to the sched_before_next_call list so that it will still
not cross calls after scheduling. */
if (REG_N_CALLS_CROSSED (regno) == 0)
add_dependence (sched_before_next_call, insn, REG_DEP_ANTI);
}
return;
}
case MEM:
{
/* Reading memory. */
rtx u;
rtx pending, pending_mem;
pending = pending_read_insns;
pending_mem = pending_read_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (read_dependence (XEXP (pending_mem, 0), x))
add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
pending = pending_write_insns;
pending_mem = pending_write_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (true_dependence (XEXP (pending_mem, 0), VOIDmode,
x, rtx_varies_p))
add_dependence (insn, XEXP (pending, 0), 0);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
/* Always add these dependencies to pending_reads, since
this insn may be followed by a write. */
add_insn_mem_dependence (&pending_read_insns, &pending_read_mems,
insn, x);
/* Take advantage of tail recursion here. */
sched_analyze_2 (XEXP (x, 0), insn);
return;
}
/* Force pending stores to memory in case a trap handler needs them. */
case TRAP_IF:
flush_pending_lists (insn, 1);
break;
case ASM_OPERANDS:
case ASM_INPUT:
case UNSPEC_VOLATILE:
{
rtx u;
/* Traditional and volatile asm instructions must be considered to use
and clobber all hard registers, all pseudo-registers and all of
memory. So must TRAP_IF and UNSPEC_VOLATILE operations.
Consider for instance a volatile asm that changes the fpu rounding
mode. An insn should not be moved across this even if it only uses
pseudo-regs because it might give an incorrectly rounded result. */
if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
{
int max_reg = max_reg_num ();
for (i = 0; i < max_reg; i++)
{
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
for (u = reg_last_clobbers[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
}
reg_pending_sets_all = 1;
flush_pending_lists (insn, 0);
}
/* For all ASM_OPERANDS, we must traverse the vector of input operands.
We can not just fall through here since then we would be confused
by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
traditional asms unlike their normal usage. */
if (code == ASM_OPERANDS)
{
for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
sched_analyze_2 (ASM_OPERANDS_INPUT (x, j), insn);
return;
}
break;
}
case PRE_DEC:
case POST_DEC:
case PRE_INC:
case POST_INC:
/* These both read and modify the result. We must handle them as writes
to get proper dependencies for following instructions. We must handle
them as reads to get proper dependencies from this to previous
instructions. Thus we need to pass them to both sched_analyze_1
and sched_analyze_2. We must call sched_analyze_2 first in order
to get the proper antecedent for the read. */
sched_analyze_2 (XEXP (x, 0), insn);
sched_analyze_1 (x, insn);
return;
default:
break;
}
/* Other cases: walk the insn. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
sched_analyze_2 (XEXP (x, i), insn);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
sched_analyze_2 (XVECEXP (x, i, j), insn);
}
}
/* Analyze an INSN with pattern X to find all dependencies. */
static void
sched_analyze_insn (x, insn, loop_notes)
rtx x, insn;
rtx loop_notes;
{
register RTX_CODE code = GET_CODE (x);
rtx link;
int maxreg = max_reg_num ();
int i;
if (code == SET || code == CLOBBER)
sched_analyze_1 (x, insn);
else if (code == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
sched_analyze_1 (XVECEXP (x, 0, i), insn);
else
sched_analyze_2 (XVECEXP (x, 0, i), insn);
}
}
else
sched_analyze_2 (x, insn);
/* Mark registers CLOBBERED or used by called function. */
if (GET_CODE (insn) == CALL_INSN)
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
{
if (GET_CODE (XEXP (link, 0)) == CLOBBER)
sched_analyze_1 (XEXP (link, 0), insn);
else
sched_analyze_2 (XEXP (link, 0), insn);
}
/* If there is a {LOOP,EHREGION}_{BEG,END} note in the middle of a basic
block, then we must be sure that no instructions are scheduled across it.
Otherwise, the reg_n_refs info (which depends on loop_depth) would
become incorrect. */
if (loop_notes)
{
int max_reg = max_reg_num ();
int schedule_barrier_found = 0;
rtx link;
/* Update loop_notes with any notes from this insn. Also determine
if any of the notes on the list correspond to instruction scheduling
barriers (loop, eh & setjmp notes, but not range notes. */
link = loop_notes;
while (XEXP (link, 1))
{
if (INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_BEG
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_END
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_BEG
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_END
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_SETJMP)
schedule_barrier_found = 1;
link = XEXP (link, 1);
}
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = loop_notes;
/* Add dependencies if a scheduling barrier was found. */
if (schedule_barrier_found)
{
for (i = 0; i < max_reg; i++)
{
rtx u;
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
for (u = reg_last_clobbers[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
}
reg_pending_sets_all = 1;
flush_pending_lists (insn, 0);
}
}
/* Accumulate clobbers until the next set so that it will be output dependant
on all of them. At the next set we can clear the clobber list, since
subsequent sets will be output dependant on it. */
EXECUTE_IF_SET_IN_REG_SET (reg_pending_sets, 0, i,
{
free_list (&reg_last_sets[i], &unused_insn_list);
free_list (&reg_last_clobbers[i],
&unused_insn_list);
reg_last_sets[i]
= alloc_INSN_LIST (insn, NULL_RTX);
});
EXECUTE_IF_SET_IN_REG_SET (reg_pending_clobbers, 0, i,
{
reg_last_clobbers[i]
= alloc_INSN_LIST (insn, reg_last_clobbers[i]);
});
CLEAR_REG_SET (reg_pending_sets);
CLEAR_REG_SET (reg_pending_clobbers);
if (reg_pending_sets_all)
{
for (i = 0; i < maxreg; i++)
{
free_list (&reg_last_sets[i], &unused_insn_list);
reg_last_sets[i] = alloc_INSN_LIST (insn, NULL_RTX);
}
reg_pending_sets_all = 0;
}
/* Handle function calls and function returns created by the epilogue
threading code. */
if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
{
rtx dep_insn;
rtx prev_dep_insn;
/* When scheduling instructions, we make sure calls don't lose their
accompanying USE insns by depending them one on another in order.
Also, we must do the same thing for returns created by the epilogue
threading code. Note this code works only in this special case,
because other passes make no guarantee that they will never emit
an instruction between a USE and a RETURN. There is such a guarantee
for USE instructions immediately before a call. */
prev_dep_insn = insn;
dep_insn = PREV_INSN (insn);
while (GET_CODE (dep_insn) == INSN
&& GET_CODE (PATTERN (dep_insn)) == USE
&& GET_CODE (XEXP (PATTERN (dep_insn), 0)) == REG)
{
SCHED_GROUP_P (prev_dep_insn) = 1;
/* Make a copy of all dependencies on dep_insn, and add to insn.
This is so that all of the dependencies will apply to the
group. */
for (link = LOG_LINKS (dep_insn); link; link = XEXP (link, 1))
add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
prev_dep_insn = dep_insn;
dep_insn = PREV_INSN (dep_insn);
}
}
}
/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS
for every dependency. */
static void
sched_analyze (head, tail)
rtx head, tail;
{
register rtx insn;
register rtx u;
rtx loop_notes = 0;
for (insn = head;; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
{
/* Make each JUMP_INSN a scheduling barrier for memory references. */
if (GET_CODE (insn) == JUMP_INSN)
last_pending_memory_flush
= alloc_INSN_LIST (insn, last_pending_memory_flush);
sched_analyze_insn (PATTERN (insn), insn, loop_notes);
loop_notes = 0;
}
else if (GET_CODE (insn) == CALL_INSN)
{
rtx x;
register int i;
CANT_MOVE (insn) = 1;
/* Any instruction using a hard register which may get clobbered
by a call needs to be marked as dependent on this call.
This prevents a use of a hard return reg from being moved
past a void call (i.e. it does not explicitly set the hard
return reg). */
/* If this call is followed by a NOTE_INSN_SETJMP, then assume that
all registers, not just hard registers, may be clobbered by this
call. */
/* Insn, being a CALL_INSN, magically depends on
`last_function_call' already. */
if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE
&& NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP)
{
int max_reg = max_reg_num ();
for (i = 0; i < max_reg; i++)
{
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
for (u = reg_last_clobbers[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), 0);
}
reg_pending_sets_all = 1;
/* Add a pair of fake REG_NOTE which we will later
convert back into a NOTE_INSN_SETJMP note. See
reemit_notes for why we use a pair of NOTEs. */
REG_NOTES (insn) = alloc_EXPR_LIST (REG_DEAD,
GEN_INT (0),
REG_NOTES (insn));
REG_NOTES (insn) = alloc_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_INSN_SETJMP),
REG_NOTES (insn));
}
else
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i] || global_regs[i])
{
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
SET_REGNO_REG_SET (reg_pending_clobbers, i);
}
}
/* For each insn which shouldn't cross a call, add a dependence
between that insn and this call insn. */
x = LOG_LINKS (sched_before_next_call);
while (x)
{
add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI);
x = XEXP (x, 1);
}
LOG_LINKS (sched_before_next_call) = 0;
sched_analyze_insn (PATTERN (insn), insn, loop_notes);
loop_notes = 0;
/* In the absence of interprocedural alias analysis, we must flush
all pending reads and writes, and start new dependencies starting
from here. But only flush writes for constant calls (which may
be passed a pointer to something we haven't written yet). */
flush_pending_lists (insn, CONST_CALL_P (insn));
/* Depend this function call (actually, the user of this
function call) on all hard register clobberage. */
/* last_function_call is now a list of insns */
free_list(&last_function_call, &unused_insn_list);
last_function_call = alloc_INSN_LIST (insn, NULL_RTX);
}
/* See comments on reemit_notes as to why we do this. */
/* ??? Actually, the reemit_notes just say what is done, not why. */
else if (GET_CODE (insn) == NOTE
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_START
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END))
{
loop_notes = alloc_EXPR_LIST (REG_DEAD, NOTE_RANGE_INFO (insn),
loop_notes);
loop_notes = alloc_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_LINE_NUMBER (insn)),
loop_notes);
}
else if (GET_CODE (insn) == NOTE
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END
|| (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP
&& GET_CODE (PREV_INSN (insn)) != CALL_INSN)))
{
loop_notes = alloc_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_BLOCK_NUMBER (insn)),
loop_notes);
loop_notes = alloc_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_LINE_NUMBER (insn)),
loop_notes);
CONST_CALL_P (loop_notes) = CONST_CALL_P (insn);
}
if (insn == tail)
return;
}
abort ();
}
/* Called when we see a set of a register. If death is true, then we are
scanning backwards. Mark that register as unborn. If nobody says
otherwise, that is how things will remain. If death is false, then we
are scanning forwards. Mark that register as being born. */
static void
sched_note_set (x, death)
rtx x;
int death;
{
register int regno;
register rtx reg = SET_DEST (x);
int subreg_p = 0;
if (reg == 0)
return;
if (GET_CODE (reg) == PARALLEL
&& GET_MODE (reg) == BLKmode)
{
register int i;
for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
sched_note_set (XVECEXP (reg, 0, i), death);
return;
}
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART
|| GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT)
{
/* Must treat modification of just one hardware register of a multi-reg
value or just a byte field of a register exactly the same way that
mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg
does not kill the entire register. */
if (GET_CODE (reg) != SUBREG
|| REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg))
subreg_p = 1;
reg = SUBREG_REG (reg);
}
if (GET_CODE (reg) != REG)
return;
/* Global registers are always live, so the code below does not apply
to them. */
regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER || !global_regs[regno])
{
if (death)
{
/* If we only set part of the register, then this set does not
kill it. */
if (subreg_p)
return;
/* Try killing this register. */
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
/* Recompute REG_BASIC_BLOCK as we update all the other
dataflow information. */
if (sched_reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
sched_reg_basic_block[regno] = current_block_num;
else if (sched_reg_basic_block[regno] != current_block_num)
sched_reg_basic_block[regno] = REG_BLOCK_GLOBAL;
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
}
}
else
{
/* Make the register live again. */
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
SET_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
SET_REGNO_REG_SET (bb_live_regs, regno);
}
}
}
}
/* Macros and functions for keeping the priority queue sorted, and
dealing with queueing and dequeueing of instructions. */
#define SCHED_SORT(READY, N_READY) \
do { if ((N_READY) == 2) \
swap_sort (READY, N_READY); \
else if ((N_READY) > 2) \
qsort (READY, N_READY, sizeof (rtx), rank_for_schedule); } \
while (0)
/* Returns a positive value if x is preferred; returns a negative value if
y is preferred. Should never return 0, since that will make the sort
unstable. */
static int
rank_for_schedule (x, y)
const GENERIC_PTR x;
const GENERIC_PTR y;
{
rtx tmp = *(rtx *)y;
rtx tmp2 = *(rtx *)x;
rtx link;
int tmp_class, tmp2_class, depend_count1, depend_count2;
int val, priority_val, spec_val, prob_val, weight_val;
/* prefer insn with higher priority */
priority_val = INSN_PRIORITY (tmp2) - INSN_PRIORITY (tmp);
if (priority_val)
return priority_val;
/* prefer an insn with smaller contribution to registers-pressure */
if (!reload_completed &&
(weight_val = INSN_REG_WEIGHT (tmp) - INSN_REG_WEIGHT (tmp2)))
return (weight_val);
/* some comparison make sense in interblock scheduling only */
if (INSN_BB (tmp) != INSN_BB (tmp2))
{
/* prefer an inblock motion on an interblock motion */
if ((INSN_BB (tmp2) == target_bb) && (INSN_BB (tmp) != target_bb))
return 1;
if ((INSN_BB (tmp) == target_bb) && (INSN_BB (tmp2) != target_bb))
return -1;
/* prefer a useful motion on a speculative one */
if ((spec_val = IS_SPECULATIVE_INSN (tmp) - IS_SPECULATIVE_INSN (tmp2)))
return (spec_val);
/* prefer a more probable (speculative) insn */
prob_val = INSN_PROBABILITY (tmp2) - INSN_PROBABILITY (tmp);
if (prob_val)
return (prob_val);
}
/* compare insns based on their relation to the last-scheduled-insn */
if (last_scheduled_insn)
{
/* Classify the instructions into three classes:
1) Data dependent on last schedule insn.
2) Anti/Output dependent on last scheduled insn.
3) Independent of last scheduled insn, or has latency of one.
Choose the insn from the highest numbered class if different. */
link = find_insn_list (tmp, INSN_DEPEND (last_scheduled_insn));
if (link == 0 || insn_cost (last_scheduled_insn, link, tmp) == 1)
tmp_class = 3;
else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
tmp_class = 1;
else
tmp_class = 2;
link = find_insn_list (tmp2, INSN_DEPEND (last_scheduled_insn));
if (link == 0 || insn_cost (last_scheduled_insn, link, tmp2) == 1)
tmp2_class = 3;
else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
tmp2_class = 1;
else
tmp2_class = 2;
if ((val = tmp2_class - tmp_class))
return val;
}
/* Prefer the insn which has more later insns that depend on it.
This gives the scheduler more freedom when scheduling later
instructions at the expense of added register pressure. */
depend_count1 = 0;
for (link = INSN_DEPEND (tmp); link; link = XEXP (link, 1))
depend_count1++;
depend_count2 = 0;
for (link = INSN_DEPEND (tmp2); link; link = XEXP (link, 1))
depend_count2++;
val = depend_count2 - depend_count1;
if (val)
return val;
/* If insns are equally good, sort by INSN_LUID (original insn order),
so that we make the sort stable. This minimizes instruction movement,
thus minimizing sched's effect on debugging and cross-jumping. */
return INSN_LUID (tmp) - INSN_LUID (tmp2);
}
/* Resort the array A in which only element at index N may be out of order. */
HAIFA_INLINE static void
swap_sort (a, n)
rtx *a;
int n;
{
rtx insn = a[n - 1];
int i = n - 2;
while (i >= 0 && rank_for_schedule (a + i, &insn) >= 0)
{
a[i + 1] = a[i];
i -= 1;
}
a[i + 1] = insn;
}
static int max_priority;
/* Add INSN to the insn queue so that it can be executed at least
N_CYCLES after the currently executing insn. Preserve insns
chain for debugging purposes. */
HAIFA_INLINE static void
queue_insn (insn, n_cycles)
rtx insn;
int n_cycles;
{
int next_q = NEXT_Q_AFTER (q_ptr, n_cycles);
rtx link = alloc_INSN_LIST (insn, insn_queue[next_q]);
insn_queue[next_q] = link;
q_size += 1;
if (sched_verbose >= 2)
{
fprintf (dump, ";;\t\tReady-->Q: insn %d: ", INSN_UID (insn));
if (INSN_BB (insn) != target_bb)
fprintf (dump, "(b%d) ", INSN_BLOCK (insn));
fprintf (dump, "queued for %d cycles.\n", n_cycles);
}
}
/* Return nonzero if PAT is the pattern of an insn which makes a
register live. */
HAIFA_INLINE static int
birthing_insn_p (pat)
rtx pat;
{
int j;
if (reload_completed == 1)
return 0;
if (GET_CODE (pat) == SET
&& (GET_CODE (SET_DEST (pat)) == REG
|| (GET_CODE (SET_DEST (pat)) == PARALLEL
&& GET_MODE (SET_DEST (pat)) == BLKmode)))
{
rtx dest = SET_DEST (pat);
int i;
/* It would be more accurate to use refers_to_regno_p or
reg_mentioned_p to determine when the dest is not live before this
insn. */
if (GET_CODE (dest) == REG)
{
i = REGNO (dest);
if (REGNO_REG_SET_P (bb_live_regs, i))
return (REG_N_SETS (i) == 1);
}
else
{
for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
{
int regno = REGNO (SET_DEST (XVECEXP (dest, 0, i)));
if (REGNO_REG_SET_P (bb_live_regs, regno))
return (REG_N_SETS (regno) == 1);
}
}
return 0;
}
if (GET_CODE (pat) == PARALLEL)
{
for (j = 0; j < XVECLEN (pat, 0); j++)
if (birthing_insn_p (XVECEXP (pat, 0, j)))
return 1;
}
return 0;
}
/* PREV is an insn that is ready to execute. Adjust its priority if that
will help shorten register lifetimes. */
HAIFA_INLINE static void
adjust_priority (prev)
rtx prev;
{
/* Trying to shorten register lives after reload has completed
is useless and wrong. It gives inaccurate schedules. */
if (reload_completed == 0)
{
rtx note;
int n_deaths = 0;
/* ??? This code has no effect, because REG_DEAD notes are removed
before we ever get here. */
for (note = REG_NOTES (prev); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD)
n_deaths += 1;
/* Defer scheduling insns which kill registers, since that
shortens register lives. Prefer scheduling insns which
make registers live for the same reason. */
switch (n_deaths)
{
default:
INSN_PRIORITY (prev) >>= 3;
break;
case 3:
INSN_PRIORITY (prev) >>= 2;
break;
case 2:
case 1:
INSN_PRIORITY (prev) >>= 1;
break;
case 0:
if (birthing_insn_p (PATTERN (prev)))
{
int max = max_priority;
if (max > INSN_PRIORITY (prev))
INSN_PRIORITY (prev) = max;
}
break;
}
#ifdef ADJUST_PRIORITY
ADJUST_PRIORITY (prev);
#endif
}
}
/* Clock at which the previous instruction was issued. */
static int last_clock_var;
/* INSN is the "currently executing insn". Launch each insn which was
waiting on INSN. READY is a vector of insns which are ready to fire.
N_READY is the number of elements in READY. CLOCK is the current
cycle. */
static int
schedule_insn (insn, ready, n_ready, clock)
rtx insn;
rtx *ready;
int n_ready;
int clock;
{
rtx link;
int unit;
unit = insn_unit (insn);
if (sched_verbose >= 2)
{
fprintf (dump, ";;\t\t--> scheduling insn <<<%d>>> on unit ", INSN_UID (insn));
insn_print_units (insn);
fprintf (dump, "\n");
}
if (sched_verbose && unit == -1)
visualize_no_unit (insn);
if (MAX_BLOCKAGE > 1 || issue_rate > 1 || sched_verbose)
schedule_unit (unit, insn, clock);
if (INSN_DEPEND (insn) == 0)
return n_ready;
/* This is used by the function adjust_priority above. */
if (n_ready > 0)
max_priority = MAX (INSN_PRIORITY (ready[0]), INSN_PRIORITY (insn));
else
max_priority = INSN_PRIORITY (insn);
for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1))
{
rtx next = XEXP (link, 0);
int cost = insn_cost (insn, link, next);
INSN_TICK (next) = MAX (INSN_TICK (next), clock + cost);
if ((INSN_DEP_COUNT (next) -= 1) == 0)
{
int effective_cost = INSN_TICK (next) - clock;
/* For speculative insns, before inserting to ready/queue,
check live, exception-free, and issue-delay */
if (INSN_BB (next) != target_bb
&& (!IS_VALID (INSN_BB (next))
|| CANT_MOVE (next)
|| (IS_SPECULATIVE_INSN (next)
&& (insn_issue_delay (next) > 3
|| !check_live (next, INSN_BB (next))
|| !is_exception_free (next, INSN_BB (next), target_bb)))))
continue;
if (sched_verbose >= 2)
{
fprintf (dump, ";;\t\tdependences resolved: insn %d ", INSN_UID (next));
if (current_nr_blocks > 1 && INSN_BB (next) != target_bb)
fprintf (dump, "/b%d ", INSN_BLOCK (next));
if (effective_cost <= 1)
fprintf (dump, "into ready\n");
else
fprintf (dump, "into queue with cost=%d\n", effective_cost);
}
/* Adjust the priority of NEXT and either put it on the ready
list or queue it. */
adjust_priority (next);
if (effective_cost <= 1)
ready[n_ready++] = next;
else
queue_insn (next, effective_cost);
}
}
/* Annotate the instruction with issue information -- TImode
indicates that the instruction is expected not to be able
to issue on the same cycle as the previous insn. A machine
may use this information to decide how the instruction should
be aligned. */
if (reload_completed && issue_rate > 1)
{
PUT_MODE (insn, clock > last_clock_var ? TImode : VOIDmode);
last_clock_var = clock;
}
return n_ready;
}
/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the
dead_notes list. */
static void
create_reg_dead_note (reg, insn)
rtx reg, insn;
{
rtx link;
/* The number of registers killed after scheduling must be the same as the
number of registers killed before scheduling. The number of REG_DEAD
notes may not be conserved, i.e. two SImode hard register REG_DEAD notes
might become one DImode hard register REG_DEAD note, but the number of
registers killed will be conserved.
We carefully remove REG_DEAD notes from the dead_notes list, so that
there will be none left at the end. If we run out early, then there
is a bug somewhere in flow, combine and/or sched. */
if (dead_notes == 0)
{
if (current_nr_blocks <= 1)
abort ();
else
link = alloc_EXPR_LIST (REG_DEAD, NULL_RTX, NULL_RTX);
}
else
{
/* Number of regs killed by REG. */
int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
/* Number of regs killed by REG_DEAD notes taken off the list. */
int reg_note_regs;
link = dead_notes;
reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
GET_MODE (XEXP (link, 0))));
while (reg_note_regs < regs_killed)
{
link = XEXP (link, 1);
/* LINK might be zero if we killed more registers after scheduling
than before, and the last hard register we kill is actually
multiple hard regs.
This is normal for interblock scheduling, so deal with it in
that case, else abort. */
if (link == NULL_RTX && current_nr_blocks <= 1)
abort ();
else if (link == NULL_RTX)
link = alloc_EXPR_LIST (REG_DEAD, gen_rtx_REG (word_mode, 0),
NULL_RTX);
reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
GET_MODE (XEXP (link, 0))));
}
dead_notes = XEXP (link, 1);
/* If we took too many regs kills off, put the extra ones back. */
while (reg_note_regs > regs_killed)
{
rtx temp_reg, temp_link;
temp_reg = gen_rtx_REG (word_mode, 0);
temp_link = alloc_EXPR_LIST (REG_DEAD, temp_reg, dead_notes);
dead_notes = temp_link;
reg_note_regs--;
}
}
XEXP (link, 0) = reg;
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = link;
}
/* Subroutine on attach_deaths_insn--handles the recursive search
through INSN. If SET_P is true, then x is being modified by the insn. */
static void
attach_deaths (x, insn, set_p)
rtx x;
rtx insn;
int set_p;
{
register int i;
register int j;
register enum rtx_code code;
register char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST_DOUBLE:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
case CODE_LABEL:
case PC:
case CC0:
/* Get rid of the easy cases first. */
return;
case REG:
{
/* If the register dies in this insn, queue that note, and mark
this register as needing to die. */
/* This code is very similar to mark_used_1 (if set_p is false)
and mark_set_1 (if set_p is true) in flow.c. */
register int regno;
int some_needed;
int all_needed;
if (set_p)
return;
regno = REGNO (x);
all_needed = some_needed = REGNO_REG_SET_P (old_live_regs, regno);
if (regno < FIRST_PSEUDO_REGISTER)
{
int n;
n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n > 0)
{
int needed = (REGNO_REG_SET_P (old_live_regs, regno + n));
some_needed |= needed;
all_needed &= needed;
}
}
/* If it wasn't live before we started, then add a REG_DEAD note.
We must check the previous lifetime info not the current info,
because we may have to execute this code several times, e.g.
once for a clobber (which doesn't add a note) and later
for a use (which does add a note).
Always make the register live. We must do this even if it was
live before, because this may be an insn which sets and uses
the same register, in which case the register has already been
killed, so we must make it live again.
Global registers are always live, and should never have a REG_DEAD
note added for them, so none of the code below applies to them. */
if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
{
/* Never add REG_DEAD notes for STACK_POINTER_REGNUM
since it's always considered to be live. Similarly
for FRAME_POINTER_REGNUM if a frame pointer is needed
and for ARG_POINTER_REGNUM if it is fixed. */
if (! (regno == FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == HARD_FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
&& regno != STACK_POINTER_REGNUM)
{
if (! all_needed && ! dead_or_set_p (insn, x))
{
/* Check for the case where the register dying partially
overlaps the register set by this insn. */
if (regno < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
{
int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n >= 0)
some_needed |= dead_or_set_regno_p (insn, regno + n);
}
/* If none of the words in X is needed, make a REG_DEAD
note. Otherwise, we must make partial REG_DEAD
notes. */
if (! some_needed)
create_reg_dead_note (x, insn);
else
{
int i;
/* Don't make a REG_DEAD note for a part of a
register that is set in the insn. */
for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
i >= 0; i--)
if (! REGNO_REG_SET_P (old_live_regs, regno+i)
&& ! dead_or_set_regno_p (insn, regno + i))
create_reg_dead_note (gen_rtx_REG (reg_raw_mode[regno + i],
regno + i),
insn);
}
}
}
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--j >= 0)
{
SET_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
/* Recompute REG_BASIC_BLOCK as we update all the other
dataflow information. */
if (sched_reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
sched_reg_basic_block[regno] = current_block_num;
else if (sched_reg_basic_block[regno] != current_block_num)
sched_reg_basic_block[regno] = REG_BLOCK_GLOBAL;
SET_REGNO_REG_SET (bb_live_regs, regno);
}
}
return;
}
case MEM:
/* Handle tail-recursive case. */
attach_deaths (XEXP (x, 0), insn, 0);
return;
case SUBREG:
attach_deaths (SUBREG_REG (x), insn,
set_p && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
<= UNITS_PER_WORD)
|| (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
== GET_MODE_SIZE (GET_MODE ((x))))));
return;
case STRICT_LOW_PART:
attach_deaths (XEXP (x, 0), insn, 0);
return;
case ZERO_EXTRACT:
case SIGN_EXTRACT:
attach_deaths (XEXP (x, 0), insn, 0);
attach_deaths (XEXP (x, 1), insn, 0);
attach_deaths (XEXP (x, 2), insn, 0);
return;
case PARALLEL:
if (set_p
&& GET_MODE (x) == BLKmode)
{
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
return;
}
/* fallthrough */
default:
/* Other cases: walk the insn. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
attach_deaths (XEXP (x, i), insn, 0);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
attach_deaths (XVECEXP (x, i, j), insn, 0);
}
}
}
/* After INSN has executed, add register death notes for each register
that is dead after INSN. */
static void
attach_deaths_insn (insn)
rtx insn;
{
rtx x = PATTERN (insn);
register RTX_CODE code = GET_CODE (x);
rtx link;
if (code == SET)
{
attach_deaths (SET_SRC (x), insn, 0);
/* A register might die here even if it is the destination, e.g.
it is the target of a volatile read and is otherwise unused.
Hence we must always call attach_deaths for the SET_DEST. */
attach_deaths (SET_DEST (x), insn, 1);
}
else if (code == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET)
{
attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0);
attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
}
/* Flow does not add REG_DEAD notes to registers that die in
clobbers, so we can't either. */
else if (code != CLOBBER)
attach_deaths (XVECEXP (x, 0, i), insn, 0);
}
}
/* If this is a CLOBBER, only add REG_DEAD notes to registers inside a
MEM being clobbered, just like flow. */
else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == MEM)
attach_deaths (XEXP (XEXP (x, 0), 0), insn, 0);
/* Otherwise don't add a death note to things being clobbered. */
else if (code != CLOBBER)
attach_deaths (x, insn, 0);
/* Make death notes for things used in the called function. */
if (GET_CODE (insn) == CALL_INSN)
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
attach_deaths (XEXP (XEXP (link, 0), 0), insn,
GET_CODE (XEXP (link, 0)) == CLOBBER);
}
/* functions for handlnig of notes */
/* Delete notes beginning with INSN and put them in the chain
of notes ended by NOTE_LIST.
Returns the insn following the notes. */
static rtx
unlink_other_notes (insn, tail)
rtx insn, tail;
{
rtx prev = PREV_INSN (insn);
while (insn != tail && GET_CODE (insn) == NOTE)
{
rtx next = NEXT_INSN (insn);
/* Delete the note from its current position. */
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
/* Don't save away NOTE_INSN_SETJMPs, because they must remain
immediately after the call they follow. We use a fake
(REG_DEAD (const_int -1)) note to remember them.
Likewise with NOTE_INSN_{LOOP,EHREGION}_{BEG, END}. */
if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_SETJMP
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_START
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_END)
{
/* Insert the note at the end of the notes list. */
PREV_INSN (insn) = note_list;
if (note_list)
NEXT_INSN (note_list) = insn;
note_list = insn;
}
insn = next;
}
return insn;
}
/* Delete line notes beginning with INSN. Record line-number notes so
they can be reused. Returns the insn following the notes. */
static rtx
unlink_line_notes (insn, tail)
rtx insn, tail;
{
rtx prev = PREV_INSN (insn);
while (insn != tail && GET_CODE (insn) == NOTE)
{
rtx next = NEXT_INSN (insn);
if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0)
{
/* Delete the note from its current position. */
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
/* Record line-number notes so they can be reused. */
LINE_NOTE (insn) = insn;
}
else
prev = insn;
insn = next;
}
return insn;
}
/* Return the head and tail pointers of BB. */
HAIFA_INLINE static void
get_block_head_tail (bb, headp, tailp)
int bb;
rtx *headp;
rtx *tailp;
{
rtx head;
rtx tail;
int b;
b = BB_TO_BLOCK (bb);
/* HEAD and TAIL delimit the basic block being scheduled. */
head = BLOCK_HEAD (b);
tail = BLOCK_END (b);
/* Don't include any notes or labels at the beginning of the
basic block, or notes at the ends of basic blocks. */
while (head != tail)
{
if (GET_CODE (head) == NOTE)
head = NEXT_INSN (head);
else if (GET_CODE (tail) == NOTE)
tail = PREV_INSN (tail);
else if (GET_CODE (head) == CODE_LABEL)
head = NEXT_INSN (head);
else
break;
}
*headp = head;
*tailp = tail;
}
/* Delete line notes from bb. Save them so they can be later restored
(in restore_line_notes ()). */
static void
rm_line_notes (bb)
int bb;
{
rtx next_tail;
rtx tail;
rtx head;
rtx insn;
get_block_head_tail (bb, &head, &tail);
if (head == tail
&& (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
return;
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev;
/* Farm out notes, and maybe save them in NOTE_LIST.
This is needed to keep the debugger from
getting completely deranged. */
if (GET_CODE (insn) == NOTE)
{
prev = insn;
insn = unlink_line_notes (insn, next_tail);
if (prev == tail)
abort ();
if (prev == head)
abort ();
if (insn == next_tail)
abort ();
}
}
}
/* Save line number notes for each insn in bb. */
static void
save_line_notes (bb)
int bb;
{
rtx head, tail;
rtx next_tail;
/* We must use the true line number for the first insn in the block
that was computed and saved at the start of this pass. We can't
use the current line number, because scheduling of the previous
block may have changed the current line number. */
rtx line = line_note_head[BB_TO_BLOCK (bb)];
rtx insn;
get_block_head_tail (bb, &head, &tail);
next_tail = NEXT_INSN (tail);
for (insn = BLOCK_HEAD (BB_TO_BLOCK (bb));
insn != next_tail;
insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
line = insn;
else
LINE_NOTE (insn) = line;
}
/* After bb was scheduled, insert line notes into the insns list. */
static void
restore_line_notes (bb)
int bb;
{
rtx line, note, prev, new;
int added_notes = 0;
int b;
rtx head, next_tail, insn;
b = BB_TO_BLOCK (bb);
head = BLOCK_HEAD (b);
next_tail = NEXT_INSN (BLOCK_END (b));
/* Determine the current line-number. We want to know the current
line number of the first insn of the block here, in case it is
different from the true line number that was saved earlier. If
different, then we need a line number note before the first insn
of this block. If it happens to be the same, then we don't want to
emit another line number note here. */
for (line = head; line; line = PREV_INSN (line))
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
break;
/* Walk the insns keeping track of the current line-number and inserting
the line-number notes as needed. */
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
line = insn;
/* This used to emit line number notes before every non-deleted note.
However, this confuses a debugger, because line notes not separated
by real instructions all end up at the same address. I can find no
use for line number notes before other notes, so none are emitted. */
else if (GET_CODE (insn) != NOTE
&& (note = LINE_NOTE (insn)) != 0
&& note != line
&& (line == 0
|| NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line)
|| NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line)))
{
line = note;
prev = PREV_INSN (insn);
if (LINE_NOTE (note))
{
/* Re-use the original line-number note. */
LINE_NOTE (note) = 0;
PREV_INSN (note) = prev;
NEXT_INSN (prev) = note;
PREV_INSN (insn) = note;
NEXT_INSN (note) = insn;
}
else
{
added_notes++;
new = emit_note_after (NOTE_LINE_NUMBER (note), prev);
NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note);
RTX_INTEGRATED_P (new) = RTX_INTEGRATED_P (note);
}
}
if (sched_verbose && added_notes)
fprintf (dump, ";; added %d line-number notes\n", added_notes);
}
/* After scheduling the function, delete redundant line notes from the
insns list. */
static void
rm_redundant_line_notes ()
{
rtx line = 0;
rtx insn = get_insns ();
int active_insn = 0;
int notes = 0;
/* Walk the insns deleting redundant line-number notes. Many of these
are already present. The remainder tend to occur at basic
block boundaries. */
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
{
/* If there are no active insns following, INSN is redundant. */
if (active_insn == 0)
{
notes++;
NOTE_SOURCE_FILE (insn) = 0;
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
}
/* If the line number is unchanged, LINE is redundant. */
else if (line
&& NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn)
&& NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn))
{
notes++;
NOTE_SOURCE_FILE (line) = 0;
NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED;
line = insn;
}
else
line = insn;
active_insn = 0;
}
else if (!((GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
|| (GET_CODE (insn) == INSN
&& (GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER))))
active_insn++;
if (sched_verbose && notes)
fprintf (dump, ";; deleted %d line-number notes\n", notes);
}
/* Delete notes between head and tail and put them in the chain
of notes ended by NOTE_LIST. */
static void
rm_other_notes (head, tail)
rtx head;
rtx tail;
{
rtx next_tail;
rtx insn;
if (head == tail
&& (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
return;
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev;
/* Farm out notes, and maybe save them in NOTE_LIST.
This is needed to keep the debugger from
getting completely deranged. */
if (GET_CODE (insn) == NOTE)
{
prev = insn;
insn = unlink_other_notes (insn, next_tail);
if (prev == tail)
abort ();
if (prev == head)
abort ();
if (insn == next_tail)
abort ();
}
}
}
/* Constructor for `sometimes' data structure. */
static int
new_sometimes_live (regs_sometimes_live, regno, sometimes_max)
struct sometimes *regs_sometimes_live;
int regno;
int sometimes_max;
{
register struct sometimes *p;
/* There should never be a register greater than max_regno here. If there
is, it means that a define_split has created a new pseudo reg. This
is not allowed, since there will not be flow info available for any
new register, so catch the error here. */
if (regno >= max_regno)
abort ();
p = &regs_sometimes_live[sometimes_max];
p->regno = regno;
p->live_length = 0;
p->calls_crossed = 0;
sometimes_max++;
return sometimes_max;
}
/* Count lengths of all regs we are currently tracking,
and find new registers no longer live. */
static void
finish_sometimes_live (regs_sometimes_live, sometimes_max)
struct sometimes *regs_sometimes_live;
int sometimes_max;
{
int i;
for (i = 0; i < sometimes_max; i++)
{
register struct sometimes *p = &regs_sometimes_live[i];
int regno = p->regno;
sched_reg_live_length[regno] += p->live_length;
sched_reg_n_calls_crossed[regno] += p->calls_crossed;
}
}
/* functions for computation of registers live/usage info */
/* It is assumed that prior to scheduling BASIC_BLOCK (b)->global_live_at_start
contains the registers that are alive at the entry to b.
Two passes follow: The first pass is performed before the scheduling
of a region. It scans each block of the region forward, computing
the set of registers alive at the end of the basic block and
discard REG_DEAD notes (done by find_pre_sched_live ()).
The second path is invoked after scheduling all region blocks.
It scans each block of the region backward, a block being traversed
only after its succesors in the region. When the set of registers
live at the end of a basic block may be changed by the scheduling
(this may happen for multiple blocks region), it is computed as
the union of the registers live at the start of its succesors.
The last-use information is updated by inserting REG_DEAD notes.
(done by find_post_sched_live ()) */
/* Scan all the insns to be scheduled, removing register death notes.
Register death notes end up in DEAD_NOTES.
Recreate the register life information for the end of this basic
block. */
static void
find_pre_sched_live (bb)
int bb;
{
rtx insn, next_tail, head, tail;
int b = BB_TO_BLOCK (bb);
get_block_head_tail (bb, &head, &tail);
COPY_REG_SET (bb_live_regs, BASIC_BLOCK (b)->global_live_at_start);
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev, next, link;
int reg_weight = 0;
/* Handle register life information. */
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* See if the register gets born here. */
/* We must check for registers being born before we check for
registers dying. It is possible for a register to be born and
die in the same insn, e.g. reading from a volatile memory
location into an otherwise unused register. Such a register
must be marked as dead after this insn. */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
{
sched_note_set (PATTERN (insn), 0);
reg_weight++;
}
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
{
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
reg_weight++;
}
/* ??? This code is obsolete and should be deleted. It
is harmless though, so we will leave it in for now. */
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
}
/* Each call cobbers (makes live) all call-clobbered regs
that are not global or fixed. Note that the function-value
reg is a call_clobbered reg. */
if (GET_CODE (insn) == CALL_INSN)
{
int j;
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
if (call_used_regs[j] && !global_regs[j]
&& ! fixed_regs[j])
{
SET_REGNO_REG_SET (bb_live_regs, j);
}
}
/* Need to know what registers this insn kills. */
for (prev = 0, link = REG_NOTES (insn); link; link = next)
{
next = XEXP (link, 1);
if ((REG_NOTE_KIND (link) == REG_DEAD
|| REG_NOTE_KIND (link) == REG_UNUSED)
/* Verify that the REG_NOTE has a valid value. */
&& GET_CODE (XEXP (link, 0)) == REG)
{
register int regno = REGNO (XEXP (link, 0));
reg_weight--;
/* Only unlink REG_DEAD notes; leave REG_UNUSED notes
alone. */
if (REG_NOTE_KIND (link) == REG_DEAD)
{
if (prev)
XEXP (prev, 1) = next;
else
REG_NOTES (insn) = next;
XEXP (link, 1) = dead_notes;
dead_notes = link;
}
else
prev = link;
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno,
GET_MODE (XEXP (link, 0)));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno+j);
}
}
else
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
}
}
else
prev = link;
}
}
INSN_REG_WEIGHT (insn) = reg_weight;
}
}
/* Update register life and usage information for block bb
after scheduling. Put register dead notes back in the code. */
static void
find_post_sched_live (bb)
int bb;
{
int sometimes_max;
int j, i;
int b;
rtx insn;
rtx head, tail, prev_head, next_tail;
register struct sometimes *regs_sometimes_live;
b = BB_TO_BLOCK (bb);
/* compute live regs at the end of bb as a function of its successors. */
if (current_nr_blocks > 1)
{
int e;
int first_edge;
first_edge = e = OUT_EDGES (b);
CLEAR_REG_SET (bb_live_regs);
if (e)
do
{
int b_succ;
b_succ = TO_BLOCK (e);
IOR_REG_SET (bb_live_regs,
BASIC_BLOCK (b_succ)->global_live_at_start);
e = NEXT_OUT (e);
}
while (e != first_edge);
}
get_block_head_tail (bb, &head, &tail);
next_tail = NEXT_INSN (tail);
prev_head = PREV_INSN (head);
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, FIRST_PSEUDO_REGISTER, i,
{
sched_reg_basic_block[i] = REG_BLOCK_GLOBAL;
});
/* if the block is empty, same regs are alive at its end and its start.
since this is not guaranteed after interblock scheduling, make sure they
are truly identical. */
if (NEXT_INSN (prev_head) == tail
&& (GET_RTX_CLASS (GET_CODE (tail)) != 'i'))
{
if (current_nr_blocks > 1)
COPY_REG_SET (BASIC_BLOCK (b)->global_live_at_start, bb_live_regs);
return;
}
b = BB_TO_BLOCK (bb);
current_block_num = b;
/* Keep track of register lives. */
old_live_regs = ALLOCA_REG_SET ();
regs_sometimes_live
= (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
sometimes_max = 0;
/* initiate "sometimes" data, starting with registers live at end */
sometimes_max = 0;
COPY_REG_SET (old_live_regs, bb_live_regs);
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, 0, j,
{
sometimes_max
= new_sometimes_live (regs_sometimes_live,
j, sometimes_max);
});
/* scan insns back, computing regs live info */
for (insn = tail; insn != prev_head; insn = PREV_INSN (insn))
{
/* First we kill registers set by this insn, and then we
make registers used by this insn live. This is the opposite
order used above because we are traversing the instructions
backwards. */
/* Strictly speaking, we should scan REG_UNUSED notes and make
every register mentioned there live, however, we will just
kill them again immediately below, so there doesn't seem to
be any reason why we bother to do this. */
/* See if this is the last notice we must take of a register. */
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
sched_note_set (PATTERN (insn), 1);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 1);
}
/* This code keeps life analysis information up to date. */
if (GET_CODE (insn) == CALL_INSN)
{
register struct sometimes *p;
/* A call kills all call used registers that are not
global or fixed, except for those mentioned in the call
pattern which will be made live again later. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i] && ! global_regs[i]
&& ! fixed_regs[i])
{
CLEAR_REGNO_REG_SET (bb_live_regs, i);
}
/* Regs live at the time of a call instruction must not
go in a register clobbered by calls. Record this for
all regs now live. Note that insns which are born or
die in a call do not cross a call, so this must be done
after the killings (above) and before the births
(below). */
p = regs_sometimes_live;
for (i = 0; i < sometimes_max; i++, p++)
if (REGNO_REG_SET_P (bb_live_regs, p->regno))
p->calls_crossed += 1;
}
/* Make every register used live, and add REG_DEAD notes for
registers which were not live before we started. */
attach_deaths_insn (insn);
/* Find registers now made live by that instruction. */
EXECUTE_IF_AND_COMPL_IN_REG_SET (bb_live_regs, old_live_regs, 0, j,
{
sometimes_max
= new_sometimes_live (regs_sometimes_live,
j, sometimes_max);
});
IOR_REG_SET (old_live_regs, bb_live_regs);
/* Count lengths of all regs we are worrying about now,
and handle registers no longer live. */
for (i = 0; i < sometimes_max; i++)
{
register struct sometimes *p = &regs_sometimes_live[i];
int regno = p->regno;
p->live_length += 1;
if (!REGNO_REG_SET_P (bb_live_regs, regno))
{
/* This is the end of one of this register's lifetime
segments. Save the lifetime info collected so far,
and clear its bit in the old_live_regs entry. */
sched_reg_live_length[regno] += p->live_length;
sched_reg_n_calls_crossed[regno] += p->calls_crossed;
CLEAR_REGNO_REG_SET (old_live_regs, p->regno);
/* Delete the reg_sometimes_live entry for this reg by
copying the last entry over top of it. */
*p = regs_sometimes_live[--sometimes_max];
/* ...and decrement i so that this newly copied entry
will be processed. */
i--;
}
}
}
finish_sometimes_live (regs_sometimes_live, sometimes_max);
/* In interblock scheduling, global_live_at_start may have changed. */
if (current_nr_blocks > 1)
COPY_REG_SET (BASIC_BLOCK (b)->global_live_at_start, bb_live_regs);
FREE_REG_SET (old_live_regs);
} /* find_post_sched_live */
/* After scheduling the subroutine, restore information about uses of
registers. */
static void
update_reg_usage ()
{
int regno;
if (n_basic_blocks > 0)
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, FIRST_PSEUDO_REGISTER, regno,
{
sched_reg_basic_block[regno]
= REG_BLOCK_GLOBAL;
});
for (regno = 0; regno < max_regno; regno++)
if (sched_reg_live_length[regno])
{
if (sched_verbose)
{
if (REG_LIVE_LENGTH (regno) > sched_reg_live_length[regno])
fprintf (dump,
";; register %d life shortened from %d to %d\n",
regno, REG_LIVE_LENGTH (regno),
sched_reg_live_length[regno]);
/* Negative values are special; don't overwrite the current
reg_live_length value if it is negative. */
else if (REG_LIVE_LENGTH (regno) < sched_reg_live_length[regno]
&& REG_LIVE_LENGTH (regno) >= 0)
fprintf (dump,
";; register %d life extended from %d to %d\n",
regno, REG_LIVE_LENGTH (regno),
sched_reg_live_length[regno]);
if (!REG_N_CALLS_CROSSED (regno)
&& sched_reg_n_calls_crossed[regno])
fprintf (dump,
";; register %d now crosses calls\n", regno);
else if (REG_N_CALLS_CROSSED (regno)
&& !sched_reg_n_calls_crossed[regno]
&& REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
fprintf (dump,
";; register %d no longer crosses calls\n", regno);
if (REG_BASIC_BLOCK (regno) != sched_reg_basic_block[regno]
&& sched_reg_basic_block[regno] != REG_BLOCK_UNKNOWN
&& REG_BASIC_BLOCK(regno) != REG_BLOCK_UNKNOWN)
fprintf (dump,
";; register %d changed basic block from %d to %d\n",
regno, REG_BASIC_BLOCK(regno),
sched_reg_basic_block[regno]);
}
/* Negative values are special; don't overwrite the current
reg_live_length value if it is negative. */
if (REG_LIVE_LENGTH (regno) >= 0)
REG_LIVE_LENGTH (regno) = sched_reg_live_length[regno];
if (sched_reg_basic_block[regno] != REG_BLOCK_UNKNOWN
&& REG_BASIC_BLOCK(regno) != REG_BLOCK_UNKNOWN)
REG_BASIC_BLOCK(regno) = sched_reg_basic_block[regno];
/* We can't change the value of reg_n_calls_crossed to zero for
pseudos which are live in more than one block.
This is because combine might have made an optimization which
invalidated global_live_at_start and reg_n_calls_crossed,
but it does not update them. If we update reg_n_calls_crossed
here, the two variables are now inconsistent, and this might
confuse the caller-save code into saving a register that doesn't
need to be saved. This is only a problem when we zero calls
crossed for a pseudo live in multiple basic blocks.
Alternatively, we could try to correctly update basic block live
at start here in sched, but that seems complicated.
Note: it is possible that a global register became local, as result
of interblock motion, but will remain marked as a global register. */
if (sched_reg_n_calls_crossed[regno]
|| REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
REG_N_CALLS_CROSSED (regno) = sched_reg_n_calls_crossed[regno];
}
}
/* Scheduling clock, modified in schedule_block() and queue_to_ready () */
static int clock_var;
/* Move insns that became ready to fire from queue to ready list. */
static int
queue_to_ready (ready, n_ready)
rtx ready[];
int n_ready;
{
rtx insn;
rtx link;
q_ptr = NEXT_Q (q_ptr);
/* Add all pending insns that can be scheduled without stalls to the
ready list. */
for (link = insn_queue[q_ptr]; link; link = XEXP (link, 1))
{
insn = XEXP (link, 0);
q_size -= 1;
if (sched_verbose >= 2)
fprintf (dump, ";;\t\tQ-->Ready: insn %d: ", INSN_UID (insn));
if (sched_verbose >= 2 && INSN_BB (insn) != target_bb)
fprintf (dump, "(b%d) ", INSN_BLOCK (insn));
ready[n_ready++] = insn;
if (sched_verbose >= 2)
fprintf (dump, "moving to ready without stalls\n");
}
insn_queue[q_ptr] = 0;
/* If there are no ready insns, stall until one is ready and add all
of the pending insns at that point to the ready list. */
if (n_ready == 0)
{
register int stalls;
for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++)
{
if ((link = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]))
{
for (; link; link = XEXP (link, 1))
{
insn = XEXP (link, 0);
q_size -= 1;
if (sched_verbose >= 2)
fprintf (dump, ";;\t\tQ-->Ready: insn %d: ", INSN_UID (insn));
if (sched_verbose >= 2 && INSN_BB (insn) != target_bb)
fprintf (dump, "(b%d) ", INSN_BLOCK (insn));
ready[n_ready++] = insn;
if (sched_verbose >= 2)
fprintf (dump, "moving to ready with %d stalls\n", stalls);
}
insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0;
if (n_ready)
break;
}
}
if (sched_verbose && stalls)
visualize_stall_cycles (BB_TO_BLOCK (target_bb), stalls);
q_ptr = NEXT_Q_AFTER (q_ptr, stalls);
clock_var += stalls;
}
return n_ready;
}
/* Print the ready list for debugging purposes. Callable from debugger. */
static void
debug_ready_list (ready, n_ready)
rtx ready[];
int n_ready;
{
int i;
for (i = 0; i < n_ready; i++)
{
fprintf (dump, " %d", INSN_UID (ready[i]));
if (current_nr_blocks > 1 && INSN_BB (ready[i]) != target_bb)
fprintf (dump, "/b%d", INSN_BLOCK (ready[i]));
}
fprintf (dump, "\n");
}
/* Print names of units on which insn can/should execute, for debugging. */
static void
insn_print_units (insn)
rtx insn;
{
int i;
int unit = insn_unit (insn);
if (unit == -1)
fprintf (dump, "none");
else if (unit >= 0)
fprintf (dump, "%s", function_units[unit].name);
else
{
fprintf (dump, "[");
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if (unit & 1)
{
fprintf (dump, "%s", function_units[i].name);
if (unit != 1)
fprintf (dump, " ");
}
fprintf (dump, "]");
}
}
/* MAX_VISUAL_LINES is the maximum number of lines in visualization table
of a basic block. If more lines are needed, table is splitted to two.
n_visual_lines is the number of lines printed so far for a block.
visual_tbl contains the block visualization info.
vis_no_unit holds insns in a cycle that are not mapped to any unit. */
#define MAX_VISUAL_LINES 100
#define INSN_LEN 30
int n_visual_lines;
char *visual_tbl;
int n_vis_no_unit;
rtx vis_no_unit[10];
/* Finds units that are in use in this fuction. Required only
for visualization. */
static void
init_target_units ()
{
rtx insn;
int unit;
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
unit = insn_unit (insn);
if (unit < 0)
target_units |= ~unit;
else
target_units |= (1 << unit);
}
}
/* Return the length of the visualization table */
static int
get_visual_tbl_length ()
{
int unit, i;
int n, n1;
char *s;
/* compute length of one field in line */
s = (char *) alloca (INSN_LEN + 5);
sprintf (s, " %33s", "uname");
n1 = strlen (s);
/* compute length of one line */
n = strlen (";; ");
n += n1;
for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
if (function_units[unit].bitmask & target_units)
for (i = 0; i < function_units[unit].multiplicity; i++)
n += n1;
n += n1;
n += strlen ("\n") + 2;
/* compute length of visualization string */
return (MAX_VISUAL_LINES * n);
}
/* Init block visualization debugging info */
static void
init_block_visualization ()
{
strcpy (visual_tbl, "");
n_visual_lines = 0;
n_vis_no_unit = 0;
}
#define BUF_LEN 256
static char *
safe_concat (buf, cur, str)
char *buf;
char *cur;
char *str;
{
char *end = buf + BUF_LEN - 2; /* leave room for null */
int c;
if (cur > end)
{
*end = '\0';
return end;
}
while (cur < end && (c = *str++) != '\0')
*cur++ = c;
*cur = '\0';
return cur;
}
/* This recognizes rtx, I classified as expressions. These are always */
/* represent some action on values or results of other expression, */
/* that may be stored in objects representing values. */
static void
print_exp (buf, x, verbose)
char *buf;
rtx x;
int verbose;
{
char tmp[BUF_LEN];
char *st[4];
char *cur = buf;
char *fun = (char *)0;
char *sep;
rtx op[4];
int i;
for (i = 0; i < 4; i++)
{
st[i] = (char *)0;
op[i] = NULL_RTX;
}
switch (GET_CODE (x))
{
case PLUS:
op[0] = XEXP (x, 0);
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < 0)
{
st[1] = "-";
op[1] = GEN_INT (-INTVAL (XEXP (x, 1)));
}
else
{
st[1] = "+";
op[1] = XEXP (x, 1);
}
break;
case LO_SUM:
op[0] = XEXP (x, 0);
st[1] = "+low(";
op[1] = XEXP (x, 1);
st[2] = ")";
break;
case MINUS:
op[0] = XEXP (x, 0);
st[1] = "-";
op[1] = XEXP (x, 1);
break;
case COMPARE:
fun = "cmp";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case NEG:
st[0] = "-";
op[0] = XEXP (x, 0);
break;
case MULT:
op[0] = XEXP (x, 0);
st[1] = "*";
op[1] = XEXP (x, 1);
break;
case DIV:
op[0] = XEXP (x, 0);
st[1] = "/";
op[1] = XEXP (x, 1);
break;
case UDIV:
fun = "udiv";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case MOD:
op[0] = XEXP (x, 0);
st[1] = "%";
op[1] = XEXP (x, 1);
break;
case UMOD:
fun = "umod";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case SMIN:
fun = "smin";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case SMAX:
fun = "smax";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case UMIN:
fun = "umin";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case UMAX:
fun = "umax";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case NOT:
st[0] = "!";
op[0] = XEXP (x, 0);
break;
case AND:
op[0] = XEXP (x, 0);
st[1] = "&";
op[1] = XEXP (x, 1);
break;
case IOR:
op[0] = XEXP (x, 0);
st[1] = "|";
op[1] = XEXP (x, 1);
break;
case XOR:
op[0] = XEXP (x, 0);
st[1] = "^";
op[1] = XEXP (x, 1);
break;
case ASHIFT:
op[0] = XEXP (x, 0);
st[1] = "<<";
op[1] = XEXP (x, 1);
break;
case LSHIFTRT:
op[0] = XEXP (x, 0);
st[1] = " 0>>";
op[1] = XEXP (x, 1);
break;
case ASHIFTRT:
op[0] = XEXP (x, 0);
st[1] = ">>";
op[1] = XEXP (x, 1);
break;
case ROTATE:
op[0] = XEXP (x, 0);
st[1] = "<-<";
op[1] = XEXP (x, 1);
break;
case ROTATERT:
op[0] = XEXP (x, 0);
st[1] = ">->";
op[1] = XEXP (x, 1);
break;
case ABS:
fun = "abs";
op[0] = XEXP (x, 0);
break;
case SQRT:
fun = "sqrt";
op[0] = XEXP (x, 0);
break;
case FFS:
fun = "ffs";
op[0] = XEXP (x, 0);
break;
case EQ:
op[0] = XEXP (x, 0);
st[1] = "==";
op[1] = XEXP (x, 1);
break;
case NE:
op[0] = XEXP (x, 0);
st[1] = "!=";
op[1] = XEXP (x, 1);
break;
case GT:
op[0] = XEXP (x, 0);
st[1] = ">";
op[1] = XEXP (x, 1);
break;
case GTU:
fun = "gtu";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case LT:
op[0] = XEXP (x, 0);
st[1] = "<";
op[1] = XEXP (x, 1);
break;
case LTU:
fun = "ltu";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case GE:
op[0] = XEXP (x, 0);
st[1] = ">=";
op[1] = XEXP (x, 1);
break;
case GEU:
fun = "geu";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case LE:
op[0] = XEXP (x, 0);
st[1] = "<=";
op[1] = XEXP (x, 1);
break;
case LEU:
fun = "leu";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
break;
case SIGN_EXTRACT:
fun = (verbose) ? "sign_extract" : "sxt";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
op[2] = XEXP (x, 2);
break;
case ZERO_EXTRACT:
fun = (verbose) ? "zero_extract" : "zxt";
op[0] = XEXP (x, 0);
op[1] = XEXP (x, 1);
op[2] = XEXP (x, 2);
break;
case SIGN_EXTEND:
fun = (verbose) ? "sign_extend" : "sxn";
op[0] = XEXP (x, 0);
break;
case ZERO_EXTEND:
fun = (verbose) ? "zero_extend" : "zxn";
op[0] = XEXP (x, 0);
break;
case FLOAT_EXTEND:
fun = (verbose) ? "float_extend" : "fxn";
op[0] = XEXP (x, 0);
break;
case TRUNCATE:
fun = (verbose) ? "trunc" : "trn";
op[0] = XEXP (x, 0);
break;
case FLOAT_TRUNCATE:
fun = (verbose) ? "float_trunc" : "ftr";
op[0] = XEXP (x, 0);
break;
case FLOAT:
fun = (verbose) ? "float" : "flt";
op[0] = XEXP (x, 0);
break;
case UNSIGNED_FLOAT:
fun = (verbose) ? "uns_float" : "ufl";
op[0] = XEXP (x, 0);
break;
case FIX:
fun = "fix";
op[0] = XEXP (x, 0);
break;
case UNSIGNED_FIX:
fun = (verbose) ? "uns_fix" : "ufx";
op[0] = XEXP (x, 0);
break;
case PRE_DEC:
st[0] = "--";
op[0] = XEXP (x, 0);
break;
case PRE_INC:
st[0] = "++";
op[0] = XEXP (x, 0);
break;
case POST_DEC:
op[0] = XEXP (x, 0);
st[1] = "--";
break;
case POST_INC:
op[0] = XEXP (x, 0);
st[1] = "++";
break;
case CALL:
st[0] = "call ";
op[0] = XEXP (x, 0);
if (verbose)
{
st[1] = " argc:";
op[1] = XEXP (x, 1);
}
break;
case IF_THEN_ELSE:
st[0] = "{(";
op[0] = XEXP (x, 0);
st[1] = ")?";
op[1] = XEXP (x, 1);
st[2] = ":";
op[2] = XEXP (x, 2);
st[3] = "}";
break;
case TRAP_IF:
fun = "trap_if";
op[0] = TRAP_CONDITION (x);
break;
case UNSPEC:
case UNSPEC_VOLATILE:
{
cur = safe_concat (buf, cur, "unspec");
if (GET_CODE (x) == UNSPEC_VOLATILE)
cur = safe_concat (buf, cur, "/v");
cur = safe_concat (buf, cur, "[");
sep = "";
for (i = 0; i < XVECLEN (x, 0); i++)
{
print_pattern (tmp, XVECEXP (x, 0, i), verbose);
cur = safe_concat (buf, cur, sep);
cur = safe_concat (buf, cur, tmp);
sep = ",";
}
cur = safe_concat (buf, cur, "] ");
sprintf (tmp, "%d", XINT (x, 1));
cur = safe_concat (buf, cur, tmp);
}
break;
default:
/* if (verbose) debug_rtx (x); */
st[0] = GET_RTX_NAME (GET_CODE (x));
break;
}
/* Print this as a function? */
if (fun)
{
cur = safe_concat (buf, cur, fun);
cur = safe_concat (buf, cur, "(");
}
for (i = 0; i < 4; i++)
{
if (st[i])
cur = safe_concat (buf, cur, st[i]);
if (op[i])
{
if (fun && i != 0)
cur = safe_concat (buf, cur, ",");
print_value (tmp, op[i], verbose);
cur = safe_concat (buf, cur, tmp);
}
}
if (fun)
cur = safe_concat (buf, cur, ")");
} /* print_exp */
/* Prints rtxes, i customly classified as values. They're constants, */
/* registers, labels, symbols and memory accesses. */
static void
print_value (buf, x, verbose)
char *buf;
rtx x;
int verbose;
{
char t[BUF_LEN];
char *cur = buf;
switch (GET_CODE (x))
{
case CONST_INT:
sprintf (t, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
cur = safe_concat (buf, cur, t);
break;
case CONST_DOUBLE:
sprintf (t, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3));
cur = safe_concat (buf, cur, t);
break;
case CONST_STRING:
cur = safe_concat (buf, cur, "\"");
cur = safe_concat (buf, cur, XSTR (x, 0));
cur = safe_concat (buf, cur, "\"");
break;
case SYMBOL_REF:
cur = safe_concat (buf, cur, "`");
cur = safe_concat (buf, cur, XSTR (x, 0));
cur = safe_concat (buf, cur, "'");
break;
case LABEL_REF:
sprintf (t, "L%d", INSN_UID (XEXP (x, 0)));
cur = safe_concat (buf, cur, t);
break;
case CONST:
print_value (t, XEXP (x, 0), verbose);
cur = safe_concat (buf, cur, "const(");
cur = safe_concat (buf, cur, t);
cur = safe_concat (buf, cur, ")");
break;
case HIGH:
print_value (t, XEXP (x, 0), verbose);
cur = safe_concat (buf, cur, "high(");
cur = safe_concat (buf, cur, t);
cur = safe_concat (buf, cur, ")");
break;
case REG:
if (REGNO (x) < FIRST_PSEUDO_REGISTER)
{
int c = reg_names[ REGNO (x) ][0];
if (c >= '0' && c <= '9')
cur = safe_concat (buf, cur, "%");
cur = safe_concat (buf, cur, reg_names[ REGNO (x) ]);
}
else
{
sprintf (t, "r%d", REGNO (x));
cur = safe_concat (buf, cur, t);
}
break;
case SUBREG:
print_value (t, SUBREG_REG (x), verbose);
cur = safe_concat (buf, cur, t);
sprintf (t, "#%d", SUBREG_WORD (x));
cur = safe_concat (buf, cur, t);
break;
case SCRATCH:
cur = safe_concat (buf, cur, "scratch");
break;
case CC0:
cur = safe_concat (buf, cur, "cc0");
break;
case PC:
cur = safe_concat (buf, cur, "pc");
break;
case MEM:
print_value (t, XEXP (x, 0), verbose);
cur = safe_concat (buf, cur, "[");
cur = safe_concat (buf, cur, t);
cur = safe_concat (buf, cur, "]");
break;
default:
print_exp (t, x, verbose);
cur = safe_concat (buf, cur, t);
break;
}
} /* print_value */
/* The next step in insn detalization, its pattern recognition */
static void
print_pattern (buf, x, verbose)
char *buf;
rtx x;
int verbose;
{
char t1[BUF_LEN], t2[BUF_LEN], t3[BUF_LEN];
switch (GET_CODE (x))
{
case SET:
print_value (t1, SET_DEST (x), verbose);
print_value (t2, SET_SRC (x), verbose);
sprintf (buf, "%s=%s", t1, t2);
break;
case RETURN:
sprintf (buf, "return");
break;
case CALL:
print_exp (buf, x, verbose);
break;
case CLOBBER:
print_value (t1, XEXP (x, 0), verbose);
sprintf (buf, "clobber %s", t1);
break;
case USE:
print_value (t1, XEXP (x, 0), verbose);
sprintf (buf, "use %s", t1);
break;
case PARALLEL:
{
int i;
sprintf (t1, "{");
for (i = 0; i < XVECLEN (x, 0); i++)
{
print_pattern (t2, XVECEXP (x, 0, i), verbose);
sprintf (t3, "%s%s;", t1, t2);
strcpy (t1, t3);
}
sprintf (buf, "%s}", t1);
}
break;
case SEQUENCE:
{
int i;
sprintf (t1, "%%{");
for (i = 0; i < XVECLEN (x, 0); i++)
{
print_insn (t2, XVECEXP (x, 0, i), verbose);
sprintf (t3, "%s%s;", t1, t2);
strcpy (t1, t3);
}
sprintf (buf, "%s%%}", t1);
}
break;
case ASM_INPUT:
sprintf (buf, "asm {%s}", XSTR (x, 0));
break;
case ADDR_VEC:
break;
case ADDR_DIFF_VEC:
print_value (buf, XEXP (x, 0), verbose);
break;
case TRAP_IF:
print_value (t1, TRAP_CONDITION (x), verbose);
sprintf (buf, "trap_if %s", t1);
break;
case UNSPEC:
{
int i;
sprintf (t1, "unspec{");
for (i = 0; i < XVECLEN (x, 0); i++)
{
print_pattern (t2, XVECEXP (x, 0, i), verbose);
sprintf (t3, "%s%s;", t1, t2);
strcpy (t1, t3);
}
sprintf (buf, "%s}", t1);
}
break;
case UNSPEC_VOLATILE:
{
int i;
sprintf (t1, "unspec/v{");
for (i = 0; i < XVECLEN (x, 0); i++)
{
print_pattern (t2, XVECEXP (x, 0, i), verbose);
sprintf (t3, "%s%s;", t1, t2);
strcpy (t1, t3);
}
sprintf (buf, "%s}", t1);
}
break;
default:
print_value (buf, x, verbose);
}
} /* print_pattern */
/* This is the main function in rtl visualization mechanism. It
accepts an rtx and tries to recognize it as an insn, then prints it
properly in human readable form, resembling assembler mnemonics. */
/* For every insn it prints its UID and BB the insn belongs */
/* too. (probably the last "option" should be extended somehow, since */
/* it depends now on sched.c inner variables ...) */
static void
print_insn (buf, x, verbose)
char *buf;
rtx x;
int verbose;
{
char t[BUF_LEN];
rtx insn = x;
switch (GET_CODE (x))
{
case INSN:
print_pattern (t, PATTERN (x), verbose);
if (verbose)
sprintf (buf, "b%d: i% 4d: %s", INSN_BB (x),
INSN_UID (x), t);
else
sprintf (buf, "%-4d %s", INSN_UID (x), t);
break;
case JUMP_INSN:
print_pattern (t, PATTERN (x), verbose);
if (verbose)
sprintf (buf, "b%d: i% 4d: jump %s", INSN_BB (x),
INSN_UID (x), t);
else
sprintf (buf, "%-4d %s", INSN_UID (x), t);
break;
case CALL_INSN:
x = PATTERN (insn);
if (GET_CODE (x) == PARALLEL)
{
x = XVECEXP (x, 0, 0);
print_pattern (t, x, verbose);
}
else
strcpy (t, "call <...>");
if (verbose)
sprintf (buf, "b%d: i% 4d: %s", INSN_BB (insn),
INSN_UID (insn), t);
else
sprintf (buf, "%-4d %s", INSN_UID (insn), t);
break;
case CODE_LABEL:
sprintf (buf, "L%d:", INSN_UID (x));
break;
case BARRIER:
sprintf (buf, "i% 4d: barrier", INSN_UID (x));
break;
case NOTE:
if (NOTE_LINE_NUMBER (x) > 0)
sprintf (buf, "%4d note \"%s\" %d", INSN_UID (x),
NOTE_SOURCE_FILE (x), NOTE_LINE_NUMBER (x));
else
sprintf (buf, "%4d %s", INSN_UID (x),
GET_NOTE_INSN_NAME (NOTE_LINE_NUMBER (x)));
break;
default:
if (verbose)
{
sprintf (buf, "Not an INSN at all\n");
debug_rtx (x);
}
else
sprintf (buf, "i%-4d <What?>", INSN_UID (x));
}
} /* print_insn */
/* Print visualization debugging info */
static void
print_block_visualization (b, s)
int b;
char *s;
{
int unit, i;
/* print header */
fprintf (dump, "\n;; ==================== scheduling visualization for block %d %s \n", b, s);
/* Print names of units */
fprintf (dump, ";; %-8s", "clock");
for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
if (function_units[unit].bitmask & target_units)
for (i = 0; i < function_units[unit].multiplicity; i++)
fprintf (dump, " %-33s", function_units[unit].name);
fprintf (dump, " %-8s\n", "no-unit");
fprintf (dump, ";; %-8s", "=====");
for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
if (function_units[unit].bitmask & target_units)
for (i = 0; i < function_units[unit].multiplicity; i++)
fprintf (dump, " %-33s", "==============================");
fprintf (dump, " %-8s\n", "=======");
/* Print insns in each cycle */
fprintf (dump, "%s\n", visual_tbl);
}
/* Print insns in the 'no_unit' column of visualization */
static void
visualize_no_unit (insn)
rtx insn;
{
vis_no_unit[n_vis_no_unit] = insn;
n_vis_no_unit++;
}
/* Print insns scheduled in clock, for visualization. */
static void
visualize_scheduled_insns (b, clock)
int b, clock;
{
int i, unit;
/* if no more room, split table into two */
if (n_visual_lines >= MAX_VISUAL_LINES)
{
print_block_visualization (b, "(incomplete)");
init_block_visualization ();
}
n_visual_lines++;
sprintf (visual_tbl + strlen (visual_tbl), ";; %-8d", clock);
for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
if (function_units[unit].bitmask & target_units)
for (i = 0; i < function_units[unit].multiplicity; i++)
{
int instance = unit + i * FUNCTION_UNITS_SIZE;
rtx insn = unit_last_insn[instance];
/* print insns that still keep the unit busy */
if (insn &&
actual_hazard_this_instance (unit, instance, insn, clock, 0))
{
char str[BUF_LEN];
print_insn (str, insn, 0);
str[INSN_LEN] = '\0';
sprintf (visual_tbl + strlen (visual_tbl), " %-33s", str);
}
else
sprintf (visual_tbl + strlen (visual_tbl), " %-33s", "------------------------------");
}
/* print insns that are not assigned to any unit */
for (i = 0; i < n_vis_no_unit; i++)
sprintf (visual_tbl + strlen (visual_tbl), " %-8d",
INSN_UID (vis_no_unit[i]));
n_vis_no_unit = 0;
sprintf (visual_tbl + strlen (visual_tbl), "\n");
}
/* Print stalled cycles */
static void
visualize_stall_cycles (b, stalls)
int b, stalls;
{
int i;
/* if no more room, split table into two */
if (n_visual_lines >= MAX_VISUAL_LINES)
{
print_block_visualization (b, "(incomplete)");
init_block_visualization ();
}
n_visual_lines++;
sprintf (visual_tbl + strlen (visual_tbl), ";; ");
for (i = 0; i < stalls; i++)
sprintf (visual_tbl + strlen (visual_tbl), ".");
sprintf (visual_tbl + strlen (visual_tbl), "\n");
}
/* move_insn1: Remove INSN from insn chain, and link it after LAST insn */
static rtx
move_insn1 (insn, last)
rtx insn, last;
{
NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
NEXT_INSN (insn) = NEXT_INSN (last);
PREV_INSN (NEXT_INSN (last)) = insn;
NEXT_INSN (last) = insn;
PREV_INSN (insn) = last;
return insn;
}
/* Search INSN for fake REG_DEAD note pairs for NOTE_INSN_SETJMP,
NOTE_INSN_{LOOP,EHREGION}_{BEG,END}; and convert them back into
NOTEs. The REG_DEAD note following first one is contains the saved
value for NOTE_BLOCK_NUMBER which is useful for
NOTE_INSN_EH_REGION_{BEG,END} NOTEs. LAST is the last instruction
output by the instruction scheduler. Return the new value of LAST. */
static rtx
reemit_notes (insn, last)
rtx insn;
rtx last;
{
rtx note, retval;
retval = last;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (XEXP (note, 0)) == CONST_INT)
{
int note_type = INTVAL (XEXP (note, 0));
if (note_type == NOTE_INSN_SETJMP)
{
retval = emit_note_after (NOTE_INSN_SETJMP, insn);
CONST_CALL_P (retval) = CONST_CALL_P (note);
remove_note (insn, note);
note = XEXP (note, 1);
}
else if (note_type == NOTE_INSN_RANGE_START
|| note_type == NOTE_INSN_RANGE_END)
{
last = emit_note_before (note_type, last);
remove_note (insn, note);
note = XEXP (note, 1);
NOTE_RANGE_INFO (last) = XEXP (note, 0);
}
else
{
last = emit_note_before (INTVAL (XEXP (note, 0)), last);
remove_note (insn, note);
note = XEXP (note, 1);
NOTE_BLOCK_NUMBER (last) = INTVAL (XEXP (note, 0));
}
remove_note (insn, note);
}
}
return retval;
}
/* Move INSN, and all insns which should be issued before it,
due to SCHED_GROUP_P flag. Reemit notes if needed.
Return the last insn emitted by the scheduler, which is the
return value from the first call to reemit_notes. */
static rtx
move_insn (insn, last)
rtx insn, last;
{
rtx retval = NULL;
/* If INSN has SCHED_GROUP_P set, then issue it and any other
insns with SCHED_GROUP_P set first. */
while (SCHED_GROUP_P (insn))
{
rtx prev = PREV_INSN (insn);
/* Move a SCHED_GROUP_P insn. */
move_insn1 (insn, last);
/* If this is the first call to reemit_notes, then record
its return value. */
if (retval == NULL_RTX)
retval = reemit_notes (insn, insn);
else
reemit_notes (insn, insn);
insn = prev;
}
/* Now move the first non SCHED_GROUP_P insn. */
move_insn1 (insn, last);
/* If this is the first call to reemit_notes, then record
its return value. */
if (retval == NULL_RTX)
retval = reemit_notes (insn, insn);
else
reemit_notes (insn, insn);
return retval;
}
/* Return an insn which represents a SCHED_GROUP, which is
the last insn in the group. */
static rtx
group_leader (insn)
rtx insn;
{
rtx prev;
do
{
prev = insn;
insn = next_nonnote_insn (insn);
}
while (insn && SCHED_GROUP_P (insn) && (GET_CODE (insn) != CODE_LABEL));
return prev;
}
/* Use forward list scheduling to rearrange insns of block BB in region RGN,
possibly bringing insns from subsequent blocks in the same region.
Return number of insns scheduled. */
static int
schedule_block (bb, rgn_n_insns)
int bb;
int rgn_n_insns;
{
/* Local variables. */
rtx insn, last;
rtx *ready;
int i;
int n_ready = 0;
int can_issue_more;
/* flow block of this bb */
int b = BB_TO_BLOCK (bb);
/* target_n_insns == number of insns in b before scheduling starts.
sched_target_n_insns == how many of b's insns were scheduled.
sched_n_insns == how many insns were scheduled in b */
int target_n_insns = 0;
int sched_target_n_insns = 0;
int sched_n_insns = 0;
#define NEED_NOTHING 0
#define NEED_HEAD 1
#define NEED_TAIL 2
int new_needs;
/* head/tail info for this block */
rtx prev_head;
rtx next_tail;
rtx head;
rtx tail;
int bb_src;
/* We used to have code to avoid getting parameters moved from hard
argument registers into pseudos.
However, it was removed when it proved to be of marginal benefit
and caused problems because schedule_block and compute_forward_dependences
had different notions of what the "head" insn was. */
get_block_head_tail (bb, &head, &tail);
/* Interblock scheduling could have moved the original head insn from this
block into a proceeding block. This may also cause schedule_block and
compute_forward_dependences to have different notions of what the
"head" insn was.
If the interblock movement happened to make this block start with
some notes (LOOP, EH or SETJMP) before the first real insn, then
HEAD will have various special notes attached to it which must be
removed so that we don't end up with extra copies of the notes. */
if (GET_RTX_CLASS (GET_CODE (head)) == 'i')
{
rtx note;
for (note = REG_NOTES (head); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (XEXP (note, 0)) == CONST_INT)
remove_note (head, note);
}
next_tail = NEXT_INSN (tail);
prev_head = PREV_INSN (head);
/* If the only insn left is a NOTE or a CODE_LABEL, then there is no need
to schedule this block. */
if (head == tail
&& (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
return (sched_n_insns);
/* debug info */
if (sched_verbose)
{
fprintf (dump, ";; ======================================================\n");
fprintf (dump,
";; -- basic block %d from %d to %d -- %s reload\n",
b, INSN_UID (BLOCK_HEAD (b)), INSN_UID (BLOCK_END (b)),
(reload_completed ? "after" : "before"));
fprintf (dump, ";; ======================================================\n");
fprintf (dump, "\n");
visual_tbl = (char *) alloca (get_visual_tbl_length ());
init_block_visualization ();
}
/* remove remaining note insns from the block, save them in
note_list. These notes are restored at the end of
schedule_block (). */
note_list = 0;
rm_other_notes (head, tail);
target_bb = bb;
/* prepare current target block info */
if (current_nr_blocks > 1)
{
candidate_table = (candidate *) alloca (current_nr_blocks * sizeof (candidate));
bblst_last = 0;
/* ??? It is not clear why bblst_size is computed this way. The original
number was clearly too small as it resulted in compiler failures.
Multiplying by the original number by 2 (to account for update_bbs
members) seems to be a reasonable solution. */
/* ??? Or perhaps there is a bug somewhere else in this file? */
bblst_size = (current_nr_blocks - bb) * rgn_nr_edges * 2;
bblst_table = (int *) alloca (bblst_size * sizeof (int));
bitlst_table_last = 0;
bitlst_table_size = rgn_nr_edges;
bitlst_table = (int *) alloca (rgn_nr_edges * sizeof (int));
compute_trg_info (bb);
}
clear_units ();
/* Allocate the ready list */
ready = (rtx *) alloca ((rgn_n_insns + 1) * sizeof (rtx));
/* Print debugging information. */
if (sched_verbose >= 5)
debug_dependencies ();
/* Initialize ready list with all 'ready' insns in target block.
Count number of insns in the target block being scheduled. */
n_ready = 0;
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx next;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
next = NEXT_INSN (insn);
if (INSN_DEP_COUNT (insn) == 0
&& (SCHED_GROUP_P (next) == 0 || GET_RTX_CLASS (GET_CODE (next)) != 'i'))
ready[n_ready++] = insn;
if (!(SCHED_GROUP_P (insn)))
target_n_insns++;
}
/* Add to ready list all 'ready' insns in valid source blocks.
For speculative insns, check-live, exception-free, and
issue-delay. */
for (bb_src = bb + 1; bb_src < current_nr_blocks; bb_src++)
if (IS_VALID (bb_src))
{
rtx src_head;
rtx src_next_tail;
rtx tail, head;
get_block_head_tail (bb_src, &head, &tail);
src_next_tail = NEXT_INSN (tail);
src_head = head;
if (head == tail
&& (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
continue;
for (insn = src_head; insn != src_next_tail; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (!CANT_MOVE (insn)
&& (!IS_SPECULATIVE_INSN (insn)
|| (insn_issue_delay (insn) <= 3
&& check_live (insn, bb_src)
&& is_exception_free (insn, bb_src, target_bb))))
{
rtx next;
next = NEXT_INSN (insn);
if (INSN_DEP_COUNT (insn) == 0
&& (SCHED_GROUP_P (next) == 0
|| GET_RTX_CLASS (GET_CODE (next)) != 'i'))
ready[n_ready++] = insn;
}
}
}
#ifdef MD_SCHED_INIT
MD_SCHED_INIT (dump, sched_verbose);
#endif
/* no insns scheduled in this block yet */
last_scheduled_insn = 0;
/* Sort the ready list */
SCHED_SORT (ready, n_ready);
#ifdef MD_SCHED_REORDER
MD_SCHED_REORDER (dump, sched_verbose, ready, n_ready);
#endif
if (sched_verbose >= 2)
{
fprintf (dump, ";;\t\tReady list initially: ");
debug_ready_list (ready, n_ready);
}
/* Q_SIZE is the total number of insns in the queue. */
q_ptr = 0;
q_size = 0;
clock_var = 0;
last_clock_var = 0;
bzero ((char *) insn_queue, sizeof (insn_queue));
/* We start inserting insns after PREV_HEAD. */
last = prev_head;
/* Initialize INSN_QUEUE, LIST and NEW_NEEDS. */
new_needs = (NEXT_INSN (prev_head) == BLOCK_HEAD (b)
? NEED_HEAD : NEED_NOTHING);
if (PREV_INSN (next_tail) == BLOCK_END (b))
new_needs |= NEED_TAIL;
/* loop until all the insns in BB are scheduled. */
while (sched_target_n_insns < target_n_insns)
{
int b1;
clock_var++;
/* Add to the ready list all pending insns that can be issued now.
If there are no ready insns, increment clock until one
is ready and add all pending insns at that point to the ready
list. */
n_ready = queue_to_ready (ready, n_ready);
if (n_ready == 0)
abort ();
if (sched_verbose >= 2)
{
fprintf (dump, ";;\t\tReady list after queue_to_ready: ");
debug_ready_list (ready, n_ready);
}
/* Sort the ready list. */
SCHED_SORT (ready, n_ready);
#ifdef MD_SCHED_REORDER
MD_SCHED_REORDER (dump, sched_verbose, ready, n_ready);
#endif
if (sched_verbose)
{
fprintf (dump, "\n;;\tReady list (t =%3d): ", clock_var);
debug_ready_list (ready, n_ready);
}
/* Issue insns from ready list.
It is important to count down from n_ready, because n_ready may change
as insns are issued. */
can_issue_more = issue_rate;
for (i = n_ready - 1; i >= 0 && can_issue_more; i--)
{
rtx insn = ready[i];
int cost = actual_hazard (insn_unit (insn), insn, clock_var, 0);
if (cost > 1)
{
queue_insn (insn, cost);
ready[i] = ready[--n_ready]; /* remove insn from ready list */
}
else if (cost == 0)
{
/* an interblock motion? */
if (INSN_BB (insn) != target_bb)
{
rtx temp;
if (IS_SPECULATIVE_INSN (insn))
{
if (!check_live (insn, INSN_BB (insn)))
{
/* speculative motion, live check failed, remove
insn from ready list */
ready[i] = ready[--n_ready];
continue;
}
update_live (insn, INSN_BB (insn));
/* for speculative load, mark insns fed by it. */
if (IS_LOAD_INSN (insn) || FED_BY_SPEC_LOAD (insn))
set_spec_fed (insn);
nr_spec++;
}
nr_inter++;
temp = insn;
while (SCHED_GROUP_P (temp))
temp = PREV_INSN (temp);
/* Update source block boundaries. */
b1 = INSN_BLOCK (temp);
if (temp == BLOCK_HEAD (b1)
&& insn == BLOCK_END (b1))
{
/* We moved all the insns in the basic block.
Emit a note after the last insn and update the
begin/end boundaries to point to the note. */
emit_note_after (NOTE_INSN_DELETED, insn);
BLOCK_END (b1) = NEXT_INSN (insn);
BLOCK_HEAD (b1) = NEXT_INSN (insn);
}
else if (insn == BLOCK_END (b1))
{
/* We took insns from the end of the basic block,
so update the end of block boundary so that it
points to the first insn we did not move. */
BLOCK_END (b1) = PREV_INSN (temp);
}
else if (temp == BLOCK_HEAD (b1))
{
/* We took insns from the start of the basic block,
so update the start of block boundary so that
it points to the first insn we did not move. */
BLOCK_HEAD (b1) = NEXT_INSN (insn);
}
}
else
{
/* in block motion */
sched_target_n_insns++;
}
last_scheduled_insn = insn;
last = move_insn (insn, last);
sched_n_insns++;
#ifdef MD_SCHED_VARIABLE_ISSUE
MD_SCHED_VARIABLE_ISSUE (dump, sched_verbose, insn, can_issue_more);
#else
can_issue_more--;
#endif
n_ready = schedule_insn (insn, ready, n_ready, clock_var);
/* remove insn from ready list */
ready[i] = ready[--n_ready];
/* close this block after scheduling its jump */
if (GET_CODE (last_scheduled_insn) == JUMP_INSN)
break;
}
}
/* debug info */
if (sched_verbose)
{
visualize_scheduled_insns (b, clock_var);
}
}
/* debug info */
if (sched_verbose)
{
fprintf (dump, ";;\tReady list (final): ");
debug_ready_list (ready, n_ready);
print_block_visualization (b, "");
}
/* Sanity check -- queue must be empty now. Meaningless if region has
multiple bbs. */
if (current_nr_blocks > 1)
if (!flag_schedule_interblock && q_size != 0)
abort ();
/* update head/tail boundaries. */
head = NEXT_INSN (prev_head);
tail = last;
/* Restore-other-notes: NOTE_LIST is the end of a chain of notes
previously found among the insns. Insert them at the beginning
of the insns. */
if (note_list != 0)
{
rtx note_head = note_list;
while (PREV_INSN (note_head))
{
note_head = PREV_INSN (note_head);
}
PREV_INSN (note_head) = PREV_INSN (head);
NEXT_INSN (PREV_INSN (head)) = note_head;
PREV_INSN (head) = note_list;
NEXT_INSN (note_list) = head;
head = note_head;
}
/* update target block boundaries. */
if (new_needs & NEED_HEAD)
BLOCK_HEAD (b) = head;
if (new_needs & NEED_TAIL)
BLOCK_END (b) = tail;
/* debugging */
if (sched_verbose)
{
fprintf (dump, ";; total time = %d\n;; new basic block head = %d\n",
clock_var, INSN_UID (BLOCK_HEAD (b)));
fprintf (dump, ";; new basic block end = %d\n\n",
INSN_UID (BLOCK_END (b)));
}
return (sched_n_insns);
} /* schedule_block () */
/* print the bit-set of registers, S. callable from debugger */
extern void
debug_reg_vector (s)
regset s;
{
int regno;
EXECUTE_IF_SET_IN_REG_SET (s, 0, regno,
{
fprintf (dump, " %d", regno);
});
fprintf (dump, "\n");
}
/* Use the backward dependences from LOG_LINKS to build
forward dependences in INSN_DEPEND. */
static void
compute_block_forward_dependences (bb)
int bb;
{
rtx insn, link;
rtx tail, head;
rtx next_tail;
enum reg_note dep_type;
get_block_head_tail (bb, &head, &tail);
next_tail = NEXT_INSN (tail);
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
insn = group_leader (insn);
for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
{
rtx x = group_leader (XEXP (link, 0));
rtx new_link;
if (x != XEXP (link, 0))
continue;
/* Ignore dependences upon deleted insn */
if (GET_CODE (x) == NOTE || INSN_DELETED_P (x))
continue;
if (find_insn_list (insn, INSN_DEPEND (x)))
continue;
new_link = alloc_INSN_LIST (insn, INSN_DEPEND (x));
dep_type = REG_NOTE_KIND (link);
PUT_REG_NOTE_KIND (new_link, dep_type);
INSN_DEPEND (x) = new_link;
INSN_DEP_COUNT (insn) += 1;
}
}
}
/* Initialize variables for region data dependence analysis.
n_bbs is the number of region blocks */
__inline static void
init_rgn_data_dependences (n_bbs)
int n_bbs;
{
int bb;
/* variables for which one copy exists for each block */
bzero ((char *) bb_pending_read_insns, n_bbs * sizeof (rtx));
bzero ((char *) bb_pending_read_mems, n_bbs * sizeof (rtx));
bzero ((char *) bb_pending_write_insns, n_bbs * sizeof (rtx));
bzero ((char *) bb_pending_write_mems, n_bbs * sizeof (rtx));
bzero ((char *) bb_pending_lists_length, n_bbs * sizeof (rtx));
bzero ((char *) bb_last_pending_memory_flush, n_bbs * sizeof (rtx));
bzero ((char *) bb_last_function_call, n_bbs * sizeof (rtx));
bzero ((char *) bb_sched_before_next_call, n_bbs * sizeof (rtx));
/* Create an insn here so that we can hang dependencies off of it later. */
for (bb = 0; bb < n_bbs; bb++)
{
bb_sched_before_next_call[bb] =
gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,
NULL_RTX, 0, NULL_RTX, NULL_RTX);
LOG_LINKS (bb_sched_before_next_call[bb]) = 0;
}
}
/* Add dependences so that branches are scheduled to run last in their block */
static void
add_branch_dependences (head, tail)
rtx head, tail;
{
rtx insn, last;
/* For all branches, calls, uses, and cc0 setters, force them to remain
in order at the end of the block by adding dependencies and giving
the last a high priority. There may be notes present, and prev_head
may also be a note.
Branches must obviously remain at the end. Calls should remain at the
end since moving them results in worse register allocation. Uses remain
at the end to ensure proper register allocation. cc0 setters remaim
at the end because they can't be moved away from their cc0 user. */
insn = tail;
last = 0;
while (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
|| (GET_CODE (insn) == INSN
&& (GET_CODE (PATTERN (insn)) == USE
#ifdef HAVE_cc0
|| sets_cc0_p (PATTERN (insn))
#endif
))
|| GET_CODE (insn) == NOTE)
{
if (GET_CODE (insn) != NOTE)
{
if (last != 0
&& !find_insn_list (insn, LOG_LINKS (last)))
{
add_dependence (last, insn, REG_DEP_ANTI);
INSN_REF_COUNT (insn)++;
}
CANT_MOVE (insn) = 1;
last = insn;
/* Skip over insns that are part of a group.
Make each insn explicitly depend on the previous insn.
This ensures that only the group header will ever enter
the ready queue (and, when scheduled, will automatically
schedule the SCHED_GROUP_P block). */
while (SCHED_GROUP_P (insn))
{
rtx temp = prev_nonnote_insn (insn);
add_dependence (insn, temp, REG_DEP_ANTI);
insn = temp;
}
}
/* Don't overrun the bounds of the basic block. */
if (insn == head)
break;
insn = PREV_INSN (insn);
}
/* make sure these insns are scheduled last in their block */
insn = last;
if (insn != 0)
while (insn != head)
{
insn = prev_nonnote_insn (insn);
if (INSN_REF_COUNT (insn) != 0)
continue;
if (!find_insn_list (last, LOG_LINKS (insn)))
add_dependence (last, insn, REG_DEP_ANTI);
INSN_REF_COUNT (insn) = 1;
/* Skip over insns that are part of a group. */
while (SCHED_GROUP_P (insn))
insn = prev_nonnote_insn (insn);
}
}
/* Compute bacward dependences inside BB. In a multiple blocks region:
(1) a bb is analyzed after its predecessors, and (2) the lists in
effect at the end of bb (after analyzing for bb) are inherited by
bb's successrs.
Specifically for reg-reg data dependences, the block insns are
scanned by sched_analyze () top-to-bottom. Two lists are
naintained by sched_analyze (): reg_last_defs[] for register DEFs,
and reg_last_uses[] for register USEs.
When analysis is completed for bb, we update for its successors:
; - DEFS[succ] = Union (DEFS [succ], DEFS [bb])
; - USES[succ] = Union (USES [succ], DEFS [bb])
The mechanism for computing mem-mem data dependence is very
similar, and the result is interblock dependences in the region. */
static void
compute_block_backward_dependences (bb)
int bb;
{
int b;
rtx x;
rtx head, tail;
int max_reg = max_reg_num ();
b = BB_TO_BLOCK (bb);
if (current_nr_blocks == 1)
{
reg_last_uses = (rtx *) alloca (max_reg * sizeof (rtx));
reg_last_sets = (rtx *) alloca (max_reg * sizeof (rtx));
reg_last_clobbers = (rtx *) alloca (max_reg * sizeof (rtx));
bzero ((char *) reg_last_uses, max_reg * sizeof (rtx));
bzero ((char *) reg_last_sets, max_reg * sizeof (rtx));
bzero ((char *) reg_last_clobbers, max_reg * sizeof (rtx));
pending_read_insns = 0;
pending_read_mems = 0;
pending_write_insns = 0;
pending_write_mems = 0;
pending_lists_length = 0;
last_function_call = 0;
last_pending_memory_flush = 0;
sched_before_next_call
= gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,
NULL_RTX, 0, NULL_RTX, NULL_RTX);
LOG_LINKS (sched_before_next_call) = 0;
}
else
{
reg_last_uses = bb_reg_last_uses[bb];
reg_last_sets = bb_reg_last_sets[bb];
reg_last_clobbers = bb_reg_last_clobbers[bb];
pending_read_insns = bb_pending_read_insns[bb];
pending_read_mems = bb_pending_read_mems[bb];
pending_write_insns = bb_pending_write_insns[bb];
pending_write_mems = bb_pending_write_mems[bb];
pending_lists_length = bb_pending_lists_length[bb];
last_function_call = bb_last_function_call[bb];
last_pending_memory_flush = bb_last_pending_memory_flush[bb];
sched_before_next_call = bb_sched_before_next_call[bb];
}
/* do the analysis for this block */
get_block_head_tail (bb, &head, &tail);
sched_analyze (head, tail);
add_branch_dependences (head, tail);
if (current_nr_blocks > 1)
{
int e, first_edge;
int b_succ, bb_succ;
int reg;
rtx link_insn, link_mem;
rtx u;
/* these lists should point to the right place, for correct freeing later. */
bb_pending_read_insns[bb] = pending_read_insns;
bb_pending_read_mems[bb] = pending_read_mems;
bb_pending_write_insns[bb] = pending_write_insns;
bb_pending_write_mems[bb] = pending_write_mems;
/* bb's structures are inherited by it's successors */
first_edge = e = OUT_EDGES (b);
if (e > 0)
do
{
b_succ = TO_BLOCK (e);
bb_succ = BLOCK_TO_BB (b_succ);
/* only bbs "below" bb, in the same region, are interesting */
if (CONTAINING_RGN (b) != CONTAINING_RGN (b_succ)
|| bb_succ <= bb)
{
e = NEXT_OUT (e);
continue;
}
for (reg = 0; reg < max_reg; reg++)
{
/* reg-last-uses lists are inherited by bb_succ */
for (u = reg_last_uses[reg]; u; u = XEXP (u, 1))
{
if (find_insn_list (XEXP (u, 0), (bb_reg_last_uses[bb_succ])[reg]))
continue;
(bb_reg_last_uses[bb_succ])[reg]
= alloc_INSN_LIST (XEXP (u, 0),
(bb_reg_last_uses[bb_succ])[reg]);
}
/* reg-last-defs lists are inherited by bb_succ */
for (u = reg_last_sets[reg]; u; u = XEXP (u, 1))
{
if (find_insn_list (XEXP (u, 0), (bb_reg_last_sets[bb_succ])[reg]))
continue;
(bb_reg_last_sets[bb_succ])[reg]
= alloc_INSN_LIST (XEXP (u, 0),
(bb_reg_last_sets[bb_succ])[reg]);
}
for (u = reg_last_clobbers[reg]; u; u = XEXP (u, 1))
{
if (find_insn_list (XEXP (u, 0), (bb_reg_last_clobbers[bb_succ])[reg]))
continue;
(bb_reg_last_clobbers[bb_succ])[reg]
= alloc_INSN_LIST (XEXP (u, 0),
(bb_reg_last_clobbers[bb_succ])[reg]);
}
}
/* mem read/write lists are inherited by bb_succ */
link_insn = pending_read_insns;
link_mem = pending_read_mems;
while (link_insn)
{
if (!(find_insn_mem_list (XEXP (link_insn, 0), XEXP (link_mem, 0),
bb_pending_read_insns[bb_succ],
bb_pending_read_mems[bb_succ])))
add_insn_mem_dependence (&bb_pending_read_insns[bb_succ],
&bb_pending_read_mems[bb_succ],
XEXP (link_insn, 0), XEXP (link_mem, 0));
link_insn = XEXP (link_insn, 1);
link_mem = XEXP (link_mem, 1);
}
link_insn = pending_write_insns;
link_mem = pending_write_mems;
while (link_insn)
{
if (!(find_insn_mem_list (XEXP (link_insn, 0), XEXP (link_mem, 0),
bb_pending_write_insns[bb_succ],
bb_pending_write_mems[bb_succ])))
add_insn_mem_dependence (&bb_pending_write_insns[bb_succ],
&bb_pending_write_mems[bb_succ],
XEXP (link_insn, 0), XEXP (link_mem, 0));
link_insn = XEXP (link_insn, 1);
link_mem = XEXP (link_mem, 1);
}
/* last_function_call is inherited by bb_succ */
for (u = last_function_call; u; u = XEXP (u, 1))
{
if (find_insn_list (XEXP (u, 0), bb_last_function_call[bb_succ]))
continue;
bb_last_function_call[bb_succ]
= alloc_INSN_LIST (XEXP (u, 0),
bb_last_function_call[bb_succ]);
}
/* last_pending_memory_flush is inherited by bb_succ */
for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
{
if (find_insn_list (XEXP (u, 0), bb_last_pending_memory_flush[bb_succ]))
continue;
bb_last_pending_memory_flush[bb_succ]
= alloc_INSN_LIST (XEXP (u, 0),
bb_last_pending_memory_flush[bb_succ]);
}
/* sched_before_next_call is inherited by bb_succ */
x = LOG_LINKS (sched_before_next_call);
for (; x; x = XEXP (x, 1))
add_dependence (bb_sched_before_next_call[bb_succ],
XEXP (x, 0), REG_DEP_ANTI);
e = NEXT_OUT (e);
}
while (e != first_edge);
}
/* Free up the INSN_LISTs
Note this loop is executed max_reg * nr_regions times. It's first
implementation accounted for over 90% of the calls to free_list.
The list was empty for the vast majority of those calls. On the PA,
not calling free_list in those cases improves -O2 compile times by
3-5% on average. */
for (b = 0; b < max_reg; ++b)
{
if (reg_last_clobbers[b])
free_list (&reg_last_clobbers[b], &unused_insn_list);
if (reg_last_sets[b])
free_list (&reg_last_sets[b], &unused_insn_list);
if (reg_last_uses[b])
free_list (&reg_last_uses[b], &unused_insn_list);
}
/* Assert that we won't need bb_reg_last_* for this block anymore. */
if (current_nr_blocks > 1)
{
bb_reg_last_uses[bb] = (rtx *) NULL_RTX;
bb_reg_last_sets[bb] = (rtx *) NULL_RTX;
bb_reg_last_clobbers[bb] = (rtx *) NULL_RTX;
}
}
/* Print dependences for debugging, callable from debugger */
void
debug_dependencies ()
{
int bb;
fprintf (dump, ";; --------------- forward dependences: ------------ \n");
for (bb = 0; bb < current_nr_blocks; bb++)
{
if (1)
{
rtx head, tail;
rtx next_tail;
rtx insn;
get_block_head_tail (bb, &head, &tail);
next_tail = NEXT_INSN (tail);
fprintf (dump, "\n;; --- Region Dependences --- b %d bb %d \n",
BB_TO_BLOCK (bb), bb);
fprintf (dump, ";; %7s%6s%6s%6s%6s%6s%11s%6s\n",
"insn", "code", "bb", "dep", "prio", "cost", "blockage", "units");
fprintf (dump, ";; %7s%6s%6s%6s%6s%6s%11s%6s\n",
"----", "----", "--", "---", "----", "----", "--------", "-----");
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx link;
int unit, range;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
{
int n;
fprintf (dump, ";; %6d ", INSN_UID (insn));
if (GET_CODE (insn) == NOTE)
{
n = NOTE_LINE_NUMBER (insn);
if (n < 0)
fprintf (dump, "%s\n", GET_NOTE_INSN_NAME (n));
else
fprintf (dump, "line %d, file %s\n", n,
NOTE_SOURCE_FILE (insn));
}
else
fprintf (dump, " {%s}\n", GET_RTX_NAME (GET_CODE (insn)));
continue;
}
unit = insn_unit (insn);
range = (unit < 0
|| function_units[unit].blockage_range_function == 0) ? 0 :
function_units[unit].blockage_range_function (insn);
fprintf (dump,
";; %s%5d%6d%6d%6d%6d%6d %3d -%3d ",
(SCHED_GROUP_P (insn) ? "+" : " "),
INSN_UID (insn),
INSN_CODE (insn),
INSN_BB (insn),
INSN_DEP_COUNT (insn),
INSN_PRIORITY (insn),
insn_cost (insn, 0, 0),
(int) MIN_BLOCKAGE_COST (range),
(int) MAX_BLOCKAGE_COST (range));
insn_print_units (insn);
fprintf (dump, "\t: ");
for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
fprintf (dump, "%d ", INSN_UID (XEXP (link, 0)));
fprintf (dump, "\n");
}
}
}
fprintf (dump, "\n");
}
/* Set_priorities: compute priority of each insn in the block */
static int
set_priorities (bb)
int bb;
{
rtx insn;
int n_insn;
rtx tail;
rtx prev_head;
rtx head;
get_block_head_tail (bb, &head, &tail);
prev_head = PREV_INSN (head);
if (head == tail
&& (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
return 0;
n_insn = 0;
for (insn = tail; insn != prev_head; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
continue;
if (!(SCHED_GROUP_P (insn)))
n_insn++;
(void) priority (insn);
}
return n_insn;
}
/* Make each element of VECTOR point at an rtx-vector,
taking the space for all those rtx-vectors from SPACE.
SPACE is of type (rtx *), but it is really as long as NELTS rtx-vectors.
BYTES_PER_ELT is the number of bytes in one rtx-vector.
(this is the same as init_regset_vector () in flow.c) */
static void
init_rtx_vector (vector, space, nelts, bytes_per_elt)
rtx **vector;
rtx *space;
int nelts;
int bytes_per_elt;
{
register int i;
register rtx *p = space;
for (i = 0; i < nelts; i++)
{
vector[i] = p;
p += bytes_per_elt / sizeof (*p);
}
}
/* Schedule a region. A region is either an inner loop, a loop-free
subroutine, or a single basic block. Each bb in the region is
scheduled after its flow predecessors. */
static void
schedule_region (rgn)
int rgn;
{
int bb;
int rgn_n_insns = 0;
int sched_rgn_n_insns = 0;
/* set variables for the current region */
current_nr_blocks = RGN_NR_BLOCKS (rgn);
current_blocks = RGN_BLOCKS (rgn);
reg_pending_sets = ALLOCA_REG_SET ();
reg_pending_clobbers = ALLOCA_REG_SET ();
reg_pending_sets_all = 0;
/* initializations for region data dependence analyisis */
if (current_nr_blocks > 1)
{
rtx *space;
int maxreg = max_reg_num ();
bb_reg_last_uses = (rtx **) alloca (current_nr_blocks * sizeof (rtx *));
space = (rtx *) alloca (current_nr_blocks * maxreg * sizeof (rtx));
bzero ((char *) space, current_nr_blocks * maxreg * sizeof (rtx));
init_rtx_vector (bb_reg_last_uses, space, current_nr_blocks,
maxreg * sizeof (rtx *));
bb_reg_last_sets = (rtx **) alloca (current_nr_blocks * sizeof (rtx *));
space = (rtx *) alloca (current_nr_blocks * maxreg * sizeof (rtx));
bzero ((char *) space, current_nr_blocks * maxreg * sizeof (rtx));
init_rtx_vector (bb_reg_last_sets, space, current_nr_blocks,
maxreg * sizeof (rtx *));
bb_reg_last_clobbers =
(rtx **) alloca (current_nr_blocks * sizeof (rtx *));
space = (rtx *) alloca (current_nr_blocks * maxreg * sizeof (rtx));
bzero ((char *) space, current_nr_blocks * maxreg * sizeof (rtx));
init_rtx_vector (bb_reg_last_clobbers, space, current_nr_blocks,
maxreg * sizeof (rtx *));
bb_pending_read_insns = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
bb_pending_read_mems = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
bb_pending_write_insns =
(rtx *) alloca (current_nr_blocks * sizeof (rtx));
bb_pending_write_mems = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
bb_pending_lists_length =
(int *) alloca (current_nr_blocks * sizeof (int));
bb_last_pending_memory_flush =
(rtx *) alloca (current_nr_blocks * sizeof (rtx));
bb_last_function_call = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
bb_sched_before_next_call =
(rtx *) alloca (current_nr_blocks * sizeof (rtx));
init_rgn_data_dependences (current_nr_blocks);
}
/* compute LOG_LINKS */
for (bb = 0; bb < current_nr_blocks; bb++)
compute_block_backward_dependences (bb);
/* compute INSN_DEPEND */
for (bb = current_nr_blocks - 1; bb >= 0; bb--)
compute_block_forward_dependences (bb);
/* Delete line notes, compute live-regs at block end, and set priorities. */
dead_notes = 0;
for (bb = 0; bb < current_nr_blocks; bb++)
{
if (reload_completed == 0)
find_pre_sched_live (bb);
if (write_symbols != NO_DEBUG)
{
save_line_notes (bb);
rm_line_notes (bb);
}
rgn_n_insns += set_priorities (bb);
}
/* compute interblock info: probabilities, split-edges, dominators, etc. */
if (current_nr_blocks > 1)
{
int i;
prob = (float *) alloca ((current_nr_blocks) * sizeof (float));
bbset_size = current_nr_blocks / HOST_BITS_PER_WIDE_INT + 1;
dom = (bbset *) alloca (current_nr_blocks * sizeof (bbset));
for (i = 0; i < current_nr_blocks; i++)
{
dom[i] = (bbset) alloca (bbset_size * sizeof (HOST_WIDE_INT));
bzero ((char *) dom[i], bbset_size * sizeof (HOST_WIDE_INT));
}
/* edge to bit */
rgn_nr_edges = 0;
edge_to_bit = (int *) alloca (nr_edges * sizeof (int));
for (i = 1; i < nr_edges; i++)
if (CONTAINING_RGN (FROM_BLOCK (i)) == rgn)
EDGE_TO_BIT (i) = rgn_nr_edges++;
rgn_edges = (int *) alloca (rgn_nr_edges * sizeof (int));
rgn_nr_edges = 0;
for (i = 1; i < nr_edges; i++)
if (CONTAINING_RGN (FROM_BLOCK (i)) == (rgn))
rgn_edges[rgn_nr_edges++] = i;
/* split edges */
edgeset_size = rgn_nr_edges / HOST_BITS_PER_WIDE_INT + 1;
pot_split = (edgeset *) alloca (current_nr_blocks * sizeof (edgeset));
ancestor_edges = (edgeset *) alloca (current_nr_blocks * sizeof (edgeset));
for (i = 0; i < current_nr_blocks; i++)
{
pot_split[i] =
(edgeset) alloca (edgeset_size * sizeof (HOST_WIDE_INT));
bzero ((char *) pot_split[i],
edgeset_size * sizeof (HOST_WIDE_INT));
ancestor_edges[i] =
(edgeset) alloca (edgeset_size * sizeof (HOST_WIDE_INT));
bzero ((char *) ancestor_edges[i],
edgeset_size * sizeof (HOST_WIDE_INT));
}
/* compute probabilities, dominators, split_edges */
for (bb = 0; bb < current_nr_blocks; bb++)
compute_dom_prob_ps (bb);
}
/* now we can schedule all blocks */
for (bb = 0; bb < current_nr_blocks; bb++)
{
sched_rgn_n_insns += schedule_block (bb, rgn_n_insns);
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
/* sanity check: verify that all region insns were scheduled */
if (sched_rgn_n_insns != rgn_n_insns)
abort ();
/* update register life and usage information */
if (reload_completed == 0)
{
for (bb = current_nr_blocks - 1; bb >= 0; bb--)
find_post_sched_live (bb);
if (current_nr_blocks <= 1)
/* Sanity check. There should be no REG_DEAD notes leftover at the end.
In practice, this can occur as the result of bugs in flow, combine.c,
and/or sched.c. The values of the REG_DEAD notes remaining are
meaningless, because dead_notes is just used as a free list. */
if (dead_notes != 0)
abort ();
}
/* restore line notes. */
if (write_symbols != NO_DEBUG)
{
for (bb = 0; bb < current_nr_blocks; bb++)
restore_line_notes (bb);
}
/* Done with this region */
free_pending_lists ();
FREE_REG_SET (reg_pending_sets);
FREE_REG_SET (reg_pending_clobbers);
}
/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are
needed for the hard register mentioned in the note. This can happen
if the reference to the hard register in the original insn was split into
several smaller hard register references in the split insns. */
static void
split_hard_reg_notes (note, first, last)
rtx note, first, last;
{
rtx reg, temp, link;
int n_regs, i, new_reg;
rtx insn;
/* Assume that this is a REG_DEAD note. */
if (REG_NOTE_KIND (note) != REG_DEAD)
abort ();
reg = XEXP (note, 0);
n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg));
for (i = 0; i < n_regs; i++)
{
new_reg = REGNO (reg) + i;
/* Check for references to new_reg in the split insns. */
for (insn = last;; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (temp = regno_use_in (new_reg, PATTERN (insn))))
{
/* Create a new reg dead note ere. */
link = alloc_EXPR_LIST (REG_DEAD, temp, REG_NOTES (insn));
REG_NOTES (insn) = link;
/* If killed multiple registers here, then add in the excess. */
i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1;
break;
}
/* It isn't mentioned anywhere, so no new reg note is needed for
this register. */
if (insn == first)
break;
}
}
}
/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an
insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */
static void
new_insn_dead_notes (pat, insn, last, orig_insn)
rtx pat, insn, last, orig_insn;
{
rtx dest, tem, set;
/* PAT is either a CLOBBER or a SET here. */
dest = XEXP (pat, 0);
while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == SIGN_EXTRACT)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG)
{
/* If the original insn already used this register, we may not add new
notes for it. One example for a split that needs this test is
when a multi-word memory access with register-indirect addressing
is split into multiple memory accesses with auto-increment and
one adjusting add instruction for the address register. */
if (reg_referenced_p (dest, PATTERN (orig_insn)))
return;
for (tem = last; tem != insn; tem = PREV_INSN (tem))
{
if (GET_RTX_CLASS (GET_CODE (tem)) == 'i'
&& reg_overlap_mentioned_p (dest, PATTERN (tem))
&& (set = single_set (tem)))
{
rtx tem_dest = SET_DEST (set);
while (GET_CODE (tem_dest) == ZERO_EXTRACT
|| GET_CODE (tem_dest) == SUBREG
|| GET_CODE (tem_dest) == STRICT_LOW_PART
|| GET_CODE (tem_dest) == SIGN_EXTRACT)
tem_dest = XEXP (tem_dest, 0);
if (!rtx_equal_p (tem_dest, dest))
{
/* Use the same scheme as combine.c, don't put both REG_DEAD
and REG_UNUSED notes on the same insn. */
if (!find_regno_note (tem, REG_UNUSED, REGNO (dest))
&& !find_regno_note (tem, REG_DEAD, REGNO (dest)))
{
rtx note = alloc_EXPR_LIST (REG_DEAD, dest,
REG_NOTES (tem));
REG_NOTES (tem) = note;
}
/* The reg only dies in one insn, the last one that uses
it. */
break;
}
else if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
/* We found an instruction that both uses the register,
and sets it, so no new REG_NOTE is needed for this set. */
break;
}
}
/* If this is a set, it must die somewhere, unless it is the dest of
the original insn, and hence is live after the original insn. Abort
if it isn't supposed to be live after the original insn.
If this is a clobber, then just add a REG_UNUSED note. */
if (tem == insn)
{
int live_after_orig_insn = 0;
rtx pattern = PATTERN (orig_insn);
int i;
if (GET_CODE (pat) == CLOBBER)
{
rtx note = alloc_EXPR_LIST (REG_UNUSED, dest, REG_NOTES (insn));
REG_NOTES (insn) = note;
return;
}
/* The original insn could have multiple sets, so search the
insn for all sets. */
if (GET_CODE (pattern) == SET)
{
if (reg_overlap_mentioned_p (dest, SET_DEST (pattern)))
live_after_orig_insn = 1;
}
else if (GET_CODE (pattern) == PARALLEL)
{
for (i = 0; i < XVECLEN (pattern, 0); i++)
if (GET_CODE (XVECEXP (pattern, 0, i)) == SET
&& reg_overlap_mentioned_p (dest,
SET_DEST (XVECEXP (pattern,
0, i))))
live_after_orig_insn = 1;
}
if (!live_after_orig_insn)
abort ();
}
}
}
/* Subroutine of update_flow_info. Update the value of reg_n_sets for all
registers modified by X. INC is -1 if the containing insn is being deleted,
and is 1 if the containing insn is a newly generated insn. */
static void
update_n_sets (x, inc)
rtx x;
int inc;
{
rtx dest = SET_DEST (x);
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
{
int regno = REGNO (dest);
if (regno < FIRST_PSEUDO_REGISTER)
{
register int i;
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest));
for (i = regno; i < endregno; i++)
REG_N_SETS (i) += inc;
}
else
REG_N_SETS (regno) += inc;
}
}
/* Updates all flow-analysis related quantities (including REG_NOTES) for
the insns from FIRST to LAST inclusive that were created by splitting
ORIG_INSN. NOTES are the original REG_NOTES. */
void
update_flow_info (notes, first, last, orig_insn)
rtx notes;
rtx first, last;
rtx orig_insn;
{
rtx insn, note;
rtx next;
rtx orig_dest, temp;
rtx set;
/* Get and save the destination set by the original insn. */
orig_dest = single_set (orig_insn);
if (orig_dest)
orig_dest = SET_DEST (orig_dest);
/* Move REG_NOTES from the original insn to where they now belong. */
for (note = notes; note; note = next)
{
next = XEXP (note, 1);
switch (REG_NOTE_KIND (note))
{
case REG_DEAD:
case REG_UNUSED:
/* Move these notes from the original insn to the last new insn where
the register is now set. */
for (insn = last;; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
{
/* If this note refers to a multiple word hard register, it
may have been split into several smaller hard register
references, so handle it specially. */
temp = XEXP (note, 0);
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (temp) == REG
&& REGNO (temp) < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1)
split_hard_reg_notes (note, first, last);
else
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
}
/* Sometimes need to convert REG_UNUSED notes to REG_DEAD
notes. */
/* ??? This won't handle multiple word registers correctly,
but should be good enough for now. */
if (REG_NOTE_KIND (note) == REG_UNUSED
&& GET_CODE (XEXP (note, 0)) != SCRATCH
&& !dead_or_set_p (insn, XEXP (note, 0)))
PUT_REG_NOTE_KIND (note, REG_DEAD);
/* The reg only dies in one insn, the last one that uses
it. */
break;
}
/* It must die somewhere, fail it we couldn't find where it died.
If this is a REG_UNUSED note, then it must be a temporary
register that was not needed by this instantiation of the
pattern, so we can safely ignore it. */
if (insn == first)
{
if (REG_NOTE_KIND (note) != REG_UNUSED)
abort ();
break;
}
}
break;
case REG_WAS_0:
/* If the insn that set the register to 0 was deleted, this
note cannot be relied on any longer. The destination might
even have been moved to memory.
This was observed for SH4 with execute/920501-6.c compilation,
-O2 -fomit-frame-pointer -finline-functions . */
if (GET_CODE (XEXP (note, 0)) == NOTE
|| INSN_DELETED_P (XEXP (note, 0)))
break;
/* This note applies to the dest of the original insn. Find the
first new insn that now has the same dest, and move the note
there. */
if (!orig_dest)
abort ();
for (insn = first;; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (temp = single_set (insn))
&& rtx_equal_p (SET_DEST (temp), orig_dest))
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* The reg is only zero before one insn, the first that
uses it. */
break;
}
/* If this note refers to a multiple word hard
register, it may have been split into several smaller
hard register references. We could split the notes,
but simply dropping them is good enough. */
if (GET_CODE (orig_dest) == REG
&& REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (REGNO (orig_dest),
GET_MODE (orig_dest)) > 1)
break;
/* It must be set somewhere, fail if we couldn't find where it
was set. */
if (insn == last)
abort ();
}
break;
case REG_EQUAL:
case REG_EQUIV:
/* A REG_EQUIV or REG_EQUAL note on an insn with more than one
set is meaningless. Just drop the note. */
if (!orig_dest)
break;
case REG_NO_CONFLICT:
/* These notes apply to the dest of the original insn. Find the last
new insn that now has the same dest, and move the note there. */
if (!orig_dest)
abort ();
for (insn = last;; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (temp = single_set (insn))
&& rtx_equal_p (SET_DEST (temp), orig_dest))
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* Only put this note on one of the new insns. */
break;
}
/* The original dest must still be set someplace. Abort if we
couldn't find it. */
if (insn == first)
{
/* However, if this note refers to a multiple word hard
register, it may have been split into several smaller
hard register references. We could split the notes,
but simply dropping them is good enough. */
if (GET_CODE (orig_dest) == REG
&& REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (REGNO (orig_dest),
GET_MODE (orig_dest)) > 1)
break;
/* Likewise for multi-word memory references. */
if (GET_CODE (orig_dest) == MEM
&& SIZE_FOR_MODE (orig_dest) > UNITS_PER_WORD)
break;
abort ();
}
}
break;
case REG_LIBCALL:
/* Move a REG_LIBCALL note to the first insn created, and update
the corresponding REG_RETVAL note. */
XEXP (note, 1) = REG_NOTES (first);
REG_NOTES (first) = note;
insn = XEXP (note, 0);
note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
if (note)
XEXP (note, 0) = first;
break;
case REG_EXEC_COUNT:
/* Move a REG_EXEC_COUNT note to the first insn created. */
XEXP (note, 1) = REG_NOTES (first);
REG_NOTES (first) = note;
break;
case REG_RETVAL:
/* Move a REG_RETVAL note to the last insn created, and update
the corresponding REG_LIBCALL note. */
XEXP (note, 1) = REG_NOTES (last);
REG_NOTES (last) = note;
insn = XEXP (note, 0);
note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
if (note)
XEXP (note, 0) = last;
break;
case REG_NONNEG:
case REG_BR_PROB:
/* This should be moved to whichever instruction is a JUMP_INSN. */
for (insn = last;; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN)
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* Only put this note on one of the new insns. */
break;
}
/* Fail if we couldn't find a JUMP_INSN. */
if (insn == first)
abort ();
}
break;
case REG_INC:
/* reload sometimes leaves obsolete REG_INC notes around. */
if (reload_completed)
break;
/* This should be moved to whichever instruction now has the
increment operation. */
abort ();
case REG_LABEL:
/* Should be moved to the new insn(s) which use the label. */
for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
{
REG_NOTES (insn) = alloc_EXPR_LIST (REG_LABEL,
XEXP (note, 0),
REG_NOTES (insn));
}
break;
case REG_CC_SETTER:
case REG_CC_USER:
/* These two notes will never appear until after reorg, so we don't
have to handle them here. */
default:
abort ();
}
}
/* Each new insn created, except the last, has a new set. If the destination
is a register, then this reg is now live across several insns, whereas
previously the dest reg was born and died within the same insn. To
reflect this, we now need a REG_DEAD note on the insn where this
dest reg dies.
Similarly, the new insns may have clobbers that need REG_UNUSED notes. */
for (insn = first; insn != last; insn = NEXT_INSN (insn))
{
rtx pat;
int i;
pat = PATTERN (insn);
if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER)
new_insn_dead_notes (pat, insn, last, orig_insn);
else if (GET_CODE (pat) == PARALLEL)
{
for (i = 0; i < XVECLEN (pat, 0); i++)
if (GET_CODE (XVECEXP (pat, 0, i)) == SET
|| GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER)
new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn);
}
}
/* If any insn, except the last, uses the register set by the last insn,
then we need a new REG_DEAD note on that insn. In this case, there
would not have been a REG_DEAD note for this register in the original
insn because it was used and set within one insn. */
set = single_set (last);
if (set)
{
rtx dest = SET_DEST (set);
while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == SIGN_EXTRACT)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG
/* Global registers are always live, so the code below does not
apply to them. */
&& (REGNO (dest) >= FIRST_PSEUDO_REGISTER
|| ! global_regs[REGNO (dest)]))
{
rtx stop_insn = PREV_INSN (first);
/* If the last insn uses the register that it is setting, then
we don't want to put a REG_DEAD note there. Search backwards
to find the first insn that sets but does not use DEST. */
insn = last;
if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
{
for (insn = PREV_INSN (insn); insn != first;
insn = PREV_INSN (insn))
{
if ((set = single_set (insn))
&& reg_mentioned_p (dest, SET_DEST (set))
&& ! reg_overlap_mentioned_p (dest, SET_SRC (set)))
break;
}
}
/* Now find the first insn that uses but does not set DEST. */
for (insn = PREV_INSN (insn); insn != stop_insn;
insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (dest, PATTERN (insn))
&& (set = single_set (insn)))
{
rtx insn_dest = SET_DEST (set);
while (GET_CODE (insn_dest) == ZERO_EXTRACT
|| GET_CODE (insn_dest) == SUBREG
|| GET_CODE (insn_dest) == STRICT_LOW_PART
|| GET_CODE (insn_dest) == SIGN_EXTRACT)
insn_dest = XEXP (insn_dest, 0);
if (insn_dest != dest)
{
note = alloc_EXPR_LIST (REG_DEAD, dest, REG_NOTES (insn));
REG_NOTES (insn) = note;
/* The reg only dies in one insn, the last one
that uses it. */
break;
}
}
}
}
}
/* If the original dest is modifying a multiple register target, and the
original instruction was split such that the original dest is now set
by two or more SUBREG sets, then the split insns no longer kill the
destination of the original insn.
In this case, if there exists an instruction in the same basic block,
before the split insn, which uses the original dest, and this use is
killed by the original insn, then we must remove the REG_DEAD note on
this insn, because it is now superfluous.
This does not apply when a hard register gets split, because the code
knows how to handle overlapping hard registers properly. */
if (orig_dest && GET_CODE (orig_dest) == REG)
{
int found_orig_dest = 0;
int found_split_dest = 0;
for (insn = first;; insn = NEXT_INSN (insn))
{
rtx pat;
int i;
/* I'm not sure if this can happen, but let's be safe. */
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
pat = PATTERN (insn);
i = GET_CODE (pat) == PARALLEL ? XVECLEN (pat, 0) : 0;
set = pat;
for (;;)
{
if (GET_CODE (set) == SET)
{
if (GET_CODE (SET_DEST (set)) == REG
&& REGNO (SET_DEST (set)) == REGNO (orig_dest))
{
found_orig_dest = 1;
break;
}
else if (GET_CODE (SET_DEST (set)) == SUBREG
&& SUBREG_REG (SET_DEST (set)) == orig_dest)
{
found_split_dest = 1;
break;
}
}
if (--i < 0)
break;
set = XVECEXP (pat, 0, i);
}
if (insn == last)
break;
}
if (found_split_dest)
{
/* Search backwards from FIRST, looking for the first insn that uses
the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN.
If we find an insn, and it has a REG_DEAD note, then delete the
note. */
for (insn = first; insn; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == JUMP_INSN)
break;
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (orig_dest, insn))
{
note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest));
if (note)
remove_note (insn, note);
}
}
}
else if (!found_orig_dest)
{
int i, regno;
/* Should never reach here for a pseudo reg. */
if (REGNO (orig_dest) >= FIRST_PSEUDO_REGISTER)
abort ();
/* This can happen for a hard register, if the splitter
does not bother to emit instructions which would be no-ops.
We try to verify that this is the case by checking to see if
the original instruction uses all of the registers that it
set. This case is OK, because deleting a no-op can not affect
REG_DEAD notes on other insns. If this is not the case, then
abort. */
regno = REGNO (orig_dest);
for (i = HARD_REGNO_NREGS (regno, GET_MODE (orig_dest)) - 1;
i >= 0; i--)
if (! refers_to_regno_p (regno + i, regno + i + 1, orig_insn,
NULL_PTR))
break;
if (i >= 0)
abort ();
}
}
/* Update reg_n_sets. This is necessary to prevent local alloc from
converting REG_EQUAL notes to REG_EQUIV when splitting has modified
a reg from set once to set multiple times. */
{
rtx x = PATTERN (orig_insn);
RTX_CODE code = GET_CODE (x);
if (code == SET || code == CLOBBER)
update_n_sets (x, -1);
else if (code == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
update_n_sets (XVECEXP (x, 0, i), -1);
}
}
for (insn = first;; insn = NEXT_INSN (insn))
{
x = PATTERN (insn);
code = GET_CODE (x);
if (code == SET || code == CLOBBER)
update_n_sets (x, 1);
else if (code == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
update_n_sets (XVECEXP (x, 0, i), 1);
}
}
if (insn == last)
break;
}
}
}
/* The one entry point in this file. DUMP_FILE is the dump file for
this pass. */
void
schedule_insns (dump_file)
FILE *dump_file;
{
int max_uid;
int b;
rtx insn;
int rgn;
int luid;
/* disable speculative loads in their presence if cc0 defined */
#ifdef HAVE_cc0
flag_schedule_speculative_load = 0;
#endif
/* Taking care of this degenerate case makes the rest of
this code simpler. */
if (n_basic_blocks == 0)
return;
/* set dump and sched_verbose for the desired debugging output. If no
dump-file was specified, but -fsched-verbose-N (any N), print to stderr.
For -fsched-verbose-N, N>=10, print everything to stderr. */
sched_verbose = sched_verbose_param;
if (sched_verbose_param == 0 && dump_file)
sched_verbose = 1;
dump = ((sched_verbose_param >= 10 || !dump_file) ? stderr : dump_file);
nr_inter = 0;
nr_spec = 0;
/* Initialize the unused_*_lists. We can't use the ones left over from
the previous function, because gcc has freed that memory. We can use
the ones left over from the first sched pass in the second pass however,
so only clear them on the first sched pass. The first pass is before
reload if flag_schedule_insns is set, otherwise it is afterwards. */
if (reload_completed == 0 || !flag_schedule_insns)
{
unused_insn_list = 0;
unused_expr_list = 0;
}
/* initialize issue_rate */
issue_rate = ISSUE_RATE;
/* do the splitting first for all blocks */
for (b = 0; b < n_basic_blocks; b++)
split_block_insns (b, 1);
max_uid = (get_max_uid () + 1);
cant_move = (char *) xmalloc (max_uid * sizeof (char));
bzero ((char *) cant_move, max_uid * sizeof (char));
fed_by_spec_load = (char *) xmalloc (max_uid * sizeof (char));
bzero ((char *) fed_by_spec_load, max_uid * sizeof (char));
is_load_insn = (char *) xmalloc (max_uid * sizeof (char));
bzero ((char *) is_load_insn, max_uid * sizeof (char));
insn_orig_block = (int *) xmalloc (max_uid * sizeof (int));
insn_luid = (int *) xmalloc (max_uid * sizeof (int));
luid = 0;
for (b = 0; b < n_basic_blocks; b++)
for (insn = BLOCK_HEAD (b);; insn = NEXT_INSN (insn))
{
INSN_BLOCK (insn) = b;
INSN_LUID (insn) = luid++;
if (insn == BLOCK_END (b))
break;
}
/* after reload, remove inter-blocks dependences computed before reload. */
if (reload_completed)
{
int b;
rtx insn;
for (b = 0; b < n_basic_blocks; b++)
for (insn = BLOCK_HEAD (b);; insn = NEXT_INSN (insn))
{
rtx link, prev;
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
prev = NULL_RTX;
link = LOG_LINKS (insn);
while (link)
{
rtx x = XEXP (link, 0);
if (INSN_BLOCK (x) != b)
{
remove_dependence (insn, x);
link = prev ? XEXP (prev, 1) : LOG_LINKS (insn);
}
else
prev = link, link = XEXP (prev, 1);
}
}
if (insn == BLOCK_END (b))
break;
}
}
nr_regions = 0;
rgn_table = (region *) alloca ((n_basic_blocks) * sizeof (region));
rgn_bb_table = (int *) alloca ((n_basic_blocks) * sizeof (int));
block_to_bb = (int *) alloca ((n_basic_blocks) * sizeof (int));
containing_rgn = (int *) alloca ((n_basic_blocks) * sizeof (int));
/* compute regions for scheduling */
if (reload_completed
|| n_basic_blocks == 1
|| !flag_schedule_interblock)
{
find_single_block_region ();
}
else
{
/* verify that a 'good' control flow graph can be built */
if (is_cfg_nonregular ())
{
find_single_block_region ();
}
else
{
int_list_ptr *s_preds, *s_succs;
int *num_preds, *num_succs;
sbitmap *dom, *pdom;
s_preds = (int_list_ptr *) alloca (n_basic_blocks
* sizeof (int_list_ptr));
s_succs = (int_list_ptr *) alloca (n_basic_blocks
* sizeof (int_list_ptr));
num_preds = (int *) alloca (n_basic_blocks * sizeof (int));
num_succs = (int *) alloca (n_basic_blocks * sizeof (int));
dom = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
pdom = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
/* The scheduler runs after flow; therefore, we can't blindly call
back into find_basic_blocks since doing so could invalidate the
info in global_live_at_start.
Consider a block consisting entirely of dead stores; after life
analysis it would be a block of NOTE_INSN_DELETED notes. If
we call find_basic_blocks again, then the block would be removed
entirely and invalidate our the register live information.
We could (should?) recompute register live information. Doing
so may even be beneficial. */
compute_preds_succs (s_preds, s_succs, num_preds, num_succs);
/* Compute the dominators and post dominators. We don't currently use
post dominators, but we should for speculative motion analysis. */
compute_dominators (dom, pdom, s_preds, s_succs);
/* build_control_flow will return nonzero if it detects unreachable
blocks or any other irregularity with the cfg which prevents
cross block scheduling. */
if (build_control_flow (s_preds, s_succs, num_preds, num_succs) != 0)
find_single_block_region ();
else
find_rgns (s_preds, s_succs, num_preds, num_succs, dom);
if (sched_verbose >= 3)
debug_regions ();
/* For now. This will move as more and more of haifa is converted
to using the cfg code in flow.c */
free_bb_mem ();
free (dom);
free (pdom);
}
}
/* Allocate data for this pass. See comments, above,
for what these vectors do.
We use xmalloc instead of alloca, because max_uid can be very large
when there is a lot of function inlining. If we used alloca, we could
exceed stack limits on some hosts for some inputs. */
insn_priority = (int *) xmalloc (max_uid * sizeof (int));
insn_reg_weight = (int *) xmalloc (max_uid * sizeof (int));
insn_tick = (int *) xmalloc (max_uid * sizeof (int));
insn_costs = (short *) xmalloc (max_uid * sizeof (short));
insn_units = (short *) xmalloc (max_uid * sizeof (short));
insn_blockage = (unsigned int *) xmalloc (max_uid * sizeof (unsigned int));
insn_ref_count = (int *) xmalloc (max_uid * sizeof (int));
/* Allocate for forward dependencies */
insn_dep_count = (int *) xmalloc (max_uid * sizeof (int));
insn_depend = (rtx *) xmalloc (max_uid * sizeof (rtx));
if (reload_completed == 0)
{
int i;
sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int));
sched_reg_live_length = (int *) alloca (max_regno * sizeof (int));
sched_reg_basic_block = (int *) alloca (max_regno * sizeof (int));
bb_live_regs = ALLOCA_REG_SET ();
bzero ((char *) sched_reg_n_calls_crossed, max_regno * sizeof (int));
bzero ((char *) sched_reg_live_length, max_regno * sizeof (int));
for (i = 0; i < max_regno; i++)
sched_reg_basic_block[i] = REG_BLOCK_UNKNOWN;
}
else
{
sched_reg_n_calls_crossed = 0;
sched_reg_live_length = 0;
bb_live_regs = 0;
}
init_alias_analysis ();
if (write_symbols != NO_DEBUG)
{
rtx line;
line_note = (rtx *) xmalloc (max_uid * sizeof (rtx));
bzero ((char *) line_note, max_uid * sizeof (rtx));
line_note_head = (rtx *) alloca (n_basic_blocks * sizeof (rtx));
bzero ((char *) line_note_head, n_basic_blocks * sizeof (rtx));
/* Save-line-note-head:
Determine the line-number at the start of each basic block.
This must be computed and saved now, because after a basic block's
predecessor has been scheduled, it is impossible to accurately
determine the correct line number for the first insn of the block. */
for (b = 0; b < n_basic_blocks; b++)
for (line = BLOCK_HEAD (b); line; line = PREV_INSN (line))
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
{
line_note_head[b] = line;
break;
}
}
bzero ((char *) insn_priority, max_uid * sizeof (int));
bzero ((char *) insn_reg_weight, max_uid * sizeof (int));
bzero ((char *) insn_tick, max_uid * sizeof (int));
bzero ((char *) insn_costs, max_uid * sizeof (short));
bzero ((char *) insn_units, max_uid * sizeof (short));
bzero ((char *) insn_blockage, max_uid * sizeof (unsigned int));
bzero ((char *) insn_ref_count, max_uid * sizeof (int));
/* Initialize for forward dependencies */
bzero ((char *) insn_depend, max_uid * sizeof (rtx));
bzero ((char *) insn_dep_count, max_uid * sizeof (int));
/* Find units used in this fuction, for visualization */
if (sched_verbose)
init_target_units ();
/* ??? Add a NOTE after the last insn of the last basic block. It is not
known why this is done. */
insn = BLOCK_END (n_basic_blocks - 1);
if (NEXT_INSN (insn) == 0
|| (GET_CODE (insn) != NOTE
&& GET_CODE (insn) != CODE_LABEL
/* Don't emit a NOTE if it would end up between an unconditional
jump and a BARRIER. */
&& !(GET_CODE (insn) == JUMP_INSN
&& GET_CODE (NEXT_INSN (insn)) == BARRIER)))
emit_note_after (NOTE_INSN_DELETED, BLOCK_END (n_basic_blocks - 1));
/* Schedule every region in the subroutine */
for (rgn = 0; rgn < nr_regions; rgn++)
{
schedule_region (rgn);
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
/* Reposition the prologue and epilogue notes in case we moved the
prologue/epilogue insns. */
if (reload_completed)
reposition_prologue_and_epilogue_notes (get_insns ());
/* delete redundant line notes. */
if (write_symbols != NO_DEBUG)
rm_redundant_line_notes ();
/* Update information about uses of registers in the subroutine. */
if (reload_completed == 0)
update_reg_usage ();
if (sched_verbose)
{
if (reload_completed == 0 && flag_schedule_interblock)
{
fprintf (dump, "\n;; Procedure interblock/speculative motions == %d/%d \n",
nr_inter, nr_spec);
}
else
{
if (nr_inter > 0)
abort ();
}
fprintf (dump, "\n\n");
}
free (cant_move);
free (fed_by_spec_load);
free (is_load_insn);
free (insn_orig_block);
free (insn_luid);
free (insn_priority);
free (insn_reg_weight);
free (insn_tick);
free (insn_costs);
free (insn_units);
free (insn_blockage);
free (insn_ref_count);
free (insn_dep_count);
free (insn_depend);
if (write_symbols != NO_DEBUG)
free (line_note);
if (bb_live_regs)
FREE_REG_SET (bb_live_regs);
if (edge_table)
{
free (edge_table);
edge_table = NULL;
}
if (in_edges)
{
free (in_edges);
in_edges = NULL;
}
if (out_edges)
{
free (out_edges);
out_edges = NULL;
}
}
#endif /* INSN_SCHEDULING */
Index: head/contrib/gcc/jump.c
===================================================================
--- head/contrib/gcc/jump.c (revision 52750)
+++ head/contrib/gcc/jump.c (revision 52751)
@@ -1,5158 +1,5182 @@
/* Optimize jump instructions, for GNU compiler.
Copyright (C) 1987, 88, 89, 91-98, 1999 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 is the jump-optimization pass of the compiler.
It is run two or three times: once before cse, sometimes once after cse,
and once after reload (before final).
jump_optimize deletes unreachable code and labels that are not used.
It also deletes jumps that jump to the following insn,
and simplifies jumps around unconditional jumps and jumps
to unconditional jumps.
Each CODE_LABEL has a count of the times it is used
stored in the LABEL_NUSES internal field, and each JUMP_INSN
has one label that it refers to stored in the
JUMP_LABEL internal field. With this we can detect labels that
become unused because of the deletion of all the jumps that
formerly used them. The JUMP_LABEL info is sometimes looked
at by later passes.
Optionally, cross-jumping can be done. Currently it is done
only the last time (when after reload and before final).
In fact, the code for cross-jumping now assumes that register
allocation has been done, since it uses `rtx_renumbered_equal_p'.
Jump optimization is done after cse when cse's constant-propagation
causes jumps to become unconditional or to be deleted.
Unreachable loops are not detected here, because the labels
have references and the insns appear reachable from the labels.
find_basic_blocks in flow.c finds and deletes such loops.
The subroutines delete_insn, redirect_jump, and invert_jump are used
from other passes as well. */
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "flags.h"
#include "hard-reg-set.h"
#include "regs.h"
#include "insn-config.h"
#include "insn-flags.h"
#include "insn-attr.h"
#include "recog.h"
#include "expr.h"
#include "real.h"
#include "except.h"
#include "toplev.h"
/* ??? Eventually must record somehow the labels used by jumps
from nested functions. */
/* Pre-record the next or previous real insn for each label?
No, this pass is very fast anyway. */
/* Condense consecutive labels?
This would make life analysis faster, maybe. */
/* Optimize jump y; x: ... y: jumpif... x?
Don't know if it is worth bothering with. */
/* Optimize two cases of conditional jump to conditional jump?
This can never delete any instruction or make anything dead,
or even change what is live at any point.
So perhaps let combiner do it. */
/* Vector indexed by uid.
For each CODE_LABEL, index by its uid to get first unconditional jump
that jumps to the label.
For each JUMP_INSN, index by its uid to get the next unconditional jump
that jumps to the same label.
Element 0 is the start of a chain of all return insns.
(It is safe to use element 0 because insn uid 0 is not used. */
static rtx *jump_chain;
/* List of labels referred to from initializers.
These can never be deleted. */
rtx forced_labels;
/* Maximum index in jump_chain. */
static int max_jump_chain;
/* Set nonzero by jump_optimize if control can fall through
to the end of the function. */
int can_reach_end;
/* Indicates whether death notes are significant in cross jump analysis.
Normally they are not significant, because of A and B jump to C,
and R dies in A, it must die in B. But this might not be true after
stack register conversion, and we must compare death notes in that
case. */
static int cross_jump_death_matters = 0;
static int init_label_info PROTO((rtx));
static void delete_barrier_successors PROTO((rtx));
static void mark_all_labels PROTO((rtx, int));
static rtx delete_unreferenced_labels PROTO((rtx));
static void delete_noop_moves PROTO((rtx));
static int calculate_can_reach_end PROTO((rtx, int, int));
static int duplicate_loop_exit_test PROTO((rtx));
static void find_cross_jump PROTO((rtx, rtx, int, rtx *, rtx *));
static void do_cross_jump PROTO((rtx, rtx, rtx));
static int jump_back_p PROTO((rtx, rtx));
static int tension_vector_labels PROTO((rtx, int));
static void mark_jump_label PROTO((rtx, rtx, int));
static void delete_computation PROTO((rtx));
static void delete_from_jump_chain PROTO((rtx));
static int delete_labelref_insn PROTO((rtx, rtx, int));
static void mark_modified_reg PROTO((rtx, rtx));
static void redirect_tablejump PROTO((rtx, rtx));
static void jump_optimize_1 PROTO ((rtx, int, int, int, int));
#ifndef HAVE_cc0
static rtx find_insert_position PROTO((rtx, rtx));
#endif
/* Main external entry point into the jump optimizer. See comments before
jump_optimize_1 for descriptions of the arguments. */
void
jump_optimize (f, cross_jump, noop_moves, after_regscan)
rtx f;
int cross_jump;
int noop_moves;
int after_regscan;
{
jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, 0);
}
/* Alternate entry into the jump optimizer. This entry point only rebuilds
the JUMP_LABEL field in jumping insns and REG_LABEL notes in non-jumping
instructions. */
void
rebuild_jump_labels (f)
rtx f;
{
jump_optimize_1 (f, 0, 0, 0, 1);
}
/* Delete no-op jumps and optimize jumps to jumps
and jumps around jumps.
Delete unused labels and unreachable code.
If CROSS_JUMP is 1, detect matching code
before a jump and its destination and unify them.
If CROSS_JUMP is 2, do cross-jumping, but pay attention to death notes.
If NOOP_MOVES is nonzero, delete no-op move insns.
If AFTER_REGSCAN is nonzero, then this jump pass is being run immediately
after regscan, and it is safe to use regno_first_uid and regno_last_uid.
If MARK_LABELS_ONLY is nonzero, then we only rebuild the jump chain
and JUMP_LABEL field for jumping insns.
If `optimize' is zero, don't change any code,
just determine whether control drops off the end of the function.
This case occurs when we have -W and not -O.
It works because `delete_insn' checks the value of `optimize'
and refrains from actually deleting when that is 0. */
static void
jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, mark_labels_only)
rtx f;
int cross_jump;
int noop_moves;
int after_regscan;
int mark_labels_only;
{
register rtx insn, next;
int changed;
int old_max_reg;
int first = 1;
int max_uid = 0;
rtx last_insn;
cross_jump_death_matters = (cross_jump == 2);
max_uid = init_label_info (f) + 1;
/* If we are performing cross jump optimizations, then initialize
tables mapping UIDs to EH regions to avoid incorrect movement
of insns from one EH region to another. */
if (flag_exceptions && cross_jump)
init_insn_eh_region (f, max_uid);
delete_barrier_successors (f);
/* Leave some extra room for labels and duplicate exit test insns
we make. */
max_jump_chain = max_uid * 14 / 10;
jump_chain = (rtx *) alloca (max_jump_chain * sizeof (rtx));
bzero ((char *) jump_chain, max_jump_chain * sizeof (rtx));
mark_all_labels (f, cross_jump);
/* Keep track of labels used from static data;
they cannot ever be deleted. */
for (insn = forced_labels; insn; insn = XEXP (insn, 1))
LABEL_NUSES (XEXP (insn, 0))++;
check_exception_handler_labels ();
/* Keep track of labels used for marking handlers for exception
regions; they cannot usually be deleted. */
for (insn = exception_handler_labels; insn; insn = XEXP (insn, 1))
LABEL_NUSES (XEXP (insn, 0))++;
/* Quit now if we just wanted to rebuild the JUMP_LABEL and REG_LABEL
notes and recompute LABEL_NUSES. */
if (mark_labels_only)
return;
exception_optimize ();
last_insn = delete_unreferenced_labels (f);
if (!optimize)
{
/* CAN_REACH_END is persistent for each function. Once set it should
not be cleared. This is especially true for the case where we
delete the NOTE_FUNCTION_END note. CAN_REACH_END is cleared by
the front-end before compiling each function. */
if (calculate_can_reach_end (last_insn, 1, 0))
can_reach_end = 1;
/* Zero the "deleted" flag of all the "deleted" insns. */
for (insn = f; insn; insn = NEXT_INSN (insn))
INSN_DELETED_P (insn) = 0;
/* Show that the jump chain is not valid. */
jump_chain = 0;
return;
}
#ifdef HAVE_return
if (HAVE_return)
{
/* If we fall through to the epilogue, see if we can insert a RETURN insn
in front of it. If the machine allows it at this point (we might be
after reload for a leaf routine), it will improve optimization for it
to be there. */
insn = get_last_insn ();
while (insn && GET_CODE (insn) == NOTE)
insn = PREV_INSN (insn);
if (insn && GET_CODE (insn) != BARRIER)
{
emit_jump_insn (gen_return ());
emit_barrier ();
}
}
#endif
if (noop_moves)
delete_noop_moves (f);
/* If we haven't yet gotten to reload and we have just run regscan,
delete any insn that sets a register that isn't used elsewhere.
This helps some of the optimizations below by having less insns
being jumped around. */
if (! reload_completed && after_regscan)
for (insn = f; insn; insn = next)
{
rtx set = single_set (insn);
next = NEXT_INSN (insn);
if (set && GET_CODE (SET_DEST (set)) == REG
&& REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
&& REGNO_FIRST_UID (REGNO (SET_DEST (set))) == INSN_UID (insn)
/* We use regno_last_note_uid so as not to delete the setting
of a reg that's used in notes. A subsequent optimization
might arrange to use that reg for real. */
&& REGNO_LAST_NOTE_UID (REGNO (SET_DEST (set))) == INSN_UID (insn)
&& ! side_effects_p (SET_SRC (set))
&& ! find_reg_note (insn, REG_RETVAL, 0))
delete_insn (insn);
}
/* Now iterate optimizing jumps until nothing changes over one pass. */
changed = 1;
old_max_reg = max_reg_num ();
while (changed)
{
changed = 0;
for (insn = f; insn; insn = next)
{
rtx reallabelprev;
rtx temp, temp1, temp2, temp3, temp4, temp5, temp6;
rtx nlabel;
int this_is_simplejump, this_is_condjump, reversep = 0;
int this_is_condjump_in_parallel;
#if 0
/* If NOT the first iteration, if this is the last jump pass
(just before final), do the special peephole optimizations.
Avoiding the first iteration gives ordinary jump opts
a chance to work before peephole opts. */
if (reload_completed && !first && !flag_no_peephole)
if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
peephole (insn);
#endif
/* That could have deleted some insns after INSN, so check now
what the following insn is. */
next = NEXT_INSN (insn);
/* See if this is a NOTE_INSN_LOOP_BEG followed by an unconditional
jump. Try to optimize by duplicating the loop exit test if so.
This is only safe immediately after regscan, because it uses
the values of regno_first_uid and regno_last_uid. */
if (after_regscan && GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
&& (temp1 = next_nonnote_insn (insn)) != 0
&& simplejump_p (temp1))
{
temp = PREV_INSN (insn);
if (duplicate_loop_exit_test (insn))
{
changed = 1;
next = NEXT_INSN (temp);
continue;
}
}
if (GET_CODE (insn) != JUMP_INSN)
continue;
this_is_simplejump = simplejump_p (insn);
this_is_condjump = condjump_p (insn);
this_is_condjump_in_parallel = condjump_in_parallel_p (insn);
/* Tension the labels in dispatch tables. */
if (GET_CODE (PATTERN (insn)) == ADDR_VEC)
changed |= tension_vector_labels (PATTERN (insn), 0);
if (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
changed |= tension_vector_labels (PATTERN (insn), 1);
/* If a dispatch table always goes to the same place,
get rid of it and replace the insn that uses it. */
if (GET_CODE (PATTERN (insn)) == ADDR_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
{
int i;
rtx pat = PATTERN (insn);
int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC;
int len = XVECLEN (pat, diff_vec_p);
rtx dispatch = prev_real_insn (insn);
+ rtx set;
for (i = 0; i < len; i++)
if (XEXP (XVECEXP (pat, diff_vec_p, i), 0)
!= XEXP (XVECEXP (pat, diff_vec_p, 0), 0))
break;
+
if (i == len
&& dispatch != 0
&& GET_CODE (dispatch) == JUMP_INSN
&& JUMP_LABEL (dispatch) != 0
- /* Don't mess with a casesi insn. */
- && !(GET_CODE (PATTERN (dispatch)) == SET
- && (GET_CODE (SET_SRC (PATTERN (dispatch)))
- == IF_THEN_ELSE))
+ /* Don't mess with a casesi insn.
+ XXX according to the comment before computed_jump_p(),
+ all casesi insns should be a parallel of the jump
+ and a USE of a LABEL_REF. */
+ && ! ((set = single_set (dispatch)) != NULL
+ && (GET_CODE (SET_SRC (set)) == IF_THEN_ELSE))
&& next_real_insn (JUMP_LABEL (dispatch)) == insn)
{
redirect_tablejump (dispatch,
XEXP (XVECEXP (pat, diff_vec_p, 0), 0));
changed = 1;
}
}
reallabelprev = prev_active_insn (JUMP_LABEL (insn));
/* If a jump references the end of the function, try to turn
it into a RETURN insn, possibly a conditional one. */
if (JUMP_LABEL (insn)
&& (next_active_insn (JUMP_LABEL (insn)) == 0
|| GET_CODE (PATTERN (next_active_insn (JUMP_LABEL (insn))))
== RETURN))
changed |= redirect_jump (insn, NULL_RTX);
/* Detect jump to following insn. */
if (reallabelprev == insn && condjump_p (insn))
{
next = next_real_insn (JUMP_LABEL (insn));
delete_jump (insn);
changed = 1;
continue;
}
/* If we have an unconditional jump preceded by a USE, try to put
the USE before the target and jump there. This simplifies many
of the optimizations below since we don't have to worry about
dealing with these USE insns. We only do this if the label
being branch to already has the identical USE or if code
never falls through to that label. */
if (this_is_simplejump
&& (temp = prev_nonnote_insn (insn)) != 0
&& GET_CODE (temp) == INSN && GET_CODE (PATTERN (temp)) == USE
&& (temp1 = prev_nonnote_insn (JUMP_LABEL (insn))) != 0
&& (GET_CODE (temp1) == BARRIER
|| (GET_CODE (temp1) == INSN
&& rtx_equal_p (PATTERN (temp), PATTERN (temp1))))
/* Don't do this optimization if we have a loop containing only
the USE instruction, and the loop start label has a usage
count of 1. This is because we will redo this optimization
everytime through the outer loop, and jump opt will never
exit. */
&& ! ((temp2 = prev_nonnote_insn (temp)) != 0
&& temp2 == JUMP_LABEL (insn)
&& LABEL_NUSES (temp2) == 1))
{
if (GET_CODE (temp1) == BARRIER)
{
emit_insn_after (PATTERN (temp), temp1);
temp1 = NEXT_INSN (temp1);
}
delete_insn (temp);
redirect_jump (insn, get_label_before (temp1));
reallabelprev = prev_real_insn (temp1);
changed = 1;
}
/* Simplify if (...) x = a; else x = b; by converting it
to x = b; if (...) x = a;
if B is sufficiently simple, the test doesn't involve X,
and nothing in the test modifies B or X.
If we have small register classes, we also can't do this if X
is a hard register.
If the "x = b;" insn has any REG_NOTES, we don't do this because
of the possibility that we are running after CSE and there is a
REG_EQUAL note that is only valid if the branch has already been
taken. If we move the insn with the REG_EQUAL note, we may
fold the comparison to always be false in a later CSE pass.
(We could also delete the REG_NOTES when moving the insn, but it
seems simpler to not move it.) An exception is that we can move
the insn if the only note is a REG_EQUAL or REG_EQUIV whose
value is the same as "b".
INSN is the branch over the `else' part.
We set:
TEMP to the jump insn preceding "x = a;"
TEMP1 to X
TEMP2 to the insn that sets "x = b;"
TEMP3 to the insn that sets "x = a;"
TEMP4 to the set of "x = b"; */
if (this_is_simplejump
&& (temp3 = prev_active_insn (insn)) != 0
&& GET_CODE (temp3) == INSN
&& (temp4 = single_set (temp3)) != 0
&& GET_CODE (temp1 = SET_DEST (temp4)) == REG
&& (! SMALL_REGISTER_CLASSES
|| REGNO (temp1) >= FIRST_PSEUDO_REGISTER)
&& (temp2 = next_active_insn (insn)) != 0
&& GET_CODE (temp2) == INSN
&& (temp4 = single_set (temp2)) != 0
&& rtx_equal_p (SET_DEST (temp4), temp1)
&& ! side_effects_p (SET_SRC (temp4))
&& ! may_trap_p (SET_SRC (temp4))
&& (REG_NOTES (temp2) == 0
|| ((REG_NOTE_KIND (REG_NOTES (temp2)) == REG_EQUAL
|| REG_NOTE_KIND (REG_NOTES (temp2)) == REG_EQUIV)
&& XEXP (REG_NOTES (temp2), 1) == 0
&& rtx_equal_p (XEXP (REG_NOTES (temp2), 0),
SET_SRC (temp4))))
&& (temp = prev_active_insn (temp3)) != 0
&& condjump_p (temp) && ! simplejump_p (temp)
/* TEMP must skip over the "x = a;" insn */
&& prev_real_insn (JUMP_LABEL (temp)) == insn
&& no_labels_between_p (insn, JUMP_LABEL (temp))
/* There must be no other entries to the "x = b;" insn. */
&& no_labels_between_p (JUMP_LABEL (temp), temp2)
/* INSN must either branch to the insn after TEMP2 or the insn
after TEMP2 must branch to the same place as INSN. */
&& (reallabelprev == temp2
|| ((temp5 = next_active_insn (temp2)) != 0
&& simplejump_p (temp5)
&& JUMP_LABEL (temp5) == JUMP_LABEL (insn))))
{
/* The test expression, X, may be a complicated test with
multiple branches. See if we can find all the uses of
the label that TEMP branches to without hitting a CALL_INSN
or a jump to somewhere else. */
rtx target = JUMP_LABEL (temp);
int nuses = LABEL_NUSES (target);
rtx p;
#ifdef HAVE_cc0
rtx q;
#endif
/* Set P to the first jump insn that goes around "x = a;". */
for (p = temp; nuses && p; p = prev_nonnote_insn (p))
{
if (GET_CODE (p) == JUMP_INSN)
{
if (condjump_p (p) && ! simplejump_p (p)
&& JUMP_LABEL (p) == target)
{
nuses--;
if (nuses == 0)
break;
}
else
break;
}
else if (GET_CODE (p) == CALL_INSN)
break;
}
#ifdef HAVE_cc0
/* We cannot insert anything between a set of cc and its use
so if P uses cc0, we must back up to the previous insn. */
q = prev_nonnote_insn (p);
if (q && GET_RTX_CLASS (GET_CODE (q)) == 'i'
&& sets_cc0_p (PATTERN (q)))
p = q;
#endif
if (p)
p = PREV_INSN (p);
/* If we found all the uses and there was no data conflict, we
can move the assignment unless we can branch into the middle
from somewhere. */
if (nuses == 0 && p
&& no_labels_between_p (p, insn)
&& ! reg_referenced_between_p (temp1, p, NEXT_INSN (temp3))
&& ! reg_set_between_p (temp1, p, temp3)
&& (GET_CODE (SET_SRC (temp4)) == CONST_INT
|| ! modified_between_p (SET_SRC (temp4), p, temp2))
/* Verify that registers used by the jump are not clobbered
by the instruction being moved. */
&& ! regs_set_between_p (PATTERN (temp),
PREV_INSN (temp2),
NEXT_INSN (temp2)))
{
emit_insn_after_with_line_notes (PATTERN (temp2), p, temp2);
delete_insn (temp2);
/* Set NEXT to an insn that we know won't go away. */
next = next_active_insn (insn);
/* Delete the jump around the set. Note that we must do
this before we redirect the test jumps so that it won't
delete the code immediately following the assignment
we moved (which might be a jump). */
delete_insn (insn);
/* We either have two consecutive labels or a jump to
a jump, so adjust all the JUMP_INSNs to branch to where
INSN branches to. */
for (p = NEXT_INSN (p); p != next; p = NEXT_INSN (p))
if (GET_CODE (p) == JUMP_INSN)
redirect_jump (p, target);
changed = 1;
continue;
}
}
/* Simplify if (...) { x = a; goto l; } x = b; by converting it
to x = a; if (...) goto l; x = b;
if A is sufficiently simple, the test doesn't involve X,
and nothing in the test modifies A or X.
If we have small register classes, we also can't do this if X
is a hard register.
If the "x = a;" insn has any REG_NOTES, we don't do this because
of the possibility that we are running after CSE and there is a
REG_EQUAL note that is only valid if the branch has already been
taken. If we move the insn with the REG_EQUAL note, we may
fold the comparison to always be false in a later CSE pass.
(We could also delete the REG_NOTES when moving the insn, but it
seems simpler to not move it.) An exception is that we can move
the insn if the only note is a REG_EQUAL or REG_EQUIV whose
value is the same as "a".
INSN is the goto.
We set:
TEMP to the jump insn preceding "x = a;"
TEMP1 to X
TEMP2 to the insn that sets "x = b;"
TEMP3 to the insn that sets "x = a;"
TEMP4 to the set of "x = a"; */
if (this_is_simplejump
&& (temp2 = next_active_insn (insn)) != 0
&& GET_CODE (temp2) == INSN
&& (temp4 = single_set (temp2)) != 0
&& GET_CODE (temp1 = SET_DEST (temp4)) == REG
&& (! SMALL_REGISTER_CLASSES
|| REGNO (temp1) >= FIRST_PSEUDO_REGISTER)
&& (temp3 = prev_active_insn (insn)) != 0
&& GET_CODE (temp3) == INSN
&& (temp4 = single_set (temp3)) != 0
&& rtx_equal_p (SET_DEST (temp4), temp1)
&& ! side_effects_p (SET_SRC (temp4))
&& ! may_trap_p (SET_SRC (temp4))
&& (REG_NOTES (temp3) == 0
|| ((REG_NOTE_KIND (REG_NOTES (temp3)) == REG_EQUAL
|| REG_NOTE_KIND (REG_NOTES (temp3)) == REG_EQUIV)
&& XEXP (REG_NOTES (temp3), 1) == 0
&& rtx_equal_p (XEXP (REG_NOTES (temp3), 0),
SET_SRC (temp4))))
&& (temp = prev_active_insn (temp3)) != 0
&& condjump_p (temp) && ! simplejump_p (temp)
/* TEMP must skip over the "x = a;" insn */
&& prev_real_insn (JUMP_LABEL (temp)) == insn
&& no_labels_between_p (temp, insn))
{
rtx prev_label = JUMP_LABEL (temp);
rtx insert_after = prev_nonnote_insn (temp);
#ifdef HAVE_cc0
/* We cannot insert anything between a set of cc and its use. */
if (insert_after && GET_RTX_CLASS (GET_CODE (insert_after)) == 'i'
&& sets_cc0_p (PATTERN (insert_after)))
insert_after = prev_nonnote_insn (insert_after);
#endif
++LABEL_NUSES (prev_label);
if (insert_after
&& no_labels_between_p (insert_after, temp)
&& ! reg_referenced_between_p (temp1, insert_after, temp3)
&& ! reg_referenced_between_p (temp1, temp3,
NEXT_INSN (temp2))
&& ! reg_set_between_p (temp1, insert_after, temp)
&& ! modified_between_p (SET_SRC (temp4), insert_after, temp)
/* Verify that registers used by the jump are not clobbered
by the instruction being moved. */
&& ! regs_set_between_p (PATTERN (temp),
PREV_INSN (temp3),
NEXT_INSN (temp3))
&& invert_jump (temp, JUMP_LABEL (insn)))
{
emit_insn_after_with_line_notes (PATTERN (temp3),
insert_after, temp3);
delete_insn (temp3);
delete_insn (insn);
/* Set NEXT to an insn that we know won't go away. */
next = temp2;
changed = 1;
}
if (prev_label && --LABEL_NUSES (prev_label) == 0)
delete_insn (prev_label);
if (changed)
continue;
}
#ifndef HAVE_cc0
/* If we have if (...) x = exp; and branches are expensive,
EXP is a single insn, does not have any side effects, cannot
trap, and is not too costly, convert this to
t = exp; if (...) x = t;
Don't do this when we have CC0 because it is unlikely to help
and we'd need to worry about where to place the new insn and
the potential for conflicts. We also can't do this when we have
notes on the insn for the same reason as above.
We set:
TEMP to the "x = exp;" insn.
TEMP1 to the single set in the "x = exp;" insn.
TEMP2 to "x". */
if (! reload_completed
&& this_is_condjump && ! this_is_simplejump
&& BRANCH_COST >= 3
&& (temp = next_nonnote_insn (insn)) != 0
&& GET_CODE (temp) == INSN
&& REG_NOTES (temp) == 0
&& (reallabelprev == temp
|| ((temp2 = next_active_insn (temp)) != 0
&& simplejump_p (temp2)
&& JUMP_LABEL (temp2) == JUMP_LABEL (insn)))
&& (temp1 = single_set (temp)) != 0
&& (temp2 = SET_DEST (temp1), GET_CODE (temp2) == REG)
&& (! SMALL_REGISTER_CLASSES
|| REGNO (temp2) >= FIRST_PSEUDO_REGISTER)
&& GET_CODE (SET_SRC (temp1)) != REG
&& GET_CODE (SET_SRC (temp1)) != SUBREG
&& GET_CODE (SET_SRC (temp1)) != CONST_INT
&& ! side_effects_p (SET_SRC (temp1))
&& ! may_trap_p (SET_SRC (temp1))
&& rtx_cost (SET_SRC (temp1), SET) < 10)
{
rtx new = gen_reg_rtx (GET_MODE (temp2));
if ((temp3 = find_insert_position (insn, temp))
&& validate_change (temp, &SET_DEST (temp1), new, 0))
{
next = emit_insn_after (gen_move_insn (temp2, new), insn);
emit_insn_after_with_line_notes (PATTERN (temp),
PREV_INSN (temp3), temp);
delete_insn (temp);
reallabelprev = prev_active_insn (JUMP_LABEL (insn));
if (after_regscan)
{
reg_scan_update (temp3, NEXT_INSN (next), old_max_reg);
old_max_reg = max_reg_num ();
}
}
}
/* Similarly, if it takes two insns to compute EXP but they
have the same destination. Here TEMP3 will be the second
insn and TEMP4 the SET from that insn. */
if (! reload_completed
&& this_is_condjump && ! this_is_simplejump
&& BRANCH_COST >= 4
&& (temp = next_nonnote_insn (insn)) != 0
&& GET_CODE (temp) == INSN
&& REG_NOTES (temp) == 0
&& (temp3 = next_nonnote_insn (temp)) != 0
&& GET_CODE (temp3) == INSN
&& REG_NOTES (temp3) == 0
&& (reallabelprev == temp3
|| ((temp2 = next_active_insn (temp3)) != 0
&& simplejump_p (temp2)
&& JUMP_LABEL (temp2) == JUMP_LABEL (insn)))
&& (temp1 = single_set (temp)) != 0
&& (temp2 = SET_DEST (temp1), GET_CODE (temp2) == REG)
&& GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT
&& (! SMALL_REGISTER_CLASSES
|| REGNO (temp2) >= FIRST_PSEUDO_REGISTER)
&& ! side_effects_p (SET_SRC (temp1))
&& ! may_trap_p (SET_SRC (temp1))
&& rtx_cost (SET_SRC (temp1), SET) < 10
&& (temp4 = single_set (temp3)) != 0
&& rtx_equal_p (SET_DEST (temp4), temp2)
&& ! side_effects_p (SET_SRC (temp4))
&& ! may_trap_p (SET_SRC (temp4))
&& rtx_cost (SET_SRC (temp4), SET) < 10)
{
rtx new = gen_reg_rtx (GET_MODE (temp2));
if ((temp5 = find_insert_position (insn, temp))
&& (temp6 = find_insert_position (insn, temp3))
&& validate_change (temp, &SET_DEST (temp1), new, 0))
{
/* Use the earliest of temp5 and temp6. */
if (temp5 != insn)
temp6 = temp5;
next = emit_insn_after (gen_move_insn (temp2, new), insn);
emit_insn_after_with_line_notes (PATTERN (temp),
PREV_INSN (temp6), temp);
emit_insn_after_with_line_notes
(replace_rtx (PATTERN (temp3), temp2, new),
PREV_INSN (temp6), temp3);
delete_insn (temp);
delete_insn (temp3);
reallabelprev = prev_active_insn (JUMP_LABEL (insn));
if (after_regscan)
{
reg_scan_update (temp6, NEXT_INSN (next), old_max_reg);
old_max_reg = max_reg_num ();
}
}
}
/* Finally, handle the case where two insns are used to
compute EXP but a temporary register is used. Here we must
ensure that the temporary register is not used anywhere else. */
if (! reload_completed
&& after_regscan
&& this_is_condjump && ! this_is_simplejump
&& BRANCH_COST >= 4
&& (temp = next_nonnote_insn (insn)) != 0
&& GET_CODE (temp) == INSN
&& REG_NOTES (temp) == 0
&& (temp3 = next_nonnote_insn (temp)) != 0
&& GET_CODE (temp3) == INSN
&& REG_NOTES (temp3) == 0
&& (reallabelprev == temp3
|| ((temp2 = next_active_insn (temp3)) != 0
&& simplejump_p (temp2)
&& JUMP_LABEL (temp2) == JUMP_LABEL (insn)))
&& (temp1 = single_set (temp)) != 0
&& (temp5 = SET_DEST (temp1),
(GET_CODE (temp5) == REG
|| (GET_CODE (temp5) == SUBREG
&& (temp5 = SUBREG_REG (temp5),
GET_CODE (temp5) == REG))))
&& REGNO (temp5) >= FIRST_PSEUDO_REGISTER
&& REGNO_FIRST_UID (REGNO (temp5)) == INSN_UID (temp)
&& REGNO_LAST_UID (REGNO (temp5)) == INSN_UID (temp3)
&& ! side_effects_p (SET_SRC (temp1))
&& ! may_trap_p (SET_SRC (temp1))
&& rtx_cost (SET_SRC (temp1), SET) < 10
&& (temp4 = single_set (temp3)) != 0
&& (temp2 = SET_DEST (temp4), GET_CODE (temp2) == REG)
&& GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT
&& (! SMALL_REGISTER_CLASSES
|| REGNO (temp2) >= FIRST_PSEUDO_REGISTER)
&& rtx_equal_p (SET_DEST (temp4), temp2)
&& ! side_effects_p (SET_SRC (temp4))
&& ! may_trap_p (SET_SRC (temp4))
&& rtx_cost (SET_SRC (temp4), SET) < 10)
{
rtx new = gen_reg_rtx (GET_MODE (temp2));
if ((temp5 = find_insert_position (insn, temp))
&& (temp6 = find_insert_position (insn, temp3))
&& validate_change (temp3, &SET_DEST (temp4), new, 0))
{
/* Use the earliest of temp5 and temp6. */
if (temp5 != insn)
temp6 = temp5;
next = emit_insn_after (gen_move_insn (temp2, new), insn);
emit_insn_after_with_line_notes (PATTERN (temp),
PREV_INSN (temp6), temp);
emit_insn_after_with_line_notes (PATTERN (temp3),
PREV_INSN (temp6), temp3);
delete_insn (temp);
delete_insn (temp3);
reallabelprev = prev_active_insn (JUMP_LABEL (insn));
if (after_regscan)
{
reg_scan_update (temp6, NEXT_INSN (next), old_max_reg);
old_max_reg = max_reg_num ();
}
}
}
#endif /* HAVE_cc0 */
/* Try to use a conditional move (if the target has them), or a
store-flag insn. The general case is:
1) x = a; if (...) x = b; and
2) if (...) x = b;
If the jump would be faster, the machine should not have defined
the movcc or scc insns!. These cases are often made by the
previous optimization.
The second case is treated as x = x; if (...) x = b;.
INSN here is the jump around the store. We set:
TEMP to the "x = b;" insn.
TEMP1 to X.
TEMP2 to B.
TEMP3 to A (X in the second case).
TEMP4 to the condition being tested.
TEMP5 to the earliest insn used to find the condition. */
if (/* We can't do this after reload has completed. */
! reload_completed
&& this_is_condjump && ! this_is_simplejump
/* Set TEMP to the "x = b;" insn. */
&& (temp = next_nonnote_insn (insn)) != 0
&& GET_CODE (temp) == INSN
&& GET_CODE (PATTERN (temp)) == SET
&& GET_CODE (temp1 = SET_DEST (PATTERN (temp))) == REG
&& (! SMALL_REGISTER_CLASSES
|| REGNO (temp1) >= FIRST_PSEUDO_REGISTER)
&& ! side_effects_p (temp2 = SET_SRC (PATTERN (temp)))
&& ! may_trap_p (temp2)
/* Allow either form, but prefer the former if both apply.
There is no point in using the old value of TEMP1 if
it is a register, since cse will alias them. It can
lose if the old value were a hard register since CSE
won't replace hard registers. Avoid using TEMP3 if
small register classes and it is a hard register. */
&& (((temp3 = reg_set_last (temp1, insn)) != 0
&& ! (SMALL_REGISTER_CLASSES && GET_CODE (temp3) == REG
&& REGNO (temp3) < FIRST_PSEUDO_REGISTER))
/* Make the latter case look like x = x; if (...) x = b; */
|| (temp3 = temp1, 1))
/* INSN must either branch to the insn after TEMP or the insn
after TEMP must branch to the same place as INSN. */
&& (reallabelprev == temp
|| ((temp4 = next_active_insn (temp)) != 0
&& simplejump_p (temp4)
&& JUMP_LABEL (temp4) == JUMP_LABEL (insn)))
&& (temp4 = get_condition (insn, &temp5)) != 0
/* We must be comparing objects whose modes imply the size.
We could handle BLKmode if (1) emit_store_flag could
and (2) we could find the size reliably. */
&& GET_MODE (XEXP (temp4, 0)) != BLKmode
/* Even if branches are cheap, the store_flag optimization
can win when the operation to be performed can be
expressed directly. */
#ifdef HAVE_cc0
/* If the previous insn sets CC0 and something else, we can't
do this since we are going to delete that insn. */
&& ! ((temp6 = prev_nonnote_insn (insn)) != 0
&& GET_CODE (temp6) == INSN
&& (sets_cc0_p (PATTERN (temp6)) == -1
|| (sets_cc0_p (PATTERN (temp6)) == 1
&& FIND_REG_INC_NOTE (temp6, NULL_RTX))))
#endif
)
{
#ifdef HAVE_conditional_move
/* First try a conditional move. */
{
enum rtx_code code = GET_CODE (temp4);
rtx var = temp1;
rtx cond0, cond1, aval, bval;
rtx target;
/* Copy the compared variables into cond0 and cond1, so that
any side effects performed in or after the old comparison,
will not affect our compare which will come later. */
/* ??? Is it possible to just use the comparison in the jump
insn? After all, we're going to delete it. We'd have
to modify emit_conditional_move to take a comparison rtx
instead or write a new function. */
cond0 = gen_reg_rtx (GET_MODE (XEXP (temp4, 0)));
/* We want the target to be able to simplify comparisons with
zero (and maybe other constants as well), so don't create
pseudos for them. There's no need to either. */
if (GET_CODE (XEXP (temp4, 1)) == CONST_INT
|| GET_CODE (XEXP (temp4, 1)) == CONST_DOUBLE)
cond1 = XEXP (temp4, 1);
else
cond1 = gen_reg_rtx (GET_MODE (XEXP (temp4, 1)));
aval = temp3;
bval = temp2;
start_sequence ();
target = emit_conditional_move (var, code,
cond0, cond1, VOIDmode,
aval, bval, GET_MODE (var),
(code == LTU || code == GEU
|| code == LEU || code == GTU));
if (target)
{
rtx seq1,seq2,last;
/* Save the conditional move sequence but don't emit it
yet. On some machines, like the alpha, it is possible
that temp5 == insn, so next generate the sequence that
saves the compared values and then emit both
sequences ensuring seq1 occurs before seq2. */
seq2 = get_insns ();
end_sequence ();
/* Now that we can't fail, generate the copy insns that
preserve the compared values. */
start_sequence ();
emit_move_insn (cond0, XEXP (temp4, 0));
if (cond1 != XEXP (temp4, 1))
emit_move_insn (cond1, XEXP (temp4, 1));
seq1 = get_insns ();
end_sequence ();
emit_insns_before (seq1, temp5);
/* Insert conditional move after insn, to be sure that
the jump and a possible compare won't be separated */
last = emit_insns_after (seq2, insn);
/* ??? We can also delete the insn that sets X to A.
Flow will do it too though. */
delete_insn (temp);
next = NEXT_INSN (insn);
delete_jump (insn);
if (after_regscan)
{
reg_scan_update (seq1, NEXT_INSN (last), old_max_reg);
old_max_reg = max_reg_num ();
}
changed = 1;
continue;
}
else
end_sequence ();
}
#endif
/* That didn't work, try a store-flag insn.
We further divide the cases into:
1) x = a; if (...) x = b; and either A or B is zero,
2) if (...) x = 0; and jumps are expensive,
3) x = a; if (...) x = b; and A and B are constants where all
the set bits in A are also set in B and jumps are expensive,
4) x = a; if (...) x = b; and A and B non-zero, and jumps are
more expensive, and
5) if (...) x = b; if jumps are even more expensive. */
if (GET_MODE_CLASS (GET_MODE (temp1)) == MODE_INT
&& ((GET_CODE (temp3) == CONST_INT)
/* Make the latter case look like
x = x; if (...) x = 0; */
|| (temp3 = temp1,
((BRANCH_COST >= 2
&& temp2 == const0_rtx)
|| BRANCH_COST >= 3)))
/* If B is zero, OK; if A is zero, can only do (1) if we
can reverse the condition. See if (3) applies possibly
by reversing the condition. Prefer reversing to (4) when
branches are very expensive. */
&& (((BRANCH_COST >= 2
|| STORE_FLAG_VALUE == -1
|| (STORE_FLAG_VALUE == 1
/* Check that the mask is a power of two,
so that it can probably be generated
with a shift. */
&& GET_CODE (temp3) == CONST_INT
&& exact_log2 (INTVAL (temp3)) >= 0))
&& (reversep = 0, temp2 == const0_rtx))
|| ((BRANCH_COST >= 2
|| STORE_FLAG_VALUE == -1
|| (STORE_FLAG_VALUE == 1
&& GET_CODE (temp2) == CONST_INT
&& exact_log2 (INTVAL (temp2)) >= 0))
&& temp3 == const0_rtx
&& (reversep = can_reverse_comparison_p (temp4, insn)))
|| (BRANCH_COST >= 2
&& GET_CODE (temp2) == CONST_INT
&& GET_CODE (temp3) == CONST_INT
&& ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp2)
|| ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp3)
&& (reversep = can_reverse_comparison_p (temp4,
insn)))))
|| BRANCH_COST >= 3)
)
{
enum rtx_code code = GET_CODE (temp4);
rtx uval, cval, var = temp1;
int normalizep;
rtx target;
/* If necessary, reverse the condition. */
if (reversep)
code = reverse_condition (code), uval = temp2, cval = temp3;
else
uval = temp3, cval = temp2;
/* If CVAL is non-zero, normalize to -1. Otherwise, if UVAL
is the constant 1, it is best to just compute the result
directly. If UVAL is constant and STORE_FLAG_VALUE
includes all of its bits, it is best to compute the flag
value unnormalized and `and' it with UVAL. Otherwise,
normalize to -1 and `and' with UVAL. */
normalizep = (cval != const0_rtx ? -1
: (uval == const1_rtx ? 1
: (GET_CODE (uval) == CONST_INT
&& (INTVAL (uval) & ~STORE_FLAG_VALUE) == 0)
? 0 : -1));
/* We will be putting the store-flag insn immediately in
front of the comparison that was originally being done,
so we know all the variables in TEMP4 will be valid.
However, this might be in front of the assignment of
A to VAR. If it is, it would clobber the store-flag
we will be emitting.
Therefore, emit into a temporary which will be copied to
VAR immediately after TEMP. */
start_sequence ();
target = emit_store_flag (gen_reg_rtx (GET_MODE (var)), code,
XEXP (temp4, 0), XEXP (temp4, 1),
VOIDmode,
(code == LTU || code == LEU
|| code == GEU || code == GTU),
normalizep);
if (target)
{
rtx seq;
rtx before = insn;
seq = get_insns ();
end_sequence ();
/* Put the store-flag insns in front of the first insn
used to compute the condition to ensure that we
use the same values of them as the current
comparison. However, the remainder of the insns we
generate will be placed directly in front of the
jump insn, in case any of the pseudos we use
are modified earlier. */
emit_insns_before (seq, temp5);
start_sequence ();
/* Both CVAL and UVAL are non-zero. */
if (cval != const0_rtx && uval != const0_rtx)
{
rtx tem1, tem2;
tem1 = expand_and (uval, target, NULL_RTX);
if (GET_CODE (cval) == CONST_INT
&& GET_CODE (uval) == CONST_INT
&& (INTVAL (cval) & INTVAL (uval)) == INTVAL (cval))
tem2 = cval;
else
{
tem2 = expand_unop (GET_MODE (var), one_cmpl_optab,
target, NULL_RTX, 0);
tem2 = expand_and (cval, tem2,
(GET_CODE (tem2) == REG
? tem2 : 0));
}
/* If we usually make new pseudos, do so here. This
turns out to help machines that have conditional
move insns. */
/* ??? Conditional moves have already been handled.
This may be obsolete. */
if (flag_expensive_optimizations)
target = 0;
target = expand_binop (GET_MODE (var), ior_optab,
tem1, tem2, target,
1, OPTAB_WIDEN);
}
else if (normalizep != 1)
{
/* We know that either CVAL or UVAL is zero. If
UVAL is zero, negate TARGET and `and' with CVAL.
Otherwise, `and' with UVAL. */
if (uval == const0_rtx)
{
target = expand_unop (GET_MODE (var), one_cmpl_optab,
target, NULL_RTX, 0);
uval = cval;
}
target = expand_and (uval, target,
(GET_CODE (target) == REG
&& ! preserve_subexpressions_p ()
? target : NULL_RTX));
}
emit_move_insn (var, target);
seq = get_insns ();
end_sequence ();
#ifdef HAVE_cc0
/* If INSN uses CC0, we must not separate it from the
insn that sets cc0. */
if (reg_mentioned_p (cc0_rtx, PATTERN (before)))
before = prev_nonnote_insn (before);
#endif
emit_insns_before (seq, before);
delete_insn (temp);
next = NEXT_INSN (insn);
delete_jump (insn);
if (after_regscan)
{
reg_scan_update (seq, NEXT_INSN (next), old_max_reg);
old_max_reg = max_reg_num ();
}
changed = 1;
continue;
}
else
end_sequence ();
}
}
/* If branches are expensive, convert
if (foo) bar++; to bar += (foo != 0);
and similarly for "bar--;"
INSN is the conditional branch around the arithmetic. We set:
TEMP is the arithmetic insn.
TEMP1 is the SET doing the arithmetic.
TEMP2 is the operand being incremented or decremented.
TEMP3 to the condition being tested.
TEMP4 to the earliest insn used to find the condition. */
if ((BRANCH_COST >= 2
#ifdef HAVE_incscc
|| HAVE_incscc
#endif
#ifdef HAVE_decscc
|| HAVE_decscc
#endif
)
&& ! reload_completed
&& this_is_condjump && ! this_is_simplejump
&& (temp = next_nonnote_insn (insn)) != 0
&& (temp1 = single_set (temp)) != 0
&& (temp2 = SET_DEST (temp1),
GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT)
&& GET_CODE (SET_SRC (temp1)) == PLUS
&& (XEXP (SET_SRC (temp1), 1) == const1_rtx
|| XEXP (SET_SRC (temp1), 1) == constm1_rtx)
&& rtx_equal_p (temp2, XEXP (SET_SRC (temp1), 0))
&& ! side_effects_p (temp2)
&& ! may_trap_p (temp2)
/* INSN must either branch to the insn after TEMP or the insn
after TEMP must branch to the same place as INSN. */
&& (reallabelprev == temp
|| ((temp3 = next_active_insn (temp)) != 0
&& simplejump_p (temp3)
&& JUMP_LABEL (temp3) == JUMP_LABEL (insn)))
&& (temp3 = get_condition (insn, &temp4)) != 0
/* We must be comparing objects whose modes imply the size.
We could handle BLKmode if (1) emit_store_flag could
and (2) we could find the size reliably. */
&& GET_MODE (XEXP (temp3, 0)) != BLKmode
&& can_reverse_comparison_p (temp3, insn))
{
rtx temp6, target = 0, seq, init_insn = 0, init = temp2;
enum rtx_code code = reverse_condition (GET_CODE (temp3));
start_sequence ();
/* It must be the case that TEMP2 is not modified in the range
[TEMP4, INSN). The one exception we make is if the insn
before INSN sets TEMP2 to something which is also unchanged
in that range. In that case, we can move the initialization
into our sequence. */
if ((temp5 = prev_active_insn (insn)) != 0
&& no_labels_between_p (temp5, insn)
&& GET_CODE (temp5) == INSN
&& (temp6 = single_set (temp5)) != 0
&& rtx_equal_p (temp2, SET_DEST (temp6))
&& (CONSTANT_P (SET_SRC (temp6))
|| GET_CODE (SET_SRC (temp6)) == REG
|| GET_CODE (SET_SRC (temp6)) == SUBREG))
{
emit_insn (PATTERN (temp5));
init_insn = temp5;
init = SET_SRC (temp6);
}
if (CONSTANT_P (init)
|| ! reg_set_between_p (init, PREV_INSN (temp4), insn))
target = emit_store_flag (gen_reg_rtx (GET_MODE (temp2)), code,
XEXP (temp3, 0), XEXP (temp3, 1),
VOIDmode,
(code == LTU || code == LEU
|| code == GTU || code == GEU), 1);
/* If we can do the store-flag, do the addition or
subtraction. */
if (target)
target = expand_binop (GET_MODE (temp2),
(XEXP (SET_SRC (temp1), 1) == const1_rtx
? add_optab : sub_optab),
temp2, target, temp2, 0, OPTAB_WIDEN);
if (target != 0)
{
/* Put the result back in temp2 in case it isn't already.
Then replace the jump, possible a CC0-setting insn in
front of the jump, and TEMP, with the sequence we have
made. */
if (target != temp2)
emit_move_insn (temp2, target);
seq = get_insns ();
end_sequence ();
emit_insns_before (seq, temp4);
delete_insn (temp);
if (init_insn)
delete_insn (init_insn);
next = NEXT_INSN (insn);
#ifdef HAVE_cc0
delete_insn (prev_nonnote_insn (insn));
#endif
delete_insn (insn);
if (after_regscan)
{
reg_scan_update (seq, NEXT_INSN (next), old_max_reg);
old_max_reg = max_reg_num ();
}
changed = 1;
continue;
}
else
end_sequence ();
}
/* Simplify if (...) x = 1; else {...} if (x) ...
We recognize this case scanning backwards as well.
TEMP is the assignment to x;
TEMP1 is the label at the head of the second if. */
/* ?? This should call get_condition to find the values being
compared, instead of looking for a COMPARE insn when HAVE_cc0
is not defined. This would allow it to work on the m88k. */
/* ?? This optimization is only safe before cse is run if HAVE_cc0
is not defined and the condition is tested by a separate compare
insn. This is because the code below assumes that the result
of the compare dies in the following branch.
Not only that, but there might be other insns between the
compare and branch whose results are live. Those insns need
to be executed.
A way to fix this is to move the insns at JUMP_LABEL (insn)
to before INSN. If we are running before flow, they will
be deleted if they aren't needed. But this doesn't work
well after flow.
This is really a special-case of jump threading, anyway. The
right thing to do is to replace this and jump threading with
much simpler code in cse.
This code has been turned off in the non-cc0 case in the
meantime. */
#ifdef HAVE_cc0
else if (this_is_simplejump
/* Safe to skip USE and CLOBBER insns here
since they will not be deleted. */
&& (temp = prev_active_insn (insn))
&& no_labels_between_p (temp, insn)
&& GET_CODE (temp) == INSN
&& GET_CODE (PATTERN (temp)) == SET
&& GET_CODE (SET_DEST (PATTERN (temp))) == REG
&& CONSTANT_P (SET_SRC (PATTERN (temp)))
&& (temp1 = next_active_insn (JUMP_LABEL (insn)))
/* If we find that the next value tested is `x'
(TEMP1 is the insn where this happens), win. */
&& GET_CODE (temp1) == INSN
&& GET_CODE (PATTERN (temp1)) == SET
#ifdef HAVE_cc0
/* Does temp1 `tst' the value of x? */
&& SET_SRC (PATTERN (temp1)) == SET_DEST (PATTERN (temp))
&& SET_DEST (PATTERN (temp1)) == cc0_rtx
&& (temp1 = next_nonnote_insn (temp1))
#else
/* Does temp1 compare the value of x against zero? */
&& GET_CODE (SET_SRC (PATTERN (temp1))) == COMPARE
&& XEXP (SET_SRC (PATTERN (temp1)), 1) == const0_rtx
&& (XEXP (SET_SRC (PATTERN (temp1)), 0)
== SET_DEST (PATTERN (temp)))
&& GET_CODE (SET_DEST (PATTERN (temp1))) == REG
&& (temp1 = find_next_ref (SET_DEST (PATTERN (temp1)), temp1))
#endif
&& condjump_p (temp1))
{
/* Get the if_then_else from the condjump. */
rtx choice = SET_SRC (PATTERN (temp1));
if (GET_CODE (choice) == IF_THEN_ELSE)
{
enum rtx_code code = GET_CODE (XEXP (choice, 0));
rtx val = SET_SRC (PATTERN (temp));
rtx cond
= simplify_relational_operation (code, GET_MODE (SET_DEST (PATTERN (temp))),
val, const0_rtx);
rtx ultimate;
if (cond == const_true_rtx)
ultimate = XEXP (choice, 1);
else if (cond == const0_rtx)
ultimate = XEXP (choice, 2);
else
ultimate = 0;
if (ultimate == pc_rtx)
ultimate = get_label_after (temp1);
else if (ultimate && GET_CODE (ultimate) != RETURN)
ultimate = XEXP (ultimate, 0);
if (ultimate && JUMP_LABEL(insn) != ultimate)
changed |= redirect_jump (insn, ultimate);
}
}
#endif
#if 0
/* @@ This needs a bit of work before it will be right.
Any type of comparison can be accepted for the first and
second compare. When rewriting the first jump, we must
compute the what conditions can reach label3, and use the
appropriate code. We can not simply reverse/swap the code
of the first jump. In some cases, the second jump must be
rewritten also.
For example,
< == converts to > ==
< != converts to == >
etc.
If the code is written to only accept an '==' test for the second
compare, then all that needs to be done is to swap the condition
of the first branch.
It is questionable whether we want this optimization anyways,
since if the user wrote code like this because he/she knew that
the jump to label1 is taken most of the time, then rewriting
this gives slower code. */
/* @@ This should call get_condition to find the values being
compared, instead of looking for a COMPARE insn when HAVE_cc0
is not defined. This would allow it to work on the m88k. */
/* @@ This optimization is only safe before cse is run if HAVE_cc0
is not defined and the condition is tested by a separate compare
insn. This is because the code below assumes that the result
of the compare dies in the following branch. */
/* Simplify test a ~= b
condjump label1;
test a == b
condjump label2;
jump label3;
label1:
rewriting as
test a ~~= b
condjump label3
test a == b
condjump label2
label1:
where ~= is an inequality, e.g. >, and ~~= is the swapped
inequality, e.g. <.
We recognize this case scanning backwards.
TEMP is the conditional jump to `label2';
TEMP1 is the test for `a == b';
TEMP2 is the conditional jump to `label1';
TEMP3 is the test for `a ~= b'. */
else if (this_is_simplejump
&& (temp = prev_active_insn (insn))
&& no_labels_between_p (temp, insn)
&& condjump_p (temp)
&& (temp1 = prev_active_insn (temp))
&& no_labels_between_p (temp1, temp)
&& GET_CODE (temp1) == INSN
&& GET_CODE (PATTERN (temp1)) == SET
#ifdef HAVE_cc0
&& sets_cc0_p (PATTERN (temp1)) == 1
#else
&& GET_CODE (SET_SRC (PATTERN (temp1))) == COMPARE
&& GET_CODE (SET_DEST (PATTERN (temp1))) == REG
&& (temp == find_next_ref (SET_DEST (PATTERN (temp1)), temp1))
#endif
&& (temp2 = prev_active_insn (temp1))
&& no_labels_between_p (temp2, temp1)
&& condjump_p (temp2)
&& JUMP_LABEL (temp2) == next_nonnote_insn (NEXT_INSN (insn))
&& (temp3 = prev_active_insn (temp2))
&& no_labels_between_p (temp3, temp2)
&& GET_CODE (PATTERN (temp3)) == SET
&& rtx_equal_p (SET_DEST (PATTERN (temp3)),
SET_DEST (PATTERN (temp1)))
&& rtx_equal_p (SET_SRC (PATTERN (temp1)),
SET_SRC (PATTERN (temp3)))
&& ! inequality_comparisons_p (PATTERN (temp))
&& inequality_comparisons_p (PATTERN (temp2)))
{
rtx fallthrough_label = JUMP_LABEL (temp2);
++LABEL_NUSES (fallthrough_label);
if (swap_jump (temp2, JUMP_LABEL (insn)))
{
delete_insn (insn);
changed = 1;
}
if (--LABEL_NUSES (fallthrough_label) == 0)
delete_insn (fallthrough_label);
}
#endif
/* Simplify if (...) {... x = 1;} if (x) ...
We recognize this case backwards.
TEMP is the test of `x';
TEMP1 is the assignment to `x' at the end of the
previous statement. */
/* @@ This should call get_condition to find the values being
compared, instead of looking for a COMPARE insn when HAVE_cc0
is not defined. This would allow it to work on the m88k. */
/* @@ This optimization is only safe before cse is run if HAVE_cc0
is not defined and the condition is tested by a separate compare
insn. This is because the code below assumes that the result
of the compare dies in the following branch. */
/* ??? This has to be turned off. The problem is that the
unconditional jump might indirectly end up branching to the
label between TEMP1 and TEMP. We can't detect this, in general,
since it may become a jump to there after further optimizations.
If that jump is done, it will be deleted, so we will retry
this optimization in the next pass, thus an infinite loop.
The present code prevents this by putting the jump after the
label, but this is not logically correct. */
#if 0
else if (this_is_condjump
/* Safe to skip USE and CLOBBER insns here
since they will not be deleted. */
&& (temp = prev_active_insn (insn))
&& no_labels_between_p (temp, insn)
&& GET_CODE (temp) == INSN
&& GET_CODE (PATTERN (temp)) == SET
#ifdef HAVE_cc0
&& sets_cc0_p (PATTERN (temp)) == 1
&& GET_CODE (SET_SRC (PATTERN (temp))) == REG
#else
/* Temp must be a compare insn, we can not accept a register
to register move here, since it may not be simply a
tst insn. */
&& GET_CODE (SET_SRC (PATTERN (temp))) == COMPARE
&& XEXP (SET_SRC (PATTERN (temp)), 1) == const0_rtx
&& GET_CODE (XEXP (SET_SRC (PATTERN (temp)), 0)) == REG
&& GET_CODE (SET_DEST (PATTERN (temp))) == REG
&& insn == find_next_ref (SET_DEST (PATTERN (temp)), temp)
#endif
/* May skip USE or CLOBBER insns here
for checking for opportunity, since we
take care of them later. */
&& (temp1 = prev_active_insn (temp))
&& GET_CODE (temp1) == INSN
&& GET_CODE (PATTERN (temp1)) == SET
#ifdef HAVE_cc0
&& SET_SRC (PATTERN (temp)) == SET_DEST (PATTERN (temp1))
#else
&& (XEXP (SET_SRC (PATTERN (temp)), 0)
== SET_DEST (PATTERN (temp1)))
#endif
&& CONSTANT_P (SET_SRC (PATTERN (temp1)))
/* If this isn't true, cse will do the job. */
&& ! no_labels_between_p (temp1, temp))
{
/* Get the if_then_else from the condjump. */
rtx choice = SET_SRC (PATTERN (insn));
if (GET_CODE (choice) == IF_THEN_ELSE
&& (GET_CODE (XEXP (choice, 0)) == EQ
|| GET_CODE (XEXP (choice, 0)) == NE))
{
int want_nonzero = (GET_CODE (XEXP (choice, 0)) == NE);
rtx last_insn;
rtx ultimate;
rtx p;
/* Get the place that condjump will jump to
if it is reached from here. */
if ((SET_SRC (PATTERN (temp1)) != const0_rtx)
== want_nonzero)
ultimate = XEXP (choice, 1);
else
ultimate = XEXP (choice, 2);
/* Get it as a CODE_LABEL. */
if (ultimate == pc_rtx)
ultimate = get_label_after (insn);
else
/* Get the label out of the LABEL_REF. */
ultimate = XEXP (ultimate, 0);
/* Insert the jump immediately before TEMP, specifically
after the label that is between TEMP1 and TEMP. */
last_insn = PREV_INSN (temp);
/* If we would be branching to the next insn, the jump
would immediately be deleted and the re-inserted in
a subsequent pass over the code. So don't do anything
in that case. */
if (next_active_insn (last_insn)
!= next_active_insn (ultimate))
{
emit_barrier_after (last_insn);
p = emit_jump_insn_after (gen_jump (ultimate),
last_insn);
JUMP_LABEL (p) = ultimate;
++LABEL_NUSES (ultimate);
if (INSN_UID (ultimate) < max_jump_chain
&& INSN_CODE (p) < max_jump_chain)
{
jump_chain[INSN_UID (p)]
= jump_chain[INSN_UID (ultimate)];
jump_chain[INSN_UID (ultimate)] = p;
}
changed = 1;
continue;
}
}
}
#endif
/* Detect a conditional jump going to the same place
as an immediately following unconditional jump. */
else if (this_is_condjump
&& (temp = next_active_insn (insn)) != 0
&& simplejump_p (temp)
&& (next_active_insn (JUMP_LABEL (insn))
== next_active_insn (JUMP_LABEL (temp))))
{
rtx tem = temp;
/* ??? Optional. Disables some optimizations, but makes
gcov output more accurate with -O. */
if (flag_test_coverage && !reload_completed)
for (tem = insn; tem != temp; tem = NEXT_INSN (tem))
if (GET_CODE (tem) == NOTE && NOTE_LINE_NUMBER (tem) > 0)
break;
if (tem == temp)
{
delete_jump (insn);
changed = 1;
continue;
}
}
#ifdef HAVE_trap
/* Detect a conditional jump jumping over an unconditional trap. */
else if (HAVE_trap
&& this_is_condjump && ! this_is_simplejump
&& reallabelprev != 0
&& GET_CODE (reallabelprev) == INSN
&& GET_CODE (PATTERN (reallabelprev)) == TRAP_IF
&& TRAP_CONDITION (PATTERN (reallabelprev)) == const_true_rtx
&& prev_active_insn (reallabelprev) == insn
&& no_labels_between_p (insn, reallabelprev)
&& (temp2 = get_condition (insn, &temp4))
&& can_reverse_comparison_p (temp2, insn))
{
rtx new = gen_cond_trap (reverse_condition (GET_CODE (temp2)),
XEXP (temp2, 0), XEXP (temp2, 1),
TRAP_CODE (PATTERN (reallabelprev)));
if (new)
{
emit_insn_before (new, temp4);
delete_insn (reallabelprev);
delete_jump (insn);
changed = 1;
continue;
}
}
/* Detect a jump jumping to an unconditional trap. */
else if (HAVE_trap && this_is_condjump
&& (temp = next_active_insn (JUMP_LABEL (insn)))
&& GET_CODE (temp) == INSN
&& GET_CODE (PATTERN (temp)) == TRAP_IF
&& (this_is_simplejump
|| (temp2 = get_condition (insn, &temp4))))
{
rtx tc = TRAP_CONDITION (PATTERN (temp));
if (tc == const_true_rtx
|| (! this_is_simplejump && rtx_equal_p (temp2, tc)))
{
rtx new;
/* Replace an unconditional jump to a trap with a trap. */
if (this_is_simplejump)
{
emit_barrier_after (emit_insn_before (gen_trap (), insn));
delete_jump (insn);
changed = 1;
continue;
}
new = gen_cond_trap (GET_CODE (temp2), XEXP (temp2, 0),
XEXP (temp2, 1),
TRAP_CODE (PATTERN (temp)));
if (new)
{
emit_insn_before (new, temp4);
delete_jump (insn);
changed = 1;
continue;
}
}
/* If the trap condition and jump condition are mutually
exclusive, redirect the jump to the following insn. */
else if (GET_RTX_CLASS (GET_CODE (tc)) == '<'
&& ! this_is_simplejump
&& swap_condition (GET_CODE (temp2)) == GET_CODE (tc)
&& rtx_equal_p (XEXP (tc, 0), XEXP (temp2, 0))
&& rtx_equal_p (XEXP (tc, 1), XEXP (temp2, 1))
&& redirect_jump (insn, get_label_after (temp)))
{
changed = 1;
continue;
}
}
#endif
/* Detect a conditional jump jumping over an unconditional jump. */
else if ((this_is_condjump || this_is_condjump_in_parallel)
&& ! this_is_simplejump
&& reallabelprev != 0
&& GET_CODE (reallabelprev) == JUMP_INSN
&& prev_active_insn (reallabelprev) == insn
&& no_labels_between_p (insn, reallabelprev)
&& simplejump_p (reallabelprev))
{
/* When we invert the unconditional jump, we will be
decrementing the usage count of its old label.
Make sure that we don't delete it now because that
might cause the following code to be deleted. */
rtx prev_uses = prev_nonnote_insn (reallabelprev);
rtx prev_label = JUMP_LABEL (insn);
if (prev_label)
++LABEL_NUSES (prev_label);
if (invert_jump (insn, JUMP_LABEL (reallabelprev)))
{
/* It is very likely that if there are USE insns before
this jump, they hold REG_DEAD notes. These REG_DEAD
notes are no longer valid due to this optimization,
and will cause the life-analysis that following passes
(notably delayed-branch scheduling) to think that
these registers are dead when they are not.
To prevent this trouble, we just remove the USE insns
from the insn chain. */
while (prev_uses && GET_CODE (prev_uses) == INSN
&& GET_CODE (PATTERN (prev_uses)) == USE)
{
rtx useless = prev_uses;
prev_uses = prev_nonnote_insn (prev_uses);
delete_insn (useless);
}
delete_insn (reallabelprev);
next = insn;
changed = 1;
}
/* We can now safely delete the label if it is unreferenced
since the delete_insn above has deleted the BARRIER. */
if (prev_label && --LABEL_NUSES (prev_label) == 0)
delete_insn (prev_label);
continue;
}
else
{
/* Detect a jump to a jump. */
nlabel = follow_jumps (JUMP_LABEL (insn));
if (nlabel != JUMP_LABEL (insn)
&& redirect_jump (insn, nlabel))
{
changed = 1;
next = insn;
}
/* Look for if (foo) bar; else break; */
/* The insns look like this:
insn = condjump label1;
...range1 (some insns)...
jump label2;
label1:
...range2 (some insns)...
jump somewhere unconditionally
label2: */
{
rtx label1 = next_label (insn);
rtx range1end = label1 ? prev_active_insn (label1) : 0;
/* Don't do this optimization on the first round, so that
jump-around-a-jump gets simplified before we ask here
whether a jump is unconditional.
Also don't do it when we are called after reload since
it will confuse reorg. */
if (! first
&& (reload_completed ? ! flag_delayed_branch : 1)
/* Make sure INSN is something we can invert. */
&& condjump_p (insn)
&& label1 != 0
&& JUMP_LABEL (insn) == label1
&& LABEL_NUSES (label1) == 1
&& GET_CODE (range1end) == JUMP_INSN
&& simplejump_p (range1end))
{
rtx label2 = next_label (label1);
rtx range2end = label2 ? prev_active_insn (label2) : 0;
if (range1end != range2end
&& JUMP_LABEL (range1end) == label2
&& GET_CODE (range2end) == JUMP_INSN
&& GET_CODE (NEXT_INSN (range2end)) == BARRIER
/* Invert the jump condition, so we
still execute the same insns in each case. */
&& invert_jump (insn, label1))
{
rtx range1beg = next_active_insn (insn);
rtx range2beg = next_active_insn (label1);
rtx range1after, range2after;
rtx range1before, range2before;
rtx rangenext;
/* Include in each range any notes before it, to be
sure that we get the line number note if any, even
if there are other notes here. */
while (PREV_INSN (range1beg)
&& GET_CODE (PREV_INSN (range1beg)) == NOTE)
range1beg = PREV_INSN (range1beg);
while (PREV_INSN (range2beg)
&& GET_CODE (PREV_INSN (range2beg)) == NOTE)
range2beg = PREV_INSN (range2beg);
/* Don't move NOTEs for blocks or loops; shift them
outside the ranges, where they'll stay put. */
range1beg = squeeze_notes (range1beg, range1end);
range2beg = squeeze_notes (range2beg, range2end);
/* Get current surrounds of the 2 ranges. */
range1before = PREV_INSN (range1beg);
range2before = PREV_INSN (range2beg);
range1after = NEXT_INSN (range1end);
range2after = NEXT_INSN (range2end);
/* Splice range2 where range1 was. */
NEXT_INSN (range1before) = range2beg;
PREV_INSN (range2beg) = range1before;
NEXT_INSN (range2end) = range1after;
PREV_INSN (range1after) = range2end;
/* Splice range1 where range2 was. */
NEXT_INSN (range2before) = range1beg;
PREV_INSN (range1beg) = range2before;
NEXT_INSN (range1end) = range2after;
PREV_INSN (range2after) = range1end;
/* Check for a loop end note between the end of
range2, and the next code label. If there is one,
then what we have really seen is
if (foo) break; end_of_loop;
and moved the break sequence outside the loop.
We must move the LOOP_END note to where the
loop really ends now, or we will confuse loop
optimization. Stop if we find a LOOP_BEG note
first, since we don't want to move the LOOP_END
note in that case. */
for (;range2after != label2; range2after = rangenext)
{
rangenext = NEXT_INSN (range2after);
if (GET_CODE (range2after) == NOTE)
{
if (NOTE_LINE_NUMBER (range2after)
== NOTE_INSN_LOOP_END)
{
NEXT_INSN (PREV_INSN (range2after))
= rangenext;
PREV_INSN (rangenext)
= PREV_INSN (range2after);
PREV_INSN (range2after)
= PREV_INSN (range1beg);
NEXT_INSN (range2after) = range1beg;
NEXT_INSN (PREV_INSN (range1beg))
= range2after;
PREV_INSN (range1beg) = range2after;
}
else if (NOTE_LINE_NUMBER (range2after)
== NOTE_INSN_LOOP_BEG)
break;
}
}
changed = 1;
continue;
}
}
}
/* Now that the jump has been tensioned,
try cross jumping: check for identical code
before the jump and before its target label. */
/* First, cross jumping of conditional jumps: */
if (cross_jump && condjump_p (insn))
{
rtx newjpos, newlpos;
rtx x = prev_real_insn (JUMP_LABEL (insn));
/* A conditional jump may be crossjumped
only if the place it jumps to follows
an opposing jump that comes back here. */
if (x != 0 && ! jump_back_p (x, insn))
/* We have no opposing jump;
cannot cross jump this insn. */
x = 0;
newjpos = 0;
/* TARGET is nonzero if it is ok to cross jump
to code before TARGET. If so, see if matches. */
if (x != 0)
find_cross_jump (insn, x, 2,
&newjpos, &newlpos);
if (newjpos != 0)
{
do_cross_jump (insn, newjpos, newlpos);
/* Make the old conditional jump
into an unconditional one. */
SET_SRC (PATTERN (insn))
= gen_rtx_LABEL_REF (VOIDmode, JUMP_LABEL (insn));
INSN_CODE (insn) = -1;
emit_barrier_after (insn);
/* Add to jump_chain unless this is a new label
whose UID is too large. */
if (INSN_UID (JUMP_LABEL (insn)) < max_jump_chain)
{
jump_chain[INSN_UID (insn)]
= jump_chain[INSN_UID (JUMP_LABEL (insn))];
jump_chain[INSN_UID (JUMP_LABEL (insn))] = insn;
}
changed = 1;
next = insn;
}
}
/* Cross jumping of unconditional jumps:
a few differences. */
if (cross_jump && simplejump_p (insn))
{
rtx newjpos, newlpos;
rtx target;
newjpos = 0;
/* TARGET is nonzero if it is ok to cross jump
to code before TARGET. If so, see if matches. */
find_cross_jump (insn, JUMP_LABEL (insn), 1,
&newjpos, &newlpos);
/* If cannot cross jump to code before the label,
see if we can cross jump to another jump to
the same label. */
/* Try each other jump to this label. */
if (INSN_UID (JUMP_LABEL (insn)) < max_uid)
for (target = jump_chain[INSN_UID (JUMP_LABEL (insn))];
target != 0 && newjpos == 0;
target = jump_chain[INSN_UID (target)])
if (target != insn
&& JUMP_LABEL (target) == JUMP_LABEL (insn)
/* Ignore TARGET if it's deleted. */
&& ! INSN_DELETED_P (target))
find_cross_jump (insn, target, 2,
&newjpos, &newlpos);
if (newjpos != 0)
{
do_cross_jump (insn, newjpos, newlpos);
changed = 1;
next = insn;
}
}
/* This code was dead in the previous jump.c! */
if (cross_jump && GET_CODE (PATTERN (insn)) == RETURN)
{
/* Return insns all "jump to the same place"
so we can cross-jump between any two of them. */
rtx newjpos, newlpos, target;
newjpos = 0;
/* If cannot cross jump to code before the label,
see if we can cross jump to another jump to
the same label. */
/* Try each other jump to this label. */
for (target = jump_chain[0];
target != 0 && newjpos == 0;
target = jump_chain[INSN_UID (target)])
if (target != insn
&& ! INSN_DELETED_P (target)
&& GET_CODE (PATTERN (target)) == RETURN)
find_cross_jump (insn, target, 2,
&newjpos, &newlpos);
if (newjpos != 0)
{
do_cross_jump (insn, newjpos, newlpos);
changed = 1;
next = insn;
}
}
}
}
first = 0;
}
/* Delete extraneous line number notes.
Note that two consecutive notes for different lines are not really
extraneous. There should be some indication where that line belonged,
even if it became empty. */
{
rtx last_note = 0;
for (insn = f; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0)
{
/* Delete this note if it is identical to previous note. */
if (last_note
&& NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last_note)
&& NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last_note))
{
delete_insn (insn);
continue;
}
last_note = insn;
}
}
#ifdef HAVE_return
if (HAVE_return)
{
/* If we fall through to the epilogue, see if we can insert a RETURN insn
in front of it. If the machine allows it at this point (we might be
after reload for a leaf routine), it will improve optimization for it
to be there. We do this both here and at the start of this pass since
the RETURN might have been deleted by some of our optimizations. */
insn = get_last_insn ();
while (insn && GET_CODE (insn) == NOTE)
insn = PREV_INSN (insn);
if (insn && GET_CODE (insn) != BARRIER)
{
emit_jump_insn (gen_return ());
emit_barrier ();
}
}
#endif
/* CAN_REACH_END is persistent for each function. Once set it should
not be cleared. This is especially true for the case where we
delete the NOTE_FUNCTION_END note. CAN_REACH_END is cleared by
the front-end before compiling each function. */
if (calculate_can_reach_end (last_insn, 0, 1))
can_reach_end = 1;
/* Show JUMP_CHAIN no longer valid. */
jump_chain = 0;
}
/* Initialize LABEL_NUSES and JUMP_LABEL fields. Delete any REG_LABEL
notes whose labels don't occur in the insn any more. Returns the
largest INSN_UID found. */
static int
init_label_info (f)
rtx f;
{
int largest_uid = 0;
rtx insn;
for (insn = f; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL)
LABEL_NUSES (insn) = (LABEL_PRESERVE_P (insn) != 0);
else if (GET_CODE (insn) == JUMP_INSN)
JUMP_LABEL (insn) = 0;
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
{
rtx note, next;
for (note = REG_NOTES (insn); note; note = next)
{
next = XEXP (note, 1);
if (REG_NOTE_KIND (note) == REG_LABEL
&& ! reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
remove_note (insn, note);
}
}
if (INSN_UID (insn) > largest_uid)
largest_uid = INSN_UID (insn);
}
return largest_uid;
}
/* Delete insns following barriers, up to next label.
Also delete no-op jumps created by gcse. */
static void
delete_barrier_successors (f)
rtx f;
{
rtx insn;
for (insn = f; insn;)
{
if (GET_CODE (insn) == BARRIER)
{
insn = NEXT_INSN (insn);
while (insn != 0 && GET_CODE (insn) != CODE_LABEL)
{
if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)
insn = NEXT_INSN (insn);
else
insn = delete_insn (insn);
}
/* INSN is now the code_label. */
}
/* Also remove (set (pc) (pc)) insns which can be created by
gcse. We eliminate such insns now to avoid having them
cause problems later. */
else if (GET_CODE (insn) == JUMP_INSN
&& SET_SRC (PATTERN (insn)) == pc_rtx
&& SET_DEST (PATTERN (insn)) == pc_rtx)
insn = delete_insn (insn);
else
insn = NEXT_INSN (insn);
}
}
/* Mark the label each jump jumps to.
Combine consecutive labels, and count uses of labels.
For each label, make a chain (using `jump_chain')
of all the *unconditional* jumps that jump to it;
also make a chain of all returns.
CROSS_JUMP indicates whether we are doing cross jumping
and if we are whether we will be paying attention to
death notes or not. */
static void
mark_all_labels (f, cross_jump)
rtx f;
int cross_jump;
{
rtx insn;
for (insn = f; insn; insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
mark_jump_label (PATTERN (insn), insn, cross_jump);
if (! INSN_DELETED_P (insn) && GET_CODE (insn) == JUMP_INSN)
{
if (JUMP_LABEL (insn) != 0 && simplejump_p (insn))
{
jump_chain[INSN_UID (insn)]
= jump_chain[INSN_UID (JUMP_LABEL (insn))];
jump_chain[INSN_UID (JUMP_LABEL (insn))] = insn;
}
if (GET_CODE (PATTERN (insn)) == RETURN)
{
jump_chain[INSN_UID (insn)] = jump_chain[0];
jump_chain[0] = insn;
}
}
}
}
/* Delete all labels already not referenced.
Also find and return the last insn. */
static rtx
delete_unreferenced_labels (f)
rtx f;
{
rtx final = NULL_RTX;
rtx insn;
for (insn = f; insn; )
{
if (GET_CODE (insn) == CODE_LABEL && LABEL_NUSES (insn) == 0)
insn = delete_insn (insn);
else
{
final = insn;
insn = NEXT_INSN (insn);
}
}
return final;
}
/* Delete various simple forms of moves which have no necessary
side effect. */
static void
delete_noop_moves (f)
rtx f;
{
rtx insn, next;
for (insn = f; insn; )
{
next = NEXT_INSN (insn);
if (GET_CODE (insn) == INSN)
{
register rtx body = PATTERN (insn);
/* Combine stack_adjusts with following push_insns. */
#ifdef PUSH_ROUNDING
if (GET_CODE (body) == SET
&& SET_DEST (body) == stack_pointer_rtx
&& GET_CODE (SET_SRC (body)) == PLUS
&& XEXP (SET_SRC (body), 0) == stack_pointer_rtx
&& GET_CODE (XEXP (SET_SRC (body), 1)) == CONST_INT
&& INTVAL (XEXP (SET_SRC (body), 1)) > 0)
{
rtx p;
rtx stack_adjust_insn = insn;
int stack_adjust_amount = INTVAL (XEXP (SET_SRC (body), 1));
int total_pushed = 0;
int pushes = 0;
/* Find all successive push insns. */
p = insn;
/* Don't convert more than three pushes;
that starts adding too many displaced addresses
and the whole thing starts becoming a losing
proposition. */
while (pushes < 3)
{
rtx pbody, dest;
p = next_nonnote_insn (p);
if (p == 0 || GET_CODE (p) != INSN)
break;
pbody = PATTERN (p);
if (GET_CODE (pbody) != SET)
break;
dest = SET_DEST (pbody);
/* Allow a no-op move between the adjust and the push. */
if (GET_CODE (dest) == REG
&& GET_CODE (SET_SRC (pbody)) == REG
&& REGNO (dest) == REGNO (SET_SRC (pbody)))
continue;
if (! (GET_CODE (dest) == MEM
&& GET_CODE (XEXP (dest, 0)) == POST_INC
&& XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx))
break;
pushes++;
if (total_pushed + GET_MODE_SIZE (GET_MODE (SET_DEST (pbody)))
> stack_adjust_amount)
break;
total_pushed += GET_MODE_SIZE (GET_MODE (SET_DEST (pbody)));
}
/* Discard the amount pushed from the stack adjust;
maybe eliminate it entirely. */
if (total_pushed >= stack_adjust_amount)
{
delete_computation (stack_adjust_insn);
total_pushed = stack_adjust_amount;
}
else
XEXP (SET_SRC (PATTERN (stack_adjust_insn)), 1)
= GEN_INT (stack_adjust_amount - total_pushed);
/* Change the appropriate push insns to ordinary stores. */
p = insn;
while (total_pushed > 0)
{
rtx pbody, dest;
p = next_nonnote_insn (p);
if (GET_CODE (p) != INSN)
break;
pbody = PATTERN (p);
if (GET_CODE (pbody) != SET)
break;
dest = SET_DEST (pbody);
/* Allow a no-op move between the adjust and the push. */
if (GET_CODE (dest) == REG
&& GET_CODE (SET_SRC (pbody)) == REG
&& REGNO (dest) == REGNO (SET_SRC (pbody)))
continue;
if (! (GET_CODE (dest) == MEM
&& GET_CODE (XEXP (dest, 0)) == POST_INC
&& XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx))
break;
total_pushed -= GET_MODE_SIZE (GET_MODE (SET_DEST (pbody)));
/* If this push doesn't fully fit in the space
of the stack adjust that we deleted,
make another stack adjust here for what we
didn't use up. There should be peepholes
to recognize the resulting sequence of insns. */
if (total_pushed < 0)
{
emit_insn_before (gen_add2_insn (stack_pointer_rtx,
GEN_INT (- total_pushed)),
p);
break;
}
XEXP (dest, 0)
= plus_constant (stack_pointer_rtx, total_pushed);
}
}
#endif
/* Detect and delete no-op move instructions
resulting from not allocating a parameter in a register. */
if (GET_CODE (body) == SET
&& (SET_DEST (body) == SET_SRC (body)
|| (GET_CODE (SET_DEST (body)) == MEM
&& GET_CODE (SET_SRC (body)) == MEM
&& rtx_equal_p (SET_SRC (body), SET_DEST (body))))
&& ! (GET_CODE (SET_DEST (body)) == MEM
&& MEM_VOLATILE_P (SET_DEST (body)))
&& ! (GET_CODE (SET_SRC (body)) == MEM
&& MEM_VOLATILE_P (SET_SRC (body))))
delete_computation (insn);
/* Detect and ignore no-op move instructions
resulting from smart or fortuitous register allocation. */
else if (GET_CODE (body) == SET)
{
int sreg = true_regnum (SET_SRC (body));
int dreg = true_regnum (SET_DEST (body));
if (sreg == dreg && sreg >= 0)
delete_insn (insn);
else if (sreg >= 0 && dreg >= 0)
{
rtx trial;
rtx tem = find_equiv_reg (NULL_RTX, insn, 0,
sreg, NULL_PTR, dreg,
GET_MODE (SET_SRC (body)));
if (tem != 0
&& GET_MODE (tem) == GET_MODE (SET_DEST (body)))
{
/* DREG may have been the target of a REG_DEAD note in
the insn which makes INSN redundant. If so, reorg
would still think it is dead. So search for such a
note and delete it if we find it. */
if (! find_regno_note (insn, REG_UNUSED, dreg))
for (trial = prev_nonnote_insn (insn);
trial && GET_CODE (trial) != CODE_LABEL;
trial = prev_nonnote_insn (trial))
if (find_regno_note (trial, REG_DEAD, dreg))
{
remove_death (dreg, trial);
break;
}
/* Deleting insn could lose a death-note for SREG. */
if ((trial = find_regno_note (insn, REG_DEAD, sreg)))
{
/* Change this into a USE so that we won't emit
code for it, but still can keep the note. */
PATTERN (insn)
= gen_rtx_USE (VOIDmode, XEXP (trial, 0));
INSN_CODE (insn) = -1;
/* Remove all reg notes but the REG_DEAD one. */
REG_NOTES (insn) = trial;
XEXP (trial, 1) = NULL_RTX;
}
else
delete_insn (insn);
}
}
else if (dreg >= 0 && CONSTANT_P (SET_SRC (body))
&& find_equiv_reg (SET_SRC (body), insn, 0, dreg,
NULL_PTR, 0,
GET_MODE (SET_DEST (body))))
{
/* This handles the case where we have two consecutive
assignments of the same constant to pseudos that didn't
get a hard reg. Each SET from the constant will be
converted into a SET of the spill register and an
output reload will be made following it. This produces
two loads of the same constant into the same spill
register. */
rtx in_insn = insn;
/* Look back for a death note for the first reg.
If there is one, it is no longer accurate. */
while (in_insn && GET_CODE (in_insn) != CODE_LABEL)
{
if ((GET_CODE (in_insn) == INSN
|| GET_CODE (in_insn) == JUMP_INSN)
&& find_regno_note (in_insn, REG_DEAD, dreg))
{
remove_death (dreg, in_insn);
break;
}
in_insn = PREV_INSN (in_insn);
}
/* Delete the second load of the value. */
delete_insn (insn);
}
}
else if (GET_CODE (body) == PARALLEL)
{
/* If each part is a set between two identical registers or
a USE or CLOBBER, delete the insn. */
int i, sreg, dreg;
rtx tem;
for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
{
tem = XVECEXP (body, 0, i);
if (GET_CODE (tem) == USE || GET_CODE (tem) == CLOBBER)
continue;
if (GET_CODE (tem) != SET
|| (sreg = true_regnum (SET_SRC (tem))) < 0
|| (dreg = true_regnum (SET_DEST (tem))) < 0
|| dreg != sreg)
break;
}
if (i < 0)
delete_insn (insn);
}
/* Also delete insns to store bit fields if they are no-ops. */
/* Not worth the hair to detect this in the big-endian case. */
else if (! BYTES_BIG_ENDIAN
&& GET_CODE (body) == SET
&& GET_CODE (SET_DEST (body)) == ZERO_EXTRACT
&& XEXP (SET_DEST (body), 2) == const0_rtx
&& XEXP (SET_DEST (body), 0) == SET_SRC (body)
&& ! (GET_CODE (SET_SRC (body)) == MEM
&& MEM_VOLATILE_P (SET_SRC (body))))
delete_insn (insn);
}
insn = next;
}
}
/* See if there is still a NOTE_INSN_FUNCTION_END in this function.
If so indicate that this function can drop off the end by returning
1, else return 0.
CHECK_DELETED indicates whether we must check if the note being
searched for has the deleted flag set.
DELETE_FINAL_NOTE indicates whether we should delete the note
if we find it. */
static int
calculate_can_reach_end (last, check_deleted, delete_final_note)
rtx last;
int check_deleted;
int delete_final_note;
{
rtx insn = last;
int n_labels = 1;
while (insn != NULL_RTX)
{
int ok = 0;
/* One label can follow the end-note: the return label. */
if (GET_CODE (insn) == CODE_LABEL && n_labels-- > 0)
ok = 1;
/* Ordinary insns can follow it if returning a structure. */
else if (GET_CODE (insn) == INSN)
ok = 1;
/* If machine uses explicit RETURN insns, no epilogue,
then one of them follows the note. */
else if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RETURN)
ok = 1;
/* A barrier can follow the return insn. */
else if (GET_CODE (insn) == BARRIER)
ok = 1;
/* Other kinds of notes can follow also. */
else if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)
ok = 1;
if (ok != 1)
break;
insn = PREV_INSN (insn);
}
/* See if we backed up to the appropriate type of note. */
if (insn != NULL_RTX
&& GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END
&& (check_deleted == 0
|| ! INSN_DELETED_P (insn)))
{
if (delete_final_note)
delete_insn (insn);
return 1;
}
return 0;
}
/* LOOP_START is a NOTE_INSN_LOOP_BEG note that is followed by an unconditional
jump. Assume that this unconditional jump is to the exit test code. If
the code is sufficiently simple, make a copy of it before INSN,
followed by a jump to the exit of the loop. Then delete the unconditional
jump after INSN.
Return 1 if we made the change, else 0.
This is only safe immediately after a regscan pass because it uses the
values of regno_first_uid and regno_last_uid. */
static int
duplicate_loop_exit_test (loop_start)
rtx loop_start;
{
rtx insn, set, reg, p, link;
- rtx copy = 0;
+ rtx copy = 0, first_copy = 0;
int num_insns = 0;
rtx exitcode = NEXT_INSN (JUMP_LABEL (next_nonnote_insn (loop_start)));
rtx lastexit;
int max_reg = max_reg_num ();
rtx *reg_map = 0;
/* Scan the exit code. We do not perform this optimization if any insn:
is a CALL_INSN
is a CODE_LABEL
has a REG_RETVAL or REG_LIBCALL note (hard to adjust)
is a NOTE_INSN_LOOP_BEG because this means we have a nested loop
is a NOTE_INSN_BLOCK_{BEG,END} because duplicating these notes
is not valid.
We also do not do this if we find an insn with ASM_OPERANDS. While
this restriction should not be necessary, copying an insn with
ASM_OPERANDS can confuse asm_noperands in some cases.
Also, don't do this if the exit code is more than 20 insns. */
for (insn = exitcode;
insn
&& ! (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
insn = NEXT_INSN (insn))
{
switch (GET_CODE (insn))
{
case CODE_LABEL:
case CALL_INSN:
return 0;
case NOTE:
/* We could be in front of the wrong NOTE_INSN_LOOP_END if there is
a jump immediately after the loop start that branches outside
the loop but within an outer loop, near the exit test.
If we copied this exit test and created a phony
NOTE_INSN_LOOP_VTOP, this could make instructions immediately
before the exit test look like these could be safely moved
out of the loop even if they actually may be never executed.
This can be avoided by checking here for NOTE_INSN_LOOP_CONT. */
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT)
return 0;
if (optimize < 2
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END))
/* If we were to duplicate this code, we would not move
the BLOCK notes, and so debugging the moved code would
be difficult. Thus, we only move the code with -O2 or
higher. */
return 0;
break;
case JUMP_INSN:
case INSN:
/* The code below would grossly mishandle REG_WAS_0 notes,
so get rid of them here. */
while ((p = find_reg_note (insn, REG_WAS_0, NULL_RTX)) != 0)
remove_note (insn, p);
if (++num_insns > 20
|| find_reg_note (insn, REG_RETVAL, NULL_RTX)
|| find_reg_note (insn, REG_LIBCALL, NULL_RTX)
|| asm_noperands (PATTERN (insn)) > 0)
return 0;
break;
default:
break;
}
}
/* Unless INSN is zero, we can do the optimization. */
if (insn == 0)
return 0;
lastexit = insn;
/* See if any insn sets a register only used in the loop exit code and
not a user variable. If so, replace it with a new register. */
for (insn = exitcode; insn != lastexit; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == INSN
&& (set = single_set (insn)) != 0
&& ((reg = SET_DEST (set), GET_CODE (reg) == REG)
|| (GET_CODE (reg) == SUBREG
&& (reg = SUBREG_REG (reg), GET_CODE (reg) == REG)))
&& REGNO (reg) >= FIRST_PSEUDO_REGISTER
&& REGNO_FIRST_UID (REGNO (reg)) == INSN_UID (insn))
{
for (p = NEXT_INSN (insn); p != lastexit; p = NEXT_INSN (p))
if (REGNO_LAST_UID (REGNO (reg)) == INSN_UID (p))
break;
if (p != lastexit)
{
/* We can do the replacement. Allocate reg_map if this is the
first replacement we found. */
if (reg_map == 0)
{
reg_map = (rtx *) alloca (max_reg * sizeof (rtx));
bzero ((char *) reg_map, max_reg * sizeof (rtx));
}
REG_LOOP_TEST_P (reg) = 1;
reg_map[REGNO (reg)] = gen_reg_rtx (GET_MODE (reg));
}
}
/* Now copy each insn. */
for (insn = exitcode; insn != lastexit; insn = NEXT_INSN (insn))
- switch (GET_CODE (insn))
- {
- case BARRIER:
- copy = emit_barrier_before (loop_start);
- break;
- case NOTE:
- /* Only copy line-number notes. */
- if (NOTE_LINE_NUMBER (insn) >= 0)
- {
- copy = emit_note_before (NOTE_LINE_NUMBER (insn), loop_start);
- NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn);
- }
- break;
+ {
+ switch (GET_CODE (insn))
+ {
+ case BARRIER:
+ copy = emit_barrier_before (loop_start);
+ break;
+ case NOTE:
+ /* Only copy line-number notes. */
+ if (NOTE_LINE_NUMBER (insn) >= 0)
+ {
+ copy = emit_note_before (NOTE_LINE_NUMBER (insn), loop_start);
+ NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn);
+ }
+ break;
case INSN:
copy = emit_insn_before (copy_rtx (PATTERN (insn)), loop_start);
if (reg_map)
replace_regs (PATTERN (copy), reg_map, max_reg, 1);
mark_jump_label (PATTERN (copy), copy, 0);
/* Copy all REG_NOTES except REG_LABEL since mark_jump_label will
make them. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) != REG_LABEL)
REG_NOTES (copy)
= copy_rtx (gen_rtx_EXPR_LIST (REG_NOTE_KIND (link),
XEXP (link, 0),
REG_NOTES (copy)));
if (reg_map && REG_NOTES (copy))
replace_regs (REG_NOTES (copy), reg_map, max_reg, 1);
break;
- case JUMP_INSN:
- copy = emit_jump_insn_before (copy_rtx (PATTERN (insn)), loop_start);
- if (reg_map)
- replace_regs (PATTERN (copy), reg_map, max_reg, 1);
- mark_jump_label (PATTERN (copy), copy, 0);
- if (REG_NOTES (insn))
- {
- REG_NOTES (copy) = copy_rtx (REG_NOTES (insn));
- if (reg_map)
- replace_regs (REG_NOTES (copy), reg_map, max_reg, 1);
- }
+ case JUMP_INSN:
+ copy = emit_jump_insn_before (copy_rtx (PATTERN (insn)), loop_start);
+ if (reg_map)
+ replace_regs (PATTERN (copy), reg_map, max_reg, 1);
+ mark_jump_label (PATTERN (copy), copy, 0);
+ if (REG_NOTES (insn))
+ {
+ REG_NOTES (copy) = copy_rtx (REG_NOTES (insn));
+ if (reg_map)
+ replace_regs (REG_NOTES (copy), reg_map, max_reg, 1);
+ }
- /* If this is a simple jump, add it to the jump chain. */
+ /* If this is a simple jump, add it to the jump chain. */
- if (INSN_UID (copy) < max_jump_chain && JUMP_LABEL (copy)
- && simplejump_p (copy))
- {
- jump_chain[INSN_UID (copy)]
- = jump_chain[INSN_UID (JUMP_LABEL (copy))];
- jump_chain[INSN_UID (JUMP_LABEL (copy))] = copy;
- }
- break;
+ if (INSN_UID (copy) < max_jump_chain && JUMP_LABEL (copy)
+ && simplejump_p (copy))
+ {
+ jump_chain[INSN_UID (copy)]
+ = jump_chain[INSN_UID (JUMP_LABEL (copy))];
+ jump_chain[INSN_UID (JUMP_LABEL (copy))] = copy;
+ }
+ break;
- default:
- abort ();
- }
+ default:
+ abort ();
+ }
+ /* Record the first insn we copied. We need it so that we can
+ scan the copied insns for new pseudo registers. */
+ if (! first_copy)
+ first_copy = copy;
+ }
+
/* Now clean up by emitting a jump to the end label and deleting the jump
at the start of the loop. */
if (! copy || GET_CODE (copy) != BARRIER)
{
copy = emit_jump_insn_before (gen_jump (get_label_after (insn)),
loop_start);
+
+ /* Record the first insn we copied. We need it so that we can
+ scan the copied insns for new pseudo registers. This may not
+ be strictly necessary since we should have copied at least one
+ insn above. But I am going to be safe. */
+ if (! first_copy)
+ first_copy = copy;
+
mark_jump_label (PATTERN (copy), copy, 0);
if (INSN_UID (copy) < max_jump_chain
&& INSN_UID (JUMP_LABEL (copy)) < max_jump_chain)
{
jump_chain[INSN_UID (copy)]
= jump_chain[INSN_UID (JUMP_LABEL (copy))];
jump_chain[INSN_UID (JUMP_LABEL (copy))] = copy;
}
emit_barrier_before (loop_start);
}
+
+ /* Now scan from the first insn we copied to the last insn we copied
+ (copy) for new pseudo registers. Do this after the code to jump to
+ the end label since that might create a new pseudo too. */
+ reg_scan_update (first_copy, copy, max_reg);
/* Mark the exit code as the virtual top of the converted loop. */
emit_note_before (NOTE_INSN_LOOP_VTOP, exitcode);
delete_insn (next_nonnote_insn (loop_start));
return 1;
}
/* Move all block-beg, block-end, loop-beg, loop-cont, loop-vtop, and
loop-end notes between START and END out before START. Assume that
END is not such a note. START may be such a note. Returns the value
of the new starting insn, which may be different if the original start
was such a note. */
rtx
squeeze_notes (start, end)
rtx start, end;
{
rtx insn;
rtx next;
for (insn = start; insn != end; insn = next)
{
next = NEXT_INSN (insn);
if (GET_CODE (insn) == NOTE
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_VTOP))
{
if (insn == start)
start = next;
else
{
rtx prev = PREV_INSN (insn);
PREV_INSN (insn) = PREV_INSN (start);
NEXT_INSN (insn) = start;
NEXT_INSN (PREV_INSN (insn)) = insn;
PREV_INSN (NEXT_INSN (insn)) = insn;
NEXT_INSN (prev) = next;
PREV_INSN (next) = prev;
}
}
}
return start;
}
/* Compare the instructions before insn E1 with those before E2
to find an opportunity for cross jumping.
(This means detecting identical sequences of insns followed by
jumps to the same place, or followed by a label and a jump
to that label, and replacing one with a jump to the other.)
Assume E1 is a jump that jumps to label E2
(that is not always true but it might as well be).
Find the longest possible equivalent sequences
and store the first insns of those sequences into *F1 and *F2.
Store zero there if no equivalent preceding instructions are found.
We give up if we find a label in stream 1.
Actually we could transfer that label into stream 2. */
static void
find_cross_jump (e1, e2, minimum, f1, f2)
rtx e1, e2;
int minimum;
rtx *f1, *f2;
{
register rtx i1 = e1, i2 = e2;
register rtx p1, p2;
int lose = 0;
rtx last1 = 0, last2 = 0;
rtx afterlast1 = 0, afterlast2 = 0;
*f1 = 0;
*f2 = 0;
while (1)
{
i1 = prev_nonnote_insn (i1);
i2 = PREV_INSN (i2);
while (i2 && (GET_CODE (i2) == NOTE || GET_CODE (i2) == CODE_LABEL))
i2 = PREV_INSN (i2);
if (i1 == 0)
break;
/* Don't allow the range of insns preceding E1 or E2
to include the other (E2 or E1). */
if (i2 == e1 || i1 == e2)
break;
/* If we will get to this code by jumping, those jumps will be
tensioned to go directly to the new label (before I2),
so this cross-jumping won't cost extra. So reduce the minimum. */
if (GET_CODE (i1) == CODE_LABEL)
{
--minimum;
break;
}
if (i2 == 0 || GET_CODE (i1) != GET_CODE (i2))
break;
/* Avoid moving insns across EH regions if either of the insns
can throw. */
if (flag_exceptions
&& (asynchronous_exceptions || GET_CODE (i1) == CALL_INSN)
&& !in_same_eh_region (i1, i2))
break;
p1 = PATTERN (i1);
p2 = PATTERN (i2);
/* If this is a CALL_INSN, compare register usage information.
If we don't check this on stack register machines, the two
CALL_INSNs might be merged leaving reg-stack.c with mismatching
numbers of stack registers in the same basic block.
If we don't check this on machines with delay slots, a delay slot may
be filled that clobbers a parameter expected by the subroutine.
??? We take the simple route for now and assume that if they're
equal, they were constructed identically. */
if (GET_CODE (i1) == CALL_INSN
&& ! rtx_equal_p (CALL_INSN_FUNCTION_USAGE (i1),
CALL_INSN_FUNCTION_USAGE (i2)))
lose = 1;
#ifdef STACK_REGS
/* If cross_jump_death_matters is not 0, the insn's mode
indicates whether or not the insn contains any stack-like
regs. */
if (!lose && cross_jump_death_matters && stack_regs_mentioned (i1))
{
/* If register stack conversion has already been done, then
death notes must also be compared before it is certain that
the two instruction streams match. */
rtx note;
HARD_REG_SET i1_regset, i2_regset;
CLEAR_HARD_REG_SET (i1_regset);
CLEAR_HARD_REG_SET (i2_regset);
for (note = REG_NOTES (i1); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD
&& STACK_REG_P (XEXP (note, 0)))
SET_HARD_REG_BIT (i1_regset, REGNO (XEXP (note, 0)));
for (note = REG_NOTES (i2); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD
&& STACK_REG_P (XEXP (note, 0)))
SET_HARD_REG_BIT (i2_regset, REGNO (XEXP (note, 0)));
GO_IF_HARD_REG_EQUAL (i1_regset, i2_regset, done);
lose = 1;
done:
;
}
#endif
/* Don't allow old-style asm or volatile extended asms to be accepted
for cross jumping purposes. It is conceptually correct to allow
them, since cross-jumping preserves the dynamic instruction order
even though it is changing the static instruction order. However,
if an asm is being used to emit an assembler pseudo-op, such as
the MIPS `.set reorder' pseudo-op, then the static instruction order
matters and it must be preserved. */
if (GET_CODE (p1) == ASM_INPUT || GET_CODE (p2) == ASM_INPUT
|| (GET_CODE (p1) == ASM_OPERANDS && MEM_VOLATILE_P (p1))
|| (GET_CODE (p2) == ASM_OPERANDS && MEM_VOLATILE_P (p2)))
lose = 1;
if (lose || GET_CODE (p1) != GET_CODE (p2)
|| ! rtx_renumbered_equal_p (p1, p2))
{
/* The following code helps take care of G++ cleanups. */
rtx equiv1;
rtx equiv2;
if (!lose && GET_CODE (p1) == GET_CODE (p2)
&& ((equiv1 = find_reg_note (i1, REG_EQUAL, NULL_RTX)) != 0
|| (equiv1 = find_reg_note (i1, REG_EQUIV, NULL_RTX)) != 0)
&& ((equiv2 = find_reg_note (i2, REG_EQUAL, NULL_RTX)) != 0
|| (equiv2 = find_reg_note (i2, REG_EQUIV, NULL_RTX)) != 0)
/* If the equivalences are not to a constant, they may
reference pseudos that no longer exist, so we can't
use them. */
&& CONSTANT_P (XEXP (equiv1, 0))
&& rtx_equal_p (XEXP (equiv1, 0), XEXP (equiv2, 0)))
{
rtx s1 = single_set (i1);
rtx s2 = single_set (i2);
if (s1 != 0 && s2 != 0
&& rtx_renumbered_equal_p (SET_DEST (s1), SET_DEST (s2)))
{
validate_change (i1, &SET_SRC (s1), XEXP (equiv1, 0), 1);
validate_change (i2, &SET_SRC (s2), XEXP (equiv2, 0), 1);
if (! rtx_renumbered_equal_p (p1, p2))
cancel_changes (0);
else if (apply_change_group ())
goto win;
}
}
/* Insns fail to match; cross jumping is limited to the following
insns. */
#ifdef HAVE_cc0
/* Don't allow the insn after a compare to be shared by
cross-jumping unless the compare is also shared.
Here, if either of these non-matching insns is a compare,
exclude the following insn from possible cross-jumping. */
if (sets_cc0_p (p1) || sets_cc0_p (p2))
last1 = afterlast1, last2 = afterlast2, ++minimum;
#endif
/* If cross-jumping here will feed a jump-around-jump
optimization, this jump won't cost extra, so reduce
the minimum. */
if (GET_CODE (i1) == JUMP_INSN
&& JUMP_LABEL (i1)
&& prev_real_insn (JUMP_LABEL (i1)) == e1)
--minimum;
break;
}
win:
if (GET_CODE (p1) != USE && GET_CODE (p1) != CLOBBER)
{
/* Ok, this insn is potentially includable in a cross-jump here. */
afterlast1 = last1, afterlast2 = last2;
last1 = i1, last2 = i2, --minimum;
}
}
if (minimum <= 0 && last1 != 0 && last1 != e1)
*f1 = last1, *f2 = last2;
}
static void
do_cross_jump (insn, newjpos, newlpos)
rtx insn, newjpos, newlpos;
{
/* Find an existing label at this point
or make a new one if there is none. */
register rtx label = get_label_before (newlpos);
/* Make the same jump insn jump to the new point. */
if (GET_CODE (PATTERN (insn)) == RETURN)
{
/* Remove from jump chain of returns. */
delete_from_jump_chain (insn);
/* Change the insn. */
PATTERN (insn) = gen_jump (label);
INSN_CODE (insn) = -1;
JUMP_LABEL (insn) = label;
LABEL_NUSES (label)++;
/* Add to new the jump chain. */
if (INSN_UID (label) < max_jump_chain
&& INSN_UID (insn) < max_jump_chain)
{
jump_chain[INSN_UID (insn)] = jump_chain[INSN_UID (label)];
jump_chain[INSN_UID (label)] = insn;
}
}
else
redirect_jump (insn, label);
/* Delete the matching insns before the jump. Also, remove any REG_EQUAL
or REG_EQUIV note in the NEWLPOS stream that isn't also present in
the NEWJPOS stream. */
while (newjpos != insn)
{
rtx lnote;
for (lnote = REG_NOTES (newlpos); lnote; lnote = XEXP (lnote, 1))
if ((REG_NOTE_KIND (lnote) == REG_EQUAL
|| REG_NOTE_KIND (lnote) == REG_EQUIV)
&& ! find_reg_note (newjpos, REG_EQUAL, XEXP (lnote, 0))
&& ! find_reg_note (newjpos, REG_EQUIV, XEXP (lnote, 0)))
remove_note (newlpos, lnote);
delete_insn (newjpos);
newjpos = next_real_insn (newjpos);
newlpos = next_real_insn (newlpos);
}
}
/* Return the label before INSN, or put a new label there. */
rtx
get_label_before (insn)
rtx insn;
{
rtx label;
/* Find an existing label at this point
or make a new one if there is none. */
label = prev_nonnote_insn (insn);
if (label == 0 || GET_CODE (label) != CODE_LABEL)
{
rtx prev = PREV_INSN (insn);
label = gen_label_rtx ();
emit_label_after (label, prev);
LABEL_NUSES (label) = 0;
}
return label;
}
/* Return the label after INSN, or put a new label there. */
rtx
get_label_after (insn)
rtx insn;
{
rtx label;
/* Find an existing label at this point
or make a new one if there is none. */
label = next_nonnote_insn (insn);
if (label == 0 || GET_CODE (label) != CODE_LABEL)
{
label = gen_label_rtx ();
emit_label_after (label, insn);
LABEL_NUSES (label) = 0;
}
return label;
}
/* Return 1 if INSN is a jump that jumps to right after TARGET
only on the condition that TARGET itself would drop through.
Assumes that TARGET is a conditional jump. */
static int
jump_back_p (insn, target)
rtx insn, target;
{
rtx cinsn, ctarget;
enum rtx_code codei, codet;
if (simplejump_p (insn) || ! condjump_p (insn)
|| simplejump_p (target)
|| target != prev_real_insn (JUMP_LABEL (insn)))
return 0;
cinsn = XEXP (SET_SRC (PATTERN (insn)), 0);
ctarget = XEXP (SET_SRC (PATTERN (target)), 0);
codei = GET_CODE (cinsn);
codet = GET_CODE (ctarget);
if (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx)
{
if (! can_reverse_comparison_p (cinsn, insn))
return 0;
codei = reverse_condition (codei);
}
if (XEXP (SET_SRC (PATTERN (target)), 2) == pc_rtx)
{
if (! can_reverse_comparison_p (ctarget, target))
return 0;
codet = reverse_condition (codet);
}
return (codei == codet
&& rtx_renumbered_equal_p (XEXP (cinsn, 0), XEXP (ctarget, 0))
&& rtx_renumbered_equal_p (XEXP (cinsn, 1), XEXP (ctarget, 1)));
}
/* Given a comparison, COMPARISON, inside a conditional jump insn, INSN,
return non-zero if it is safe to reverse this comparison. It is if our
floating-point is not IEEE, if this is an NE or EQ comparison, or if
this is known to be an integer comparison. */
int
can_reverse_comparison_p (comparison, insn)
rtx comparison;
rtx insn;
{
rtx arg0;
/* If this is not actually a comparison, we can't reverse it. */
if (GET_RTX_CLASS (GET_CODE (comparison)) != '<')
return 0;
if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
/* If this is an NE comparison, it is safe to reverse it to an EQ
comparison and vice versa, even for floating point. If no operands
are NaNs, the reversal is valid. If some operand is a NaN, EQ is
always false and NE is always true, so the reversal is also valid. */
|| flag_fast_math
|| GET_CODE (comparison) == NE
|| GET_CODE (comparison) == EQ)
return 1;
arg0 = XEXP (comparison, 0);
/* Make sure ARG0 is one of the actual objects being compared. If we
can't do this, we can't be sure the comparison can be reversed.
Handle cc0 and a MODE_CC register. */
if ((GET_CODE (arg0) == REG && GET_MODE_CLASS (GET_MODE (arg0)) == MODE_CC)
#ifdef HAVE_cc0
|| arg0 == cc0_rtx
#endif
)
{
rtx prev = prev_nonnote_insn (insn);
rtx set;
/* If the comparison itself was a loop invariant, it could have been
hoisted out of the loop. If we proceed to unroll such a loop, then
we may not be able to find the comparison when copying the loop.
Returning zero in that case is the safe thing to do. */
if (prev == 0)
return 0;
set = single_set (prev);
if (set == 0 || SET_DEST (set) != arg0)
return 0;
arg0 = SET_SRC (set);
if (GET_CODE (arg0) == COMPARE)
arg0 = XEXP (arg0, 0);
}
/* We can reverse this if ARG0 is a CONST_INT or if its mode is
not VOIDmode and neither a MODE_CC nor MODE_FLOAT type. */
return (GET_CODE (arg0) == CONST_INT
|| (GET_MODE (arg0) != VOIDmode
&& GET_MODE_CLASS (GET_MODE (arg0)) != MODE_CC
&& GET_MODE_CLASS (GET_MODE (arg0)) != MODE_FLOAT));
}
/* Given an rtx-code for a comparison, return the code
for the negated comparison.
WATCH OUT! reverse_condition is not safe to use on a jump
that might be acting on the results of an IEEE floating point comparison,
because of the special treatment of non-signaling nans in comparisons.
Use can_reverse_comparison_p to be sure. */
enum rtx_code
reverse_condition (code)
enum rtx_code code;
{
switch (code)
{
case EQ:
return NE;
case NE:
return EQ;
case GT:
return LE;
case GE:
return LT;
case LT:
return GE;
case LE:
return GT;
case GTU:
return LEU;
case GEU:
return LTU;
case LTU:
return GEU;
case LEU:
return GTU;
default:
abort ();
return UNKNOWN;
}
}
/* Similar, but return the code when two operands of a comparison are swapped.
This IS safe for IEEE floating-point. */
enum rtx_code
swap_condition (code)
enum rtx_code code;
{
switch (code)
{
case EQ:
case NE:
return code;
case GT:
return LT;
case GE:
return LE;
case LT:
return GT;
case LE:
return GE;
case GTU:
return LTU;
case GEU:
return LEU;
case LTU:
return GTU;
case LEU:
return GEU;
default:
abort ();
return UNKNOWN;
}
}
/* Given a comparison CODE, return the corresponding unsigned comparison.
If CODE is an equality comparison or already an unsigned comparison,
CODE is returned. */
enum rtx_code
unsigned_condition (code)
enum rtx_code code;
{
switch (code)
{
case EQ:
case NE:
case GTU:
case GEU:
case LTU:
case LEU:
return code;
case GT:
return GTU;
case GE:
return GEU;
case LT:
return LTU;
case LE:
return LEU;
default:
abort ();
}
}
/* Similarly, return the signed version of a comparison. */
enum rtx_code
signed_condition (code)
enum rtx_code code;
{
switch (code)
{
case EQ:
case NE:
case GT:
case GE:
case LT:
case LE:
return code;
case GTU:
return GT;
case GEU:
return GE;
case LTU:
return LT;
case LEU:
return LE;
default:
abort ();
}
}
/* Return non-zero if CODE1 is more strict than CODE2, i.e., if the
truth of CODE1 implies the truth of CODE2. */
int
comparison_dominates_p (code1, code2)
enum rtx_code code1, code2;
{
if (code1 == code2)
return 1;
switch (code1)
{
case EQ:
if (code2 == LE || code2 == LEU || code2 == GE || code2 == GEU)
return 1;
break;
case LT:
if (code2 == LE || code2 == NE)
return 1;
break;
case GT:
if (code2 == GE || code2 == NE)
return 1;
break;
case LTU:
if (code2 == LEU || code2 == NE)
return 1;
break;
case GTU:
if (code2 == GEU || code2 == NE)
return 1;
break;
default:
break;
}
return 0;
}
/* Return 1 if INSN is an unconditional jump and nothing else. */
int
simplejump_p (insn)
rtx insn;
{
return (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == SET
&& GET_CODE (SET_DEST (PATTERN (insn))) == PC
&& GET_CODE (SET_SRC (PATTERN (insn))) == LABEL_REF);
}
/* Return nonzero if INSN is a (possibly) conditional jump
and nothing more. */
int
condjump_p (insn)
rtx insn;
{
register rtx x = PATTERN (insn);
if (GET_CODE (x) != SET)
return 0;
if (GET_CODE (SET_DEST (x)) != PC)
return 0;
if (GET_CODE (SET_SRC (x)) == LABEL_REF)
return 1;
if (GET_CODE (SET_SRC (x)) != IF_THEN_ELSE)
return 0;
if (XEXP (SET_SRC (x), 2) == pc_rtx
&& (GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF
|| GET_CODE (XEXP (SET_SRC (x), 1)) == RETURN))
return 1;
if (XEXP (SET_SRC (x), 1) == pc_rtx
&& (GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF
|| GET_CODE (XEXP (SET_SRC (x), 2)) == RETURN))
return 1;
return 0;
}
/* Return nonzero if INSN is a (possibly) conditional jump
and nothing more. */
int
condjump_in_parallel_p (insn)
rtx insn;
{
register rtx x = PATTERN (insn);
if (GET_CODE (x) != PARALLEL)
return 0;
else
x = XVECEXP (x, 0, 0);
if (GET_CODE (x) != SET)
return 0;
if (GET_CODE (SET_DEST (x)) != PC)
return 0;
if (GET_CODE (SET_SRC (x)) == LABEL_REF)
return 1;
if (GET_CODE (SET_SRC (x)) != IF_THEN_ELSE)
return 0;
if (XEXP (SET_SRC (x), 2) == pc_rtx
&& (GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF
|| GET_CODE (XEXP (SET_SRC (x), 1)) == RETURN))
return 1;
if (XEXP (SET_SRC (x), 1) == pc_rtx
&& (GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF
|| GET_CODE (XEXP (SET_SRC (x), 2)) == RETURN))
return 1;
return 0;
}
/* Return the label of a conditional jump. */
rtx
condjump_label (insn)
rtx insn;
{
register rtx x = PATTERN (insn);
if (GET_CODE (x) == PARALLEL)
x = XVECEXP (x, 0, 0);
if (GET_CODE (x) != SET)
return NULL_RTX;
if (GET_CODE (SET_DEST (x)) != PC)
return NULL_RTX;
x = SET_SRC (x);
if (GET_CODE (x) == LABEL_REF)
return x;
if (GET_CODE (x) != IF_THEN_ELSE)
return NULL_RTX;
if (XEXP (x, 2) == pc_rtx && GET_CODE (XEXP (x, 1)) == LABEL_REF)
return XEXP (x, 1);
if (XEXP (x, 1) == pc_rtx && GET_CODE (XEXP (x, 2)) == LABEL_REF)
return XEXP (x, 2);
return NULL_RTX;
}
/* Return true if INSN is a (possibly conditional) return insn. */
static int
returnjump_p_1 (loc, data)
rtx *loc;
void *data ATTRIBUTE_UNUSED;
{
rtx x = *loc;
return GET_CODE (x) == RETURN;
}
int
returnjump_p (insn)
rtx insn;
{
return for_each_rtx (&PATTERN (insn), returnjump_p_1, NULL);
}
#ifdef HAVE_cc0
/* Return 1 if X is an RTX that does nothing but set the condition codes
and CLOBBER or USE registers.
Return -1 if X does explicitly set the condition codes,
but also does other things. */
int
sets_cc0_p (x)
rtx x ATTRIBUTE_UNUSED;
{
if (GET_CODE (x) == SET && SET_DEST (x) == cc0_rtx)
return 1;
if (GET_CODE (x) == PARALLEL)
{
int i;
int sets_cc0 = 0;
int other_things = 0;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
if (GET_CODE (XVECEXP (x, 0, i)) == SET
&& SET_DEST (XVECEXP (x, 0, i)) == cc0_rtx)
sets_cc0 = 1;
else if (GET_CODE (XVECEXP (x, 0, i)) == SET)
other_things = 1;
}
return ! sets_cc0 ? 0 : other_things ? -1 : 1;
}
return 0;
}
#endif
/* Follow any unconditional jump at LABEL;
return the ultimate label reached by any such chain of jumps.
If LABEL is not followed by a jump, return LABEL.
If the chain loops or we can't find end, return LABEL,
since that tells caller to avoid changing the insn.
If RELOAD_COMPLETED is 0, we do not chain across a NOTE_INSN_LOOP_BEG or
a USE or CLOBBER. */
rtx
follow_jumps (label)
rtx label;
{
register rtx insn;
register rtx next;
register rtx value = label;
register int depth;
for (depth = 0;
(depth < 10
&& (insn = next_active_insn (value)) != 0
&& GET_CODE (insn) == JUMP_INSN
&& ((JUMP_LABEL (insn) != 0 && simplejump_p (insn))
|| GET_CODE (PATTERN (insn)) == RETURN)
&& (next = NEXT_INSN (insn))
&& GET_CODE (next) == BARRIER);
depth++)
{
/* Don't chain through the insn that jumps into a loop
from outside the loop,
since that would create multiple loop entry jumps
and prevent loop optimization. */
rtx tem;
if (!reload_completed)
for (tem = value; tem != insn; tem = NEXT_INSN (tem))
if (GET_CODE (tem) == NOTE
&& (NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG
/* ??? Optional. Disables some optimizations, but makes
gcov output more accurate with -O. */
|| (flag_test_coverage && NOTE_LINE_NUMBER (tem) > 0)))
return value;
/* If we have found a cycle, make the insn jump to itself. */
if (JUMP_LABEL (insn) == label)
return label;
tem = next_active_insn (JUMP_LABEL (insn));
if (tem && (GET_CODE (PATTERN (tem)) == ADDR_VEC
|| GET_CODE (PATTERN (tem)) == ADDR_DIFF_VEC))
break;
value = JUMP_LABEL (insn);
}
if (depth == 10)
return label;
return value;
}
/* Assuming that field IDX of X is a vector of label_refs,
replace each of them by the ultimate label reached by it.
Return nonzero if a change is made.
If IGNORE_LOOPS is 0, we do not chain across a NOTE_INSN_LOOP_BEG. */
static int
tension_vector_labels (x, idx)
register rtx x;
register int idx;
{
int changed = 0;
register int i;
for (i = XVECLEN (x, idx) - 1; i >= 0; i--)
{
register rtx olabel = XEXP (XVECEXP (x, idx, i), 0);
register rtx nlabel = follow_jumps (olabel);
if (nlabel && nlabel != olabel)
{
XEXP (XVECEXP (x, idx, i), 0) = nlabel;
++LABEL_NUSES (nlabel);
if (--LABEL_NUSES (olabel) == 0)
delete_insn (olabel);
changed = 1;
}
}
return changed;
}
/* Find all CODE_LABELs referred to in X, and increment their use counts.
If INSN is a JUMP_INSN and there is at least one CODE_LABEL referenced
in INSN, then store one of them in JUMP_LABEL (INSN).
If INSN is an INSN or a CALL_INSN and there is at least one CODE_LABEL
referenced in INSN, add a REG_LABEL note containing that label to INSN.
Also, when there are consecutive labels, canonicalize on the last of them.
Note that two labels separated by a loop-beginning note
must be kept distinct if we have not yet done loop-optimization,
because the gap between them is where loop-optimize
will want to move invariant code to. CROSS_JUMP tells us
that loop-optimization is done with.
Once reload has completed (CROSS_JUMP non-zero), we need not consider
two labels distinct if they are separated by only USE or CLOBBER insns. */
static void
mark_jump_label (x, insn, cross_jump)
register rtx x;
rtx insn;
int cross_jump;
{
register RTX_CODE code = GET_CODE (x);
register int i;
register char *fmt;
switch (code)
{
case PC:
case CC0:
case REG:
case SUBREG:
case CONST_INT:
case SYMBOL_REF:
case CONST_DOUBLE:
case CLOBBER:
case CALL:
return;
case MEM:
/* If this is a constant-pool reference, see if it is a label. */
if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
mark_jump_label (get_pool_constant (XEXP (x, 0)), insn, cross_jump);
break;
case LABEL_REF:
{
rtx label = XEXP (x, 0);
rtx olabel = label;
rtx note;
rtx next;
if (GET_CODE (label) != CODE_LABEL)
abort ();
/* Ignore references to labels of containing functions. */
if (LABEL_REF_NONLOCAL_P (x))
break;
/* If there are other labels following this one,
replace it with the last of the consecutive labels. */
for (next = NEXT_INSN (label); next; next = NEXT_INSN (next))
{
if (GET_CODE (next) == CODE_LABEL)
label = next;
else if (cross_jump && GET_CODE (next) == INSN
&& (GET_CODE (PATTERN (next)) == USE
|| GET_CODE (PATTERN (next)) == CLOBBER))
continue;
else if (GET_CODE (next) != NOTE)
break;
else if (! cross_jump
&& (NOTE_LINE_NUMBER (next) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (next) == NOTE_INSN_FUNCTION_END
/* ??? Optional. Disables some optimizations, but
makes gcov output more accurate with -O. */
|| (flag_test_coverage && NOTE_LINE_NUMBER (next) > 0)))
break;
}
XEXP (x, 0) = label;
if (! insn || ! INSN_DELETED_P (insn))
++LABEL_NUSES (label);
if (insn)
{
if (GET_CODE (insn) == JUMP_INSN)
JUMP_LABEL (insn) = label;
/* If we've changed OLABEL and we had a REG_LABEL note
for it, update it as well. */
else if (label != olabel
&& (note = find_reg_note (insn, REG_LABEL, olabel)) != 0)
XEXP (note, 0) = label;
/* Otherwise, add a REG_LABEL note for LABEL unless there already
is one. */
else if (! find_reg_note (insn, REG_LABEL, label))
{
/* This code used to ignore labels which refered to dispatch
tables to avoid flow.c generating worse code.
However, in the presense of global optimizations like
gcse which call find_basic_blocks without calling
life_analysis, not recording such labels will lead
to compiler aborts because of inconsistencies in the
flow graph. So we go ahead and record the label.
It may also be the case that the optimization argument
is no longer valid because of the more accurate cfg
we build in find_basic_blocks -- it no longer pessimizes
code when it finds a REG_LABEL note. */
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL, label,
REG_NOTES (insn));
}
}
return;
}
/* Do walk the labels in a vector, but not the first operand of an
ADDR_DIFF_VEC. Don't set the JUMP_LABEL of a vector. */
case ADDR_VEC:
case ADDR_DIFF_VEC:
if (! INSN_DELETED_P (insn))
{
int eltnum = code == ADDR_DIFF_VEC ? 1 : 0;
for (i = 0; i < XVECLEN (x, eltnum); i++)
mark_jump_label (XVECEXP (x, eltnum, i), NULL_RTX, cross_jump);
}
return;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
mark_jump_label (XEXP (x, i), insn, cross_jump);
else if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
mark_jump_label (XVECEXP (x, i, j), insn, cross_jump);
}
}
}
/* If all INSN does is set the pc, delete it,
and delete the insn that set the condition codes for it
if that's what the previous thing was. */
void
delete_jump (insn)
rtx insn;
{
register rtx set = single_set (insn);
if (set && GET_CODE (SET_DEST (set)) == PC)
delete_computation (insn);
}
/* Delete INSN and recursively delete insns that compute values used only
by INSN. This uses the REG_DEAD notes computed during flow analysis.
If we are running before flow.c, we need do nothing since flow.c will
delete dead code. We also can't know if the registers being used are
dead or not at this point.
Otherwise, look at all our REG_DEAD notes. If a previous insn does
nothing other than set a register that dies in this insn, we can delete
that insn as well.
On machines with CC0, if CC0 is used in this insn, we may be able to
delete the insn that set it. */
static void
delete_computation (insn)
rtx insn;
{
rtx note, next;
#ifdef HAVE_cc0
if (reg_referenced_p (cc0_rtx, PATTERN (insn)))
{
rtx prev = prev_nonnote_insn (insn);
/* We assume that at this stage
CC's are always set explicitly
and always immediately before the jump that
will use them. So if the previous insn
exists to set the CC's, delete it
(unless it performs auto-increments, etc.). */
if (prev && GET_CODE (prev) == INSN
&& sets_cc0_p (PATTERN (prev)))
{
if (sets_cc0_p (PATTERN (prev)) > 0
&& !FIND_REG_INC_NOTE (prev, NULL_RTX))
delete_computation (prev);
else
/* Otherwise, show that cc0 won't be used. */
REG_NOTES (prev) = gen_rtx_EXPR_LIST (REG_UNUSED,
cc0_rtx, REG_NOTES (prev));
}
}
#endif
#ifdef INSN_SCHEDULING
/* ?!? The schedulers do not keep REG_DEAD notes accurate after
reload has completed. The schedulers need to be fixed. Until
they are, we must not rely on the death notes here. */
if (reload_completed && flag_schedule_insns_after_reload)
{
delete_insn (insn);
return;
}
#endif
for (note = REG_NOTES (insn); note; note = next)
{
rtx our_prev;
next = XEXP (note, 1);
if (REG_NOTE_KIND (note) != REG_DEAD
/* Verify that the REG_NOTE is legitimate. */
|| GET_CODE (XEXP (note, 0)) != REG)
continue;
for (our_prev = prev_nonnote_insn (insn);
our_prev && GET_CODE (our_prev) == INSN;
our_prev = prev_nonnote_insn (our_prev))
{
/* If we reach a SEQUENCE, it is too complex to try to
do anything with it, so give up. */
if (GET_CODE (PATTERN (our_prev)) == SEQUENCE)
break;
if (GET_CODE (PATTERN (our_prev)) == USE
&& GET_CODE (XEXP (PATTERN (our_prev), 0)) == INSN)
/* reorg creates USEs that look like this. We leave them
alone because reorg needs them for its own purposes. */
break;
if (reg_set_p (XEXP (note, 0), PATTERN (our_prev)))
{
if (FIND_REG_INC_NOTE (our_prev, NULL_RTX))
break;
if (GET_CODE (PATTERN (our_prev)) == PARALLEL)
{
/* If we find a SET of something else, we can't
delete the insn. */
int i;
for (i = 0; i < XVECLEN (PATTERN (our_prev), 0); i++)
{
rtx part = XVECEXP (PATTERN (our_prev), 0, i);
if (GET_CODE (part) == SET
&& SET_DEST (part) != XEXP (note, 0))
break;
}
if (i == XVECLEN (PATTERN (our_prev), 0))
delete_computation (our_prev);
}
else if (GET_CODE (PATTERN (our_prev)) == SET
&& SET_DEST (PATTERN (our_prev)) == XEXP (note, 0))
delete_computation (our_prev);
break;
}
/* If OUR_PREV references the register that dies here, it is an
additional use. Hence any prior SET isn't dead. However, this
insn becomes the new place for the REG_DEAD note. */
if (reg_overlap_mentioned_p (XEXP (note, 0),
PATTERN (our_prev)))
{
XEXP (note, 1) = REG_NOTES (our_prev);
REG_NOTES (our_prev) = note;
break;
}
}
}
delete_insn (insn);
}
/* Delete insn INSN from the chain of insns and update label ref counts.
May delete some following insns as a consequence; may even delete
a label elsewhere and insns that follow it.
Returns the first insn after INSN that was not deleted. */
rtx
delete_insn (insn)
register rtx insn;
{
register rtx next = NEXT_INSN (insn);
register rtx prev = PREV_INSN (insn);
register int was_code_label = (GET_CODE (insn) == CODE_LABEL);
register int dont_really_delete = 0;
while (next && INSN_DELETED_P (next))
next = NEXT_INSN (next);
/* This insn is already deleted => return first following nondeleted. */
if (INSN_DELETED_P (insn))
return next;
if (was_code_label)
remove_node_from_expr_list (insn, &nonlocal_goto_handler_labels);
/* Don't delete user-declared labels. Convert them to special NOTEs
instead. */
if (was_code_label && LABEL_NAME (insn) != 0
&& optimize && ! dont_really_delete)
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED_LABEL;
NOTE_SOURCE_FILE (insn) = 0;
dont_really_delete = 1;
}
else
/* Mark this insn as deleted. */
INSN_DELETED_P (insn) = 1;
/* If this is an unconditional jump, delete it from the jump chain. */
if (simplejump_p (insn))
delete_from_jump_chain (insn);
/* If instruction is followed by a barrier,
delete the barrier too. */
if (next != 0 && GET_CODE (next) == BARRIER)
{
INSN_DELETED_P (next) = 1;
next = NEXT_INSN (next);
}
/* Patch out INSN (and the barrier if any) */
if (optimize && ! dont_really_delete)
{
if (prev)
{
NEXT_INSN (prev) = next;
if (GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SEQUENCE)
NEXT_INSN (XVECEXP (PATTERN (prev), 0,
XVECLEN (PATTERN (prev), 0) - 1)) = next;
}
if (next)
{
PREV_INSN (next) = prev;
if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == SEQUENCE)
PREV_INSN (XVECEXP (PATTERN (next), 0, 0)) = prev;
}
if (prev && NEXT_INSN (prev) == 0)
set_last_insn (prev);
}
/* If deleting a jump, decrement the count of the label,
and delete the label if it is now unused. */
if (GET_CODE (insn) == JUMP_INSN && JUMP_LABEL (insn))
{
rtx lab = JUMP_LABEL (insn), lab_next;
if (--LABEL_NUSES (lab) == 0)
{
/* This can delete NEXT or PREV,
either directly if NEXT is JUMP_LABEL (INSN),
or indirectly through more levels of jumps. */
delete_insn (lab);
/* I feel a little doubtful about this loop,
but I see no clean and sure alternative way
to find the first insn after INSN that is not now deleted.
I hope this works. */
while (next && INSN_DELETED_P (next))
next = NEXT_INSN (next);
return next;
}
else if ((lab_next = next_nonnote_insn (lab)) != NULL
&& GET_CODE (lab_next) == JUMP_INSN
&& (GET_CODE (PATTERN (lab_next)) == ADDR_VEC
|| GET_CODE (PATTERN (lab_next)) == ADDR_DIFF_VEC))
{
/* If we're deleting the tablejump, delete the dispatch table.
We may not be able to kill the label immediately preceeding
just yet, as it might be referenced in code leading up to
the tablejump. */
delete_insn (lab_next);
}
}
/* Likewise if we're deleting a dispatch table. */
if (GET_CODE (insn) == JUMP_INSN
&& (GET_CODE (PATTERN (insn)) == ADDR_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
{
rtx pat = PATTERN (insn);
int i, diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC;
int len = XVECLEN (pat, diff_vec_p);
for (i = 0; i < len; i++)
if (--LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0)) == 0)
delete_insn (XEXP (XVECEXP (pat, diff_vec_p, i), 0));
while (next && INSN_DELETED_P (next))
next = NEXT_INSN (next);
return next;
}
while (prev && (INSN_DELETED_P (prev) || GET_CODE (prev) == NOTE))
prev = PREV_INSN (prev);
/* If INSN was a label and a dispatch table follows it,
delete the dispatch table. The tablejump must have gone already.
It isn't useful to fall through into a table. */
if (was_code_label
&& NEXT_INSN (insn) != 0
&& GET_CODE (NEXT_INSN (insn)) == JUMP_INSN
&& (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC
|| GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC))
next = delete_insn (NEXT_INSN (insn));
/* If INSN was a label, delete insns following it if now unreachable. */
if (was_code_label && prev && GET_CODE (prev) == BARRIER)
{
register RTX_CODE code;
while (next != 0
&& (GET_RTX_CLASS (code = GET_CODE (next)) == 'i'
|| code == NOTE || code == BARRIER
|| (code == CODE_LABEL && INSN_DELETED_P (next))))
{
if (code == NOTE
&& NOTE_LINE_NUMBER (next) != NOTE_INSN_FUNCTION_END)
next = NEXT_INSN (next);
/* Keep going past other deleted labels to delete what follows. */
else if (code == CODE_LABEL && INSN_DELETED_P (next))
next = NEXT_INSN (next);
else
/* Note: if this deletes a jump, it can cause more
deletion of unreachable code, after a different label.
As long as the value from this recursive call is correct,
this invocation functions correctly. */
next = delete_insn (next);
}
}
return next;
}
/* Advance from INSN till reaching something not deleted
then return that. May return INSN itself. */
rtx
next_nondeleted_insn (insn)
rtx insn;
{
while (INSN_DELETED_P (insn))
insn = NEXT_INSN (insn);
return insn;
}
/* Delete a range of insns from FROM to TO, inclusive.
This is for the sake of peephole optimization, so assume
that whatever these insns do will still be done by a new
peephole insn that will replace them. */
void
delete_for_peephole (from, to)
register rtx from, to;
{
register rtx insn = from;
while (1)
{
register rtx next = NEXT_INSN (insn);
register rtx prev = PREV_INSN (insn);
if (GET_CODE (insn) != NOTE)
{
INSN_DELETED_P (insn) = 1;
/* Patch this insn out of the chain. */
/* We don't do this all at once, because we
must preserve all NOTEs. */
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
}
if (insn == to)
break;
insn = next;
}
/* Note that if TO is an unconditional jump
we *do not* delete the BARRIER that follows,
since the peephole that replaces this sequence
is also an unconditional jump in that case. */
}
/* Invert the condition of the jump JUMP, and make it jump
to label NLABEL instead of where it jumps now. */
int
invert_jump (jump, nlabel)
rtx jump, nlabel;
{
/* We have to either invert the condition and change the label or
do neither. Either operation could fail. We first try to invert
the jump. If that succeeds, we try changing the label. If that fails,
we invert the jump back to what it was. */
if (! invert_exp (PATTERN (jump), jump))
return 0;
if (redirect_jump (jump, nlabel))
{
if (flag_branch_probabilities)
{
rtx note = find_reg_note (jump, REG_BR_PROB, 0);
/* An inverted jump means that a probability taken becomes a
probability not taken. Subtract the branch probability from the
probability base to convert it back to a taken probability.
(We don't flip the probability on a branch that's never taken. */
if (note && XINT (XEXP (note, 0), 0) >= 0)
XINT (XEXP (note, 0), 0) = REG_BR_PROB_BASE - XINT (XEXP (note, 0), 0);
}
return 1;
}
if (! invert_exp (PATTERN (jump), jump))
/* This should just be putting it back the way it was. */
abort ();
return 0;
}
/* Invert the jump condition of rtx X contained in jump insn, INSN.
Return 1 if we can do so, 0 if we cannot find a way to do so that
matches a pattern. */
int
invert_exp (x, insn)
rtx x;
rtx insn;
{
register RTX_CODE code;
register int i;
register char *fmt;
code = GET_CODE (x);
if (code == IF_THEN_ELSE)
{
register rtx comp = XEXP (x, 0);
register rtx tem;
/* We can do this in two ways: The preferable way, which can only
be done if this is not an integer comparison, is to reverse
the comparison code. Otherwise, swap the THEN-part and ELSE-part
of the IF_THEN_ELSE. If we can't do either, fail. */
if (can_reverse_comparison_p (comp, insn)
&& validate_change (insn, &XEXP (x, 0),
gen_rtx_fmt_ee (reverse_condition (GET_CODE (comp)),
GET_MODE (comp), XEXP (comp, 0),
XEXP (comp, 1)), 0))
return 1;
tem = XEXP (x, 1);
validate_change (insn, &XEXP (x, 1), XEXP (x, 2), 1);
validate_change (insn, &XEXP (x, 2), tem, 1);
return apply_change_group ();
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
if (! invert_exp (XEXP (x, i), insn))
return 0;
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
if (!invert_exp (XVECEXP (x, i, j), insn))
return 0;
}
}
return 1;
}
/* Make jump JUMP jump to label NLABEL instead of where it jumps now.
If the old jump target label is unused as a result,
it and the code following it may be deleted.
If NLABEL is zero, we are to turn the jump into a (possibly conditional)
RETURN insn.
The return value will be 1 if the change was made, 0 if it wasn't (this
can only occur for NLABEL == 0). */
int
redirect_jump (jump, nlabel)
rtx jump, nlabel;
{
register rtx olabel = JUMP_LABEL (jump);
if (nlabel == olabel)
return 1;
if (! redirect_exp (&PATTERN (jump), olabel, nlabel, jump))
return 0;
/* If this is an unconditional branch, delete it from the jump_chain of
OLABEL and add it to the jump_chain of NLABEL (assuming both labels
have UID's in range and JUMP_CHAIN is valid). */
if (jump_chain && (simplejump_p (jump)
|| GET_CODE (PATTERN (jump)) == RETURN))
{
int label_index = nlabel ? INSN_UID (nlabel) : 0;
delete_from_jump_chain (jump);
if (label_index < max_jump_chain
&& INSN_UID (jump) < max_jump_chain)
{
jump_chain[INSN_UID (jump)] = jump_chain[label_index];
jump_chain[label_index] = jump;
}
}
JUMP_LABEL (jump) = nlabel;
if (nlabel)
++LABEL_NUSES (nlabel);
if (olabel && --LABEL_NUSES (olabel) == 0)
delete_insn (olabel);
return 1;
}
/* Delete the instruction JUMP from any jump chain it might be on. */
static void
delete_from_jump_chain (jump)
rtx jump;
{
int index;
rtx olabel = JUMP_LABEL (jump);
/* Handle unconditional jumps. */
if (jump_chain && olabel != 0
&& INSN_UID (olabel) < max_jump_chain
&& simplejump_p (jump))
index = INSN_UID (olabel);
/* Handle return insns. */
else if (jump_chain && GET_CODE (PATTERN (jump)) == RETURN)
index = 0;
else return;
if (jump_chain[index] == jump)
jump_chain[index] = jump_chain[INSN_UID (jump)];
else
{
rtx insn;
for (insn = jump_chain[index];
insn != 0;
insn = jump_chain[INSN_UID (insn)])
if (jump_chain[INSN_UID (insn)] == jump)
{
jump_chain[INSN_UID (insn)] = jump_chain[INSN_UID (jump)];
break;
}
}
}
/* If NLABEL is nonzero, throughout the rtx at LOC,
alter (LABEL_REF OLABEL) to (LABEL_REF NLABEL). If OLABEL is
zero, alter (RETURN) to (LABEL_REF NLABEL).
If NLABEL is zero, alter (LABEL_REF OLABEL) to (RETURN) and check
validity with validate_change. Convert (set (pc) (label_ref olabel))
to (return).
Return 0 if we found a change we would like to make but it is invalid.
Otherwise, return 1. */
int
redirect_exp (loc, olabel, nlabel, insn)
rtx *loc;
rtx olabel, nlabel;
rtx insn;
{
register rtx x = *loc;
register RTX_CODE code = GET_CODE (x);
register int i;
register char *fmt;
if (code == LABEL_REF)
{
if (XEXP (x, 0) == olabel)
{
if (nlabel)
XEXP (x, 0) = nlabel;
else
return validate_change (insn, loc, gen_rtx_RETURN (VOIDmode), 0);
return 1;
}
}
else if (code == RETURN && olabel == 0)
{
x = gen_rtx_LABEL_REF (VOIDmode, nlabel);
if (loc == &PATTERN (insn))
x = gen_rtx_SET (VOIDmode, pc_rtx, x);
return validate_change (insn, loc, x, 0);
}
if (code == SET && nlabel == 0 && SET_DEST (x) == pc_rtx
&& GET_CODE (SET_SRC (x)) == LABEL_REF
&& XEXP (SET_SRC (x), 0) == olabel)
return validate_change (insn, loc, gen_rtx_RETURN (VOIDmode), 0);
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
if (! redirect_exp (&XEXP (x, i), olabel, nlabel, insn))
return 0;
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
if (! redirect_exp (&XVECEXP (x, i, j), olabel, nlabel, insn))
return 0;
}
}
return 1;
}
/* Make jump JUMP jump to label NLABEL, assuming it used to be a tablejump.
If the old jump target label (before the dispatch table) becomes unused,
it and the dispatch table may be deleted. In that case, find the insn
before the jump references that label and delete it and logical successors
too. */
static void
redirect_tablejump (jump, nlabel)
rtx jump, nlabel;
{
register rtx olabel = JUMP_LABEL (jump);
/* Add this jump to the jump_chain of NLABEL. */
if (jump_chain && INSN_UID (nlabel) < max_jump_chain
&& INSN_UID (jump) < max_jump_chain)
{
jump_chain[INSN_UID (jump)] = jump_chain[INSN_UID (nlabel)];
jump_chain[INSN_UID (nlabel)] = jump;
}
PATTERN (jump) = gen_jump (nlabel);
JUMP_LABEL (jump) = nlabel;
++LABEL_NUSES (nlabel);
INSN_CODE (jump) = -1;
if (--LABEL_NUSES (olabel) == 0)
{
delete_labelref_insn (jump, olabel, 0);
delete_insn (olabel);
}
}
/* Find the insn referencing LABEL that is a logical predecessor of INSN.
If we found one, delete it and then delete this insn if DELETE_THIS is
non-zero. Return non-zero if INSN or a predecessor references LABEL. */
static int
delete_labelref_insn (insn, label, delete_this)
rtx insn, label;
int delete_this;
{
int deleted = 0;
rtx link;
if (GET_CODE (insn) != NOTE
&& reg_mentioned_p (label, PATTERN (insn)))
{
if (delete_this)
{
delete_insn (insn);
deleted = 1;
}
else
return 1;
}
for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
if (delete_labelref_insn (XEXP (link, 0), label, 1))
{
if (delete_this)
{
delete_insn (insn);
deleted = 1;
}
else
return 1;
}
return deleted;
}
/* Like rtx_equal_p except that it considers two REGs as equal
if they renumber to the same value and considers two commutative
operations to be the same if the order of the operands has been
reversed.
??? Addition is not commutative on the PA due to the weird implicit
space register selection rules for memory addresses. Therefore, we
don't consider a + b == b + a.
We could/should make this test a little tighter. Possibly only
disabling it on the PA via some backend macro or only disabling this
case when the PLUS is inside a MEM. */
int
rtx_renumbered_equal_p (x, y)
rtx x, y;
{
register int i;
register RTX_CODE code = GET_CODE (x);
register char *fmt;
if (x == y)
return 1;
if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
&& (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG
&& GET_CODE (SUBREG_REG (y)) == REG)))
{
int reg_x = -1, reg_y = -1;
int word_x = 0, word_y = 0;
if (GET_MODE (x) != GET_MODE (y))
return 0;
/* If we haven't done any renumbering, don't
make any assumptions. */
if (reg_renumber == 0)
return rtx_equal_p (x, y);
if (code == SUBREG)
{
reg_x = REGNO (SUBREG_REG (x));
word_x = SUBREG_WORD (x);
if (reg_renumber[reg_x] >= 0)
{
reg_x = reg_renumber[reg_x] + word_x;
word_x = 0;
}
}
else
{
reg_x = REGNO (x);
if (reg_renumber[reg_x] >= 0)
reg_x = reg_renumber[reg_x];
}
if (GET_CODE (y) == SUBREG)
{
reg_y = REGNO (SUBREG_REG (y));
word_y = SUBREG_WORD (y);
if (reg_renumber[reg_y] >= 0)
{
reg_y = reg_renumber[reg_y];
word_y = 0;
}
}
else
{
reg_y = REGNO (y);
if (reg_renumber[reg_y] >= 0)
reg_y = reg_renumber[reg_y];
}
return reg_x >= 0 && reg_x == reg_y && word_x == word_y;
}
/* Now we have disposed of all the cases
in which different rtx codes can match. */
if (code != GET_CODE (y))
return 0;
switch (code)
{
case PC:
case CC0:
case ADDR_VEC:
case ADDR_DIFF_VEC:
return 0;
case CONST_INT:
return INTVAL (x) == INTVAL (y);
case LABEL_REF:
/* We can't assume nonlocal labels have their following insns yet. */
if (LABEL_REF_NONLOCAL_P (x) || LABEL_REF_NONLOCAL_P (y))
return XEXP (x, 0) == XEXP (y, 0);
/* Two label-refs are equivalent if they point at labels
in the same position in the instruction stream. */
return (next_real_insn (XEXP (x, 0))
== next_real_insn (XEXP (y, 0)));
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
case CODE_LABEL:
/* If we didn't match EQ equality above, they aren't the same. */
return 0;
default:
break;
}
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
if (GET_MODE (x) != GET_MODE (y))
return 0;
/* For commutative operations, the RTX match if the operand match in any
order. Also handle the simple binary and unary cases without a loop.
??? Don't consider PLUS a commutative operator; see comments above. */
if ((code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
&& code != PLUS)
return ((rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0))
&& rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 1)))
|| (rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 1))
&& rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 0))));
else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == '2')
return (rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0))
&& rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 1)));
else if (GET_RTX_CLASS (code) == '1')
return rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0));
/* Compare the elements. If any pair of corresponding elements
fail to match, return 0 for the whole things. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
register int j;
switch (fmt[i])
{
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'e':
if (! rtx_renumbered_equal_p (XEXP (x, i), XEXP (y, i)))
return 0;
break;
case 'u':
if (XEXP (x, i) != XEXP (y, i))
return 0;
/* fall through. */
case '0':
break;
case 'E':
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (!rtx_renumbered_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)))
return 0;
break;
default:
abort ();
}
}
return 1;
}
/* If X is a hard register or equivalent to one or a subregister of one,
return the hard register number. If X is a pseudo register that was not
assigned a hard register, return the pseudo register number. Otherwise,
return -1. Any rtx is valid for X. */
int
true_regnum (x)
rtx x;
{
if (GET_CODE (x) == REG)
{
if (REGNO (x) >= FIRST_PSEUDO_REGISTER && reg_renumber[REGNO (x)] >= 0)
return reg_renumber[REGNO (x)];
return REGNO (x);
}
if (GET_CODE (x) == SUBREG)
{
int base = true_regnum (SUBREG_REG (x));
if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
return SUBREG_WORD (x) + base;
}
return -1;
}
/* Optimize code of the form:
for (x = a[i]; x; ...)
...
for (x = a[i]; x; ...)
...
foo:
Loop optimize will change the above code into
if (x = a[i])
for (;;)
{ ...; if (! (x = ...)) break; }
if (x = a[i])
for (;;)
{ ...; if (! (x = ...)) break; }
foo:
In general, if the first test fails, the program can branch
directly to `foo' and skip the second try which is doomed to fail.
We run this after loop optimization and before flow analysis. */
/* When comparing the insn patterns, we track the fact that different
pseudo-register numbers may have been used in each computation.
The following array stores an equivalence -- same_regs[I] == J means
that pseudo register I was used in the first set of tests in a context
where J was used in the second set. We also count the number of such
pending equivalences. If nonzero, the expressions really aren't the
same. */
static int *same_regs;
static int num_same_regs;
/* Track any registers modified between the target of the first jump and
the second jump. They never compare equal. */
static char *modified_regs;
/* Record if memory was modified. */
static int modified_mem;
/* Called via note_stores on each insn between the target of the first
branch and the second branch. It marks any changed registers. */
static void
mark_modified_reg (dest, x)
rtx dest;
rtx x ATTRIBUTE_UNUSED;
{
int regno, i;
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == MEM)
modified_mem = 1;
if (GET_CODE (dest) != REG)
return;
regno = REGNO (dest);
if (regno >= FIRST_PSEUDO_REGISTER)
modified_regs[regno] = 1;
else
for (i = 0; i < HARD_REGNO_NREGS (regno, GET_MODE (dest)); i++)
modified_regs[regno + i] = 1;
}
/* F is the first insn in the chain of insns. */
void
thread_jumps (f, max_reg, flag_before_loop)
rtx f;
int max_reg;
int flag_before_loop;
{
/* Basic algorithm is to find a conditional branch,
the label it may branch to, and the branch after
that label. If the two branches test the same condition,
walk back from both branch paths until the insn patterns
differ, or code labels are hit. If we make it back to
the target of the first branch, then we know that the first branch
will either always succeed or always fail depending on the relative
senses of the two branches. So adjust the first branch accordingly
in this case. */
rtx label, b1, b2, t1, t2;
enum rtx_code code1, code2;
rtx b1op0, b1op1, b2op0, b2op1;
int changed = 1;
int i;
int *all_reset;
/* Allocate register tables and quick-reset table. */
modified_regs = (char *) alloca (max_reg * sizeof (char));
same_regs = (int *) alloca (max_reg * sizeof (int));
all_reset = (int *) alloca (max_reg * sizeof (int));
for (i = 0; i < max_reg; i++)
all_reset[i] = -1;
while (changed)
{
changed = 0;
for (b1 = f; b1; b1 = NEXT_INSN (b1))
{
/* Get to a candidate branch insn. */
if (GET_CODE (b1) != JUMP_INSN
|| ! condjump_p (b1) || simplejump_p (b1)
|| JUMP_LABEL (b1) == 0)
continue;
bzero (modified_regs, max_reg * sizeof (char));
modified_mem = 0;
bcopy ((char *) all_reset, (char *) same_regs,
max_reg * sizeof (int));
num_same_regs = 0;
label = JUMP_LABEL (b1);
/* Look for a branch after the target. Record any registers and
memory modified between the target and the branch. Stop when we
get to a label since we can't know what was changed there. */
for (b2 = NEXT_INSN (label); b2; b2 = NEXT_INSN (b2))
{
if (GET_CODE (b2) == CODE_LABEL)
break;
else if (GET_CODE (b2) == JUMP_INSN)
{
/* If this is an unconditional jump and is the only use of
its target label, we can follow it. */
if (simplejump_p (b2)
&& JUMP_LABEL (b2) != 0
&& LABEL_NUSES (JUMP_LABEL (b2)) == 1)
{
b2 = JUMP_LABEL (b2);
continue;
}
else
break;
}
if (GET_CODE (b2) != CALL_INSN && GET_CODE (b2) != INSN)
continue;
if (GET_CODE (b2) == CALL_INSN)
{
modified_mem = 1;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i] && ! fixed_regs[i]
&& i != STACK_POINTER_REGNUM
&& i != FRAME_POINTER_REGNUM
&& i != HARD_FRAME_POINTER_REGNUM
&& i != ARG_POINTER_REGNUM)
modified_regs[i] = 1;
}
note_stores (PATTERN (b2), mark_modified_reg);
}
/* Check the next candidate branch insn from the label
of the first. */
if (b2 == 0
|| GET_CODE (b2) != JUMP_INSN
|| b2 == b1
|| ! condjump_p (b2)
|| simplejump_p (b2))
continue;
/* Get the comparison codes and operands, reversing the
codes if appropriate. If we don't have comparison codes,
we can't do anything. */
b1op0 = XEXP (XEXP (SET_SRC (PATTERN (b1)), 0), 0);
b1op1 = XEXP (XEXP (SET_SRC (PATTERN (b1)), 0), 1);
code1 = GET_CODE (XEXP (SET_SRC (PATTERN (b1)), 0));
if (XEXP (SET_SRC (PATTERN (b1)), 1) == pc_rtx)
code1 = reverse_condition (code1);
b2op0 = XEXP (XEXP (SET_SRC (PATTERN (b2)), 0), 0);
b2op1 = XEXP (XEXP (SET_SRC (PATTERN (b2)), 0), 1);
code2 = GET_CODE (XEXP (SET_SRC (PATTERN (b2)), 0));
if (XEXP (SET_SRC (PATTERN (b2)), 1) == pc_rtx)
code2 = reverse_condition (code2);
/* If they test the same things and knowing that B1 branches
tells us whether or not B2 branches, check if we
can thread the branch. */
if (rtx_equal_for_thread_p (b1op0, b2op0, b2)
&& rtx_equal_for_thread_p (b1op1, b2op1, b2)
&& (comparison_dominates_p (code1, code2)
|| (comparison_dominates_p (code1, reverse_condition (code2))
&& can_reverse_comparison_p (XEXP (SET_SRC (PATTERN (b1)),
0),
b1))))
{
t1 = prev_nonnote_insn (b1);
t2 = prev_nonnote_insn (b2);
while (t1 != 0 && t2 != 0)
{
if (t2 == label)
{
/* We have reached the target of the first branch.
If there are no pending register equivalents,
we know that this branch will either always
succeed (if the senses of the two branches are
the same) or always fail (if not). */
rtx new_label;
if (num_same_regs != 0)
break;
if (comparison_dominates_p (code1, code2))
new_label = JUMP_LABEL (b2);
else
new_label = get_label_after (b2);
if (JUMP_LABEL (b1) != new_label)
{
rtx prev = PREV_INSN (new_label);
if (flag_before_loop
&& GET_CODE (prev) == NOTE
&& NOTE_LINE_NUMBER (prev) == NOTE_INSN_LOOP_BEG)
{
/* Don't thread to the loop label. If a loop
label is reused, loop optimization will
be disabled for that loop. */
new_label = gen_label_rtx ();
emit_label_after (new_label, PREV_INSN (prev));
}
changed |= redirect_jump (b1, new_label);
}
break;
}
/* If either of these is not a normal insn (it might be
a JUMP_INSN, CALL_INSN, or CODE_LABEL) we fail. (NOTEs
have already been skipped above.) Similarly, fail
if the insns are different. */
if (GET_CODE (t1) != INSN || GET_CODE (t2) != INSN
|| recog_memoized (t1) != recog_memoized (t2)
|| ! rtx_equal_for_thread_p (PATTERN (t1),
PATTERN (t2), t2))
break;
t1 = prev_nonnote_insn (t1);
t2 = prev_nonnote_insn (t2);
}
}
}
}
}
/* This is like RTX_EQUAL_P except that it knows about our handling of
possibly equivalent registers and knows to consider volatile and
modified objects as not equal.
YINSN is the insn containing Y. */
int
rtx_equal_for_thread_p (x, y, yinsn)
rtx x, y;
rtx yinsn;
{
register int i;
register int j;
register enum rtx_code code;
register char *fmt;
code = GET_CODE (x);
/* Rtx's of different codes cannot be equal. */
if (code != GET_CODE (y))
return 0;
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
(REG:SI x) and (REG:HI x) are NOT equivalent. */
if (GET_MODE (x) != GET_MODE (y))
return 0;
/* For floating-point, consider everything unequal. This is a bit
pessimistic, but this pass would only rarely do anything for FP
anyway. */
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& FLOAT_MODE_P (GET_MODE (x)) && ! flag_fast_math)
return 0;
/* For commutative operations, the RTX match if the operand match in any
order. Also handle the simple binary and unary cases without a loop. */
if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
return ((rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn)
&& rtx_equal_for_thread_p (XEXP (x, 1), XEXP (y, 1), yinsn))
|| (rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 1), yinsn)
&& rtx_equal_for_thread_p (XEXP (x, 1), XEXP (y, 0), yinsn)));
else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == '2')
return (rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn)
&& rtx_equal_for_thread_p (XEXP (x, 1), XEXP (y, 1), yinsn));
else if (GET_RTX_CLASS (code) == '1')
return rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn);
/* Handle special-cases first. */
switch (code)
{
case REG:
if (REGNO (x) == REGNO (y) && ! modified_regs[REGNO (x)])
return 1;
/* If neither is user variable or hard register, check for possible
equivalence. */
if (REG_USERVAR_P (x) || REG_USERVAR_P (y)
|| REGNO (x) < FIRST_PSEUDO_REGISTER
|| REGNO (y) < FIRST_PSEUDO_REGISTER)
return 0;
if (same_regs[REGNO (x)] == -1)
{
same_regs[REGNO (x)] = REGNO (y);
num_same_regs++;
/* If this is the first time we are seeing a register on the `Y'
side, see if it is the last use. If not, we can't thread the
jump, so mark it as not equivalent. */
if (REGNO_LAST_UID (REGNO (y)) != INSN_UID (yinsn))
return 0;
return 1;
}
else
return (same_regs[REGNO (x)] == REGNO (y));
break;
case MEM:
/* If memory modified or either volatile, not equivalent.
Else, check address. */
if (modified_mem || MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;
return rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn);
case ASM_INPUT:
if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;
break;
case SET:
/* Cancel a pending `same_regs' if setting equivalenced registers.
Then process source. */
if (GET_CODE (SET_DEST (x)) == REG
&& GET_CODE (SET_DEST (y)) == REG)
{
if (same_regs[REGNO (SET_DEST (x))] == REGNO (SET_DEST (y)))
{
same_regs[REGNO (SET_DEST (x))] = -1;
num_same_regs--;
}
else if (REGNO (SET_DEST (x)) != REGNO (SET_DEST (y)))
return 0;
}
else
if (rtx_equal_for_thread_p (SET_DEST (x), SET_DEST (y), yinsn) == 0)
return 0;
return rtx_equal_for_thread_p (SET_SRC (x), SET_SRC (y), yinsn);
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
default:
break;
}
if (x == y)
return 1;
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case 'n':
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 'V':
case 'E':
/* Two vectors must have the same length. */
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
/* And the corresponding elements must match. */
for (j = 0; j < XVECLEN (x, i); j++)
if (rtx_equal_for_thread_p (XVECEXP (x, i, j),
XVECEXP (y, i, j), yinsn) == 0)
return 0;
break;
case 'e':
if (rtx_equal_for_thread_p (XEXP (x, i), XEXP (y, i), yinsn) == 0)
return 0;
break;
case 'S':
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'u':
/* These are just backpointers, so they don't matter. */
break;
case '0':
break;
/* It is believed that rtx's at this level will never
contain anything but integers and other rtx's,
except for within LABEL_REFs and SYMBOL_REFs. */
default:
abort ();
}
}
return 1;
}
#ifndef HAVE_cc0
/* Return the insn that NEW can be safely inserted in front of starting at
the jump insn INSN. Return 0 if it is not safe to do this jump
optimization. Note that NEW must contain a single set. */
static rtx
find_insert_position (insn, new)
rtx insn;
rtx new;
{
int i;
rtx prev;
/* If NEW does not clobber, it is safe to insert NEW before INSN. */
if (GET_CODE (PATTERN (new)) != PARALLEL)
return insn;
for (i = XVECLEN (PATTERN (new), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (new), 0, i)) == CLOBBER
&& reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (new), 0, i), 0),
insn))
break;
if (i < 0)
return insn;
/* There is a good chance that the previous insn PREV sets the thing
being clobbered (often the CC in a hard reg). If PREV does not
use what NEW sets, we can insert NEW before PREV. */
prev = prev_active_insn (insn);
for (i = XVECLEN (PATTERN (new), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (new), 0, i)) == CLOBBER
&& reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (new), 0, i), 0),
insn)
&& ! modified_in_p (XEXP (XVECEXP (PATTERN (new), 0, i), 0),
prev))
return 0;
return reg_mentioned_p (SET_DEST (single_set (new)), prev) ? 0 : prev;
}
#endif /* !HAVE_cc0 */
Index: head/contrib/gcc/local-alloc.c
===================================================================
--- head/contrib/gcc/local-alloc.c (revision 52750)
+++ head/contrib/gcc/local-alloc.c (revision 52751)
@@ -1,2266 +1,2267 @@
/* Allocate registers within a basic block, for GNU compiler.
Copyright (C) 1987, 88, 91, 93-98, 1999 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. */
/* Allocation of hard register numbers to pseudo registers is done in
two passes. In this pass we consider only regs that are born and
die once within one basic block. We do this one basic block at a
time. Then the next pass allocates the registers that remain.
Two passes are used because this pass uses methods that work only
on linear code, but that do a better job than the general methods
used in global_alloc, and more quickly too.
The assignments made are recorded in the vector reg_renumber
whose space is allocated here. The rtl code itself is not altered.
We assign each instruction in the basic block a number
which is its order from the beginning of the block.
Then we can represent the lifetime of a pseudo register with
a pair of numbers, and check for conflicts easily.
We can record the availability of hard registers with a
HARD_REG_SET for each instruction. The HARD_REG_SET
contains 0 or 1 for each hard reg.
To avoid register shuffling, we tie registers together when one
dies by being copied into another, or dies in an instruction that
does arithmetic to produce another. The tied registers are
allocated as one. Registers with different reg class preferences
can never be tied unless the class preferred by one is a subclass
of the one preferred by the other.
Tying is represented with "quantity numbers".
A non-tied register is given a new quantity number.
Tied registers have the same quantity number.
We have provision to exempt registers, even when they are contained
within the block, that can be tied to others that are not contained in it.
This is so that global_alloc could process them both and tie them then.
But this is currently disabled since tying in global_alloc is not
yet implemented. */
/* Pseudos allocated here can be reallocated by global.c if the hard register
is used as a spill register. Currently we don't allocate such pseudos
here if their preferred class is likely to be used by spills. */
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "flags.h"
#include "basic-block.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "recog.h"
#include "output.h"
#include "toplev.h"
/* Next quantity number available for allocation. */
static int next_qty;
/* In all the following vectors indexed by quantity number. */
/* Element Q is the hard reg number chosen for quantity Q,
or -1 if none was found. */
static short *qty_phys_reg;
/* We maintain two hard register sets that indicate suggested hard registers
for each quantity. The first, qty_phys_copy_sugg, contains hard registers
that are tied to the quantity by a simple copy. The second contains all
hard registers that are tied to the quantity via an arithmetic operation.
The former register set is given priority for allocation. This tends to
eliminate copy insns. */
/* Element Q is a set of hard registers that are suggested for quantity Q by
copy insns. */
static HARD_REG_SET *qty_phys_copy_sugg;
/* Element Q is a set of hard registers that are suggested for quantity Q by
arithmetic insns. */
static HARD_REG_SET *qty_phys_sugg;
/* Element Q is the number of suggested registers in qty_phys_copy_sugg. */
static short *qty_phys_num_copy_sugg;
/* Element Q is the number of suggested registers in qty_phys_sugg. */
static short *qty_phys_num_sugg;
/* Element Q is the number of refs to quantity Q. */
static int *qty_n_refs;
/* Element Q is a reg class contained in (smaller than) the
preferred classes of all the pseudo regs that are tied in quantity Q.
This is the preferred class for allocating that quantity. */
static enum reg_class *qty_min_class;
/* Insn number (counting from head of basic block)
where quantity Q was born. -1 if birth has not been recorded. */
static int *qty_birth;
/* Insn number (counting from head of basic block)
where quantity Q died. Due to the way tying is done,
and the fact that we consider in this pass only regs that die but once,
a quantity can die only once. Each quantity's life span
is a set of consecutive insns. -1 if death has not been recorded. */
static int *qty_death;
/* Number of words needed to hold the data in quantity Q.
This depends on its machine mode. It is used for these purposes:
1. It is used in computing the relative importances of qtys,
which determines the order in which we look for regs for them.
2. It is used in rules that prevent tying several registers of
different sizes in a way that is geometrically impossible
(see combine_regs). */
static int *qty_size;
/* This holds the mode of the registers that are tied to qty Q,
or VOIDmode if registers with differing modes are tied together. */
static enum machine_mode *qty_mode;
/* Number of times a reg tied to qty Q lives across a CALL_INSN. */
static int *qty_n_calls_crossed;
/* Register class within which we allocate qty Q if we can't get
its preferred class. */
static enum reg_class *qty_alternate_class;
/* Element Q is nonzero if this quantity has been used in a SUBREG
that changes its size. */
static char *qty_changes_size;
/* Element Q is the register number of one pseudo register whose
reg_qty value is Q. This register should be the head of the chain
maintained in reg_next_in_qty. */
static int *qty_first_reg;
/* If (REG N) has been assigned a quantity number, is a register number
of another register assigned the same quantity number, or -1 for the
end of the chain. qty_first_reg point to the head of this chain. */
static int *reg_next_in_qty;
/* reg_qty[N] (where N is a pseudo reg number) is the qty number of that reg
if it is >= 0,
of -1 if this register cannot be allocated by local-alloc,
or -2 if not known yet.
Note that if we see a use or death of pseudo register N with
reg_qty[N] == -2, register N must be local to the current block. If
it were used in more than one block, we would have reg_qty[N] == -1.
This relies on the fact that if reg_basic_block[N] is >= 0, register N
will not appear in any other block. We save a considerable number of
tests by exploiting this.
If N is < FIRST_PSEUDO_REGISTER, reg_qty[N] is undefined and should not
be referenced. */
static int *reg_qty;
/* The offset (in words) of register N within its quantity.
This can be nonzero if register N is SImode, and has been tied
to a subreg of a DImode register. */
static char *reg_offset;
/* Vector of substitutions of register numbers,
used to map pseudo regs into hardware regs.
This is set up as a result of register allocation.
Element N is the hard reg assigned to pseudo reg N,
or is -1 if no hard reg was assigned.
If N is a hard reg number, element N is N. */
short *reg_renumber;
/* Set of hard registers live at the current point in the scan
of the instructions in a basic block. */
static HARD_REG_SET regs_live;
/* Each set of hard registers indicates registers live at a particular
point in the basic block. For N even, regs_live_at[N] says which
hard registers are needed *after* insn N/2 (i.e., they may not
conflict with the outputs of insn N/2 or the inputs of insn N/2 + 1.
If an object is to conflict with the inputs of insn J but not the
outputs of insn J + 1, we say it is born at index J*2 - 1. Similarly,
if it is to conflict with the outputs of insn J but not the inputs of
insn J + 1, it is said to die at index J*2 + 1. */
static HARD_REG_SET *regs_live_at;
/* Communicate local vars `insn_number' and `insn'
from `block_alloc' to `reg_is_set', `wipe_dead_reg', and `alloc_qty'. */
static int this_insn_number;
static rtx this_insn;
/* Used to communicate changes made by update_equiv_regs to
memref_referenced_p. reg_equiv_replacement is set for any REG_EQUIV note
found or created, so that we can keep track of what memory accesses might
be created later, e.g. by reload. */
static rtx *reg_equiv_replacement;
/* Used for communication between update_equiv_regs and no_equiv. */
static rtx *reg_equiv_init_insns;
/* Nonzero if we recorded an equivalence for a LABEL_REF. */
static int recorded_label_ref;
static void alloc_qty PROTO((int, enum machine_mode, int, int));
static void validate_equiv_mem_from_store PROTO((rtx, rtx));
static int validate_equiv_mem PROTO((rtx, rtx, rtx));
static int contains_replace_regs PROTO((rtx, char *));
static int memref_referenced_p PROTO((rtx, rtx));
static int memref_used_between_p PROTO((rtx, rtx, rtx));
static void update_equiv_regs PROTO((void));
static void no_equiv PROTO((rtx, rtx));
static void block_alloc PROTO((int));
static int qty_sugg_compare PROTO((int, int));
static int qty_sugg_compare_1 PROTO((const GENERIC_PTR, const GENERIC_PTR));
static int qty_compare PROTO((int, int));
static int qty_compare_1 PROTO((const GENERIC_PTR, const GENERIC_PTR));
static int combine_regs PROTO((rtx, rtx, int, int, rtx, int));
static int reg_meets_class_p PROTO((int, enum reg_class));
static void update_qty_class PROTO((int, int));
static void reg_is_set PROTO((rtx, rtx));
static void reg_is_born PROTO((rtx, int));
static void wipe_dead_reg PROTO((rtx, int));
static int find_free_reg PROTO((enum reg_class, enum machine_mode,
int, int, int, int, int));
static void mark_life PROTO((int, enum machine_mode, int));
static void post_mark_life PROTO((int, enum machine_mode, int, int, int));
static int no_conflict_p PROTO((rtx, rtx, rtx));
static int requires_inout PROTO((const char *));
/* Allocate a new quantity (new within current basic block)
for register number REGNO which is born at index BIRTH
within the block. MODE and SIZE are info on reg REGNO. */
static void
alloc_qty (regno, mode, size, birth)
int regno;
enum machine_mode mode;
int size, birth;
{
register int qty = next_qty++;
reg_qty[regno] = qty;
reg_offset[regno] = 0;
reg_next_in_qty[regno] = -1;
qty_first_reg[qty] = regno;
qty_size[qty] = size;
qty_mode[qty] = mode;
qty_birth[qty] = birth;
qty_n_calls_crossed[qty] = REG_N_CALLS_CROSSED (regno);
qty_min_class[qty] = reg_preferred_class (regno);
qty_alternate_class[qty] = reg_alternate_class (regno);
qty_n_refs[qty] = REG_N_REFS (regno);
qty_changes_size[qty] = REG_CHANGES_SIZE (regno);
}
/* Main entry point of this file. */
int
local_alloc ()
{
register int b, i;
int max_qty;
/* We need to keep track of whether or not we recorded a LABEL_REF so
that we know if the jump optimizer needs to be rerun. */
recorded_label_ref = 0;
/* Leaf functions and non-leaf functions have different needs.
If defined, let the machine say what kind of ordering we
should use. */
#ifdef ORDER_REGS_FOR_LOCAL_ALLOC
ORDER_REGS_FOR_LOCAL_ALLOC;
#endif
/* Promote REG_EQUAL notes to REG_EQUIV notes and adjust status of affected
registers. */
update_equiv_regs ();
/* This sets the maximum number of quantities we can have. Quantity
numbers start at zero and we can have one for each pseudo. */
max_qty = (max_regno - FIRST_PSEUDO_REGISTER);
/* Allocate vectors of temporary data.
See the declarations of these variables, above,
for what they mean. */
qty_phys_reg = (short *) alloca (max_qty * sizeof (short));
qty_phys_copy_sugg
= (HARD_REG_SET *) alloca (max_qty * sizeof (HARD_REG_SET));
qty_phys_num_copy_sugg = (short *) alloca (max_qty * sizeof (short));
qty_phys_sugg = (HARD_REG_SET *) alloca (max_qty * sizeof (HARD_REG_SET));
qty_phys_num_sugg = (short *) alloca (max_qty * sizeof (short));
qty_birth = (int *) alloca (max_qty * sizeof (int));
qty_death = (int *) alloca (max_qty * sizeof (int));
qty_first_reg = (int *) alloca (max_qty * sizeof (int));
qty_size = (int *) alloca (max_qty * sizeof (int));
qty_mode
= (enum machine_mode *) alloca (max_qty * sizeof (enum machine_mode));
qty_n_calls_crossed = (int *) alloca (max_qty * sizeof (int));
qty_min_class
= (enum reg_class *) alloca (max_qty * sizeof (enum reg_class));
qty_alternate_class
= (enum reg_class *) alloca (max_qty * sizeof (enum reg_class));
qty_n_refs = (int *) alloca (max_qty * sizeof (int));
qty_changes_size = (char *) alloca (max_qty * sizeof (char));
reg_qty = (int *) xmalloc (max_regno * sizeof (int));
reg_offset = (char *) xmalloc (max_regno * sizeof (char));
reg_next_in_qty = (int *) xmalloc(max_regno * sizeof (int));
/* Allocate the reg_renumber array */
allocate_reg_info (max_regno, FALSE, TRUE);
/* Determine which pseudo-registers can be allocated by local-alloc.
In general, these are the registers used only in a single block and
which only die once. However, if a register's preferred class has only
a few entries, don't allocate this register here unless it is preferred
or nothing since retry_global_alloc won't be able to move it to
GENERAL_REGS if a reload register of this class is needed.
We need not be concerned with which block actually uses the register
since we will never see it outside that block. */
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
{
if (REG_BASIC_BLOCK (i) >= 0 && REG_N_DEATHS (i) == 1
&& (reg_alternate_class (i) == NO_REGS
|| ! CLASS_LIKELY_SPILLED_P (reg_preferred_class (i))))
reg_qty[i] = -2;
else
reg_qty[i] = -1;
}
/* Force loop below to initialize entire quantity array. */
next_qty = max_qty;
/* Allocate each block's local registers, block by block. */
for (b = 0; b < n_basic_blocks; b++)
{
/* NEXT_QTY indicates which elements of the `qty_...'
vectors might need to be initialized because they were used
for the previous block; it is set to the entire array before
block 0. Initialize those, with explicit loop if there are few,
else with bzero and bcopy. Do not initialize vectors that are
explicit set by `alloc_qty'. */
if (next_qty < 6)
{
for (i = 0; i < next_qty; i++)
{
CLEAR_HARD_REG_SET (qty_phys_copy_sugg[i]);
qty_phys_num_copy_sugg[i] = 0;
CLEAR_HARD_REG_SET (qty_phys_sugg[i]);
qty_phys_num_sugg[i] = 0;
}
}
else
{
#define CLEAR(vector) \
bzero ((char *) (vector), (sizeof (*(vector))) * next_qty);
CLEAR (qty_phys_copy_sugg);
CLEAR (qty_phys_num_copy_sugg);
CLEAR (qty_phys_sugg);
CLEAR (qty_phys_num_sugg);
}
next_qty = 0;
block_alloc (b);
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
free (reg_qty);
free (reg_offset);
free (reg_next_in_qty);
return recorded_label_ref;
}
/* Depth of loops we are in while in update_equiv_regs. */
static int loop_depth;
/* Used for communication between the following two functions: contains
a MEM that we wish to ensure remains unchanged. */
static rtx equiv_mem;
/* Set nonzero if EQUIV_MEM is modified. */
static int equiv_mem_modified;
/* If EQUIV_MEM is modified by modifying DEST, indicate that it is modified.
Called via note_stores. */
static void
validate_equiv_mem_from_store (dest, set)
rtx dest;
rtx set ATTRIBUTE_UNUSED;
{
if ((GET_CODE (dest) == REG
&& reg_overlap_mentioned_p (dest, equiv_mem))
|| (GET_CODE (dest) == MEM
&& true_dependence (dest, VOIDmode, equiv_mem, rtx_varies_p)))
equiv_mem_modified = 1;
}
/* Verify that no store between START and the death of REG invalidates
MEMREF. MEMREF is invalidated by modifying a register used in MEMREF,
by storing into an overlapping memory location, or with a non-const
CALL_INSN.
Return 1 if MEMREF remains valid. */
static int
validate_equiv_mem (start, reg, memref)
rtx start;
rtx reg;
rtx memref;
{
rtx insn;
rtx note;
equiv_mem = memref;
equiv_mem_modified = 0;
/* If the memory reference has side effects or is volatile, it isn't a
valid equivalence. */
if (side_effects_p (memref))
return 0;
for (insn = start; insn && ! equiv_mem_modified; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
if (find_reg_note (insn, REG_DEAD, reg))
return 1;
if (GET_CODE (insn) == CALL_INSN && ! RTX_UNCHANGING_P (memref)
&& ! CONST_CALL_P (insn))
return 0;
note_stores (PATTERN (insn), validate_equiv_mem_from_store);
/* If a register mentioned in MEMREF is modified via an
auto-increment, we lose the equivalence. Do the same if one
dies; although we could extend the life, it doesn't seem worth
the trouble. */
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if ((REG_NOTE_KIND (note) == REG_INC
|| REG_NOTE_KIND (note) == REG_DEAD)
&& GET_CODE (XEXP (note, 0)) == REG
&& reg_overlap_mentioned_p (XEXP (note, 0), memref))
return 0;
}
return 0;
}
/* TRUE if X uses any registers for which reg_equiv_replace is true. */
static int
contains_replace_regs (x, reg_equiv_replace)
rtx x;
char *reg_equiv_replace;
{
int i, j;
char *fmt;
enum rtx_code code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
case PC:
case CC0:
case HIGH:
case LO_SUM:
return 0;
case REG:
return reg_equiv_replace[REGNO (x)];
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
switch (fmt[i])
{
case 'e':
if (contains_replace_regs (XEXP (x, i), reg_equiv_replace))
return 1;
break;
case 'E':
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (contains_replace_regs (XVECEXP (x, i, j), reg_equiv_replace))
return 1;
break;
}
return 0;
}
/* TRUE if X references a memory location that would be affected by a store
to MEMREF. */
static int
memref_referenced_p (memref, x)
rtx x;
rtx memref;
{
int i, j;
char *fmt;
enum rtx_code code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
case PC:
case CC0:
case HIGH:
case LO_SUM:
return 0;
case REG:
return (reg_equiv_replacement[REGNO (x)]
&& memref_referenced_p (memref,
reg_equiv_replacement[REGNO (x)]));
case MEM:
if (true_dependence (memref, VOIDmode, x, rtx_varies_p))
return 1;
break;
case SET:
/* If we are setting a MEM, it doesn't count (its address does), but any
other SET_DEST that has a MEM in it is referencing the MEM. */
if (GET_CODE (SET_DEST (x)) == MEM)
{
if (memref_referenced_p (memref, XEXP (SET_DEST (x), 0)))
return 1;
}
else if (memref_referenced_p (memref, SET_DEST (x)))
return 1;
return memref_referenced_p (memref, SET_SRC (x));
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
switch (fmt[i])
{
case 'e':
if (memref_referenced_p (memref, XEXP (x, i)))
return 1;
break;
case 'E':
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (memref_referenced_p (memref, XVECEXP (x, i, j)))
return 1;
break;
}
return 0;
}
/* TRUE if some insn in the range (START, END] references a memory location
that would be affected by a store to MEMREF. */
static int
memref_used_between_p (memref, start, end)
rtx memref;
rtx start;
rtx end;
{
rtx insn;
for (insn = NEXT_INSN (start); insn != NEXT_INSN (end);
insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& memref_referenced_p (memref, PATTERN (insn)))
return 1;
return 0;
}
/* Return nonzero if the rtx X is invariant over the current function. */
int
function_invariant_p (x)
rtx x;
{
if (CONSTANT_P (x))
return 1;
if (x == frame_pointer_rtx || x == arg_pointer_rtx)
return 1;
if (GET_CODE (x) == PLUS
&& (XEXP (x, 0) == frame_pointer_rtx || XEXP (x, 0) == arg_pointer_rtx)
&& CONSTANT_P (XEXP (x, 1)))
return 1;
return 0;
}
/* Find registers that are equivalent to a single value throughout the
compilation (either because they can be referenced in memory or are set once
from a single constant). Lower their priority for a register.
If such a register is only referenced once, try substituting its value
into the using insn. If it succeeds, we can eliminate the register
completely. */
static void
update_equiv_regs ()
{
/* Set when an attempt should be made to replace a register with the
associated reg_equiv_replacement entry at the end of this function. */
char *reg_equiv_replace
= (char *) alloca (max_regno * sizeof *reg_equiv_replace);
rtx insn;
int block, depth;
reg_equiv_init_insns = (rtx *) alloca (max_regno * sizeof (rtx));
reg_equiv_replacement = (rtx *) alloca (max_regno * sizeof (rtx));
bzero ((char *) reg_equiv_init_insns, max_regno * sizeof (rtx));
bzero ((char *) reg_equiv_replacement, max_regno * sizeof (rtx));
bzero ((char *) reg_equiv_replace, max_regno * sizeof *reg_equiv_replace);
init_alias_analysis ();
loop_depth = 1;
/* Scan the insns and find which registers have equivalences. Do this
in a separate scan of the insns because (due to -fcse-follow-jumps)
a register can be set below its use. */
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
rtx note;
rtx set;
rtx dest, src;
int regno;
if (GET_CODE (insn) == NOTE)
{
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
loop_depth++;
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
loop_depth--;
}
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_INC)
no_equiv (XEXP (note, 0), note);
set = single_set (insn);
/* If this insn contains more (or less) than a single SET,
only mark all destinations as having no known equivalence. */
if (set == 0)
{
note_stores (PATTERN (insn), no_equiv);
continue;
}
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int i;
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
{
rtx part = XVECEXP (PATTERN (insn), 0, i);
if (part != set)
note_stores (part, no_equiv);
}
}
dest = SET_DEST (set);
src = SET_SRC (set);
/* If this sets a MEM to the contents of a REG that is only used
in a single basic block, see if the register is always equivalent
to that memory location and if moving the store from INSN to the
insn that set REG is safe. If so, put a REG_EQUIV note on the
initializing insn.
Don't add a REG_EQUIV note if the insn already has one. The existing
REG_EQUIV is likely more useful than the one we are adding.
If one of the regs in the address is marked as reg_equiv_replace,
then we can't add this REG_EQUIV note. The reg_equiv_replace
optimization may move the set of this register immediately before
insn, which puts it after reg_equiv_init_insns[regno], and hence
the mention in the REG_EQUIV note would be to an uninitialized
pseudo. */
/* ????? This test isn't good enough; we might see a MEM with a use of
a pseudo register before we see its setting insn that will cause
reg_equiv_replace for that pseudo to be set.
Equivalences to MEMs should be made in another pass, after the
reg_equiv_replace information has been gathered. */
if (GET_CODE (dest) == MEM && GET_CODE (src) == REG
&& (regno = REGNO (src)) >= FIRST_PSEUDO_REGISTER
&& REG_BASIC_BLOCK (regno) >= 0
&& REG_N_SETS (regno) == 1
&& reg_equiv_init_insns[regno] != 0
&& reg_equiv_init_insns[regno] != const0_rtx
- && ! find_reg_note (insn, REG_EQUIV, NULL_RTX)
+ && ! find_reg_note (XEXP (reg_equiv_init_insns[regno], 0),
+ REG_EQUIV, NULL_RTX)
&& ! contains_replace_regs (XEXP (dest, 0), reg_equiv_replace))
{
rtx init_insn = XEXP (reg_equiv_init_insns[regno], 0);
if (validate_equiv_mem (init_insn, src, dest)
&& ! memref_used_between_p (dest, init_insn, insn))
REG_NOTES (init_insn)
= gen_rtx_EXPR_LIST (REG_EQUIV, dest, REG_NOTES (init_insn));
}
/* We only handle the case of a pseudo register being set
once, or always to the same value. */
/* ??? The mn10200 port breaks if we add equivalences for
values that need an ADDRESS_REGS register and set them equivalent
to a MEM of a pseudo. The actual problem is in the over-conservative
handling of INPADDR_ADDRESS / INPUT_ADDRESS / INPUT triples in
calculate_needs, but we traditionally work around this problem
here by rejecting equivalences when the destination is in a register
that's likely spilled. This is fragile, of course, since the
preferred class of a pseudo depends on all instructions that set
or use it. */
if (GET_CODE (dest) != REG
|| (regno = REGNO (dest)) < FIRST_PSEUDO_REGISTER
|| reg_equiv_init_insns[regno] == const0_rtx
|| (CLASS_LIKELY_SPILLED_P (reg_preferred_class (regno))
&& GET_CODE (src) == MEM))
{
/* This might be seting a SUBREG of a pseudo, a pseudo that is
also set somewhere else to a constant. */
note_stores (set, no_equiv);
continue;
}
/* Don't handle the equivalence if the source is in a register
class that's likely to be spilled. */
if (GET_CODE (src) == REG
&& REGNO (src) >= FIRST_PSEUDO_REGISTER
&& CLASS_LIKELY_SPILLED_P (reg_preferred_class (REGNO (src))))
{
no_equiv (dest, set);
continue;
}
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
if (REG_N_SETS (regno) != 1
&& (! note
|| ! function_invariant_p (XEXP (note, 0))
|| (reg_equiv_replacement[regno]
&& ! rtx_equal_p (XEXP (note, 0),
reg_equiv_replacement[regno]))))
{
no_equiv (dest, set);
continue;
}
/* Record this insn as initializing this register. */
reg_equiv_init_insns[regno]
= gen_rtx_INSN_LIST (VOIDmode, insn, reg_equiv_init_insns[regno]);
/* If this register is known to be equal to a constant, record that
it is always equivalent to the constant. */
if (note && function_invariant_p (XEXP (note, 0)))
PUT_MODE (note, (enum machine_mode) REG_EQUIV);
/* If this insn introduces a "constant" register, decrease the priority
of that register. Record this insn if the register is only used once
more and the equivalence value is the same as our source.
The latter condition is checked for two reasons: First, it is an
indication that it may be more efficient to actually emit the insn
as written (if no registers are available, reload will substitute
the equivalence). Secondly, it avoids problems with any registers
dying in this insn whose death notes would be missed.
If we don't have a REG_EQUIV note, see if this insn is loading
a register used only in one basic block from a MEM. If so, and the
MEM remains unchanged for the life of the register, add a REG_EQUIV
note. */
note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
if (note == 0 && REG_BASIC_BLOCK (regno) >= 0
&& GET_CODE (SET_SRC (set)) == MEM
&& validate_equiv_mem (insn, dest, SET_SRC (set)))
REG_NOTES (insn) = note = gen_rtx_EXPR_LIST (REG_EQUIV, SET_SRC (set),
REG_NOTES (insn));
if (note)
{
int regno = REGNO (dest);
/* Record whether or not we created a REG_EQUIV note for a LABEL_REF.
We might end up substituting the LABEL_REF for uses of the
pseudo here or later. That kind of transformation may turn an
indirect jump into a direct jump, in which case we must rerun the
jump optimizer to ensure that the JUMP_LABEL fields are valid. */
if (GET_CODE (XEXP (note, 0)) == LABEL_REF
|| (GET_CODE (XEXP (note, 0)) == CONST
&& GET_CODE (XEXP (XEXP (note, 0), 0)) == PLUS
&& (GET_CODE (XEXP (XEXP (XEXP (note, 0), 0), 0))
== LABEL_REF)))
recorded_label_ref = 1;
reg_equiv_replacement[regno] = XEXP (note, 0);
/* Don't mess with things live during setjmp. */
if (REG_LIVE_LENGTH (regno) >= 0)
{
/* Note that the statement below does not affect the priority
in local-alloc! */
REG_LIVE_LENGTH (regno) *= 2;
/* If the register is referenced exactly twice, meaning it is
set once and used once, indicate that the reference may be
replaced by the equivalence we computed above. If the
register is only used in one basic block, this can't succeed
or combine would have done it.
It would be nice to use "loop_depth * 2" in the compare
below. Unfortunately, LOOP_DEPTH need not be constant within
a basic block so this would be too complicated.
This case normally occurs when a parameter is read from
memory and then used exactly once, not in a loop. */
if (REG_N_REFS (regno) == 2
&& REG_BASIC_BLOCK (regno) < 0
&& rtx_equal_p (XEXP (note, 0), SET_SRC (set)))
reg_equiv_replace[regno] = 1;
}
}
}
/* Now scan all regs killed in an insn to see if any of them are
registers only used that once. If so, see if we can replace the
reference with the equivalent from. If we can, delete the
initializing reference and this register will go away. If we
can't replace the reference, and the instruction is not in a
loop, then move the register initialization just before the use,
so that they are in the same basic block. */
block = -1;
depth = 0;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
rtx link;
/* Keep track of which basic block we are in. */
if (block + 1 < n_basic_blocks
&& BLOCK_HEAD (block + 1) == insn)
++block;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
{
if (GET_CODE (insn) == NOTE)
{
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
++depth;
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
{
--depth;
if (depth < 0)
abort ();
}
}
continue;
}
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
{
if (REG_NOTE_KIND (link) == REG_DEAD
/* Make sure this insn still refers to the register. */
&& reg_mentioned_p (XEXP (link, 0), PATTERN (insn)))
{
int regno = REGNO (XEXP (link, 0));
rtx equiv_insn;
if (! reg_equiv_replace[regno])
continue;
/* reg_equiv_replace[REGNO] gets set only when
REG_N_REFS[REGNO] is 2, i.e. the register is set
once and used once. (If it were only set, but not used,
flow would have deleted the setting insns.) Hence
there can only be one insn in reg_equiv_init_insns. */
equiv_insn = XEXP (reg_equiv_init_insns[regno], 0);
if (validate_replace_rtx (regno_reg_rtx[regno],
reg_equiv_replacement[regno], insn))
{
remove_death (regno, insn);
REG_N_REFS (regno) = 0;
PUT_CODE (equiv_insn, NOTE);
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (equiv_insn) = 0;
}
/* If we aren't in a loop, and there are no calls in
INSN or in the initialization of the register, then
move the initialization of the register to just
before INSN. Update the flow information. */
else if (depth == 0
&& GET_CODE (equiv_insn) == INSN
&& GET_CODE (insn) == INSN
&& REG_BASIC_BLOCK (regno) < 0)
{
int l;
emit_insn_before (copy_rtx (PATTERN (equiv_insn)), insn);
REG_NOTES (PREV_INSN (insn)) = REG_NOTES (equiv_insn);
PUT_CODE (equiv_insn, NOTE);
NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (equiv_insn) = 0;
REG_NOTES (equiv_insn) = 0;
if (block < 0)
REG_BASIC_BLOCK (regno) = 0;
else
REG_BASIC_BLOCK (regno) = block;
REG_N_CALLS_CROSSED (regno) = 0;
REG_LIVE_LENGTH (regno) = 2;
if (block >= 0 && insn == BLOCK_HEAD (block))
BLOCK_HEAD (block) = PREV_INSN (insn);
for (l = 0; l < n_basic_blocks; l++)
CLEAR_REGNO_REG_SET (BASIC_BLOCK (l)->global_live_at_start,
regno);
}
}
}
}
}
/* Mark REG as having no known equivalence.
Some instructions might have been proceessed before and furnished
with REG_EQUIV notes for this register; these notes will have to be
removed.
STORE is the piece of RTL that does the non-constant / conflicting
assignment - a SET, CLOBBER or REG_INC note. It is currently not used,
but needs to be there because this function is called from note_stores. */
static void
no_equiv (reg, store)
rtx reg, store ATTRIBUTE_UNUSED;
{
int regno;
rtx list;
if (GET_CODE (reg) != REG)
return;
regno = REGNO (reg);
list = reg_equiv_init_insns[regno];
if (list == const0_rtx)
return;
for (; list; list = XEXP (list, 1))
{
rtx insn = XEXP (list, 0);
remove_note (insn, find_reg_note (insn, REG_EQUIV, NULL_RTX));
}
reg_equiv_init_insns[regno] = const0_rtx;
reg_equiv_replacement[regno] = NULL_RTX;
}
/* Allocate hard regs to the pseudo regs used only within block number B.
Only the pseudos that die but once can be handled. */
static void
block_alloc (b)
int b;
{
register int i, q;
register rtx insn;
rtx note;
int insn_number = 0;
int insn_count = 0;
int max_uid = get_max_uid ();
int *qty_order;
int no_conflict_combined_regno = -1;
/* Count the instructions in the basic block. */
insn = BLOCK_END (b);
while (1)
{
if (GET_CODE (insn) != NOTE)
if (++insn_count > max_uid)
abort ();
if (insn == BLOCK_HEAD (b))
break;
insn = PREV_INSN (insn);
}
/* +2 to leave room for a post_mark_life at the last insn and for
the birth of a CLOBBER in the first insn. */
regs_live_at = (HARD_REG_SET *) alloca ((2 * insn_count + 2)
* sizeof (HARD_REG_SET));
bzero ((char *) regs_live_at, (2 * insn_count + 2) * sizeof (HARD_REG_SET));
/* Initialize table of hardware registers currently live. */
REG_SET_TO_HARD_REG_SET (regs_live, BASIC_BLOCK (b)->global_live_at_start);
/* This loop scans the instructions of the basic block
and assigns quantities to registers.
It computes which registers to tie. */
insn = BLOCK_HEAD (b);
while (1)
{
register rtx body = PATTERN (insn);
if (GET_CODE (insn) != NOTE)
insn_number++;
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
register rtx link, set;
register int win = 0;
register rtx r0, r1;
int combined_regno = -1;
int i;
this_insn_number = insn_number;
this_insn = insn;
extract_insn (insn);
which_alternative = -1;
/* Is this insn suitable for tying two registers?
If so, try doing that.
Suitable insns are those with at least two operands and where
operand 0 is an output that is a register that is not
earlyclobber.
We can tie operand 0 with some operand that dies in this insn.
First look for operands that are required to be in the same
register as operand 0. If we find such, only try tying that
operand or one that can be put into that operand if the
operation is commutative. If we don't find an operand
that is required to be in the same register as operand 0,
we can tie with any operand.
Subregs in place of regs are also ok.
If tying is done, WIN is set nonzero. */
if (1
#ifdef REGISTER_CONSTRAINTS
&& recog_n_operands > 1
&& recog_constraints[0][0] == '='
&& recog_constraints[0][1] != '&'
#else
&& GET_CODE (PATTERN (insn)) == SET
&& rtx_equal_p (SET_DEST (PATTERN (insn)), recog_operand[0])
#endif
)
{
#ifdef REGISTER_CONSTRAINTS
/* If non-negative, is an operand that must match operand 0. */
int must_match_0 = -1;
/* Counts number of alternatives that require a match with
operand 0. */
int n_matching_alts = 0;
for (i = 1; i < recog_n_operands; i++)
{
const char *p = recog_constraints[i];
int this_match = (requires_inout (p));
n_matching_alts += this_match;
if (this_match == recog_n_alternatives)
must_match_0 = i;
}
#endif
r0 = recog_operand[0];
for (i = 1; i < recog_n_operands; i++)
{
#ifdef REGISTER_CONSTRAINTS
/* Skip this operand if we found an operand that
must match operand 0 and this operand isn't it
and can't be made to be it by commutativity. */
if (must_match_0 >= 0 && i != must_match_0
&& ! (i == must_match_0 + 1
&& recog_constraints[i-1][0] == '%')
&& ! (i == must_match_0 - 1
&& recog_constraints[i][0] == '%'))
continue;
/* Likewise if each alternative has some operand that
must match operand zero. In that case, skip any
operand that doesn't list operand 0 since we know that
the operand always conflicts with operand 0. We
ignore commutatity in this case to keep things simple. */
if (n_matching_alts == recog_n_alternatives
&& 0 == requires_inout (recog_constraints[i]))
continue;
#endif
r1 = recog_operand[i];
/* If the operand is an address, find a register in it.
There may be more than one register, but we only try one
of them. */
if (
#ifdef REGISTER_CONSTRAINTS
recog_constraints[i][0] == 'p'
#else
recog_operand_address_p[i]
#endif
)
while (GET_CODE (r1) == PLUS || GET_CODE (r1) == MULT)
r1 = XEXP (r1, 0);
if (GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG)
{
/* We have two priorities for hard register preferences.
If we have a move insn or an insn whose first input
can only be in the same register as the output, give
priority to an equivalence found from that insn. */
int may_save_copy
= ((SET_DEST (body) == r0 && SET_SRC (body) == r1)
#ifdef REGISTER_CONSTRAINTS
|| (r1 == recog_operand[i] && must_match_0 >= 0)
#endif
);
if (GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)
win = combine_regs (r1, r0, may_save_copy,
insn_number, insn, 0);
}
if (win)
break;
}
}
/* Recognize an insn sequence with an ultimate result
which can safely overlap one of the inputs.
The sequence begins with a CLOBBER of its result,
and ends with an insn that copies the result to itself
and has a REG_EQUAL note for an equivalent formula.
That note indicates what the inputs are.
The result and the input can overlap if each insn in
the sequence either doesn't mention the input
or has a REG_NO_CONFLICT note to inhibit the conflict.
We do the combining test at the CLOBBER so that the
destination register won't have had a quantity number
assigned, since that would prevent combining. */
if (GET_CODE (PATTERN (insn)) == CLOBBER
&& (r0 = XEXP (PATTERN (insn), 0),
GET_CODE (r0) == REG)
&& (link = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0
&& XEXP (link, 0) != 0
&& GET_CODE (XEXP (link, 0)) == INSN
&& (set = single_set (XEXP (link, 0))) != 0
&& SET_DEST (set) == r0 && SET_SRC (set) == r0
&& (note = find_reg_note (XEXP (link, 0), REG_EQUAL,
NULL_RTX)) != 0)
{
if (r1 = XEXP (note, 0), GET_CODE (r1) == REG
/* Check that we have such a sequence. */
&& no_conflict_p (insn, r0, r1))
win = combine_regs (r1, r0, 1, insn_number, insn, 1);
else if (GET_RTX_FORMAT (GET_CODE (XEXP (note, 0)))[0] == 'e'
&& (r1 = XEXP (XEXP (note, 0), 0),
GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)
&& no_conflict_p (insn, r0, r1))
win = combine_regs (r1, r0, 0, insn_number, insn, 1);
/* Here we care if the operation to be computed is
commutative. */
else if ((GET_CODE (XEXP (note, 0)) == EQ
|| GET_CODE (XEXP (note, 0)) == NE
|| GET_RTX_CLASS (GET_CODE (XEXP (note, 0))) == 'c')
&& (r1 = XEXP (XEXP (note, 0), 1),
(GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG))
&& no_conflict_p (insn, r0, r1))
win = combine_regs (r1, r0, 0, insn_number, insn, 1);
/* If we did combine something, show the register number
in question so that we know to ignore its death. */
if (win)
no_conflict_combined_regno = REGNO (r1);
}
/* If registers were just tied, set COMBINED_REGNO
to the number of the register used in this insn
that was tied to the register set in this insn.
This register's qty should not be "killed". */
if (win)
{
while (GET_CODE (r1) == SUBREG)
r1 = SUBREG_REG (r1);
combined_regno = REGNO (r1);
}
/* Mark the death of everything that dies in this instruction,
except for anything that was just combined. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_DEAD
&& GET_CODE (XEXP (link, 0)) == REG
&& combined_regno != REGNO (XEXP (link, 0))
&& (no_conflict_combined_regno != REGNO (XEXP (link, 0))
|| ! find_reg_note (insn, REG_NO_CONFLICT, XEXP (link, 0))))
wipe_dead_reg (XEXP (link, 0), 0);
/* Allocate qty numbers for all registers local to this block
that are born (set) in this instruction.
A pseudo that already has a qty is not changed. */
note_stores (PATTERN (insn), reg_is_set);
/* If anything is set in this insn and then unused, mark it as dying
after this insn, so it will conflict with our outputs. This
can't match with something that combined, and it doesn't matter
if it did. Do this after the calls to reg_is_set since these
die after, not during, the current insn. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_UNUSED
&& GET_CODE (XEXP (link, 0)) == REG)
wipe_dead_reg (XEXP (link, 0), 1);
/* If this is an insn that has a REG_RETVAL note pointing at a
CLOBBER insn, we have reached the end of a REG_NO_CONFLICT
block, so clear any register number that combined within it. */
if ((note = find_reg_note (insn, REG_RETVAL, NULL_RTX)) != 0
&& GET_CODE (XEXP (note, 0)) == INSN
&& GET_CODE (PATTERN (XEXP (note, 0))) == CLOBBER)
no_conflict_combined_regno = -1;
}
/* Set the registers live after INSN_NUMBER. Note that we never
record the registers live before the block's first insn, since no
pseudos we care about are live before that insn. */
IOR_HARD_REG_SET (regs_live_at[2 * insn_number], regs_live);
IOR_HARD_REG_SET (regs_live_at[2 * insn_number + 1], regs_live);
if (insn == BLOCK_END (b))
break;
insn = NEXT_INSN (insn);
}
/* Now every register that is local to this basic block
should have been given a quantity, or else -1 meaning ignore it.
Every quantity should have a known birth and death.
Order the qtys so we assign them registers in order of the
number of suggested registers they need so we allocate those with
the most restrictive needs first. */
qty_order = (int *) alloca (next_qty * sizeof (int));
for (i = 0; i < next_qty; i++)
qty_order[i] = i;
#define EXCHANGE(I1, I2) \
{ i = qty_order[I1]; qty_order[I1] = qty_order[I2]; qty_order[I2] = i; }
switch (next_qty)
{
case 3:
/* Make qty_order[2] be the one to allocate last. */
if (qty_sugg_compare (0, 1) > 0)
EXCHANGE (0, 1);
if (qty_sugg_compare (1, 2) > 0)
EXCHANGE (2, 1);
/* ... Fall through ... */
case 2:
/* Put the best one to allocate in qty_order[0]. */
if (qty_sugg_compare (0, 1) > 0)
EXCHANGE (0, 1);
/* ... Fall through ... */
case 1:
case 0:
/* Nothing to do here. */
break;
default:
qsort (qty_order, next_qty, sizeof (int), qty_sugg_compare_1);
}
/* Try to put each quantity in a suggested physical register, if it has one.
This may cause registers to be allocated that otherwise wouldn't be, but
this seems acceptable in local allocation (unlike global allocation). */
for (i = 0; i < next_qty; i++)
{
q = qty_order[i];
if (qty_phys_num_sugg[q] != 0 || qty_phys_num_copy_sugg[q] != 0)
qty_phys_reg[q] = find_free_reg (qty_min_class[q], qty_mode[q], q,
0, 1, qty_birth[q], qty_death[q]);
else
qty_phys_reg[q] = -1;
}
/* Order the qtys so we assign them registers in order of
decreasing length of life. Normally call qsort, but if we
have only a very small number of quantities, sort them ourselves. */
for (i = 0; i < next_qty; i++)
qty_order[i] = i;
#define EXCHANGE(I1, I2) \
{ i = qty_order[I1]; qty_order[I1] = qty_order[I2]; qty_order[I2] = i; }
switch (next_qty)
{
case 3:
/* Make qty_order[2] be the one to allocate last. */
if (qty_compare (0, 1) > 0)
EXCHANGE (0, 1);
if (qty_compare (1, 2) > 0)
EXCHANGE (2, 1);
/* ... Fall through ... */
case 2:
/* Put the best one to allocate in qty_order[0]. */
if (qty_compare (0, 1) > 0)
EXCHANGE (0, 1);
/* ... Fall through ... */
case 1:
case 0:
/* Nothing to do here. */
break;
default:
qsort (qty_order, next_qty, sizeof (int), qty_compare_1);
}
/* Now for each qty that is not a hardware register,
look for a hardware register to put it in.
First try the register class that is cheapest for this qty,
if there is more than one class. */
for (i = 0; i < next_qty; i++)
{
q = qty_order[i];
if (qty_phys_reg[q] < 0)
{
#ifdef INSN_SCHEDULING
/* These values represent the adjusted lifetime of a qty so
that it conflicts with qtys which appear near the start/end
of this qty's lifetime.
The purpose behind extending the lifetime of this qty is to
discourage the register allocator from creating false
dependencies.
The adjustment value is choosen to indicate that this qty
conflicts with all the qtys in the instructions immediately
before and after the lifetime of this qty.
Experiments have shown that higher values tend to hurt
overall code performance.
If allocation using the extended lifetime fails we will try
again with the qty's unadjusted lifetime. */
int fake_birth = MAX (0, qty_birth[q] - 2 + qty_birth[q] % 2);
int fake_death = MIN (insn_number * 2 + 1,
qty_death[q] + 2 - qty_death[q] % 2);
#endif
if (N_REG_CLASSES > 1)
{
#ifdef INSN_SCHEDULING
/* We try to avoid using hard registers allocated to qtys which
are born immediately after this qty or die immediately before
this qty.
This optimization is only appropriate when we will run
a scheduling pass after reload and we are not optimizing
for code size. */
if (flag_schedule_insns_after_reload
&& !optimize_size
&& !SMALL_REGISTER_CLASSES)
{
qty_phys_reg[q] = find_free_reg (qty_min_class[q],
qty_mode[q], q, 0, 0,
fake_birth, fake_death);
if (qty_phys_reg[q] >= 0)
continue;
}
#endif
qty_phys_reg[q] = find_free_reg (qty_min_class[q],
qty_mode[q], q, 0, 0,
qty_birth[q], qty_death[q]);
if (qty_phys_reg[q] >= 0)
continue;
}
#ifdef INSN_SCHEDULING
/* Similarly, avoid false dependencies. */
if (flag_schedule_insns_after_reload
&& !optimize_size
&& !SMALL_REGISTER_CLASSES
&& qty_alternate_class[q] != NO_REGS)
qty_phys_reg[q] = find_free_reg (qty_alternate_class[q],
qty_mode[q], q, 0, 0,
fake_birth, fake_death);
#endif
if (qty_alternate_class[q] != NO_REGS)
qty_phys_reg[q] = find_free_reg (qty_alternate_class[q],
qty_mode[q], q, 0, 0,
qty_birth[q], qty_death[q]);
}
}
/* Now propagate the register assignments
to the pseudo regs belonging to the qtys. */
for (q = 0; q < next_qty; q++)
if (qty_phys_reg[q] >= 0)
{
for (i = qty_first_reg[q]; i >= 0; i = reg_next_in_qty[i])
reg_renumber[i] = qty_phys_reg[q] + reg_offset[i];
}
}
/* Compare two quantities' priority for getting real registers.
We give shorter-lived quantities higher priority.
Quantities with more references are also preferred, as are quantities that
require multiple registers. This is the identical prioritization as
done by global-alloc.
We used to give preference to registers with *longer* lives, but using
the same algorithm in both local- and global-alloc can speed up execution
of some programs by as much as a factor of three! */
/* Note that the quotient will never be bigger than
the value of floor_log2 times the maximum number of
times a register can occur in one insn (surely less than 100).
Multiplying this by 10000 can't overflow.
QTY_CMP_PRI is also used by qty_sugg_compare. */
#define QTY_CMP_PRI(q) \
((int) (((double) (floor_log2 (qty_n_refs[q]) * qty_n_refs[q] * qty_size[q]) \
/ (qty_death[q] - qty_birth[q])) * 10000))
static int
qty_compare (q1, q2)
int q1, q2;
{
return QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
}
static int
qty_compare_1 (q1p, q2p)
const GENERIC_PTR q1p;
const GENERIC_PTR q2p;
{
register int q1 = *(int *)q1p, q2 = *(int *)q2p;
register int tem = QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
if (tem != 0)
return tem;
/* If qtys are equally good, sort by qty number,
so that the results of qsort leave nothing to chance. */
return q1 - q2;
}
/* Compare two quantities' priority for getting real registers. This version
is called for quantities that have suggested hard registers. First priority
goes to quantities that have copy preferences, then to those that have
normal preferences. Within those groups, quantities with the lower
number of preferences have the highest priority. Of those, we use the same
algorithm as above. */
#define QTY_CMP_SUGG(q) \
(qty_phys_num_copy_sugg[q] \
? qty_phys_num_copy_sugg[q] \
: qty_phys_num_sugg[q] * FIRST_PSEUDO_REGISTER)
static int
qty_sugg_compare (q1, q2)
int q1, q2;
{
register int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
if (tem != 0)
return tem;
return QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
}
static int
qty_sugg_compare_1 (q1p, q2p)
const GENERIC_PTR q1p;
const GENERIC_PTR q2p;
{
register int q1 = *(int *)q1p, q2 = *(int *)q2p;
register int tem = QTY_CMP_SUGG (q1) - QTY_CMP_SUGG (q2);
if (tem != 0)
return tem;
tem = QTY_CMP_PRI (q2) - QTY_CMP_PRI (q1);
if (tem != 0)
return tem;
/* If qtys are equally good, sort by qty number,
so that the results of qsort leave nothing to chance. */
return q1 - q2;
}
#undef QTY_CMP_SUGG
#undef QTY_CMP_PRI
/* Attempt to combine the two registers (rtx's) USEDREG and SETREG.
Returns 1 if have done so, or 0 if cannot.
Combining registers means marking them as having the same quantity
and adjusting the offsets within the quantity if either of
them is a SUBREG).
We don't actually combine a hard reg with a pseudo; instead
we just record the hard reg as the suggestion for the pseudo's quantity.
If we really combined them, we could lose if the pseudo lives
across an insn that clobbers the hard reg (eg, movstr).
ALREADY_DEAD is non-zero if USEDREG is known to be dead even though
there is no REG_DEAD note on INSN. This occurs during the processing
of REG_NO_CONFLICT blocks.
MAY_SAVE_COPYCOPY is non-zero if this insn is simply copying USEDREG to
SETREG or if the input and output must share a register.
In that case, we record a hard reg suggestion in QTY_PHYS_COPY_SUGG.
There are elaborate checks for the validity of combining. */
static int
combine_regs (usedreg, setreg, may_save_copy, insn_number, insn, already_dead)
rtx usedreg, setreg;
int may_save_copy;
int insn_number;
rtx insn;
int already_dead;
{
register int ureg, sreg;
register int offset = 0;
int usize, ssize;
register int sqty;
/* Determine the numbers and sizes of registers being used. If a subreg
is present that does not change the entire register, don't consider
this a copy insn. */
while (GET_CODE (usedreg) == SUBREG)
{
if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (usedreg))) > UNITS_PER_WORD)
may_save_copy = 0;
offset += SUBREG_WORD (usedreg);
usedreg = SUBREG_REG (usedreg);
}
if (GET_CODE (usedreg) != REG)
return 0;
ureg = REGNO (usedreg);
usize = REG_SIZE (usedreg);
while (GET_CODE (setreg) == SUBREG)
{
if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (setreg))) > UNITS_PER_WORD)
may_save_copy = 0;
offset -= SUBREG_WORD (setreg);
setreg = SUBREG_REG (setreg);
}
if (GET_CODE (setreg) != REG)
return 0;
sreg = REGNO (setreg);
ssize = REG_SIZE (setreg);
/* If UREG is a pseudo-register that hasn't already been assigned a
quantity number, it means that it is not local to this block or dies
more than once. In either event, we can't do anything with it. */
if ((ureg >= FIRST_PSEUDO_REGISTER && reg_qty[ureg] < 0)
/* Do not combine registers unless one fits within the other. */
|| (offset > 0 && usize + offset > ssize)
|| (offset < 0 && usize + offset < ssize)
/* Do not combine with a smaller already-assigned object
if that smaller object is already combined with something bigger. */
|| (ssize > usize && ureg >= FIRST_PSEUDO_REGISTER
&& usize < qty_size[reg_qty[ureg]])
/* Can't combine if SREG is not a register we can allocate. */
|| (sreg >= FIRST_PSEUDO_REGISTER && reg_qty[sreg] == -1)
/* Don't combine with a pseudo mentioned in a REG_NO_CONFLICT note.
These have already been taken care of. This probably wouldn't
combine anyway, but don't take any chances. */
|| (ureg >= FIRST_PSEUDO_REGISTER
&& find_reg_note (insn, REG_NO_CONFLICT, usedreg))
/* Don't tie something to itself. In most cases it would make no
difference, but it would screw up if the reg being tied to itself
also dies in this insn. */
|| ureg == sreg
/* Don't try to connect two different hardware registers. */
|| (ureg < FIRST_PSEUDO_REGISTER && sreg < FIRST_PSEUDO_REGISTER)
/* Don't use a hard reg that might be spilled. */
|| (ureg < FIRST_PSEUDO_REGISTER
&& CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (ureg)))
|| (sreg < FIRST_PSEUDO_REGISTER
&& CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (sreg)))
/* Don't connect two different machine modes if they have different
implications as to which registers may be used. */
|| !MODES_TIEABLE_P (GET_MODE (usedreg), GET_MODE (setreg)))
return 0;
/* Now, if UREG is a hard reg and SREG is a pseudo, record the hard reg in
qty_phys_sugg for the pseudo instead of tying them.
Return "failure" so that the lifespan of UREG is terminated here;
that way the two lifespans will be disjoint and nothing will prevent
the pseudo reg from being given this hard reg. */
if (ureg < FIRST_PSEUDO_REGISTER)
{
/* Allocate a quantity number so we have a place to put our
suggestions. */
if (reg_qty[sreg] == -2)
reg_is_born (setreg, 2 * insn_number);
if (reg_qty[sreg] >= 0)
{
if (may_save_copy
&& ! TEST_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[sreg]], ureg))
{
SET_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[sreg]], ureg);
qty_phys_num_copy_sugg[reg_qty[sreg]]++;
}
else if (! TEST_HARD_REG_BIT (qty_phys_sugg[reg_qty[sreg]], ureg))
{
SET_HARD_REG_BIT (qty_phys_sugg[reg_qty[sreg]], ureg);
qty_phys_num_sugg[reg_qty[sreg]]++;
}
}
return 0;
}
/* Similarly for SREG a hard register and UREG a pseudo register. */
if (sreg < FIRST_PSEUDO_REGISTER)
{
if (may_save_copy
&& ! TEST_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[ureg]], sreg))
{
SET_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[ureg]], sreg);
qty_phys_num_copy_sugg[reg_qty[ureg]]++;
}
else if (! TEST_HARD_REG_BIT (qty_phys_sugg[reg_qty[ureg]], sreg))
{
SET_HARD_REG_BIT (qty_phys_sugg[reg_qty[ureg]], sreg);
qty_phys_num_sugg[reg_qty[ureg]]++;
}
return 0;
}
/* At this point we know that SREG and UREG are both pseudos.
Do nothing if SREG already has a quantity or is a register that we
don't allocate. */
if (reg_qty[sreg] >= -1
/* If we are not going to let any regs live across calls,
don't tie a call-crossing reg to a non-call-crossing reg. */
|| (current_function_has_nonlocal_label
&& ((REG_N_CALLS_CROSSED (ureg) > 0)
!= (REG_N_CALLS_CROSSED (sreg) > 0))))
return 0;
/* We don't already know about SREG, so tie it to UREG
if this is the last use of UREG, provided the classes they want
are compatible. */
if ((already_dead || find_regno_note (insn, REG_DEAD, ureg))
&& reg_meets_class_p (sreg, qty_min_class[reg_qty[ureg]]))
{
/* Add SREG to UREG's quantity. */
sqty = reg_qty[ureg];
reg_qty[sreg] = sqty;
reg_offset[sreg] = reg_offset[ureg] + offset;
reg_next_in_qty[sreg] = qty_first_reg[sqty];
qty_first_reg[sqty] = sreg;
/* If SREG's reg class is smaller, set qty_min_class[SQTY]. */
update_qty_class (sqty, sreg);
/* Update info about quantity SQTY. */
qty_n_calls_crossed[sqty] += REG_N_CALLS_CROSSED (sreg);
qty_n_refs[sqty] += REG_N_REFS (sreg);
if (usize < ssize)
{
register int i;
for (i = qty_first_reg[sqty]; i >= 0; i = reg_next_in_qty[i])
reg_offset[i] -= offset;
qty_size[sqty] = ssize;
qty_mode[sqty] = GET_MODE (setreg);
}
}
else
return 0;
return 1;
}
/* Return 1 if the preferred class of REG allows it to be tied
to a quantity or register whose class is CLASS.
True if REG's reg class either contains or is contained in CLASS. */
static int
reg_meets_class_p (reg, class)
int reg;
enum reg_class class;
{
register enum reg_class rclass = reg_preferred_class (reg);
return (reg_class_subset_p (rclass, class)
|| reg_class_subset_p (class, rclass));
}
/* Update the class of QTY assuming that REG is being tied to it. */
static void
update_qty_class (qty, reg)
int qty;
int reg;
{
enum reg_class rclass = reg_preferred_class (reg);
if (reg_class_subset_p (rclass, qty_min_class[qty]))
qty_min_class[qty] = rclass;
rclass = reg_alternate_class (reg);
if (reg_class_subset_p (rclass, qty_alternate_class[qty]))
qty_alternate_class[qty] = rclass;
if (REG_CHANGES_SIZE (reg))
qty_changes_size[qty] = 1;
}
/* Handle something which alters the value of an rtx REG.
REG is whatever is set or clobbered. SETTER is the rtx that
is modifying the register.
If it is not really a register, we do nothing.
The file-global variables `this_insn' and `this_insn_number'
carry info from `block_alloc'. */
static void
reg_is_set (reg, setter)
rtx reg;
rtx setter;
{
/* Note that note_stores will only pass us a SUBREG if it is a SUBREG of
a hard register. These may actually not exist any more. */
if (GET_CODE (reg) != SUBREG
&& GET_CODE (reg) != REG)
return;
/* Mark this register as being born. If it is used in a CLOBBER, mark
it as being born halfway between the previous insn and this insn so that
it conflicts with our inputs but not the outputs of the previous insn. */
reg_is_born (reg, 2 * this_insn_number - (GET_CODE (setter) == CLOBBER));
}
/* Handle beginning of the life of register REG.
BIRTH is the index at which this is happening. */
static void
reg_is_born (reg, birth)
rtx reg;
int birth;
{
register int regno;
if (GET_CODE (reg) == SUBREG)
regno = REGNO (SUBREG_REG (reg)) + SUBREG_WORD (reg);
else
regno = REGNO (reg);
if (regno < FIRST_PSEUDO_REGISTER)
{
mark_life (regno, GET_MODE (reg), 1);
/* If the register was to have been born earlier that the present
insn, mark it as live where it is actually born. */
if (birth < 2 * this_insn_number)
post_mark_life (regno, GET_MODE (reg), 1, birth, 2 * this_insn_number);
}
else
{
if (reg_qty[regno] == -2)
alloc_qty (regno, GET_MODE (reg), PSEUDO_REGNO_SIZE (regno), birth);
/* If this register has a quantity number, show that it isn't dead. */
if (reg_qty[regno] >= 0)
qty_death[reg_qty[regno]] = -1;
}
}
/* Record the death of REG in the current insn. If OUTPUT_P is non-zero,
REG is an output that is dying (i.e., it is never used), otherwise it
is an input (the normal case).
If OUTPUT_P is 1, then we extend the life past the end of this insn. */
static void
wipe_dead_reg (reg, output_p)
register rtx reg;
int output_p;
{
register int regno = REGNO (reg);
/* If this insn has multiple results,
and the dead reg is used in one of the results,
extend its life to after this insn,
so it won't get allocated together with any other result of this insn.
It is unsafe to use !single_set here since it will ignore an unused
output. Just because an output is unused does not mean the compiler
can assume the side effect will not occur. Consider if REG appears
in the address of an output and we reload the output. If we allocate
REG to the same hard register as an unused output we could set the hard
register before the output reload insn. */
if (GET_CODE (PATTERN (this_insn)) == PARALLEL
&& multiple_sets (this_insn))
{
int i;
for (i = XVECLEN (PATTERN (this_insn), 0) - 1; i >= 0; i--)
{
rtx set = XVECEXP (PATTERN (this_insn), 0, i);
if (GET_CODE (set) == SET
&& GET_CODE (SET_DEST (set)) != REG
&& !rtx_equal_p (reg, SET_DEST (set))
&& reg_overlap_mentioned_p (reg, SET_DEST (set)))
output_p = 1;
}
}
/* If this register is used in an auto-increment address, then extend its
life to after this insn, so that it won't get allocated together with
the result of this insn. */
if (! output_p && find_regno_note (this_insn, REG_INC, regno))
output_p = 1;
if (regno < FIRST_PSEUDO_REGISTER)
{
mark_life (regno, GET_MODE (reg), 0);
/* If a hard register is dying as an output, mark it as in use at
the beginning of this insn (the above statement would cause this
not to happen). */
if (output_p)
post_mark_life (regno, GET_MODE (reg), 1,
2 * this_insn_number, 2 * this_insn_number+ 1);
}
else if (reg_qty[regno] >= 0)
qty_death[reg_qty[regno]] = 2 * this_insn_number + output_p;
}
/* Find a block of SIZE words of hard regs in reg_class CLASS
that can hold something of machine-mode MODE
(but actually we test only the first of the block for holding MODE)
and still free between insn BORN_INDEX and insn DEAD_INDEX,
and return the number of the first of them.
Return -1 if such a block cannot be found.
If QTY crosses calls, insist on a register preserved by calls,
unless ACCEPT_CALL_CLOBBERED is nonzero.
If JUST_TRY_SUGGESTED is non-zero, only try to see if the suggested
register is available. If not, return -1. */
static int
find_free_reg (class, mode, qty, accept_call_clobbered, just_try_suggested,
born_index, dead_index)
enum reg_class class;
enum machine_mode mode;
int qty;
int accept_call_clobbered;
int just_try_suggested;
int born_index, dead_index;
{
register int i, ins;
#ifdef HARD_REG_SET
register /* Declare it register if it's a scalar. */
#endif
HARD_REG_SET used, first_used;
#ifdef ELIMINABLE_REGS
static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
#endif
/* Validate our parameters. */
if (born_index < 0 || born_index > dead_index)
abort ();
/* Don't let a pseudo live in a reg across a function call
if we might get a nonlocal goto. */
if (current_function_has_nonlocal_label
&& qty_n_calls_crossed[qty] > 0)
return -1;
if (accept_call_clobbered)
COPY_HARD_REG_SET (used, call_fixed_reg_set);
else if (qty_n_calls_crossed[qty] == 0)
COPY_HARD_REG_SET (used, fixed_reg_set);
else
COPY_HARD_REG_SET (used, call_used_reg_set);
if (accept_call_clobbered)
IOR_HARD_REG_SET (used, losing_caller_save_reg_set);
for (ins = born_index; ins < dead_index; ins++)
IOR_HARD_REG_SET (used, regs_live_at[ins]);
IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]);
/* Don't use the frame pointer reg in local-alloc even if
we may omit the frame pointer, because if we do that and then we
need a frame pointer, reload won't know how to move the pseudo
to another hard reg. It can move only regs made by global-alloc.
This is true of any register that can be eliminated. */
#ifdef ELIMINABLE_REGS
for (i = 0; i < (int)(sizeof eliminables / sizeof eliminables[0]); i++)
SET_HARD_REG_BIT (used, eliminables[i].from);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
/* If FRAME_POINTER_REGNUM is not a real register, then protect the one
that it might be eliminated into. */
SET_HARD_REG_BIT (used, HARD_FRAME_POINTER_REGNUM);
#endif
#else
SET_HARD_REG_BIT (used, FRAME_POINTER_REGNUM);
#endif
#ifdef CLASS_CANNOT_CHANGE_SIZE
if (qty_changes_size[qty])
IOR_HARD_REG_SET (used,
reg_class_contents[(int) CLASS_CANNOT_CHANGE_SIZE]);
#endif
/* Normally, the registers that can be used for the first register in
a multi-register quantity are the same as those that can be used for
subsequent registers. However, if just trying suggested registers,
restrict our consideration to them. If there are copy-suggested
register, try them. Otherwise, try the arithmetic-suggested
registers. */
COPY_HARD_REG_SET (first_used, used);
if (just_try_suggested)
{
if (qty_phys_num_copy_sugg[qty] != 0)
IOR_COMPL_HARD_REG_SET (first_used, qty_phys_copy_sugg[qty]);
else
IOR_COMPL_HARD_REG_SET (first_used, qty_phys_sugg[qty]);
}
/* If all registers are excluded, we can't do anything. */
GO_IF_HARD_REG_SUBSET (reg_class_contents[(int) ALL_REGS], first_used, fail);
/* If at least one would be suitable, test each hard reg. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
#ifdef REG_ALLOC_ORDER
int regno = reg_alloc_order[i];
#else
int regno = i;
#endif
if (! TEST_HARD_REG_BIT (first_used, regno)
&& HARD_REGNO_MODE_OK (regno, mode)
&& (qty_n_calls_crossed[qty] == 0
|| accept_call_clobbered
|| ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
{
register int j;
register int size1 = HARD_REGNO_NREGS (regno, mode);
for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, regno + j); j++);
if (j == size1)
{
/* Mark that this register is in use between its birth and death
insns. */
post_mark_life (regno, mode, 1, born_index, dead_index);
return regno;
}
#ifndef REG_ALLOC_ORDER
i += j; /* Skip starting points we know will lose */
#endif
}
}
fail:
/* If we are just trying suggested register, we have just tried copy-
suggested registers, and there are arithmetic-suggested registers,
try them. */
/* If it would be profitable to allocate a call-clobbered register
and save and restore it around calls, do that. */
if (just_try_suggested && qty_phys_num_copy_sugg[qty] != 0
&& qty_phys_num_sugg[qty] != 0)
{
/* Don't try the copy-suggested regs again. */
qty_phys_num_copy_sugg[qty] = 0;
return find_free_reg (class, mode, qty, accept_call_clobbered, 1,
born_index, dead_index);
}
/* We need not check to see if the current function has nonlocal
labels because we don't put any pseudos that are live over calls in
registers in that case. */
if (! accept_call_clobbered
&& flag_caller_saves
&& ! just_try_suggested
&& qty_n_calls_crossed[qty] != 0
&& CALLER_SAVE_PROFITABLE (qty_n_refs[qty], qty_n_calls_crossed[qty]))
{
i = find_free_reg (class, mode, qty, 1, 0, born_index, dead_index);
if (i >= 0)
caller_save_needed = 1;
return i;
}
return -1;
}
/* Mark that REGNO with machine-mode MODE is live starting from the current
insn (if LIFE is non-zero) or dead starting at the current insn (if LIFE
is zero). */
static void
mark_life (regno, mode, life)
register int regno;
enum machine_mode mode;
int life;
{
register int j = HARD_REGNO_NREGS (regno, mode);
if (life)
while (--j >= 0)
SET_HARD_REG_BIT (regs_live, regno + j);
else
while (--j >= 0)
CLEAR_HARD_REG_BIT (regs_live, regno + j);
}
/* Mark register number REGNO (with machine-mode MODE) as live (if LIFE
is non-zero) or dead (if LIFE is zero) from insn number BIRTH (inclusive)
to insn number DEATH (exclusive). */
static void
post_mark_life (regno, mode, life, birth, death)
int regno;
enum machine_mode mode;
int life, birth, death;
{
register int j = HARD_REGNO_NREGS (regno, mode);
#ifdef HARD_REG_SET
register /* Declare it register if it's a scalar. */
#endif
HARD_REG_SET this_reg;
CLEAR_HARD_REG_SET (this_reg);
while (--j >= 0)
SET_HARD_REG_BIT (this_reg, regno + j);
if (life)
while (birth < death)
{
IOR_HARD_REG_SET (regs_live_at[birth], this_reg);
birth++;
}
else
while (birth < death)
{
AND_COMPL_HARD_REG_SET (regs_live_at[birth], this_reg);
birth++;
}
}
/* INSN is the CLOBBER insn that starts a REG_NO_NOCONFLICT block, R0
is the register being clobbered, and R1 is a register being used in
the equivalent expression.
If R1 dies in the block and has a REG_NO_CONFLICT note on every insn
in which it is used, return 1.
Otherwise, return 0. */
static int
no_conflict_p (insn, r0, r1)
rtx insn, r0, r1;
{
int ok = 0;
rtx note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
rtx p, last;
/* If R1 is a hard register, return 0 since we handle this case
when we scan the insns that actually use it. */
if (note == 0
|| (GET_CODE (r1) == REG && REGNO (r1) < FIRST_PSEUDO_REGISTER)
|| (GET_CODE (r1) == SUBREG && GET_CODE (SUBREG_REG (r1)) == REG
&& REGNO (SUBREG_REG (r1)) < FIRST_PSEUDO_REGISTER))
return 0;
last = XEXP (note, 0);
for (p = NEXT_INSN (insn); p && p != last; p = NEXT_INSN (p))
if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
{
if (find_reg_note (p, REG_DEAD, r1))
ok = 1;
/* There must be a REG_NO_CONFLICT note on every insn, otherwise
some earlier optimization pass has inserted instructions into
the sequence, and it is not safe to perform this optimization.
Note that emit_no_conflict_block always ensures that this is
true when these sequences are created. */
if (! find_reg_note (p, REG_NO_CONFLICT, r1))
return 0;
}
return ok;
}
#ifdef REGISTER_CONSTRAINTS
/* Return the number of alternatives for which the constraint string P
indicates that the operand must be equal to operand 0 and that no register
is acceptable. */
static int
requires_inout (p)
const char *p;
{
char c;
int found_zero = 0;
int reg_allowed = 0;
int num_matching_alts = 0;
while ((c = *p++))
switch (c)
{
case '=': case '+': case '?':
case '#': case '&': case '!':
case '*': case '%':
case '1': case '2': case '3': case '4':
case 'm': case '<': case '>': case 'V': case 'o':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P':
#ifdef EXTRA_CONSTRAINT
case 'Q': case 'R': case 'S': case 'T': case 'U':
#endif
case 'X':
/* These don't say anything we care about. */
break;
case ',':
if (found_zero && ! reg_allowed)
num_matching_alts++;
found_zero = reg_allowed = 0;
break;
case '0':
found_zero = 1;
break;
case 'p':
case 'g': case 'r':
default:
reg_allowed = 1;
break;
}
if (found_zero && ! reg_allowed)
num_matching_alts++;
return num_matching_alts;
}
#endif /* REGISTER_CONSTRAINTS */
void
dump_local_alloc (file)
FILE *file;
{
register int i;
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
if (reg_renumber[i] != -1)
fprintf (file, ";; Register %d in %d.\n", i, reg_renumber[i]);
}
Index: head/contrib/gcc/loop.c
===================================================================
--- head/contrib/gcc/loop.c (revision 52750)
+++ head/contrib/gcc/loop.c (revision 52751)
@@ -1,9703 +1,9749 @@
/* Perform various loop optimizations, including strength reduction.
Copyright (C) 1987, 88, 89, 91-98, 1999 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 is the loop optimization pass of the compiler.
It finds invariant computations within loops and moves them
to the beginning of the loop. Then it identifies basic and
general induction variables. Strength reduction is applied to the general
induction variables, and induction variable elimination is applied to
the basic induction variables.
It also finds cases where
a register is set within the loop by zero-extending a narrower value
and changes these to zero the entire register once before the loop
and merely copy the low part within the loop.
Most of the complexity is in heuristics to decide when it is worth
while to do these things. */
#include "config.h"
#include "system.h"
#include "rtl.h"
#include "obstack.h"
#include "expr.h"
#include "insn-config.h"
#include "insn-flags.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "recog.h"
#include "flags.h"
#include "real.h"
#include "loop.h"
#include "except.h"
#include "toplev.h"
/* Vector mapping INSN_UIDs to luids.
The luids are like uids but increase monotonically always.
We use them to see whether a jump comes from outside a given loop. */
int *uid_luid;
/* Indexed by INSN_UID, contains the ordinal giving the (innermost) loop
number the insn is contained in. */
int *uid_loop_num;
/* 1 + largest uid of any insn. */
int max_uid_for_loop;
/* 1 + luid of last insn. */
static int max_luid;
/* Number of loops detected in current function. Used as index to the
next few tables. */
static int max_loop_num;
/* Indexed by loop number, contains the first and last insn of each loop. */
static rtx *loop_number_loop_starts, *loop_number_loop_ends;
/* Likewise for the continue insn */
static rtx *loop_number_loop_cont;
/* The first code_label that is reached in every loop iteration.
0 when not computed yet, initially const0_rtx if a jump couldn't be
followed.
Also set to 0 when there is no such label before the NOTE_INSN_LOOP_CONT
of this loop, or in verify_dominator, if a jump couldn't be followed. */
static rtx *loop_number_cont_dominator;
/* For each loop, gives the containing loop number, -1 if none. */
int *loop_outer_loop;
#ifdef HAVE_decrement_and_branch_on_count
/* Records whether resource in use by inner loop. */
int *loop_used_count_register;
#endif /* HAVE_decrement_and_branch_on_count */
/* Indexed by loop number, contains a nonzero value if the "loop" isn't
really a loop (an insn outside the loop branches into it). */
static char *loop_invalid;
/* Indexed by loop number, links together all LABEL_REFs which refer to
code labels outside the loop. Used by routines that need to know all
loop exits, such as final_biv_value and final_giv_value.
This does not include loop exits due to return instructions. This is
because all bivs and givs are pseudos, and hence must be dead after a
return, so the presense of a return does not affect any of the
optimizations that use this info. It is simpler to just not include return
instructions on this list. */
rtx *loop_number_exit_labels;
/* Indexed by loop number, counts the number of LABEL_REFs on
loop_number_exit_labels for this loop and all loops nested inside it. */
int *loop_number_exit_count;
/* Nonzero if there is a subroutine call in the current loop. */
static int loop_has_call;
/* Nonzero if there is a volatile memory reference in the current
loop. */
static int loop_has_volatile;
/* Nonzero if there is a tablejump in the current loop. */
static int loop_has_tablejump;
/* Added loop_continue which is the NOTE_INSN_LOOP_CONT of the
current loop. A continue statement will generate a branch to
NEXT_INSN (loop_continue). */
static rtx loop_continue;
/* Indexed by register number, contains the number of times the reg
is set during the loop being scanned.
During code motion, a negative value indicates a reg that has been
made a candidate; in particular -2 means that it is an candidate that
we know is equal to a constant and -1 means that it is an candidate
not known equal to a constant.
After code motion, regs moved have 0 (which is accurate now)
while the failed candidates have the original number of times set.
Therefore, at all times, == 0 indicates an invariant register;
< 0 a conditionally invariant one. */
static varray_type set_in_loop;
/* Original value of set_in_loop; same except that this value
is not set negative for a reg whose sets have been made candidates
and not set to 0 for a reg that is moved. */
static varray_type n_times_set;
/* Index by register number, 1 indicates that the register
cannot be moved or strength reduced. */
static varray_type may_not_optimize;
/* Contains the insn in which a register was used if it was used
exactly once; contains const0_rtx if it was used more than once. */
static varray_type reg_single_usage;
/* Nonzero means reg N has already been moved out of one loop.
This reduces the desire to move it out of another. */
static char *moved_once;
/* List of MEMs that are stored in this loop. */
static rtx loop_store_mems;
/* The insn where the first of these was found. */
static rtx first_loop_store_insn;
typedef struct loop_mem_info {
rtx mem; /* The MEM itself. */
rtx reg; /* Corresponding pseudo, if any. */
int optimize; /* Nonzero if we can optimize access to this MEM. */
} loop_mem_info;
/* Array of MEMs that are used (read or written) in this loop, but
cannot be aliased by anything in this loop, except perhaps
themselves. In other words, if loop_mems[i] is altered during the
loop, it is altered by an expression that is rtx_equal_p to it. */
static loop_mem_info *loop_mems;
/* The index of the next available slot in LOOP_MEMS. */
static int loop_mems_idx;
/* The number of elements allocated in LOOP_MEMs. */
static int loop_mems_allocated;
/* Nonzero if we don't know what MEMs were changed in the current loop.
This happens if the loop contains a call (in which case `loop_has_call'
will also be set) or if we store into more than NUM_STORES MEMs. */
static int unknown_address_altered;
/* Count of movable (i.e. invariant) instructions discovered in the loop. */
static int num_movables;
/* Count of memory write instructions discovered in the loop. */
static int num_mem_sets;
/* Number of loops contained within the current one, including itself. */
static int loops_enclosed;
/* Bound on pseudo register number before loop optimization.
A pseudo has valid regscan info if its number is < max_reg_before_loop. */
int max_reg_before_loop;
/* This obstack is used in product_cheap_p to allocate its rtl. It
may call gen_reg_rtx which, in turn, may reallocate regno_reg_rtx.
If we used the same obstack that it did, we would be deallocating
that array. */
static struct obstack temp_obstack;
/* This is where the pointer to the obstack being used for RTL is stored. */
extern struct obstack *rtl_obstack;
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
/* During the analysis of a loop, a chain of `struct movable's
is made to record all the movable insns found.
Then the entire chain can be scanned to decide which to move. */
struct movable
{
rtx insn; /* A movable insn */
rtx set_src; /* The expression this reg is set from. */
rtx set_dest; /* The destination of this SET. */
rtx dependencies; /* When INSN is libcall, this is an EXPR_LIST
of any registers used within the LIBCALL. */
int consec; /* Number of consecutive following insns
that must be moved with this one. */
int regno; /* The register it sets */
short lifetime; /* lifetime of that register;
may be adjusted when matching movables
that load the same value are found. */
short savings; /* Number of insns we can move for this reg,
including other movables that force this
or match this one. */
unsigned int cond : 1; /* 1 if only conditionally movable */
unsigned int force : 1; /* 1 means MUST move this insn */
unsigned int global : 1; /* 1 means reg is live outside this loop */
/* If PARTIAL is 1, GLOBAL means something different:
that the reg is live outside the range from where it is set
to the following label. */
unsigned int done : 1; /* 1 inhibits further processing of this */
unsigned int partial : 1; /* 1 means this reg is used for zero-extending.
In particular, moving it does not make it
invariant. */
unsigned int move_insn : 1; /* 1 means that we call emit_move_insn to
load SRC, rather than copying INSN. */
unsigned int move_insn_first:1;/* Same as above, if this is necessary for the
first insn of a consecutive sets group. */
unsigned int is_equiv : 1; /* 1 means a REG_EQUIV is present on INSN. */
enum machine_mode savemode; /* Nonzero means it is a mode for a low part
that we should avoid changing when clearing
the rest of the reg. */
struct movable *match; /* First entry for same value */
struct movable *forces; /* An insn that must be moved if this is */
struct movable *next;
};
static struct movable *the_movables;
FILE *loop_dump_stream;
+/* For communicating return values from note_set_pseudo_multiple_uses. */
+static int note_set_pseudo_multiple_uses_retval;
+
/* Forward declarations. */
static void verify_dominator PROTO((int));
static void find_and_verify_loops PROTO((rtx));
static void mark_loop_jump PROTO((rtx, int));
static void prescan_loop PROTO((rtx, rtx));
static int reg_in_basic_block_p PROTO((rtx, rtx));
static int consec_sets_invariant_p PROTO((rtx, int, rtx));
static int labels_in_range_p PROTO((rtx, int));
static void count_one_set PROTO((rtx, rtx, varray_type, rtx *));
static void count_loop_regs_set PROTO((rtx, rtx, varray_type, varray_type,
int *, int));
static void note_addr_stored PROTO((rtx, rtx));
+static void note_set_pseudo_multiple_uses PROTO((rtx, rtx));
static int loop_reg_used_before_p PROTO((rtx, rtx, rtx, rtx, rtx));
static void scan_loop PROTO((rtx, rtx, rtx, int, int));
#if 0
static void replace_call_address PROTO((rtx, rtx, rtx));
#endif
static rtx skip_consec_insns PROTO((rtx, int));
static int libcall_benefit PROTO((rtx));
static void ignore_some_movables PROTO((struct movable *));
static void force_movables PROTO((struct movable *));
static void combine_movables PROTO((struct movable *, int));
static int regs_match_p PROTO((rtx, rtx, struct movable *));
static int rtx_equal_for_loop_p PROTO((rtx, rtx, struct movable *));
static void add_label_notes PROTO((rtx, rtx));
static void move_movables PROTO((struct movable *, int, int, rtx, rtx, int));
static int count_nonfixed_reads PROTO((rtx));
static void strength_reduce PROTO((rtx, rtx, rtx, int, rtx, rtx, rtx, int, int));
static void find_single_use_in_loop PROTO((rtx, rtx, varray_type));
static int valid_initial_value_p PROTO((rtx, rtx, int, rtx));
static void find_mem_givs PROTO((rtx, rtx, int, rtx, rtx));
static void record_biv PROTO((struct induction *, rtx, rtx, rtx, rtx, rtx *, int, int));
static void check_final_value PROTO((struct induction *, rtx, rtx,
unsigned HOST_WIDE_INT));
static void record_giv PROTO((struct induction *, rtx, rtx, rtx, rtx, rtx, int, enum g_types, int, rtx *, rtx, rtx));
static void update_giv_derive PROTO((rtx));
static int basic_induction_var PROTO((rtx, enum machine_mode, rtx, rtx, rtx *, rtx *, rtx **));
static rtx simplify_giv_expr PROTO((rtx, int *));
static int general_induction_var PROTO((rtx, rtx *, rtx *, rtx *, int, int *));
static int consec_sets_giv PROTO((int, rtx, rtx, rtx, rtx *, rtx *, rtx *));
static int check_dbra_loop PROTO((rtx, int, rtx, struct loop_info *));
static rtx express_from_1 PROTO((rtx, rtx, rtx));
static rtx combine_givs_p PROTO((struct induction *, struct induction *));
static void combine_givs PROTO((struct iv_class *));
struct recombine_givs_stats;
static int find_life_end PROTO((rtx, struct recombine_givs_stats *, rtx, rtx));
static void recombine_givs PROTO((struct iv_class *, rtx, rtx, int));
static int product_cheap_p PROTO((rtx, rtx));
static int maybe_eliminate_biv PROTO((struct iv_class *, rtx, rtx, int, int, int));
static int maybe_eliminate_biv_1 PROTO((rtx, rtx, struct iv_class *, int, rtx));
static int last_use_this_basic_block PROTO((rtx, rtx));
static void record_initial PROTO((rtx, rtx));
static void update_reg_last_use PROTO((rtx, rtx));
static rtx next_insn_in_loop PROTO((rtx, rtx, rtx, rtx));
static void load_mems_and_recount_loop_regs_set PROTO((rtx, rtx, rtx,
rtx, int *));
static void load_mems PROTO((rtx, rtx, rtx, rtx));
static int insert_loop_mem PROTO((rtx *, void *));
static int replace_loop_mem PROTO((rtx *, void *));
static int replace_label PROTO((rtx *, void *));
typedef struct rtx_and_int {
rtx r;
int i;
} rtx_and_int;
typedef struct rtx_pair {
rtx r1;
rtx r2;
} rtx_pair;
/* Nonzero iff INSN is between START and END, inclusive. */
#define INSN_IN_RANGE_P(INSN, START, END) \
(INSN_UID (INSN) < max_uid_for_loop \
&& INSN_LUID (INSN) >= INSN_LUID (START) \
&& INSN_LUID (INSN) <= INSN_LUID (END))
#ifdef HAVE_decrement_and_branch_on_count
/* Test whether BCT applicable and safe. */
static void insert_bct PROTO((rtx, rtx, struct loop_info *));
/* Auxiliary function that inserts the BCT pattern into the loop. */
static void instrument_loop_bct PROTO((rtx, rtx, rtx));
#endif /* HAVE_decrement_and_branch_on_count */
/* Indirect_jump_in_function is computed once per function. */
int indirect_jump_in_function = 0;
static int indirect_jump_in_function_p PROTO((rtx));
static int compute_luids PROTO((rtx, rtx, int));
static int biv_elimination_giv_has_0_offset PROTO((struct induction *,
struct induction *, rtx));
/* Relative gain of eliminating various kinds of operations. */
static int add_cost;
#if 0
static int shift_cost;
static int mult_cost;
#endif
/* Benefit penalty, if a giv is not replaceable, i.e. must emit an insn to
copy the value of the strength reduced giv to its original register. */
static int copy_cost;
/* Cost of using a register, to normalize the benefits of a giv. */
static int reg_address_cost;
void
init_loop ()
{
char *free_point = (char *) oballoc (1);
rtx reg = gen_rtx_REG (word_mode, LAST_VIRTUAL_REGISTER + 1);
add_cost = rtx_cost (gen_rtx_PLUS (word_mode, reg, reg), SET);
#ifdef ADDRESS_COST
reg_address_cost = ADDRESS_COST (reg);
#else
reg_address_cost = rtx_cost (reg, MEM);
#endif
/* We multiply by 2 to reconcile the difference in scale between
these two ways of computing costs. Otherwise the cost of a copy
will be far less than the cost of an add. */
copy_cost = 2 * 2;
/* Free the objects we just allocated. */
obfree (free_point);
/* Initialize the obstack used for rtl in product_cheap_p. */
gcc_obstack_init (&temp_obstack);
}
/* Compute the mapping from uids to luids.
LUIDs are numbers assigned to insns, like uids,
except that luids increase monotonically through the code.
Start at insn START and stop just before END. Assign LUIDs
starting with PREV_LUID + 1. Return the last assigned LUID + 1. */
static int
compute_luids (start, end, prev_luid)
rtx start, end;
int prev_luid;
{
int i;
rtx insn;
for (insn = start, i = prev_luid; insn != end; insn = NEXT_INSN (insn))
{
if (INSN_UID (insn) >= max_uid_for_loop)
continue;
/* Don't assign luids to line-number NOTEs, so that the distance in
luids between two insns is not affected by -g. */
if (GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) <= 0)
uid_luid[INSN_UID (insn)] = ++i;
else
/* Give a line number note the same luid as preceding insn. */
uid_luid[INSN_UID (insn)] = i;
}
return i + 1;
}
/* Entry point of this file. Perform loop optimization
on the current function. F is the first insn of the function
and DUMPFILE is a stream for output of a trace of actions taken
(or 0 if none should be output). */
void
loop_optimize (f, dumpfile, unroll_p, bct_p)
/* f is the first instruction of a chain of insns for one function */
rtx f;
FILE *dumpfile;
int unroll_p, bct_p;
{
register rtx insn;
register int i;
loop_dump_stream = dumpfile;
init_recog_no_volatile ();
max_reg_before_loop = max_reg_num ();
moved_once = (char *) alloca (max_reg_before_loop);
bzero (moved_once, max_reg_before_loop);
regs_may_share = 0;
/* Count the number of loops. */
max_loop_num = 0;
for (insn = f; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
max_loop_num++;
}
/* Don't waste time if no loops. */
if (max_loop_num == 0)
return;
/* Get size to use for tables indexed by uids.
Leave some space for labels allocated by find_and_verify_loops. */
max_uid_for_loop = get_max_uid () + 1 + max_loop_num * 32;
uid_luid = (int *) alloca (max_uid_for_loop * sizeof (int));
uid_loop_num = (int *) alloca (max_uid_for_loop * sizeof (int));
bzero ((char *) uid_luid, max_uid_for_loop * sizeof (int));
bzero ((char *) uid_loop_num, max_uid_for_loop * sizeof (int));
/* Allocate tables for recording each loop. We set each entry, so they need
not be zeroed. */
loop_number_loop_starts = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_loop_ends = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_loop_cont = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_cont_dominator = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_outer_loop = (int *) alloca (max_loop_num * sizeof (int));
loop_invalid = (char *) alloca (max_loop_num * sizeof (char));
loop_number_exit_labels = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_exit_count = (int *) alloca (max_loop_num * sizeof (int));
#ifdef HAVE_decrement_and_branch_on_count
/* Allocate for BCT optimization */
loop_used_count_register = (int *) alloca (max_loop_num * sizeof (int));
bzero ((char *) loop_used_count_register, max_loop_num * sizeof (int));
#endif /* HAVE_decrement_and_branch_on_count */
/* Find and process each loop.
First, find them, and record them in order of their beginnings. */
find_and_verify_loops (f);
/* Now find all register lifetimes. This must be done after
find_and_verify_loops, because it might reorder the insns in the
function. */
reg_scan (f, max_reg_num (), 1);
/* This must occur after reg_scan so that registers created by gcse
will have entries in the register tables.
We could have added a call to reg_scan after gcse_main in toplev.c,
but moving this call to init_alias_analysis is more efficient. */
init_alias_analysis ();
/* See if we went too far. Note that get_max_uid already returns
one more that the maximum uid of all insn. */
if (get_max_uid () > max_uid_for_loop)
abort ();
/* Now reset it to the actual size we need. See above. */
max_uid_for_loop = get_max_uid ();
/* find_and_verify_loops has already called compute_luids, but it might
have rearranged code afterwards, so we need to recompute the luids now. */
max_luid = compute_luids (f, NULL_RTX, 0);
/* Don't leave gaps in uid_luid for insns that have been
deleted. It is possible that the first or last insn
using some register has been deleted by cross-jumping.
Make sure that uid_luid for that former insn's uid
points to the general area where that insn used to be. */
for (i = 0; i < max_uid_for_loop; i++)
{
uid_luid[0] = uid_luid[i];
if (uid_luid[0] != 0)
break;
}
for (i = 0; i < max_uid_for_loop; i++)
if (uid_luid[i] == 0)
uid_luid[i] = uid_luid[i - 1];
/* Create a mapping from loops to BLOCK tree nodes. */
if (unroll_p && write_symbols != NO_DEBUG)
find_loop_tree_blocks ();
/* Determine if the function has indirect jump. On some systems
this prevents low overhead loop instructions from being used. */
indirect_jump_in_function = indirect_jump_in_function_p (f);
/* Now scan the loops, last ones first, since this means inner ones are done
before outer ones. */
for (i = max_loop_num-1; i >= 0; i--)
if (! loop_invalid[i] && loop_number_loop_ends[i])
scan_loop (loop_number_loop_starts[i], loop_number_loop_ends[i],
loop_number_loop_cont[i], unroll_p, bct_p);
/* If debugging and unrolling loops, we must replicate the tree nodes
corresponding to the blocks inside the loop, so that the original one
to one mapping will remain. */
if (unroll_p && write_symbols != NO_DEBUG)
unroll_block_trees ();
end_alias_analysis ();
}
/* Returns the next insn, in execution order, after INSN. START and
END are the NOTE_INSN_LOOP_BEG and NOTE_INSN_LOOP_END for the loop,
respectively. LOOP_TOP, if non-NULL, is the top of the loop in the
insn-stream; it is used with loops that are entered near the
bottom. */
static rtx
next_insn_in_loop (insn, start, end, loop_top)
rtx insn;
rtx start;
rtx end;
rtx loop_top;
{
insn = NEXT_INSN (insn);
if (insn == end)
{
if (loop_top)
/* Go to the top of the loop, and continue there. */
insn = loop_top;
else
/* We're done. */
insn = NULL_RTX;
}
if (insn == start)
/* We're done. */
insn = NULL_RTX;
return insn;
}
/* Optimize one loop whose start is LOOP_START and end is END.
LOOP_START is the NOTE_INSN_LOOP_BEG and END is the matching
NOTE_INSN_LOOP_END.
LOOP_CONT is the NOTE_INSN_LOOP_CONT. */
/* ??? Could also move memory writes out of loops if the destination address
is invariant, the source is invariant, the memory write is not volatile,
and if we can prove that no read inside the loop can read this address
before the write occurs. If there is a read of this address after the
write, then we can also mark the memory read as invariant. */
static void
scan_loop (loop_start, end, loop_cont, unroll_p, bct_p)
rtx loop_start, end, loop_cont;
int unroll_p, bct_p;
{
register int i;
rtx p;
/* 1 if we are scanning insns that could be executed zero times. */
int maybe_never = 0;
/* 1 if we are scanning insns that might never be executed
due to a subroutine call which might exit before they are reached. */
int call_passed = 0;
/* For a rotated loop that is entered near the bottom,
this is the label at the top. Otherwise it is zero. */
rtx loop_top = 0;
/* Jump insn that enters the loop, or 0 if control drops in. */
rtx loop_entry_jump = 0;
/* Place in the loop where control enters. */
rtx scan_start;
/* Number of insns in the loop. */
int insn_count;
int in_libcall = 0;
int tem;
rtx temp;
/* The SET from an insn, if it is the only SET in the insn. */
rtx set, set1;
/* Chain describing insns movable in current loop. */
struct movable *movables = 0;
/* Last element in `movables' -- so we can add elements at the end. */
struct movable *last_movable = 0;
/* Ratio of extra register life span we can justify
for saving an instruction. More if loop doesn't call subroutines
since in that case saving an insn makes more difference
and more registers are available. */
int threshold;
/* Nonzero if we are scanning instructions in a sub-loop. */
int loop_depth = 0;
int nregs;
/* Determine whether this loop starts with a jump down to a test at
the end. This will occur for a small number of loops with a test
that is too complex to duplicate in front of the loop.
We search for the first insn or label in the loop, skipping NOTEs.
However, we must be careful not to skip past a NOTE_INSN_LOOP_BEG
(because we might have a loop executed only once that contains a
loop which starts with a jump to its exit test) or a NOTE_INSN_LOOP_END
(in case we have a degenerate loop).
Note that if we mistakenly think that a loop is entered at the top
when, in fact, it is entered at the exit test, the only effect will be
slightly poorer optimization. Making the opposite error can generate
incorrect code. Since very few loops now start with a jump to the
exit test, the code here to detect that case is very conservative. */
for (p = NEXT_INSN (loop_start);
p != end
&& GET_CODE (p) != CODE_LABEL && GET_RTX_CLASS (GET_CODE (p)) != 'i'
&& (GET_CODE (p) != NOTE
|| (NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_BEG
&& NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_END));
p = NEXT_INSN (p))
;
scan_start = p;
/* Set up variables describing this loop. */
prescan_loop (loop_start, end);
threshold = (loop_has_call ? 1 : 2) * (1 + n_non_fixed_regs);
/* If loop has a jump before the first label,
the true entry is the target of that jump.
Start scan from there.
But record in LOOP_TOP the place where the end-test jumps
back to so we can scan that after the end of the loop. */
if (GET_CODE (p) == JUMP_INSN)
{
loop_entry_jump = p;
/* Loop entry must be unconditional jump (and not a RETURN) */
if (simplejump_p (p)
&& JUMP_LABEL (p) != 0
/* Check to see whether the jump actually
jumps out of the loop (meaning it's no loop).
This case can happen for things like
do {..} while (0). If this label was generated previously
by loop, we can't tell anything about it and have to reject
the loop. */
&& INSN_IN_RANGE_P (JUMP_LABEL (p), loop_start, end))
{
loop_top = next_label (scan_start);
scan_start = JUMP_LABEL (p);
}
}
/* If SCAN_START was an insn created by loop, we don't know its luid
as required by loop_reg_used_before_p. So skip such loops. (This
test may never be true, but it's best to play it safe.)
Also, skip loops where we do not start scanning at a label. This
test also rejects loops starting with a JUMP_INSN that failed the
test above. */
if (INSN_UID (scan_start) >= max_uid_for_loop
|| GET_CODE (scan_start) != CODE_LABEL)
{
if (loop_dump_stream)
fprintf (loop_dump_stream, "\nLoop from %d to %d is phony.\n\n",
INSN_UID (loop_start), INSN_UID (end));
return;
}
/* Count number of times each reg is set during this loop.
Set VARRAY_CHAR (may_not_optimize, I) if it is not safe to move out
the setting of register I. Set VARRAY_RTX (reg_single_usage, I). */
/* Allocate extra space for REGS that might be created by
load_mems. We allocate a little extra slop as well, in the hopes
that even after the moving of movables creates some new registers
we won't have to reallocate these arrays. However, we do grow
the arrays, if necessary, in load_mems_recount_loop_regs_set. */
nregs = max_reg_num () + loop_mems_idx + 16;
VARRAY_INT_INIT (set_in_loop, nregs, "set_in_loop");
VARRAY_INT_INIT (n_times_set, nregs, "n_times_set");
VARRAY_CHAR_INIT (may_not_optimize, nregs, "may_not_optimize");
VARRAY_RTX_INIT (reg_single_usage, nregs, "reg_single_usage");
count_loop_regs_set (loop_top ? loop_top : loop_start, end,
may_not_optimize, reg_single_usage, &insn_count, nregs);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
VARRAY_CHAR (may_not_optimize, i) = 1;
VARRAY_INT (set_in_loop, i) = 1;
}
#ifdef AVOID_CCMODE_COPIES
/* Don't try to move insns which set CC registers if we should not
create CCmode register copies. */
for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
if (GET_MODE_CLASS (GET_MODE (regno_reg_rtx[i])) == MODE_CC)
VARRAY_CHAR (may_not_optimize, i) = 1;
#endif
bcopy ((char *) &set_in_loop->data,
(char *) &n_times_set->data, nregs * sizeof (int));
if (loop_dump_stream)
{
fprintf (loop_dump_stream, "\nLoop from %d to %d: %d real insns.\n",
INSN_UID (loop_start), INSN_UID (end), insn_count);
if (loop_continue)
fprintf (loop_dump_stream, "Continue at insn %d.\n",
INSN_UID (loop_continue));
}
/* Scan through the loop finding insns that are safe to move.
Set set_in_loop negative for the reg being set, so that
this reg will be considered invariant for subsequent insns.
We consider whether subsequent insns use the reg
in deciding whether it is worth actually moving.
MAYBE_NEVER is nonzero if we have passed a conditional jump insn
and therefore it is possible that the insns we are scanning
would never be executed. At such times, we must make sure
that it is safe to execute the insn once instead of zero times.
When MAYBE_NEVER is 0, all insns will be executed at least once
so that is not a problem. */
for (p = next_insn_in_loop (scan_start, scan_start, end, loop_top);
p != NULL_RTX;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& find_reg_note (p, REG_LIBCALL, NULL_RTX))
in_libcall = 1;
else if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& find_reg_note (p, REG_RETVAL, NULL_RTX))
in_libcall = 0;
if (GET_CODE (p) == INSN
&& (set = single_set (p))
&& GET_CODE (SET_DEST (set)) == REG
&& ! VARRAY_CHAR (may_not_optimize, REGNO (SET_DEST (set))))
{
int tem1 = 0;
int tem2 = 0;
int move_insn = 0;
rtx src = SET_SRC (set);
rtx dependencies = 0;
/* Figure out what to use as a source of this insn. If a REG_EQUIV
note is given or if a REG_EQUAL note with a constant operand is
specified, use it as the source and mark that we should move
this insn by calling emit_move_insn rather that duplicating the
insn.
Otherwise, only use the REG_EQUAL contents if a REG_RETVAL note
is present. */
temp = find_reg_note (p, REG_EQUIV, NULL_RTX);
if (temp)
src = XEXP (temp, 0), move_insn = 1;
else
{
temp = find_reg_note (p, REG_EQUAL, NULL_RTX);
if (temp && CONSTANT_P (XEXP (temp, 0)))
src = XEXP (temp, 0), move_insn = 1;
if (temp && find_reg_note (p, REG_RETVAL, NULL_RTX))
{
src = XEXP (temp, 0);
/* A libcall block can use regs that don't appear in
the equivalent expression. To move the libcall,
we must move those regs too. */
dependencies = libcall_other_reg (p, src);
}
}
/* Don't try to optimize a register that was made
by loop-optimization for an inner loop.
We don't know its life-span, so we can't compute the benefit. */
if (REGNO (SET_DEST (set)) >= max_reg_before_loop)
;
else if (/* The register is used in basic blocks other
than the one where it is set (meaning that
something after this point in the loop might
depend on its value before the set). */
! reg_in_basic_block_p (p, SET_DEST (set))
/* And the set is not guaranteed to be executed one
the loop starts, or the value before the set is
needed before the set occurs...
??? Note we have quadratic behaviour here, mitigated
by the fact that the previous test will often fail for
large loops. Rather than re-scanning the entire loop
each time for register usage, we should build tables
of the register usage and use them here instead. */
&& (maybe_never
|| loop_reg_used_before_p (set, p, loop_start,
scan_start, end)))
/* It is unsafe to move the set.
This code used to consider it OK to move a set of a variable
which was not created by the user and not used in an exit test.
That behavior is incorrect and was removed. */
;
else if ((tem = invariant_p (src))
&& (dependencies == 0
|| (tem2 = invariant_p (dependencies)) != 0)
&& (VARRAY_INT (set_in_loop,
REGNO (SET_DEST (set))) == 1
|| (tem1
= consec_sets_invariant_p
(SET_DEST (set),
VARRAY_INT (set_in_loop, REGNO (SET_DEST (set))),
p)))
/* If the insn can cause a trap (such as divide by zero),
can't move it unless it's guaranteed to be executed
once loop is entered. Even a function call might
prevent the trap insn from being reached
(since it might exit!) */
&& ! ((maybe_never || call_passed)
&& may_trap_p (src)))
{
register struct movable *m;
register int regno = REGNO (SET_DEST (set));
/* A potential lossage is where we have a case where two insns
can be combined as long as they are both in the loop, but
we move one of them outside the loop. For large loops,
this can lose. The most common case of this is the address
of a function being called.
Therefore, if this register is marked as being used exactly
once if we are in a loop with calls (a "large loop"), see if
we can replace the usage of this register with the source
of this SET. If we can, delete this insn.
Don't do this if P has a REG_RETVAL note or if we have
SMALL_REGISTER_CLASSES and SET_SRC is a hard register. */
if (loop_has_call
&& VARRAY_RTX (reg_single_usage, regno) != 0
&& VARRAY_RTX (reg_single_usage, regno) != const0_rtx
&& REGNO_FIRST_UID (regno) == INSN_UID (p)
&& (REGNO_LAST_UID (regno)
== INSN_UID (VARRAY_RTX (reg_single_usage, regno)))
&& VARRAY_INT (set_in_loop, regno) == 1
&& ! side_effects_p (SET_SRC (set))
&& ! find_reg_note (p, REG_RETVAL, NULL_RTX)
&& (! SMALL_REGISTER_CLASSES
|| (! (GET_CODE (SET_SRC (set)) == REG
&& REGNO (SET_SRC (set)) < FIRST_PSEUDO_REGISTER)))
/* This test is not redundant; SET_SRC (set) might be
a call-clobbered register and the life of REGNO
might span a call. */
&& ! modified_between_p (SET_SRC (set), p,
VARRAY_RTX
(reg_single_usage, regno))
&& no_labels_between_p (p, VARRAY_RTX (reg_single_usage, regno))
&& validate_replace_rtx (SET_DEST (set), SET_SRC (set),
VARRAY_RTX
(reg_single_usage, regno)))
{
/* Replace any usage in a REG_EQUAL note. Must copy the
new source, so that we don't get rtx sharing between the
SET_SOURCE and REG_NOTES of insn p. */
REG_NOTES (VARRAY_RTX (reg_single_usage, regno))
= replace_rtx (REG_NOTES (VARRAY_RTX
(reg_single_usage, regno)),
SET_DEST (set), copy_rtx (SET_SRC (set)));
PUT_CODE (p, NOTE);
NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (p) = 0;
VARRAY_INT (set_in_loop, regno) = 0;
continue;
}
m = (struct movable *) alloca (sizeof (struct movable));
m->next = 0;
m->insn = p;
m->set_src = src;
m->dependencies = dependencies;
m->set_dest = SET_DEST (set);
m->force = 0;
m->consec = VARRAY_INT (set_in_loop,
REGNO (SET_DEST (set))) - 1;
m->done = 0;
m->forces = 0;
m->partial = 0;
m->move_insn = move_insn;
m->move_insn_first = 0;
m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0);
m->savemode = VOIDmode;
m->regno = regno;
/* Set M->cond if either invariant_p or consec_sets_invariant_p
returned 2 (only conditionally invariant). */
m->cond = ((tem | tem1 | tem2) > 1);
m->global = (uid_luid[REGNO_LAST_UID (regno)] > INSN_LUID (end)
|| uid_luid[REGNO_FIRST_UID (regno)] < INSN_LUID (loop_start));
m->match = 0;
m->lifetime = (uid_luid[REGNO_LAST_UID (regno)]
- uid_luid[REGNO_FIRST_UID (regno)]);
m->savings = VARRAY_INT (n_times_set, regno);
if (find_reg_note (p, REG_RETVAL, NULL_RTX))
m->savings += libcall_benefit (p);
VARRAY_INT (set_in_loop, regno) = move_insn ? -2 : -1;
/* Add M to the end of the chain MOVABLES. */
if (movables == 0)
movables = m;
else
last_movable->next = m;
last_movable = m;
if (m->consec > 0)
{
/* It is possible for the first instruction to have a
REG_EQUAL note but a non-invariant SET_SRC, so we must
remember the status of the first instruction in case
the last instruction doesn't have a REG_EQUAL note. */
m->move_insn_first = m->move_insn;
/* Skip this insn, not checking REG_LIBCALL notes. */
p = next_nonnote_insn (p);
/* Skip the consecutive insns, if there are any. */
p = skip_consec_insns (p, m->consec);
/* Back up to the last insn of the consecutive group. */
p = prev_nonnote_insn (p);
/* We must now reset m->move_insn, m->is_equiv, and possibly
m->set_src to correspond to the effects of all the
insns. */
temp = find_reg_note (p, REG_EQUIV, NULL_RTX);
if (temp)
m->set_src = XEXP (temp, 0), m->move_insn = 1;
else
{
temp = find_reg_note (p, REG_EQUAL, NULL_RTX);
if (temp && CONSTANT_P (XEXP (temp, 0)))
m->set_src = XEXP (temp, 0), m->move_insn = 1;
else
m->move_insn = 0;
}
m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0);
}
}
/* If this register is always set within a STRICT_LOW_PART
or set to zero, then its high bytes are constant.
So clear them outside the loop and within the loop
just load the low bytes.
We must check that the machine has an instruction to do so.
Also, if the value loaded into the register
depends on the same register, this cannot be done. */
else if (SET_SRC (set) == const0_rtx
&& GET_CODE (NEXT_INSN (p)) == INSN
&& (set1 = single_set (NEXT_INSN (p)))
&& GET_CODE (set1) == SET
&& (GET_CODE (SET_DEST (set1)) == STRICT_LOW_PART)
&& (GET_CODE (XEXP (SET_DEST (set1), 0)) == SUBREG)
&& (SUBREG_REG (XEXP (SET_DEST (set1), 0))
== SET_DEST (set))
&& !reg_mentioned_p (SET_DEST (set), SET_SRC (set1)))
{
register int regno = REGNO (SET_DEST (set));
if (VARRAY_INT (set_in_loop, regno) == 2)
{
register struct movable *m;
m = (struct movable *) alloca (sizeof (struct movable));
m->next = 0;
m->insn = p;
m->set_dest = SET_DEST (set);
m->dependencies = 0;
m->force = 0;
m->consec = 0;
m->done = 0;
m->forces = 0;
m->move_insn = 0;
m->move_insn_first = 0;
m->partial = 1;
/* If the insn may not be executed on some cycles,
we can't clear the whole reg; clear just high part.
Not even if the reg is used only within this loop.
Consider this:
while (1)
while (s != t) {
if (foo ()) x = *s;
use (x);
}
Clearing x before the inner loop could clobber a value
being saved from the last time around the outer loop.
However, if the reg is not used outside this loop
and all uses of the register are in the same
basic block as the store, there is no problem.
If this insn was made by loop, we don't know its
INSN_LUID and hence must make a conservative
assumption. */
m->global = (INSN_UID (p) >= max_uid_for_loop
|| (uid_luid[REGNO_LAST_UID (regno)]
> INSN_LUID (end))
|| (uid_luid[REGNO_FIRST_UID (regno)]
< INSN_LUID (p))
|| (labels_in_range_p
(p, uid_luid[REGNO_FIRST_UID (regno)])));
if (maybe_never && m->global)
m->savemode = GET_MODE (SET_SRC (set1));
else
m->savemode = VOIDmode;
m->regno = regno;
m->cond = 0;
m->match = 0;
m->lifetime = (uid_luid[REGNO_LAST_UID (regno)]
- uid_luid[REGNO_FIRST_UID (regno)]);
m->savings = 1;
VARRAY_INT (set_in_loop, regno) = -1;
/* Add M to the end of the chain MOVABLES. */
if (movables == 0)
movables = m;
else
last_movable->next = m;
last_movable = m;
}
}
}
/* Past a call insn, we get to insns which might not be executed
because the call might exit. This matters for insns that trap.
Call insns inside a REG_LIBCALL/REG_RETVAL block always return,
so they don't count. */
else if (GET_CODE (p) == CALL_INSN && ! in_libcall)
call_passed = 1;
/* Past a label or a jump, we get to insns for which we
can't count on whether or how many times they will be
executed during each iteration. Therefore, we can
only move out sets of trivial variables
(those not used after the loop). */
/* Similar code appears twice in strength_reduce. */
else if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
/* If we enter the loop in the middle, and scan around to the
beginning, don't set maybe_never for that. This must be an
unconditional jump, otherwise the code at the top of the
loop might never be executed. Unconditional jumps are
followed a by barrier then loop end. */
&& ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top
&& NEXT_INSN (NEXT_INSN (p)) == end
&& simplejump_p (p)))
maybe_never = 1;
else if (GET_CODE (p) == NOTE)
{
/* At the virtual top of a converted loop, insns are again known to
be executed: logically, the loop begins here even though the exit
code has been duplicated. */
if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP && loop_depth == 0)
maybe_never = call_passed = 0;
else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
loop_depth++;
else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
loop_depth--;
}
}
/* If one movable subsumes another, ignore that other. */
ignore_some_movables (movables);
/* For each movable insn, see if the reg that it loads
leads when it dies right into another conditionally movable insn.
If so, record that the second insn "forces" the first one,
since the second can be moved only if the first is. */
force_movables (movables);
/* See if there are multiple movable insns that load the same value.
If there are, make all but the first point at the first one
through the `match' field, and add the priorities of them
all together as the priority of the first. */
combine_movables (movables, nregs);
/* Now consider each movable insn to decide whether it is worth moving.
Store 0 in set_in_loop for each reg that is moved.
Generally this increases code size, so do not move moveables when
optimizing for code size. */
if (! optimize_size)
move_movables (movables, threshold,
insn_count, loop_start, end, nregs);
/* Now candidates that still are negative are those not moved.
Change set_in_loop to indicate that those are not actually invariant. */
for (i = 0; i < nregs; i++)
if (VARRAY_INT (set_in_loop, i) < 0)
VARRAY_INT (set_in_loop, i) = VARRAY_INT (n_times_set, i);
/* Now that we've moved some things out of the loop, we might be able to
hoist even more memory references. */
load_mems_and_recount_loop_regs_set (scan_start, end, loop_top,
loop_start, &insn_count);
if (flag_strength_reduce)
{
the_movables = movables;
strength_reduce (scan_start, end, loop_top,
insn_count, loop_start, end, loop_cont, unroll_p, bct_p);
}
VARRAY_FREE (reg_single_usage);
VARRAY_FREE (set_in_loop);
VARRAY_FREE (n_times_set);
VARRAY_FREE (may_not_optimize);
}
/* Add elements to *OUTPUT to record all the pseudo-regs
mentioned in IN_THIS but not mentioned in NOT_IN_THIS. */
void
record_excess_regs (in_this, not_in_this, output)
rtx in_this, not_in_this;
rtx *output;
{
enum rtx_code code;
char *fmt;
int i;
code = GET_CODE (in_this);
switch (code)
{
case PC:
case CC0:
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return;
case REG:
if (REGNO (in_this) >= FIRST_PSEUDO_REGISTER
&& ! reg_mentioned_p (in_this, not_in_this))
*output = gen_rtx_EXPR_LIST (VOIDmode, in_this, *output);
return;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
int j;
switch (fmt[i])
{
case 'E':
for (j = 0; j < XVECLEN (in_this, i); j++)
record_excess_regs (XVECEXP (in_this, i, j), not_in_this, output);
break;
case 'e':
record_excess_regs (XEXP (in_this, i), not_in_this, output);
break;
}
}
}
/* Check what regs are referred to in the libcall block ending with INSN,
aside from those mentioned in the equivalent value.
If there are none, return 0.
If there are one or more, return an EXPR_LIST containing all of them. */
rtx
libcall_other_reg (insn, equiv)
rtx insn, equiv;
{
rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
rtx p = XEXP (note, 0);
rtx output = 0;
/* First, find all the regs used in the libcall block
that are not mentioned as inputs to the result. */
while (p != insn)
{
if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN)
record_excess_regs (PATTERN (p), equiv, &output);
p = NEXT_INSN (p);
}
return output;
}
/* Return 1 if all uses of REG
are between INSN and the end of the basic block. */
static int
reg_in_basic_block_p (insn, reg)
rtx insn, reg;
{
int regno = REGNO (reg);
rtx p;
if (REGNO_FIRST_UID (regno) != INSN_UID (insn))
return 0;
/* Search this basic block for the already recorded last use of the reg. */
for (p = insn; p; p = NEXT_INSN (p))
{
switch (GET_CODE (p))
{
case NOTE:
break;
case INSN:
case CALL_INSN:
/* Ordinary insn: if this is the last use, we win. */
if (REGNO_LAST_UID (regno) == INSN_UID (p))
return 1;
break;
case JUMP_INSN:
/* Jump insn: if this is the last use, we win. */
if (REGNO_LAST_UID (regno) == INSN_UID (p))
return 1;
/* Otherwise, it's the end of the basic block, so we lose. */
return 0;
case CODE_LABEL:
case BARRIER:
/* It's the end of the basic block, so we lose. */
return 0;
default:
break;
}
}
/* The "last use" doesn't follow the "first use"?? */
abort ();
}
/* Compute the benefit of eliminating the insns in the block whose
last insn is LAST. This may be a group of insns used to compute a
value directly or can contain a library call. */
static int
libcall_benefit (last)
rtx last;
{
rtx insn;
int benefit = 0;
for (insn = XEXP (find_reg_note (last, REG_RETVAL, NULL_RTX), 0);
insn != last; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == CALL_INSN)
benefit += 10; /* Assume at least this many insns in a library
routine. */
else if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) != USE
&& GET_CODE (PATTERN (insn)) != CLOBBER)
benefit++;
}
return benefit;
}
/* Skip COUNT insns from INSN, counting library calls as 1 insn. */
static rtx
skip_consec_insns (insn, count)
rtx insn;
int count;
{
for (; count > 0; count--)
{
rtx temp;
/* If first insn of libcall sequence, skip to end. */
/* Do this at start of loop, since INSN is guaranteed to
be an insn here. */
if (GET_CODE (insn) != NOTE
&& (temp = find_reg_note (insn, REG_LIBCALL, NULL_RTX)))
insn = XEXP (temp, 0);
do insn = NEXT_INSN (insn);
while (GET_CODE (insn) == NOTE);
}
return insn;
}
/* Ignore any movable whose insn falls within a libcall
which is part of another movable.
We make use of the fact that the movable for the libcall value
was made later and so appears later on the chain. */
static void
ignore_some_movables (movables)
struct movable *movables;
{
register struct movable *m, *m1;
for (m = movables; m; m = m->next)
{
/* Is this a movable for the value of a libcall? */
rtx note = find_reg_note (m->insn, REG_RETVAL, NULL_RTX);
if (note)
{
rtx insn;
/* Check for earlier movables inside that range,
and mark them invalid. We cannot use LUIDs here because
insns created by loop.c for prior loops don't have LUIDs.
Rather than reject all such insns from movables, we just
explicitly check each insn in the libcall (since invariant
libcalls aren't that common). */
for (insn = XEXP (note, 0); insn != m->insn; insn = NEXT_INSN (insn))
for (m1 = movables; m1 != m; m1 = m1->next)
if (m1->insn == insn)
m1->done = 1;
}
}
}
/* For each movable insn, see if the reg that it loads
leads when it dies right into another conditionally movable insn.
If so, record that the second insn "forces" the first one,
since the second can be moved only if the first is. */
static void
force_movables (movables)
struct movable *movables;
{
register struct movable *m, *m1;
for (m1 = movables; m1; m1 = m1->next)
/* Omit this if moving just the (SET (REG) 0) of a zero-extend. */
if (!m1->partial && !m1->done)
{
int regno = m1->regno;
for (m = m1->next; m; m = m->next)
/* ??? Could this be a bug? What if CSE caused the
register of M1 to be used after this insn?
Since CSE does not update regno_last_uid,
this insn M->insn might not be where it dies.
But very likely this doesn't matter; what matters is
that M's reg is computed from M1's reg. */
if (INSN_UID (m->insn) == REGNO_LAST_UID (regno)
&& !m->done)
break;
if (m != 0 && m->set_src == m1->set_dest
/* If m->consec, m->set_src isn't valid. */
&& m->consec == 0)
m = 0;
/* Increase the priority of the moving the first insn
since it permits the second to be moved as well. */
if (m != 0)
{
m->forces = m1;
m1->lifetime += m->lifetime;
m1->savings += m->savings;
}
}
}
/* Find invariant expressions that are equal and can be combined into
one register. */
static void
combine_movables (movables, nregs)
struct movable *movables;
int nregs;
{
register struct movable *m;
char *matched_regs = (char *) alloca (nregs);
enum machine_mode mode;
/* Regs that are set more than once are not allowed to match
or be matched. I'm no longer sure why not. */
/* Perhaps testing m->consec_sets would be more appropriate here? */
for (m = movables; m; m = m->next)
if (m->match == 0 && VARRAY_INT (n_times_set, m->regno) == 1 && !m->partial)
{
register struct movable *m1;
int regno = m->regno;
bzero (matched_regs, nregs);
matched_regs[regno] = 1;
/* We want later insns to match the first one. Don't make the first
one match any later ones. So start this loop at m->next. */
for (m1 = m->next; m1; m1 = m1->next)
if (m != m1 && m1->match == 0 && VARRAY_INT (n_times_set, m1->regno) == 1
/* A reg used outside the loop mustn't be eliminated. */
&& !m1->global
/* A reg used for zero-extending mustn't be eliminated. */
&& !m1->partial
&& (matched_regs[m1->regno]
||
(
/* Can combine regs with different modes loaded from the
same constant only if the modes are the same or
if both are integer modes with M wider or the same
width as M1. The check for integer is redundant, but
safe, since the only case of differing destination
modes with equal sources is when both sources are
VOIDmode, i.e., CONST_INT. */
(GET_MODE (m->set_dest) == GET_MODE (m1->set_dest)
|| (GET_MODE_CLASS (GET_MODE (m->set_dest)) == MODE_INT
&& GET_MODE_CLASS (GET_MODE (m1->set_dest)) == MODE_INT
&& (GET_MODE_BITSIZE (GET_MODE (m->set_dest))
>= GET_MODE_BITSIZE (GET_MODE (m1->set_dest)))))
/* See if the source of M1 says it matches M. */
&& ((GET_CODE (m1->set_src) == REG
&& matched_regs[REGNO (m1->set_src)])
|| rtx_equal_for_loop_p (m->set_src, m1->set_src,
movables))))
&& ((m->dependencies == m1->dependencies)
|| rtx_equal_p (m->dependencies, m1->dependencies)))
{
m->lifetime += m1->lifetime;
m->savings += m1->savings;
m1->done = 1;
m1->match = m;
matched_regs[m1->regno] = 1;
}
}
/* Now combine the regs used for zero-extension.
This can be done for those not marked `global'
provided their lives don't overlap. */
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
register struct movable *m0 = 0;
/* Combine all the registers for extension from mode MODE.
Don't combine any that are used outside this loop. */
for (m = movables; m; m = m->next)
if (m->partial && ! m->global
&& mode == GET_MODE (SET_SRC (PATTERN (NEXT_INSN (m->insn)))))
{
register struct movable *m1;
int first = uid_luid[REGNO_FIRST_UID (m->regno)];
int last = uid_luid[REGNO_LAST_UID (m->regno)];
if (m0 == 0)
{
/* First one: don't check for overlap, just record it. */
m0 = m;
continue;
}
/* Make sure they extend to the same mode.
(Almost always true.) */
if (GET_MODE (m->set_dest) != GET_MODE (m0->set_dest))
continue;
/* We already have one: check for overlap with those
already combined together. */
for (m1 = movables; m1 != m; m1 = m1->next)
if (m1 == m0 || (m1->partial && m1->match == m0))
if (! (uid_luid[REGNO_FIRST_UID (m1->regno)] > last
|| uid_luid[REGNO_LAST_UID (m1->regno)] < first))
goto overlap;
/* No overlap: we can combine this with the others. */
m0->lifetime += m->lifetime;
m0->savings += m->savings;
m->done = 1;
m->match = m0;
overlap: ;
}
}
}
/* Return 1 if regs X and Y will become the same if moved. */
static int
regs_match_p (x, y, movables)
rtx x, y;
struct movable *movables;
{
int xn = REGNO (x);
int yn = REGNO (y);
struct movable *mx, *my;
for (mx = movables; mx; mx = mx->next)
if (mx->regno == xn)
break;
for (my = movables; my; my = my->next)
if (my->regno == yn)
break;
return (mx && my
&& ((mx->match == my->match && mx->match != 0)
|| mx->match == my
|| mx == my->match));
}
/* Return 1 if X and Y are identical-looking rtx's.
This is the Lisp function EQUAL for rtx arguments.
If two registers are matching movables or a movable register and an
equivalent constant, consider them equal. */
static int
rtx_equal_for_loop_p (x, y, movables)
rtx x, y;
struct movable *movables;
{
register int i;
register int j;
register struct movable *m;
register enum rtx_code code;
register char *fmt;
if (x == y)
return 1;
if (x == 0 || y == 0)
return 0;
code = GET_CODE (x);
/* If we have a register and a constant, they may sometimes be
equal. */
if (GET_CODE (x) == REG && VARRAY_INT (set_in_loop, REGNO (x)) == -2
&& CONSTANT_P (y))
{
for (m = movables; m; m = m->next)
if (m->move_insn && m->regno == REGNO (x)
&& rtx_equal_p (m->set_src, y))
return 1;
}
else if (GET_CODE (y) == REG && VARRAY_INT (set_in_loop, REGNO (y)) == -2
&& CONSTANT_P (x))
{
for (m = movables; m; m = m->next)
if (m->move_insn && m->regno == REGNO (y)
&& rtx_equal_p (m->set_src, x))
return 1;
}
/* Otherwise, rtx's of different codes cannot be equal. */
if (code != GET_CODE (y))
return 0;
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
(REG:SI x) and (REG:HI x) are NOT equivalent. */
if (GET_MODE (x) != GET_MODE (y))
return 0;
/* These three types of rtx's can be compared nonrecursively. */
if (code == REG)
return (REGNO (x) == REGNO (y) || regs_match_p (x, y, movables));
if (code == LABEL_REF)
return XEXP (x, 0) == XEXP (y, 0);
if (code == SYMBOL_REF)
return XSTR (x, 0) == XSTR (y, 0);
/* Compare the elements. If any pair of corresponding elements
fail to match, return 0 for the whole things. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'w':
if (XWINT (x, i) != XWINT (y, i))
return 0;
break;
case 'i':
if (XINT (x, i) != XINT (y, i))
return 0;
break;
case 'E':
/* Two vectors must have the same length. */
if (XVECLEN (x, i) != XVECLEN (y, i))
return 0;
/* And the corresponding elements must match. */
for (j = 0; j < XVECLEN (x, i); j++)
if (rtx_equal_for_loop_p (XVECEXP (x, i, j), XVECEXP (y, i, j), movables) == 0)
return 0;
break;
case 'e':
if (rtx_equal_for_loop_p (XEXP (x, i), XEXP (y, i), movables) == 0)
return 0;
break;
case 's':
if (strcmp (XSTR (x, i), XSTR (y, i)))
return 0;
break;
case 'u':
/* These are just backpointers, so they don't matter. */
break;
case '0':
break;
/* It is believed that rtx's at this level will never
contain anything but integers and other rtx's,
except for within LABEL_REFs and SYMBOL_REFs. */
default:
abort ();
}
}
return 1;
}
/* If X contains any LABEL_REF's, add REG_LABEL notes for them to all
insns in INSNS which use thet reference. */
static void
add_label_notes (x, insns)
rtx x;
rtx insns;
{
enum rtx_code code = GET_CODE (x);
int i, j;
char *fmt;
rtx insn;
if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x))
{
/* This code used to ignore labels that referred to dispatch tables to
avoid flow generating (slighly) worse code.
We no longer ignore such label references (see LABEL_REF handling in
mark_jump_label for additional information). */
for (insn = insns; insn; insn = NEXT_INSN (insn))
if (reg_mentioned_p (XEXP (x, 0), insn))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL, XEXP (x, 0),
REG_NOTES (insn));
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
add_label_notes (XEXP (x, i), insns);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
add_label_notes (XVECEXP (x, i, j), insns);
}
}
/* Scan MOVABLES, and move the insns that deserve to be moved.
If two matching movables are combined, replace one reg with the
other throughout. */
static void
move_movables (movables, threshold, insn_count, loop_start, end, nregs)
struct movable *movables;
int threshold;
int insn_count;
rtx loop_start;
rtx end;
int nregs;
{
rtx new_start = 0;
register struct movable *m;
register rtx p;
/* Map of pseudo-register replacements to handle combining
when we move several insns that load the same value
into different pseudo-registers. */
rtx *reg_map = (rtx *) alloca (nregs * sizeof (rtx));
char *already_moved = (char *) alloca (nregs);
bzero (already_moved, nregs);
bzero ((char *) reg_map, nregs * sizeof (rtx));
num_movables = 0;
for (m = movables; m; m = m->next)
{
/* Describe this movable insn. */
if (loop_dump_stream)
{
fprintf (loop_dump_stream, "Insn %d: regno %d (life %d), ",
INSN_UID (m->insn), m->regno, m->lifetime);
if (m->consec > 0)
fprintf (loop_dump_stream, "consec %d, ", m->consec);
if (m->cond)
fprintf (loop_dump_stream, "cond ");
if (m->force)
fprintf (loop_dump_stream, "force ");
if (m->global)
fprintf (loop_dump_stream, "global ");
if (m->done)
fprintf (loop_dump_stream, "done ");
if (m->move_insn)
fprintf (loop_dump_stream, "move-insn ");
if (m->match)
fprintf (loop_dump_stream, "matches %d ",
INSN_UID (m->match->insn));
if (m->forces)
fprintf (loop_dump_stream, "forces %d ",
INSN_UID (m->forces->insn));
}
/* Count movables. Value used in heuristics in strength_reduce. */
num_movables++;
/* Ignore the insn if it's already done (it matched something else).
Otherwise, see if it is now safe to move. */
if (!m->done
&& (! m->cond
|| (1 == invariant_p (m->set_src)
&& (m->dependencies == 0
|| 1 == invariant_p (m->dependencies))
&& (m->consec == 0
|| 1 == consec_sets_invariant_p (m->set_dest,
m->consec + 1,
m->insn))))
&& (! m->forces || m->forces->done))
{
register int regno;
register rtx p;
int savings = m->savings;
/* We have an insn that is safe to move.
Compute its desirability. */
p = m->insn;
regno = m->regno;
if (loop_dump_stream)
fprintf (loop_dump_stream, "savings %d ", savings);
if (moved_once[regno] && loop_dump_stream)
fprintf (loop_dump_stream, "halved since already moved ");
/* An insn MUST be moved if we already moved something else
which is safe only if this one is moved too: that is,
if already_moved[REGNO] is nonzero. */
/* An insn is desirable to move if the new lifetime of the
register is no more than THRESHOLD times the old lifetime.
If it's not desirable, it means the loop is so big
that moving won't speed things up much,
and it is liable to make register usage worse. */
/* It is also desirable to move if it can be moved at no
extra cost because something else was already moved. */
if (already_moved[regno]
|| flag_move_all_movables
|| (threshold * savings * m->lifetime) >=
(moved_once[regno] ? insn_count * 2 : insn_count)
|| (m->forces && m->forces->done
&& VARRAY_INT (n_times_set, m->forces->regno) == 1))
{
int count;
register struct movable *m1;
rtx first;
/* Now move the insns that set the reg. */
if (m->partial && m->match)
{
rtx newpat, i1;
rtx r1, r2;
/* Find the end of this chain of matching regs.
Thus, we load each reg in the chain from that one reg.
And that reg is loaded with 0 directly,
since it has ->match == 0. */
for (m1 = m; m1->match; m1 = m1->match);
newpat = gen_move_insn (SET_DEST (PATTERN (m->insn)),
SET_DEST (PATTERN (m1->insn)));
i1 = emit_insn_before (newpat, loop_start);
/* Mark the moved, invariant reg as being allowed to
share a hard reg with the other matching invariant. */
REG_NOTES (i1) = REG_NOTES (m->insn);
r1 = SET_DEST (PATTERN (m->insn));
r2 = SET_DEST (PATTERN (m1->insn));
regs_may_share
= gen_rtx_EXPR_LIST (VOIDmode, r1,
gen_rtx_EXPR_LIST (VOIDmode, r2,
regs_may_share));
delete_insn (m->insn);
if (new_start == 0)
new_start = i1;
if (loop_dump_stream)
fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1));
}
/* If we are to re-generate the item being moved with a
new move insn, first delete what we have and then emit
the move insn before the loop. */
else if (m->move_insn)
{
rtx i1, temp;
for (count = m->consec; count >= 0; count--)
{
/* If this is the first insn of a library call sequence,
skip to the end. */
if (GET_CODE (p) != NOTE
&& (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
p = XEXP (temp, 0);
/* If this is the last insn of a libcall sequence, then
delete every insn in the sequence except the last.
The last insn is handled in the normal manner. */
if (GET_CODE (p) != NOTE
&& (temp = find_reg_note (p, REG_RETVAL, NULL_RTX)))
{
temp = XEXP (temp, 0);
while (temp != p)
temp = delete_insn (temp);
}
temp = p;
p = delete_insn (p);
/* simplify_giv_expr expects that it can walk the insns
at m->insn forwards and see this old sequence we are
tossing here. delete_insn does preserve the next
pointers, but when we skip over a NOTE we must fix
it up. Otherwise that code walks into the non-deleted
insn stream. */
while (p && GET_CODE (p) == NOTE)
p = NEXT_INSN (temp) = NEXT_INSN (p);
}
start_sequence ();
emit_move_insn (m->set_dest, m->set_src);
temp = get_insns ();
end_sequence ();
add_label_notes (m->set_src, temp);
i1 = emit_insns_before (temp, loop_start);
if (! find_reg_note (i1, REG_EQUAL, NULL_RTX))
REG_NOTES (i1)
= gen_rtx_EXPR_LIST (m->is_equiv ? REG_EQUIV : REG_EQUAL,
m->set_src, REG_NOTES (i1));
if (loop_dump_stream)
fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1));
/* The more regs we move, the less we like moving them. */
threshold -= 3;
}
else
{
for (count = m->consec; count >= 0; count--)
{
rtx i1, temp;
/* If first insn of libcall sequence, skip to end. */
/* Do this at start of loop, since p is guaranteed to
be an insn here. */
if (GET_CODE (p) != NOTE
&& (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
p = XEXP (temp, 0);
/* If last insn of libcall sequence, move all
insns except the last before the loop. The last
insn is handled in the normal manner. */
if (GET_CODE (p) != NOTE
&& (temp = find_reg_note (p, REG_RETVAL, NULL_RTX)))
{
rtx fn_address = 0;
rtx fn_reg = 0;
rtx fn_address_insn = 0;
first = 0;
for (temp = XEXP (temp, 0); temp != p;
temp = NEXT_INSN (temp))
{
rtx body;
rtx n;
rtx next;
if (GET_CODE (temp) == NOTE)
continue;
body = PATTERN (temp);
/* Find the next insn after TEMP,
not counting USE or NOTE insns. */
for (next = NEXT_INSN (temp); next != p;
next = NEXT_INSN (next))
if (! (GET_CODE (next) == INSN
&& GET_CODE (PATTERN (next)) == USE)
&& GET_CODE (next) != NOTE)
break;
/* If that is the call, this may be the insn
that loads the function address.
Extract the function address from the insn
that loads it into a register.
If this insn was cse'd, we get incorrect code.
So emit a new move insn that copies the
function address into the register that the
call insn will use. flow.c will delete any
redundant stores that we have created. */
if (GET_CODE (next) == CALL_INSN
&& GET_CODE (body) == SET
&& GET_CODE (SET_DEST (body)) == REG
&& (n = find_reg_note (temp, REG_EQUAL,
NULL_RTX)))
{
fn_reg = SET_SRC (body);
if (GET_CODE (fn_reg) != REG)
fn_reg = SET_DEST (body);
fn_address = XEXP (n, 0);
fn_address_insn = temp;
}
/* We have the call insn.
If it uses the register we suspect it might,
load it with the correct address directly. */
if (GET_CODE (temp) == CALL_INSN
&& fn_address != 0
&& reg_referenced_p (fn_reg, body))
emit_insn_after (gen_move_insn (fn_reg,
fn_address),
fn_address_insn);
if (GET_CODE (temp) == CALL_INSN)
{
i1 = emit_call_insn_before (body, loop_start);
/* Because the USAGE information potentially
contains objects other than hard registers
we need to copy it. */
if (CALL_INSN_FUNCTION_USAGE (temp))
CALL_INSN_FUNCTION_USAGE (i1)
= copy_rtx (CALL_INSN_FUNCTION_USAGE (temp));
}
else
i1 = emit_insn_before (body, loop_start);
if (first == 0)
first = i1;
if (temp == fn_address_insn)
fn_address_insn = i1;
REG_NOTES (i1) = REG_NOTES (temp);
delete_insn (temp);
}
if (new_start == 0)
new_start = first;
}
if (m->savemode != VOIDmode)
{
/* P sets REG to zero; but we should clear only
the bits that are not covered by the mode
m->savemode. */
rtx reg = m->set_dest;
rtx sequence;
rtx tem;
start_sequence ();
tem = expand_binop
(GET_MODE (reg), and_optab, reg,
GEN_INT ((((HOST_WIDE_INT) 1
<< GET_MODE_BITSIZE (m->savemode)))
- 1),
reg, 1, OPTAB_LIB_WIDEN);
if (tem == 0)
abort ();
if (tem != reg)
emit_move_insn (reg, tem);
sequence = gen_sequence ();
end_sequence ();
i1 = emit_insn_before (sequence, loop_start);
}
else if (GET_CODE (p) == CALL_INSN)
{
i1 = emit_call_insn_before (PATTERN (p), loop_start);
/* Because the USAGE information potentially
contains objects other than hard registers
we need to copy it. */
if (CALL_INSN_FUNCTION_USAGE (p))
CALL_INSN_FUNCTION_USAGE (i1)
= copy_rtx (CALL_INSN_FUNCTION_USAGE (p));
}
else if (count == m->consec && m->move_insn_first)
{
/* The SET_SRC might not be invariant, so we must
use the REG_EQUAL note. */
start_sequence ();
emit_move_insn (m->set_dest, m->set_src);
temp = get_insns ();
end_sequence ();
add_label_notes (m->set_src, temp);
i1 = emit_insns_before (temp, loop_start);
if (! find_reg_note (i1, REG_EQUAL, NULL_RTX))
REG_NOTES (i1)
= gen_rtx_EXPR_LIST ((m->is_equiv ? REG_EQUIV
: REG_EQUAL),
m->set_src, REG_NOTES (i1));
}
else
i1 = emit_insn_before (PATTERN (p), loop_start);
if (REG_NOTES (i1) == 0)
{
REG_NOTES (i1) = REG_NOTES (p);
/* If there is a REG_EQUAL note present whose value
is not loop invariant, then delete it, since it
may cause problems with later optimization passes.
It is possible for cse to create such notes
like this as a result of record_jump_cond. */
if ((temp = find_reg_note (i1, REG_EQUAL, NULL_RTX))
&& ! invariant_p (XEXP (temp, 0)))
remove_note (i1, temp);
}
if (new_start == 0)
new_start = i1;
if (loop_dump_stream)
fprintf (loop_dump_stream, " moved to %d",
INSN_UID (i1));
/* If library call, now fix the REG_NOTES that contain
insn pointers, namely REG_LIBCALL on FIRST
and REG_RETVAL on I1. */
if ((temp = find_reg_note (i1, REG_RETVAL, NULL_RTX)))
{
XEXP (temp, 0) = first;
temp = find_reg_note (first, REG_LIBCALL, NULL_RTX);
XEXP (temp, 0) = i1;
}
temp = p;
delete_insn (p);
p = NEXT_INSN (p);
/* simplify_giv_expr expects that it can walk the insns
at m->insn forwards and see this old sequence we are
tossing here. delete_insn does preserve the next
pointers, but when we skip over a NOTE we must fix
it up. Otherwise that code walks into the non-deleted
insn stream. */
while (p && GET_CODE (p) == NOTE)
p = NEXT_INSN (temp) = NEXT_INSN (p);
}
/* The more regs we move, the less we like moving them. */
threshold -= 3;
}
/* Any other movable that loads the same register
MUST be moved. */
already_moved[regno] = 1;
/* This reg has been moved out of one loop. */
moved_once[regno] = 1;
/* The reg set here is now invariant. */
if (! m->partial)
VARRAY_INT (set_in_loop, regno) = 0;
m->done = 1;
/* Change the length-of-life info for the register
to say it lives at least the full length of this loop.
This will help guide optimizations in outer loops. */
if (uid_luid[REGNO_FIRST_UID (regno)] > INSN_LUID (loop_start))
/* This is the old insn before all the moved insns.
We can't use the moved insn because it is out of range
in uid_luid. Only the old insns have luids. */
REGNO_FIRST_UID (regno) = INSN_UID (loop_start);
if (uid_luid[REGNO_LAST_UID (regno)] < INSN_LUID (end))
REGNO_LAST_UID (regno) = INSN_UID (end);
/* Combine with this moved insn any other matching movables. */
if (! m->partial)
for (m1 = movables; m1; m1 = m1->next)
if (m1->match == m)
{
rtx temp;
/* Schedule the reg loaded by M1
for replacement so that shares the reg of M.
If the modes differ (only possible in restricted
circumstances, make a SUBREG. */
if (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest))
reg_map[m1->regno] = m->set_dest;
else
reg_map[m1->regno]
= gen_lowpart_common (GET_MODE (m1->set_dest),
m->set_dest);
/* Get rid of the matching insn
and prevent further processing of it. */
m1->done = 1;
/* if library call, delete all insn except last, which
is deleted below */
if ((temp = find_reg_note (m1->insn, REG_RETVAL,
NULL_RTX)))
{
for (temp = XEXP (temp, 0); temp != m1->insn;
temp = NEXT_INSN (temp))
delete_insn (temp);
}
delete_insn (m1->insn);
/* Any other movable that loads the same register
MUST be moved. */
already_moved[m1->regno] = 1;
/* The reg merged here is now invariant,
if the reg it matches is invariant. */
if (! m->partial)
VARRAY_INT (set_in_loop, m1->regno) = 0;
}
}
else if (loop_dump_stream)
fprintf (loop_dump_stream, "not desirable");
}
else if (loop_dump_stream && !m->match)
fprintf (loop_dump_stream, "not safe");
if (loop_dump_stream)
fprintf (loop_dump_stream, "\n");
}
if (new_start == 0)
new_start = loop_start;
/* Go through all the instructions in the loop, making
all the register substitutions scheduled in REG_MAP. */
for (p = new_start; p != end; p = NEXT_INSN (p))
if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN)
{
replace_regs (PATTERN (p), reg_map, nregs, 0);
replace_regs (REG_NOTES (p), reg_map, nregs, 0);
INSN_CODE (p) = -1;
}
}
#if 0
/* Scan X and replace the address of any MEM in it with ADDR.
REG is the address that MEM should have before the replacement. */
static void
replace_call_address (x, reg, addr)
rtx x, reg, addr;
{
register enum rtx_code code;
register int i;
register char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case PC:
case CC0:
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case REG:
return;
case SET:
/* Short cut for very common case. */
replace_call_address (XEXP (x, 1), reg, addr);
return;
case CALL:
/* Short cut for very common case. */
replace_call_address (XEXP (x, 0), reg, addr);
return;
case MEM:
/* If this MEM uses a reg other than the one we expected,
something is wrong. */
if (XEXP (x, 0) != reg)
abort ();
XEXP (x, 0) = addr;
return;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
replace_call_address (XEXP (x, i), reg, addr);
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
replace_call_address (XVECEXP (x, i, j), reg, addr);
}
}
}
#endif
/* Return the number of memory refs to addresses that vary
in the rtx X. */
static int
count_nonfixed_reads (x)
rtx x;
{
register enum rtx_code code;
register int i;
register char *fmt;
int value;
if (x == 0)
return 0;
code = GET_CODE (x);
switch (code)
{
case PC:
case CC0:
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case REG:
return 0;
case MEM:
return ((invariant_p (XEXP (x, 0)) != 1)
+ count_nonfixed_reads (XEXP (x, 0)));
default:
break;
}
value = 0;
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
value += count_nonfixed_reads (XEXP (x, i));
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
value += count_nonfixed_reads (XVECEXP (x, i, j));
}
}
return value;
}
#if 0
/* P is an instruction that sets a register to the result of a ZERO_EXTEND.
Replace it with an instruction to load just the low bytes
if the machine supports such an instruction,
and insert above LOOP_START an instruction to clear the register. */
static void
constant_high_bytes (p, loop_start)
rtx p, loop_start;
{
register rtx new;
register int insn_code_number;
/* Try to change (SET (REG ...) (ZERO_EXTEND (..:B ...)))
to (SET (STRICT_LOW_PART (SUBREG:B (REG...))) ...). */
new = gen_rtx_SET (VOIDmode,
gen_rtx_STRICT_LOW_PART (VOIDmode,
gen_rtx_SUBREG (GET_MODE (XEXP (SET_SRC (PATTERN (p)), 0)),
SET_DEST (PATTERN (p)),
0)),
XEXP (SET_SRC (PATTERN (p)), 0));
insn_code_number = recog (new, p);
if (insn_code_number)
{
register int i;
/* Clear destination register before the loop. */
emit_insn_before (gen_rtx_SET (VOIDmode, SET_DEST (PATTERN (p)),
const0_rtx),
loop_start);
/* Inside the loop, just load the low part. */
PATTERN (p) = new;
}
}
#endif
/* Scan a loop setting the variables `unknown_address_altered',
`num_mem_sets', `loop_continue', `loops_enclosed', `loop_has_call',
`loop_has_volatile', and `loop_has_tablejump'.
Also, fill in the array `loop_mems' and the list `loop_store_mems'. */
static void
prescan_loop (start, end)
rtx start, end;
{
register int level = 1;
rtx insn;
int loop_has_multiple_exit_targets = 0;
/* The label after END. Jumping here is just like falling off the
end of the loop. We use next_nonnote_insn instead of next_label
as a hedge against the (pathological) case where some actual insn
might end up between the two. */
rtx exit_target = next_nonnote_insn (end);
if (exit_target == NULL_RTX || GET_CODE (exit_target) != CODE_LABEL)
loop_has_multiple_exit_targets = 1;
unknown_address_altered = 0;
loop_has_call = 0;
loop_has_volatile = 0;
loop_has_tablejump = 0;
loop_store_mems = NULL_RTX;
first_loop_store_insn = NULL_RTX;
loop_mems_idx = 0;
num_mem_sets = 0;
loops_enclosed = 1;
loop_continue = 0;
for (insn = NEXT_INSN (start); insn != NEXT_INSN (end);
insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
{
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
{
++level;
/* Count number of loops contained in this one. */
loops_enclosed++;
}
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
{
--level;
if (level == 0)
{
end = insn;
break;
}
}
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT)
{
if (level == 1)
loop_continue = insn;
}
}
else if (GET_CODE (insn) == CALL_INSN)
{
if (! CONST_CALL_P (insn))
unknown_address_altered = 1;
loop_has_call = 1;
}
else if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
{
rtx label1 = NULL_RTX;
rtx label2 = NULL_RTX;
if (volatile_refs_p (PATTERN (insn)))
loop_has_volatile = 1;
if (GET_CODE (insn) == JUMP_INSN
&& (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
|| GET_CODE (PATTERN (insn)) == ADDR_VEC))
loop_has_tablejump = 1;
note_stores (PATTERN (insn), note_addr_stored);
if (! first_loop_store_insn && loop_store_mems)
first_loop_store_insn = insn;
if (! loop_has_multiple_exit_targets
&& GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == pc_rtx)
{
if (GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)
{
label1 = XEXP (SET_SRC (PATTERN (insn)), 1);
label2 = XEXP (SET_SRC (PATTERN (insn)), 2);
}
else
{
label1 = SET_SRC (PATTERN (insn));
}
do {
if (label1 && label1 != pc_rtx)
{
if (GET_CODE (label1) != LABEL_REF)
{
/* Something tricky. */
loop_has_multiple_exit_targets = 1;
break;
}
else if (XEXP (label1, 0) != exit_target
&& LABEL_OUTSIDE_LOOP_P (label1))
{
/* A jump outside the current loop. */
loop_has_multiple_exit_targets = 1;
break;
}
}
label1 = label2;
label2 = NULL_RTX;
} while (label1);
}
}
else if (GET_CODE (insn) == RETURN)
loop_has_multiple_exit_targets = 1;
}
/* Now, rescan the loop, setting up the LOOP_MEMS array. */
if (/* We can't tell what MEMs are aliased by what. */
!unknown_address_altered
/* An exception thrown by a called function might land us
anywhere. */
&& !loop_has_call
/* We don't want loads for MEMs moved to a location before the
one at which their stack memory becomes allocated. (Note
that this is not a problem for malloc, etc., since those
require actual function calls. */
&& !current_function_calls_alloca
/* There are ways to leave the loop other than falling off the
end. */
&& !loop_has_multiple_exit_targets)
for (insn = NEXT_INSN (start); insn != NEXT_INSN (end);
insn = NEXT_INSN (insn))
for_each_rtx (&insn, insert_loop_mem, 0);
}
/* LOOP_NUMBER_CONT_DOMINATOR is now the last label between the loop start
and the continue note that is a the destination of a (cond)jump after
the continue note. If there is any (cond)jump between the loop start
and what we have so far as LOOP_NUMBER_CONT_DOMINATOR that has a
target between LOOP_DOMINATOR and the continue note, move
LOOP_NUMBER_CONT_DOMINATOR forward to that label; if a jump's
destination cannot be determined, clear LOOP_NUMBER_CONT_DOMINATOR. */
static void
verify_dominator (loop_number)
int loop_number;
{
rtx insn;
if (! loop_number_cont_dominator[loop_number])
/* This can happen for an empty loop, e.g. in
gcc.c-torture/compile/920410-2.c */
return;
if (loop_number_cont_dominator[loop_number] == const0_rtx)
{
loop_number_cont_dominator[loop_number] = 0;
return;
}
for (insn = loop_number_loop_starts[loop_number];
insn != loop_number_cont_dominator[loop_number];
insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != RETURN)
{
rtx label = JUMP_LABEL (insn);
int label_luid;
/* If it is not a jump we can easily understand or for
which we do not have jump target information in the JUMP_LABEL
field (consider ADDR_VEC and ADDR_DIFF_VEC insns), then clear
LOOP_NUMBER_CONT_DOMINATOR. */
if ((! condjump_p (insn)
&& ! condjump_in_parallel_p (insn))
|| label == NULL_RTX)
{
loop_number_cont_dominator[loop_number] = NULL_RTX;
return;
}
label_luid = INSN_LUID (label);
if (label_luid < INSN_LUID (loop_number_loop_cont[loop_number])
&& (label_luid
> INSN_LUID (loop_number_cont_dominator[loop_number])))
loop_number_cont_dominator[loop_number] = label;
}
}
}
/* Scan the function looking for loops. Record the start and end of each loop.
Also mark as invalid loops any loops that contain a setjmp or are branched
to from outside the loop. */
static void
find_and_verify_loops (f)
rtx f;
{
rtx insn, label;
int current_loop = -1;
int next_loop = -1;
int loop;
compute_luids (f, NULL_RTX, 0);
/* If there are jumps to undefined labels,
treat them as jumps out of any/all loops.
This also avoids writing past end of tables when there are no loops. */
uid_loop_num[0] = -1;
/* Find boundaries of loops, mark which loops are contained within
loops, and invalidate loops that have setjmp. */
for (insn = f; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
switch (NOTE_LINE_NUMBER (insn))
{
case NOTE_INSN_LOOP_BEG:
loop_number_loop_starts[++next_loop] = insn;
loop_number_loop_ends[next_loop] = 0;
loop_number_loop_cont[next_loop] = 0;
loop_number_cont_dominator[next_loop] = 0;
loop_outer_loop[next_loop] = current_loop;
loop_invalid[next_loop] = 0;
loop_number_exit_labels[next_loop] = 0;
loop_number_exit_count[next_loop] = 0;
current_loop = next_loop;
break;
case NOTE_INSN_SETJMP:
/* In this case, we must invalidate our current loop and any
enclosing loop. */
for (loop = current_loop; loop != -1; loop = loop_outer_loop[loop])
{
loop_invalid[loop] = 1;
if (loop_dump_stream)
fprintf (loop_dump_stream,
"\nLoop at %d ignored due to setjmp.\n",
INSN_UID (loop_number_loop_starts[loop]));
}
break;
case NOTE_INSN_LOOP_CONT:
loop_number_loop_cont[current_loop] = insn;
break;
case NOTE_INSN_LOOP_END:
if (current_loop == -1)
abort ();
loop_number_loop_ends[current_loop] = insn;
verify_dominator (current_loop);
current_loop = loop_outer_loop[current_loop];
break;
default:
break;
}
/* If for any loop, this is a jump insn between the NOTE_INSN_LOOP_CONT
and NOTE_INSN_LOOP_END notes, update loop_number_loop_dominator. */
else if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != RETURN
&& current_loop >= 0)
{
int this_loop;
rtx label = JUMP_LABEL (insn);
if (! condjump_p (insn) && ! condjump_in_parallel_p (insn))
label = NULL_RTX;
this_loop = current_loop;
do
{
/* First see if we care about this loop. */
if (loop_number_loop_cont[this_loop]
&& loop_number_cont_dominator[this_loop] != const0_rtx)
{
/* If the jump destination is not known, invalidate
loop_number_const_dominator. */
if (! label)
loop_number_cont_dominator[this_loop] = const0_rtx;
else
/* Check if the destination is between loop start and
cont. */
if ((INSN_LUID (label)
< INSN_LUID (loop_number_loop_cont[this_loop]))
&& (INSN_LUID (label)
> INSN_LUID (loop_number_loop_starts[this_loop]))
/* And if there is no later destination already
recorded. */
&& (! loop_number_cont_dominator[this_loop]
|| (INSN_LUID (label)
> INSN_LUID (loop_number_cont_dominator
[this_loop]))))
loop_number_cont_dominator[this_loop] = label;
}
this_loop = loop_outer_loop[this_loop];
}
while (this_loop >= 0);
}
/* Note that this will mark the NOTE_INSN_LOOP_END note as being in the
enclosing loop, but this doesn't matter. */
uid_loop_num[INSN_UID (insn)] = current_loop;
}
/* Any loop containing a label used in an initializer must be invalidated,
because it can be jumped into from anywhere. */
for (label = forced_labels; label; label = XEXP (label, 1))
{
int loop_num;
for (loop_num = uid_loop_num[INSN_UID (XEXP (label, 0))];
loop_num != -1;
loop_num = loop_outer_loop[loop_num])
loop_invalid[loop_num] = 1;
}
/* Any loop containing a label used for an exception handler must be
invalidated, because it can be jumped into from anywhere. */
for (label = exception_handler_labels; label; label = XEXP (label, 1))
{
int loop_num;
for (loop_num = uid_loop_num[INSN_UID (XEXP (label, 0))];
loop_num != -1;
loop_num = loop_outer_loop[loop_num])
loop_invalid[loop_num] = 1;
}
/* Now scan all insn's in the function. If any JUMP_INSN branches into a
loop that it is not contained within, that loop is marked invalid.
If any INSN or CALL_INSN uses a label's address, then the loop containing
that label is marked invalid, because it could be jumped into from
anywhere.
Also look for blocks of code ending in an unconditional branch that
exits the loop. If such a block is surrounded by a conditional
branch around the block, move the block elsewhere (see below) and
invert the jump to point to the code block. This may eliminate a
label in our loop and will simplify processing by both us and a
possible second cse pass. */
for (insn = f; insn; insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
int this_loop_num = uid_loop_num[INSN_UID (insn)];
if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
{
rtx note = find_reg_note (insn, REG_LABEL, NULL_RTX);
if (note)
{
int loop_num;
for (loop_num = uid_loop_num[INSN_UID (XEXP (note, 0))];
loop_num != -1;
loop_num = loop_outer_loop[loop_num])
loop_invalid[loop_num] = 1;
}
}
if (GET_CODE (insn) != JUMP_INSN)
continue;
mark_loop_jump (PATTERN (insn), this_loop_num);
/* See if this is an unconditional branch outside the loop. */
if (this_loop_num != -1
&& (GET_CODE (PATTERN (insn)) == RETURN
|| (simplejump_p (insn)
&& (uid_loop_num[INSN_UID (JUMP_LABEL (insn))]
!= this_loop_num)))
&& get_max_uid () < max_uid_for_loop)
{
rtx p;
rtx our_next = next_real_insn (insn);
int dest_loop;
int outer_loop = -1;
/* Go backwards until we reach the start of the loop, a label,
or a JUMP_INSN. */
for (p = PREV_INSN (insn);
GET_CODE (p) != CODE_LABEL
&& ! (GET_CODE (p) == NOTE
&& NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
&& GET_CODE (p) != JUMP_INSN;
p = PREV_INSN (p))
;
/* Check for the case where we have a jump to an inner nested
loop, and do not perform the optimization in that case. */
if (JUMP_LABEL (insn))
{
dest_loop = uid_loop_num[INSN_UID (JUMP_LABEL (insn))];
if (dest_loop != -1)
{
for (outer_loop = dest_loop; outer_loop != -1;
outer_loop = loop_outer_loop[outer_loop])
if (outer_loop == this_loop_num)
break;
}
}
/* Make sure that the target of P is within the current loop. */
if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p)
&& uid_loop_num[INSN_UID (JUMP_LABEL (p))] != this_loop_num)
outer_loop = this_loop_num;
/* If we stopped on a JUMP_INSN to the next insn after INSN,
we have a block of code to try to move.
We look backward and then forward from the target of INSN
to find a BARRIER at the same loop depth as the target.
If we find such a BARRIER, we make a new label for the start
of the block, invert the jump in P and point it to that label,
and move the block of code to the spot we found. */
if (outer_loop == -1
&& GET_CODE (p) == JUMP_INSN
&& JUMP_LABEL (p) != 0
/* Just ignore jumps to labels that were never emitted.
These always indicate compilation errors. */
&& INSN_UID (JUMP_LABEL (p)) != 0
&& condjump_p (p)
&& ! simplejump_p (p)
&& next_real_insn (JUMP_LABEL (p)) == our_next)
{
rtx target
= JUMP_LABEL (insn) ? JUMP_LABEL (insn) : get_last_insn ();
int target_loop_num = uid_loop_num[INSN_UID (target)];
rtx loc;
for (loc = target; loc; loc = PREV_INSN (loc))
if (GET_CODE (loc) == BARRIER
&& uid_loop_num[INSN_UID (loc)] == target_loop_num)
break;
if (loc == 0)
for (loc = target; loc; loc = NEXT_INSN (loc))
if (GET_CODE (loc) == BARRIER
&& uid_loop_num[INSN_UID (loc)] == target_loop_num)
break;
if (loc)
{
rtx cond_label = JUMP_LABEL (p);
rtx new_label = get_label_after (p);
/* Ensure our label doesn't go away. */
LABEL_NUSES (cond_label)++;
/* Verify that uid_loop_num is large enough and that
we can invert P. */
if (invert_jump (p, new_label))
{
rtx q, r;
/* If no suitable BARRIER was found, create a suitable
one before TARGET. Since TARGET is a fall through
path, we'll need to insert an jump around our block
and a add a BARRIER before TARGET.
This creates an extra unconditional jump outside
the loop. However, the benefits of removing rarely
executed instructions from inside the loop usually
outweighs the cost of the extra unconditional jump
outside the loop. */
if (loc == 0)
{
rtx temp;
temp = gen_jump (JUMP_LABEL (insn));
temp = emit_jump_insn_before (temp, target);
JUMP_LABEL (temp) = JUMP_LABEL (insn);
LABEL_NUSES (JUMP_LABEL (insn))++;
loc = emit_barrier_before (target);
}
/* Include the BARRIER after INSN and copy the
block after LOC. */
new_label = squeeze_notes (new_label, NEXT_INSN (insn));
reorder_insns (new_label, NEXT_INSN (insn), loc);
/* All those insns are now in TARGET_LOOP_NUM. */
for (q = new_label; q != NEXT_INSN (NEXT_INSN (insn));
q = NEXT_INSN (q))
uid_loop_num[INSN_UID (q)] = target_loop_num;
/* The label jumped to by INSN is no longer a loop exit.
Unless INSN does not have a label (e.g., it is a
RETURN insn), search loop_number_exit_labels to find
its label_ref, and remove it. Also turn off
LABEL_OUTSIDE_LOOP_P bit. */
if (JUMP_LABEL (insn))
{
int loop_num;
for (q = 0,
r = loop_number_exit_labels[this_loop_num];
r; q = r, r = LABEL_NEXTREF (r))
if (XEXP (r, 0) == JUMP_LABEL (insn))
{
LABEL_OUTSIDE_LOOP_P (r) = 0;
if (q)
LABEL_NEXTREF (q) = LABEL_NEXTREF (r);
else
loop_number_exit_labels[this_loop_num]
= LABEL_NEXTREF (r);
break;
}
for (loop_num = this_loop_num;
loop_num != -1 && loop_num != target_loop_num;
loop_num = loop_outer_loop[loop_num])
loop_number_exit_count[loop_num]--;
/* If we didn't find it, then something is wrong. */
if (! r)
abort ();
}
/* P is now a jump outside the loop, so it must be put
in loop_number_exit_labels, and marked as such.
The easiest way to do this is to just call
mark_loop_jump again for P. */
mark_loop_jump (PATTERN (p), this_loop_num);
/* If INSN now jumps to the insn after it,
delete INSN. */
if (JUMP_LABEL (insn) != 0
&& (next_real_insn (JUMP_LABEL (insn))
== next_real_insn (insn)))
delete_insn (insn);
}
/* Continue the loop after where the conditional
branch used to jump, since the only branch insn
in the block (if it still remains) is an inter-loop
branch and hence needs no processing. */
insn = NEXT_INSN (cond_label);
if (--LABEL_NUSES (cond_label) == 0)
delete_insn (cond_label);
/* This loop will be continued with NEXT_INSN (insn). */
insn = PREV_INSN (insn);
}
}
}
}
}
/* If any label in X jumps to a loop different from LOOP_NUM and any of the
loops it is contained in, mark the target loop invalid.
For speed, we assume that X is part of a pattern of a JUMP_INSN. */
static void
mark_loop_jump (x, loop_num)
rtx x;
int loop_num;
{
int dest_loop;
int outer_loop;
int i;
switch (GET_CODE (x))
{
case PC:
case USE:
case CLOBBER:
case REG:
case MEM:
case CONST_INT:
case CONST_DOUBLE:
case RETURN:
return;
case CONST:
/* There could be a label reference in here. */
mark_loop_jump (XEXP (x, 0), loop_num);
return;
case PLUS:
case MINUS:
case MULT:
mark_loop_jump (XEXP (x, 0), loop_num);
mark_loop_jump (XEXP (x, 1), loop_num);
return;
case LO_SUM:
/* This may refer to a LABEL_REF or SYMBOL_REF. */
mark_loop_jump (XEXP (x, 1), loop_num);
return;
case SIGN_EXTEND:
case ZERO_EXTEND:
mark_loop_jump (XEXP (x, 0), loop_num);
return;
case LABEL_REF:
dest_loop = uid_loop_num[INSN_UID (XEXP (x, 0))];
/* Link together all labels that branch outside the loop. This
is used by final_[bg]iv_value and the loop unrolling code. Also
mark this LABEL_REF so we know that this branch should predict
false. */
/* A check to make sure the label is not in an inner nested loop,
since this does not count as a loop exit. */
if (dest_loop != -1)
{
for (outer_loop = dest_loop; outer_loop != -1;
outer_loop = loop_outer_loop[outer_loop])
if (outer_loop == loop_num)
break;
}
else
outer_loop = -1;
if (loop_num != -1 && outer_loop == -1)
{
LABEL_OUTSIDE_LOOP_P (x) = 1;
LABEL_NEXTREF (x) = loop_number_exit_labels[loop_num];
loop_number_exit_labels[loop_num] = x;
for (outer_loop = loop_num;
outer_loop != -1 && outer_loop != dest_loop;
outer_loop = loop_outer_loop[outer_loop])
loop_number_exit_count[outer_loop]++;
}
/* If this is inside a loop, but not in the current loop or one enclosed
by it, it invalidates at least one loop. */
if (dest_loop == -1)
return;
/* We must invalidate every nested loop containing the target of this
label, except those that also contain the jump insn. */
for (; dest_loop != -1; dest_loop = loop_outer_loop[dest_loop])
{
/* Stop when we reach a loop that also contains the jump insn. */
for (outer_loop = loop_num; outer_loop != -1;
outer_loop = loop_outer_loop[outer_loop])
if (dest_loop == outer_loop)
return;
/* If we get here, we know we need to invalidate a loop. */
if (loop_dump_stream && ! loop_invalid[dest_loop])
fprintf (loop_dump_stream,
"\nLoop at %d ignored due to multiple entry points.\n",
INSN_UID (loop_number_loop_starts[dest_loop]));
loop_invalid[dest_loop] = 1;
}
return;
case SET:
/* If this is not setting pc, ignore. */
if (SET_DEST (x) == pc_rtx)
mark_loop_jump (SET_SRC (x), loop_num);
return;
case IF_THEN_ELSE:
mark_loop_jump (XEXP (x, 1), loop_num);
mark_loop_jump (XEXP (x, 2), loop_num);
return;
case PARALLEL:
case ADDR_VEC:
for (i = 0; i < XVECLEN (x, 0); i++)
mark_loop_jump (XVECEXP (x, 0, i), loop_num);
return;
case ADDR_DIFF_VEC:
for (i = 0; i < XVECLEN (x, 1); i++)
mark_loop_jump (XVECEXP (x, 1, i), loop_num);
return;
default:
/* Strictly speaking this is not a jump into the loop, only a possible
jump out of the loop. However, we have no way to link the destination
of this jump onto the list of exit labels. To be safe we mark this
loop and any containing loops as invalid. */
if (loop_num != -1)
{
for (outer_loop = loop_num; outer_loop != -1;
outer_loop = loop_outer_loop[outer_loop])
{
if (loop_dump_stream && ! loop_invalid[outer_loop])
fprintf (loop_dump_stream,
"\nLoop at %d ignored due to unknown exit jump.\n",
INSN_UID (loop_number_loop_starts[outer_loop]));
loop_invalid[outer_loop] = 1;
}
}
return;
}
}
/* Return nonzero if there is a label in the range from
insn INSN to and including the insn whose luid is END
INSN must have an assigned luid (i.e., it must not have
been previously created by loop.c). */
static int
labels_in_range_p (insn, end)
rtx insn;
int end;
{
while (insn && INSN_LUID (insn) <= end)
{
if (GET_CODE (insn) == CODE_LABEL)
return 1;
insn = NEXT_INSN (insn);
}
return 0;
}
/* Record that a memory reference X is being set. */
static void
note_addr_stored (x, y)
rtx x;
rtx y ATTRIBUTE_UNUSED;
{
if (x == 0 || GET_CODE (x) != MEM)
return;
/* Count number of memory writes.
This affects heuristics in strength_reduce. */
num_mem_sets++;
/* BLKmode MEM means all memory is clobbered. */
if (GET_MODE (x) == BLKmode)
unknown_address_altered = 1;
if (unknown_address_altered)
return;
loop_store_mems = gen_rtx_EXPR_LIST (VOIDmode, x, loop_store_mems);
}
+
+/* X is a value modified by an INSN that references a biv inside a loop
+ exit test (ie, X is somehow related to the value of the biv). If X
+ is a pseudo that is used more than once, then the biv is (effectively)
+ used more than once. */
+
+static void
+note_set_pseudo_multiple_uses (x, y)
+ rtx x;
+ rtx y ATTRIBUTE_UNUSED;
+{
+ if (x == 0)
+ return;
+
+ while (GET_CODE (x) == STRICT_LOW_PART
+ || GET_CODE (x) == SIGN_EXTRACT
+ || GET_CODE (x) == ZERO_EXTRACT
+ || GET_CODE (x) == SUBREG)
+ x = XEXP (x, 0);
+
+ if (GET_CODE (x) != REG || REGNO (x) < FIRST_PSEUDO_REGISTER)
+ return;
+
+ /* If we do not have usage information, or if we know the register
+ is used more than once, note that fact for check_dbra_loop. */
+ if (REGNO (x) >= max_reg_before_loop
+ || ! VARRAY_RTX (reg_single_usage, REGNO (x))
+ || VARRAY_RTX (reg_single_usage, REGNO (x)) == const0_rtx)
+ note_set_pseudo_multiple_uses_retval = 1;
+}
/* Return nonzero if the rtx X is invariant over the current loop.
The value is 2 if we refer to something only conditionally invariant.
If `unknown_address_altered' is nonzero, no memory ref is invariant.
Otherwise, a memory ref is invariant if it does not conflict with
anything stored in `loop_store_mems'. */
int
invariant_p (x)
register rtx x;
{
register int i;
register enum rtx_code code;
register char *fmt;
int conditional = 0;
rtx mem_list_entry;
if (x == 0)
return 1;
code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case CONST:
return 1;
case LABEL_REF:
/* A LABEL_REF is normally invariant, however, if we are unrolling
loops, and this label is inside the loop, then it isn't invariant.
This is because each unrolled copy of the loop body will have
a copy of this label. If this was invariant, then an insn loading
the address of this label into a register might get moved outside
the loop, and then each loop body would end up using the same label.
We don't know the loop bounds here though, so just fail for all
labels. */
if (flag_unroll_loops)
return 0;
else
return 1;
case PC:
case CC0:
case UNSPEC_VOLATILE:
return 0;
case REG:
/* We used to check RTX_UNCHANGING_P (x) here, but that is invalid
since the reg might be set by initialization within the loop. */
if ((x == frame_pointer_rtx || x == hard_frame_pointer_rtx
|| x == arg_pointer_rtx)
&& ! current_function_has_nonlocal_goto)
return 1;
if (loop_has_call
&& REGNO (x) < FIRST_PSEUDO_REGISTER && call_used_regs[REGNO (x)])
return 0;
if (VARRAY_INT (set_in_loop, REGNO (x)) < 0)
return 2;
return VARRAY_INT (set_in_loop, REGNO (x)) == 0;
case MEM:
/* Volatile memory references must be rejected. Do this before
checking for read-only items, so that volatile read-only items
will be rejected also. */
if (MEM_VOLATILE_P (x))
return 0;
/* Read-only items (such as constants in a constant pool) are
invariant if their address is. */
if (RTX_UNCHANGING_P (x))
break;
/* If we had a subroutine call, any location in memory could have been
clobbered. */
if (unknown_address_altered)
return 0;
/* See if there is any dependence between a store and this load. */
mem_list_entry = loop_store_mems;
while (mem_list_entry)
{
if (true_dependence (XEXP (mem_list_entry, 0), VOIDmode,
x, rtx_varies_p))
return 0;
mem_list_entry = XEXP (mem_list_entry, 1);
}
/* It's not invalidated by a store in memory
but we must still verify the address is invariant. */
break;
case ASM_OPERANDS:
/* Don't mess with insns declared volatile. */
if (MEM_VOLATILE_P (x))
return 0;
break;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
int tem = invariant_p (XEXP (x, i));
if (tem == 0)
return 0;
if (tem == 2)
conditional = 1;
}
else if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
{
int tem = invariant_p (XVECEXP (x, i, j));
if (tem == 0)
return 0;
if (tem == 2)
conditional = 1;
}
}
}
return 1 + conditional;
}
/* Return nonzero if all the insns in the loop that set REG
are INSN and the immediately following insns,
and if each of those insns sets REG in an invariant way
(not counting uses of REG in them).
The value is 2 if some of these insns are only conditionally invariant.
We assume that INSN itself is the first set of REG
and that its source is invariant. */
static int
consec_sets_invariant_p (reg, n_sets, insn)
int n_sets;
rtx reg, insn;
{
register rtx p = insn;
register int regno = REGNO (reg);
rtx temp;
/* Number of sets we have to insist on finding after INSN. */
int count = n_sets - 1;
int old = VARRAY_INT (set_in_loop, regno);
int value = 0;
int this;
/* If N_SETS hit the limit, we can't rely on its value. */
if (n_sets == 127)
return 0;
VARRAY_INT (set_in_loop, regno) = 0;
while (count > 0)
{
register enum rtx_code code;
rtx set;
p = NEXT_INSN (p);
code = GET_CODE (p);
/* If library call, skip to end of it. */
if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
p = XEXP (temp, 0);
this = 0;
if (code == INSN
&& (set = single_set (p))
&& GET_CODE (SET_DEST (set)) == REG
&& REGNO (SET_DEST (set)) == regno)
{
this = invariant_p (SET_SRC (set));
if (this != 0)
value |= this;
else if ((temp = find_reg_note (p, REG_EQUAL, NULL_RTX)))
{
/* If this is a libcall, then any invariant REG_EQUAL note is OK.
If this is an ordinary insn, then only CONSTANT_P REG_EQUAL
notes are OK. */
this = (CONSTANT_P (XEXP (temp, 0))
|| (find_reg_note (p, REG_RETVAL, NULL_RTX)
&& invariant_p (XEXP (temp, 0))));
if (this != 0)
value |= this;
}
}
if (this != 0)
count--;
else if (code != NOTE)
{
VARRAY_INT (set_in_loop, regno) = old;
return 0;
}
}
VARRAY_INT (set_in_loop, regno) = old;
/* If invariant_p ever returned 2, we return 2. */
return 1 + (value & 2);
}
#if 0
/* I don't think this condition is sufficient to allow INSN
to be moved, so we no longer test it. */
/* Return 1 if all insns in the basic block of INSN and following INSN
that set REG are invariant according to TABLE. */
static int
all_sets_invariant_p (reg, insn, table)
rtx reg, insn;
short *table;
{
register rtx p = insn;
register int regno = REGNO (reg);
while (1)
{
register enum rtx_code code;
p = NEXT_INSN (p);
code = GET_CODE (p);
if (code == CODE_LABEL || code == JUMP_INSN)
return 1;
if (code == INSN && GET_CODE (PATTERN (p)) == SET
&& GET_CODE (SET_DEST (PATTERN (p))) == REG
&& REGNO (SET_DEST (PATTERN (p))) == regno)
{
if (!invariant_p (SET_SRC (PATTERN (p)), table))
return 0;
}
}
}
#endif /* 0 */
/* Look at all uses (not sets) of registers in X. For each, if it is
the single use, set USAGE[REGNO] to INSN; if there was a previous use in
a different insn, set USAGE[REGNO] to const0_rtx. */
static void
find_single_use_in_loop (insn, x, usage)
rtx insn;
rtx x;
varray_type usage;
{
enum rtx_code code = GET_CODE (x);
char *fmt = GET_RTX_FORMAT (code);
int i, j;
if (code == REG)
VARRAY_RTX (usage, REGNO (x))
= (VARRAY_RTX (usage, REGNO (x)) != 0
&& VARRAY_RTX (usage, REGNO (x)) != insn)
? const0_rtx : insn;
else if (code == SET)
{
/* Don't count SET_DEST if it is a REG; otherwise count things
in SET_DEST because if a register is partially modified, it won't
show up as a potential movable so we don't care how USAGE is set
for it. */
if (GET_CODE (SET_DEST (x)) != REG)
find_single_use_in_loop (insn, SET_DEST (x), usage);
find_single_use_in_loop (insn, SET_SRC (x), usage);
}
else
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e' && XEXP (x, i) != 0)
find_single_use_in_loop (insn, XEXP (x, i), usage);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
find_single_use_in_loop (insn, XVECEXP (x, i, j), usage);
}
}
/* Count and record any set in X which is contained in INSN. Update
MAY_NOT_MOVE and LAST_SET for any register set in X. */
static void
count_one_set (insn, x, may_not_move, last_set)
rtx insn, x;
varray_type may_not_move;
rtx *last_set;
{
if (GET_CODE (x) == CLOBBER && GET_CODE (XEXP (x, 0)) == REG)
/* Don't move a reg that has an explicit clobber.
It's not worth the pain to try to do it correctly. */
VARRAY_CHAR (may_not_move, REGNO (XEXP (x, 0))) = 1;
if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
{
rtx dest = SET_DEST (x);
while (GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT
|| GET_CODE (dest) == SIGN_EXTRACT
|| GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG)
{
register int regno = REGNO (dest);
/* If this is the first setting of this reg
in current basic block, and it was set before,
it must be set in two basic blocks, so it cannot
be moved out of the loop. */
if (VARRAY_INT (set_in_loop, regno) > 0
&& last_set[regno] == 0)
VARRAY_CHAR (may_not_move, regno) = 1;
/* If this is not first setting in current basic block,
see if reg was used in between previous one and this.
If so, neither one can be moved. */
if (last_set[regno] != 0
&& reg_used_between_p (dest, last_set[regno], insn))
VARRAY_CHAR (may_not_move, regno) = 1;
if (VARRAY_INT (set_in_loop, regno) < 127)
++VARRAY_INT (set_in_loop, regno);
last_set[regno] = insn;
}
}
}
/* Increment SET_IN_LOOP at the index of each register
that is modified by an insn between FROM and TO.
If the value of an element of SET_IN_LOOP becomes 127 or more,
stop incrementing it, to avoid overflow.
Store in SINGLE_USAGE[I] the single insn in which register I is
used, if it is only used once. Otherwise, it is set to 0 (for no
uses) or const0_rtx for more than one use. This parameter may be zero,
in which case this processing is not done.
Store in *COUNT_PTR the number of actual instruction
in the loop. We use this to decide what is worth moving out. */
/* last_set[n] is nonzero iff reg n has been set in the current basic block.
In that case, it is the insn that last set reg n. */
static void
count_loop_regs_set (from, to, may_not_move, single_usage, count_ptr, nregs)
register rtx from, to;
varray_type may_not_move;
varray_type single_usage;
int *count_ptr;
int nregs;
{
register rtx *last_set = (rtx *) alloca (nregs * sizeof (rtx));
register rtx insn;
register int count = 0;
bzero ((char *) last_set, nregs * sizeof (rtx));
for (insn = from; insn != to; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
++count;
/* Record registers that have exactly one use. */
find_single_use_in_loop (insn, PATTERN (insn), single_usage);
/* Include uses in REG_EQUAL notes. */
if (REG_NOTES (insn))
find_single_use_in_loop (insn, REG_NOTES (insn), single_usage);
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
count_one_set (insn, PATTERN (insn), may_not_move, last_set);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
register int i;
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
count_one_set (insn, XVECEXP (PATTERN (insn), 0, i),
may_not_move, last_set);
}
}
if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN)
bzero ((char *) last_set, nregs * sizeof (rtx));
}
*count_ptr = count;
}
/* Given a loop that is bounded by LOOP_START and LOOP_END
and that is entered at SCAN_START,
return 1 if the register set in SET contained in insn INSN is used by
any insn that precedes INSN in cyclic order starting
from the loop entry point.
We don't want to use INSN_LUID here because if we restrict INSN to those
that have a valid INSN_LUID, it means we cannot move an invariant out
from an inner loop past two loops. */
static int
loop_reg_used_before_p (set, insn, loop_start, scan_start, loop_end)
rtx set, insn, loop_start, scan_start, loop_end;
{
rtx reg = SET_DEST (set);
rtx p;
/* Scan forward checking for register usage. If we hit INSN, we
are done. Otherwise, if we hit LOOP_END, wrap around to LOOP_START. */
for (p = scan_start; p != insn; p = NEXT_INSN (p))
{
if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
&& reg_overlap_mentioned_p (reg, PATTERN (p)))
return 1;
if (p == loop_end)
p = loop_start;
}
return 0;
}
/* A "basic induction variable" or biv is a pseudo reg that is set
(within this loop) only by incrementing or decrementing it. */
/* A "general induction variable" or giv is a pseudo reg whose
value is a linear function of a biv. */
/* Bivs are recognized by `basic_induction_var';
Givs by `general_induction_var'. */
/* Indexed by register number, indicates whether or not register is an
induction variable, and if so what type. */
varray_type reg_iv_type;
/* Indexed by register number, contains pointer to `struct induction'
if register is an induction variable. This holds general info for
all induction variables. */
varray_type reg_iv_info;
/* Indexed by register number, contains pointer to `struct iv_class'
if register is a basic induction variable. This holds info describing
the class (a related group) of induction variables that the biv belongs
to. */
struct iv_class **reg_biv_class;
/* The head of a list which links together (via the next field)
every iv class for the current loop. */
struct iv_class *loop_iv_list;
/* Givs made from biv increments are always splittable for loop unrolling.
Since there is no regscan info for them, we have to keep track of them
separately. */
int first_increment_giv, last_increment_giv;
/* Communication with routines called via `note_stores'. */
static rtx note_insn;
/* Dummy register to have non-zero DEST_REG for DEST_ADDR type givs. */
static rtx addr_placeholder;
/* ??? Unfinished optimizations, and possible future optimizations,
for the strength reduction code. */
/* ??? The interaction of biv elimination, and recognition of 'constant'
bivs, may cause problems. */
/* ??? Add heuristics so that DEST_ADDR strength reduction does not cause
performance problems.
Perhaps don't eliminate things that can be combined with an addressing
mode. Find all givs that have the same biv, mult_val, and add_val;
then for each giv, check to see if its only use dies in a following
memory address. If so, generate a new memory address and check to see
if it is valid. If it is valid, then store the modified memory address,
otherwise, mark the giv as not done so that it will get its own iv. */
/* ??? Could try to optimize branches when it is known that a biv is always
positive. */
/* ??? When replace a biv in a compare insn, we should replace with closest
giv so that an optimized branch can still be recognized by the combiner,
e.g. the VAX acb insn. */
/* ??? Many of the checks involving uid_luid could be simplified if regscan
was rerun in loop_optimize whenever a register was added or moved.
Also, some of the optimizations could be a little less conservative. */
/* Perform strength reduction and induction variable elimination.
Pseudo registers created during this function will be beyond the last
valid index in several tables including n_times_set and regno_last_uid.
This does not cause a problem here, because the added registers cannot be
givs outside of their loop, and hence will never be reconsidered.
But scan_loop must check regnos to make sure they are in bounds.
SCAN_START is the first instruction in the loop, as the loop would
actually be executed. END is the NOTE_INSN_LOOP_END. LOOP_TOP is
the first instruction in the loop, as it is layed out in the
instruction stream. LOOP_START is the NOTE_INSN_LOOP_BEG.
LOOP_CONT is the NOTE_INSN_LOOP_CONT. */
static void
strength_reduce (scan_start, end, loop_top, insn_count,
loop_start, loop_end, loop_cont, unroll_p, bct_p)
rtx scan_start;
rtx end;
rtx loop_top;
int insn_count;
rtx loop_start;
rtx loop_end;
rtx loop_cont;
int unroll_p, bct_p ATTRIBUTE_UNUSED;
{
rtx p;
rtx set;
rtx inc_val;
rtx mult_val;
rtx dest_reg;
rtx *location;
/* This is 1 if current insn is not executed at least once for every loop
iteration. */
int not_every_iteration = 0;
/* This is 1 if current insn may be executed more than once for every
loop iteration. */
int maybe_multiple = 0;
/* This is 1 if we have past a branch back to the top of the loop
(aka a loop latch). */
int past_loop_latch = 0;
/* Temporary list pointers for traversing loop_iv_list. */
struct iv_class *bl, **backbl;
/* Ratio of extra register life span we can justify
for saving an instruction. More if loop doesn't call subroutines
since in that case saving an insn makes more difference
and more registers are available. */
/* ??? could set this to last value of threshold in move_movables */
int threshold = (loop_has_call ? 1 : 2) * (3 + n_non_fixed_regs);
/* Map of pseudo-register replacements. */
rtx *reg_map;
int reg_map_size;
int call_seen;
rtx test;
rtx end_insert_before;
int loop_depth = 0;
int n_extra_increment;
struct loop_info loop_iteration_info;
struct loop_info *loop_info = &loop_iteration_info;
/* If scan_start points to the loop exit test, we have to be wary of
subversive use of gotos inside expression statements. */
if (prev_nonnote_insn (scan_start) != prev_nonnote_insn (loop_start))
maybe_multiple = back_branch_in_range_p (scan_start, loop_start, loop_end);
VARRAY_INT_INIT (reg_iv_type, max_reg_before_loop, "reg_iv_type");
VARRAY_GENERIC_PTR_INIT (reg_iv_info, max_reg_before_loop, "reg_iv_info");
reg_biv_class = (struct iv_class **)
alloca (max_reg_before_loop * sizeof (struct iv_class *));
bzero ((char *) reg_biv_class, (max_reg_before_loop
* sizeof (struct iv_class *)));
loop_iv_list = 0;
addr_placeholder = gen_reg_rtx (Pmode);
/* Save insn immediately after the loop_end. Insns inserted after loop_end
must be put before this insn, so that they will appear in the right
order (i.e. loop order).
If loop_end is the end of the current function, then emit a
NOTE_INSN_DELETED after loop_end and set end_insert_before to the
dummy note insn. */
if (NEXT_INSN (loop_end) != 0)
end_insert_before = NEXT_INSN (loop_end);
else
end_insert_before = emit_note_after (NOTE_INSN_DELETED, loop_end);
/* Scan through loop to find all possible bivs. */
for (p = next_insn_in_loop (scan_start, scan_start, end, loop_top);
p != NULL_RTX;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
if (GET_CODE (p) == INSN
&& (set = single_set (p))
&& GET_CODE (SET_DEST (set)) == REG)
{
dest_reg = SET_DEST (set);
if (REGNO (dest_reg) < max_reg_before_loop
&& REGNO (dest_reg) >= FIRST_PSEUDO_REGISTER
&& REG_IV_TYPE (REGNO (dest_reg)) != NOT_BASIC_INDUCT)
{
if (basic_induction_var (SET_SRC (set), GET_MODE (SET_SRC (set)),
dest_reg, p, &inc_val, &mult_val,
&location))
{
/* It is a possible basic induction variable.
Create and initialize an induction structure for it. */
struct induction *v
= (struct induction *) alloca (sizeof (struct induction));
record_biv (v, p, dest_reg, inc_val, mult_val, location,
not_every_iteration, maybe_multiple);
REG_IV_TYPE (REGNO (dest_reg)) = BASIC_INDUCT;
}
else if (REGNO (dest_reg) < max_reg_before_loop)
REG_IV_TYPE (REGNO (dest_reg)) = NOT_BASIC_INDUCT;
}
}
/* Past CODE_LABEL, we get to insns that may be executed multiple
times. The only way we can be sure that they can't is if every
jump insn between here and the end of the loop either
returns, exits the loop, is a jump to a location that is still
behind the label, or is a jump to the loop start. */
if (GET_CODE (p) == CODE_LABEL)
{
rtx insn = p;
maybe_multiple = 0;
while (1)
{
insn = NEXT_INSN (insn);
if (insn == scan_start)
break;
if (insn == end)
{
if (loop_top != 0)
insn = loop_top;
else
break;
if (insn == scan_start)
break;
}
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != RETURN
&& (! condjump_p (insn)
|| (JUMP_LABEL (insn) != 0
&& JUMP_LABEL (insn) != scan_start
&& ! loop_insn_first_p (p, JUMP_LABEL (insn)))))
{
maybe_multiple = 1;
break;
}
}
}
/* Past a jump, we get to insns for which we can't count
on whether they will be executed during each iteration. */
/* This code appears twice in strength_reduce. There is also similar
code in scan_loop. */
if (GET_CODE (p) == JUMP_INSN
/* If we enter the loop in the middle, and scan around to the
beginning, don't set not_every_iteration for that.
This can be any kind of jump, since we want to know if insns
will be executed if the loop is executed. */
&& ! (JUMP_LABEL (p) == loop_top
&& ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p))
|| (NEXT_INSN (p) == loop_end && condjump_p (p)))))
{
rtx label = 0;
/* If this is a jump outside the loop, then it also doesn't
matter. Check to see if the target of this branch is on the
loop_number_exits_labels list. */
for (label = loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]];
label;
label = LABEL_NEXTREF (label))
if (XEXP (label, 0) == JUMP_LABEL (p))
break;
if (! label)
not_every_iteration = 1;
}
else if (GET_CODE (p) == NOTE)
{
/* At the virtual top of a converted loop, insns are again known to
be executed each iteration: logically, the loop begins here
even though the exit code has been duplicated.
Insns are also again known to be executed each iteration at
the LOOP_CONT note. */
if ((NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_CONT)
&& loop_depth == 0)
not_every_iteration = 0;
else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
loop_depth++;
else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
loop_depth--;
}
/* Note if we pass a loop latch. If we do, then we can not clear
NOT_EVERY_ITERATION below when we pass the last CODE_LABEL in
a loop since a jump before the last CODE_LABEL may have started
a new loop iteration.
Note that LOOP_TOP is only set for rotated loops and we need
this check for all loops, so compare against the CODE_LABEL
which immediately follows LOOP_START. */
if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == NEXT_INSN (loop_start))
past_loop_latch = 1;
/* Unlike in the code motion pass where MAYBE_NEVER indicates that
an insn may never be executed, NOT_EVERY_ITERATION indicates whether
or not an insn is known to be executed each iteration of the
loop, whether or not any iterations are known to occur.
Therefore, if we have just passed a label and have no more labels
between here and the test insn of the loop, and we have not passed
a jump to the top of the loop, then we know these insns will be
executed each iteration. */
if (not_every_iteration
&& ! past_loop_latch
&& GET_CODE (p) == CODE_LABEL
&& no_labels_between_p (p, loop_end)
&& loop_insn_first_p (p, loop_cont))
not_every_iteration = 0;
}
/* Scan loop_iv_list to remove all regs that proved not to be bivs.
Make a sanity check against n_times_set. */
for (backbl = &loop_iv_list, bl = *backbl; bl; bl = bl->next)
{
if (REG_IV_TYPE (bl->regno) != BASIC_INDUCT
/* Above happens if register modified by subreg, etc. */
/* Make sure it is not recognized as a basic induction var: */
|| VARRAY_INT (n_times_set, bl->regno) != bl->biv_count
/* If never incremented, it is invariant that we decided not to
move. So leave it alone. */
|| ! bl->incremented)
{
if (loop_dump_stream)
fprintf (loop_dump_stream, "Reg %d: biv discarded, %s\n",
bl->regno,
(REG_IV_TYPE (bl->regno) != BASIC_INDUCT
? "not induction variable"
: (! bl->incremented ? "never incremented"
: "count error")));
REG_IV_TYPE (bl->regno) = NOT_BASIC_INDUCT;
*backbl = bl->next;
}
else
{
backbl = &bl->next;
if (loop_dump_stream)
fprintf (loop_dump_stream, "Reg %d: biv verified\n", bl->regno);
}
}
/* Exit if there are no bivs. */
if (! loop_iv_list)
{
/* Can still unroll the loop anyways, but indicate that there is no
strength reduction info available. */
if (unroll_p)
unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
loop_info, 0);
return;
}
/* Find initial value for each biv by searching backwards from loop_start,
halting at first label. Also record any test condition. */
call_seen = 0;
for (p = loop_start; p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
{
note_insn = p;
if (GET_CODE (p) == CALL_INSN)
call_seen = 1;
if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN)
note_stores (PATTERN (p), record_initial);
/* Record any test of a biv that branches around the loop if no store
between it and the start of loop. We only care about tests with
constants and registers and only certain of those. */
if (GET_CODE (p) == JUMP_INSN
&& JUMP_LABEL (p) != 0
&& next_real_insn (JUMP_LABEL (p)) == next_real_insn (loop_end)
&& (test = get_condition_for_loop (p)) != 0
&& GET_CODE (XEXP (test, 0)) == REG
&& REGNO (XEXP (test, 0)) < max_reg_before_loop
&& (bl = reg_biv_class[REGNO (XEXP (test, 0))]) != 0
&& valid_initial_value_p (XEXP (test, 1), p, call_seen, loop_start)
&& bl->init_insn == 0)
{
/* If an NE test, we have an initial value! */
if (GET_CODE (test) == NE)
{
bl->init_insn = p;
bl->init_set = gen_rtx_SET (VOIDmode,
XEXP (test, 0), XEXP (test, 1));
}
else
bl->initial_test = test;
}
}
/* Look at the each biv and see if we can say anything better about its
initial value from any initializing insns set up above. (This is done
in two passes to avoid missing SETs in a PARALLEL.) */
for (backbl = &loop_iv_list; (bl = *backbl); backbl = &bl->next)
{
rtx src;
rtx note;
if (! bl->init_insn)
continue;
/* IF INIT_INSN has a REG_EQUAL or REG_EQUIV note and the value
is a constant, use the value of that. */
if (((note = find_reg_note (bl->init_insn, REG_EQUAL, 0)) != NULL
&& CONSTANT_P (XEXP (note, 0)))
|| ((note = find_reg_note (bl->init_insn, REG_EQUIV, 0)) != NULL
&& CONSTANT_P (XEXP (note, 0))))
src = XEXP (note, 0);
else
src = SET_SRC (bl->init_set);
if (loop_dump_stream)
fprintf (loop_dump_stream,
"Biv %d initialized at insn %d: initial value ",
bl->regno, INSN_UID (bl->init_insn));
if ((GET_MODE (src) == GET_MODE (regno_reg_rtx[bl->regno])
|| GET_MODE (src) == VOIDmode)
&& valid_initial_value_p (src, bl->init_insn, call_seen, loop_start))
{
bl->initial_value = src;
if (loop_dump_stream)
{
if (GET_CODE (src) == CONST_INT)
{
fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, INTVAL (src));
fputc ('\n', loop_dump_stream);
}
else
{
print_rtl (loop_dump_stream, src);
fprintf (loop_dump_stream, "\n");
}
}
}
else
{
struct iv_class *bl2 = 0;
rtx increment;
/* Biv initial value is not a simple move. If it is the sum of
another biv and a constant, check if both bivs are incremented
in lockstep. Then we are actually looking at a giv.
For simplicity, we only handle the case where there is but a
single increment, and the register is not used elsewhere. */
if (bl->biv_count == 1
&& bl->regno < max_reg_before_loop
&& uid_luid[REGNO_LAST_UID (bl->regno)] < INSN_LUID (loop_end)
&& GET_CODE (src) == PLUS
&& GET_CODE (XEXP (src, 0)) == REG
&& CONSTANT_P (XEXP (src, 1))
&& ((increment = biv_total_increment (bl, loop_start, loop_end))
!= NULL_RTX))
{
int regno = REGNO (XEXP (src, 0));
for (bl2 = loop_iv_list; bl2; bl2 = bl2->next)
if (bl2->regno == regno)
break;
}
/* Now, can we transform this biv into a giv? */
if (bl2
&& bl2->biv_count == 1
&& rtx_equal_p (increment,
biv_total_increment (bl2, loop_start, loop_end))
/* init_insn is only set to insns that are before loop_start
without any intervening labels. */
&& ! reg_set_between_p (bl2->biv->src_reg,
PREV_INSN (bl->init_insn), loop_start)
/* The register from BL2 must be set before the register from
BL is set, or we must be able to move the latter set after
the former set. Currently there can't be any labels
in-between when biv_toal_increment returns nonzero both times
but we test it here in case some day some real cfg analysis
gets used to set always_computable. */
&& ((loop_insn_first_p (bl2->biv->insn, bl->biv->insn)
&& no_labels_between_p (bl2->biv->insn, bl->biv->insn))
|| (! reg_used_between_p (bl->biv->src_reg, bl->biv->insn,
bl2->biv->insn)
&& no_jumps_between_p (bl->biv->insn, bl2->biv->insn)))
&& validate_change (bl->biv->insn,
&SET_SRC (single_set (bl->biv->insn)),
copy_rtx (src), 0))
{
int loop_num = uid_loop_num[INSN_UID (loop_start)];
rtx dominator = loop_number_cont_dominator[loop_num];
rtx giv = bl->biv->src_reg;
rtx giv_insn = bl->biv->insn;
rtx after_giv = NEXT_INSN (giv_insn);
if (loop_dump_stream)
fprintf (loop_dump_stream, "is giv of biv %d\n", bl2->regno);
/* Let this giv be discovered by the generic code. */
REG_IV_TYPE (bl->regno) = UNKNOWN_INDUCT;
/* We can get better optimization if we can move the giv setting
before the first giv use. */
if (dominator
&& ! loop_insn_first_p (dominator, scan_start)
&& ! reg_set_between_p (bl2->biv->src_reg, loop_start,
dominator)
&& ! reg_used_between_p (giv, loop_start, dominator)
&& ! reg_used_between_p (giv, giv_insn, loop_end))
{
rtx p;
rtx next;
for (next = NEXT_INSN (dominator); ; next = NEXT_INSN (next))
{
if ((GET_RTX_CLASS (GET_CODE (next)) == 'i'
&& (reg_mentioned_p (giv, PATTERN (next))
|| reg_set_p (bl2->biv->src_reg, next)))
|| GET_CODE (next) == JUMP_INSN)
break;
#ifdef HAVE_cc0
if (GET_RTX_CLASS (GET_CODE (next)) != 'i'
|| ! sets_cc0_p (PATTERN (next)))
#endif
dominator = next;
}
if (loop_dump_stream)
fprintf (loop_dump_stream, "move after insn %d\n",
INSN_UID (dominator));
/* Avoid problems with luids by actually moving the insn
and adjusting all luids in the range. */
reorder_insns (giv_insn, giv_insn, dominator);
for (p = dominator; INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
compute_luids (giv_insn, after_giv, INSN_LUID (p));
/* If the only purpose of the init insn is to initialize
this giv, delete it. */
if (single_set (bl->init_insn)
&& ! reg_used_between_p (giv, bl->init_insn, loop_start))
delete_insn (bl->init_insn);
}
else if (! loop_insn_first_p (bl2->biv->insn, bl->biv->insn))
{
rtx p = PREV_INSN (giv_insn);
while (INSN_UID (p) >= max_uid_for_loop)
p = PREV_INSN (p);
reorder_insns (giv_insn, giv_insn, bl2->biv->insn);
compute_luids (after_giv, NEXT_INSN (giv_insn),
INSN_LUID (p));
}
/* Remove this biv from the chain. */
if (bl->next)
*bl = *bl->next;
else
{
*backbl = 0;
break;
}
}
/* If we can't make it a giv,
let biv keep initial value of "itself". */
else if (loop_dump_stream)
fprintf (loop_dump_stream, "is complex\n");
}
}
/* If a biv is unconditionally incremented several times in a row, convert
all but the last increment into a giv. */
/* Get an upper bound for the number of registers
we might have after all bivs have been processed. */
first_increment_giv = max_reg_num ();
for (n_extra_increment = 0, bl = loop_iv_list; bl; bl = bl->next)
n_extra_increment += bl->biv_count - 1;
/* If the loop contains volatile memory references do not allow any
replacements to take place, since this could loose the volatile
markers.
Disabled for the gcc-2.95 release. There are still some problems with
giv recombination. We have a patch from Joern which should fix those
problems. But the patch is fairly complex and not really suitable for
the gcc-2.95 branch at this stage. */
if (0 && n_extra_increment && ! loop_has_volatile)
{
int nregs = first_increment_giv + n_extra_increment;
/* Reallocate reg_iv_type and reg_iv_info. */
VARRAY_GROW (reg_iv_type, nregs);
VARRAY_GROW (reg_iv_info, nregs);
for (bl = loop_iv_list; bl; bl = bl->next)
{
struct induction **vp, *v, *next;
int biv_dead_after_loop = 0;
/* The biv increments lists are in reverse order. Fix this first. */
for (v = bl->biv, bl->biv = 0; v; v = next)
{
next = v->next_iv;
v->next_iv = bl->biv;
bl->biv = v;
}
/* We must guard against the case that an early exit between v->insn
and next->insn leaves the biv live after the loop, since that
would mean that we'd be missing an increment for the final
value. The following test to set biv_dead_after_loop is like
the first part of the test to set bl->eliminable.
We don't check here if we can calculate the final value, since
this can't succeed if we already know that there is a jump
between v->insn and next->insn, yet next->always_executed is
set and next->maybe_multiple is cleared. Such a combination
implies that the jump destination is outside the loop.
If we want to make this check more sophisticated, we should
check each branch between v->insn and next->insn individually
to see if the biv is dead at its destination. */
if (uid_luid[REGNO_LAST_UID (bl->regno)] < INSN_LUID (loop_end)
&& bl->init_insn
&& INSN_UID (bl->init_insn) < max_uid_for_loop
&& (uid_luid[REGNO_FIRST_UID (bl->regno)]
>= INSN_LUID (bl->init_insn))
#ifdef HAVE_decrement_and_branch_until_zero
&& ! bl->nonneg
#endif
&& ! reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set)))
biv_dead_after_loop = 1;
for (vp = &bl->biv, next = *vp; v = next, next = v->next_iv;)
{
HOST_WIDE_INT offset;
rtx set, add_val, old_reg, dest_reg, last_use_insn;
int old_regno, new_regno;
if (! v->always_executed
|| v->maybe_multiple
|| GET_CODE (v->add_val) != CONST_INT
|| ! next->always_executed
|| next->maybe_multiple
|| ! CONSTANT_P (next->add_val)
|| v->mult_val != const1_rtx
|| next->mult_val != const1_rtx
|| ! (biv_dead_after_loop
|| no_jumps_between_p (v->insn, next->insn)))
{
vp = &v->next_iv;
continue;
}
offset = INTVAL (v->add_val);
set = single_set (v->insn);
add_val = plus_constant (next->add_val, offset);
old_reg = v->dest_reg;
dest_reg = gen_reg_rtx (v->mode);
/* Unlike reg_iv_type / reg_iv_info, the other three arrays
have been allocated with some slop space, so we may not
actually need to reallocate them. If we do, the following
if statement will be executed just once in this loop. */
if ((unsigned) max_reg_num () > n_times_set->num_elements)
{
/* Grow all the remaining arrays. */
VARRAY_GROW (set_in_loop, nregs);
VARRAY_GROW (n_times_set, nregs);
VARRAY_GROW (may_not_optimize, nregs);
VARRAY_GROW (reg_single_usage, nregs);
}
if (! validate_change (next->insn, next->location, add_val, 0))
{
vp = &v->next_iv;
continue;
}
/* Here we can try to eliminate the increment by combining
it into the uses. */
/* Set last_use_insn so that we can check against it. */
for (last_use_insn = v->insn, p = NEXT_INSN (v->insn);
p != next->insn;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (reg_mentioned_p (old_reg, PATTERN (p)))
{
last_use_insn = p;
}
}
/* If we can't get the LUIDs for the insns, we can't
calculate the lifetime. This is likely from unrolling
of an inner loop, so there is little point in making this
a DEST_REG giv anyways. */
if (INSN_UID (v->insn) >= max_uid_for_loop
|| INSN_UID (last_use_insn) >= max_uid_for_loop
|| ! validate_change (v->insn, &SET_DEST (set), dest_reg, 0))
{
/* Change the increment at NEXT back to what it was. */
if (! validate_change (next->insn, next->location,
next->add_val, 0))
abort ();
vp = &v->next_iv;
continue;
}
next->add_val = add_val;
v->dest_reg = dest_reg;
v->giv_type = DEST_REG;
v->location = &SET_SRC (set);
v->cant_derive = 0;
v->combined_with = 0;
v->maybe_dead = 0;
v->derive_adjustment = 0;
v->same = 0;
v->ignore = 0;
v->new_reg = 0;
v->final_value = 0;
v->same_insn = 0;
v->auto_inc_opt = 0;
v->unrolled = 0;
v->shared = 0;
v->derived_from = 0;
v->always_computable = 1;
v->always_executed = 1;
v->replaceable = 1;
v->no_const_addval = 0;
old_regno = REGNO (old_reg);
new_regno = REGNO (dest_reg);
VARRAY_INT (set_in_loop, old_regno)--;
VARRAY_INT (set_in_loop, new_regno) = 1;
VARRAY_INT (n_times_set, old_regno)--;
VARRAY_INT (n_times_set, new_regno) = 1;
VARRAY_CHAR (may_not_optimize, new_regno) = 0;
REG_IV_TYPE (new_regno) = GENERAL_INDUCT;
REG_IV_INFO (new_regno) = v;
/* Remove the increment from the list of biv increments,
and record it as a giv. */
*vp = next;
bl->biv_count--;
v->next_iv = bl->giv;
bl->giv = v;
bl->giv_count++;
v->benefit = rtx_cost (SET_SRC (set), SET);
bl->total_benefit += v->benefit;
/* Now replace the biv with DEST_REG in all insns between
the replaced increment and the next increment, and
remember the last insn that needed a replacement. */
for (last_use_insn = v->insn, p = NEXT_INSN (v->insn);
p != next->insn;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
rtx note;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (reg_mentioned_p (old_reg, PATTERN (p)))
{
last_use_insn = p;
if (! validate_replace_rtx (old_reg, dest_reg, p))
abort ();
}
for (note = REG_NOTES (p); note; note = XEXP (note, 1))
{
if (GET_CODE (note) == EXPR_LIST)
XEXP (note, 0)
= replace_rtx (XEXP (note, 0), old_reg, dest_reg);
}
}
v->last_use = last_use_insn;
v->lifetime = INSN_LUID (v->insn) - INSN_LUID (last_use_insn);
/* If the lifetime is zero, it means that this register is really
a dead store. So mark this as a giv that can be ignored.
This will not prevent the biv from being eliminated. */
if (v->lifetime == 0)
v->ignore = 1;
if (loop_dump_stream)
fprintf (loop_dump_stream,
"Increment %d of biv %d converted to giv %d.\n\n",
INSN_UID (v->insn), old_regno, new_regno);
}
}
}
last_increment_giv = max_reg_num () - 1;
/* Search the loop for general induction variables. */
/* A register is a giv if: it is only set once, it is a function of a
biv and a constant (or invariant), and it is not a biv. */
not_every_iteration = 0;
loop_depth = 0;
p = scan_start;
while (1)
{
p = NEXT_INSN (p);
/* At end of a straight-in loop, we are done.
At end of a loop entered at the bottom, scan the top. */
if (p == scan_start)
break;
if (p == end)
{
if (loop_top != 0)
p = loop_top;
else
break;
if (p == scan_start)
break;
}
/* Look for a general induction variable in a register. */
if (GET_CODE (p) == INSN
&& (set = single_set (p))
&& GET_CODE (SET_DEST (set)) == REG
&& ! VARRAY_CHAR (may_not_optimize, REGNO (SET_DEST (set))))
{
rtx src_reg;
rtx add_val;
rtx mult_val;
int benefit;
rtx regnote = 0;
rtx last_consec_insn;
dest_reg = SET_DEST (set);
if (REGNO (dest_reg) < FIRST_PSEUDO_REGISTER)
continue;
if (/* SET_SRC is a giv. */
(general_induction_var (SET_SRC (set), &src_reg, &add_val,
&mult_val, 0, &benefit)
/* Equivalent expression is a giv. */
|| ((regnote = find_reg_note (p, REG_EQUAL, NULL_RTX))
&& general_induction_var (XEXP (regnote, 0), &src_reg,
&add_val, &mult_val, 0,
&benefit)))
/* Don't try to handle any regs made by loop optimization.
We have nothing on them in regno_first_uid, etc. */
&& REGNO (dest_reg) < max_reg_before_loop
/* Don't recognize a BASIC_INDUCT_VAR here. */
&& dest_reg != src_reg
/* This must be the only place where the register is set. */
&& (VARRAY_INT (n_times_set, REGNO (dest_reg)) == 1
/* or all sets must be consecutive and make a giv. */
|| (benefit = consec_sets_giv (benefit, p,
src_reg, dest_reg,
&add_val, &mult_val,
&last_consec_insn))))
{
struct induction *v
= (struct induction *) alloca (sizeof (struct induction));
/* If this is a library call, increase benefit. */
if (find_reg_note (p, REG_RETVAL, NULL_RTX))
benefit += libcall_benefit (p);
/* Skip the consecutive insns, if there are any. */
if (VARRAY_INT (n_times_set, REGNO (dest_reg)) != 1)
p = last_consec_insn;
record_giv (v, p, src_reg, dest_reg, mult_val, add_val, benefit,
DEST_REG, not_every_iteration, NULL_PTR, loop_start,
loop_end);
}
}
#ifndef DONT_REDUCE_ADDR
/* Look for givs which are memory addresses. */
/* This resulted in worse code on a VAX 8600. I wonder if it
still does. */
if (GET_CODE (p) == INSN)
find_mem_givs (PATTERN (p), p, not_every_iteration, loop_start,
loop_end);
#endif
/* Update the status of whether giv can derive other givs. This can
change when we pass a label or an insn that updates a biv. */
if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CODE_LABEL)
update_giv_derive (p);
/* Past a jump, we get to insns for which we can't count
on whether they will be executed during each iteration. */
/* This code appears twice in strength_reduce. There is also similar
code in scan_loop. */
if (GET_CODE (p) == JUMP_INSN
/* If we enter the loop in the middle, and scan around to the
beginning, don't set not_every_iteration for that.
This can be any kind of jump, since we want to know if insns
will be executed if the loop is executed. */
&& ! (JUMP_LABEL (p) == loop_top
&& ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p))
|| (NEXT_INSN (p) == loop_end && condjump_p (p)))))
{
rtx label = 0;
/* If this is a jump outside the loop, then it also doesn't
matter. Check to see if the target of this branch is on the
loop_number_exits_labels list. */
for (label = loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]];
label;
label = LABEL_NEXTREF (label))
if (XEXP (label, 0) == JUMP_LABEL (p))
break;
if (! label)
not_every_iteration = 1;
}
else if (GET_CODE (p) == NOTE)
{
/* At the virtual top of a converted loop, insns are again known to
be executed each iteration: logically, the loop begins here
even though the exit code has been duplicated.
Insns are also again known to be executed each iteration at
the LOOP_CONT note. */
if ((NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_CONT)
&& loop_depth == 0)
not_every_iteration = 0;
else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
loop_depth++;
else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
loop_depth--;
}
/* Unlike in the code motion pass where MAYBE_NEVER indicates that
an insn may never be executed, NOT_EVERY_ITERATION indicates whether
or not an insn is known to be executed each iteration of the
loop, whether or not any iterations are known to occur.
Therefore, if we have just passed a label and have no more labels
between here and the test insn of the loop, we know these insns
will be executed each iteration. */
if (not_every_iteration && GET_CODE (p) == CODE_LABEL
&& no_labels_between_p (p, loop_end)
&& loop_insn_first_p (p, loop_cont))
not_every_iteration = 0;
}
/* Try to calculate and save the number of loop iterations. This is
set to zero if the actual number can not be calculated. This must
be called after all giv's have been identified, since otherwise it may
fail if the iteration variable is a giv. */
loop_iterations (loop_start, loop_end, loop_info);
/* Now for each giv for which we still don't know whether or not it is
replaceable, check to see if it is replaceable because its final value
can be calculated. This must be done after loop_iterations is called,
so that final_giv_value will work correctly. */
for (bl = loop_iv_list; bl; bl = bl->next)
{
struct induction *v;
for (v = bl->giv; v; v = v->next_iv)
if (! v->replaceable && ! v->not_replaceable)
check_final_value (v, loop_start, loop_end, loop_info->n_iterations);
}
/* Try to prove that the loop counter variable (if any) is always
nonnegative; if so, record that fact with a REG_NONNEG note
so that "decrement and branch until zero" insn can be used. */
check_dbra_loop (loop_end, insn_count, loop_start, loop_info);
/* Create reg_map to hold substitutions for replaceable giv regs.
Some givs might have been made from biv increments, so look at
reg_iv_type for a suitable size. */
reg_map_size = reg_iv_type->num_elements;
reg_map = (rtx *) alloca (reg_map_size * sizeof (rtx));
bzero ((char *) reg_map, reg_map_size * sizeof (rtx));
/* Examine each iv class for feasibility of strength reduction/induction
variable elimination. */
for (bl = loop_iv_list; bl; bl = bl->next)
{
struct induction *v;
int benefit;
int all_reduced;
rtx final_value = 0;
unsigned nregs;
/* Test whether it will be possible to eliminate this biv
provided all givs are reduced. This is possible if either
the reg is not used outside the loop, or we can compute
what its final value will be.
For architectures with a decrement_and_branch_until_zero insn,
don't do this if we put a REG_NONNEG note on the endtest for
this biv. */
/* Compare against bl->init_insn rather than loop_start.
We aren't concerned with any uses of the biv between
init_insn and loop_start since these won't be affected
by the value of the biv elsewhere in the function, so
long as init_insn doesn't use the biv itself.
March 14, 1989 -- self@bayes.arc.nasa.gov */
if ((uid_luid[REGNO_LAST_UID (bl->regno)] < INSN_LUID (loop_end)
&& bl->init_insn
&& INSN_UID (bl->init_insn) < max_uid_for_loop
&& uid_luid[REGNO_FIRST_UID (bl->regno)] >= INSN_LUID (bl->init_insn)
#ifdef HAVE_decrement_and_branch_until_zero
&& ! bl->nonneg
#endif
&& ! reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set)))
|| ((final_value = final_biv_value (bl, loop_start, loop_end,
loop_info->n_iterations))
#ifdef HAVE_decrement_and_branch_until_zero
&& ! bl->nonneg
#endif
))
bl->eliminable = maybe_eliminate_biv (bl, loop_start, end, 0,
threshold, insn_count);
else
{
if (loop_dump_stream)
{
fprintf (loop_dump_stream,
"Cannot eliminate biv %d.\n",
bl->regno);
fprintf (loop_dump_stream,
"First use: insn %d, last use: insn %d.\n",
REGNO_FIRST_UID (bl->regno),
REGNO_LAST_UID (bl->regno));
}
}
/* Combine all giv's for this iv_class. */
combine_givs (bl);
/* This will be true at the end, if all givs which depend on this
biv have been strength reduced.
We can't (currently) eliminate the biv unless this is so. */
all_reduced = 1;
/* Check each giv in this class to see if we will benefit by reducing
it. Skip giv's combined with others. */
for (v = bl->giv; v; v = v->next_iv)
{
struct induction *tv;
if (v->ignore || v->same)
continue;
benefit = v->benefit;
/* Reduce benefit if not replaceable, since we will insert
a move-insn to replace the insn that calculates this giv.
Don't do this unless the giv is a user variable, since it
will often be marked non-replaceable because of the duplication
of the exit code outside the loop. In such a case, the copies
we insert are dead and will be deleted. So they don't have
a cost. Similar situations exist. */
/* ??? The new final_[bg]iv_value code does a much better job
of finding replaceable giv's, and hence this code may no longer
be necessary. */
if (! v->replaceable && ! bl->eliminable
&& REG_USERVAR_P (v->dest_reg))
benefit -= copy_cost;
/* Decrease the benefit to count the add-insns that we will
insert to increment the reduced reg for the giv. */
benefit -= add_cost * bl->biv_count;
/* Decide whether to strength-reduce this giv or to leave the code
unchanged (recompute it from the biv each time it is used).
This decision can be made independently for each giv. */
#ifdef AUTO_INC_DEC
/* Attempt to guess whether autoincrement will handle some of the
new add insns; if so, increase BENEFIT (undo the subtraction of
add_cost that was done above). */
if (v->giv_type == DEST_ADDR
&& GET_CODE (v->mult_val) == CONST_INT)
{
if (HAVE_POST_INCREMENT
&& INTVAL (v->mult_val) == GET_MODE_SIZE (v->mem_mode))
benefit += add_cost * bl->biv_count;
else if (HAVE_PRE_INCREMENT
&& INTVAL (v->mult_val) == GET_MODE_SIZE (v->mem_mode))
benefit += add_cost * bl->biv_count;
else if (HAVE_POST_DECREMENT
&& -INTVAL (v->mult_val) == GET_MODE_SIZE (v->mem_mode))
benefit += add_cost * bl->biv_count;
else if (HAVE_PRE_DECREMENT
&& -INTVAL (v->mult_val) == GET_MODE_SIZE (v->mem_mode))
benefit += add_cost * bl->biv_count;
}
#endif
/* If an insn is not to be strength reduced, then set its ignore
flag, and clear all_reduced. */
/* A giv that depends on a reversed biv must be reduced if it is
used after the loop exit, otherwise, it would have the wrong
value after the loop exit. To make it simple, just reduce all
of such giv's whether or not we know they are used after the loop
exit. */
if ( ! flag_reduce_all_givs && v->lifetime * threshold * benefit < insn_count
&& ! bl->reversed )
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"giv of insn %d not worth while, %d vs %d.\n",
INSN_UID (v->insn),
v->lifetime * threshold * benefit, insn_count);
v->ignore = 1;
all_reduced = 0;
}
else
{
/* Check that we can increment the reduced giv without a
multiply insn. If not, reject it. */
for (tv = bl->biv; tv; tv = tv->next_iv)
if (tv->mult_val == const1_rtx
&& ! product_cheap_p (tv->add_val, v->mult_val))
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"giv of insn %d: would need a multiply.\n",
INSN_UID (v->insn));
v->ignore = 1;
all_reduced = 0;
break;
}
}
}
/* Check for givs whose first use is their definition and whose
last use is the definition of another giv. If so, it is likely
dead and should not be used to derive another giv nor to
eliminate a biv. */
for (v = bl->giv; v; v = v->next_iv)
{
if (v->ignore
|| (v->same && v->same->ignore))
continue;
if (v->last_use)
{
struct induction *v1;
for (v1 = bl->giv; v1; v1 = v1->next_iv)
if (v->last_use == v1->insn)
v->maybe_dead = 1;
}
else if (v->giv_type == DEST_REG
&& REGNO_FIRST_UID (REGNO (v->dest_reg)) == INSN_UID (v->insn))
{
struct induction *v1;
for (v1 = bl->giv; v1; v1 = v1->next_iv)
if (REGNO_LAST_UID (REGNO (v->dest_reg)) == INSN_UID (v1->insn))
v->maybe_dead = 1;
}
}
/* Now that we know which givs will be reduced, try to rearrange the
combinations to reduce register pressure.
recombine_givs calls find_life_end, which needs reg_iv_type and
reg_iv_info to be valid for all pseudos. We do the necessary
reallocation here since it allows to check if there are still
more bivs to process. */
nregs = max_reg_num ();
if (nregs > reg_iv_type->num_elements)
{
/* If there are still more bivs to process, allocate some slack
space so that we're not constantly reallocating these arrays. */
if (bl->next)
nregs += nregs / 4;
/* Reallocate reg_iv_type and reg_iv_info. */
VARRAY_GROW (reg_iv_type, nregs);
VARRAY_GROW (reg_iv_info, nregs);
}
#if 0
/* Disabled for the gcc-2.95 release. There are still some problems with
giv recombination. We have a patch from Joern which should fix those
problems. But the patch is fairly complex and not really suitable for
the gcc-2.95 branch at this stage. */
recombine_givs (bl, loop_start, loop_end, unroll_p);
#endif
/* Reduce each giv that we decided to reduce. */
for (v = bl->giv; v; v = v->next_iv)
{
struct induction *tv;
if (! v->ignore && v->same == 0)
{
int auto_inc_opt = 0;
/* If the code for derived givs immediately below has already
allocated a new_reg, we must keep it. */
if (! v->new_reg)
v->new_reg = gen_reg_rtx (v->mode);
if (v->derived_from)
{
struct induction *d = v->derived_from;
/* In case d->dest_reg is not replaceable, we have
to replace it in v->insn now. */
if (! d->new_reg)
d->new_reg = gen_reg_rtx (d->mode);
PATTERN (v->insn)
= replace_rtx (PATTERN (v->insn), d->dest_reg, d->new_reg);
PATTERN (v->insn)
= replace_rtx (PATTERN (v->insn), v->dest_reg, v->new_reg);
if (bl->biv_count != 1)
{
/* For each place where the biv is incremented, add an
insn to set the new, reduced reg for the giv. */
for (tv = bl->biv; tv; tv = tv->next_iv)
{
/* We always emit reduced giv increments before the
biv increment when bl->biv_count != 1. So by
emitting the add insns for derived givs after the
biv increment, they pick up the updated value of
the reduced giv. */
emit_insn_after (copy_rtx (PATTERN (v->insn)),
tv->insn);
}
}
continue;
}
#ifdef AUTO_INC_DEC
/* If the target has auto-increment addressing modes, and
this is an address giv, then try to put the increment
immediately after its use, so that flow can create an
auto-increment addressing mode. */
if (v->giv_type == DEST_ADDR && bl->biv_count == 1
&& bl->biv->always_executed && ! bl->biv->maybe_multiple
/* We don't handle reversed biv's because bl->biv->insn
does not have a valid INSN_LUID. */
&& ! bl->reversed
&& v->always_executed && ! v->maybe_multiple
&& INSN_UID (v->insn) < max_uid_for_loop)
{
/* If other giv's have been combined with this one, then
this will work only if all uses of the other giv's occur
before this giv's insn. This is difficult to check.
We simplify this by looking for the common case where
there is one DEST_REG giv, and this giv's insn is the
last use of the dest_reg of that DEST_REG giv. If the
increment occurs after the address giv, then we can
perform the optimization. (Otherwise, the increment
would have to go before other_giv, and we would not be
able to combine it with the address giv to get an
auto-inc address.) */
if (v->combined_with)
{
struct induction *other_giv = 0;
for (tv = bl->giv; tv; tv = tv->next_iv)
if (tv->same == v)
{
if (other_giv)
break;
else
other_giv = tv;
}
if (! tv && other_giv
&& REGNO (other_giv->dest_reg) < max_reg_before_loop
&& (REGNO_LAST_UID (REGNO (other_giv->dest_reg))
== INSN_UID (v->insn))
&& INSN_LUID (v->insn) < INSN_LUID (bl->biv->insn))
auto_inc_opt = 1;
}
/* Check for case where increment is before the address
giv. Do this test in "loop order". */
else if ((INSN_LUID (v->insn) > INSN_LUID (bl->biv->insn)
&& (INSN_LUID (v->insn) < INSN_LUID (scan_start)
|| (INSN_LUID (bl->biv->insn)
> INSN_LUID (scan_start))))
|| (INSN_LUID (v->insn) < INSN_LUID (scan_start)
&& (INSN_LUID (scan_start)
< INSN_LUID (bl->biv->insn))))
auto_inc_opt = -1;
else
auto_inc_opt = 1;
#ifdef HAVE_cc0
{
rtx prev;
/* We can't put an insn immediately after one setting
cc0, or immediately before one using cc0. */
if ((auto_inc_opt == 1 && sets_cc0_p (PATTERN (v->insn)))
|| (auto_inc_opt == -1
&& (prev = prev_nonnote_insn (v->insn)) != 0
&& GET_RTX_CLASS (GET_CODE (prev)) == 'i'
&& sets_cc0_p (PATTERN (prev))))
auto_inc_opt = 0;
}
#endif
if (auto_inc_opt)
v->auto_inc_opt = 1;
}
#endif
/* For each place where the biv is incremented, add an insn
to increment the new, reduced reg for the giv. */
for (tv = bl->biv; tv; tv = tv->next_iv)
{
rtx insert_before;
if (! auto_inc_opt)
insert_before = tv->insn;
else if (auto_inc_opt == 1)
insert_before = NEXT_INSN (v->insn);
else
insert_before = v->insn;
if (tv->mult_val == const1_rtx)
emit_iv_add_mult (tv->add_val, v->mult_val,
v->new_reg, v->new_reg, insert_before);
else /* tv->mult_val == const0_rtx */
/* A multiply is acceptable here
since this is presumed to be seldom executed. */
emit_iv_add_mult (tv->add_val, v->mult_val,
v->add_val, v->new_reg, insert_before);
}
/* Add code at loop start to initialize giv's reduced reg. */
emit_iv_add_mult (bl->initial_value, v->mult_val,
v->add_val, v->new_reg, loop_start);
}
}
/* Rescan all givs. If a giv is the same as a giv not reduced, mark it
as not reduced.
For each giv register that can be reduced now: if replaceable,
substitute reduced reg wherever the old giv occurs;
else add new move insn "giv_reg = reduced_reg". */
for (v = bl->giv; v; v = v->next_iv)
{
if (v->same && v->same->ignore)
v->ignore = 1;
if (v->ignore)
continue;
/* Update expression if this was combined, in case other giv was
replaced. */
if (v->same)
v->new_reg = replace_rtx (v->new_reg,
v->same->dest_reg, v->same->new_reg);
if (v->giv_type == DEST_ADDR)
/* Store reduced reg as the address in the memref where we found
this giv. */
validate_change (v->insn, v->location, v->new_reg, 0);
else if (v->replaceable)
{
reg_map[REGNO (v->dest_reg)] = v->new_reg;
#if 0
/* I can no longer duplicate the original problem. Perhaps
this is unnecessary now? */
/* Replaceable; it isn't strictly necessary to delete the old
insn and emit a new one, because v->dest_reg is now dead.
However, especially when unrolling loops, the special
handling for (set REG0 REG1) in the second cse pass may
make v->dest_reg live again. To avoid this problem, emit
an insn to set the original giv reg from the reduced giv.
We can not delete the original insn, since it may be part
of a LIBCALL, and the code in flow that eliminates dead
libcalls will fail if it is deleted. */
emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg),
v->insn);
#endif
}
else
{
/* Not replaceable; emit an insn to set the original giv reg from
the reduced giv, same as above. */
emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg),
v->insn);
}
/* When a loop is reversed, givs which depend on the reversed
biv, and which are live outside the loop, must be set to their
correct final value. This insn is only needed if the giv is
not replaceable. The correct final value is the same as the
value that the giv starts the reversed loop with. */
if (bl->reversed && ! v->replaceable)
emit_iv_add_mult (bl->initial_value, v->mult_val,
v->add_val, v->dest_reg, end_insert_before);
else if (v->final_value)
{
rtx insert_before;
/* If the loop has multiple exits, emit the insn before the
loop to ensure that it will always be executed no matter
how the loop exits. Otherwise, emit the insn after the loop,
since this is slightly more efficient. */
if (loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]])
insert_before = loop_start;
else
insert_before = end_insert_before;
emit_insn_before (gen_move_insn (v->dest_reg, v->final_value),
insert_before);
#if 0
/* If the insn to set the final value of the giv was emitted
before the loop, then we must delete the insn inside the loop
that sets it. If this is a LIBCALL, then we must delete
every insn in the libcall. Note, however, that
final_giv_value will only succeed when there are multiple
exits if the giv is dead at each exit, hence it does not
matter that the original insn remains because it is dead
anyways. */
/* Delete the insn inside the loop that sets the giv since
the giv is now set before (or after) the loop. */
delete_insn (v->insn);
#endif
}
if (loop_dump_stream)
{
fprintf (loop_dump_stream, "giv at %d reduced to ",
INSN_UID (v->insn));
print_rtl (loop_dump_stream, v->new_reg);
fprintf (loop_dump_stream, "\n");
}
}
/* All the givs based on the biv bl have been reduced if they
merit it. */
/* For each giv not marked as maybe dead that has been combined with a
second giv, clear any "maybe dead" mark on that second giv.
v->new_reg will either be or refer to the register of the giv it
combined with.
Doing this clearing avoids problems in biv elimination where a
giv's new_reg is a complex value that can't be put in the insn but
the giv combined with (with a reg as new_reg) is marked maybe_dead.
Since the register will be used in either case, we'd prefer it be
used from the simpler giv. */
for (v = bl->giv; v; v = v->next_iv)
if (! v->maybe_dead && v->same)
v->same->maybe_dead = 0;
/* Try to eliminate the biv, if it is a candidate.
This won't work if ! all_reduced,
since the givs we planned to use might not have been reduced.
We have to be careful that we didn't initially think we could eliminate
this biv because of a giv that we now think may be dead and shouldn't
be used as a biv replacement.
Also, there is the possibility that we may have a giv that looks
like it can be used to eliminate a biv, but the resulting insn
isn't valid. This can happen, for example, on the 88k, where a
JUMP_INSN can compare a register only with zero. Attempts to
replace it with a compare with a constant will fail.
Note that in cases where this call fails, we may have replaced some
of the occurrences of the biv with a giv, but no harm was done in
doing so in the rare cases where it can occur. */
if (all_reduced == 1 && bl->eliminable
&& maybe_eliminate_biv (bl, loop_start, end, 1,
threshold, insn_count))
{
/* ?? If we created a new test to bypass the loop entirely,
or otherwise drop straight in, based on this test, then
we might want to rewrite it also. This way some later
pass has more hope of removing the initialization of this
biv entirely. */
/* If final_value != 0, then the biv may be used after loop end
and we must emit an insn to set it just in case.
Reversed bivs already have an insn after the loop setting their
value, so we don't need another one. We can't calculate the
proper final value for such a biv here anyways. */
if (final_value != 0 && ! bl->reversed)
{
rtx insert_before;
/* If the loop has multiple exits, emit the insn before the
loop to ensure that it will always be executed no matter
how the loop exits. Otherwise, emit the insn after the
loop, since this is slightly more efficient. */
if (loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]])
insert_before = loop_start;
else
insert_before = end_insert_before;
emit_insn_before (gen_move_insn (bl->biv->dest_reg, final_value),
end_insert_before);
}
#if 0
/* Delete all of the instructions inside the loop which set
the biv, as they are all dead. If is safe to delete them,
because an insn setting a biv will never be part of a libcall. */
/* However, deleting them will invalidate the regno_last_uid info,
so keeping them around is more convenient. Final_biv_value
will only succeed when there are multiple exits if the biv
is dead at each exit, hence it does not matter that the original
insn remains, because it is dead anyways. */
for (v = bl->biv; v; v = v->next_iv)
delete_insn (v->insn);
#endif
if (loop_dump_stream)
fprintf (loop_dump_stream, "Reg %d: biv eliminated\n",
bl->regno);
}
}
/* Go through all the instructions in the loop, making all the
register substitutions scheduled in REG_MAP. */
for (p = loop_start; p != end; p = NEXT_INSN (p))
if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN)
{
replace_regs (PATTERN (p), reg_map, reg_map_size, 0);
replace_regs (REG_NOTES (p), reg_map, reg_map_size, 0);
INSN_CODE (p) = -1;
}
/* Unroll loops from within strength reduction so that we can use the
induction variable information that strength_reduce has already
collected. */
if (unroll_p)
unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
loop_info, 1);
#ifdef HAVE_decrement_and_branch_on_count
/* Instrument the loop with BCT insn. */
if (HAVE_decrement_and_branch_on_count && bct_p
&& flag_branch_on_count_reg)
insert_bct (loop_start, loop_end, loop_info);
#endif /* HAVE_decrement_and_branch_on_count */
if (loop_dump_stream)
fprintf (loop_dump_stream, "\n");
VARRAY_FREE (reg_iv_type);
VARRAY_FREE (reg_iv_info);
}
/* Return 1 if X is a valid source for an initial value (or as value being
compared against in an initial test).
X must be either a register or constant and must not be clobbered between
the current insn and the start of the loop.
INSN is the insn containing X. */
static int
valid_initial_value_p (x, insn, call_seen, loop_start)
rtx x;
rtx insn;
int call_seen;
rtx loop_start;
{
if (CONSTANT_P (x))
return 1;
/* Only consider pseudos we know about initialized in insns whose luids
we know. */
if (GET_CODE (x) != REG
|| REGNO (x) >= max_reg_before_loop)
return 0;
/* Don't use call-clobbered registers across a call which clobbers it. On
some machines, don't use any hard registers at all. */
if (REGNO (x) < FIRST_PSEUDO_REGISTER
&& (SMALL_REGISTER_CLASSES
|| (call_used_regs[REGNO (x)] && call_seen)))
return 0;
/* Don't use registers that have been clobbered before the start of the
loop. */
if (reg_set_between_p (x, insn, loop_start))
return 0;
return 1;
}
/* Scan X for memory refs and check each memory address
as a possible giv. INSN is the insn whose pattern X comes from.
NOT_EVERY_ITERATION is 1 if the insn might not be executed during
every loop iteration. */
static void
find_mem_givs (x, insn, not_every_iteration, loop_start, loop_end)
rtx x;
rtx insn;
int not_every_iteration;
rtx loop_start, loop_end;
{
register int i, j;
register enum rtx_code code;
register char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case REG:
case CONST_INT:
case CONST:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case PC:
case CC0:
case ADDR_VEC:
case ADDR_DIFF_VEC:
case USE:
case CLOBBER:
return;
case MEM:
{
rtx src_reg;
rtx add_val;
rtx mult_val;
int benefit;
/* This code used to disable creating GIVs with mult_val == 1 and
add_val == 0. However, this leads to lost optimizations when
it comes time to combine a set of related DEST_ADDR GIVs, since
this one would not be seen. */
if (general_induction_var (XEXP (x, 0), &src_reg, &add_val,
&mult_val, 1, &benefit))
{
/* Found one; record it. */
struct induction *v
= (struct induction *) oballoc (sizeof (struct induction));
record_giv (v, insn, src_reg, addr_placeholder, mult_val,
add_val, benefit, DEST_ADDR, not_every_iteration,
&XEXP (x, 0), loop_start, loop_end);
v->mem_mode = GET_MODE (x);
}
}
return;
default:
break;
}
/* Recursively scan the subexpressions for other mem refs. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
if (fmt[i] == 'e')
find_mem_givs (XEXP (x, i), insn, not_every_iteration, loop_start,
loop_end);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
find_mem_givs (XVECEXP (x, i, j), insn, not_every_iteration,
loop_start, loop_end);
}
/* Fill in the data about one biv update.
V is the `struct induction' in which we record the biv. (It is
allocated by the caller, with alloca.)
INSN is the insn that sets it.
DEST_REG is the biv's reg.
MULT_VAL is const1_rtx if the biv is being incremented here, in which case
INC_VAL is the increment. Otherwise, MULT_VAL is const0_rtx and the biv is
being set to INC_VAL.
NOT_EVERY_ITERATION is nonzero if this biv update is not know to be
executed every iteration; MAYBE_MULTIPLE is nonzero if this biv update
can be executed more than once per iteration. If MAYBE_MULTIPLE
and NOT_EVERY_ITERATION are both zero, we know that the biv update is
executed exactly once per iteration. */
static void
record_biv (v, insn, dest_reg, inc_val, mult_val, location,
not_every_iteration, maybe_multiple)
struct induction *v;
rtx insn;
rtx dest_reg;
rtx inc_val;
rtx mult_val;
rtx *location;
int not_every_iteration;
int maybe_multiple;
{
struct iv_class *bl;
v->insn = insn;
v->src_reg = dest_reg;
v->dest_reg = dest_reg;
v->mult_val = mult_val;
v->add_val = inc_val;
v->location = location;
v->mode = GET_MODE (dest_reg);
v->always_computable = ! not_every_iteration;
v->always_executed = ! not_every_iteration;
v->maybe_multiple = maybe_multiple;
/* Add this to the reg's iv_class, creating a class
if this is the first incrementation of the reg. */
bl = reg_biv_class[REGNO (dest_reg)];
if (bl == 0)
{
/* Create and initialize new iv_class. */
bl = (struct iv_class *) oballoc (sizeof (struct iv_class));
bl->regno = REGNO (dest_reg);
bl->biv = 0;
bl->giv = 0;
bl->biv_count = 0;
bl->giv_count = 0;
/* Set initial value to the reg itself. */
bl->initial_value = dest_reg;
/* We haven't seen the initializing insn yet */
bl->init_insn = 0;
bl->init_set = 0;
bl->initial_test = 0;
bl->incremented = 0;
bl->eliminable = 0;
bl->nonneg = 0;
bl->reversed = 0;
bl->total_benefit = 0;
/* Add this class to loop_iv_list. */
bl->next = loop_iv_list;
loop_iv_list = bl;
/* Put it in the array of biv register classes. */
reg_biv_class[REGNO (dest_reg)] = bl;
}
/* Update IV_CLASS entry for this biv. */
v->next_iv = bl->biv;
bl->biv = v;
bl->biv_count++;
if (mult_val == const1_rtx)
bl->incremented = 1;
if (loop_dump_stream)
{
fprintf (loop_dump_stream,
"Insn %d: possible biv, reg %d,",
INSN_UID (insn), REGNO (dest_reg));
if (GET_CODE (inc_val) == CONST_INT)
{
fprintf (loop_dump_stream, " const =");
fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, INTVAL (inc_val));
fputc ('\n', loop_dump_stream);
}
else
{
fprintf (loop_dump_stream, " const = ");
print_rtl (loop_dump_stream, inc_val);
fprintf (loop_dump_stream, "\n");
}
}
}
/* Fill in the data about one giv.
V is the `struct induction' in which we record the giv. (It is
allocated by the caller, with alloca.)
INSN is the insn that sets it.
BENEFIT estimates the savings from deleting this insn.
TYPE is DEST_REG or DEST_ADDR; it says whether the giv is computed
into a register or is used as a memory address.
SRC_REG is the biv reg which the giv is computed from.
DEST_REG is the giv's reg (if the giv is stored in a reg).
MULT_VAL and ADD_VAL are the coefficients used to compute the giv.
LOCATION points to the place where this giv's value appears in INSN. */
static void
record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit,
type, not_every_iteration, location, loop_start, loop_end)
struct induction *v;
rtx insn;
rtx src_reg;
rtx dest_reg;
rtx mult_val, add_val;
int benefit;
enum g_types type;
int not_every_iteration;
rtx *location;
rtx loop_start, loop_end;
{
struct induction *b;
struct iv_class *bl;
rtx set = single_set (insn);
v->insn = insn;
v->src_reg = src_reg;
v->giv_type = type;
v->dest_reg = dest_reg;
v->mult_val = mult_val;
v->add_val = add_val;
v->benefit = benefit;
v->location = location;
v->cant_derive = 0;
v->combined_with = 0;
v->maybe_multiple = 0;
v->maybe_dead = 0;
v->derive_adjustment = 0;
v->same = 0;
v->ignore = 0;
v->new_reg = 0;
v->final_value = 0;
v->same_insn = 0;
v->auto_inc_opt = 0;
v->unrolled = 0;
v->shared = 0;
v->derived_from = 0;
v->last_use = 0;
/* The v->always_computable field is used in update_giv_derive, to
determine whether a giv can be used to derive another giv. For a
DEST_REG giv, INSN computes a new value for the giv, so its value
isn't computable if INSN insn't executed every iteration.
However, for a DEST_ADDR giv, INSN merely uses the value of the giv;
it does not compute a new value. Hence the value is always computable
regardless of whether INSN is executed each iteration. */
if (type == DEST_ADDR)
v->always_computable = 1;
else
v->always_computable = ! not_every_iteration;
v->always_executed = ! not_every_iteration;
if (type == DEST_ADDR)
{
v->mode = GET_MODE (*location);
v->lifetime = 1;
}
else /* type == DEST_REG */
{
v->mode = GET_MODE (SET_DEST (set));
v->lifetime = (uid_luid[REGNO_LAST_UID (REGNO (dest_reg))]
- uid_luid[REGNO_FIRST_UID (REGNO (dest_reg))]);
/* If the lifetime is zero, it means that this register is
really a dead store. So mark this as a giv that can be
ignored. This will not prevent the biv from being eliminated. */
if (v->lifetime == 0)
v->ignore = 1;
REG_IV_TYPE (REGNO (dest_reg)) = GENERAL_INDUCT;
REG_IV_INFO (REGNO (dest_reg)) = v;
}
/* Add the giv to the class of givs computed from one biv. */
bl = reg_biv_class[REGNO (src_reg)];
if (bl)
{
v->next_iv = bl->giv;
bl->giv = v;
/* Don't count DEST_ADDR. This is supposed to count the number of
insns that calculate givs. */
if (type == DEST_REG)
bl->giv_count++;
bl->total_benefit += benefit;
}
else
/* Fatal error, biv missing for this giv? */
abort ();
if (type == DEST_ADDR)
v->replaceable = 1;
else
{
/* The giv can be replaced outright by the reduced register only if all
of the following conditions are true:
- the insn that sets the giv is always executed on any iteration
on which the giv is used at all
(there are two ways to deduce this:
either the insn is executed on every iteration,
or all uses follow that insn in the same basic block),
- the giv is not used outside the loop
- no assignments to the biv occur during the giv's lifetime. */
if (REGNO_FIRST_UID (REGNO (dest_reg)) == INSN_UID (insn)
/* Previous line always fails if INSN was moved by loop opt. */
&& uid_luid[REGNO_LAST_UID (REGNO (dest_reg))] < INSN_LUID (loop_end)
&& (! not_every_iteration
|| last_use_this_basic_block (dest_reg, insn)))
{
/* Now check that there are no assignments to the biv within the
giv's lifetime. This requires two separate checks. */
/* Check each biv update, and fail if any are between the first
and last use of the giv.
If this loop contains an inner loop that was unrolled, then
the insn modifying the biv may have been emitted by the loop
unrolling code, and hence does not have a valid luid. Just
mark the biv as not replaceable in this case. It is not very
useful as a biv, because it is used in two different loops.
It is very unlikely that we would be able to optimize the giv
using this biv anyways. */
v->replaceable = 1;
for (b = bl->biv; b; b = b->next_iv)
{
if (INSN_UID (b->insn) >= max_uid_for_loop
|| ((uid_luid[INSN_UID (b->insn)]
>= uid_luid[REGNO_FIRST_UID (REGNO (dest_reg))])
&& (uid_luid[INSN_UID (b->insn)]
<= uid_luid[REGNO_LAST_UID (REGNO (dest_reg))])))
{
v->replaceable = 0;
v->not_replaceable = 1;
break;
}
}
/* If there are any backwards branches that go from after the
biv update to before it, then this giv is not replaceable. */
if (v->replaceable)
for (b = bl->biv; b; b = b->next_iv)
if (back_branch_in_range_p (b->insn, loop_start, loop_end))
{
v->replaceable = 0;
v->not_replaceable = 1;
break;
}
}
else
{
/* May still be replaceable, we don't have enough info here to
decide. */
v->replaceable = 0;
v->not_replaceable = 0;
}
}
/* Record whether the add_val contains a const_int, for later use by
combine_givs. */
{
rtx tem = add_val;
v->no_const_addval = 1;
if (tem == const0_rtx)
;
else if (GET_CODE (tem) == CONST_INT)
v->no_const_addval = 0;
else if (GET_CODE (tem) == PLUS)
{
while (1)
{
if (GET_CODE (XEXP (tem, 0)) == PLUS)
tem = XEXP (tem, 0);
else if (GET_CODE (XEXP (tem, 1)) == PLUS)
tem = XEXP (tem, 1);
else
break;
}
if (GET_CODE (XEXP (tem, 1)) == CONST_INT)
v->no_const_addval = 0;
}
}
if (loop_dump_stream)
{
if (type == DEST_REG)
fprintf (loop_dump_stream, "Insn %d: giv reg %d",
INSN_UID (insn), REGNO (dest_reg));
else
fprintf (loop_dump_stream, "Insn %d: dest address",
INSN_UID (insn));
fprintf (loop_dump_stream, " src reg %d benefit %d",
REGNO (src_reg), v->benefit);
fprintf (loop_dump_stream, " lifetime %d",
v->lifetime);
if (v->replaceable)
fprintf (loop_dump_stream, " replaceable");
if (v->no_const_addval)
fprintf (loop_dump_stream, " ncav");
if (GET_CODE (mult_val) == CONST_INT)
{
fprintf (loop_dump_stream, " mult ");
fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, INTVAL (mult_val));
}
else
{
fprintf (loop_dump_stream, " mult ");
print_rtl (loop_dump_stream, mult_val);
}
if (GET_CODE (add_val) == CONST_INT)
{
fprintf (loop_dump_stream, " add ");
fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, INTVAL (add_val));
}
else
{
fprintf (loop_dump_stream, " add ");
print_rtl (loop_dump_stream, add_val);
}
}
if (loop_dump_stream)
fprintf (loop_dump_stream, "\n");
}
/* All this does is determine whether a giv can be made replaceable because
its final value can be calculated. This code can not be part of record_giv
above, because final_giv_value requires that the number of loop iterations
be known, and that can not be accurately calculated until after all givs
have been identified. */
static void
check_final_value (v, loop_start, loop_end, n_iterations)
struct induction *v;
rtx loop_start, loop_end;
unsigned HOST_WIDE_INT n_iterations;
{
struct iv_class *bl;
rtx final_value = 0;
bl = reg_biv_class[REGNO (v->src_reg)];
/* DEST_ADDR givs will never reach here, because they are always marked
replaceable above in record_giv. */
/* The giv can be replaced outright by the reduced register only if all
of the following conditions are true:
- the insn that sets the giv is always executed on any iteration
on which the giv is used at all
(there are two ways to deduce this:
either the insn is executed on every iteration,
or all uses follow that insn in the same basic block),
- its final value can be calculated (this condition is different
than the one above in record_giv)
- no assignments to the biv occur during the giv's lifetime. */
#if 0
/* This is only called now when replaceable is known to be false. */
/* Clear replaceable, so that it won't confuse final_giv_value. */
v->replaceable = 0;
#endif
if ((final_value = final_giv_value (v, loop_start, loop_end, n_iterations))
&& (v->always_computable || last_use_this_basic_block (v->dest_reg, v->insn)))
{
int biv_increment_seen = 0;
rtx p = v->insn;
rtx last_giv_use;
v->replaceable = 1;
/* When trying to determine whether or not a biv increment occurs
during the lifetime of the giv, we can ignore uses of the variable
outside the loop because final_value is true. Hence we can not
use regno_last_uid and regno_first_uid as above in record_giv. */
/* Search the loop to determine whether any assignments to the
biv occur during the giv's lifetime. Start with the insn
that sets the giv, and search around the loop until we come
back to that insn again.
Also fail if there is a jump within the giv's lifetime that jumps
to somewhere outside the lifetime but still within the loop. This
catches spaghetti code where the execution order is not linear, and
hence the above test fails. Here we assume that the giv lifetime
does not extend from one iteration of the loop to the next, so as
to make the test easier. Since the lifetime isn't known yet,
this requires two loops. See also record_giv above. */
last_giv_use = v->insn;
while (1)
{
p = NEXT_INSN (p);
if (p == loop_end)
p = NEXT_INSN (loop_start);
if (p == v->insn)
break;
if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN)
{
if (biv_increment_seen)
{
if (reg_mentioned_p (v->dest_reg, PATTERN (p)))
{
v->replaceable = 0;
v->not_replaceable = 1;
break;
}
}
else if (reg_set_p (v->src_reg, PATTERN (p)))
biv_increment_seen = 1;
else if (reg_mentioned_p (v->dest_reg, PATTERN (p)))
last_giv_use = p;
}
}
/* Now that the lifetime of the giv is known, check for branches
from within the lifetime to outside the lifetime if it is still
replaceable. */
if (v->replaceable)
{
p = v->insn;
while (1)
{
p = NEXT_INSN (p);
if (p == loop_end)
p = NEXT_INSN (loop_start);
if (p == last_giv_use)
break;
if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p)
&& LABEL_NAME (JUMP_LABEL (p))
&& ((loop_insn_first_p (JUMP_LABEL (p), v->insn)
&& loop_insn_first_p (loop_start, JUMP_LABEL (p)))
|| (loop_insn_first_p (last_giv_use, JUMP_LABEL (p))
&& loop_insn_first_p (JUMP_LABEL (p), loop_end))))
{
v->replaceable = 0;
v->not_replaceable = 1;
if (loop_dump_stream)
fprintf (loop_dump_stream,
"Found branch outside giv lifetime.\n");
break;
}
}
}
/* If it is replaceable, then save the final value. */
if (v->replaceable)
v->final_value = final_value;
}
if (loop_dump_stream && v->replaceable)
fprintf (loop_dump_stream, "Insn %d: giv reg %d final_value replaceable\n",
INSN_UID (v->insn), REGNO (v->dest_reg));
}
/* Update the status of whether a giv can derive other givs.
We need to do something special if there is or may be an update to the biv
between the time the giv is defined and the time it is used to derive
another giv.
In addition, a giv that is only conditionally set is not allowed to
derive another giv once a label has been passed.
The cases we look at are when a label or an update to a biv is passed. */
static void
update_giv_derive (p)
rtx p;
{
struct iv_class *bl;
struct induction *biv, *giv;
rtx tem;
int dummy;
/* Search all IV classes, then all bivs, and finally all givs.
There are three cases we are concerned with. First we have the situation
of a giv that is only updated conditionally. In that case, it may not
derive any givs after a label is passed.
The second case is when a biv update occurs, or may occur, after the
definition of a giv. For certain biv updates (see below) that are
known to occur between the giv definition and use, we can adjust the
giv definition. For others, or when the biv update is conditional,
we must prevent the giv from deriving any other givs. There are two
sub-cases within this case.
If this is a label, we are concerned with any biv update that is done
conditionally, since it may be done after the giv is defined followed by
a branch here (actually, we need to pass both a jump and a label, but
this extra tracking doesn't seem worth it).
If this is a jump, we are concerned about any biv update that may be
executed multiple times. We are actually only concerned about
backward jumps, but it is probably not worth performing the test
on the jump again here.
If this is a biv update, we must adjust the giv status to show that a
subsequent biv update was performed. If this adjustment cannot be done,
the giv cannot derive further givs. */
for (bl = loop_iv_list; bl; bl = bl->next)
for (biv = bl->biv; biv; biv = biv->next_iv)
if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
|| biv->insn == p)
{
for (giv = bl->giv; giv; giv = giv->next_iv)
{
/* If cant_derive is already true, there is no point in
checking all of these conditions again. */
if (giv->cant_derive)
continue;
/* If this giv is conditionally set and we have passed a label,
it cannot derive anything. */
if (GET_CODE (p) == CODE_LABEL && ! giv->always_computable)
giv->cant_derive = 1;
/* Skip givs that have mult_val == 0, since
they are really invariants. Also skip those that are
replaceable, since we know their lifetime doesn't contain
any biv update. */
else if (giv->mult_val == const0_rtx || giv->replaceable)
continue;
/* The only way we can allow this giv to derive another
is if this is a biv increment and we can form the product
of biv->add_val and giv->mult_val. In this case, we will
be able to compute a compensation. */
else if (biv->insn == p)
{
tem = 0;
if (biv->mult_val == const1_rtx)
tem = simplify_giv_expr (gen_rtx_MULT (giv->mode,
biv->add_val,
giv->mult_val),
&dummy);
if (tem && giv->derive_adjustment)
tem = simplify_giv_expr (gen_rtx_PLUS (giv->mode, tem,
giv->derive_adjustment),
&dummy);
if (tem)
giv->derive_adjustment = tem;
else
giv->cant_derive = 1;
}
else if ((GET_CODE (p) == CODE_LABEL && ! biv->always_computable)
|| (GET_CODE (p) == JUMP_INSN && biv->maybe_multiple))
giv->cant_derive = 1;
}
}
}
/* Check whether an insn is an increment legitimate for a basic induction var.
X is the source of insn P, or a part of it.
MODE is the mode in which X should be interpreted.
DEST_REG is the putative biv, also the destination of the insn.
We accept patterns of these forms:
REG = REG + INVARIANT (includes REG = REG - CONSTANT)
REG = INVARIANT + REG
If X is suitable, we return 1, set *MULT_VAL to CONST1_RTX,
store the additive term into *INC_VAL, and store the place where
we found the additive term into *LOCATION.
If X is an assignment of an invariant into DEST_REG, we set
*MULT_VAL to CONST0_RTX, and store the invariant into *INC_VAL.
We also want to detect a BIV when it corresponds to a variable
whose mode was promoted via PROMOTED_MODE. In that case, an increment
of the variable may be a PLUS that adds a SUBREG of that variable to
an invariant and then sign- or zero-extends the result of the PLUS
into the variable.
Most GIVs in such cases will be in the promoted mode, since that is the
probably the natural computation mode (and almost certainly the mode
used for addresses) on the machine. So we view the pseudo-reg containing
the variable as the BIV, as if it were simply incremented.
Note that treating the entire pseudo as a BIV will result in making
simple increments to any GIVs based on it. However, if the variable
overflows in its declared mode but not its promoted mode, the result will
be incorrect. This is acceptable if the variable is signed, since
overflows in such cases are undefined, but not if it is unsigned, since
those overflows are defined. So we only check for SIGN_EXTEND and
not ZERO_EXTEND.
If we cannot find a biv, we return 0. */
static int
basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val, location)
register rtx x;
enum machine_mode mode;
rtx p;
rtx dest_reg;
rtx *inc_val;
rtx *mult_val;
rtx **location;
{
register enum rtx_code code;
rtx *argp, arg;
rtx insn, set = 0;
code = GET_CODE (x);
switch (code)
{
case PLUS:
if (rtx_equal_p (XEXP (x, 0), dest_reg)
|| (GET_CODE (XEXP (x, 0)) == SUBREG
&& SUBREG_PROMOTED_VAR_P (XEXP (x, 0))
&& SUBREG_REG (XEXP (x, 0)) == dest_reg))
{
argp = &XEXP (x, 1);
}
else if (rtx_equal_p (XEXP (x, 1), dest_reg)
|| (GET_CODE (XEXP (x, 1)) == SUBREG
&& SUBREG_PROMOTED_VAR_P (XEXP (x, 1))
&& SUBREG_REG (XEXP (x, 1)) == dest_reg))
{
argp = &XEXP (x, 0);
}
else
return 0;
arg = *argp;
if (invariant_p (arg) != 1)
return 0;
*inc_val = convert_modes (GET_MODE (dest_reg), GET_MODE (x), arg, 0);
*mult_val = const1_rtx;
*location = argp;
return 1;
case SUBREG:
/* If this is a SUBREG for a promoted variable, check the inner
value. */
if (SUBREG_PROMOTED_VAR_P (x))
return basic_induction_var (SUBREG_REG (x), GET_MODE (SUBREG_REG (x)),
dest_reg, p, inc_val, mult_val, location);
return 0;
case REG:
/* If this register is assigned in a previous insn, look at its
source, but don't go outside the loop or past a label. */
insn = p;
while (1)
{
do {
insn = PREV_INSN (insn);
} while (insn && GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG);
if (!insn)
break;
set = single_set (insn);
if (set == 0)
break;
if ((SET_DEST (set) == x
|| (GET_CODE (SET_DEST (set)) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (SET_DEST (set)))
<= UNITS_PER_WORD)
&& SUBREG_REG (SET_DEST (set)) == x))
&& basic_induction_var (SET_SRC (set),
(GET_MODE (SET_SRC (set)) == VOIDmode
? GET_MODE (x)
: GET_MODE (SET_SRC (set))),
dest_reg, insn,
inc_val, mult_val, location))
return 1;
}
/* ... fall through ... */
/* Can accept constant setting of biv only when inside inner most loop.
Otherwise, a biv of an inner loop may be incorrectly recognized
as a biv of the outer loop,
causing code to be moved INTO the inner loop. */
case MEM:
if (invariant_p (x) != 1)
return 0;
case CONST_INT:
case SYMBOL_REF:
case CONST:
/* convert_modes aborts if we try to convert to or from CCmode, so just
exclude that case. It is very unlikely that a condition code value
would be a useful iterator anyways. */
if (loops_enclosed == 1
&& GET_MODE_CLASS (mode) != MODE_CC
&& GET_MODE_CLASS (GET_MODE (dest_reg)) != MODE_CC)
{
/* Possible bug here? Perhaps we don't know the mode of X. */
*inc_val = convert_modes (GET_MODE (dest_reg), mode, x, 0);
*mult_val = const0_rtx;
return 1;
}
else
return 0;
case SIGN_EXTEND:
return basic_induction_var (XEXP (x, 0), GET_MODE (XEXP (x, 0)),
dest_reg, p, inc_val, mult_val, location);
case ASHIFTRT:
/* Similar, since this can be a sign extension. */
for (insn = PREV_INSN (p);
(insn && GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG);
insn = PREV_INSN (insn))
;
if (insn)
set = single_set (insn);
if (set && SET_DEST (set) == XEXP (x, 0)
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) >= 0
&& GET_CODE (SET_SRC (set)) == ASHIFT
&& XEXP (x, 1) == XEXP (SET_SRC (set), 1))
return basic_induction_var (XEXP (SET_SRC (set), 0),
GET_MODE (XEXP (x, 0)),
dest_reg, insn, inc_val, mult_val,
location);
return 0;
default:
return 0;
}
}
/* A general induction variable (giv) is any quantity that is a linear
function of a basic induction variable,
i.e. giv = biv * mult_val + add_val.
The coefficients can be any loop invariant quantity.
A giv need not be computed directly from the biv;
it can be computed by way of other givs. */
/* Determine whether X computes a giv.
If it does, return a nonzero value
which is the benefit from eliminating the computation of X;
set *SRC_REG to the register of the biv that it is computed from;
set *ADD_VAL and *MULT_VAL to the coefficients,
such that the value of X is biv * mult + add; */
static int
general_induction_var (x, src_reg, add_val, mult_val, is_addr, pbenefit)
rtx x;
rtx *src_reg;
rtx *add_val;
rtx *mult_val;
int is_addr;
int *pbenefit;
{
rtx orig_x = x;
char *storage;
/* If this is an invariant, forget it, it isn't a giv. */
if (invariant_p (x) == 1)
return 0;
/* See if the expression could be a giv and get its form.
Mark our place on the obstack in case we don't find a giv. */
storage = (char *) oballoc (0);
*pbenefit = 0;
x = simplify_giv_expr (x, pbenefit);
if (x == 0)
{
obfree (storage);
return 0;
}
switch (GET_CODE (x))
{
case USE:
case CONST_INT:
/* Since this is now an invariant and wasn't before, it must be a giv
with MULT_VAL == 0. It doesn't matter which BIV we associate this
with. */
*src_reg = loop_iv_list->biv->dest_reg;
*mult_val = const0_rtx;
*add_val = x;
break;
case REG:
/* This is equivalent to a BIV. */
*src_reg = x;
*mult_val = const1_rtx;
*add_val = const0_rtx;
break;
case PLUS:
/* Either (plus (biv) (invar)) or
(plus (mult (biv) (invar_1)) (invar_2)). */
if (GET_CODE (XEXP (x, 0)) == MULT)
{
*src_reg = XEXP (XEXP (x, 0), 0);
*mult_val = XEXP (XEXP (x, 0), 1);
}
else
{
*src_reg = XEXP (x, 0);
*mult_val = const1_rtx;
}
*add_val = XEXP (x, 1);
break;
case MULT:
/* ADD_VAL is zero. */
*src_reg = XEXP (x, 0);
*mult_val = XEXP (x, 1);
*add_val = const0_rtx;
break;
default:
abort ();
}
/* Remove any enclosing USE from ADD_VAL and MULT_VAL (there will be
unless they are CONST_INT). */
if (GET_CODE (*add_val) == USE)
*add_val = XEXP (*add_val, 0);
if (GET_CODE (*mult_val) == USE)
*mult_val = XEXP (*mult_val, 0);
if (is_addr)
{
#ifdef ADDRESS_COST
*pbenefit += ADDRESS_COST (orig_x) - reg_address_cost;
#else
*pbenefit += rtx_cost (orig_x, MEM) - reg_address_cost;
#endif
}
else
*pbenefit += rtx_cost (orig_x, SET);
/* Always return true if this is a giv so it will be detected as such,
even if the benefit is zero or negative. This allows elimination
of bivs that might otherwise not be eliminated. */
return 1;
}
/* Given an expression, X, try to form it as a linear function of a biv.
We will canonicalize it to be of the form
(plus (mult (BIV) (invar_1))
(invar_2))
with possible degeneracies.
The invariant expressions must each be of a form that can be used as a
machine operand. We surround then with a USE rtx (a hack, but localized
and certainly unambiguous!) if not a CONST_INT for simplicity in this
routine; it is the caller's responsibility to strip them.
If no such canonicalization is possible (i.e., two biv's are used or an
expression that is neither invariant nor a biv or giv), this routine
returns 0.
For a non-zero return, the result will have a code of CONST_INT, USE,
REG (for a BIV), PLUS, or MULT. No other codes will occur.
*BENEFIT will be incremented by the benefit of any sub-giv encountered. */
static rtx sge_plus PROTO ((enum machine_mode, rtx, rtx));
static rtx sge_plus_constant PROTO ((rtx, rtx));
static rtx
simplify_giv_expr (x, benefit)
rtx x;
int *benefit;
{
enum machine_mode mode = GET_MODE (x);
rtx arg0, arg1;
rtx tem;
/* If this is not an integer mode, or if we cannot do arithmetic in this
mode, this can't be a giv. */
if (mode != VOIDmode
&& (GET_MODE_CLASS (mode) != MODE_INT
|| GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT))
return NULL_RTX;
switch (GET_CODE (x))
{
case PLUS:
arg0 = simplify_giv_expr (XEXP (x, 0), benefit);
arg1 = simplify_giv_expr (XEXP (x, 1), benefit);
if (arg0 == 0 || arg1 == 0)
return NULL_RTX;
/* Put constant last, CONST_INT last if both constant. */
if ((GET_CODE (arg0) == USE
|| GET_CODE (arg0) == CONST_INT)
&& ! ((GET_CODE (arg0) == USE
&& GET_CODE (arg1) == USE)
|| GET_CODE (arg1) == CONST_INT))
tem = arg0, arg0 = arg1, arg1 = tem;
/* Handle addition of zero, then addition of an invariant. */
if (arg1 == const0_rtx)
return arg0;
else if (GET_CODE (arg1) == CONST_INT || GET_CODE (arg1) == USE)
switch (GET_CODE (arg0))
{
case CONST_INT:
case USE:
/* Adding two invariants must result in an invariant, so enclose
addition operation inside a USE and return it. */
if (GET_CODE (arg0) == USE)
arg0 = XEXP (arg0, 0);
if (GET_CODE (arg1) == USE)
arg1 = XEXP (arg1, 0);
if (GET_CODE (arg0) == CONST_INT)
tem = arg0, arg0 = arg1, arg1 = tem;
if (GET_CODE (arg1) == CONST_INT)
tem = sge_plus_constant (arg0, arg1);
else
tem = sge_plus (mode, arg0, arg1);
if (GET_CODE (tem) != CONST_INT)
tem = gen_rtx_USE (mode, tem);
return tem;
case REG:
case MULT:
/* biv + invar or mult + invar. Return sum. */
return gen_rtx_PLUS (mode, arg0, arg1);
case PLUS:
/* (a + invar_1) + invar_2. Associate. */
return simplify_giv_expr (
gen_rtx_PLUS (mode, XEXP (arg0, 0),
gen_rtx_PLUS (mode, XEXP (arg0, 1), arg1)),
benefit);
default:
abort ();
}
/* Each argument must be either REG, PLUS, or MULT. Convert REG to
MULT to reduce cases. */
if (GET_CODE (arg0) == REG)
arg0 = gen_rtx_MULT (mode, arg0, const1_rtx);
if (GET_CODE (arg1) == REG)
arg1 = gen_rtx_MULT (mode, arg1, const1_rtx);
/* Now have PLUS + PLUS, PLUS + MULT, MULT + PLUS, or MULT + MULT.
Put a MULT first, leaving PLUS + PLUS, MULT + PLUS, or MULT + MULT.
Recurse to associate the second PLUS. */
if (GET_CODE (arg1) == MULT)
tem = arg0, arg0 = arg1, arg1 = tem;
if (GET_CODE (arg1) == PLUS)
return simplify_giv_expr (gen_rtx_PLUS (mode,
gen_rtx_PLUS (mode, arg0,
XEXP (arg1, 0)),
XEXP (arg1, 1)),
benefit);
/* Now must have MULT + MULT. Distribute if same biv, else not giv. */
if (GET_CODE (arg0) != MULT || GET_CODE (arg1) != MULT)
return NULL_RTX;
if (!rtx_equal_p (arg0, arg1))
return NULL_RTX;
return simplify_giv_expr (gen_rtx_MULT (mode,
XEXP (arg0, 0),
gen_rtx_PLUS (mode,
XEXP (arg0, 1),
XEXP (arg1, 1))),
benefit);
case MINUS:
/* Handle "a - b" as "a + b * (-1)". */
return simplify_giv_expr (gen_rtx_PLUS (mode,
XEXP (x, 0),
gen_rtx_MULT (mode, XEXP (x, 1),
constm1_rtx)),
benefit);
case MULT:
arg0 = simplify_giv_expr (XEXP (x, 0), benefit);
arg1 = simplify_giv_expr (XEXP (x, 1), benefit);
if (arg0 == 0 || arg1 == 0)
return NULL_RTX;
/* Put constant last, CONST_INT last if both constant. */
if ((GET_CODE (arg0) == USE || GET_CODE (arg0) == CONST_INT)
&& GET_CODE (arg1) != CONST_INT)
tem = arg0, arg0 = arg1, arg1 = tem;
/* If second argument is not now constant, not giv. */
if (GET_CODE (arg1) != USE && GET_CODE (arg1) != CONST_INT)
return NULL_RTX;
/* Handle multiply by 0 or 1. */
if (arg1 == const0_rtx)
return const0_rtx;
else if (arg1 == const1_rtx)
return arg0;
switch (GET_CODE (arg0))
{
case REG:
/* biv * invar. Done. */
return gen_rtx_MULT (mode, arg0, arg1);
case CONST_INT:
/* Product of two constants. */
return GEN_INT (INTVAL (arg0) * INTVAL (arg1));
case USE:
/* invar * invar. It is a giv, but very few of these will
actually pay off, so limit to simple registers. */
if (GET_CODE (arg1) != CONST_INT)
return NULL_RTX;
arg0 = XEXP (arg0, 0);
if (GET_CODE (arg0) == REG)
tem = gen_rtx_MULT (mode, arg0, arg1);
else if (GET_CODE (arg0) == MULT
&& GET_CODE (XEXP (arg0, 0)) == REG
&& GET_CODE (XEXP (arg0, 1)) == CONST_INT)
{
tem = gen_rtx_MULT (mode, XEXP (arg0, 0),
GEN_INT (INTVAL (XEXP (arg0, 1))
* INTVAL (arg1)));
}
else
return NULL_RTX;
return gen_rtx_USE (mode, tem);
case MULT:
/* (a * invar_1) * invar_2. Associate. */
return simplify_giv_expr (gen_rtx_MULT (mode, XEXP (arg0, 0),
gen_rtx_MULT (mode,
XEXP (arg0, 1),
arg1)),
benefit);
case PLUS:
/* (a + invar_1) * invar_2. Distribute. */
return simplify_giv_expr (gen_rtx_PLUS (mode,
gen_rtx_MULT (mode,
XEXP (arg0, 0),
arg1),
gen_rtx_MULT (mode,
XEXP (arg0, 1),
arg1)),
benefit);
default:
abort ();
}
case ASHIFT:
/* Shift by constant is multiply by power of two. */
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
return 0;
return simplify_giv_expr (gen_rtx_MULT (mode,
XEXP (x, 0),
GEN_INT ((HOST_WIDE_INT) 1
<< INTVAL (XEXP (x, 1)))),
benefit);
case NEG:
/* "-a" is "a * (-1)" */
return simplify_giv_expr (gen_rtx_MULT (mode, XEXP (x, 0), constm1_rtx),
benefit);
case NOT:
/* "~a" is "-a - 1". Silly, but easy. */
return simplify_giv_expr (gen_rtx_MINUS (mode,
gen_rtx_NEG (mode, XEXP (x, 0)),
const1_rtx),
benefit);
case USE:
/* Already in proper form for invariant. */
return x;
case REG:
/* If this is a new register, we can't deal with it. */
if (REGNO (x) >= max_reg_before_loop)
return 0;
/* Check for biv or giv. */
switch (REG_IV_TYPE (REGNO (x)))
{
case BASIC_INDUCT:
return x;
case GENERAL_INDUCT:
{
struct induction *v = REG_IV_INFO (REGNO (x));
/* Form expression from giv and add benefit. Ensure this giv
can derive another and subtract any needed adjustment if so. */
*benefit += v->benefit;
if (v->cant_derive)
return 0;
tem = gen_rtx_PLUS (mode, gen_rtx_MULT (mode, v->src_reg,
v->mult_val),
v->add_val);
if (v->derive_adjustment)
tem = gen_rtx_MINUS (mode, tem, v->derive_adjustment);
return simplify_giv_expr (tem, benefit);
}
default:
/* If it isn't an induction variable, and it is invariant, we
may be able to simplify things further by looking through
the bits we just moved outside the loop. */
if (invariant_p (x) == 1)
{
struct movable *m;
for (m = the_movables; m ; m = m->next)
if (rtx_equal_p (x, m->set_dest))
{
/* Ok, we found a match. Substitute and simplify. */
/* If we match another movable, we must use that, as
this one is going away. */
if (m->match)
return simplify_giv_expr (m->match->set_dest, benefit);
/* If consec is non-zero, this is a member of a group of
instructions that were moved together. We handle this
case only to the point of seeking to the last insn and
looking for a REG_EQUAL. Fail if we don't find one. */
if (m->consec != 0)
{
int i = m->consec;
tem = m->insn;
do { tem = NEXT_INSN (tem); } while (--i > 0);
tem = find_reg_note (tem, REG_EQUAL, NULL_RTX);
if (tem)
tem = XEXP (tem, 0);
}
else
{
tem = single_set (m->insn);
if (tem)
tem = SET_SRC (tem);
}
if (tem)
{
/* What we are most interested in is pointer
arithmetic on invariants -- only take
patterns we may be able to do something with. */
if (GET_CODE (tem) == PLUS
|| GET_CODE (tem) == MULT
|| GET_CODE (tem) == ASHIFT
|| GET_CODE (tem) == CONST_INT
|| GET_CODE (tem) == SYMBOL_REF)
{
tem = simplify_giv_expr (tem, benefit);
if (tem)
return tem;
}
else if (GET_CODE (tem) == CONST
&& GET_CODE (XEXP (tem, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (tem, 0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (tem, 0), 1)) == CONST_INT)
{
tem = simplify_giv_expr (XEXP (tem, 0), benefit);
if (tem)
return tem;
}
}
break;
}
}
break;
}
/* Fall through to general case. */
default:
/* If invariant, return as USE (unless CONST_INT).
Otherwise, not giv. */
if (GET_CODE (x) == USE)
x = XEXP (x, 0);
if (invariant_p (x) == 1)
{
if (GET_CODE (x) == CONST_INT)
return x;
if (GET_CODE (x) == CONST
&& GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
x = XEXP (x, 0);
return gen_rtx_USE (mode, x);
}
else
return 0;
}
}
/* This routine folds invariants such that there is only ever one
CONST_INT in the summation. It is only used by simplify_giv_expr. */
static rtx
sge_plus_constant (x, c)
rtx x, c;
{
if (GET_CODE (x) == CONST_INT)
return GEN_INT (INTVAL (x) + INTVAL (c));
else if (GET_CODE (x) != PLUS)
return gen_rtx_PLUS (GET_MODE (x), x, c);
else if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{
return gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0),
GEN_INT (INTVAL (XEXP (x, 1)) + INTVAL (c)));
}
else if (GET_CODE (XEXP (x, 0)) == PLUS
|| GET_CODE (XEXP (x, 1)) != PLUS)
{
return gen_rtx_PLUS (GET_MODE (x),
sge_plus_constant (XEXP (x, 0), c), XEXP (x, 1));
}
else
{
return gen_rtx_PLUS (GET_MODE (x),
sge_plus_constant (XEXP (x, 1), c), XEXP (x, 0));
}
}
static rtx
sge_plus (mode, x, y)
enum machine_mode mode;
rtx x, y;
{
while (GET_CODE (y) == PLUS)
{
rtx a = XEXP (y, 0);
if (GET_CODE (a) == CONST_INT)
x = sge_plus_constant (x, a);
else
x = gen_rtx_PLUS (mode, x, a);
y = XEXP (y, 1);
}
if (GET_CODE (y) == CONST_INT)
x = sge_plus_constant (x, y);
else
x = gen_rtx_PLUS (mode, x, y);
return x;
}
/* Help detect a giv that is calculated by several consecutive insns;
for example,
giv = biv * M
giv = giv + A
The caller has already identified the first insn P as having a giv as dest;
we check that all other insns that set the same register follow
immediately after P, that they alter nothing else,
and that the result of the last is still a giv.
The value is 0 if the reg set in P is not really a giv.
Otherwise, the value is the amount gained by eliminating
all the consecutive insns that compute the value.
FIRST_BENEFIT is the amount gained by eliminating the first insn, P.
SRC_REG is the reg of the biv; DEST_REG is the reg of the giv.
The coefficients of the ultimate giv value are stored in
*MULT_VAL and *ADD_VAL. */
static int
consec_sets_giv (first_benefit, p, src_reg, dest_reg,
add_val, mult_val, last_consec_insn)
int first_benefit;
rtx p;
rtx src_reg;
rtx dest_reg;
rtx *add_val;
rtx *mult_val;
rtx *last_consec_insn;
{
int count;
enum rtx_code code;
int benefit;
rtx temp;
rtx set;
/* Indicate that this is a giv so that we can update the value produced in
each insn of the multi-insn sequence.
This induction structure will be used only by the call to
general_induction_var below, so we can allocate it on our stack.
If this is a giv, our caller will replace the induct var entry with
a new induction structure. */
struct induction *v
= (struct induction *) alloca (sizeof (struct induction));
v->src_reg = src_reg;
v->mult_val = *mult_val;
v->add_val = *add_val;
v->benefit = first_benefit;
v->cant_derive = 0;
v->derive_adjustment = 0;
REG_IV_TYPE (REGNO (dest_reg)) = GENERAL_INDUCT;
REG_IV_INFO (REGNO (dest_reg)) = v;
count = VARRAY_INT (n_times_set, REGNO (dest_reg)) - 1;
while (count > 0)
{
p = NEXT_INSN (p);
code = GET_CODE (p);
/* If libcall, skip to end of call sequence. */
if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
p = XEXP (temp, 0);
if (code == INSN
&& (set = single_set (p))
&& GET_CODE (SET_DEST (set)) == REG
&& SET_DEST (set) == dest_reg
&& (general_induction_var (SET_SRC (set), &src_reg,
add_val, mult_val, 0, &benefit)
/* Giv created by equivalent expression. */
|| ((temp = find_reg_note (p, REG_EQUAL, NULL_RTX))
&& general_induction_var (XEXP (temp, 0), &src_reg,
add_val, mult_val, 0, &benefit)))
&& src_reg == v->src_reg)
{
if (find_reg_note (p, REG_RETVAL, NULL_RTX))
benefit += libcall_benefit (p);
count--;
v->mult_val = *mult_val;
v->add_val = *add_val;
v->benefit = benefit;
}
else if (code != NOTE)
{
/* Allow insns that set something other than this giv to a
constant. Such insns are needed on machines which cannot
include long constants and should not disqualify a giv. */
if (code == INSN
&& (set = single_set (p))
&& SET_DEST (set) != dest_reg
&& CONSTANT_P (SET_SRC (set)))
continue;
REG_IV_TYPE (REGNO (dest_reg)) = UNKNOWN_INDUCT;
return 0;
}
}
*last_consec_insn = p;
return v->benefit;
}
/* Return an rtx, if any, that expresses giv G2 as a function of the register
represented by G1. If no such expression can be found, or it is clear that
it cannot possibly be a valid address, 0 is returned.
To perform the computation, we note that
G1 = x * v + a and
G2 = y * v + b
where `v' is the biv.
So G2 = (y/b) * G1 + (b - a*y/x).
Note that MULT = y/x.
Update: A and B are now allowed to be additive expressions such that
B contains all variables in A. That is, computing B-A will not require
subtracting variables. */
static rtx
express_from_1 (a, b, mult)
rtx a, b, mult;
{
/* If MULT is zero, then A*MULT is zero, and our expression is B. */
if (mult == const0_rtx)
return b;
/* If MULT is not 1, we cannot handle A with non-constants, since we
would then be required to subtract multiples of the registers in A.
This is theoretically possible, and may even apply to some Fortran
constructs, but it is a lot of work and we do not attempt it here. */
if (mult != const1_rtx && GET_CODE (a) != CONST_INT)
return NULL_RTX;
/* In general these structures are sorted top to bottom (down the PLUS
chain), but not left to right across the PLUS. If B is a higher
order giv than A, we can strip one level and recurse. If A is higher
order, we'll eventually bail out, but won't know that until the end.
If they are the same, we'll strip one level around this loop. */
while (GET_CODE (a) == PLUS && GET_CODE (b) == PLUS)
{
rtx ra, rb, oa, ob, tmp;
ra = XEXP (a, 0), oa = XEXP (a, 1);
if (GET_CODE (ra) == PLUS)
tmp = ra, ra = oa, oa = tmp;
rb = XEXP (b, 0), ob = XEXP (b, 1);
if (GET_CODE (rb) == PLUS)
tmp = rb, rb = ob, ob = tmp;
if (rtx_equal_p (ra, rb))
/* We matched: remove one reg completely. */
a = oa, b = ob;
else if (GET_CODE (ob) != PLUS && rtx_equal_p (ra, ob))
/* An alternate match. */
a = oa, b = rb;
else if (GET_CODE (oa) != PLUS && rtx_equal_p (oa, rb))
/* An alternate match. */
a = ra, b = ob;
else
{
/* Indicates an extra register in B. Strip one level from B and
recurse, hoping B was the higher order expression. */
ob = express_from_1 (a, ob, mult);
if (ob == NULL_RTX)
return NULL_RTX;
return gen_rtx_PLUS (GET_MODE (b), rb, ob);
}
}
/* Here we are at the last level of A, go through the cases hoping to
get rid of everything but a constant. */
if (GET_CODE (a) == PLUS)
{
rtx ra, oa;
ra = XEXP (a, 0), oa = XEXP (a, 1);
if (rtx_equal_p (oa, b))
oa = ra;
else if (!rtx_equal_p (ra, b))
return NULL_RTX;
if (GET_CODE (oa) != CONST_INT)
return NULL_RTX;
return GEN_INT (-INTVAL (oa) * INTVAL (mult));
}
else if (GET_CODE (a) == CONST_INT)
{
return plus_constant (b, -INTVAL (a) * INTVAL (mult));
}
else if (GET_CODE (b) == PLUS)
{
if (rtx_equal_p (a, XEXP (b, 0)))
return XEXP (b, 1);
else if (rtx_equal_p (a, XEXP (b, 1)))
return XEXP (b, 0);
else
return NULL_RTX;
}
else if (rtx_equal_p (a, b))
return const0_rtx;
return NULL_RTX;
}
rtx
express_from (g1, g2)
struct induction *g1, *g2;
{
rtx mult, add;
/* The value that G1 will be multiplied by must be a constant integer. Also,
the only chance we have of getting a valid address is if b*c/a (see above
for notation) is also an integer. */
if (GET_CODE (g1->mult_val) == CONST_INT
&& GET_CODE (g2->mult_val) == CONST_INT)
{
if (g1->mult_val == const0_rtx
|| INTVAL (g2->mult_val) % INTVAL (g1->mult_val) != 0)
return NULL_RTX;
mult = GEN_INT (INTVAL (g2->mult_val) / INTVAL (g1->mult_val));
}
else if (rtx_equal_p (g1->mult_val, g2->mult_val))
mult = const1_rtx;
else
{
/* ??? Find out if the one is a multiple of the other? */
return NULL_RTX;
}
add = express_from_1 (g1->add_val, g2->add_val, mult);
if (add == NULL_RTX)
return NULL_RTX;
/* Form simplified final result. */
if (mult == const0_rtx)
return add;
else if (mult == const1_rtx)
mult = g1->dest_reg;
else
mult = gen_rtx_MULT (g2->mode, g1->dest_reg, mult);
if (add == const0_rtx)
return mult;
else
{
if (GET_CODE (add) == PLUS
&& CONSTANT_P (XEXP (add, 1)))
{
rtx tem = XEXP (add, 1);
mult = gen_rtx_PLUS (g2->mode, mult, XEXP (add, 0));
add = tem;
}
return gen_rtx_PLUS (g2->mode, mult, add);
}
}
/* Return an rtx, if any, that expresses giv G2 as a function of the register
represented by G1. This indicates that G2 should be combined with G1 and
that G2 can use (either directly or via an address expression) a register
used to represent G1. */
static rtx
combine_givs_p (g1, g2)
struct induction *g1, *g2;
{
rtx tem = express_from (g1, g2);
/* If these givs are identical, they can be combined. We use the results
of express_from because the addends are not in a canonical form, so
rtx_equal_p is a weaker test. */
/* But don't combine a DEST_REG giv with a DEST_ADDR giv; we want the
combination to be the other way round. */
if (tem == g1->dest_reg
&& (g1->giv_type == DEST_REG || g2->giv_type == DEST_ADDR))
{
return g1->dest_reg;
}
/* If G2 can be expressed as a function of G1 and that function is valid
as an address and no more expensive than using a register for G2,
the expression of G2 in terms of G1 can be used. */
if (tem != NULL_RTX
&& g2->giv_type == DEST_ADDR
&& memory_address_p (g2->mem_mode, tem)
/* ??? Looses, especially with -fforce-addr, where *g2->location
will always be a register, and so anything more complicated
gets discarded. */
#if 0
#ifdef ADDRESS_COST
&& ADDRESS_COST (tem) <= ADDRESS_COST (*g2->location)
#else
&& rtx_cost (tem, MEM) <= rtx_cost (*g2->location, MEM)
#endif
#endif
)
{
return tem;
}
return NULL_RTX;
}
struct combine_givs_stats
{
int giv_number;
int total_benefit;
};
static int
cmp_combine_givs_stats (x, y)
struct combine_givs_stats *x, *y;
{
int d;
d = y->total_benefit - x->total_benefit;
/* Stabilize the sort. */
if (!d)
d = x->giv_number - y->giv_number;
return d;
}
/* Check all pairs of givs for iv_class BL and see if any can be combined with
any other. If so, point SAME to the giv combined with and set NEW_REG to
be an expression (in terms of the other giv's DEST_REG) equivalent to the
giv. Also, update BENEFIT and related fields for cost/benefit analysis. */
static void
combine_givs (bl)
struct iv_class *bl;
{
/* Additional benefit to add for being combined multiple times. */
const int extra_benefit = 3;
struct induction *g1, *g2, **giv_array;
int i, j, k, giv_count;
struct combine_givs_stats *stats;
rtx *can_combine;
/* Count givs, because bl->giv_count is incorrect here. */
giv_count = 0;
for (g1 = bl->giv; g1; g1 = g1->next_iv)
if (!g1->ignore)
giv_count++;
giv_array
= (struct induction **) alloca (giv_count * sizeof (struct induction *));
i = 0;
for (g1 = bl->giv; g1; g1 = g1->next_iv)
if (!g1->ignore)
giv_array[i++] = g1;
stats = (struct combine_givs_stats *) alloca (giv_count * sizeof (*stats));
bzero ((char *) stats, giv_count * sizeof (*stats));
can_combine = (rtx *) alloca (giv_count * giv_count * sizeof(rtx));
bzero ((char *) can_combine, giv_count * giv_count * sizeof(rtx));
for (i = 0; i < giv_count; i++)
{
int this_benefit;
rtx single_use;
g1 = giv_array[i];
stats[i].giv_number = i;
/* If a DEST_REG GIV is used only once, do not allow it to combine
with anything, for in doing so we will gain nothing that cannot
be had by simply letting the GIV with which we would have combined
to be reduced on its own. The losage shows up in particular with
DEST_ADDR targets on hosts with reg+reg addressing, though it can
be seen elsewhere as well. */
if (g1->giv_type == DEST_REG
&& (single_use = VARRAY_RTX (reg_single_usage, REGNO (g1->dest_reg)))
&& single_use != const0_rtx)
continue;
this_benefit = g1->benefit;
/* Add an additional weight for zero addends. */
if (g1->no_const_addval)
this_benefit += 1;
for (j = 0; j < giv_count; j++)
{
rtx this_combine;
g2 = giv_array[j];
if (g1 != g2
&& (this_combine = combine_givs_p (g1, g2)) != NULL_RTX)
{
can_combine[i*giv_count + j] = this_combine;
this_benefit += g2->benefit + extra_benefit;
}
}
stats[i].total_benefit = this_benefit;
}
/* Iterate, combining until we can't. */
restart:
qsort (stats, giv_count, sizeof(*stats), cmp_combine_givs_stats);
if (loop_dump_stream)
{
fprintf (loop_dump_stream, "Sorted combine statistics:\n");
for (k = 0; k < giv_count; k++)
{
g1 = giv_array[stats[k].giv_number];
if (!g1->combined_with && !g1->same)
fprintf (loop_dump_stream, " {%d, %d}",
INSN_UID (giv_array[stats[k].giv_number]->insn),
stats[k].total_benefit);
}
putc ('\n', loop_dump_stream);
}
for (k = 0; k < giv_count; k++)
{
int g1_add_benefit = 0;
i = stats[k].giv_number;
g1 = giv_array[i];
/* If it has already been combined, skip. */
if (g1->combined_with || g1->same)
continue;
for (j = 0; j < giv_count; j++)
{
g2 = giv_array[j];
if (g1 != g2 && can_combine[i*giv_count + j]
/* If it has already been combined, skip. */
&& ! g2->same && ! g2->combined_with)
{
int l;
g2->new_reg = can_combine[i*giv_count + j];
g2->same = g1;
g1->combined_with++;
g1->lifetime += g2->lifetime;
g1_add_benefit += g2->benefit;
/* ??? The new final_[bg]iv_value code does a much better job
of finding replaceable giv's, and hence this code may no
longer be necessary. */
if (! g2->replaceable && REG_USERVAR_P (g2->dest_reg))
g1_add_benefit -= copy_cost;
/* To help optimize the next set of combinations, remove
this giv from the benefits of other potential mates. */
for (l = 0; l < giv_count; ++l)
{
int m = stats[l].giv_number;
if (can_combine[m*giv_count + j])
stats[l].total_benefit -= g2->benefit + extra_benefit;
}
if (loop_dump_stream)
fprintf (loop_dump_stream,
"giv at %d combined with giv at %d\n",
INSN_UID (g2->insn), INSN_UID (g1->insn));
}
}
/* To help optimize the next set of combinations, remove
this giv from the benefits of other potential mates. */
if (g1->combined_with)
{
for (j = 0; j < giv_count; ++j)
{
int m = stats[j].giv_number;
if (can_combine[m*giv_count + i])
stats[j].total_benefit -= g1->benefit + extra_benefit;
}
g1->benefit += g1_add_benefit;
/* We've finished with this giv, and everything it touched.
Restart the combination so that proper weights for the
rest of the givs are properly taken into account. */
/* ??? Ideally we would compact the arrays at this point, so
as to not cover old ground. But sanely compacting
can_combine is tricky. */
goto restart;
}
}
}
struct recombine_givs_stats
{
int giv_number;
int start_luid, end_luid;
};
/* Used below as comparison function for qsort. We want a ascending luid
when scanning the array starting at the end, thus the arguments are
used in reverse. */
static int
cmp_recombine_givs_stats (x, y)
struct recombine_givs_stats *x, *y;
{
int d;
d = y->start_luid - x->start_luid;
/* Stabilize the sort. */
if (!d)
d = y->giv_number - x->giv_number;
return d;
}
/* Scan X, which is a part of INSN, for the end of life of a giv. Also
look for the start of life of a giv where the start has not been seen
yet to unlock the search for the end of its life.
Only consider givs that belong to BIV.
Return the total number of lifetime ends that have been found. */
static int
find_life_end (x, stats, insn, biv)
rtx x, insn, biv;
struct recombine_givs_stats *stats;
{
enum rtx_code code;
char *fmt;
int i, j;
int retval;
code = GET_CODE (x);
switch (code)
{
case SET:
{
rtx reg = SET_DEST (x);
if (GET_CODE (reg) == REG)
{
int regno = REGNO (reg);
struct induction *v = REG_IV_INFO (regno);
if (REG_IV_TYPE (regno) == GENERAL_INDUCT
&& ! v->ignore
&& v->src_reg == biv
&& stats[v->ix].end_luid <= 0)
{
/* If we see a 0 here for end_luid, it means that we have
scanned the entire loop without finding any use at all.
We must not predicate this code on a start_luid match
since that would make the test fail for givs that have
been hoisted out of inner loops. */
if (stats[v->ix].end_luid == 0)
{
stats[v->ix].end_luid = stats[v->ix].start_luid;
return 1 + find_life_end (SET_SRC (x), stats, insn, biv);
}
else if (stats[v->ix].start_luid == INSN_LUID (insn))
stats[v->ix].end_luid = 0;
}
return find_life_end (SET_SRC (x), stats, insn, biv);
}
break;
}
case REG:
{
int regno = REGNO (x);
struct induction *v = REG_IV_INFO (regno);
if (REG_IV_TYPE (regno) == GENERAL_INDUCT
&& ! v->ignore
&& v->src_reg == biv
&& stats[v->ix].end_luid == 0)
{
while (INSN_UID (insn) >= max_uid_for_loop)
insn = NEXT_INSN (insn);
stats[v->ix].end_luid = INSN_LUID (insn);
return 1;
}
return 0;
}
case LABEL_REF:
case CONST_DOUBLE:
case CONST_INT:
case CONST:
return 0;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
retval = 0;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
retval += find_life_end (XEXP (x, i), stats, insn, biv);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
retval += find_life_end (XVECEXP (x, i, j), stats, insn, biv);
}
return retval;
}
/* For each giv that has been combined with another, look if
we can combine it with the most recently used one instead.
This tends to shorten giv lifetimes, and helps the next step:
try to derive givs from other givs. */
static void
recombine_givs (bl, loop_start, loop_end, unroll_p)
struct iv_class *bl;
rtx loop_start, loop_end;
int unroll_p;
{
struct induction *v, **giv_array, *last_giv;
struct recombine_givs_stats *stats;
int giv_count;
int i, rescan;
int ends_need_computing;
for (giv_count = 0, v = bl->giv; v; v = v->next_iv)
{
if (! v->ignore)
giv_count++;
}
giv_array
= (struct induction **) alloca (giv_count * sizeof (struct induction *));
stats = (struct recombine_givs_stats *) alloca (giv_count * sizeof *stats);
/* Initialize stats and set up the ix field for each giv in stats to name
the corresponding index into stats. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
rtx p;
if (v->ignore)
continue;
giv_array[i] = v;
stats[i].giv_number = i;
/* If this giv has been hoisted out of an inner loop, use the luid of
the previous insn. */
for (p = v->insn; INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
stats[i].start_luid = INSN_LUID (p);
v->ix = i;
i++;
}
qsort (stats, giv_count, sizeof(*stats), cmp_recombine_givs_stats);
/* Do the actual most-recently-used recombination. */
for (last_giv = 0, i = giv_count - 1; i >= 0; i--)
{
v = giv_array[stats[i].giv_number];
if (v->same)
{
struct induction *old_same = v->same;
rtx new_combine;
/* combine_givs_p actually says if we can make this transformation.
The other tests are here only to avoid keeping a giv alive
that could otherwise be eliminated. */
if (last_giv
&& ((old_same->maybe_dead && ! old_same->combined_with)
|| ! last_giv->maybe_dead
|| last_giv->combined_with)
&& (new_combine = combine_givs_p (last_giv, v)))
{
old_same->combined_with--;
v->new_reg = new_combine;
v->same = last_giv;
last_giv->combined_with++;
/* No need to update lifetimes / benefits here since we have
already decided what to reduce. */
if (loop_dump_stream)
{
fprintf (loop_dump_stream,
"giv at %d recombined with giv at %d as ",
INSN_UID (v->insn), INSN_UID (last_giv->insn));
print_rtl (loop_dump_stream, v->new_reg);
putc ('\n', loop_dump_stream);
}
continue;
}
v = v->same;
}
else if (v->giv_type != DEST_REG)
continue;
if (! last_giv
|| (last_giv->maybe_dead && ! last_giv->combined_with)
|| ! v->maybe_dead
|| v->combined_with)
last_giv = v;
}
ends_need_computing = 0;
/* For each DEST_REG giv, compute lifetime starts, and try to compute
lifetime ends from regscan info. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
if (v->ignore)
continue;
if (v->giv_type == DEST_ADDR)
{
/* Loop unrolling of an inner loop can even create new DEST_REG
givs. */
rtx p;
for (p = v->insn; INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
stats[i].start_luid = stats[i].end_luid = INSN_LUID (p);
if (p != v->insn)
stats[i].end_luid++;
}
else /* v->giv_type == DEST_REG */
{
if (v->last_use)
{
stats[i].start_luid = INSN_LUID (v->insn);
stats[i].end_luid = INSN_LUID (v->last_use);
}
else if (INSN_UID (v->insn) >= max_uid_for_loop)
{
rtx p;
/* This insn has been created by loop optimization on an inner
loop. We don't have a proper start_luid that will match
when we see the first set. But we do know that there will
be no use before the set, so we can set end_luid to 0 so that
we'll start looking for the last use right away. */
for (p = PREV_INSN (v->insn); INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
stats[i].start_luid = INSN_LUID (p);
stats[i].end_luid = 0;
ends_need_computing++;
}
else
{
int regno = REGNO (v->dest_reg);
int count = VARRAY_INT (n_times_set, regno) - 1;
rtx p = v->insn;
/* Find the first insn that sets the giv, so that we can verify
if this giv's lifetime wraps around the loop. We also need
the luid of the first setting insn in order to detect the
last use properly. */
while (count)
{
p = prev_nonnote_insn (p);
if (reg_set_p (v->dest_reg, p))
count--;
}
stats[i].start_luid = INSN_LUID (p);
if (stats[i].start_luid > uid_luid[REGNO_FIRST_UID (regno)])
{
stats[i].end_luid = -1;
ends_need_computing++;
}
else
{
stats[i].end_luid = uid_luid[REGNO_LAST_UID (regno)];
if (stats[i].end_luid > INSN_LUID (loop_end))
{
stats[i].end_luid = -1;
ends_need_computing++;
}
}
}
}
i++;
}
/* If the regscan information was unconclusive for one or more DEST_REG
givs, scan the all insn in the loop to find out lifetime ends. */
if (ends_need_computing)
{
rtx biv = bl->biv->src_reg;
rtx p = loop_end;
do
{
if (p == loop_start)
p = loop_end;
p = PREV_INSN (p);
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
ends_need_computing -= find_life_end (PATTERN (p), stats, p, biv);
}
while (ends_need_computing);
}
/* Set start_luid back to the last insn that sets the giv. This allows
more combinations. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
if (v->ignore)
continue;
if (INSN_UID (v->insn) < max_uid_for_loop)
stats[i].start_luid = INSN_LUID (v->insn);
i++;
}
/* Now adjust lifetime ends by taking combined givs into account. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
unsigned luid;
int j;
if (v->ignore)
continue;
if (v->same && ! v->same->ignore)
{
j = v->same->ix;
luid = stats[i].start_luid;
/* Use unsigned arithmetic to model loop wrap-around. */
if (luid - stats[j].start_luid
> (unsigned) stats[j].end_luid - stats[j].start_luid)
stats[j].end_luid = luid;
}
i++;
}
qsort (stats, giv_count, sizeof(*stats), cmp_recombine_givs_stats);
/* Try to derive DEST_REG givs from previous DEST_REG givs with the
same mult_val and non-overlapping lifetime. This reduces register
pressure.
Once we find a DEST_REG giv that is suitable to derive others from,
we set last_giv to this giv, and try to derive as many other DEST_REG
givs from it without joining overlapping lifetimes. If we then
encounter a DEST_REG giv that we can't derive, we set rescan to the
index for this giv (unless rescan is already set).
When we are finished with the current LAST_GIV (i.e. the inner loop
terminates), we start again with rescan, which then becomes the new
LAST_GIV. */
for (i = giv_count - 1; i >= 0; i = rescan)
{
int life_start, life_end;
for (last_giv = 0, rescan = -1; i >= 0; i--)
{
rtx sum;
v = giv_array[stats[i].giv_number];
if (v->giv_type != DEST_REG || v->derived_from || v->same)
continue;
if (! last_giv)
{
/* Don't use a giv that's likely to be dead to derive
others - that would be likely to keep that giv alive. */
if (! v->maybe_dead || v->combined_with)
{
last_giv = v;
life_start = stats[i].start_luid;
life_end = stats[i].end_luid;
}
continue;
}
/* Use unsigned arithmetic to model loop wrap around. */
if (((unsigned) stats[i].start_luid - life_start
>= (unsigned) life_end - life_start)
&& ((unsigned) stats[i].end_luid - life_start
> (unsigned) life_end - life_start)
/* Check that the giv insn we're about to use for deriving
precedes all uses of that giv. Note that initializing the
derived giv would defeat the purpose of reducing register
pressure.
??? We could arrange to move the insn. */
&& ((unsigned) stats[i].end_luid - INSN_LUID (loop_start)
> (unsigned) stats[i].start_luid - INSN_LUID (loop_start))
&& rtx_equal_p (last_giv->mult_val, v->mult_val)
/* ??? Could handle libcalls, but would need more logic. */
&& ! find_reg_note (v->insn, REG_RETVAL, NULL_RTX)
/* We would really like to know if for any giv that v
is combined with, v->insn or any intervening biv increment
dominates that combined giv. However, we
don't have this detailed control flow information.
N.B. since last_giv will be reduced, it is valid
anywhere in the loop, so we don't need to check the
validity of last_giv.
We rely here on the fact that v->always_executed implies that
there is no jump to someplace else in the loop before the
giv insn, and hence any insn that is executed before the
giv insn in the loop will have a lower luid. */
&& (v->always_executed || ! v->combined_with)
&& (sum = express_from (last_giv, v))
/* Make sure we don't make the add more expensive. ADD_COST
doesn't take different costs of registers and constants into
account, so compare the cost of the actual SET_SRCs. */
&& (rtx_cost (sum, SET)
<= rtx_cost (SET_SRC (single_set (v->insn)), SET))
/* ??? unroll can't understand anything but reg + const_int
sums. It would be cleaner to fix unroll. */
&& ((GET_CODE (sum) == PLUS
&& GET_CODE (XEXP (sum, 0)) == REG
&& GET_CODE (XEXP (sum, 1)) == CONST_INT)
|| ! unroll_p)
&& validate_change (v->insn, &PATTERN (v->insn),
gen_rtx_SET (VOIDmode, v->dest_reg, sum), 0))
{
v->derived_from = last_giv;
life_end = stats[i].end_luid;
if (loop_dump_stream)
{
fprintf (loop_dump_stream,
"giv at %d derived from %d as ",
INSN_UID (v->insn), INSN_UID (last_giv->insn));
print_rtl (loop_dump_stream, sum);
putc ('\n', loop_dump_stream);
}
}
else if (rescan < 0)
rescan = i;
}
}
}
/* EMIT code before INSERT_BEFORE to set REG = B * M + A. */
void
emit_iv_add_mult (b, m, a, reg, insert_before)
rtx b; /* initial value of basic induction variable */
rtx m; /* multiplicative constant */
rtx a; /* additive constant */
rtx reg; /* destination register */
rtx insert_before;
{
rtx seq;
rtx result;
/* Prevent unexpected sharing of these rtx. */
a = copy_rtx (a);
b = copy_rtx (b);
/* Increase the lifetime of any invariants moved further in code. */
update_reg_last_use (a, insert_before);
update_reg_last_use (b, insert_before);
update_reg_last_use (m, insert_before);
start_sequence ();
result = expand_mult_add (b, reg, m, a, GET_MODE (reg), 0);
if (reg != result)
emit_move_insn (reg, result);
seq = gen_sequence ();
end_sequence ();
emit_insn_before (seq, insert_before);
/* It is entirely possible that the expansion created lots of new
registers. Iterate over the sequence we just created and
record them all. */
if (GET_CODE (seq) == SEQUENCE)
{
int i;
for (i = 0; i < XVECLEN (seq, 0); ++i)
{
rtx set = single_set (XVECEXP (seq, 0, i));
if (set && GET_CODE (SET_DEST (set)) == REG)
record_base_value (REGNO (SET_DEST (set)), SET_SRC (set), 0);
}
}
else if (GET_CODE (seq) == SET
&& GET_CODE (SET_DEST (seq)) == REG)
record_base_value (REGNO (SET_DEST (seq)), SET_SRC (seq), 0);
}
/* Test whether A * B can be computed without
an actual multiply insn. Value is 1 if so. */
static int
product_cheap_p (a, b)
rtx a;
rtx b;
{
int i;
rtx tmp;
struct obstack *old_rtl_obstack = rtl_obstack;
char *storage = (char *) obstack_alloc (&temp_obstack, 0);
int win = 1;
/* If only one is constant, make it B. */
if (GET_CODE (a) == CONST_INT)
tmp = a, a = b, b = tmp;
/* If first constant, both constant, so don't need multiply. */
if (GET_CODE (a) == CONST_INT)
return 1;
/* If second not constant, neither is constant, so would need multiply. */
if (GET_CODE (b) != CONST_INT)
return 0;
/* One operand is constant, so might not need multiply insn. Generate the
code for the multiply and see if a call or multiply, or long sequence
of insns is generated. */
rtl_obstack = &temp_obstack;
start_sequence ();
expand_mult (GET_MODE (a), a, b, NULL_RTX, 0);
tmp = gen_sequence ();
end_sequence ();
if (GET_CODE (tmp) == SEQUENCE)
{
if (XVEC (tmp, 0) == 0)
win = 1;
else if (XVECLEN (tmp, 0) > 3)
win = 0;
else
for (i = 0; i < XVECLEN (tmp, 0); i++)
{
rtx insn = XVECEXP (tmp, 0, i);
if (GET_CODE (insn) != INSN
|| (GET_CODE (PATTERN (insn)) == SET
&& GET_CODE (SET_SRC (PATTERN (insn))) == MULT)
|| (GET_CODE (PATTERN (insn)) == PARALLEL
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == MULT))
{
win = 0;
break;
}
}
}
else if (GET_CODE (tmp) == SET
&& GET_CODE (SET_SRC (tmp)) == MULT)
win = 0;
else if (GET_CODE (tmp) == PARALLEL
&& GET_CODE (XVECEXP (tmp, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (tmp, 0, 0))) == MULT)
win = 0;
/* Free any storage we obtained in generating this multiply and restore rtl
allocation to its normal obstack. */
obstack_free (&temp_obstack, storage);
rtl_obstack = old_rtl_obstack;
return win;
}
/* Check to see if loop can be terminated by a "decrement and branch until
zero" instruction. If so, add a REG_NONNEG note to the branch insn if so.
Also try reversing an increment loop to a decrement loop
to see if the optimization can be performed.
Value is nonzero if optimization was performed. */
/* This is useful even if the architecture doesn't have such an insn,
because it might change a loops which increments from 0 to n to a loop
which decrements from n to 0. A loop that decrements to zero is usually
faster than one that increments from zero. */
/* ??? This could be rewritten to use some of the loop unrolling procedures,
such as approx_final_value, biv_total_increment, loop_iterations, and
final_[bg]iv_value. */
static int
check_dbra_loop (loop_end, insn_count, loop_start, loop_info)
rtx loop_end;
int insn_count;
rtx loop_start;
struct loop_info *loop_info;
{
struct iv_class *bl;
rtx reg;
rtx jump_label;
rtx final_value;
rtx start_value;
rtx new_add_val;
rtx comparison;
rtx before_comparison;
rtx p;
rtx jump;
rtx first_compare;
int compare_and_branch;
/* If last insn is a conditional branch, and the insn before tests a
register value, try to optimize it. Otherwise, we can't do anything. */
jump = PREV_INSN (loop_end);
comparison = get_condition_for_loop (jump);
if (comparison == 0)
return 0;
/* Try to compute whether the compare/branch at the loop end is one or
two instructions. */
get_condition (jump, &first_compare);
if (first_compare == jump)
compare_and_branch = 1;
else if (first_compare == prev_nonnote_insn (jump))
compare_and_branch = 2;
else
return 0;
/* Check all of the bivs to see if the compare uses one of them.
Skip biv's set more than once because we can't guarantee that
it will be zero on the last iteration. Also skip if the biv is
used between its update and the test insn. */
for (bl = loop_iv_list; bl; bl = bl->next)
{
if (bl->biv_count == 1
&& bl->biv->dest_reg == XEXP (comparison, 0)
&& ! reg_used_between_p (regno_reg_rtx[bl->regno], bl->biv->insn,
first_compare))
break;
}
if (! bl)
return 0;
/* Look for the case where the basic induction variable is always
nonnegative, and equals zero on the last iteration.
In this case, add a reg_note REG_NONNEG, which allows the
m68k DBRA instruction to be used. */
if (((GET_CODE (comparison) == GT
&& GET_CODE (XEXP (comparison, 1)) == CONST_INT
&& INTVAL (XEXP (comparison, 1)) == -1)
|| (GET_CODE (comparison) == NE && XEXP (comparison, 1) == const0_rtx))
&& GET_CODE (bl->biv->add_val) == CONST_INT
&& INTVAL (bl->biv->add_val) < 0)
{
/* Initial value must be greater than 0,
init_val % -dec_value == 0 to ensure that it equals zero on
the last iteration */
if (GET_CODE (bl->initial_value) == CONST_INT
&& INTVAL (bl->initial_value) > 0
&& (INTVAL (bl->initial_value)
% (-INTVAL (bl->biv->add_val))) == 0)
{
/* register always nonnegative, add REG_NOTE to branch */
REG_NOTES (PREV_INSN (loop_end))
= gen_rtx_EXPR_LIST (REG_NONNEG, NULL_RTX,
REG_NOTES (PREV_INSN (loop_end)));
bl->nonneg = 1;
return 1;
}
/* If the decrement is 1 and the value was tested as >= 0 before
the loop, then we can safely optimize. */
for (p = loop_start; p; p = PREV_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL)
break;
if (GET_CODE (p) != JUMP_INSN)
continue;
before_comparison = get_condition_for_loop (p);
if (before_comparison
&& XEXP (before_comparison, 0) == bl->biv->dest_reg
&& GET_CODE (before_comparison) == LT
&& XEXP (before_comparison, 1) == const0_rtx
&& ! reg_set_between_p (bl->biv->dest_reg, p, loop_start)
&& INTVAL (bl->biv->add_val) == -1)
{
REG_NOTES (PREV_INSN (loop_end))
= gen_rtx_EXPR_LIST (REG_NONNEG, NULL_RTX,
REG_NOTES (PREV_INSN (loop_end)));
bl->nonneg = 1;
return 1;
}
}
}
else if (INTVAL (bl->biv->add_val) > 0)
{
/* Try to change inc to dec, so can apply above optimization. */
/* Can do this if:
all registers modified are induction variables or invariant,
all memory references have non-overlapping addresses
(obviously true if only one write)
allow 2 insns for the compare/jump at the end of the loop. */
/* Also, we must avoid any instructions which use both the reversed
biv and another biv. Such instructions will fail if the loop is
reversed. We meet this condition by requiring that either
no_use_except_counting is true, or else that there is only
one biv. */
int num_nonfixed_reads = 0;
/* 1 if the iteration var is used only to count iterations. */
int no_use_except_counting = 0;
/* 1 if the loop has no memory store, or it has a single memory store
which is reversible. */
int reversible_mem_store = 1;
if (bl->giv_count == 0
&& ! loop_number_exit_count[uid_loop_num[INSN_UID (loop_start)]])
{
rtx bivreg = regno_reg_rtx[bl->regno];
/* If there are no givs for this biv, and the only exit is the
fall through at the end of the loop, then
see if perhaps there are no uses except to count. */
no_use_except_counting = 1;
for (p = loop_start; p != loop_end; p = NEXT_INSN (p))
if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
{
rtx set = single_set (p);
if (set && GET_CODE (SET_DEST (set)) == REG
&& REGNO (SET_DEST (set)) == bl->regno)
/* An insn that sets the biv is okay. */
;
- else if (p == prev_nonnote_insn (prev_nonnote_insn (loop_end))
- || p == prev_nonnote_insn (loop_end))
- /* Don't bother about the end test. */
- ;
+ else if ((p == prev_nonnote_insn (prev_nonnote_insn (loop_end))
+ || p == prev_nonnote_insn (loop_end))
+ && reg_mentioned_p (bivreg, PATTERN (p)))
+ {
+ /* If either of these insns uses the biv and sets a pseudo
+ that has more than one usage, then the biv has uses
+ other than counting since it's used to derive a value
+ that is used more than one time. */
+ note_set_pseudo_multiple_uses_retval = 0;
+ note_stores (PATTERN (p), note_set_pseudo_multiple_uses);
+ if (note_set_pseudo_multiple_uses_retval)
+ {
+ no_use_except_counting = 0;
+ break;
+ }
+ }
else if (reg_mentioned_p (bivreg, PATTERN (p)))
{
no_use_except_counting = 0;
break;
}
}
}
if (no_use_except_counting)
; /* no need to worry about MEMs. */
else if (num_mem_sets <= 1)
{
for (p = loop_start; p != loop_end; p = NEXT_INSN (p))
if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
num_nonfixed_reads += count_nonfixed_reads (PATTERN (p));
/* If the loop has a single store, and the destination address is
invariant, then we can't reverse the loop, because this address
might then have the wrong value at loop exit.
This would work if the source was invariant also, however, in that
case, the insn should have been moved out of the loop. */
if (num_mem_sets == 1)
{
struct induction *v;
reversible_mem_store
= (! unknown_address_altered
&& ! invariant_p (XEXP (XEXP (loop_store_mems, 0), 0)));
/* If the store depends on a register that is set after the
store, it depends on the initial value, and is thus not
reversible. */
for (v = bl->giv; reversible_mem_store && v; v = v->next_iv)
{
if (v->giv_type == DEST_REG
&& reg_mentioned_p (v->dest_reg,
XEXP (loop_store_mems, 0))
&& loop_insn_first_p (first_loop_store_insn, v->insn))
reversible_mem_store = 0;
}
}
}
else
return 0;
/* This code only acts for innermost loops. Also it simplifies
the memory address check by only reversing loops with
zero or one memory access.
Two memory accesses could involve parts of the same array,
and that can't be reversed.
If the biv is used only for counting, than we don't need to worry
about all these things. */
if ((num_nonfixed_reads <= 1
&& !loop_has_call
&& !loop_has_volatile
&& reversible_mem_store
&& (bl->giv_count + bl->biv_count + num_mem_sets
+ num_movables + compare_and_branch == insn_count)
&& (bl == loop_iv_list && bl->next == 0))
|| no_use_except_counting)
{
rtx tem;
/* Loop can be reversed. */
if (loop_dump_stream)
fprintf (loop_dump_stream, "Can reverse loop\n");
/* Now check other conditions:
The increment must be a constant, as must the initial value,
and the comparison code must be LT.
This test can probably be improved since +/- 1 in the constant
can be obtained by changing LT to LE and vice versa; this is
confusing. */
if (comparison
/* for constants, LE gets turned into LT */
&& (GET_CODE (comparison) == LT
|| (GET_CODE (comparison) == LE
&& no_use_except_counting)))
{
HOST_WIDE_INT add_val, add_adjust, comparison_val;
rtx initial_value, comparison_value;
int nonneg = 0;
enum rtx_code cmp_code;
int comparison_const_width;
unsigned HOST_WIDE_INT comparison_sign_mask;
add_val = INTVAL (bl->biv->add_val);
comparison_value = XEXP (comparison, 1);
if (GET_MODE (comparison_value) == VOIDmode)
comparison_const_width
= GET_MODE_BITSIZE (GET_MODE (XEXP (comparison, 0)));
else
comparison_const_width
= GET_MODE_BITSIZE (GET_MODE (comparison_value));
if (comparison_const_width > HOST_BITS_PER_WIDE_INT)
comparison_const_width = HOST_BITS_PER_WIDE_INT;
comparison_sign_mask
= (unsigned HOST_WIDE_INT)1 << (comparison_const_width - 1);
/* If the comparison value is not a loop invariant, then we
can not reverse this loop.
??? If the insns which initialize the comparison value as
a whole compute an invariant result, then we could move
them out of the loop and proceed with loop reversal. */
if (!invariant_p (comparison_value))
return 0;
if (GET_CODE (comparison_value) == CONST_INT)
comparison_val = INTVAL (comparison_value);
initial_value = bl->initial_value;
/* Normalize the initial value if it is an integer and
has no other use except as a counter. This will allow
a few more loops to be reversed. */
if (no_use_except_counting
&& GET_CODE (comparison_value) == CONST_INT
&& GET_CODE (initial_value) == CONST_INT)
{
comparison_val = comparison_val - INTVAL (bl->initial_value);
/* The code below requires comparison_val to be a multiple
of add_val in order to do the loop reversal, so
round up comparison_val to a multiple of add_val.
Since comparison_value is constant, we know that the
current comparison code is LT. */
comparison_val = comparison_val + add_val - 1;
comparison_val
-= (unsigned HOST_WIDE_INT) comparison_val % add_val;
/* We postpone overflow checks for COMPARISON_VAL here;
even if there is an overflow, we might still be able to
reverse the loop, if converting the loop exit test to
NE is possible. */
initial_value = const0_rtx;
}
/* First check if we can do a vanilla loop reversal. */
if (initial_value == const0_rtx
/* If we have a decrement_and_branch_on_count, prefer
the NE test, since this will allow that instruction to
be generated. Note that we must use a vanilla loop
reversal if the biv is used to calculate a giv or has
a non-counting use. */
#if ! defined (HAVE_decrement_and_branch_until_zero) && defined (HAVE_decrement_and_branch_on_count)
&& (! (add_val == 1 && loop_info->vtop
&& (bl->biv_count == 0
|| no_use_except_counting)))
#endif
&& GET_CODE (comparison_value) == CONST_INT
/* Now do postponed overflow checks on COMPARISON_VAL. */
&& ! (((comparison_val - add_val) ^ INTVAL (comparison_value))
& comparison_sign_mask))
{
/* Register will always be nonnegative, with value
0 on last iteration */
add_adjust = add_val;
nonneg = 1;
cmp_code = GE;
}
else if (add_val == 1 && loop_info->vtop
&& (bl->biv_count == 0
|| no_use_except_counting))
{
add_adjust = 0;
cmp_code = NE;
}
else
return 0;
if (GET_CODE (comparison) == LE)
add_adjust -= add_val;
/* If the initial value is not zero, or if the comparison
value is not an exact multiple of the increment, then we
can not reverse this loop. */
if (initial_value == const0_rtx
&& GET_CODE (comparison_value) == CONST_INT)
{
if (((unsigned HOST_WIDE_INT) comparison_val % add_val) != 0)
return 0;
}
else
{
if (! no_use_except_counting || add_val != 1)
return 0;
}
final_value = comparison_value;
/* Reset these in case we normalized the initial value
and comparison value above. */
if (GET_CODE (comparison_value) == CONST_INT
&& GET_CODE (initial_value) == CONST_INT)
{
comparison_value = GEN_INT (comparison_val);
final_value
= GEN_INT (comparison_val + INTVAL (bl->initial_value));
}
bl->initial_value = initial_value;
/* Save some info needed to produce the new insns. */
reg = bl->biv->dest_reg;
jump_label = XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 1);
if (jump_label == pc_rtx)
jump_label = XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 2);
new_add_val = GEN_INT (- INTVAL (bl->biv->add_val));
/* Set start_value; if this is not a CONST_INT, we need
to generate a SUB.
Initialize biv to start_value before loop start.
The old initializing insn will be deleted as a
dead store by flow.c. */
if (initial_value == const0_rtx
&& GET_CODE (comparison_value) == CONST_INT)
{
start_value = GEN_INT (comparison_val - add_adjust);
emit_insn_before (gen_move_insn (reg, start_value),
loop_start);
}
else if (GET_CODE (initial_value) == CONST_INT)
{
rtx offset = GEN_INT (-INTVAL (initial_value) - add_adjust);
enum machine_mode mode = GET_MODE (reg);
enum insn_code icode
= add_optab->handlers[(int) mode].insn_code;
if (! (*insn_operand_predicate[icode][0]) (reg, mode)
|| ! ((*insn_operand_predicate[icode][1])
(comparison_value, mode))
|| ! (*insn_operand_predicate[icode][2]) (offset, mode))
return 0;
start_value
= gen_rtx_PLUS (mode, comparison_value, offset);
emit_insn_before ((GEN_FCN (icode)
(reg, comparison_value, offset)),
loop_start);
if (GET_CODE (comparison) == LE)
final_value = gen_rtx_PLUS (mode, comparison_value,
GEN_INT (add_val));
}
else if (! add_adjust)
{
enum machine_mode mode = GET_MODE (reg);
enum insn_code icode
= sub_optab->handlers[(int) mode].insn_code;
if (! (*insn_operand_predicate[icode][0]) (reg, mode)
|| ! ((*insn_operand_predicate[icode][1])
(comparison_value, mode))
|| ! ((*insn_operand_predicate[icode][2])
(initial_value, mode)))
return 0;
start_value
= gen_rtx_MINUS (mode, comparison_value, initial_value);
emit_insn_before ((GEN_FCN (icode)
(reg, comparison_value, initial_value)),
loop_start);
}
else
/* We could handle the other cases too, but it'll be
better to have a testcase first. */
return 0;
/* We may not have a single insn which can increment a reg, so
create a sequence to hold all the insns from expand_inc. */
start_sequence ();
expand_inc (reg, new_add_val);
tem = gen_sequence ();
end_sequence ();
p = emit_insn_before (tem, bl->biv->insn);
delete_insn (bl->biv->insn);
/* Update biv info to reflect its new status. */
bl->biv->insn = p;
bl->initial_value = start_value;
bl->biv->add_val = new_add_val;
/* Update loop info. */
loop_info->initial_value = reg;
loop_info->initial_equiv_value = reg;
loop_info->final_value = const0_rtx;
loop_info->final_equiv_value = const0_rtx;
loop_info->comparison_value = const0_rtx;
loop_info->comparison_code = cmp_code;
loop_info->increment = new_add_val;
/* Inc LABEL_NUSES so that delete_insn will
not delete the label. */
LABEL_NUSES (XEXP (jump_label, 0)) ++;
/* Emit an insn after the end of the loop to set the biv's
proper exit value if it is used anywhere outside the loop. */
if ((REGNO_LAST_UID (bl->regno) != INSN_UID (first_compare))
|| ! bl->init_insn
|| REGNO_FIRST_UID (bl->regno) != INSN_UID (bl->init_insn))
emit_insn_after (gen_move_insn (reg, final_value),
loop_end);
/* Delete compare/branch at end of loop. */
delete_insn (PREV_INSN (loop_end));
if (compare_and_branch == 2)
delete_insn (first_compare);
/* Add new compare/branch insn at end of loop. */
start_sequence ();
emit_cmp_and_jump_insns (reg, const0_rtx, cmp_code, NULL_RTX,
GET_MODE (reg), 0, 0,
XEXP (jump_label, 0));
tem = gen_sequence ();
end_sequence ();
emit_jump_insn_before (tem, loop_end);
for (tem = PREV_INSN (loop_end);
tem && GET_CODE (tem) != JUMP_INSN;
tem = PREV_INSN (tem))
;
if (tem)
JUMP_LABEL (tem) = XEXP (jump_label, 0);
if (nonneg)
{
if (tem)
{
/* Increment of LABEL_NUSES done above. */
/* Register is now always nonnegative,
so add REG_NONNEG note to the branch. */
REG_NOTES (tem) = gen_rtx_EXPR_LIST (REG_NONNEG, NULL_RTX,
REG_NOTES (tem));
}
bl->nonneg = 1;
}
/* Mark that this biv has been reversed. Each giv which depends
on this biv, and which is also live past the end of the loop
will have to be fixed up. */
bl->reversed = 1;
if (loop_dump_stream)
{
fprintf (loop_dump_stream, "Reversed loop");
if (bl->nonneg)
fprintf (loop_dump_stream, " and added reg_nonneg\n");
else
fprintf (loop_dump_stream, "\n");
}
return 1;
}
}
}
return 0;
}
/* Verify whether the biv BL appears to be eliminable,
based on the insns in the loop that refer to it.
LOOP_START is the first insn of the loop, and END is the end insn.
If ELIMINATE_P is non-zero, actually do the elimination.
THRESHOLD and INSN_COUNT are from loop_optimize and are used to
determine whether invariant insns should be placed inside or at the
start of the loop. */
static int
maybe_eliminate_biv (bl, loop_start, end, eliminate_p, threshold, insn_count)
struct iv_class *bl;
rtx loop_start;
rtx end;
int eliminate_p;
int threshold, insn_count;
{
rtx reg = bl->biv->dest_reg;
rtx p;
/* Scan all insns in the loop, stopping if we find one that uses the
biv in a way that we cannot eliminate. */
for (p = loop_start; p != end; p = NEXT_INSN (p))
{
enum rtx_code code = GET_CODE (p);
rtx where = threshold >= insn_count ? loop_start : p;
/* If this is a libcall that sets a giv, skip ahead to its end. */
if (GET_RTX_CLASS (code) == 'i')
{
rtx note = find_reg_note (p, REG_LIBCALL, NULL_RTX);
if (note)
{
rtx last = XEXP (note, 0);
rtx set = single_set (last);
if (set && GET_CODE (SET_DEST (set)) == REG)
{
int regno = REGNO (SET_DEST (set));
if (regno < max_reg_before_loop
&& REG_IV_TYPE (regno) == GENERAL_INDUCT
&& REG_IV_INFO (regno)->src_reg == bl->biv->src_reg)
p = last;
}
}
}
if ((code == INSN || code == JUMP_INSN || code == CALL_INSN)
&& reg_mentioned_p (reg, PATTERN (p))
&& ! maybe_eliminate_biv_1 (PATTERN (p), p, bl, eliminate_p, where))
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"Cannot eliminate biv %d: biv used in insn %d.\n",
bl->regno, INSN_UID (p));
break;
}
}
if (p == end)
{
if (loop_dump_stream)
fprintf (loop_dump_stream, "biv %d %s eliminated.\n",
bl->regno, eliminate_p ? "was" : "can be");
return 1;
}
return 0;
}
/* INSN and REFERENCE are instructions in the same insn chain.
Return non-zero if INSN is first. */
int
loop_insn_first_p (insn, reference)
rtx insn, reference;
{
rtx p, q;
for (p = insn, q = reference; ;)
{
/* Start with test for not first so that INSN == REFERENCE yields not
first. */
if (q == insn || ! p)
return 0;
if (p == reference || ! q)
return 1;
/* Either of P or Q might be a NOTE. Notes have the same LUID as the
previous insn, hence the <= comparison below does not work if
P is a note. */
if (INSN_UID (p) < max_uid_for_loop
&& INSN_UID (q) < max_uid_for_loop
&& GET_CODE (p) != NOTE)
return INSN_LUID (p) <= INSN_LUID (q);
if (INSN_UID (p) >= max_uid_for_loop
|| GET_CODE (p) == NOTE)
p = NEXT_INSN (p);
if (INSN_UID (q) >= max_uid_for_loop)
q = NEXT_INSN (q);
}
}
/* We are trying to eliminate BIV in INSN using GIV. Return non-zero if
the offset that we have to take into account due to auto-increment /
div derivation is zero. */
static int
biv_elimination_giv_has_0_offset (biv, giv, insn)
struct induction *biv, *giv;
rtx insn;
{
/* If the giv V had the auto-inc address optimization applied
to it, and INSN occurs between the giv insn and the biv
insn, then we'd have to adjust the value used here.
This is rare, so we don't bother to make this possible. */
if (giv->auto_inc_opt
&& ((loop_insn_first_p (giv->insn, insn)
&& loop_insn_first_p (insn, biv->insn))
|| (loop_insn_first_p (biv->insn, insn)
&& loop_insn_first_p (insn, giv->insn))))
return 0;
/* If the giv V was derived from another giv, and INSN does
not occur between the giv insn and the biv insn, then we'd
have to adjust the value used here. This is rare, so we don't
bother to make this possible. */
if (giv->derived_from
&& ! (giv->always_executed
&& loop_insn_first_p (giv->insn, insn)
&& loop_insn_first_p (insn, biv->insn)))
return 0;
if (giv->same
&& giv->same->derived_from
&& ! (giv->same->always_executed
&& loop_insn_first_p (giv->same->insn, insn)
&& loop_insn_first_p (insn, biv->insn)))
return 0;
return 1;
}
/* If BL appears in X (part of the pattern of INSN), see if we can
eliminate its use. If so, return 1. If not, return 0.
If BIV does not appear in X, return 1.
If ELIMINATE_P is non-zero, actually do the elimination. WHERE indicates
where extra insns should be added. Depending on how many items have been
moved out of the loop, it will either be before INSN or at the start of
the loop. */
static int
maybe_eliminate_biv_1 (x, insn, bl, eliminate_p, where)
rtx x, insn;
struct iv_class *bl;
int eliminate_p;
rtx where;
{
enum rtx_code code = GET_CODE (x);
rtx reg = bl->biv->dest_reg;
enum machine_mode mode = GET_MODE (reg);
struct induction *v;
rtx arg, tem;
#ifdef HAVE_cc0
rtx new;
#endif
int arg_operand;
char *fmt;
int i, j;
switch (code)
{
case REG:
/* If we haven't already been able to do something with this BIV,
we can't eliminate it. */
if (x == reg)
return 0;
return 1;
case SET:
/* If this sets the BIV, it is not a problem. */
if (SET_DEST (x) == reg)
return 1;
/* If this is an insn that defines a giv, it is also ok because
it will go away when the giv is reduced. */
for (v = bl->giv; v; v = v->next_iv)
if (v->giv_type == DEST_REG && SET_DEST (x) == v->dest_reg)
return 1;
#ifdef HAVE_cc0
if (SET_DEST (x) == cc0_rtx && SET_SRC (x) == reg)
{
/* Can replace with any giv that was reduced and
that has (MULT_VAL != 0) and (ADD_VAL == 0).
Require a constant for MULT_VAL, so we know it's nonzero.
??? We disable this optimization to avoid potential
overflows. */
for (v = bl->giv; v; v = v->next_iv)
if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx
&& v->add_val == const0_rtx
&& ! v->ignore && ! v->maybe_dead && v->always_computable
&& v->mode == mode
&& 0)
{
if (! biv_elimination_giv_has_0_offset (bl->biv, v, insn))
continue;
if (! eliminate_p)
return 1;
/* If the giv has the opposite direction of change,
then reverse the comparison. */
if (INTVAL (v->mult_val) < 0)
new = gen_rtx_COMPARE (GET_MODE (v->new_reg),
const0_rtx, v->new_reg);
else
new = v->new_reg;
/* We can probably test that giv's reduced reg. */
if (validate_change (insn, &SET_SRC (x), new, 0))
return 1;
}
/* Look for a giv with (MULT_VAL != 0) and (ADD_VAL != 0);
replace test insn with a compare insn (cmp REDUCED_GIV ADD_VAL).
Require a constant for MULT_VAL, so we know it's nonzero.
??? Do this only if ADD_VAL is a pointer to avoid a potential
overflow problem. */
for (v = bl->giv; v; v = v->next_iv)
if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx
&& ! v->ignore && ! v->maybe_dead && v->always_computable
&& v->mode == mode
&& (GET_CODE (v->add_val) == SYMBOL_REF
|| GET_CODE (v->add_val) == LABEL_REF
|| GET_CODE (v->add_val) == CONST
|| (GET_CODE (v->add_val) == REG
&& REGNO_POINTER_FLAG (REGNO (v->add_val)))))
{
if (! biv_elimination_giv_has_0_offset (bl->biv, v, insn))
continue;
if (! eliminate_p)
return 1;
/* If the giv has the opposite direction of change,
then reverse the comparison. */
if (INTVAL (v->mult_val) < 0)
new = gen_rtx_COMPARE (VOIDmode, copy_rtx (v->add_val),
v->new_reg);
else
new = gen_rtx_COMPARE (VOIDmode, v->new_reg,
copy_rtx (v->add_val));
/* Replace biv with the giv's reduced register. */
update_reg_last_use (v->add_val, insn);
if (validate_change (insn, &SET_SRC (PATTERN (insn)), new, 0))
return 1;
/* Insn doesn't support that constant or invariant. Copy it
into a register (it will be a loop invariant.) */
tem = gen_reg_rtx (GET_MODE (v->new_reg));
emit_insn_before (gen_move_insn (tem, copy_rtx (v->add_val)),
where);
/* Substitute the new register for its invariant value in
the compare expression. */
XEXP (new, (INTVAL (v->mult_val) < 0) ? 0 : 1) = tem;
if (validate_change (insn, &SET_SRC (PATTERN (insn)), new, 0))
return 1;
}
}
#endif
break;
case COMPARE:
case EQ: case NE:
case GT: case GE: case GTU: case GEU:
case LT: case LE: case LTU: case LEU:
/* See if either argument is the biv. */
if (XEXP (x, 0) == reg)
arg = XEXP (x, 1), arg_operand = 1;
else if (XEXP (x, 1) == reg)
arg = XEXP (x, 0), arg_operand = 0;
else
break;
if (CONSTANT_P (arg))
{
/* First try to replace with any giv that has constant positive
mult_val and constant add_val. We might be able to support
negative mult_val, but it seems complex to do it in general. */
for (v = bl->giv; v; v = v->next_iv)
if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0
&& (GET_CODE (v->add_val) == SYMBOL_REF
|| GET_CODE (v->add_val) == LABEL_REF
|| GET_CODE (v->add_val) == CONST
|| (GET_CODE (v->add_val) == REG
&& REGNO_POINTER_FLAG (REGNO (v->add_val))))
&& ! v->ignore && ! v->maybe_dead && v->always_computable
&& v->mode == mode)
{
if (! biv_elimination_giv_has_0_offset (bl->biv, v, insn))
continue;
if (! eliminate_p)
return 1;
/* Replace biv with the giv's reduced reg. */
XEXP (x, 1-arg_operand) = v->new_reg;
/* If all constants are actually constant integers and
the derived constant can be directly placed in the COMPARE,
do so. */
if (GET_CODE (arg) == CONST_INT
&& GET_CODE (v->mult_val) == CONST_INT
&& GET_CODE (v->add_val) == CONST_INT
&& validate_change (insn, &XEXP (x, arg_operand),
GEN_INT (INTVAL (arg)
* INTVAL (v->mult_val)
+ INTVAL (v->add_val)), 0))
return 1;
/* Otherwise, load it into a register. */
tem = gen_reg_rtx (mode);
emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where);
if (validate_change (insn, &XEXP (x, arg_operand), tem, 0))
return 1;
/* If that failed, put back the change we made above. */
XEXP (x, 1-arg_operand) = reg;
}
/* Look for giv with positive constant mult_val and nonconst add_val.
Insert insns to calculate new compare value.
??? Turn this off due to possible overflow. */
for (v = bl->giv; v; v = v->next_iv)
if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0
&& ! v->ignore && ! v->maybe_dead && v->always_computable
&& v->mode == mode
&& 0)
{
rtx tem;
if (! biv_elimination_giv_has_0_offset (bl->biv, v, insn))
continue;
if (! eliminate_p)
return 1;
tem = gen_reg_rtx (mode);
/* Replace biv with giv's reduced register. */
validate_change (insn, &XEXP (x, 1 - arg_operand),
v->new_reg, 1);
/* Compute value to compare against. */
emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where);
/* Use it in this insn. */
validate_change (insn, &XEXP (x, arg_operand), tem, 1);
if (apply_change_group ())
return 1;
}
}
else if (GET_CODE (arg) == REG || GET_CODE (arg) == MEM)
{
if (invariant_p (arg) == 1)
{
/* Look for giv with constant positive mult_val and nonconst
add_val. Insert insns to compute new compare value.
??? Turn this off due to possible overflow. */
for (v = bl->giv; v; v = v->next_iv)
if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0
&& ! v->ignore && ! v->maybe_dead && v->always_computable
&& v->mode == mode
&& 0)
{
rtx tem;
if (! biv_elimination_giv_has_0_offset (bl->biv, v, insn))
continue;
if (! eliminate_p)
return 1;
tem = gen_reg_rtx (mode);
/* Replace biv with giv's reduced register. */
validate_change (insn, &XEXP (x, 1 - arg_operand),
v->new_reg, 1);
/* Compute value to compare against. */
emit_iv_add_mult (arg, v->mult_val, v->add_val,
tem, where);
validate_change (insn, &XEXP (x, arg_operand), tem, 1);
if (apply_change_group ())
return 1;
}
}
/* This code has problems. Basically, you can't know when
seeing if we will eliminate BL, whether a particular giv
of ARG will be reduced. If it isn't going to be reduced,
we can't eliminate BL. We can try forcing it to be reduced,
but that can generate poor code.
The problem is that the benefit of reducing TV, below should
be increased if BL can actually be eliminated, but this means
we might have to do a topological sort of the order in which
we try to process biv. It doesn't seem worthwhile to do
this sort of thing now. */
#if 0
/* Otherwise the reg compared with had better be a biv. */
if (GET_CODE (arg) != REG
|| REG_IV_TYPE (REGNO (arg)) != BASIC_INDUCT)
return 0;
/* Look for a pair of givs, one for each biv,
with identical coefficients. */
for (v = bl->giv; v; v = v->next_iv)
{
struct induction *tv;
if (v->ignore || v->maybe_dead || v->mode != mode)
continue;
for (tv = reg_biv_class[REGNO (arg)]->giv; tv; tv = tv->next_iv)
if (! tv->ignore && ! tv->maybe_dead
&& rtx_equal_p (tv->mult_val, v->mult_val)
&& rtx_equal_p (tv->add_val, v->add_val)
&& tv->mode == mode)
{
if (! biv_elimination_giv_has_0_offset (bl->biv, v, insn))
continue;
if (! eliminate_p)
return 1;
/* Replace biv with its giv's reduced reg. */
XEXP (x, 1-arg_operand) = v->new_reg;
/* Replace other operand with the other giv's
reduced reg. */
XEXP (x, arg_operand) = tv->new_reg;
return 1;
}
}
#endif
}
/* If we get here, the biv can't be eliminated. */
return 0;
case MEM:
/* If this address is a DEST_ADDR giv, it doesn't matter if the
biv is used in it, since it will be replaced. */
for (v = bl->giv; v; v = v->next_iv)
if (v->giv_type == DEST_ADDR && v->location == &XEXP (x, 0))
return 1;
break;
default:
break;
}
/* See if any subexpression fails elimination. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
switch (fmt[i])
{
case 'e':
if (! maybe_eliminate_biv_1 (XEXP (x, i), insn, bl,
eliminate_p, where))
return 0;
break;
case 'E':
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (! maybe_eliminate_biv_1 (XVECEXP (x, i, j), insn, bl,
eliminate_p, where))
return 0;
break;
}
}
return 1;
}
/* Return nonzero if the last use of REG
is in an insn following INSN in the same basic block. */
static int
last_use_this_basic_block (reg, insn)
rtx reg;
rtx insn;
{
rtx n;
for (n = insn;
n && GET_CODE (n) != CODE_LABEL && GET_CODE (n) != JUMP_INSN;
n = NEXT_INSN (n))
{
if (REGNO_LAST_UID (REGNO (reg)) == INSN_UID (n))
return 1;
}
return 0;
}
/* Called via `note_stores' to record the initial value of a biv. Here we
just record the location of the set and process it later. */
static void
record_initial (dest, set)
rtx dest;
rtx set;
{
struct iv_class *bl;
if (GET_CODE (dest) != REG
|| REGNO (dest) >= max_reg_before_loop
|| REG_IV_TYPE (REGNO (dest)) != BASIC_INDUCT)
return;
bl = reg_biv_class[REGNO (dest)];
/* If this is the first set found, record it. */
if (bl->init_insn == 0)
{
bl->init_insn = note_insn;
bl->init_set = set;
}
}
/* If any of the registers in X are "old" and currently have a last use earlier
than INSN, update them to have a last use of INSN. Their actual last use
will be the previous insn but it will not have a valid uid_luid so we can't
use it. */
static void
update_reg_last_use (x, insn)
rtx x;
rtx insn;
{
/* Check for the case where INSN does not have a valid luid. In this case,
there is no need to modify the regno_last_uid, as this can only happen
when code is inserted after the loop_end to set a pseudo's final value,
and hence this insn will never be the last use of x. */
if (GET_CODE (x) == REG && REGNO (x) < max_reg_before_loop
&& INSN_UID (insn) < max_uid_for_loop
&& uid_luid[REGNO_LAST_UID (REGNO (x))] < uid_luid[INSN_UID (insn)])
REGNO_LAST_UID (REGNO (x)) = INSN_UID (insn);
else
{
register int i, j;
register char *fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
update_reg_last_use (XEXP (x, i), insn);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
update_reg_last_use (XVECEXP (x, i, j), insn);
}
}
}
/* Given a jump insn JUMP, return the condition that will cause it to branch
to its JUMP_LABEL. If the condition cannot be understood, or is an
inequality floating-point comparison which needs to be reversed, 0 will
be returned.
If EARLIEST is non-zero, it is a pointer to a place where the earliest
insn used in locating the condition was found. If a replacement test
of the condition is desired, it should be placed in front of that
insn and we will be sure that the inputs are still valid.
The condition will be returned in a canonical form to simplify testing by
callers. Specifically:
(1) The code will always be a comparison operation (EQ, NE, GT, etc.).
(2) Both operands will be machine operands; (cc0) will have been replaced.
(3) If an operand is a constant, it will be the second operand.
(4) (LE x const) will be replaced with (LT x <const+1>) and similarly
for GE, GEU, and LEU. */
rtx
get_condition (jump, earliest)
rtx jump;
rtx *earliest;
{
enum rtx_code code;
rtx prev = jump;
rtx set;
rtx tem;
rtx op0, op1;
int reverse_code = 0;
int did_reverse_condition = 0;
enum machine_mode mode;
/* If this is not a standard conditional jump, we can't parse it. */
if (GET_CODE (jump) != JUMP_INSN
|| ! condjump_p (jump) || simplejump_p (jump))
return 0;
code = GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 0));
mode = GET_MODE (XEXP (SET_SRC (PATTERN (jump)), 0));
op0 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 0);
op1 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 1);
if (earliest)
*earliest = jump;
/* If this branches to JUMP_LABEL when the condition is false, reverse
the condition. */
if (GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 2)) == LABEL_REF
&& XEXP (XEXP (SET_SRC (PATTERN (jump)), 2), 0) == JUMP_LABEL (jump))
code = reverse_condition (code), did_reverse_condition ^= 1;
/* If we are comparing a register with zero, see if the register is set
in the previous insn to a COMPARE or a comparison operation. Perform
the same tests as a function of STORE_FLAG_VALUE as find_comparison_args
in cse.c */
while (GET_RTX_CLASS (code) == '<' && op1 == CONST0_RTX (GET_MODE (op0)))
{
/* Set non-zero when we find something of interest. */
rtx x = 0;
#ifdef HAVE_cc0
/* If comparison with cc0, import actual comparison from compare
insn. */
if (op0 == cc0_rtx)
{
if ((prev = prev_nonnote_insn (prev)) == 0
|| GET_CODE (prev) != INSN
|| (set = single_set (prev)) == 0
|| SET_DEST (set) != cc0_rtx)
return 0;
op0 = SET_SRC (set);
op1 = CONST0_RTX (GET_MODE (op0));
if (earliest)
*earliest = prev;
}
#endif
/* If this is a COMPARE, pick up the two things being compared. */
if (GET_CODE (op0) == COMPARE)
{
op1 = XEXP (op0, 1);
op0 = XEXP (op0, 0);
continue;
}
else if (GET_CODE (op0) != REG)
break;
/* Go back to the previous insn. Stop if it is not an INSN. We also
stop if it isn't a single set or if it has a REG_INC note because
we don't want to bother dealing with it. */
if ((prev = prev_nonnote_insn (prev)) == 0
|| GET_CODE (prev) != INSN
|| FIND_REG_INC_NOTE (prev, 0)
|| (set = single_set (prev)) == 0)
break;
/* If this is setting OP0, get what it sets it to if it looks
relevant. */
if (rtx_equal_p (SET_DEST (set), op0))
{
enum machine_mode inner_mode = GET_MODE (SET_SRC (set));
/* ??? We may not combine comparisons done in a CCmode with
comparisons not done in a CCmode. This is to aid targets
like Alpha that have an IEEE compliant EQ instruction, and
a non-IEEE compliant BEQ instruction. The use of CCmode is
actually artificial, simply to prevent the combination, but
should not affect other platforms.
However, we must allow VOIDmode comparisons to match either
CCmode or non-CCmode comparison, because some ports have
modeless comparisons inside branch patterns.
??? This mode check should perhaps look more like the mode check
in simplify_comparison in combine. */
if ((GET_CODE (SET_SRC (set)) == COMPARE
|| (((code == NE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_INT
&& (GET_MODE_BITSIZE (inner_mode)
<= HOST_BITS_PER_WIDE_INT)
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
))
&& GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<'))
&& (((GET_MODE_CLASS (mode) == MODE_CC)
== (GET_MODE_CLASS (inner_mode) == MODE_CC))
|| mode == VOIDmode || inner_mode == VOIDmode))
x = SET_SRC (set);
else if (((code == EQ
|| (code == GE
&& (GET_MODE_BITSIZE (inner_mode)
<= HOST_BITS_PER_WIDE_INT)
&& GET_MODE_CLASS (inner_mode) == MODE_INT
&& (STORE_FLAG_VALUE
& ((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (inner_mode) - 1))))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == GE
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
&& FLOAT_STORE_FLAG_VALUE < 0)
#endif
))
&& GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<'
&& (((GET_MODE_CLASS (mode) == MODE_CC)
== (GET_MODE_CLASS (inner_mode) == MODE_CC))
|| mode == VOIDmode || inner_mode == VOIDmode))
{
/* We might have reversed a LT to get a GE here. But this wasn't
actually the comparison of data, so we don't flag that we
have had to reverse the condition. */
did_reverse_condition ^= 1;
reverse_code = 1;
x = SET_SRC (set);
}
else
break;
}
else if (reg_set_p (op0, prev))
/* If this sets OP0, but not directly, we have to give up. */
break;
if (x)
{
if (GET_RTX_CLASS (GET_CODE (x)) == '<')
code = GET_CODE (x);
if (reverse_code)
{
code = reverse_condition (code);
did_reverse_condition ^= 1;
reverse_code = 0;
}
op0 = XEXP (x, 0), op1 = XEXP (x, 1);
if (earliest)
*earliest = prev;
}
}
/* If constant is first, put it last. */
if (CONSTANT_P (op0))
code = swap_condition (code), tem = op0, op0 = op1, op1 = tem;
/* If OP0 is the result of a comparison, we weren't able to find what
was really being compared, so fail. */
if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
return 0;
/* Canonicalize any ordered comparison with integers involving equality
if we can do computations in the relevant mode and we do not
overflow. */
if (GET_CODE (op1) == CONST_INT
&& GET_MODE (op0) != VOIDmode
&& GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT)
{
HOST_WIDE_INT const_val = INTVAL (op1);
unsigned HOST_WIDE_INT uconst_val = const_val;
unsigned HOST_WIDE_INT max_val
= (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0));
switch (code)
{
case LE:
if ((unsigned HOST_WIDE_INT) const_val != max_val >> 1)
code = LT, op1 = GEN_INT (const_val + 1);
break;
/* When cross-compiling, const_val might be sign-extended from
BITS_PER_WORD to HOST_BITS_PER_WIDE_INT */
case GE:
if ((HOST_WIDE_INT) (const_val & max_val)
!= (((HOST_WIDE_INT) 1
<< (GET_MODE_BITSIZE (GET_MODE (op0)) - 1))))
code = GT, op1 = GEN_INT (const_val - 1);
break;
case LEU:
if (uconst_val < max_val)
code = LTU, op1 = GEN_INT (uconst_val + 1);
break;
case GEU:
if (uconst_val != 0)
code = GTU, op1 = GEN_INT (uconst_val - 1);
break;
default:
break;
}
}
/* If this was floating-point and we reversed anything other than an
EQ or NE, return zero. */
if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
&& did_reverse_condition && code != NE && code != EQ
&& ! flag_fast_math
&& GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
return 0;
#ifdef HAVE_cc0
/* Never return CC0; return zero instead. */
if (op0 == cc0_rtx)
return 0;
#endif
return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
}
/* Similar to above routine, except that we also put an invariant last
unless both operands are invariants. */
rtx
get_condition_for_loop (x)
rtx x;
{
rtx comparison = get_condition (x, NULL_PTR);
if (comparison == 0
|| ! invariant_p (XEXP (comparison, 0))
|| invariant_p (XEXP (comparison, 1)))
return comparison;
return gen_rtx_fmt_ee (swap_condition (GET_CODE (comparison)), VOIDmode,
XEXP (comparison, 1), XEXP (comparison, 0));
}
#ifdef HAVE_decrement_and_branch_on_count
/* Instrument loop for insertion of bct instruction. We distinguish between
loops with compile-time bounds and those with run-time bounds.
Information from loop_iterations() is used to compute compile-time bounds.
Run-time bounds should use loop preconditioning, but currently ignored.
*/
static void
insert_bct (loop_start, loop_end, loop_info)
rtx loop_start, loop_end;
struct loop_info *loop_info;
{
int i;
unsigned HOST_WIDE_INT n_iterations;
int increment_direction, compare_direction;
/* If the loop condition is <= or >=, the number of iteration
is 1 more than the range of the bounds of the loop. */
int add_iteration = 0;
enum machine_mode loop_var_mode = word_mode;
int loop_num = uid_loop_num [INSN_UID (loop_start)];
/* It's impossible to instrument a competely unrolled loop. */
if (loop_info->unroll_number == -1)
return;
/* Make sure that the count register is not in use. */
if (loop_used_count_register [loop_num])
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT instrumentation failed: count register already in use\n",
loop_num);
return;
}
/* Make sure that the function has no indirect jumps. */
if (indirect_jump_in_function)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT instrumentation failed: indirect jump in function\n",
loop_num);
return;
}
/* Make sure that the last loop insn is a conditional jump. */
if (GET_CODE (PREV_INSN (loop_end)) != JUMP_INSN
|| ! condjump_p (PREV_INSN (loop_end))
|| simplejump_p (PREV_INSN (loop_end)))
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT instrumentation failed: invalid jump at loop end\n",
loop_num);
return;
}
/* Make sure that the loop does not contain a function call
(the count register might be altered by the called function). */
if (loop_has_call)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT instrumentation failed: function call in loop\n",
loop_num);
return;
}
/* Make sure that the loop does not jump via a table.
(the count register might be used to perform the branch on table). */
if (loop_has_tablejump)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT instrumentation failed: computed branch in the loop\n",
loop_num);
return;
}
/* Account for loop unrolling in instrumented iteration count. */
if (loop_info->unroll_number > 1)
n_iterations = loop_info->n_iterations / loop_info->unroll_number;
else
n_iterations = loop_info->n_iterations;
if (n_iterations != 0 && n_iterations < 3)
{
/* Allow an enclosing outer loop to benefit if possible. */
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: Too few iterations to benefit from BCT optimization\n",
loop_num);
return;
}
/* Try to instrument the loop. */
/* Handle the simpler case, where the bounds are known at compile time. */
if (n_iterations > 0)
{
/* Mark all enclosing loops that they cannot use count register. */
for (i = loop_num; i != -1; i = loop_outer_loop[i])
loop_used_count_register[i] = 1;
instrument_loop_bct (loop_start, loop_end, GEN_INT (n_iterations));
return;
}
/* Handle the more complex case, that the bounds are NOT known
at compile time. In this case we generate run_time calculation
of the number of iterations. */
if (loop_info->iteration_var == 0)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT Runtime Instrumentation failed: no loop iteration variable found\n",
loop_num);
return;
}
if (GET_MODE_CLASS (GET_MODE (loop_info->iteration_var)) != MODE_INT
|| GET_MODE_SIZE (GET_MODE (loop_info->iteration_var)) != UNITS_PER_WORD)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT Runtime Instrumentation failed: loop variable not integer\n",
loop_num);
return;
}
/* With runtime bounds, if the compare is of the form '!=' we give up */
if (loop_info->comparison_code == NE)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct %d: BCT Runtime Instrumentation failed: runtime bounds with != comparison\n",
loop_num);
return;
}
/* Use common loop preconditioning code instead. */
#if 0
else
{
/* We rely on the existence of run-time guard to ensure that the
loop executes at least once. */
rtx sequence;
rtx iterations_num_reg;
unsigned HOST_WIDE_INT increment_value_abs
= INTVAL (increment) * increment_direction;
/* make sure that the increment is a power of two, otherwise (an
expensive) divide is needed. */
if (exact_log2 (increment_value_abs) == -1)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
"insert_bct: not instrumenting BCT because the increment is not power of 2\n");
return;
}
/* compute the number of iterations */
start_sequence ();
{
rtx temp_reg;
/* Again, the number of iterations is calculated by:
;
; compare-val - initial-val + (increment -1) + additional-iteration
; num_iterations = -----------------------------------------------------------------
; increment
*/
/* ??? Do we have to call copy_rtx here before passing rtx to
expand_binop? */
if (compare_direction > 0)
{
/* <, <= :the loop variable is increasing */
temp_reg = expand_binop (loop_var_mode, sub_optab,
comparison_value, initial_value,
NULL_RTX, 0, OPTAB_LIB_WIDEN);
}
else
{
temp_reg = expand_binop (loop_var_mode, sub_optab,
initial_value, comparison_value,
NULL_RTX, 0, OPTAB_LIB_WIDEN);
}
if (increment_value_abs - 1 + add_iteration != 0)
temp_reg = expand_binop (loop_var_mode, add_optab, temp_reg,
GEN_INT (increment_value_abs - 1
+ add_iteration),
NULL_RTX, 0, OPTAB_LIB_WIDEN);
if (increment_value_abs != 1)
{
/* ??? This will generate an expensive divide instruction for
most targets. The original authors apparently expected this
to be a shift, since they test for power-of-2 divisors above,
but just naively generating a divide instruction will not give
a shift. It happens to work for the PowerPC target because
the rs6000.md file has a divide pattern that emits shifts.
It will probably not work for any other target. */
iterations_num_reg = expand_binop (loop_var_mode, sdiv_optab,
temp_reg,
GEN_INT (increment_value_abs),
NULL_RTX, 0, OPTAB_LIB_WIDEN);
}
else
iterations_num_reg = temp_reg;
}
sequence = gen_sequence ();
end_sequence ();
emit_insn_before (sequence, loop_start);
instrument_loop_bct (loop_start, loop_end, iterations_num_reg);
}
return;
#endif /* Complex case */
}
/* Instrument loop by inserting a bct in it as follows:
1. A new counter register is created.
2. In the head of the loop the new variable is initialized to the value
passed in the loop_num_iterations parameter.
3. At the end of the loop, comparison of the register with 0 is generated.
The created comparison follows the pattern defined for the
decrement_and_branch_on_count insn, so this insn will be generated.
4. The branch on the old variable are deleted. The compare must remain
because it might be used elsewhere. If the loop-variable or condition
register are used elsewhere, they will be eliminated by flow. */
static void
instrument_loop_bct (loop_start, loop_end, loop_num_iterations)
rtx loop_start, loop_end;
rtx loop_num_iterations;
{
rtx counter_reg;
rtx start_label;
rtx sequence;
if (HAVE_decrement_and_branch_on_count)
{
if (loop_dump_stream)
{
fputs ("instrument_bct: Inserting BCT (", loop_dump_stream);
if (GET_CODE (loop_num_iterations) == CONST_INT)
fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC,
INTVAL (loop_num_iterations));
else
fputs ("runtime", loop_dump_stream);
fputs (" iterations)", loop_dump_stream);
}
/* Discard original jump to continue loop. Original compare result
may still be live, so it cannot be discarded explicitly. */
delete_insn (PREV_INSN (loop_end));
/* Insert the label which will delimit the start of the loop. */
start_label = gen_label_rtx ();
emit_label_after (start_label, loop_start);
/* Insert initialization of the count register into the loop header. */
start_sequence ();
counter_reg = gen_reg_rtx (word_mode);
emit_insn (gen_move_insn (counter_reg, loop_num_iterations));
sequence = gen_sequence ();
end_sequence ();
emit_insn_before (sequence, loop_start);
/* Insert new comparison on the count register instead of the
old one, generating the needed BCT pattern (that will be
later recognized by assembly generation phase). */
emit_jump_insn_before (gen_decrement_and_branch_on_count (counter_reg,
start_label),
loop_end);
LABEL_NUSES (start_label)++;
}
}
#endif /* HAVE_decrement_and_branch_on_count */
/* Scan the function and determine whether it has indirect (computed) jumps.
This is taken mostly from flow.c; similar code exists elsewhere
in the compiler. It may be useful to put this into rtlanal.c. */
static int
indirect_jump_in_function_p (start)
rtx start;
{
rtx insn;
for (insn = start; insn; insn = NEXT_INSN (insn))
if (computed_jump_p (insn))
return 1;
return 0;
}
/* Add MEM to the LOOP_MEMS array, if appropriate. See the
documentation for LOOP_MEMS for the definition of `appropriate'.
This function is called from prescan_loop via for_each_rtx. */
static int
insert_loop_mem (mem, data)
rtx *mem;
void *data ATTRIBUTE_UNUSED;
{
int i;
rtx m = *mem;
if (m == NULL_RTX)
return 0;
switch (GET_CODE (m))
{
case MEM:
break;
case CONST_DOUBLE:
/* We're not interested in the MEM associated with a
CONST_DOUBLE, so there's no need to traverse into this. */
return -1;
default:
/* This is not a MEM. */
return 0;
}
/* See if we've already seen this MEM. */
for (i = 0; i < loop_mems_idx; ++i)
if (rtx_equal_p (m, loop_mems[i].mem))
{
if (GET_MODE (m) != GET_MODE (loop_mems[i].mem))
/* The modes of the two memory accesses are different. If
this happens, something tricky is going on, and we just
don't optimize accesses to this MEM. */
loop_mems[i].optimize = 0;
return 0;
}
/* Resize the array, if necessary. */
if (loop_mems_idx == loop_mems_allocated)
{
if (loop_mems_allocated != 0)
loop_mems_allocated *= 2;
else
loop_mems_allocated = 32;
loop_mems = (loop_mem_info*)
xrealloc (loop_mems,
loop_mems_allocated * sizeof (loop_mem_info));
}
/* Actually insert the MEM. */
loop_mems[loop_mems_idx].mem = m;
/* We can't hoist this MEM out of the loop if it's a BLKmode MEM
because we can't put it in a register. We still store it in the
table, though, so that if we see the same address later, but in a
non-BLK mode, we'll not think we can optimize it at that point. */
loop_mems[loop_mems_idx].optimize = (GET_MODE (m) != BLKmode);
loop_mems[loop_mems_idx].reg = NULL_RTX;
++loop_mems_idx;
return 0;
}
/* Like load_mems, but also ensures that SET_IN_LOOP,
MAY_NOT_OPTIMIZE, REG_SINGLE_USAGE, and INSN_COUNT have the correct
values after load_mems. */
static void
load_mems_and_recount_loop_regs_set (scan_start, end, loop_top, start,
insn_count)
rtx scan_start;
rtx end;
rtx loop_top;
rtx start;
int *insn_count;
{
int nregs = max_reg_num ();
load_mems (scan_start, end, loop_top, start);
/* Recalculate set_in_loop and friends since load_mems may have
created new registers. */
if (max_reg_num () > nregs)
{
int i;
int old_nregs;
old_nregs = nregs;
nregs = max_reg_num ();
if ((unsigned) nregs > set_in_loop->num_elements)
{
/* Grow all the arrays. */
VARRAY_GROW (set_in_loop, nregs);
VARRAY_GROW (n_times_set, nregs);
VARRAY_GROW (may_not_optimize, nregs);
VARRAY_GROW (reg_single_usage, nregs);
}
/* Clear the arrays */
bzero ((char *) &set_in_loop->data, nregs * sizeof (int));
bzero ((char *) &may_not_optimize->data, nregs * sizeof (char));
bzero ((char *) &reg_single_usage->data, nregs * sizeof (rtx));
count_loop_regs_set (loop_top ? loop_top : start, end,
may_not_optimize, reg_single_usage,
insn_count, nregs);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
VARRAY_CHAR (may_not_optimize, i) = 1;
VARRAY_INT (set_in_loop, i) = 1;
}
#ifdef AVOID_CCMODE_COPIES
/* Don't try to move insns which set CC registers if we should not
create CCmode register copies. */
for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
if (GET_MODE_CLASS (GET_MODE (regno_reg_rtx[i])) == MODE_CC)
VARRAY_CHAR (may_not_optimize, i) = 1;
#endif
/* Set n_times_set for the new registers. */
bcopy ((char *) (&set_in_loop->data.i[0] + old_nregs),
(char *) (&n_times_set->data.i[0] + old_nregs),
(nregs - old_nregs) * sizeof (int));
}
}
/* Move MEMs into registers for the duration of the loop. SCAN_START
is the first instruction in the loop (as it is executed). The
other parameters are as for next_insn_in_loop. */
static void
load_mems (scan_start, end, loop_top, start)
rtx scan_start;
rtx end;
rtx loop_top;
rtx start;
{
int maybe_never = 0;
int i;
rtx p;
rtx label = NULL_RTX;
rtx end_label;
if (loop_mems_idx > 0)
{
/* Nonzero if the next instruction may never be executed. */
int next_maybe_never = 0;
/* Check to see if it's possible that some instructions in the
loop are never executed. */
for (p = next_insn_in_loop (scan_start, scan_start, end, loop_top);
p != NULL_RTX && !maybe_never;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
if (GET_CODE (p) == CODE_LABEL)
maybe_never = 1;
else if (GET_CODE (p) == JUMP_INSN
/* If we enter the loop in the middle, and scan
around to the beginning, don't set maybe_never
for that. This must be an unconditional jump,
otherwise the code at the top of the loop might
never be executed. Unconditional jumps are
followed a by barrier then loop end. */
&& ! (GET_CODE (p) == JUMP_INSN
&& JUMP_LABEL (p) == loop_top
&& NEXT_INSN (NEXT_INSN (p)) == end
&& simplejump_p (p)))
{
if (!condjump_p (p))
/* Something complicated. */
maybe_never = 1;
else
/* If there are any more instructions in the loop, they
might not be reached. */
next_maybe_never = 1;
}
else if (next_maybe_never)
maybe_never = 1;
}
/* Actually move the MEMs. */
for (i = 0; i < loop_mems_idx; ++i)
{
int written = 0;
rtx reg;
rtx mem = loop_mems[i].mem;
rtx mem_list_entry;
if (MEM_VOLATILE_P (mem)
|| invariant_p (XEXP (mem, 0)) != 1)
/* There's no telling whether or not MEM is modified. */
loop_mems[i].optimize = 0;
/* Go through the MEMs written to in the loop to see if this
one is aliased by one of them. */
mem_list_entry = loop_store_mems;
while (mem_list_entry)
{
if (rtx_equal_p (mem, XEXP (mem_list_entry, 0)))
written = 1;
else if (true_dependence (XEXP (mem_list_entry, 0), VOIDmode,
mem, rtx_varies_p))
{
/* MEM is indeed aliased by this store. */
loop_mems[i].optimize = 0;
break;
}
mem_list_entry = XEXP (mem_list_entry, 1);
}
/* If this MEM is written to, we must be sure that there
are no reads from another MEM that aliases this one. */
if (loop_mems[i].optimize && written)
{
int j;
for (j = 0; j < loop_mems_idx; ++j)
{
if (j == i)
continue;
else if (true_dependence (mem,
VOIDmode,
loop_mems[j].mem,
rtx_varies_p))
{
/* It's not safe to hoist loop_mems[i] out of
the loop because writes to it might not be
seen by reads from loop_mems[j]. */
loop_mems[i].optimize = 0;
break;
}
}
}
if (maybe_never && may_trap_p (mem))
/* We can't access the MEM outside the loop; it might
cause a trap that wouldn't have happened otherwise. */
loop_mems[i].optimize = 0;
if (!loop_mems[i].optimize)
/* We thought we were going to lift this MEM out of the
loop, but later discovered that we could not. */
continue;
/* Allocate a pseudo for this MEM. We set REG_USERVAR_P in
order to keep scan_loop from moving stores to this MEM
out of the loop just because this REG is neither a
user-variable nor used in the loop test. */
reg = gen_reg_rtx (GET_MODE (mem));
REG_USERVAR_P (reg) = 1;
loop_mems[i].reg = reg;
/* Now, replace all references to the MEM with the
corresponding pesudos. */
for (p = next_insn_in_loop (scan_start, scan_start, end, loop_top);
p != NULL_RTX;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
rtx_and_int ri;
ri.r = p;
ri.i = i;
for_each_rtx (&p, replace_loop_mem, &ri);
}
if (!apply_change_group ())
/* We couldn't replace all occurrences of the MEM. */
loop_mems[i].optimize = 0;
else
{
rtx set;
/* Load the memory immediately before START, which is
the NOTE_LOOP_BEG. */
set = gen_move_insn (reg, mem);
emit_insn_before (set, start);
if (written)
{
if (label == NULL_RTX)
{
/* We must compute the former
right-after-the-end label before we insert
the new one. */
end_label = next_label (end);
label = gen_label_rtx ();
emit_label_after (label, end);
}
/* Store the memory immediately after END, which is
the NOTE_LOOP_END. */
set = gen_move_insn (copy_rtx (mem), reg);
emit_insn_after (set, label);
}
if (loop_dump_stream)
{
fprintf (loop_dump_stream, "Hoisted regno %d %s from ",
REGNO (reg), (written ? "r/w" : "r/o"));
print_rtl (loop_dump_stream, mem);
fputc ('\n', loop_dump_stream);
}
}
}
}
if (label != NULL_RTX)
{
/* Now, we need to replace all references to the previous exit
label with the new one. */
rtx_pair rr;
rr.r1 = end_label;
rr.r2 = label;
for (p = start; p != end; p = NEXT_INSN (p))
{
for_each_rtx (&p, replace_label, &rr);
/* If this is a JUMP_INSN, then we also need to fix the JUMP_LABEL
field. This is not handled by for_each_rtx because it doesn't
handle unprinted ('0') fields. We need to update JUMP_LABEL
because the immediately following unroll pass will use it.
replace_label would not work anyways, because that only handles
LABEL_REFs. */
if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == end_label)
JUMP_LABEL (p) = label;
}
}
}
/* Replace MEM with its associated pseudo register. This function is
called from load_mems via for_each_rtx. DATA is actually an
rtx_and_int * describing the instruction currently being scanned
and the MEM we are currently replacing. */
static int
replace_loop_mem (mem, data)
rtx *mem;
void *data;
{
rtx_and_int *ri;
rtx insn;
int i;
rtx m = *mem;
if (m == NULL_RTX)
return 0;
switch (GET_CODE (m))
{
case MEM:
break;
case CONST_DOUBLE:
/* We're not interested in the MEM associated with a
CONST_DOUBLE, so there's no need to traverse into one. */
return -1;
default:
/* This is not a MEM. */
return 0;
}
ri = (rtx_and_int*) data;
i = ri->i;
if (!rtx_equal_p (loop_mems[i].mem, m))
/* This is not the MEM we are currently replacing. */
return 0;
insn = ri->r;
/* Actually replace the MEM. */
validate_change (insn, mem, loop_mems[i].reg, 1);
return 0;
}
/* Replace occurrences of the old exit label for the loop with the new
one. DATA is an rtx_pair containing the old and new labels,
respectively. */
static int
replace_label (x, data)
rtx *x;
void *data;
{
rtx l = *x;
rtx old_label = ((rtx_pair*) data)->r1;
rtx new_label = ((rtx_pair*) data)->r2;
if (l == NULL_RTX)
return 0;
if (GET_CODE (l) != LABEL_REF)
return 0;
if (XEXP (l, 0) != old_label)
return 0;
XEXP (l, 0) = new_label;
++LABEL_NUSES (new_label);
--LABEL_NUSES (old_label);
return 0;
}
Index: head/contrib/gcc/reg-stack.c
===================================================================
--- head/contrib/gcc/reg-stack.c (revision 52750)
+++ head/contrib/gcc/reg-stack.c (revision 52751)
@@ -1,3024 +1,3032 @@
/* Register to Stack convert for GNU compiler.
Copyright (C) 1992, 93-98, 1999 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 pass converts stack-like registers from the "flat register
file" model that gcc uses, to a stack convention that the 387 uses.
* The form of the input:
On input, the function consists of insn that have had their
registers fully allocated to a set of "virtual" registers. Note that
the word "virtual" is used differently here than elsewhere in gcc: for
each virtual stack reg, there is a hard reg, but the mapping between
them is not known until this pass is run. On output, hard register
numbers have been substituted, and various pop and exchange insns have
been emitted. The hard register numbers and the virtual register
numbers completely overlap - before this pass, all stack register
numbers are virtual, and afterward they are all hard.
The virtual registers can be manipulated normally by gcc, and their
semantics are the same as for normal registers. After the hard
register numbers are substituted, the semantics of an insn containing
stack-like regs are not the same as for an insn with normal regs: for
instance, it is not safe to delete an insn that appears to be a no-op
move. In general, no insn containing hard regs should be changed
after this pass is done.
* The form of the output:
After this pass, hard register numbers represent the distance from
the current top of stack to the desired register. A reference to
FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1,
represents the register just below that, and so forth. Also, REG_DEAD
notes indicate whether or not a stack register should be popped.
A "swap" insn looks like a parallel of two patterns, where each
pattern is a SET: one sets A to B, the other B to A.
A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG
and whose SET_DEST is REG or MEM. Any other SET_DEST, such as PLUS,
will replace the existing stack top, not push a new value.
A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose
SET_SRC is REG or MEM.
The case where the SET_SRC and SET_DEST are both FIRST_STACK_REG
appears ambiguous. As a special case, the presence of a REG_DEAD note
for FIRST_STACK_REG differentiates between a load insn and a pop.
If a REG_DEAD is present, the insn represents a "pop" that discards
the top of the register stack. If there is no REG_DEAD note, then the
insn represents a "dup" or a push of the current top of stack onto the
stack.
* Methodology:
Existing REG_DEAD and REG_UNUSED notes for stack registers are
deleted and recreated from scratch. REG_DEAD is never created for a
SET_DEST, only REG_UNUSED.
* asm_operands:
There are several rules on the usage of stack-like regs in
asm_operands insns. These rules apply only to the operands that are
stack-like regs:
1. Given a set of input regs that die in an asm_operands, it is
necessary to know which are implicitly popped by the asm, and
which must be explicitly popped by gcc.
An input reg that is implicitly popped by the asm must be
explicitly clobbered, unless it is constrained to match an
output operand.
2. For any input reg that is implicitly popped by an asm, it is
necessary to know how to adjust the stack to compensate for the pop.
If any non-popped input is closer to the top of the reg-stack than
the implicitly popped reg, it would not be possible to know what the
stack looked like - it's not clear how the rest of the stack "slides
up".
All implicitly popped input regs must be closer to the top of
the reg-stack than any input that is not implicitly popped.
3. It is possible that if an input dies in an insn, reload might
use the input reg for an output reload. Consider this example:
asm ("foo" : "=t" (a) : "f" (b));
This asm says that input B is not popped by the asm, and that
the asm pushes a result onto the reg-stack, ie, the stack is one
deeper after the asm than it was before. But, it is possible that
reload will think that it can use the same reg for both the input and
the output, if input B dies in this insn.
If any input operand uses the "f" constraint, all output reg
constraints must use the "&" earlyclobber.
The asm above would be written as
asm ("foo" : "=&t" (a) : "f" (b));
4. Some operands need to be in particular places on the stack. All
output operands fall in this category - there is no other way to
know which regs the outputs appear in unless the user indicates
this in the constraints.
Output operands must specifically indicate which reg an output
appears in after an asm. "=f" is not allowed: the operand
constraints must select a class with a single reg.
5. Output operands may not be "inserted" between existing stack regs.
Since no 387 opcode uses a read/write operand, all output operands
are dead before the asm_operands, and are pushed by the asm_operands.
It makes no sense to push anywhere but the top of the reg-stack.
Output operands must start at the top of the reg-stack: output
operands may not "skip" a reg.
6. Some asm statements may need extra stack space for internal
calculations. This can be guaranteed by clobbering stack registers
unrelated to the inputs and outputs.
Here are a couple of reasonable asms to want to write. This asm
takes one input, which is internally popped, and produces two outputs.
asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp));
This asm takes two inputs, which are popped by the fyl2xp1 opcode,
and replaces them with one output. The user must code the "st(1)"
clobber for reg-stack.c to know that fyl2xp1 pops both inputs.
asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)");
*/
#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "insn-config.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "insn-flags.h"
#include "recog.h"
#include "toplev.h"
#include "varray.h"
#ifdef STACK_REGS
#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
/* This is the basic stack record. TOP is an index into REG[] such
that REG[TOP] is the top of stack. If TOP is -1 the stack is empty.
If TOP is -2, REG[] is not yet initialized. Stack initialization
consists of placing each live reg in array `reg' and setting `top'
appropriately.
REG_SET indicates which registers are live. */
typedef struct stack_def
{
int top; /* index to top stack element */
HARD_REG_SET reg_set; /* set of live registers */
char reg[REG_STACK_SIZE]; /* register - stack mapping */
} *stack;
/* highest instruction uid */
static int max_uid = 0;
/* Number of basic blocks in the current function. */
static int blocks;
/* Element N is first insn in basic block N.
This info lasts until we finish compiling the function. */
static rtx *block_begin;
/* Element N is last insn in basic block N.
This info lasts until we finish compiling the function. */
static rtx *block_end;
/* Element N is nonzero if control can drop into basic block N */
static char *block_drops_in;
/* Element N says all about the stack at entry block N */
static stack block_stack_in;
/* Element N says all about the stack life at the end of block N */
static HARD_REG_SET *block_out_reg_set;
/* This is where the BLOCK_NUM values are really stored. This is set
up by find_blocks and used there and in life_analysis. It can be used
later, but only to look up an insn that is the head or tail of some
block. life_analysis and the stack register conversion process can
add insns within a block. */
static int *block_number;
/* We use this array to cache info about insns, because otherwise we
spend too much time in stack_regs_mentioned_p.
Indexed by insn UIDs. A value of zero is uninitialized, one indicates
the insn uses stack registers, two indicates the insn does not use
stack registers. */
static varray_type stack_regs_mentioned_data;
/* This is the register file for all register after conversion */
static rtx
FP_mode_reg[LAST_STACK_REG+1-FIRST_STACK_REG][(int) MAX_MACHINE_MODE];
#define FP_MODE_REG(regno,mode) \
(FP_mode_reg[(regno)-FIRST_STACK_REG][(int)(mode)])
/* Get the basic block number of an insn. See note at block_number
definition are validity of this information. */
static int BLOCK_NUM PROTO((rtx));
#ifdef __GNUC__
__inline__
#endif
static int
BLOCK_NUM(insn)
rtx insn;
{
int tmp = INSN_UID (insn);
if (tmp > max_uid)
abort ();
tmp = block_number[tmp];
if (tmp < 0)
abort ();
return tmp;
}
extern rtx forced_labels;
/* Forward declarations */
static void mark_regs_pat PROTO((rtx, HARD_REG_SET *));
static void straighten_stack PROTO((rtx, stack));
static void pop_stack PROTO((stack, int));
static void record_label_references PROTO((rtx, rtx));
static rtx *get_true_reg PROTO((rtx *));
static void record_asm_reg_life PROTO((rtx, stack));
static void record_reg_life_pat PROTO((rtx, HARD_REG_SET *,
HARD_REG_SET *, int));
static int get_asm_operand_n_inputs PROTO((rtx));
static void record_reg_life PROTO((rtx, int, stack));
static void find_blocks PROTO((rtx));
static rtx stack_result PROTO((tree));
static void stack_reg_life_analysis PROTO((rtx, HARD_REG_SET *));
static void replace_reg PROTO((rtx *, int));
static void remove_regno_note PROTO((rtx, enum reg_note, int));
static int get_hard_regnum PROTO((stack, rtx));
static void delete_insn_for_stacker PROTO((rtx));
static rtx emit_pop_insn PROTO((rtx, stack, rtx, rtx (*) ()));
static void emit_swap_insn PROTO((rtx, stack, rtx));
static void move_for_stack_reg PROTO((rtx, stack, rtx));
static void swap_rtx_condition PROTO((rtx));
static void compare_for_stack_reg PROTO((rtx, stack, rtx));
static void subst_stack_regs_pat PROTO((rtx, stack, rtx));
static void subst_asm_stack_regs PROTO((rtx, stack));
static void subst_stack_regs PROTO((rtx, stack));
static void change_stack PROTO((rtx, stack, stack, rtx (*) ()));
static void goto_block_pat PROTO((rtx, stack, rtx));
static void convert_regs PROTO((void));
static void print_blocks PROTO((FILE *, rtx, rtx));
static void dump_stack_info PROTO((FILE *));
static int check_stack_regs_mentioned PROTO((rtx insn));
/* Initialize stack_regs_mentioned_data for INSN (growing the virtual array
if needed. Return nonzero if INSN mentions stacked registers. */
static int
check_stack_regs_mentioned (insn)
rtx insn;
{
unsigned int uid = INSN_UID (insn);
if (uid >= VARRAY_SIZE (stack_regs_mentioned_data))
/* Allocate some extra size to avoid too many reallocs, but
do not grow exponentially. */
VARRAY_GROW (stack_regs_mentioned_data, uid + uid / 20);
if (stack_regs_mentioned_p (PATTERN (insn)))
{
VARRAY_CHAR (stack_regs_mentioned_data, uid) = 1;
return 1;
}
else
VARRAY_CHAR (stack_regs_mentioned_data, uid) = 2;
return 0;
}
/* Return nonzero if INSN mentions stacked registers, else return
zero. */
int
stack_regs_mentioned (insn)
rtx insn;
{
unsigned int uid;
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
return 0;
uid = INSN_UID (insn);
if (uid >= VARRAY_SIZE (stack_regs_mentioned_data)
|| ! VARRAY_CHAR (stack_regs_mentioned_data, uid))
return (check_stack_regs_mentioned (insn));
return VARRAY_CHAR (stack_regs_mentioned_data, uid) == 1;
}
/* Mark all registers needed for this pattern. */
static void
mark_regs_pat (pat, set)
rtx pat;
HARD_REG_SET *set;
{
enum machine_mode mode;
register int regno;
register int count;
if (GET_CODE (pat) == SUBREG)
{
mode = GET_MODE (pat);
regno = SUBREG_WORD (pat);
regno += REGNO (SUBREG_REG (pat));
}
else
regno = REGNO (pat), mode = GET_MODE (pat);
for (count = HARD_REGNO_NREGS (regno, mode);
count; count--, regno++)
SET_HARD_REG_BIT (*set, regno);
}
/* Reorganise the stack into ascending numbers,
after this insn. */
static void
straighten_stack (insn, regstack)
rtx insn;
stack regstack;
{
struct stack_def temp_stack;
int top;
/* If there is only a single register on the stack, then the stack is
already in increasing order and no reorganization is needed.
Similarly if the stack is empty. */
if (regstack->top <= 0)
return;
temp_stack.reg_set = regstack->reg_set;
for (top = temp_stack.top = regstack->top; top >= 0; top--)
temp_stack.reg[top] = FIRST_STACK_REG + temp_stack.top - top;
change_stack (insn, regstack, &temp_stack, emit_insn_after);
}
/* Pop a register from the stack */
static void
pop_stack (regstack, regno)
stack regstack;
int regno;
{
int top = regstack->top;
CLEAR_HARD_REG_BIT (regstack->reg_set, regno);
regstack->top--;
/* If regno was not at the top of stack then adjust stack */
if (regstack->reg [top] != regno)
{
int i;
for (i = regstack->top; i >= 0; i--)
if (regstack->reg [i] == regno)
{
int j;
for (j = i; j < top; j++)
regstack->reg [j] = regstack->reg [j + 1];
break;
}
}
}
/* Return non-zero if any stack register is mentioned somewhere within PAT. */
int
stack_regs_mentioned_p (pat)
rtx pat;
{
register char *fmt;
register int i;
if (STACK_REG_P (pat))
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
if (stack_regs_mentioned_p (XVECEXP (pat, i, j)))
return 1;
}
else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i)))
return 1;
}
return 0;
}
/* Convert register usage from "flat" register file usage to a "stack
register file. FIRST is the first insn in the function, FILE is the
dump file, if used.
First compute the beginning and end of each basic block. Do a
register life analysis on the stack registers, recording the result
for the head and tail of each basic block. The convert each insn one
by one. Run a last jump_optimize() pass, if optimizing, to eliminate
any cross-jumping created when the converter inserts pop insns.*/
void
reg_to_stack (first, file)
rtx first;
FILE *file;
{
register rtx insn;
register int i;
int stack_reg_seen = 0;
enum machine_mode mode;
HARD_REG_SET stackentry;
max_uid = get_max_uid ();
VARRAY_CHAR_INIT (stack_regs_mentioned_data, max_uid + 1,
"stack_regs_mentioned cache");
CLEAR_HARD_REG_SET (stackentry);
{
static int initialised;
if (!initialised)
{
#if 0
initialised = 1; /* This array can not have been previously
initialised, because the rtx's are
thrown away between compilations of
functions. */
#endif
for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
{
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
for (mode = GET_CLASS_NARROWEST_MODE (MODE_COMPLEX_FLOAT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
FP_MODE_REG (i, mode) = gen_rtx_REG (mode, i);
}
}
}
/* Count the basic blocks. Also find maximum insn uid. */
{
register RTX_CODE prev_code = BARRIER;
register RTX_CODE code;
register int before_function_beg = 1;
max_uid = 0;
blocks = 0;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
/* Note that this loop must select the same block boundaries
as code in find_blocks. Also note that this code is not the
same as that used in flow.c. */
if (INSN_UID (insn) > max_uid)
max_uid = INSN_UID (insn);
code = GET_CODE (insn);
if (code == CODE_LABEL
|| (prev_code != INSN
&& prev_code != CALL_INSN
&& prev_code != CODE_LABEL
&& GET_RTX_CLASS (code) == 'i'))
blocks++;
if (code == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
before_function_beg = 0;
/* Remember whether or not this insn mentions an FP regs.
Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */
if (GET_RTX_CLASS (code) == 'i'
&& stack_regs_mentioned_p (PATTERN (insn)))
{
stack_reg_seen = 1;
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 1;
/* Note any register passing parameters. */
if (before_function_beg && code == INSN
&& GET_CODE (PATTERN (insn)) == USE)
record_reg_life_pat (PATTERN (insn), (HARD_REG_SET *) 0,
&stackentry, 1);
}
else
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 2;
if (code == CODE_LABEL)
LABEL_REFS (insn) = insn; /* delete old chain */
if (code != NOTE)
prev_code = code;
}
}
/* If no stack register reference exists in this insn, there isn't
anything to convert. */
if (! stack_reg_seen)
{
VARRAY_FREE (stack_regs_mentioned_data);
return;
}
/* If there are stack registers, there must be at least one block. */
if (! blocks)
abort ();
/* Allocate some tables that last till end of compiling this function
and some needed only in find_blocks and life_analysis. */
block_begin = (rtx *) alloca (blocks * sizeof (rtx));
block_end = (rtx *) alloca (blocks * sizeof (rtx));
block_drops_in = (char *) alloca (blocks);
block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def));
block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET));
bzero ((char *) block_stack_in, blocks * sizeof (struct stack_def));
bzero ((char *) block_out_reg_set, blocks * sizeof (HARD_REG_SET));
block_number = (int *) alloca ((max_uid + 1) * sizeof (int));
memset (block_number, -1, (max_uid + 1) * sizeof (int));
find_blocks (first);
stack_reg_life_analysis (first, &stackentry);
/* Dump the life analysis debug information before jump
optimization, as that will destroy the LABEL_REFS we keep the
information in. */
if (file)
dump_stack_info (file);
convert_regs ();
if (optimize)
jump_optimize (first, 2, 0, 0);
VARRAY_FREE (stack_regs_mentioned_data);
}
/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the
label's chain of references, and note which insn contains each
reference. */
static void
record_label_references (insn, pat)
rtx insn, pat;
{
register enum rtx_code code = GET_CODE (pat);
register int i;
register char *fmt;
if (code == LABEL_REF)
{
register rtx label = XEXP (pat, 0);
register rtx ref;
if (GET_CODE (label) != CODE_LABEL)
abort ();
/* If this is an undefined label, LABEL_REFS (label) contains
garbage. */
if (INSN_UID (label) == 0)
return;
/* Don't make a duplicate in the code_label's chain. */
for (ref = LABEL_REFS (label);
ref && ref != label;
ref = LABEL_NEXTREF (ref))
if (CONTAINING_INSN (ref) == insn)
return;
CONTAINING_INSN (pat) = insn;
LABEL_NEXTREF (pat) = LABEL_REFS (label);
LABEL_REFS (label) = pat;
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
record_label_references (insn, XEXP (pat, i));
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (pat, i); j++)
record_label_references (insn, XVECEXP (pat, i, j));
}
}
}
/* Return a pointer to the REG expression within PAT. If PAT is not a
REG, possible enclosed by a conversion rtx, return the inner part of
PAT that stopped the search. */
static rtx *
get_true_reg (pat)
rtx *pat;
{
for (;;)
switch (GET_CODE (*pat))
{
case SUBREG:
/* eliminate FP subregister accesses in favour of the
actual FP register in use. */
{
rtx subreg;
if (FP_REG_P (subreg = SUBREG_REG (*pat)))
{
*pat = FP_MODE_REG (REGNO (subreg) + SUBREG_WORD (*pat),
GET_MODE (subreg));
default:
return pat;
}
}
case FLOAT:
case FIX:
case FLOAT_EXTEND:
pat = & XEXP (*pat, 0);
}
}
/* Record the life info of each stack reg in INSN, updating REGSTACK.
N_INPUTS is the number of inputs; N_OUTPUTS the outputs.
OPERANDS is an array of all operands for the insn, and is assumed to
contain all output operands, then all inputs operands.
There are many rules that an asm statement for stack-like regs must
follow. Those rules are explained at the top of this file: the rule
numbers below refer to that explanation. */
static void
record_asm_reg_life (insn, regstack)
rtx insn;
stack regstack;
{
int i;
int n_clobbers;
int malformed_asm = 0;
rtx body = PATTERN (insn);
int reg_used_as_output[FIRST_PSEUDO_REGISTER];
int implicitly_dies[FIRST_PSEUDO_REGISTER];
int alt;
rtx *clobber_reg;
int n_inputs, n_outputs;
/* Find out what the constraints require. If no constraint
alternative matches, this asm is malformed. */
extract_insn (insn);
constrain_operands (1);
alt = which_alternative;
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_n_operands - n_inputs;
if (alt < 0)
{
malformed_asm = 1;
/* Avoid further trouble with this insn. */
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 2;
return;
}
/* Strip SUBREGs here to make the following code simpler. */
for (i = 0; i < recog_n_operands; i++)
if (GET_CODE (recog_operand[i]) == SUBREG
&& GET_CODE (SUBREG_REG (recog_operand[i])) == REG)
recog_operand[i] = SUBREG_REG (recog_operand[i]);
/* Set up CLOBBER_REG. */
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx));
for (i = 0; i < XVECLEN (body, 0); i++)
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
{
rtx clobber = XVECEXP (body, 0, i);
rtx reg = XEXP (clobber, 0);
if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
reg = SUBREG_REG (reg);
if (STACK_REG_P (reg))
{
clobber_reg[n_clobbers] = reg;
n_clobbers++;
}
}
}
/* Enforce rule #4: Output operands must specifically indicate which
reg an output appears in after an asm. "=f" is not allowed: the
operand constraints must select a class with a single reg.
Also enforce rule #5: Output operands must start at the top of
the reg-stack: output operands may not "skip" a reg. */
bzero ((char *) reg_used_as_output, sizeof (reg_used_as_output));
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
if (reg_class_size[(int) recog_op_alt[i][alt].class] != 1)
{
error_for_asm (insn, "Output constraint %d must specify a single register", i);
malformed_asm = 1;
}
else
reg_used_as_output[REGNO (recog_operand[i])] = 1;
}
/* Search for first non-popped reg. */
for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
if (! reg_used_as_output[i])
break;
/* If there are any other popped regs, that's an error. */
for (; i < LAST_STACK_REG + 1; i++)
if (reg_used_as_output[i])
break;
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn, "Output regs must be grouped at top of stack");
malformed_asm = 1;
}
/* Enforce rule #2: All implicitly popped input regs must be closer
to the top of the reg-stack than any input that is not implicitly
popped. */
bzero ((char *) implicitly_dies, sizeof (implicitly_dies));
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
/* An input reg is implicitly popped if it is tied to an
output, or if there is a CLOBBER for it. */
int j;
for (j = 0; j < n_clobbers; j++)
if (operands_match_p (clobber_reg[j], recog_operand[i]))
break;
if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
implicitly_dies[REGNO (recog_operand[i])] = 1;
}
/* Search for first non-popped reg. */
for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
if (! implicitly_dies[i])
break;
/* If there are any other popped regs, that's an error. */
for (; i < LAST_STACK_REG + 1; i++)
if (implicitly_dies[i])
break;
if (i != LAST_STACK_REG + 1)
{
error_for_asm (insn,
"Implicitly popped regs must be grouped at top of stack");
malformed_asm = 1;
}
/* Enfore rule #3: If any input operand uses the "f" constraint, all
output constraints must use the "&" earlyclobber.
??? Detect this more deterministically by having constraint_asm_operands
record any earlyclobber. */
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (recog_op_alt[i][alt].matches == -1)
{
int j;
for (j = 0; j < n_outputs; j++)
if (operands_match_p (recog_operand[j], recog_operand[i]))
{
error_for_asm (insn,
"Output operand %d must use `&' constraint", j);
malformed_asm = 1;
}
}
if (malformed_asm)
{
/* Avoid further trouble with this insn. */
PATTERN (insn) = gen_rtx_USE (VOIDmode, const0_rtx);
VARRAY_CHAR (stack_regs_mentioned_data, INSN_UID (insn)) = 2;
return;
}
/* Process all outputs */
for (i = 0; i < n_outputs; i++)
{
rtx op = recog_operand[i];
if (! STACK_REG_P (op))
{
if (stack_regs_mentioned_p (op))
abort ();
else
continue;
}
/* Each destination is dead before this insn. If the
destination is not used after this insn, record this with
REG_UNUSED. */
if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op)))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_UNUSED, op,
REG_NOTES (insn));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op));
}
/* Process all inputs */
for (i = n_outputs; i < n_outputs + n_inputs; i++)
{
rtx op = recog_operand[i];
if (! STACK_REG_P (op))
{
if (stack_regs_mentioned_p (op))
abort ();
else
continue;
}
/* If an input is dead after the insn, record a death note.
But don't record a death note if there is already a death note,
or if the input is also an output. */
if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op))
&& recog_op_alt[i][alt].matches == -1
&& find_regno_note (insn, REG_DEAD, REGNO (op)) == NULL_RTX)
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, op, REG_NOTES (insn));
SET_HARD_REG_BIT (regstack->reg_set, REGNO (op));
}
}
/* Scan PAT, which is part of INSN, and record registers appearing in
a SET_DEST in DEST, and other registers in SRC.
This function does not know about SET_DESTs that are both input and
output (such as ZERO_EXTRACT) - this cannot happen on a 387. */
static void
record_reg_life_pat (pat, src, dest, douse)
rtx pat;
HARD_REG_SET *src, *dest;
int douse;
{
register char *fmt;
register int i;
if (STACK_REG_P (pat)
|| (GET_CODE (pat) == SUBREG && STACK_REG_P (SUBREG_REG (pat))))
{
if (src)
mark_regs_pat (pat, src);
if (dest)
mark_regs_pat (pat, dest);
return;
}
if (GET_CODE (pat) == SET)
{
record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest, 0);
record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR, 0);
return;
}
/* We don't need to consider either of these cases. */
if ((GET_CODE (pat) == USE && !douse) || GET_CODE (pat) == CLOBBER)
return;
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
record_reg_life_pat (XVECEXP (pat, i, j), src, dest, 0);
}
else if (fmt[i] == 'e')
record_reg_life_pat (XEXP (pat, i), src, dest, 0);
}
}
/* Calculate the number of inputs and outputs in BODY, an
asm_operands. N_OPERANDS is the total number of operands, and
N_INPUTS and N_OUTPUTS are pointers to ints into which the results are
placed. */
static int
get_asm_operand_n_inputs (body)
rtx body;
{
if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
else if (GET_CODE (body) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (body);
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == SET)
return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)));
else if (GET_CODE (body) == PARALLEL
&& GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
abort ();
}
/* Scan INSN, which is in BLOCK, and record the life & death of stack
registers in REGSTACK. This function is called to process insns from
the last insn in a block to the first. The actual scanning is done in
record_reg_life_pat.
If a register is live after a CALL_INSN, but is not a value return
register for that CALL_INSN, then code is emitted to initialize that
register. The block_end[] data is kept accurate.
Existing death and unset notes for stack registers are deleted
before processing the insn. */
static void
record_reg_life (insn, block, regstack)
rtx insn;
int block;
stack regstack;
{
rtx note, *note_link;
int n_operands;
if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
|| INSN_DELETED_P (insn))
return;
/* Strip death notes for stack regs from this insn */
note_link = &REG_NOTES(insn);
for (note = *note_link; note; note = XEXP (note, 1))
if (STACK_REG_P (XEXP (note, 0))
&& (REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED))
*note_link = XEXP (note, 1);
else
note_link = &XEXP (note, 1);
/* Process all patterns in the insn. */
n_operands = asm_noperands (PATTERN (insn));
if (n_operands >= 0)
{
record_asm_reg_life (insn, regstack);
return;
}
{
HARD_REG_SET src, dest;
int regno;
CLEAR_HARD_REG_SET (src);
CLEAR_HARD_REG_SET (dest);
if (GET_CODE (insn) == CALL_INSN)
for (note = CALL_INSN_FUNCTION_USAGE (insn);
note;
note = XEXP (note, 1))
if (GET_CODE (XEXP (note, 0)) == USE)
record_reg_life_pat (SET_DEST (XEXP (note, 0)), &src, NULL_PTR, 0);
record_reg_life_pat (PATTERN (insn), &src, &dest, 0);
for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
if (! TEST_HARD_REG_BIT (regstack->reg_set, regno))
{
if (TEST_HARD_REG_BIT (src, regno)
&& ! TEST_HARD_REG_BIT (dest, regno))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD,
FP_MODE_REG (regno, DFmode),
REG_NOTES (insn));
else if (TEST_HARD_REG_BIT (dest, regno))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_UNUSED,
FP_MODE_REG (regno, DFmode),
REG_NOTES (insn));
}
if (GET_CODE (insn) == CALL_INSN)
{
int reg;
/* There might be a reg that is live after a function call.
Initialize it to zero so that the program does not crash. See
comment towards the end of stack_reg_life_analysis(). */
for (reg = FIRST_STACK_REG; reg <= LAST_STACK_REG; reg++)
if (! TEST_HARD_REG_BIT (dest, reg)
&& TEST_HARD_REG_BIT (regstack->reg_set, reg))
{
rtx init, pat;
/* The insn will use virtual register numbers, and so
convert_regs is expected to process these. But BLOCK_NUM
cannot be used on these insns, because they do not appear in
block_number[]. */
pat = gen_rtx_SET (VOIDmode, FP_MODE_REG (reg, DFmode),
CONST0_RTX (DFmode));
init = emit_insn_after (pat, insn);
CLEAR_HARD_REG_BIT (regstack->reg_set, reg);
/* If the CALL_INSN was the end of a block, move the
block_end to point to the new insn. */
if (block_end[block] == insn)
block_end[block] = init;
}
/* Some regs do not survive a CALL */
AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set);
}
AND_COMPL_HARD_REG_SET (regstack->reg_set, dest);
IOR_HARD_REG_SET (regstack->reg_set, src);
}
}
/* Find all basic blocks of the function, which starts with FIRST.
For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */
static void
find_blocks (first)
rtx first;
{
register rtx insn;
register int block;
register RTX_CODE prev_code = BARRIER;
register RTX_CODE code;
rtx label_value_list = 0;
/* Record where all the blocks start and end.
Record which basic blocks control can drop in to. */
block = -1;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
/* Note that this loop must select the same block boundaries
as code in reg_to_stack, but that these are not the same
as those selected in flow.c. */
code = GET_CODE (insn);
if (code == CODE_LABEL
|| (prev_code != INSN
&& prev_code != CALL_INSN
&& prev_code != CODE_LABEL
&& GET_RTX_CLASS (code) == 'i'))
{
block_begin[++block] = insn;
block_end[block] = insn;
block_drops_in[block] = prev_code != BARRIER;
}
else if (GET_RTX_CLASS (code) == 'i')
block_end[block] = insn;
if (GET_RTX_CLASS (code) == 'i')
{
rtx note;
/* Make a list of all labels referred to other than by jumps. */
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_LABEL)
label_value_list = gen_rtx_EXPR_LIST (VOIDmode, XEXP (note, 0),
label_value_list);
}
block_number[INSN_UID (insn)] = block;
if (code != NOTE)
prev_code = code;
}
if (block + 1 != blocks)
abort ();
/* generate all label references to the corresponding jump insn */
for (block = 0; block < blocks; block++)
{
insn = block_end[block];
if (GET_CODE (insn) == JUMP_INSN)
{
rtx pat = PATTERN (insn);
rtx x;
if (computed_jump_p (insn))
{
for (x = label_value_list; x; x = XEXP (x, 1))
record_label_references (insn,
gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)));
for (x = forced_labels; x; x = XEXP (x, 1))
record_label_references (insn,
gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)));
}
record_label_references (insn, pat);
}
}
}
/* If current function returns its result in an fp stack register,
return the REG. Otherwise, return 0. */
static rtx
stack_result (decl)
tree decl;
{
- rtx result = DECL_RTL (DECL_RESULT (decl));
+ rtx result;
+ /* If the value is supposed to be returned in memory, then clearly
+ it is not returned in a stack register. */
+ if (aggregate_value_p (DECL_RESULT (decl)))
+ return 0;
+
+ result = DECL_RTL (DECL_RESULT (decl));
+ /* ?!? What is this code supposed to do? Can this code actually
+ trigger if we kick out aggregates above? */
if (result != 0
&& ! (GET_CODE (result) == REG
&& REGNO (result) < FIRST_PSEUDO_REGISTER))
{
#ifdef FUNCTION_OUTGOING_VALUE
result
= FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#else
result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
#endif
}
return result != 0 && STACK_REG_P (result) ? result : 0;
}
/* Determine the which registers are live at the start of each basic
block of the function whose first insn is FIRST.
First, if the function returns a real_type, mark the function
return type as live at each return point, as the RTL may not give any
hint that the register is live.
Then, start with the last block and work back to the first block.
Similarly, work backwards within each block, insn by insn, recording
which regs are dead and which are used (and therefore live) in the
hard reg set of block_stack_in[].
After processing each basic block, if there is a label at the start
of the block, propagate the live registers to all jumps to this block.
As a special case, if there are regs live in this block, that are
not live in a block containing a jump to this label, and the block
containing the jump has already been processed, we must propagate this
block's entry register life back to the block containing the jump, and
restart life analysis from there.
In the worst case, this function may traverse the insns
REG_STACK_SIZE times. This is necessary, since a jump towards the end
of the insns may not know that a reg is live at a target that is early
in the insns. So we back up and start over with the new reg live.
If there are registers that are live at the start of the function,
insns are emitted to initialize these registers. Something similar is
done after CALL_INSNs in record_reg_life. */
static void
stack_reg_life_analysis (first, stackentry)
rtx first;
HARD_REG_SET *stackentry;
{
int reg, block;
struct stack_def regstack;
{
rtx retvalue;
if ((retvalue = stack_result (current_function_decl)))
{
/* Find all RETURN insns and mark them. */
for (block = blocks - 1; --block >= 0;)
if (GET_CODE (block_end[block]) == JUMP_INSN
&& returnjump_p (block_end[block]))
mark_regs_pat (retvalue, block_out_reg_set+block);
/* Mark off the end of last block if we "fall off" the end of the
function into the epilogue. */
if (GET_CODE (block_end[blocks-1]) != JUMP_INSN
|| returnjump_p (block_end[blocks-1]))
mark_regs_pat (retvalue, block_out_reg_set+blocks-1);
}
}
/* now scan all blocks backward for stack register use */
block = blocks - 1;
while (block >= 0)
{
register rtx insn, prev;
/* current register status at last instruction */
COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]);
prev = block_end[block];
do
{
insn = prev;
prev = PREV_INSN (insn);
/* If the insn is a CALL_INSN, we need to ensure that
everything dies. But otherwise don't process unless there
are some stack regs present. */
if (stack_regs_mentioned (insn) || GET_CODE (insn) == CALL_INSN)
record_reg_life (insn, block, &regstack);
} while (insn != block_begin[block]);
/* Set the state at the start of the block. Mark that no
register mapping information known yet. */
COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set);
block_stack_in[block].top = -2;
/* If there is a label, propagate our register life to all jumps
to this label. */
if (GET_CODE (insn) == CODE_LABEL)
{
register rtx label;
int must_restart = 0;
for (label = LABEL_REFS (insn); label != insn;
label = LABEL_NEXTREF (label))
{
int jump_block = BLOCK_NUM (CONTAINING_INSN (label));
if (jump_block < block)
IOR_HARD_REG_SET (block_out_reg_set[jump_block],
block_stack_in[block].reg_set);
else
{
/* The block containing the jump has already been
processed. If there are registers that were not known
to be live then, but are live now, we must back up
and restart life analysis from that point with the new
life information. */
GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set,
block_out_reg_set[jump_block],
win);
IOR_HARD_REG_SET (block_out_reg_set[jump_block],
block_stack_in[block].reg_set);
block = jump_block;
must_restart = 1;
break;
win:
;
}
}
if (must_restart)
continue;
}
if (block_drops_in[block])
IOR_HARD_REG_SET (block_out_reg_set[block-1],
block_stack_in[block].reg_set);
block -= 1;
}
/* If any reg is live at the start of the first block of a
function, then we must guarantee that the reg holds some value by
generating our own "load" of that register. Otherwise a 387 would
fault trying to access an empty register. */
/* Load zero into each live register. The fact that a register
appears live at the function start necessarily implies an error
in the user program: it means that (unless the offending code is *never*
executed) this program is using uninitialised floating point
variables. In order to keep broken code like this happy, we initialise
those variables with zero.
Note that we are inserting virtual register references here:
these insns must be processed by convert_regs later. Also, these
insns will not be in block_number, so BLOCK_NUM() will fail for them. */
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg)
&& ! TEST_HARD_REG_BIT (*stackentry, reg))
{
rtx init_rtx;
init_rtx = gen_rtx_SET (VOIDmode, FP_MODE_REG(reg, DFmode),
CONST0_RTX (DFmode));
block_begin[0] = emit_insn_after (init_rtx, first);
CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg);
}
}
/*****************************************************************************
This section deals with stack register substitution, and forms the second
pass over the RTL.
*****************************************************************************/
/* Replace REG, which is a pointer to a stack reg RTX, with an RTX for
the desired hard REGNO. */
static void
replace_reg (reg, regno)
rtx *reg;
int regno;
{
if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG
|| ! STACK_REG_P (*reg))
abort ();
switch (GET_MODE_CLASS (GET_MODE (*reg)))
{
default: abort ();
case MODE_FLOAT:
case MODE_COMPLEX_FLOAT:;
}
*reg = FP_MODE_REG (regno, GET_MODE (*reg));
}
/* Remove a note of type NOTE, which must be found, for register
number REGNO from INSN. Remove only one such note. */
static void
remove_regno_note (insn, note, regno)
rtx insn;
enum reg_note note;
int regno;
{
register rtx *note_link, this;
note_link = &REG_NOTES(insn);
for (this = *note_link; this; this = XEXP (this, 1))
if (REG_NOTE_KIND (this) == note
&& REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno)
{
*note_link = XEXP (this, 1);
return;
}
else
note_link = &XEXP (this, 1);
abort ();
}
/* Find the hard register number of virtual register REG in REGSTACK.
The hard register number is relative to the top of the stack. -1 is
returned if the register is not found. */
static int
get_hard_regnum (regstack, reg)
stack regstack;
rtx reg;
{
int i;
if (! STACK_REG_P (reg))
abort ();
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (reg))
break;
return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1;
}
/* Delete INSN from the RTL. Mark the insn, but don't remove it from
the chain of insns. Doing so could confuse block_begin and block_end
if this were the only insn in the block. */
static void
delete_insn_for_stacker (insn)
rtx insn;
{
int i;
/* Ensure that the side effects were clobbers when deleting a PARALLEL. */
if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) != CLOBBER)
abort ();
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
/* Emit an insn to pop virtual register REG before or after INSN.
REGSTACK is the stack state after INSN and is updated to reflect this
pop. WHEN is either emit_insn_before, emit_insn_after or NULL.
in case WHEN is NULL we don't really emit the insn, just modify stack
information. Caller is expected to emit insn himself.
A pop insn is represented as a SET whose destination is the register to
be popped and source is the top of stack. A death note for the top of stack
cases the movdf pattern to pop. */
static rtx
emit_pop_insn (insn, regstack, reg, when)
rtx insn;
stack regstack;
rtx reg;
rtx (*when)();
{
rtx pop_insn, pop_rtx;
int hard_regno;
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
abort ();
if (when)
{
pop_rtx = gen_rtx_SET (VOIDmode, FP_MODE_REG (hard_regno, DFmode),
FP_MODE_REG (FIRST_STACK_REG, DFmode));
pop_insn = (*when) (pop_rtx, insn);
REG_NOTES (pop_insn) = gen_rtx_EXPR_LIST (REG_DEAD,
FP_MODE_REG (FIRST_STACK_REG,
DFmode),
REG_NOTES (pop_insn));
}
regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
regstack->top -= 1;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg));
return pop_insn;
}
/* Emit an insn before or after INSN to swap virtual register REG with the
top of stack. WHEN should be `emit_insn_before' or `emit_insn_before'
REGSTACK is the stack state before the swap, and is updated to reflect
the swap. A swap insn is represented as a PARALLEL of two patterns:
each pattern moves one reg to the other.
If REG is already at the top of the stack, no insn is emitted. */
static void
emit_swap_insn (insn, regstack, reg)
rtx insn;
stack regstack;
rtx reg;
{
int hard_regno;
rtx gen_swapdf();
rtx swap_rtx, swap_insn;
int tmp, other_reg; /* swap regno temps */
rtx i1; /* the stack-reg insn prior to INSN */
rtx i1set = NULL_RTX; /* the SET rtx within I1 */
hard_regno = get_hard_regnum (regstack, reg);
if (hard_regno < FIRST_STACK_REG)
abort ();
if (hard_regno == FIRST_STACK_REG)
return;
other_reg = regstack->top - (hard_regno - FIRST_STACK_REG);
tmp = regstack->reg[other_reg];
regstack->reg[other_reg] = regstack->reg[regstack->top];
regstack->reg[regstack->top] = tmp;
/* Find the previous insn involving stack regs, but don't go past
any labels, calls or jumps. */
i1 = prev_nonnote_insn (insn);
while (i1 && GET_CODE (i1) == INSN && !stack_regs_mentioned (i1))
i1 = prev_nonnote_insn (i1);
if (i1)
i1set = single_set (i1);
if (i1set)
{
rtx i1src = *get_true_reg (&SET_SRC (i1set));
rtx i1dest = *get_true_reg (&SET_DEST (i1set));
/* If the previous register stack push was from the reg we are to
swap with, omit the swap. */
if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG
&& GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
/* If the previous insn wrote to the reg we are to swap with,
omit the swap. */
if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno
&& GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG
&& find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
return;
}
if (GET_RTX_CLASS (GET_CODE (i1)) == 'i' && sets_cc0_p (PATTERN (i1)))
{
i1 = next_nonnote_insn (i1);
if (i1 == insn)
abort ();
}
swap_rtx = gen_swapdf (FP_MODE_REG (hard_regno, DFmode),
FP_MODE_REG (FIRST_STACK_REG, DFmode));
swap_insn = emit_insn_after (swap_rtx, i1);
}
/* Handle a move to or from a stack register in PAT, which is in INSN.
REGSTACK is the current stack. */
static void
move_for_stack_reg (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *psrc = get_true_reg (&SET_SRC (pat));
rtx *pdest = get_true_reg (&SET_DEST (pat));
rtx src, dest;
rtx note;
src = *psrc; dest = *pdest;
if (STACK_REG_P (src) && STACK_REG_P (dest))
{
/* Write from one stack reg to another. If SRC dies here, then
just change the register mapping and delete the insn. */
note = find_regno_note (insn, REG_DEAD, REGNO (src));
if (note)
{
int i;
/* If this is a no-op move, there must not be a REG_DEAD note. */
if (REGNO (src) == REGNO (dest))
abort ();
for (i = regstack->top; i >= 0; i--)
if (regstack->reg[i] == REGNO (src))
break;
/* The source must be live, and the dest must be dead. */
if (i < 0 || get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
/* It is possible that the dest is unused after this insn.
If so, just pop the src. */
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
{
emit_pop_insn (insn, regstack, src, emit_insn_after);
delete_insn_for_stacker (insn);
return;
}
regstack->reg[i] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
delete_insn_for_stacker (insn);
return;
}
/* The source reg does not die. */
/* If this appears to be a no-op move, delete it, or else it
will confuse the machine description output patterns. But if
it is REG_UNUSED, we must pop the reg now, as per-insn processing
for REG_UNUSED will not work for deleted insns. */
if (REGNO (src) == REGNO (dest))
{
if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
emit_pop_insn (insn, regstack, dest, emit_insn_after);
delete_insn_for_stacker (insn);
return;
}
/* The destination ought to be dead */
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
replace_reg (psrc, get_hard_regnum (regstack, src));
regstack->reg[++regstack->top] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
replace_reg (pdest, FIRST_STACK_REG);
}
else if (STACK_REG_P (src))
{
/* Save from a stack reg to MEM, or possibly integer reg. Since
only top of stack may be saved, emit an exchange first if
needs be. */
emit_swap_insn (insn, regstack, src);
note = find_regno_note (insn, REG_DEAD, REGNO (src));
if (note)
{
replace_reg (&XEXP (note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (src));
}
else if (GET_MODE (src) == XFmode && regstack->top < REG_STACK_SIZE - 1)
{
/* A 387 cannot write an XFmode value to a MEM without
clobbering the source reg. The output code can handle
this by reading back the value from the MEM.
But it is more efficient to use a temp register if one is
available. Push the source value here if the register
stack is not full, and then write the value to memory via
a pop. */
rtx push_rtx, push_insn;
rtx top_stack_reg = FP_MODE_REG (FIRST_STACK_REG, XFmode);
push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
push_insn = emit_insn_before (push_rtx, insn);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD, top_stack_reg,
REG_NOTES (insn));
}
replace_reg (psrc, FIRST_STACK_REG);
}
else if (STACK_REG_P (dest))
{
/* Load from MEM, or possibly integer REG or constant, into the
stack regs. The actual target is always the top of the
stack. The stack mapping is changed to reflect that DEST is
now at top of stack. */
/* The destination ought to be dead */
if (get_hard_regnum (regstack, dest) >= FIRST_STACK_REG)
abort ();
if (regstack->top >= REG_STACK_SIZE)
abort ();
regstack->reg[++regstack->top] = REGNO (dest);
SET_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
replace_reg (pdest, FIRST_STACK_REG);
}
else
abort ();
}
static void
swap_rtx_condition (pat)
rtx pat;
{
register char *fmt;
register int i;
if (GET_RTX_CLASS (GET_CODE (pat)) == '<')
{
PUT_CODE (pat, swap_condition (GET_CODE (pat)));
return;
}
fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
swap_rtx_condition (XVECEXP (pat, i, j));
}
else if (fmt[i] == 'e')
swap_rtx_condition (XEXP (pat, i));
}
}
/* Handle a comparison. Special care needs to be taken to avoid
causing comparisons that a 387 cannot do correctly, such as EQ.
Also, a fstp instruction may need to be emitted. The 387 does have an
`fcompp' insn that can pop two regs, but it is sometimes too expensive
to do this - a `fcomp' followed by a `fstpl %st(0)' may be easier to
set up.
We can not handle this by emiting fpop instruction after compare, because
it appears between cc0 setter and user. So we emit only
REG_DEAD note and handle it as a special case in machine description.
This code used trick with delay_slot filling to emit pop insn after
comparsion but it didn't worked because it caused confusion with cc_status
in final pass. */
static void
compare_for_stack_reg (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *src1, *src2;
rtx src1_note, src2_note;
rtx cc0_user;
int have_cmove;
int hard_regno;
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
cc0_user = next_cc0_user (insn);
/* If the insn that uses cc0 is an FP-conditional move, then the destination
must be the top of stack */
if (GET_CODE (PATTERN (cc0_user)) == SET
&& SET_DEST (PATTERN (cc0_user)) != pc_rtx
&& GET_CODE (SET_SRC (PATTERN (cc0_user))) == IF_THEN_ELSE
&& (GET_MODE_CLASS (GET_MODE (SET_DEST (PATTERN (cc0_user))))
== MODE_FLOAT))
{
rtx *dest;
dest = get_true_reg (&SET_DEST (PATTERN (cc0_user)));
have_cmove = 1;
if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
&& REGNO (*dest) != regstack->reg[regstack->top])
{
emit_swap_insn (insn, regstack, *dest);
}
}
else
have_cmove = 0;
/* ??? If fxch turns out to be cheaper than fstp, give priority to
registers that die in this insn - move those to stack top first. */
if (! STACK_REG_P (*src1)
|| (STACK_REG_P (*src2)
&& get_hard_regnum (regstack, *src2) == FIRST_STACK_REG))
{
rtx temp, next;
temp = XEXP (SET_SRC (pat), 0);
XEXP (SET_SRC (pat), 0) = XEXP (SET_SRC (pat), 1);
XEXP (SET_SRC (pat), 1) = temp;
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
next = next_cc0_user (insn);
if (next == NULL_RTX)
abort ();
swap_rtx_condition (PATTERN (next));
INSN_CODE (next) = -1;
INSN_CODE (insn) = -1;
}
/* We will fix any death note later. */
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
src2_note = NULL_RTX;
if (! have_cmove)
emit_swap_insn (insn, regstack, *src1);
replace_reg (src1, FIRST_STACK_REG);
if (STACK_REG_P (*src2))
{
hard_regno = get_hard_regnum (regstack, *src2);
replace_reg (src2, hard_regno);
}
if (src1_note)
{
pop_stack (regstack, REGNO (XEXP (src1_note, 0)));
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
}
/* If the second operand dies, handle that. But if the operands are
the same stack register, don't bother, because only one death is
needed, and it was just handled. */
if (src2_note
&& ! (STACK_REG_P (*src1) && STACK_REG_P (*src2)
&& REGNO (*src1) == REGNO (*src2)))
{
/* As a special case, two regs may die in this insn if src2 is
next to top of stack and the top of stack also dies. Since
we have already popped src1, "next to top of stack" is really
at top (FIRST_STACK_REG) now. */
if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG
&& src1_note)
{
pop_stack (regstack, REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
}
else
{
/* Pop of second operand is handled using special REG_DEAD note
because we can't emit pop insn after cc0 setter. */
emit_pop_insn (insn, regstack, XEXP (src2_note, 0), NULL);
replace_reg (&XEXP (src2_note, 0), hard_regno);
}
}
}
/* Substitute new registers in PAT, which is part of INSN. REGSTACK
is the current register layout. */
static void
subst_stack_regs_pat (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx *dest, *src;
rtx *src1 = (rtx *) NULL_PTR, *src2;
rtx src1_note, src2_note;
if (GET_CODE (pat) != SET)
return;
dest = get_true_reg (&SET_DEST (pat));
src = get_true_reg (&SET_SRC (pat));
/* See if this is a `movM' pattern, and handle elsewhere if so. */
if (*dest != cc0_rtx
&& (STACK_REG_P (*src)
|| (STACK_REG_P (*dest)
&& (GET_CODE (*src) == REG || GET_CODE (*src) == MEM
|| GET_CODE (*src) == CONST_DOUBLE))))
move_for_stack_reg (insn, regstack, pat);
else
switch (GET_CODE (SET_SRC (pat)))
{
case COMPARE:
compare_for_stack_reg (insn, regstack, pat);
break;
case CALL:
{
int count;
for (count = HARD_REGNO_NREGS (REGNO (*dest), GET_MODE (*dest));
--count >= 0;)
{
regstack->reg[++regstack->top] = REGNO (*dest) + count;
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest) + count);
}
}
replace_reg (dest, FIRST_STACK_REG);
break;
case REG:
/* This is a `tstM2' case. */
if (*dest != cc0_rtx)
abort ();
src1 = src;
/* Fall through. */
case FLOAT_TRUNCATE:
case SQRT:
case ABS:
case NEG:
/* These insns only operate on the top of the stack. DEST might
be cc0_rtx if we're processing a tstM pattern. Also, it's
possible that the tstM case results in a REG_DEAD note on the
source. */
if (src1 == 0)
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
if (src1_note)
{
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
}
replace_reg (src1, FIRST_STACK_REG);
break;
case MINUS:
case DIV:
/* On i386, reversed forms of subM3 and divM3 exist for
MODE_FLOAT, so the same code that works for addM3 and mulM3
can be used. */
case MULT:
case PLUS:
/* These insns can accept the top of stack as a destination
from a stack reg or mem, or can use the top of stack as a
source and some other stack register (possibly top of stack)
as a destination. */
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
/* We will fix any death note later. */
if (STACK_REG_P (*src1))
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
else
src1_note = NULL_RTX;
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
src2_note = NULL_RTX;
/* If either operand is not a stack register, then the dest
must be top of stack. */
if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2))
emit_swap_insn (insn, regstack, *dest);
else
{
/* Both operands are REG. If neither operand is already
at the top of stack, choose to make the one that is the dest
the new top of stack. */
int src1_hard_regnum, src2_hard_regnum;
src1_hard_regnum = get_hard_regnum (regstack, *src1);
src2_hard_regnum = get_hard_regnum (regstack, *src2);
if (src1_hard_regnum == -1 || src2_hard_regnum == -1)
abort ();
if (src1_hard_regnum != FIRST_STACK_REG
&& src2_hard_regnum != FIRST_STACK_REG)
emit_swap_insn (insn, regstack, *dest);
}
if (STACK_REG_P (*src1))
replace_reg (src1, get_hard_regnum (regstack, *src1));
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
if (src1_note)
{
/* If the register that dies is at the top of stack, then
the destination is somewhere else - merely substitute it.
But if the reg that dies is not at top of stack, then
move the top of stack to the dead reg, as though we had
done the insn and then a store-with-pop. */
if (REGNO (XEXP (src1_note, 0)) == regstack->reg[regstack->top])
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
else
{
int regno = get_hard_regnum (regstack, XEXP (src1_note, 0));
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, regno);
regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
}
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src1_note, 0)));
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
}
else if (src2_note)
{
if (REGNO (XEXP (src2_note, 0)) == regstack->reg[regstack->top])
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
else
{
int regno = get_hard_regnum (regstack, XEXP (src2_note, 0));
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, regno);
regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
= regstack->reg[regstack->top];
}
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src2_note, 0)));
replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG);
regstack->top--;
}
else
{
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, get_hard_regnum (regstack, *dest));
}
break;
case UNSPEC:
switch (XINT (SET_SRC (pat), 1))
{
case 1: /* sin */
case 2: /* cos */
/* These insns only operate on the top of the stack. */
src1 = get_true_reg (&XVECEXP (SET_SRC (pat), 0, 0));
emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*dest))
replace_reg (dest, FIRST_STACK_REG);
if (src1_note)
{
replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
}
replace_reg (src1, FIRST_STACK_REG);
break;
default:
abort ();
}
break;
case IF_THEN_ELSE:
/* dest has to be on stack. */
if (get_hard_regnum (regstack, *dest) < FIRST_STACK_REG)
abort ();
/* This insn requires the top of stack to be the destination. */
/* If the comparison operator is an FP comparison operator,
it is handled correctly by compare_for_stack_reg () who
will move the destination to the top of stack. But if the
comparison operator is not an FP comparison operator, we
have to handle it here. */
if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG
&& REGNO (*dest) != regstack->reg[regstack->top])
emit_swap_insn (insn, regstack, *dest);
src1 = get_true_reg (&XEXP (SET_SRC (pat), 1));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 2));
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
{
rtx src_note [3];
int i;
src_note[0] = 0;
src_note[1] = src1_note;
src_note[2] = src2_note;
if (STACK_REG_P (*src1))
replace_reg (src1, get_hard_regnum (regstack, *src1));
if (STACK_REG_P (*src2))
replace_reg (src2, get_hard_regnum (regstack, *src2));
for (i = 1; i <= 2; i++)
if (src_note [i])
{
/* If the register that dies is not at the top of stack, then
move the top of stack to the dead reg */
if (REGNO (XEXP (src_note[i], 0))
!= regstack->reg[regstack->top])
{
remove_regno_note (insn, REG_DEAD,
REGNO (XEXP (src_note [i], 0)));
emit_pop_insn (insn, regstack, XEXP (src_note[i], 0),
emit_insn_after);
}
else
{
CLEAR_HARD_REG_BIT (regstack->reg_set,
REGNO (XEXP (src_note[i], 0)));
replace_reg (&XEXP (src_note[i], 0), FIRST_STACK_REG);
regstack->top--;
}
}
}
/* Make dest the top of stack. */
SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
replace_reg (dest, FIRST_STACK_REG);
break;
default:
abort ();
}
}
/* Substitute hard regnums for any stack regs in INSN, which has
N_INPUTS inputs and N_OUTPUTS outputs. REGSTACK is the stack info
before the insn, and is updated with changes made here.
There are several requirements and assumptions about the use of
stack-like regs in asm statements. These rules are enforced by
record_asm_stack_regs; see comments there for details. Any
asm_operands left in the RTL at this point may be assume to meet the
requirements, since record_asm_stack_regs removes any problem asm. */
static void
subst_asm_stack_regs (insn, regstack)
rtx insn;
stack regstack;
{
rtx body = PATTERN (insn);
int alt;
rtx *note_reg; /* Array of note contents */
rtx **note_loc; /* Address of REG field of each note */
enum reg_note *note_kind; /* The type of each note */
rtx *clobber_reg;
rtx **clobber_loc;
struct stack_def temp_stack;
int n_notes;
int n_clobbers;
rtx note;
int i;
int n_inputs, n_outputs;
/* Find out what the constraints required. If no constraint
alternative matches, that is a compiler bug: we should have caught
such an insn during the life analysis pass (and reload should have
caught it regardless). */
extract_insn (insn);
constrain_operands (1);
alt = which_alternative;
preprocess_constraints ();
n_inputs = get_asm_operand_n_inputs (body);
n_outputs = recog_n_operands - n_inputs;
if (alt < 0)
abort ();
/* Strip SUBREGs here to make the following code simpler. */
for (i = 0; i < recog_n_operands; i++)
if (GET_CODE (recog_operand[i]) == SUBREG
&& GET_CODE (SUBREG_REG (recog_operand[i])) == REG)
{
recog_operand_loc[i] = & SUBREG_REG (recog_operand[i]);
recog_operand[i] = SUBREG_REG (recog_operand[i]);
}
/* Set up NOTE_REG, NOTE_LOC and NOTE_KIND. */
for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1))
i++;
note_reg = (rtx *) alloca (i * sizeof (rtx));
note_loc = (rtx **) alloca (i * sizeof (rtx *));
note_kind = (enum reg_note *) alloca (i * sizeof (enum reg_note));
n_notes = 0;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
rtx reg = XEXP (note, 0);
rtx *loc = & XEXP (note, 0);
if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
{
loc = & SUBREG_REG (reg);
reg = SUBREG_REG (reg);
}
if (STACK_REG_P (reg)
&& (REG_NOTE_KIND (note) == REG_DEAD
|| REG_NOTE_KIND (note) == REG_UNUSED))
{
note_reg[n_notes] = reg;
note_loc[n_notes] = loc;
note_kind[n_notes] = REG_NOTE_KIND (note);
n_notes++;
}
}
/* Set up CLOBBER_REG and CLOBBER_LOC. */
n_clobbers = 0;
if (GET_CODE (body) == PARALLEL)
{
clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx));
clobber_loc = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx *));
for (i = 0; i < XVECLEN (body, 0); i++)
if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
{
rtx clobber = XVECEXP (body, 0, i);
rtx reg = XEXP (clobber, 0);
rtx *loc = & XEXP (clobber, 0);
if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
{
loc = & SUBREG_REG (reg);
reg = SUBREG_REG (reg);
}
if (STACK_REG_P (reg))
{
clobber_reg[n_clobbers] = reg;
clobber_loc[n_clobbers] = loc;
n_clobbers++;
}
}
}
bcopy ((char *) regstack, (char *) &temp_stack, sizeof (temp_stack));
/* Put the input regs into the desired place in TEMP_STACK. */
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i])
&& reg_class_subset_p (recog_op_alt[i][alt].class,
FLOAT_REGS)
&& recog_op_alt[i][alt].class != FLOAT_REGS)
{
/* If an operand needs to be in a particular reg in
FLOAT_REGS, the constraint was either 't' or 'u'. Since
these constraints are for single register classes, and reload
guaranteed that operand[i] is already in that class, we can
just use REGNO (recog_operand[i]) to know which actual reg this
operand needs to be in. */
int regno = get_hard_regnum (&temp_stack, recog_operand[i]);
if (regno < 0)
abort ();
if (regno != REGNO (recog_operand[i]))
{
/* recog_operand[i] is not in the right place. Find it
and swap it with whatever is already in I's place.
K is where recog_operand[i] is now. J is where it should
be. */
int j, k, temp;
k = temp_stack.top - (regno - FIRST_STACK_REG);
j = (temp_stack.top
- (REGNO (recog_operand[i]) - FIRST_STACK_REG));
temp = temp_stack.reg[k];
temp_stack.reg[k] = temp_stack.reg[j];
temp_stack.reg[j] = temp;
}
}
/* emit insns before INSN to make sure the reg-stack is in the right
order. */
change_stack (insn, regstack, &temp_stack, emit_insn_before);
/* Make the needed input register substitutions. Do death notes and
clobbers too, because these are for inputs, not outputs. */
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int regnum = get_hard_regnum (regstack, recog_operand[i]);
if (regnum < 0)
abort ();
replace_reg (recog_operand_loc[i], regnum);
}
for (i = 0; i < n_notes; i++)
if (note_kind[i] == REG_DEAD)
{
int regnum = get_hard_regnum (regstack, note_reg[i]);
if (regnum < 0)
abort ();
replace_reg (note_loc[i], regnum);
}
for (i = 0; i < n_clobbers; i++)
{
/* It's OK for a CLOBBER to reference a reg that is not live.
Don't try to replace it in that case. */
int regnum = get_hard_regnum (regstack, clobber_reg[i]);
if (regnum >= 0)
{
/* Sigh - clobbers always have QImode. But replace_reg knows
that these regs can't be MODE_INT and will abort. Just put
the right reg there without calling replace_reg. */
*clobber_loc[i] = FP_MODE_REG (regnum, DFmode);
}
}
/* Now remove from REGSTACK any inputs that the asm implicitly popped. */
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
/* An input reg is implicitly popped if it is tied to an
output, or if there is a CLOBBER for it. */
int j;
for (j = 0; j < n_clobbers; j++)
if (operands_match_p (clobber_reg[j], recog_operand[i]))
break;
if (j < n_clobbers || recog_op_alt[i][alt].matches >= 0)
{
/* recog_operand[i] might not be at the top of stack. But that's
OK, because all we need to do is pop the right number of regs
off of the top of the reg-stack. record_asm_stack_regs
guaranteed that all implicitly popped regs were grouped
at the top of the reg-stack. */
CLEAR_HARD_REG_BIT (regstack->reg_set,
regstack->reg[regstack->top]);
regstack->top--;
}
}
/* Now add to REGSTACK any outputs that the asm implicitly pushed.
Note that there isn't any need to substitute register numbers.
??? Explain why this is true. */
for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--)
{
/* See if there is an output for this hard reg. */
int j;
for (j = 0; j < n_outputs; j++)
if (STACK_REG_P (recog_operand[j]) && REGNO (recog_operand[j]) == i)
{
regstack->reg[++regstack->top] = i;
SET_HARD_REG_BIT (regstack->reg_set, i);
break;
}
}
/* Now emit a pop insn for any REG_UNUSED output, or any REG_DEAD
input that the asm didn't implicitly pop. If the asm didn't
implicitly pop an input reg, that reg will still be live.
Note that we can't use find_regno_note here: the register numbers
in the death notes have already been substituted. */
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int j;
for (j = 0; j < n_notes; j++)
if (REGNO (recog_operand[i]) == REGNO (note_reg[j])
&& note_kind[j] == REG_UNUSED)
{
insn = emit_pop_insn (insn, regstack, recog_operand[i],
emit_insn_after);
break;
}
}
for (i = n_outputs; i < n_outputs + n_inputs; i++)
if (STACK_REG_P (recog_operand[i]))
{
int j;
for (j = 0; j < n_notes; j++)
if (REGNO (recog_operand[i]) == REGNO (note_reg[j])
&& note_kind[j] == REG_DEAD
&& TEST_HARD_REG_BIT (regstack->reg_set,
REGNO (recog_operand[i])))
{
insn = emit_pop_insn (insn, regstack, recog_operand[i],
emit_insn_after);
break;
}
}
}
/* Substitute stack hard reg numbers for stack virtual registers in
INSN. Non-stack register numbers are not changed. REGSTACK is the
current stack content. Insns may be emitted as needed to arrange the
stack for the 387 based on the contents of the insn. */
static void
subst_stack_regs (insn, regstack)
rtx insn;
stack regstack;
{
register rtx *note_link, note;
register int i;
if (GET_CODE (insn) == CALL_INSN)
{
int top = regstack->top;
/* If there are any floating point parameters to be passed in
registers for this call, make sure they are in the right
order. */
if (top >= 0)
{
straighten_stack (PREV_INSN (insn), regstack);
/* Now mark the arguments as dead after the call. */
while (regstack->top >= 0)
{
CLEAR_HARD_REG_BIT (regstack->reg_set, FIRST_STACK_REG + regstack->top);
regstack->top--;
}
}
}
/* Do the actual substitution if any stack regs are mentioned.
Since we only record whether entire insn mentions stack regs, and
subst_stack_regs_pat only works for patterns that contain stack regs,
we must check each pattern in a parallel here. A call_value_pop could
fail otherwise. */
if (stack_regs_mentioned (insn))
{
int n_operands = asm_noperands (PATTERN (insn));
if (n_operands >= 0)
{
/* This insn is an `asm' with operands. Decode the operands,
decide how many are inputs, and do register substitution.
Any REG_UNUSED notes will be handled by subst_asm_stack_regs. */
subst_asm_stack_regs (insn, regstack);
return;
}
if (GET_CODE (PATTERN (insn)) == PARALLEL)
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i)))
{
subst_stack_regs_pat (insn, regstack,
XVECEXP (PATTERN (insn), 0, i));
/* subst_stack_regs_pat may have deleted a no-op insn. */
if (GET_CODE (insn) == NOTE)
break;
}
}
else
subst_stack_regs_pat (insn, regstack, PATTERN (insn));
}
/* subst_stack_regs_pat may have deleted a no-op insn. If so, any
REG_UNUSED will already have been dealt with, so just return. */
if (GET_CODE (insn) == NOTE)
return;
/* If there is a REG_UNUSED note on a stack register on this insn,
the indicated reg must be popped. The REG_UNUSED note is removed,
since the form of the newly emitted pop insn references the reg,
making it no longer `unset'. */
note_link = &REG_NOTES(insn);
for (note = *note_link; note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0)))
{
*note_link = XEXP (note, 1);
insn = emit_pop_insn (insn, regstack, XEXP (note, 0), emit_insn_after);
}
else
note_link = &XEXP (note, 1);
}
/* Change the organization of the stack so that it fits a new basic
block. Some registers might have to be popped, but there can never be
a register live in the new block that is not now live.
Insert any needed insns before or after INSN. WHEN is emit_insn_before
or emit_insn_after. OLD is the original stack layout, and NEW is
the desired form. OLD is updated to reflect the code emitted, ie, it
will be the same as NEW upon return.
This function will not preserve block_end[]. But that information
is no longer needed once this has executed. */
static void
change_stack (insn, old, new, when)
rtx insn;
stack old;
stack new;
rtx (*when)();
{
int reg;
/* We will be inserting new insns "backwards", by calling emit_insn_before.
If we are to insert after INSN, find the next insn, and insert before
it. */
if (when == emit_insn_after)
insn = NEXT_INSN (insn);
/* Pop any registers that are not needed in the new block. */
for (reg = old->top; reg >= 0; reg--)
if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
emit_pop_insn (insn, old, FP_MODE_REG (old->reg[reg], DFmode),
emit_insn_before);
if (new->top == -2)
{
/* If the new block has never been processed, then it can inherit
the old stack order. */
new->top = old->top;
bcopy (old->reg, new->reg, sizeof (new->reg));
}
else
{
/* This block has been entered before, and we must match the
previously selected stack order. */
/* By now, the only difference should be the order of the stack,
not their depth or liveliness. */
GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win);
abort ();
win:
if (old->top != new->top)
abort ();
/* If the stack is not empty (new->top != -1), loop here emitting
swaps until the stack is correct.
The worst case number of swaps emitted is N + 2, where N is the
depth of the stack. In some cases, the reg at the top of
stack may be correct, but swapped anyway in order to fix
other regs. But since we never swap any other reg away from
its correct slot, this algorithm will converge. */
if (new->top != -1)
do
{
/* Swap the reg at top of stack into the position it is
supposed to be in, until the correct top of stack appears. */
while (old->reg[old->top] != new->reg[new->top])
{
for (reg = new->top; reg >= 0; reg--)
if (new->reg[reg] == old->reg[old->top])
break;
if (reg == -1)
abort ();
emit_swap_insn (insn, old,
FP_MODE_REG (old->reg[reg], DFmode));
}
/* See if any regs remain incorrect. If so, bring an
incorrect reg to the top of stack, and let the while loop
above fix it. */
for (reg = new->top; reg >= 0; reg--)
if (new->reg[reg] != old->reg[reg])
{
emit_swap_insn (insn, old,
FP_MODE_REG (old->reg[reg], DFmode));
break;
}
} while (reg >= 0);
/* At this point there must be no differences. */
for (reg = old->top; reg >= 0; reg--)
if (old->reg[reg] != new->reg[reg])
abort ();
}
}
/* Check PAT, which points to RTL in INSN, for a LABEL_REF. If it is
found, ensure that a jump from INSN to the code_label to which the
label_ref points ends up with the same stack as that at the
code_label. Do this by inserting insns just before the code_label to
pop and rotate the stack until it is in the correct order. REGSTACK
is the order of the register stack in INSN.
Any code that is emitted here must not be later processed as part
of any block, as it will already contain hard register numbers. */
static void
goto_block_pat (insn, regstack, pat)
rtx insn;
stack regstack;
rtx pat;
{
rtx label;
rtx new_jump, new_label, new_barrier;
rtx *ref;
stack label_stack;
struct stack_def temp_stack;
int reg;
switch (GET_CODE (pat))
{
case RETURN:
straighten_stack (PREV_INSN (insn), regstack);
return;
default:
{
int i, j;
char *fmt = GET_RTX_FORMAT (GET_CODE (pat));
for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
goto_block_pat (insn, regstack, XEXP (pat, i));
if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (pat, i); j++)
goto_block_pat (insn, regstack, XVECEXP (pat, i, j));
}
return;
}
case LABEL_REF:;
}
label = XEXP (pat, 0);
if (GET_CODE (label) != CODE_LABEL)
abort ();
/* First, see if in fact anything needs to be done to the stack at all. */
if (INSN_UID (label) <= 0)
return;
label_stack = &block_stack_in[BLOCK_NUM (label)];
if (label_stack->top == -2)
{
/* If the target block hasn't had a stack order selected, then
we need merely ensure that no pops are needed. */
for (reg = regstack->top; reg >= 0; reg--)
if (! TEST_HARD_REG_BIT (label_stack->reg_set, regstack->reg[reg]))
break;
if (reg == -1)
{
/* change_stack will not emit any code in this case. */
change_stack (label, regstack, label_stack, emit_insn_after);
return;
}
}
else if (label_stack->top == regstack->top)
{
for (reg = label_stack->top; reg >= 0; reg--)
if (label_stack->reg[reg] != regstack->reg[reg])
break;
if (reg == -1)
return;
}
/* At least one insn will need to be inserted before label. Insert
a jump around the code we are about to emit. Emit a label for the new
code, and point the original insn at this new label. We can't use
redirect_jump here, because we're using fld[4] of the code labels as
LABEL_REF chains, no NUSES counters. */
new_jump = emit_jump_insn_before (gen_jump (label), label);
record_label_references (new_jump, PATTERN (new_jump));
JUMP_LABEL (new_jump) = label;
new_barrier = emit_barrier_after (new_jump);
new_label = gen_label_rtx ();
emit_label_after (new_label, new_barrier);
LABEL_REFS (new_label) = new_label;
/* The old label_ref will no longer point to the code_label if now uses,
so strip the label_ref from the code_label's chain of references. */
for (ref = &LABEL_REFS (label); *ref != label; ref = &LABEL_NEXTREF (*ref))
if (*ref == pat)
break;
if (*ref == label)
abort ();
*ref = LABEL_NEXTREF (*ref);
XEXP (pat, 0) = new_label;
record_label_references (insn, PATTERN (insn));
if (JUMP_LABEL (insn) == label)
JUMP_LABEL (insn) = new_label;
/* Now emit the needed code. */
temp_stack = *regstack;
change_stack (new_label, &temp_stack, label_stack, emit_insn_after);
}
/* Traverse all basic blocks in a function, converting the register
references in each insn from the "flat" register file that gcc uses, to
the stack-like registers the 387 uses. */
static void
convert_regs ()
{
register int block, reg;
register rtx insn, next;
struct stack_def regstack;
for (block = 0; block < blocks; block++)
{
if (block_stack_in[block].top == -2)
{
/* This block has not been previously encountered. Choose a
default mapping for any stack regs live on entry */
block_stack_in[block].top = -1;
for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, reg))
block_stack_in[block].reg[++block_stack_in[block].top] = reg;
}
/* Process all insns in this block. Keep track of `next' here,
so that we don't process any insns emitted while making
substitutions in INSN. */
next = block_begin[block];
regstack = block_stack_in[block];
do
{
insn = next;
next = NEXT_INSN (insn);
/* Don't bother processing unless there is a stack reg
mentioned or if it's a CALL_INSN (register passing of
floating point values). */
if (stack_regs_mentioned (insn) || GET_CODE (insn) == CALL_INSN)
subst_stack_regs (insn, &regstack);
} while (insn != block_end[block]);
/* For all further actions, INSN needs to be the last insn in
this basic block. If subst_stack_regs inserted additional
instructions after INSN, it is no longer the last one at
this point. */
next = PREV_INSN (next);
/* If subst_stack_regs inserted something after a JUMP_INSN, that
is almost certainly a bug. */
if (GET_CODE (insn) == JUMP_INSN && insn != next)
abort ();
insn = next;
/* Something failed if the stack life doesn't match. */
GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win);
abort ();
win:
/* Adjust the stack of this block on exit to match the stack of
the target block, or copy stack information into stack of
jump target if the target block's stack order hasn't been set
yet. */
if (GET_CODE (insn) == JUMP_INSN)
goto_block_pat (insn, &regstack, PATTERN (insn));
/* Likewise handle the case where we fall into the next block. */
if ((block < blocks - 1) && block_drops_in[block+1])
change_stack (insn, &regstack, &block_stack_in[block+1],
emit_insn_after);
}
/* If the last basic block is the end of a loop, and that loop has
regs live at its start, then the last basic block will have regs live
at its end that need to be popped before the function returns. */
{
int value_reg_low, value_reg_high;
value_reg_low = value_reg_high = -1;
{
rtx retvalue;
if ((retvalue = stack_result (current_function_decl)))
{
value_reg_low = REGNO (retvalue);
value_reg_high = value_reg_low +
HARD_REGNO_NREGS (value_reg_low, GET_MODE (retvalue)) - 1;
}
}
for (reg = regstack.top; reg >= 0; reg--)
if (regstack.reg[reg] < value_reg_low
|| regstack.reg[reg] > value_reg_high)
insn = emit_pop_insn (insn, &regstack,
FP_MODE_REG (regstack.reg[reg], DFmode),
emit_insn_after);
}
straighten_stack (insn, &regstack);
}
/* Check expression PAT, which is in INSN, for label references. if
one is found, print the block number of destination to FILE. */
static void
print_blocks (file, insn, pat)
FILE *file;
rtx insn, pat;
{
register RTX_CODE code = GET_CODE (pat);
register int i;
register char *fmt;
if (code == LABEL_REF)
{
register rtx label = XEXP (pat, 0);
if (GET_CODE (label) != CODE_LABEL)
abort ();
fprintf (file, " %d", BLOCK_NUM (label));
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
print_blocks (file, insn, XEXP (pat, i));
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (pat, i); j++)
print_blocks (file, insn, XVECEXP (pat, i, j));
}
}
}
/* Write information about stack registers and stack blocks into FILE.
This is part of making a debugging dump. */
static void
dump_stack_info (file)
FILE *file;
{
register int block;
fprintf (file, "\n%d stack blocks.\n", blocks);
for (block = 0; block < blocks; block++)
{
register rtx head, jump, end;
register int regno;
fprintf (file, "\nStack block %d: first insn %d, last %d.\n",
block, INSN_UID (block_begin[block]),
INSN_UID (block_end[block]));
head = block_begin[block];
fprintf (file, "Reached from blocks: ");
if (GET_CODE (head) == CODE_LABEL)
for (jump = LABEL_REFS (head);
jump != head;
jump = LABEL_NEXTREF (jump))
{
register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
fprintf (file, " %d", from_block);
}
if (block_drops_in[block])
fprintf (file, " previous");
fprintf (file, "\nlive stack registers on block entry: ");
for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
{
if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, regno))
fprintf (file, "%d ", regno);
}
fprintf (file, "\nlive stack registers on block exit: ");
for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
{
if (TEST_HARD_REG_BIT (block_out_reg_set[block], regno))
fprintf (file, "%d ", regno);
}
end = block_end[block];
fprintf (file, "\nJumps to blocks: ");
if (GET_CODE (end) == JUMP_INSN)
print_blocks (file, end, PATTERN (end));
if (block + 1 < blocks && block_drops_in[block+1])
fprintf (file, " next");
else if (block + 1 == blocks
|| (GET_CODE (end) == JUMP_INSN
&& GET_CODE (PATTERN (end)) == RETURN))
fprintf (file, " return");
fprintf (file, "\n");
}
}
#endif /* STACK_REGS */
Index: head/contrib/gcc/regmove.c
===================================================================
--- head/contrib/gcc/regmove.c (revision 52750)
+++ head/contrib/gcc/regmove.c (revision 52751)
@@ -1,2172 +1,2190 @@
/* Move registers around to reduce number of move instructions needed.
Copyright (C) 1987, 88, 89, 92-98, 1999 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 module looks for cases where matching constraints would force
an instruction to need a reload, and this reload would be a register
to register move. It then attempts to change the registers used by the
instruction to avoid the move instruction. */
#include "config.h"
#include "system.h"
#include "rtl.h" /* stdio.h must precede rtl.h for FFS. */
#include "insn-config.h"
#include "recog.h"
#include "output.h"
#include "reload.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "expr.h"
#include "insn-flags.h"
#include "basic-block.h"
#include "toplev.h"
static int optimize_reg_copy_1 PROTO((rtx, rtx, rtx));
static void optimize_reg_copy_2 PROTO((rtx, rtx, rtx));
static void optimize_reg_copy_3 PROTO((rtx, rtx, rtx));
static rtx gen_add3_insn PROTO((rtx, rtx, rtx));
static void copy_src_to_dest PROTO((rtx, rtx, rtx, int, int));
static int *regmove_bb_head;
struct match {
int with[MAX_RECOG_OPERANDS];
enum { READ, WRITE, READWRITE } use[MAX_RECOG_OPERANDS];
int commutative[MAX_RECOG_OPERANDS];
int early_clobber[MAX_RECOG_OPERANDS];
};
static rtx discover_flags_reg PROTO((void));
static void mark_flags_life_zones PROTO((rtx));
static void flags_set_1 PROTO((rtx, rtx));
static int try_auto_increment PROTO((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
static int find_matches PROTO((rtx, struct match *));
static int fixup_match_1 PROTO((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
;
static int reg_is_remote_constant_p PROTO((rtx, rtx, rtx));
-static int stable_but_for_p PROTO((rtx, rtx, rtx));
+static int stable_and_no_regs_but_for_p PROTO((rtx, rtx, rtx));
static int regclass_compatible_p PROTO((int, int));
static int loop_depth;
/* Return non-zero if registers with CLASS1 and CLASS2 can be merged without
causing too much register allocation problems. */
static int
regclass_compatible_p (class0, class1)
int class0, class1;
{
return (class0 == class1
|| (reg_class_subset_p (class0, class1)
&& ! CLASS_LIKELY_SPILLED_P (class0))
|| (reg_class_subset_p (class1, class0)
&& ! CLASS_LIKELY_SPILLED_P (class1)));
}
/* Generate and return an insn body to add r1 and c,
storing the result in r0. */
static rtx
gen_add3_insn (r0, r1, c)
rtx r0, r1, c;
{
int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
if (icode == CODE_FOR_nothing
|| ! (*insn_operand_predicate[icode][0]) (r0, insn_operand_mode[icode][0])
|| ! (*insn_operand_predicate[icode][1]) (r1, insn_operand_mode[icode][1])
|| ! (*insn_operand_predicate[icode][2]) (c, insn_operand_mode[icode][2]))
return NULL_RTX;
return (GEN_FCN (icode) (r0, r1, c));
}
/* INC_INSN is an instruction that adds INCREMENT to REG.
Try to fold INC_INSN as a post/pre in/decrement into INSN.
Iff INC_INSN_SET is nonzero, inc_insn has a destination different from src.
Return nonzero for success. */
static int
try_auto_increment (insn, inc_insn, inc_insn_set, reg, increment, pre)
rtx reg, insn, inc_insn ,inc_insn_set;
HOST_WIDE_INT increment;
int pre;
{
enum rtx_code inc_code;
rtx pset = single_set (insn);
if (pset)
{
/* Can't use the size of SET_SRC, we might have something like
(sign_extend:SI (mem:QI ... */
rtx use = find_use_as_address (pset, reg, 0);
if (use != 0 && use != (rtx) 1)
{
int size = GET_MODE_SIZE (GET_MODE (use));
if (0
|| (HAVE_POST_INCREMENT
&& pre == 0 && (inc_code = POST_INC, increment == size))
|| (HAVE_PRE_INCREMENT
&& pre == 1 && (inc_code = PRE_INC, increment == size))
|| (HAVE_POST_DECREMENT
&& pre == 0 && (inc_code = POST_DEC, increment == -size))
|| (HAVE_PRE_DECREMENT
&& pre == 1 && (inc_code = PRE_DEC, increment == -size))
)
{
if (inc_insn_set)
validate_change
(inc_insn,
&SET_SRC (inc_insn_set),
XEXP (SET_SRC (inc_insn_set), 0), 1);
validate_change (insn, &XEXP (use, 0),
gen_rtx_fmt_e (inc_code, Pmode, reg), 1);
if (apply_change_group ())
{
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_INC,
reg, REG_NOTES (insn));
if (! inc_insn_set)
{
PUT_CODE (inc_insn, NOTE);
NOTE_LINE_NUMBER (inc_insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (inc_insn) = 0;
}
return 1;
}
}
}
}
return 0;
}
/* Determine if the pattern generated by add_optab has a clobber,
such as might be issued for a flags hard register. To make the
code elsewhere simpler, we handle cc0 in this same framework.
Return the register if one was discovered. Return NULL_RTX if
if no flags were found. Return pc_rtx if we got confused. */
static rtx
discover_flags_reg ()
{
rtx tmp;
tmp = gen_rtx_REG (word_mode, 10000);
tmp = gen_add3_insn (tmp, tmp, GEN_INT (2));
/* If we get something that isn't a simple set, or a
[(set ..) (clobber ..)], this whole function will go wrong. */
if (GET_CODE (tmp) == SET)
return NULL_RTX;
else if (GET_CODE (tmp) == PARALLEL)
{
int found;
if (XVECLEN (tmp, 0) != 2)
return pc_rtx;
tmp = XVECEXP (tmp, 0, 1);
if (GET_CODE (tmp) != CLOBBER)
return pc_rtx;
tmp = XEXP (tmp, 0);
/* Don't do anything foolish if the md wanted to clobber a
scratch or something. We only care about hard regs.
Moreover we don't like the notion of subregs of hard regs. */
if (GET_CODE (tmp) == SUBREG
&& GET_CODE (SUBREG_REG (tmp)) == REG
&& REGNO (SUBREG_REG (tmp)) < FIRST_PSEUDO_REGISTER)
return pc_rtx;
found = (GET_CODE (tmp) == REG && REGNO (tmp) < FIRST_PSEUDO_REGISTER);
return (found ? tmp : NULL_RTX);
}
return pc_rtx;
}
/* It is a tedious task identifying when the flags register is live and
when it is safe to optimize. Since we process the instruction stream
multiple times, locate and record these live zones by marking the
mode of the instructions --
QImode is used on the instruction at which the flags becomes live.
HImode is used within the range (exclusive) that the flags are
live. Thus the user of the flags is not marked.
All other instructions are cleared to VOIDmode. */
/* Used to communicate with flags_set_1. */
static rtx flags_set_1_rtx;
static int flags_set_1_set;
static void
mark_flags_life_zones (flags)
rtx flags;
{
int flags_regno;
int flags_nregs;
int block;
#ifdef HAVE_cc0
/* If we found a flags register on a cc0 host, bail. */
if (flags == NULL_RTX)
flags = cc0_rtx;
else if (flags != cc0_rtx)
flags = pc_rtx;
#endif
/* Simple cases first: if no flags, clear all modes. If confusing,
mark the entire function as being in a flags shadow. */
if (flags == NULL_RTX || flags == pc_rtx)
{
enum machine_mode mode = (flags ? HImode : VOIDmode);
rtx insn;
for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
PUT_MODE (insn, mode);
return;
}
#ifdef HAVE_cc0
flags_regno = -1;
flags_nregs = 1;
#else
flags_regno = REGNO (flags);
flags_nregs = HARD_REGNO_NREGS (flags_regno, GET_MODE (flags));
#endif
flags_set_1_rtx = flags;
/* Process each basic block. */
for (block = n_basic_blocks - 1; block >= 0; block--)
{
rtx insn, end;
int live;
insn = BLOCK_HEAD (block);
end = BLOCK_END (block);
/* Look out for the (unlikely) case of flags being live across
basic block boundaries. */
live = 0;
#ifndef HAVE_cc0
{
int i;
for (i = 0; i < flags_nregs; ++i)
live |= REGNO_REG_SET_P (BASIC_BLOCK (block)->global_live_at_start,
flags_regno + i);
}
#endif
while (1)
{
/* Process liveness in reverse order of importance --
alive, death, birth. This lets more important info
overwrite the mode of lesser info. */
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
#ifdef HAVE_cc0
/* In the cc0 case, death is not marked in reg notes,
but is instead the mere use of cc0 when it is alive. */
if (live && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
live = 0;
#else
/* In the hard reg case, we watch death notes. */
if (live && find_regno_note (insn, REG_DEAD, flags_regno))
live = 0;
#endif
PUT_MODE (insn, (live ? HImode : VOIDmode));
/* In either case, birth is denoted simply by it's presence
as the destination of a set. */
flags_set_1_set = 0;
note_stores (PATTERN (insn), flags_set_1);
if (flags_set_1_set)
{
live = 1;
PUT_MODE (insn, QImode);
}
}
else
PUT_MODE (insn, (live ? HImode : VOIDmode));
if (insn == end)
break;
insn = NEXT_INSN (insn);
}
}
}
/* A subroutine of mark_flags_life_zones, called through note_stores. */
static void
flags_set_1 (x, pat)
rtx x, pat;
{
if (GET_CODE (pat) == SET
&& reg_overlap_mentioned_p (x, flags_set_1_rtx))
flags_set_1_set = 1;
}
static int *regno_src_regno;
/* Indicate how good a choice REG (which appears as a source) is to replace
a destination register with. The higher the returned value, the better
the choice. The main objective is to avoid using a register that is
a candidate for tying to a hard register, since the output might in
turn be a candidate to be tied to a different hard register. */
int
replacement_quality(reg)
rtx reg;
{
int src_regno;
/* Bad if this isn't a register at all. */
if (GET_CODE (reg) != REG)
return 0;
/* If this register is not meant to get a hard register,
it is a poor choice. */
if (REG_LIVE_LENGTH (REGNO (reg)) < 0)
return 0;
src_regno = regno_src_regno[REGNO (reg)];
/* If it was not copied from another register, it is fine. */
if (src_regno < 0)
return 3;
/* Copied from a hard register? */
if (src_regno < FIRST_PSEUDO_REGISTER)
return 1;
/* Copied from a pseudo register - not as bad as from a hard register,
yet still cumbersome, since the register live length will be lengthened
when the registers get tied. */
return 2;
}
/* INSN is a copy from SRC to DEST, both registers, and SRC does not die
in INSN.
Search forward to see if SRC dies before either it or DEST is modified,
but don't scan past the end of a basic block. If so, we can replace SRC
with DEST and let SRC die in INSN.
This will reduce the number of registers live in that range and may enable
DEST to be tied to SRC, thus often saving one register in addition to a
register-register copy. */
static int
optimize_reg_copy_1 (insn, dest, src)
rtx insn;
rtx dest;
rtx src;
{
rtx p, q;
rtx note;
rtx dest_death = 0;
int sregno = REGNO (src);
int dregno = REGNO (dest);
/* We don't want to mess with hard regs if register classes are small. */
if (sregno == dregno
|| (SMALL_REGISTER_CLASSES
&& (sregno < FIRST_PSEUDO_REGISTER
|| dregno < FIRST_PSEUDO_REGISTER))
/* We don't see all updates to SP if they are in an auto-inc memory
reference, so we must disallow this optimization on them. */
|| sregno == STACK_POINTER_REGNUM || dregno == STACK_POINTER_REGNUM)
return 0;
for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info (REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if it is inside
an EH region. There is no easy way to tell, so we just always break
when we see a CALL_INSN if flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (p) == CALL_INSN)
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (reg_set_p (src, p) || reg_set_p (dest, p)
/* Don't change a USE of a register. */
|| (GET_CODE (PATTERN (p)) == USE
&& reg_overlap_mentioned_p (src, XEXP (PATTERN (p), 0))))
break;
/* See if all of SRC dies in P. This test is slightly more
conservative than it needs to be. */
if ((note = find_regno_note (p, REG_DEAD, sregno)) != 0
&& GET_MODE (XEXP (note, 0)) == GET_MODE (src))
{
int failed = 0;
int d_length = 0;
int s_length = 0;
int d_n_calls = 0;
int s_n_calls = 0;
/* We can do the optimization. Scan forward from INSN again,
replacing regs as we go. Set FAILED if a replacement can't
be done. In that case, we can't move the death note for SRC.
This should be rare. */
/* Set to stop at next insn. */
for (q = next_real_insn (insn);
q != next_real_insn (p);
q = next_real_insn (q))
{
if (reg_overlap_mentioned_p (src, PATTERN (q)))
{
/* If SRC is a hard register, we might miss some
overlapping registers with validate_replace_rtx,
so we would have to undo it. We can't if DEST is
present in the insn, so fail in that combination
of cases. */
if (sregno < FIRST_PSEUDO_REGISTER
&& reg_mentioned_p (dest, PATTERN (q)))
failed = 1;
/* Replace all uses and make sure that the register
isn't still present. */
else if (validate_replace_rtx (src, dest, q)
&& (sregno >= FIRST_PSEUDO_REGISTER
|| ! reg_overlap_mentioned_p (src,
PATTERN (q))))
{
/* We assume that a register is used exactly once per
insn in the REG_N_REFS updates below. If this is not
correct, no great harm is done.
Since we do not know if we will change the lifetime of
SREGNO or DREGNO, we must not update REG_LIVE_LENGTH
or REG_N_CALLS_CROSSED at this time. */
if (sregno >= FIRST_PSEUDO_REGISTER)
REG_N_REFS (sregno) -= loop_depth;
if (dregno >= FIRST_PSEUDO_REGISTER)
REG_N_REFS (dregno) += loop_depth;
}
else
{
validate_replace_rtx (dest, src, q);
failed = 1;
}
}
/* For SREGNO, count the total number of insns scanned.
For DREGNO, count the total number of insns scanned after
passing the death note for DREGNO. */
s_length++;
if (dest_death)
d_length++;
/* If the insn in which SRC dies is a CALL_INSN, don't count it
as a call that has been crossed. Otherwise, count it. */
if (q != p && GET_CODE (q) == CALL_INSN)
{
/* Similarly, total calls for SREGNO, total calls beyond
the death note for DREGNO. */
s_n_calls++;
if (dest_death)
d_n_calls++;
}
/* If DEST dies here, remove the death note and save it for
later. Make sure ALL of DEST dies here; again, this is
overly conservative. */
if (dest_death == 0
&& (dest_death = find_regno_note (q, REG_DEAD, dregno)) != 0)
{
if (GET_MODE (XEXP (dest_death, 0)) != GET_MODE (dest))
failed = 1, dest_death = 0;
else
remove_note (q, dest_death);
}
}
if (! failed)
{
/* These counters need to be updated if and only if we are
going to move the REG_DEAD note. */
if (sregno >= FIRST_PSEUDO_REGISTER)
{
if (REG_LIVE_LENGTH (sregno) >= 0)
{
REG_LIVE_LENGTH (sregno) -= s_length;
/* REG_LIVE_LENGTH is only an approximation after
combine if sched is not run, so make sure that we
still have a reasonable value. */
if (REG_LIVE_LENGTH (sregno) < 2)
REG_LIVE_LENGTH (sregno) = 2;
}
REG_N_CALLS_CROSSED (sregno) -= s_n_calls;
}
/* Move death note of SRC from P to INSN. */
remove_note (p, note);
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
}
/* Put death note of DEST on P if we saw it die. */
if (dest_death)
{
XEXP (dest_death, 1) = REG_NOTES (p);
REG_NOTES (p) = dest_death;
if (dregno >= FIRST_PSEUDO_REGISTER)
{
/* If and only if we are moving the death note for DREGNO,
then we need to update its counters. */
if (REG_LIVE_LENGTH (dregno) >= 0)
REG_LIVE_LENGTH (dregno) += d_length;
REG_N_CALLS_CROSSED (dregno) += d_n_calls;
}
}
return ! failed;
}
/* If SRC is a hard register which is set or killed in some other
way, we can't do this optimization. */
else if (sregno < FIRST_PSEUDO_REGISTER
&& dead_or_set_p (p, src))
break;
}
return 0;
}
/* INSN is a copy of SRC to DEST, in which SRC dies. See if we now have
a sequence of insns that modify DEST followed by an insn that sets
SRC to DEST in which DEST dies, with no prior modification of DEST.
(There is no need to check if the insns in between actually modify
DEST. We should not have cases where DEST is not modified, but
the optimization is safe if no such modification is detected.)
In that case, we can replace all uses of DEST, starting with INSN and
ending with the set of SRC to DEST, with SRC. We do not do this
optimization if a CALL_INSN is crossed unless SRC already crosses a
call or if DEST dies before the copy back to SRC.
It is assumed that DEST and SRC are pseudos; it is too complicated to do
this for hard registers since the substitutions we may make might fail. */
static void
optimize_reg_copy_2 (insn, dest, src)
rtx insn;
rtx dest;
rtx src;
{
rtx p, q;
rtx set;
int sregno = REGNO (src);
int dregno = REGNO (dest);
for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info (REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if it is inside
an EH region. There is no easy way to tell, so we just always break
when we see a CALL_INSN if flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (p) == CALL_INSN)
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
set = single_set (p);
if (set && SET_SRC (set) == dest && SET_DEST (set) == src
&& find_reg_note (p, REG_DEAD, dest))
{
/* We can do the optimization. Scan forward from INSN again,
replacing regs as we go. */
/* Set to stop at next insn. */
for (q = insn; q != NEXT_INSN (p); q = NEXT_INSN (q))
if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
{
if (reg_mentioned_p (dest, PATTERN (q)))
{
PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
/* We assume that a register is used exactly once per
insn in the updates below. If this is not correct,
no great harm is done. */
REG_N_REFS (dregno) -= loop_depth;
REG_N_REFS (sregno) += loop_depth;
}
if (GET_CODE (q) == CALL_INSN)
{
REG_N_CALLS_CROSSED (dregno)--;
REG_N_CALLS_CROSSED (sregno)++;
}
}
remove_note (p, find_reg_note (p, REG_DEAD, dest));
REG_N_DEATHS (dregno)--;
remove_note (insn, find_reg_note (insn, REG_DEAD, src));
REG_N_DEATHS (sregno)--;
return;
}
if (reg_set_p (src, p)
|| find_reg_note (p, REG_DEAD, dest)
|| (GET_CODE (p) == CALL_INSN && REG_N_CALLS_CROSSED (sregno) == 0))
break;
}
}
/* INSN is a ZERO_EXTEND or SIGN_EXTEND of SRC to DEST.
Look if SRC dies there, and if it is only set once, by loading
it from memory. If so, try to encorporate the zero/sign extension
into the memory read, change SRC to the mode of DEST, and alter
the remaining accesses to use the appropriate SUBREG. This allows
SRC and DEST to be tied later. */
static void
optimize_reg_copy_3 (insn, dest, src)
rtx insn;
rtx dest;
rtx src;
{
rtx src_reg = XEXP (src, 0);
int src_no = REGNO (src_reg);
int dst_no = REGNO (dest);
rtx p, set, subreg;
enum machine_mode old_mode;
if (src_no < FIRST_PSEUDO_REGISTER
|| dst_no < FIRST_PSEUDO_REGISTER
|| ! find_reg_note (insn, REG_DEAD, src_reg)
|| REG_N_SETS (src_no) != 1)
return;
for (p = PREV_INSN (insn); ! reg_set_p (src_reg, p); p = PREV_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
return;
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info (REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if it is inside
an EH region. There is no easy way to tell, so we just always break
when we see a CALL_INSN if flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (p) == CALL_INSN)
return;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
}
if (! (set = single_set (p))
|| GET_CODE (SET_SRC (set)) != MEM
|| SET_DEST (set) != src_reg)
return;
/* Be conserative: although this optimization is also valid for
volatile memory references, that could cause trouble in later passes. */
if (MEM_VOLATILE_P (SET_SRC (set)))
return;
/* Do not use a SUBREG to truncate from one mode to another if truncation
is not a nop. */
if (GET_MODE_BITSIZE (GET_MODE (src_reg)) <= GET_MODE_BITSIZE (GET_MODE (src))
&& !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (src)),
GET_MODE_BITSIZE (GET_MODE (src_reg))))
return;
old_mode = GET_MODE (src_reg);
PUT_MODE (src_reg, GET_MODE (src));
XEXP (src, 0) = SET_SRC (set);
/* Include this change in the group so that it's easily undone if
one of the changes in the group is invalid. */
validate_change (p, &SET_SRC (set), src, 1);
/* Now walk forward making additional replacements. We want to be able
to undo all the changes if a later substitution fails. */
subreg = gen_rtx_SUBREG (old_mode, src_reg, 0);
while (p = NEXT_INSN (p), p != insn)
{
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
/* Make a tenative change. */
validate_replace_rtx_group (src_reg, subreg, p);
}
validate_replace_rtx_group (src, src_reg, insn);
/* Now see if all the changes are valid. */
if (! apply_change_group ())
{
/* One or more changes were no good. Back out everything. */
PUT_MODE (src_reg, old_mode);
XEXP (src, 0) = src_reg;
}
}
/* If we were not able to update the users of src to use dest directly, try
instead moving the value to dest directly before the operation. */
static void
copy_src_to_dest (insn, src, dest, loop_depth, old_max_uid)
rtx insn;
rtx src;
rtx dest;
int loop_depth;
int old_max_uid;
{
rtx seq;
rtx link;
rtx next;
rtx set;
rtx move_insn;
rtx *p_insn_notes;
rtx *p_move_notes;
int src_regno;
int dest_regno;
int bb;
int insn_uid;
int move_uid;
/* A REG_LIVE_LENGTH of -1 indicates the register is equivalent to a constant
or memory location and is used infrequently; a REG_LIVE_LENGTH of -2 is
parameter when there is no frame pointer that is not allocated a register.
For now, we just reject them, rather than incrementing the live length. */
if (GET_CODE (src) == REG
&& REG_LIVE_LENGTH (REGNO (src)) > 0
&& GET_CODE (dest) == REG
&& REG_LIVE_LENGTH (REGNO (dest)) > 0
&& (set = single_set (insn)) != NULL_RTX
&& !reg_mentioned_p (dest, SET_SRC (set))
&& GET_MODE (src) == GET_MODE (dest))
{
int old_num_regs = reg_rtx_no;
/* Generate the src->dest move. */
start_sequence ();
emit_move_insn (dest, src);
seq = gen_sequence ();
end_sequence ();
/* If this sequence uses new registers, we may not use it. */
if (old_num_regs != reg_rtx_no
|| ! validate_replace_rtx (src, dest, insn))
{
/* We have to restore reg_rtx_no to its old value, lest
recompute_reg_usage will try to compute the usage of the
new regs, yet reg_n_info is not valid for them. */
reg_rtx_no = old_num_regs;
return;
}
emit_insn_before (seq, insn);
move_insn = PREV_INSN (insn);
p_move_notes = &REG_NOTES (move_insn);
p_insn_notes = &REG_NOTES (insn);
/* Move any notes mentioning src to the move instruction */
for (link = REG_NOTES (insn); link != NULL_RTX; link = next)
{
next = XEXP (link, 1);
if (XEXP (link, 0) == src)
{
*p_move_notes = link;
p_move_notes = &XEXP (link, 1);
}
else
{
*p_insn_notes = link;
p_insn_notes = &XEXP (link, 1);
}
}
*p_move_notes = NULL_RTX;
*p_insn_notes = NULL_RTX;
/* Is the insn the head of a basic block? If so extend it */
insn_uid = INSN_UID (insn);
move_uid = INSN_UID (move_insn);
if (insn_uid < old_max_uid)
{
bb = regmove_bb_head[insn_uid];
if (bb >= 0)
{
BLOCK_HEAD (bb) = move_insn;
regmove_bb_head[insn_uid] = -1;
}
}
/* Update the various register tables. */
dest_regno = REGNO (dest);
REG_N_SETS (dest_regno) += loop_depth;
REG_N_REFS (dest_regno) += loop_depth;
REG_LIVE_LENGTH (dest_regno)++;
if (REGNO_FIRST_UID (dest_regno) == insn_uid)
REGNO_FIRST_UID (dest_regno) = move_uid;
src_regno = REGNO (src);
if (! find_reg_note (move_insn, REG_DEAD, src))
REG_LIVE_LENGTH (src_regno)++;
if (REGNO_FIRST_UID (src_regno) == insn_uid)
REGNO_FIRST_UID (src_regno) = move_uid;
if (REGNO_LAST_UID (src_regno) == insn_uid)
REGNO_LAST_UID (src_regno) = move_uid;
if (REGNO_LAST_NOTE_UID (src_regno) == insn_uid)
REGNO_LAST_NOTE_UID (src_regno) = move_uid;
}
}
/* Return whether REG is set in only one location, and is set to a
constant, but is set in a different basic block from INSN (an
instructions which uses REG). In this case REG is equivalent to a
constant, and we don't want to break that equivalence, because that
may increase register pressure and make reload harder. If REG is
set in the same basic block as INSN, we don't worry about it,
because we'll probably need a register anyhow (??? but what if REG
is used in a different basic block as well as this one?). FIRST is
the first insn in the function. */
static int
reg_is_remote_constant_p (reg, insn, first)
rtx reg;
rtx insn;
rtx first;
{
register rtx p;
if (REG_N_SETS (REGNO (reg)) != 1)
return 0;
/* Look for the set. */
for (p = LOG_LINKS (insn); p; p = XEXP (p, 1))
{
rtx s;
if (REG_NOTE_KIND (p) != 0)
continue;
s = single_set (XEXP (p, 0));
if (s != 0
&& GET_CODE (SET_DEST (s)) == REG
&& REGNO (SET_DEST (s)) == REGNO (reg))
{
/* The register is set in the same basic block. */
return 0;
}
}
for (p = first; p && p != insn; p = NEXT_INSN (p))
{
rtx s;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
s = single_set (p);
if (s != 0
&& GET_CODE (SET_DEST (s)) == REG
&& REGNO (SET_DEST (s)) == REGNO (reg))
{
/* This is the instruction which sets REG. If there is a
REG_EQUAL note, then REG is equivalent to a constant. */
if (find_reg_note (p, REG_EQUAL, NULL_RTX))
return 1;
return 0;
}
}
return 0;
}
/* INSN is adding a CONST_INT to a REG. We search backwards looking for
another add immediate instruction with the same source and dest registers,
and if we find one, we change INSN to an increment, and return 1. If
no changes are made, we return 0.
This changes
(set (reg100) (plus reg1 offset1))
...
(set (reg100) (plus reg1 offset2))
to
(set (reg100) (plus reg1 offset1))
...
(set (reg100) (plus reg100 offset2-offset1)) */
/* ??? What does this comment mean? */
/* cse disrupts preincrement / postdecrement squences when it finds a
hard register as ultimate source, like the frame pointer. */
int
fixup_match_2 (insn, dst, src, offset, regmove_dump_file)
rtx insn, dst, src, offset;
FILE *regmove_dump_file;
{
rtx p, dst_death = 0;
int length, num_calls = 0;
/* If SRC dies in INSN, we'd have to move the death note. This is
considered to be very unlikely, so we just skip the optimization
in this case. */
if (find_regno_note (insn, REG_DEAD, REGNO (src)))
return 0;
/* Scan backward to find the first instruction that sets DST. */
for (length = 0, p = PREV_INSN (insn); p; p = PREV_INSN (p))
{
rtx pset;
if (GET_CODE (p) == CODE_LABEL
|| GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info (REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if it is inside
an EH region. There is no easy way to tell, so we just always break
when we see a CALL_INSN if flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (p) == CALL_INSN)
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (find_regno_note (p, REG_DEAD, REGNO (dst)))
dst_death = p;
if (! dst_death)
length++;
pset = single_set (p);
if (pset && SET_DEST (pset) == dst
&& GET_CODE (SET_SRC (pset)) == PLUS
&& XEXP (SET_SRC (pset), 0) == src
&& GET_CODE (XEXP (SET_SRC (pset), 1)) == CONST_INT)
{
HOST_WIDE_INT newconst
= INTVAL (offset) - INTVAL (XEXP (SET_SRC (pset), 1));
rtx add = gen_add3_insn (dst, dst, GEN_INT (newconst));
if (add && validate_change (insn, &PATTERN (insn), add, 0))
{
/* Remove the death note for DST from DST_DEATH. */
if (dst_death)
{
remove_death (REGNO (dst), dst_death);
REG_LIVE_LENGTH (REGNO (dst)) += length;
REG_N_CALLS_CROSSED (REGNO (dst)) += num_calls;
}
REG_N_REFS (REGNO (dst)) += loop_depth;
REG_N_REFS (REGNO (src)) -= loop_depth;
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Fixed operand of insn %d.\n",
INSN_UID (insn));
#ifdef AUTO_INC_DEC
for (p = PREV_INSN (insn); p; p = PREV_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL
|| GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (reg_overlap_mentioned_p (dst, PATTERN (p)))
{
if (try_auto_increment (p, insn, 0, dst, newconst, 0))
return 1;
break;
}
}
for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL
|| GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (reg_overlap_mentioned_p (dst, PATTERN (p)))
{
try_auto_increment (p, insn, 0, dst, newconst, 1);
break;
}
}
#endif
return 1;
}
}
if (reg_set_p (dst, PATTERN (p)))
break;
/* If we have passed a call instruction, and the
pseudo-reg SRC is not already live across a call,
then don't perform the optimization. */
/* reg_set_p is overly conservative for CALL_INSNS, thinks that all
hard regs are clobbered. Thus, we only use it for src for
non-call insns. */
if (GET_CODE (p) == CALL_INSN)
{
if (! dst_death)
num_calls++;
if (REG_N_CALLS_CROSSED (REGNO (src)) == 0)
break;
if (call_used_regs [REGNO (dst)]
|| find_reg_fusage (p, CLOBBER, dst))
break;
}
else if (reg_set_p (src, PATTERN (p)))
break;
}
return 0;
}
void
regmove_optimize (f, nregs, regmove_dump_file)
rtx f;
int nregs;
FILE *regmove_dump_file;
{
int old_max_uid = get_max_uid ();
rtx insn;
struct match match;
int pass;
int i;
rtx copy_src, copy_dst;
/* Find out where a potential flags register is live, and so that we
can supress some optimizations in those zones. */
mark_flags_life_zones (discover_flags_reg ());
regno_src_regno = (int *)alloca (sizeof *regno_src_regno * nregs);
for (i = nregs; --i >= 0; ) regno_src_regno[i] = -1;
regmove_bb_head = (int *)alloca (sizeof (int) * (old_max_uid + 1));
for (i = old_max_uid; i >= 0; i--) regmove_bb_head[i] = -1;
for (i = 0; i < n_basic_blocks; i++)
regmove_bb_head[INSN_UID (BLOCK_HEAD (i))] = i;
/* A forward/backward pass. Replace output operands with input operands. */
loop_depth = 1;
for (pass = 0; pass <= 2; pass++)
{
if (! flag_regmove && pass >= flag_expensive_optimizations)
return;
if (regmove_dump_file)
fprintf (regmove_dump_file, "Starting %s pass...\n",
pass ? "backward" : "forward");
for (insn = pass ? get_last_insn () : f; insn;
insn = pass ? PREV_INSN (insn) : NEXT_INSN (insn))
{
rtx set;
int op_no, match_no;
if (GET_CODE (insn) == NOTE)
{
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
loop_depth++;
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
loop_depth--;
}
set = single_set (insn);
if (! set)
continue;
if (flag_expensive_optimizations && ! pass
&& (GET_CODE (SET_SRC (set)) == SIGN_EXTEND
|| GET_CODE (SET_SRC (set)) == ZERO_EXTEND)
&& GET_CODE (XEXP (SET_SRC (set), 0)) == REG
&& GET_CODE (SET_DEST(set)) == REG)
optimize_reg_copy_3 (insn, SET_DEST (set), SET_SRC (set));
if (flag_expensive_optimizations && ! pass
&& GET_CODE (SET_SRC (set)) == REG
&& GET_CODE (SET_DEST(set)) == REG)
{
/* If this is a register-register copy where SRC is not dead,
see if we can optimize it. If this optimization succeeds,
it will become a copy where SRC is dead. */
if ((find_reg_note (insn, REG_DEAD, SET_SRC (set))
|| optimize_reg_copy_1 (insn, SET_DEST (set), SET_SRC (set)))
&& REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER)
{
/* Similarly for a pseudo-pseudo copy when SRC is dead. */
if (REGNO (SET_SRC (set)) >= FIRST_PSEUDO_REGISTER)
optimize_reg_copy_2 (insn, SET_DEST (set), SET_SRC (set));
if (regno_src_regno[REGNO (SET_DEST (set))] < 0
&& SET_SRC (set) != SET_DEST (set))
{
int srcregno = REGNO (SET_SRC(set));
if (regno_src_regno[srcregno] >= 0)
srcregno = regno_src_regno[srcregno];
regno_src_regno[REGNO (SET_DEST (set))] = srcregno;
}
}
}
if (! flag_regmove)
continue;
#ifdef REGISTER_CONSTRAINTS
if (! find_matches (insn, &match))
continue;
/* Now scan through the operands looking for a source operand
which is supposed to match the destination operand.
Then scan forward for an instruction which uses the dest
operand.
If it dies there, then replace the dest in both operands with
the source operand. */
for (op_no = 0; op_no < recog_n_operands; op_no++)
{
rtx src, dst, src_subreg;
enum reg_class src_class, dst_class;
match_no = match.with[op_no];
/* Nothing to do if the two operands aren't supposed to match. */
if (match_no < 0)
continue;
src = recog_operand[op_no];
dst = recog_operand[match_no];
if (GET_CODE (src) != REG)
continue;
src_subreg = src;
if (GET_CODE (dst) == SUBREG
&& GET_MODE_SIZE (GET_MODE (dst))
>= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dst))))
{
src_subreg
= gen_rtx_SUBREG (GET_MODE (SUBREG_REG (dst)),
src, SUBREG_WORD (dst));
dst = SUBREG_REG (dst);
}
if (GET_CODE (dst) != REG
|| REGNO (dst) < FIRST_PSEUDO_REGISTER)
continue;
if (REGNO (src) < FIRST_PSEUDO_REGISTER)
{
if (match.commutative[op_no] < op_no)
regno_src_regno[REGNO (dst)] = REGNO (src);
continue;
}
if (REG_LIVE_LENGTH (REGNO (src)) < 0)
continue;
/* op_no/src must be a read-only operand, and
match_operand/dst must be a write-only operand. */
if (match.use[op_no] != READ
|| match.use[match_no] != WRITE)
continue;
if (match.early_clobber[match_no]
&& count_occurrences (PATTERN (insn), src) > 1)
continue;
/* Make sure match_operand is the destination. */
if (recog_operand[match_no] != SET_DEST (set))
continue;
/* If the operands already match, then there is nothing to do. */
/* But in the commutative case, we might find a better match. */
if (operands_match_p (src, dst)
|| (match.commutative[op_no] >= 0
&& operands_match_p (recog_operand[match.commutative
[op_no]], dst)
&& (replacement_quality (recog_operand[match.commutative
[op_no]])
>= replacement_quality (src))))
continue;
src_class = reg_preferred_class (REGNO (src));
dst_class = reg_preferred_class (REGNO (dst));
if (! regclass_compatible_p (src_class, dst_class))
continue;
if (fixup_match_1 (insn, set, src, src_subreg, dst, pass,
op_no, match_no,
regmove_dump_file))
break;
}
}
}
/* A backward pass. Replace input operands with output operands. */
if (regmove_dump_file)
fprintf (regmove_dump_file, "Starting backward pass...\n");
loop_depth = 1;
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
{
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
loop_depth++;
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
loop_depth--;
}
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
int op_no, match_no;
int success = 0;
if (! find_matches (insn, &match))
continue;
/* Now scan through the operands looking for a destination operand
which is supposed to match a source operand.
Then scan backward for an instruction which sets the source
operand. If safe, then replace the source operand with the
dest operand in both instructions. */
copy_src = NULL_RTX;
copy_dst = NULL_RTX;
for (op_no = 0; op_no < recog_n_operands; op_no++)
{
rtx set, p, src, dst;
rtx src_note, dst_note;
int num_calls = 0;
enum reg_class src_class, dst_class;
int length;
match_no = match.with[op_no];
/* Nothing to do if the two operands aren't supposed to match. */
if (match_no < 0)
continue;
dst = recog_operand[match_no];
src = recog_operand[op_no];
if (GET_CODE (src) != REG)
continue;
if (GET_CODE (dst) != REG
|| REGNO (dst) < FIRST_PSEUDO_REGISTER
|| REG_LIVE_LENGTH (REGNO (dst)) < 0)
continue;
/* If the operands already match, then there is nothing to do. */
if (operands_match_p (src, dst)
|| (match.commutative[op_no] >= 0
&& operands_match_p (recog_operand[match.commutative[op_no]], dst)))
continue;
set = single_set (insn);
if (! set)
continue;
/* match_no/dst must be a write-only operand, and
operand_operand/src must be a read-only operand. */
if (match.use[op_no] != READ
|| match.use[match_no] != WRITE)
continue;
if (match.early_clobber[match_no]
&& count_occurrences (PATTERN (insn), src) > 1)
continue;
/* Make sure match_no is the destination. */
if (recog_operand[match_no] != SET_DEST (set))
continue;
if (REGNO (src) < FIRST_PSEUDO_REGISTER)
{
if (GET_CODE (SET_SRC (set)) == PLUS
&& GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT
&& XEXP (SET_SRC (set), 0) == src
&& fixup_match_2 (insn, dst, src,
XEXP (SET_SRC (set), 1),
regmove_dump_file))
break;
continue;
}
src_class = reg_preferred_class (REGNO (src));
dst_class = reg_preferred_class (REGNO (dst));
if (! regclass_compatible_p (src_class, dst_class))
{
if (!copy_src)
{
copy_src = src;
copy_dst = dst;
}
continue;
}
/* Can not modify an earlier insn to set dst if this insn
uses an old value in the source. */
if (reg_overlap_mentioned_p (dst, SET_SRC (set)))
{
if (!copy_src)
{
copy_src = src;
copy_dst = dst;
}
continue;
}
if (! (src_note = find_reg_note (insn, REG_DEAD, src)))
{
if (!copy_src)
{
copy_src = src;
copy_dst = dst;
}
continue;
}
/* If src is set once in a different basic block,
and is set equal to a constant, then do not use
it for this optimization, as this would make it
no longer equivalent to a constant. */
if (reg_is_remote_constant_p (src, insn, f))
{
if (!copy_src)
{
copy_src = src;
copy_dst = dst;
}
continue;
}
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Could fix operand %d of insn %d matching operand %d.\n",
op_no, INSN_UID (insn), match_no);
/* Scan backward to find the first instruction that uses
the input operand. If the operand is set here, then
replace it in both instructions with match_no. */
for (length = 0, p = PREV_INSN (insn); p; p = PREV_INSN (p))
{
rtx pset;
if (GET_CODE (p) == CODE_LABEL
|| GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
(REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if
it is inside an EH region. There is no easy way to tell,
so we just always break when we see a CALL_INSN if
flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (p) == CALL_INSN)
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
length++;
/* ??? See if all of SRC is set in P. This test is much
more conservative than it needs to be. */
pset = single_set (p);
if (pset && SET_DEST (pset) == src)
{
/* We use validate_replace_rtx, in case there
are multiple identical source operands. All of
them have to be changed at the same time. */
if (validate_replace_rtx (src, dst, insn))
{
if (validate_change (p, &SET_DEST (pset),
dst, 0))
success = 1;
else
{
/* Change all source operands back.
This modifies the dst as a side-effect. */
validate_replace_rtx (dst, src, insn);
/* Now make sure the dst is right. */
validate_change (insn,
recog_operand_loc[match_no],
dst, 0);
}
}
break;
}
if (reg_overlap_mentioned_p (src, PATTERN (p))
|| reg_overlap_mentioned_p (dst, PATTERN (p)))
break;
/* If we have passed a call instruction, and the
pseudo-reg DST is not already live across a call,
then don't perform the optimization. */
if (GET_CODE (p) == CALL_INSN)
{
num_calls++;
if (REG_N_CALLS_CROSSED (REGNO (dst)) == 0)
break;
}
}
if (success)
{
int dstno, srcno;
/* Remove the death note for SRC from INSN. */
remove_note (insn, src_note);
/* Move the death note for SRC to P if it is used
there. */
if (reg_overlap_mentioned_p (src, PATTERN (p)))
{
XEXP (src_note, 1) = REG_NOTES (p);
REG_NOTES (p) = src_note;
}
/* If there is a REG_DEAD note for DST on P, then remove
it, because DST is now set there. */
if ((dst_note = find_reg_note (p, REG_DEAD, dst)))
remove_note (p, dst_note);
dstno = REGNO (dst);
srcno = REGNO (src);
REG_N_SETS (dstno)++;
REG_N_SETS (srcno)--;
REG_N_CALLS_CROSSED (dstno) += num_calls;
REG_N_CALLS_CROSSED (srcno) -= num_calls;
REG_LIVE_LENGTH (dstno) += length;
if (REG_LIVE_LENGTH (srcno) >= 0)
{
REG_LIVE_LENGTH (srcno) -= length;
/* REG_LIVE_LENGTH is only an approximation after
combine if sched is not run, so make sure that we
still have a reasonable value. */
if (REG_LIVE_LENGTH (srcno) < 2)
REG_LIVE_LENGTH (srcno) = 2;
}
/* We assume that a register is used exactly once per
insn in the updates above. If this is not correct,
no great harm is done. */
REG_N_REFS (dstno) += 2 * loop_depth;
REG_N_REFS (srcno) -= 2 * loop_depth;
/* If that was the only time src was set,
and src was not live at the start of the
function, we know that we have no more
references to src; clear REG_N_REFS so it
won't make reload do any work. */
if (REG_N_SETS (REGNO (src)) == 0
&& ! regno_uninitialized (REGNO (src)))
REG_N_REFS (REGNO (src)) = 0;
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Fixed operand %d of insn %d matching operand %d.\n",
op_no, INSN_UID (insn), match_no);
break;
}
}
/* If we weren't able to replace any of the alternatives, try an
alternative appoach of copying the source to the destination. */
if (!success && copy_src != NULL_RTX)
copy_src_to_dest (insn, copy_src, copy_dst, loop_depth,
old_max_uid);
}
}
#endif /* REGISTER_CONSTRAINTS */
/* In fixup_match_1, some insns may have been inserted after basic block
ends. Fix that here. */
for (i = 0; i < n_basic_blocks; i++)
{
rtx end = BLOCK_END (i);
rtx new = end;
rtx next = NEXT_INSN (new);
while (next != 0 && INSN_UID (next) >= old_max_uid
&& (i == n_basic_blocks - 1 || BLOCK_HEAD (i + 1) != next))
new = next, next = NEXT_INSN (new);
BLOCK_END (i) = new;
}
}
/* Returns nonzero if INSN's pattern has matching constraints for any operand.
Returns 0 if INSN can't be recognized, or if the alternative can't be
determined.
Initialize the info in MATCHP based on the constraints. */
static int
find_matches (insn, matchp)
rtx insn;
struct match *matchp;
{
int likely_spilled[MAX_RECOG_OPERANDS];
int op_no;
int any_matches = 0;
extract_insn (insn);
if (! constrain_operands (0))
return 0;
/* Must initialize this before main loop, because the code for
the commutative case may set matches for operands other than
the current one. */
for (op_no = recog_n_operands; --op_no >= 0; )
matchp->with[op_no] = matchp->commutative[op_no] = -1;
for (op_no = 0; op_no < recog_n_operands; op_no++)
{
const char *p;
char c;
int i = 0;
p = recog_constraints[op_no];
likely_spilled[op_no] = 0;
matchp->use[op_no] = READ;
matchp->early_clobber[op_no] = 0;
if (*p == '=')
matchp->use[op_no] = WRITE;
else if (*p == '+')
matchp->use[op_no] = READWRITE;
for (;*p && i < which_alternative; p++)
if (*p == ',')
i++;
while ((c = *p++) != '\0' && c != ',')
switch (c)
{
case '=':
break;
case '+':
break;
case '&':
matchp->early_clobber[op_no] = 1;
break;
case '%':
matchp->commutative[op_no] = op_no + 1;
matchp->commutative[op_no + 1] = op_no;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
c -= '0';
if (c < op_no && likely_spilled[(unsigned char) c])
break;
matchp->with[op_no] = c;
any_matches = 1;
if (matchp->commutative[op_no] >= 0)
matchp->with[matchp->commutative[op_no]] = c;
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'h':
case 'j': case 'k': case 'l': case 'p': case 'q': case 't': case 'u':
case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
case 'C': case 'D': case 'W': case 'Y': case 'Z':
if (CLASS_LIKELY_SPILLED_P (REG_CLASS_FROM_LETTER ((unsigned char)c)))
likely_spilled[op_no] = 1;
break;
}
}
return any_matches;
}
/* Try to replace output operand DST in SET, with input operand SRC. SET is
the only set in INSN. INSN has just been recgnized and constrained.
SRC is operand number OPERAND_NUMBER in INSN.
DST is operand number MATCH_NUMBER in INSN.
If BACKWARD is nonzero, we have been called in a backward pass.
Return nonzero for success. */
static int
fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
match_number, regmove_dump_file)
rtx insn, set, src, src_subreg, dst;
int backward, operand_number, match_number;
FILE *regmove_dump_file;
{
rtx p;
rtx post_inc = 0, post_inc_set = 0, search_end = 0;
int success = 0;
int num_calls = 0, s_num_calls = 0;
enum rtx_code code = NOTE;
HOST_WIDE_INT insn_const, newconst;
rtx overlap = 0; /* need to move insn ? */
rtx src_note = find_reg_note (insn, REG_DEAD, src), dst_note;
int length, s_length, true_loop_depth;
+ /* If SRC is marked as unchanging, we may not change it.
+ ??? Maybe we could get better code by removing the unchanging bit
+ instead, and changing it back if we don't succeed? */
+ if (RTX_UNCHANGING_P (src))
+ return 0;
+
if (! src_note)
{
/* Look for (set (regX) (op regA constX))
(set (regY) (op regA constY))
and change that to
(set (regA) (op regA constX)).
(set (regY) (op regA constY-constX)).
This works for add and shift operations, if
regA is dead after or set by the second insn. */
code = GET_CODE (SET_SRC (set));
if ((code == PLUS || code == LSHIFTRT
|| code == ASHIFT || code == ASHIFTRT)
&& XEXP (SET_SRC (set), 0) == src
&& GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
insn_const = INTVAL (XEXP (SET_SRC (set), 1));
- else if (! stable_but_for_p (SET_SRC (set), src, dst))
+ else if (! stable_and_no_regs_but_for_p (SET_SRC (set), src, dst))
return 0;
else
/* We might find a src_note while scanning. */
code = NOTE;
}
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Could fix operand %d of insn %d matching operand %d.\n",
operand_number, INSN_UID (insn), match_number);
/* If SRC is equivalent to a constant set in a different basic block,
then do not use it for this optimization. We want the equivalence
so that if we have to reload this register, we can reload the
constant, rather than extending the lifespan of the register. */
if (reg_is_remote_constant_p (src, insn, get_insns ()))
return 0;
/* Scan forward to find the next instruction that
uses the output operand. If the operand dies here,
then replace it in both instructions with
operand_number. */
for (length = s_length = 0, p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
|| (GET_CODE (p) == NOTE
&& (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
break;
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info (REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if it is
inside an EH region. There is no easy way to tell, so we just
always break when we see a CALL_INSN if flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (p) == CALL_INSN)
break;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
length++;
if (src_note)
s_length++;
if (reg_set_p (src, p) || reg_set_p (dst, p)
|| (GET_CODE (PATTERN (p)) == USE
&& reg_overlap_mentioned_p (src, XEXP (PATTERN (p), 0))))
break;
/* See if all of DST dies in P. This test is
slightly more conservative than it needs to be. */
if ((dst_note = find_regno_note (p, REG_DEAD, REGNO (dst)))
&& (GET_MODE (XEXP (dst_note, 0)) == GET_MODE (dst)))
{
if (! src_note)
{
rtx q;
rtx set2;
/* If an optimization is done, the value of SRC while P
is executed will be changed. Check that this is OK. */
if (reg_overlap_mentioned_p (src, PATTERN (p)))
break;
for (q = p; q; q = NEXT_INSN (q))
{
if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN
|| (GET_CODE (q) == NOTE
&& (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END)))
{
q = 0;
break;
}
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
(REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if
it is inside an EH region. There is no easy way to tell,
so we just always break when we see a CALL_INSN if
flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (q) == CALL_INSN)
{
q = 0;
break;
}
if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
continue;
if (reg_overlap_mentioned_p (src, PATTERN (q))
|| reg_set_p (src, q))
break;
}
if (q)
set2 = single_set (q);
if (! q || ! set2 || GET_CODE (SET_SRC (set2)) != code
|| XEXP (SET_SRC (set2), 0) != src
|| GET_CODE (XEXP (SET_SRC (set2), 1)) != CONST_INT
|| (SET_DEST (set2) != src
&& ! find_reg_note (q, REG_DEAD, src)))
{
/* If this is a PLUS, we can still save a register by doing
src += insn_const;
P;
src -= insn_const; .
This also gives opportunities for subsequent
optimizations in the backward pass, so do it there. */
if (code == PLUS && backward
/* Don't do this if we can likely tie DST to SET_DEST
of P later; we can't do this tying here if we got a
hard register. */
&& ! (dst_note && ! REG_N_CALLS_CROSSED (REGNO (dst))
&& single_set (p)
&& GET_CODE (SET_DEST (single_set (p))) == REG
&& (REGNO (SET_DEST (single_set (p)))
< FIRST_PSEUDO_REGISTER))
/* We may only emit an insn directly after P if we
are not in the shadow of a live flags register. */
&& GET_MODE (p) == VOIDmode)
{
search_end = q;
q = insn;
set2 = set;
newconst = -insn_const;
code = MINUS;
}
else
break;
}
else
{
newconst = INTVAL (XEXP (SET_SRC (set2), 1)) - insn_const;
/* Reject out of range shifts. */
if (code != PLUS
&& (newconst < 0
|| (newconst
>= GET_MODE_BITSIZE (GET_MODE (SET_SRC (set2))))))
break;
if (code == PLUS)
{
post_inc = q;
if (SET_DEST (set2) != src)
post_inc_set = set2;
}
}
/* We use 1 as last argument to validate_change so that all
changes are accepted or rejected together by apply_change_group
when it is called by validate_replace_rtx . */
validate_change (q, &XEXP (SET_SRC (set2), 1),
GEN_INT (newconst), 1);
}
validate_change (insn, recog_operand_loc[match_number], src, 1);
if (validate_replace_rtx (dst, src_subreg, p))
success = 1;
break;
}
if (reg_overlap_mentioned_p (dst, PATTERN (p)))
break;
if (! src_note && reg_overlap_mentioned_p (src, PATTERN (p)))
{
/* INSN was already checked to be movable when
we found no REG_DEAD note for src on it. */
overlap = p;
src_note = find_reg_note (p, REG_DEAD, src);
}
/* If we have passed a call instruction, and the pseudo-reg SRC is not
already live across a call, then don't perform the optimization. */
if (GET_CODE (p) == CALL_INSN)
{
if (REG_N_CALLS_CROSSED (REGNO (src)) == 0)
break;
num_calls++;
if (src_note)
s_num_calls++;
}
}
if (! success)
return 0;
true_loop_depth = backward ? 2 - loop_depth : loop_depth;
/* Remove the death note for DST from P. */
remove_note (p, dst_note);
if (code == MINUS)
{
post_inc = emit_insn_after (copy_rtx (PATTERN (insn)), p);
if ((HAVE_PRE_INCREMENT || HAVE_PRE_DECREMENT)
&& search_end
&& try_auto_increment (search_end, post_inc, 0, src, newconst, 1))
post_inc = 0;
validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (insn_const), 0);
REG_N_SETS (REGNO (src))++;
REG_N_REFS (REGNO (src)) += true_loop_depth;
REG_LIVE_LENGTH (REGNO (src))++;
}
if (overlap)
{
/* The lifetime of src and dest overlap,
but we can change this by moving insn. */
rtx pat = PATTERN (insn);
if (src_note)
remove_note (overlap, src_note);
if ((HAVE_POST_INCREMENT || HAVE_POST_DECREMENT)
&& code == PLUS
&& try_auto_increment (overlap, insn, 0, src, insn_const, 0))
insn = overlap;
else
{
rtx notes = REG_NOTES (insn);
emit_insn_after_with_line_notes (pat, PREV_INSN (p), insn);
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
/* emit_insn_after_with_line_notes has no
return value, so search for the new insn. */
for (insn = p; PATTERN (insn) != pat; )
insn = PREV_INSN (insn);
REG_NOTES (insn) = notes;
}
}
/* Sometimes we'd generate src = const; src += n;
if so, replace the instruction that set src
in the first place. */
if (! overlap && (code == PLUS || code == MINUS))
{
rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
rtx q, set2;
int num_calls2 = 0, s_length2 = 0;
if (note && CONSTANT_P (XEXP (note, 0)))
{
for (q = PREV_INSN (insn); q; q = PREV_INSN(q))
{
if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN
|| (GET_CODE (q) == NOTE
&& (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END)))
{
q = 0;
break;
}
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
(REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if
it is inside an EH region. There is no easy way to tell,
so we just always break when we see a CALL_INSN if
flag_exceptions is nonzero. */
if (flag_exceptions && GET_CODE (q) == CALL_INSN)
{
q = 0;
break;
}
if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
continue;
s_length2++;
if (reg_set_p (src, q))
{
set2 = single_set (q);
break;
}
if (reg_overlap_mentioned_p (src, PATTERN (q)))
{
q = 0;
break;
}
if (GET_CODE (p) == CALL_INSN)
num_calls2++;
}
if (q && set2 && SET_DEST (set2) == src && CONSTANT_P (SET_SRC (set2))
&& validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0))
{
PUT_CODE (q, NOTE);
NOTE_LINE_NUMBER (q) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (q) = 0;
REG_N_SETS (REGNO (src))--;
REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2;
REG_N_REFS (REGNO (src)) -= true_loop_depth;
REG_LIVE_LENGTH (REGNO (src)) -= s_length2;
insn_const = 0;
}
}
}
if ((HAVE_PRE_INCREMENT || HAVE_PRE_DECREMENT)
&& (code == PLUS || code == MINUS) && insn_const
&& try_auto_increment (p, insn, 0, src, insn_const, 1))
insn = p;
else if ((HAVE_POST_INCREMENT || HAVE_POST_DECREMENT)
&& post_inc
&& try_auto_increment (p, post_inc, post_inc_set, src, newconst, 0))
post_inc = 0;
/* If post_inc still prevails, try to find an
insn where it can be used as a pre-in/decrement.
If code is MINUS, this was already tried. */
if (post_inc && code == PLUS
/* Check that newconst is likely to be usable
in a pre-in/decrement before starting the search. */
&& ((HAVE_PRE_INCREMENT && newconst > 0 && newconst <= MOVE_MAX)
|| (HAVE_PRE_DECREMENT && newconst < 0 && newconst >= -MOVE_MAX))
&& exact_log2 (newconst))
{
rtx q, inc_dest;
inc_dest = post_inc_set ? SET_DEST (post_inc_set) : src;
for (q = post_inc; (q = NEXT_INSN (q)); )
{
if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN
|| (GET_CODE (q) == NOTE
&& (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END)))
break;
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info (REG_DEAD/basic_block_live_at_start).
A CALL_INSN might be the last insn of a basic block, if it
is inside an EH region. There is no easy way to tell so we
just always break when we see a CALL_INSN if flag_exceptions
is nonzero. */
if (flag_exceptions && GET_CODE (q) == CALL_INSN)
break;
if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
continue;
if (src != inc_dest && (reg_overlap_mentioned_p (src, PATTERN (q))
|| reg_set_p (src, q)))
break;
if (reg_set_p (inc_dest, q))
break;
if (reg_overlap_mentioned_p (inc_dest, PATTERN (q)))
{
try_auto_increment (q, post_inc,
post_inc_set, inc_dest, newconst, 1);
break;
}
}
}
/* Move the death note for DST to INSN if it is used
there. */
if (reg_overlap_mentioned_p (dst, PATTERN (insn)))
{
XEXP (dst_note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = dst_note;
}
if (src_note)
{
/* Move the death note for SRC from INSN to P. */
if (! overlap)
remove_note (insn, src_note);
XEXP (src_note, 1) = REG_NOTES (p);
REG_NOTES (p) = src_note;
REG_N_CALLS_CROSSED (REGNO (src)) += s_num_calls;
}
REG_N_SETS (REGNO (src))++;
REG_N_SETS (REGNO (dst))--;
REG_N_CALLS_CROSSED (REGNO (dst)) -= num_calls;
REG_LIVE_LENGTH (REGNO (src)) += s_length;
if (REG_LIVE_LENGTH (REGNO (dst)) >= 0)
{
REG_LIVE_LENGTH (REGNO (dst)) -= length;
/* REG_LIVE_LENGTH is only an approximation after
combine if sched is not run, so make sure that we
still have a reasonable value. */
if (REG_LIVE_LENGTH (REGNO (dst)) < 2)
REG_LIVE_LENGTH (REGNO (dst)) = 2;
}
/* We assume that a register is used exactly once per
insn in the updates above. If this is not correct,
no great harm is done. */
REG_N_REFS (REGNO (src)) += 2 * true_loop_depth;
REG_N_REFS (REGNO (dst)) -= 2 * true_loop_depth;
/* If that was the only time dst was set,
and dst was not live at the start of the
function, we know that we have no more
references to dst; clear REG_N_REFS so it
won't make reload do any work. */
if (REG_N_SETS (REGNO (dst)) == 0
&& ! regno_uninitialized (REGNO (dst)))
REG_N_REFS (REGNO (dst)) = 0;
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Fixed operand %d of insn %d matching operand %d.\n",
operand_number, INSN_UID (insn), match_number);
return 1;
}
-/* return nonzero if X is stable but for mentioning SRC or mentioning /
- changing DST . If in doubt, presume it is unstable. */
+/* return nonzero if X is stable and mentions no regsiters but for
+ mentioning SRC or mentioning / changing DST . If in doubt, presume
+ it is unstable.
+ The rationale is that we want to check if we can move an insn easily
+ while just paying attention to SRC and DST. A register is considered
+ stable if it has the RTX_UNCHANGING_P bit set, but that would still
+ leave the burden to update REG_DEAD / REG_UNUSED notes, so we don't
+ want any registers but SRC and DST. */
static int
-stable_but_for_p (x, src, dst)
+stable_and_no_regs_but_for_p (x, src, dst)
rtx x, src, dst;
{
RTX_CODE code = GET_CODE (x);
switch (GET_RTX_CLASS (code))
{
case '<': case '1': case 'c': case '2': case 'b': case '3':
{
int i;
char *fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- if (fmt[i] == 'e' && ! stable_but_for_p (XEXP (x, i), src, dst))
+ if (fmt[i] == 'e'
+ && ! stable_and_no_regs_but_for_p (XEXP (x, i), src, dst))
return 0;
return 1;
}
case 'o':
- if (x == src || x == dst)
- return 1;
+ if (code == REG)
+ return x == src || x == dst;
+ /* If this is a MEM, look inside - there might be a register hidden in
+ the address of an unchanging MEM. */
+ if (code == MEM
+ && ! stable_and_no_regs_but_for_p (XEXP (x, 0), src, dst))
+ return 0;
/* fall through */
default:
return ! rtx_unstable_p (x);
}
}
/* Test if regmove seems profitable for this target. Regmove is useful only
if some common patterns are two address, i.e. require matching constraints,
so we check that condition here. */
int
regmove_profitable_p ()
{
#ifdef REGISTER_CONSTRAINTS
struct match match;
enum machine_mode mode;
optab tstoptab = add_optab;
do /* check add_optab and ashl_optab */
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
int icode = (int) tstoptab->handlers[(int) mode].insn_code;
rtx reg0, reg1, reg2, pat;
int i;
if (GET_MODE_BITSIZE (mode) < 32 || icode == CODE_FOR_nothing)
continue;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], i))
break;
if (i + 2 >= FIRST_PSEUDO_REGISTER)
break;
reg0 = gen_rtx_REG (insn_operand_mode[icode][0], i);
reg1 = gen_rtx_REG (insn_operand_mode[icode][1], i + 1);
reg2 = gen_rtx_REG (insn_operand_mode[icode][2], i + 2);
if (! (*insn_operand_predicate[icode][0]) (reg0, VOIDmode)
|| ! (*insn_operand_predicate[icode][1]) (reg1, VOIDmode)
|| ! (*insn_operand_predicate[icode][2]) (reg2, VOIDmode))
break;
pat = GEN_FCN (icode) (reg0, reg1, reg2);
if (! pat)
continue;
if (GET_CODE (pat) == SEQUENCE)
pat = XVECEXP (pat, 0, XVECLEN (pat, 0) - 1);
else
pat = make_insn_raw (pat);
if (! single_set (pat)
|| GET_CODE (SET_SRC (single_set (pat))) != tstoptab->code)
/* Unexpected complexity; don't need to handle this unless
we find a machine where this occurs and regmove should
be enabled. */
break;
if (find_matches (pat, &match))
return 1;
break;
}
while (tstoptab != ashl_optab && (tstoptab = ashl_optab, 1));
#endif /* REGISTER_CONSTRAINTS */
return 0;
}
Index: head/contrib/gcc/sched.c
===================================================================
--- head/contrib/gcc/sched.c (revision 52750)
+++ head/contrib/gcc/sched.c (revision 52751)
@@ -1,4470 +1,4470 @@
/* Instruction scheduling pass.
Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com)
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. */
/* Instruction scheduling pass.
This pass implements list scheduling within basic blocks. It is
run after flow analysis, but before register allocation. The
scheduler works as follows:
We compute insn priorities based on data dependencies. Flow
analysis only creates a fraction of the data-dependencies we must
observe: namely, only those dependencies which the combiner can be
expected to use. For this pass, we must therefore create the
remaining dependencies we need to observe: register dependencies,
memory dependencies, dependencies to keep function calls in order,
and the dependence between a conditional branch and the setting of
condition codes are all dealt with here.
The scheduler first traverses the data flow graph, starting with
the last instruction, and proceeding to the first, assigning
values to insn_priority as it goes. This sorts the instructions
topologically by data dependence.
Once priorities have been established, we order the insns using
list scheduling. This works as follows: starting with a list of
all the ready insns, and sorted according to priority number, we
schedule the insn from the end of the list by placing its
predecessors in the list according to their priority order. We
consider this insn scheduled by setting the pointer to the "end" of
the list to point to the previous insn. When an insn has no
predecessors, we either queue it until sufficient time has elapsed
or add it to the ready list. As the instructions are scheduled or
when stalls are introduced, the queue advances and dumps insns into
the ready list. When all insns down to the lowest priority have
been scheduled, the critical path of the basic block has been made
as short as possible. The remaining insns are then scheduled in
remaining slots.
Function unit conflicts are resolved during reverse list scheduling
by tracking the time when each insn is committed to the schedule
and from that, the time the function units it uses must be free.
As insns on the ready list are considered for scheduling, those
that would result in a blockage of the already committed insns are
queued until no blockage will result. Among the remaining insns on
the ready list to be considered, the first one with the largest
potential for causing a subsequent blockage is chosen.
The following list shows the order in which we want to break ties
among insns in the ready list:
1. choose insn with lowest conflict cost, ties broken by
2. choose insn with the longest path to end of bb, ties broken by
3. choose insn that kills the most registers, ties broken by
4. choose insn that conflicts with the most ready insns, or finally
5. choose insn with lowest UID.
Memory references complicate matters. Only if we can be certain
that memory references are not part of the data dependency graph
(via true, anti, or output dependence), can we move operations past
memory references. To first approximation, reads can be done
independently, while writes introduce dependencies. Better
approximations will yield fewer dependencies.
Dependencies set up by memory references are treated in exactly the
same way as other dependencies, by using LOG_LINKS.
Having optimized the critical path, we may have also unduly
extended the lifetimes of some registers. If an operation requires
that constants be loaded into registers, it is certainly desirable
to load those constants as early as necessary, but no earlier.
I.e., it will not do to load up a bunch of registers at the
beginning of a basic block only to use them at the end, if they
could be loaded later, since this may result in excessive register
utilization.
Note that since branches are never in basic blocks, but only end
basic blocks, this pass will not do any branch scheduling. But
that is ok, since we can use GNU's delayed branch scheduling
pass to take care of this case.
Also note that no further optimizations based on algebraic identities
are performed, so this pass would be a good one to perform instruction
splitting, such as breaking up a multiply instruction into shifts
and adds where that is profitable.
Given the memory aliasing analysis that this pass should perform,
it should be possible to remove redundant stores to memory, and to
load values from registers instead of hitting memory.
This pass must update information that subsequent passes expect to be
correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths,
reg_n_calls_crossed, and reg_live_length. Also, BLOCK_HEAD,
BLOCK_END.
The information in the line number notes is carefully retained by
this pass. Notes that refer to the starting and ending of
exception regions are also carefully retained by this pass. All
other NOTE insns are grouped in their same relative order at the
beginning of basic blocks that have been scheduled. */
#include "config.h"
#include "system.h"
#include "toplev.h"
#include "rtl.h"
#include "basic-block.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "insn-config.h"
#include "insn-attr.h"
#include "recog.h"
#ifndef INSN_SCHEDULING
void
schedule_insns (dump_file)
FILE *dump_file ATTRIBUTE_UNUSED;
{
}
#else /* INSN_SCHEDULING -- rest of file */
extern char *reg_known_equiv_p;
extern rtx *reg_known_value;
/* Arrays set up by scheduling for the same respective purposes as
similar-named arrays set up by flow analysis. We work with these
arrays during the scheduling pass so we can compare values against
unscheduled code.
Values of these arrays are copied at the end of this pass into the
arrays set up by flow analysis. */
static int *sched_reg_n_calls_crossed;
static int *sched_reg_live_length;
/* Element N is the next insn that sets (hard or pseudo) register
N within the current basic block; or zero, if there is no
such insn. Needed for new registers which may be introduced
by splitting insns. */
static rtx *reg_last_uses;
static rtx *reg_last_sets;
static regset reg_pending_sets;
static int reg_pending_sets_all;
/* Vector indexed by INSN_UID giving the original ordering of the insns. */
static int *insn_luid;
#define INSN_LUID(INSN) (insn_luid[INSN_UID (INSN)])
/* Vector indexed by INSN_UID giving each instruction a priority. */
static int *insn_priority;
#define INSN_PRIORITY(INSN) (insn_priority[INSN_UID (INSN)])
static short *insn_costs;
#define INSN_COST(INSN) insn_costs[INSN_UID (INSN)]
/* Vector indexed by INSN_UID giving an encoding of the function units
used. */
static short *insn_units;
#define INSN_UNIT(INSN) insn_units[INSN_UID (INSN)]
/* Vector indexed by INSN_UID giving an encoding of the blockage range
function. The unit and the range are encoded. */
static unsigned int *insn_blockage;
#define INSN_BLOCKAGE(INSN) insn_blockage[INSN_UID (INSN)]
#define UNIT_BITS 5
#define BLOCKAGE_MASK ((1 << BLOCKAGE_BITS) - 1)
#define ENCODE_BLOCKAGE(U,R) \
((((U) << UNIT_BITS) << BLOCKAGE_BITS \
| MIN_BLOCKAGE_COST (R)) << BLOCKAGE_BITS \
| MAX_BLOCKAGE_COST (R))
#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS))
#define BLOCKAGE_RANGE(B) \
(((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \
| ((B) & BLOCKAGE_MASK))
/* Encodings of the `<name>_unit_blockage_range' function. */
#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2))
#define MAX_BLOCKAGE_COST(R) ((R) & ((1 << (HOST_BITS_PER_INT / 2)) - 1))
#define DONE_PRIORITY -1
#define MAX_PRIORITY 0x7fffffff
#define TAIL_PRIORITY 0x7ffffffe
#define LAUNCH_PRIORITY 0x7f000001
#define DONE_PRIORITY_P(INSN) (INSN_PRIORITY (INSN) < 0)
#define LOW_PRIORITY_P(INSN) ((INSN_PRIORITY (INSN) & 0x7f000000) == 0)
/* Vector indexed by INSN_UID giving number of insns referring to this insn. */
static int *insn_ref_count;
#define INSN_REF_COUNT(INSN) (insn_ref_count[INSN_UID (INSN)])
/* Vector indexed by INSN_UID giving line-number note in effect for each
insn. For line-number notes, this indicates whether the note may be
reused. */
static rtx *line_note;
#define LINE_NOTE(INSN) (line_note[INSN_UID (INSN)])
/* Vector indexed by basic block number giving the starting line-number
for each basic block. */
static rtx *line_note_head;
/* List of important notes we must keep around. This is a pointer to the
last element in the list. */
static rtx note_list;
/* Regsets telling whether a given register is live or dead before the last
scheduled insn. Must scan the instructions once before scheduling to
determine what registers are live or dead at the end of the block. */
static regset bb_dead_regs;
static regset bb_live_regs;
/* Regset telling whether a given register is live after the insn currently
being scheduled. Before processing an insn, this is equal to bb_live_regs
above. This is used so that we can find registers that are newly born/dead
after processing an insn. */
static regset old_live_regs;
/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns
during the initial scan and reused later. If there are not exactly as
many REG_DEAD notes in the post scheduled code as there were in the
prescheduled code then we trigger an abort because this indicates a bug. */
static rtx dead_notes;
/* Queues, etc. */
/* An instruction is ready to be scheduled when all insns following it
have already been scheduled. It is important to ensure that all
insns which use its result will not be executed until its result
has been computed. An insn is maintained in one of four structures:
(P) the "Pending" set of insns which cannot be scheduled until
their dependencies have been satisfied.
(Q) the "Queued" set of insns that can be scheduled when sufficient
time has passed.
(R) the "Ready" list of unscheduled, uncommitted insns.
(S) the "Scheduled" list of insns.
Initially, all insns are either "Pending" or "Ready" depending on
whether their dependencies are satisfied.
Insns move from the "Ready" list to the "Scheduled" list as they
are committed to the schedule. As this occurs, the insns in the
"Pending" list have their dependencies satisfied and move to either
the "Ready" list or the "Queued" set depending on whether
sufficient time has passed to make them ready. As time passes,
insns move from the "Queued" set to the "Ready" list. Insns may
move from the "Ready" list to the "Queued" set if they are blocked
due to a function unit conflict.
The "Pending" list (P) are the insns in the LOG_LINKS of the unscheduled
insns, i.e., those that are ready, queued, and pending.
The "Queued" set (Q) is implemented by the variable `insn_queue'.
The "Ready" list (R) is implemented by the variables `ready' and
`n_ready'.
The "Scheduled" list (S) is the new insn chain built by this pass.
The transition (R->S) is implemented in the scheduling loop in
`schedule_block' when the best insn to schedule is chosen.
The transition (R->Q) is implemented in `schedule_select' when an
insn is found to have a function unit conflict with the already
committed insns.
The transitions (P->R and P->Q) are implemented in `schedule_insn' as
insns move from the ready list to the scheduled list.
The transition (Q->R) is implemented at the top of the scheduling
loop in `schedule_block' as time passes or stalls are introduced. */
/* Implement a circular buffer to delay instructions until sufficient
time has passed. INSN_QUEUE_SIZE is a power of two larger than
MAX_BLOCKAGE and MAX_READY_COST computed by genattr.c. This is the
longest time an isnsn may be queued. */
static rtx insn_queue[INSN_QUEUE_SIZE];
static int q_ptr = 0;
static int q_size = 0;
#define NEXT_Q(X) (((X)+1) & (INSN_QUEUE_SIZE-1))
#define NEXT_Q_AFTER(X,C) (((X)+C) & (INSN_QUEUE_SIZE-1))
/* Vector indexed by INSN_UID giving the minimum clock tick at which
the insn becomes ready. This is used to note timing constraints for
insns in the pending list. */
static int *insn_tick;
#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)])
/* Data structure for keeping track of register information
during that register's life. */
struct sometimes
{
int regno;
int live_length;
int calls_crossed;
};
/* Forward declarations. */
static void add_dependence PROTO((rtx, rtx, enum reg_note));
static void remove_dependence PROTO((rtx, rtx));
static rtx find_insn_list PROTO((rtx, rtx));
static int insn_unit PROTO((rtx));
static unsigned int blockage_range PROTO((int, rtx));
static void clear_units PROTO((void));
static void prepare_unit PROTO((int));
static int actual_hazard_this_instance PROTO((int, int, rtx, int, int));
static void schedule_unit PROTO((int, rtx, int));
static int actual_hazard PROTO((int, rtx, int, int));
static int potential_hazard PROTO((int, rtx, int));
static int insn_cost PROTO((rtx, rtx, rtx));
static int priority PROTO((rtx));
static void free_pending_lists PROTO((void));
static void add_insn_mem_dependence PROTO((rtx *, rtx *, rtx, rtx));
static void flush_pending_lists PROTO((rtx, int));
static void sched_analyze_1 PROTO((rtx, rtx));
static void sched_analyze_2 PROTO((rtx, rtx));
static void sched_analyze_insn PROTO((rtx, rtx, rtx));
static int sched_analyze PROTO((rtx, rtx));
static void sched_note_set PROTO((rtx, int));
static int rank_for_schedule PROTO((const GENERIC_PTR, const GENERIC_PTR));
static void swap_sort PROTO((rtx *, int));
static void queue_insn PROTO((rtx, int));
static int birthing_insn_p PROTO((rtx));
static void adjust_priority PROTO((rtx));
static int schedule_insn PROTO((rtx, rtx *, int, int));
static int schedule_select PROTO((rtx *, int, int, FILE *));
static void create_reg_dead_note PROTO((rtx, rtx));
static void attach_deaths PROTO((rtx, rtx, int));
static void attach_deaths_insn PROTO((rtx));
static rtx unlink_notes PROTO((rtx, rtx));
static int new_sometimes_live PROTO((struct sometimes *, int, int));
static void finish_sometimes_live PROTO((struct sometimes *, int));
static rtx reemit_notes PROTO((rtx, rtx));
static void schedule_block PROTO((int, FILE *));
static void split_hard_reg_notes PROTO((rtx, rtx, rtx));
static void new_insn_dead_notes PROTO((rtx, rtx, rtx, rtx));
static void update_n_sets PROTO((rtx, int));
/* Main entry point of this file. */
void schedule_insns PROTO((FILE *));
#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X)))
/* Helper functions for instruction scheduling. */
/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the
LOG_LINKS of INSN, if not already there. DEP_TYPE indicates the type
of dependence that this link represents. */
static void
add_dependence (insn, elem, dep_type)
rtx insn;
rtx elem;
enum reg_note dep_type;
{
rtx link, next;
/* Don't depend an insn on itself. */
if (insn == elem)
return;
/* If elem is part of a sequence that must be scheduled together, then
make the dependence point to the last insn of the sequence.
When HAVE_cc0, it is possible for NOTEs to exist between users and
setters of the condition codes, so we must skip past notes here.
Otherwise, NOTEs are impossible here. */
next = NEXT_INSN (elem);
#ifdef HAVE_cc0
while (next && GET_CODE (next) == NOTE)
next = NEXT_INSN (next);
#endif
if (next && SCHED_GROUP_P (next)
&& GET_CODE (next) != CODE_LABEL)
{
/* Notes will never intervene here though, so don't bother checking
for them. */
/* We must reject CODE_LABELs, so that we don't get confused by one
that has LABEL_PRESERVE_P set, which is represented by the same
bit in the rtl as SCHED_GROUP_P. A CODE_LABEL can never be
SCHED_GROUP_P. */
while (NEXT_INSN (next) && SCHED_GROUP_P (NEXT_INSN (next))
&& GET_CODE (NEXT_INSN (next)) != CODE_LABEL)
next = NEXT_INSN (next);
/* Again, don't depend an insn on itself. */
if (insn == next)
return;
/* Make the dependence to NEXT, the last insn of the group, instead
of the original ELEM. */
elem = next;
}
/* Check that we don't already have this dependence. */
for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
if (XEXP (link, 0) == elem)
{
/* If this is a more restrictive type of dependence than the existing
one, then change the existing dependence to this type. */
if ((int) dep_type < (int) REG_NOTE_KIND (link))
PUT_REG_NOTE_KIND (link, dep_type);
return;
}
/* Might want to check one level of transitivity to save conses. */
link = rtx_alloc (INSN_LIST);
/* Insn dependency, not data dependency. */
PUT_REG_NOTE_KIND (link, dep_type);
XEXP (link, 0) = elem;
XEXP (link, 1) = LOG_LINKS (insn);
LOG_LINKS (insn) = link;
}
/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS
of INSN. Abort if not found. */
static void
remove_dependence (insn, elem)
rtx insn;
rtx elem;
{
rtx prev, link;
int found = 0;
for (prev = 0, link = LOG_LINKS (insn); link; link = XEXP (link, 1))
{
if (XEXP (link, 0) == elem)
{
RTX_INTEGRATED_P (link) = 1;
if (prev)
XEXP (prev, 1) = XEXP (link, 1);
else
LOG_LINKS (insn) = XEXP (link, 1);
found = 1;
}
else
prev = link;
}
if (! found)
abort ();
return;
}
#ifndef __GNUC__
#define __inline
#endif
/* Computation of memory dependencies. */
/* The *_insns and *_mems are paired lists. Each pending memory operation
will have a pointer to the MEM rtx on one list and a pointer to the
containing insn on the other list in the same place in the list. */
/* We can't use add_dependence like the old code did, because a single insn
may have multiple memory accesses, and hence needs to be on the list
once for each memory access. Add_dependence won't let you add an insn
to a list more than once. */
/* An INSN_LIST containing all insns with pending read operations. */
static rtx pending_read_insns;
/* An EXPR_LIST containing all MEM rtx's which are pending reads. */
static rtx pending_read_mems;
/* An INSN_LIST containing all insns with pending write operations. */
static rtx pending_write_insns;
/* An EXPR_LIST containing all MEM rtx's which are pending writes. */
static rtx pending_write_mems;
/* Indicates the combined length of the two pending lists. We must prevent
these lists from ever growing too large since the number of dependencies
produced is at least O(N*N), and execution time is at least O(4*N*N), as
a function of the length of these pending lists. */
static int pending_lists_length;
/* An INSN_LIST containing all INSN_LISTs allocated but currently unused. */
static rtx unused_insn_list;
/* An EXPR_LIST containing all EXPR_LISTs allocated but currently unused. */
static rtx unused_expr_list;
/* The last insn upon which all memory references must depend.
This is an insn which flushed the pending lists, creating a dependency
between it and all previously pending memory references. This creates
a barrier (or a checkpoint) which no memory reference is allowed to cross.
This includes all non constant CALL_INSNs. When we do interprocedural
alias analysis, this restriction can be relaxed.
This may also be an INSN that writes memory if the pending lists grow
too large. */
static rtx last_pending_memory_flush;
/* The last function call we have seen. All hard regs, and, of course,
the last function call, must depend on this. */
static rtx last_function_call;
/* The LOG_LINKS field of this is a list of insns which use a pseudo register
that does not already cross a call. We create dependencies between each
of those insn and the next call insn, to ensure that they won't cross a call
after scheduling is done. */
static rtx sched_before_next_call;
/* Pointer to the last instruction scheduled. Used by rank_for_schedule,
so that insns independent of the last scheduled insn will be preferred
over dependent instructions. */
static rtx last_scheduled_insn;
/* Process an insn's memory dependencies. There are four kinds of
dependencies:
(0) read dependence: read follows read
(1) true dependence: read follows write
(2) anti dependence: write follows read
(3) output dependence: write follows write
We are careful to build only dependencies which actually exist, and
use transitivity to avoid building too many links. */
/* Return the INSN_LIST containing INSN in LIST, or NULL
if LIST does not contain INSN. */
__inline static rtx
find_insn_list (insn, list)
rtx insn;
rtx list;
{
while (list)
{
if (XEXP (list, 0) == insn)
return list;
list = XEXP (list, 1);
}
return 0;
}
/* Compute the function units used by INSN. This caches the value
returned by function_units_used. A function unit is encoded as the
unit number if the value is non-negative and the compliment of a
mask if the value is negative. A function unit index is the
non-negative encoding. */
__inline static int
insn_unit (insn)
rtx insn;
{
register int unit = INSN_UNIT (insn);
if (unit == 0)
{
recog_memoized (insn);
/* A USE insn, or something else we don't need to understand.
We can't pass these directly to function_units_used because it will
trigger a fatal error for unrecognizable insns. */
if (INSN_CODE (insn) < 0)
unit = -1;
else
{
unit = function_units_used (insn);
/* Increment non-negative values so we can cache zero. */
if (unit >= 0) unit++;
}
/* We only cache 16 bits of the result, so if the value is out of
range, don't cache it. */
if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT
|| unit >= 0
- || (~unit & ((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
+ || (unit & ~((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
INSN_UNIT (insn) = unit;
}
return (unit > 0 ? unit - 1 : unit);
}
/* Compute the blockage range for executing INSN on UNIT. This caches
the value returned by the blockage_range_function for the unit.
These values are encoded in an int where the upper half gives the
minimum value and the lower half gives the maximum value. */
__inline static unsigned int
blockage_range (unit, insn)
int unit;
rtx insn;
{
unsigned int blockage = INSN_BLOCKAGE (insn);
unsigned int range;
if ((int) UNIT_BLOCKED (blockage) != unit + 1)
{
range = function_units[unit].blockage_range_function (insn);
/* We only cache the blockage range for one unit and then only if
the values fit. */
if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS)
INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range);
}
else
range = BLOCKAGE_RANGE (blockage);
return range;
}
/* A vector indexed by function unit instance giving the last insn to use
the unit. The value of the function unit instance index for unit U
instance I is (U + I * FUNCTION_UNITS_SIZE). */
static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
/* A vector indexed by function unit instance giving the minimum time when
the unit will unblock based on the maximum blockage cost. */
static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
/* A vector indexed by function unit number giving the number of insns
that remain to use the unit. */
static int unit_n_insns[FUNCTION_UNITS_SIZE];
/* Reset the function unit state to the null state. */
static void
clear_units ()
{
bzero ((char *) unit_last_insn, sizeof (unit_last_insn));
bzero ((char *) unit_tick, sizeof (unit_tick));
bzero ((char *) unit_n_insns, sizeof (unit_n_insns));
}
/* Record an insn as one that will use the units encoded by UNIT. */
__inline static void
prepare_unit (unit)
int unit;
{
int i;
if (unit >= 0)
unit_n_insns[unit]++;
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
prepare_unit (i);
}
/* Return the actual hazard cost of executing INSN on the unit UNIT,
instance INSTANCE at time CLOCK if the previous actual hazard cost
was COST. */
__inline static int
actual_hazard_this_instance (unit, instance, insn, clock, cost)
int unit, instance, clock, cost;
rtx insn;
{
int tick = unit_tick[instance];
if (tick - clock > cost)
{
/* The scheduler is operating in reverse, so INSN is the executing
insn and the unit's last insn is the candidate insn. We want a
more exact measure of the blockage if we execute INSN at CLOCK
given when we committed the execution of the unit's last insn.
The blockage value is given by either the unit's max blockage
constant, blockage range function, or blockage function. Use
the most exact form for the given unit. */
if (function_units[unit].blockage_range_function)
{
if (function_units[unit].blockage_function)
tick += (function_units[unit].blockage_function
(insn, unit_last_insn[instance])
- function_units[unit].max_blockage);
else
tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn))
- function_units[unit].max_blockage);
}
if (tick - clock > cost)
cost = tick - clock;
}
return cost;
}
/* Record INSN as having begun execution on the units encoded by UNIT at
time CLOCK. */
__inline static void
schedule_unit (unit, insn, clock)
int unit, clock;
rtx insn;
{
int i;
if (unit >= 0)
{
int instance = unit;
#if MAX_MULTIPLICITY > 1
/* Find the first free instance of the function unit and use that
one. We assume that one is free. */
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
if (! actual_hazard_this_instance (unit, instance, insn, clock, 0))
break;
instance += FUNCTION_UNITS_SIZE;
}
#endif
unit_last_insn[instance] = insn;
unit_tick[instance] = (clock + function_units[unit].max_blockage);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
schedule_unit (i, insn, clock);
}
/* Return the actual hazard cost of executing INSN on the units encoded by
UNIT at time CLOCK if the previous actual hazard cost was COST. */
__inline static int
actual_hazard (unit, insn, clock, cost)
int unit, clock, cost;
rtx insn;
{
int i;
if (unit >= 0)
{
/* Find the instance of the function unit with the minimum hazard. */
int instance = unit;
int best_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
#if MAX_MULTIPLICITY > 1
int this_cost;
if (best_cost > cost)
{
for (i = function_units[unit].multiplicity - 1; i > 0; i--)
{
instance += FUNCTION_UNITS_SIZE;
this_cost = actual_hazard_this_instance (unit, instance, insn,
clock, cost);
if (this_cost < best_cost)
{
best_cost = this_cost;
if (this_cost <= cost)
break;
}
}
}
#endif
cost = MAX (cost, best_cost);
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
cost = actual_hazard (i, insn, clock, cost);
return cost;
}
/* Return the potential hazard cost of executing an instruction on the
units encoded by UNIT if the previous potential hazard cost was COST.
An insn with a large blockage time is chosen in preference to one
with a smaller time; an insn that uses a unit that is more likely
to be used is chosen in preference to one with a unit that is less
used. We are trying to minimize a subsequent actual hazard. */
__inline static int
potential_hazard (unit, insn, cost)
int unit, cost;
rtx insn;
{
int i, ncost;
unsigned int minb, maxb;
if (unit >= 0)
{
minb = maxb = function_units[unit].max_blockage;
if (maxb > 1)
{
if (function_units[unit].blockage_range_function)
{
maxb = minb = blockage_range (unit, insn);
maxb = MAX_BLOCKAGE_COST (maxb);
minb = MIN_BLOCKAGE_COST (minb);
}
if (maxb > 1)
{
/* Make the number of instructions left dominate. Make the
minimum delay dominate the maximum delay. If all these
are the same, use the unit number to add an arbitrary
ordering. Other terms can be added. */
ncost = minb * 0x40 + maxb;
ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit;
if (ncost > cost)
cost = ncost;
}
}
}
else
for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
if ((unit & 1) != 0)
cost = potential_hazard (i, insn, cost);
return cost;
}
/* Compute cost of executing INSN given the dependence LINK on the insn USED.
This is the number of virtual cycles taken between instruction issue and
instruction results. */
__inline static int
insn_cost (insn, link, used)
rtx insn, link, used;
{
register int cost = INSN_COST (insn);
if (cost == 0)
{
recog_memoized (insn);
/* A USE insn, or something else we don't need to understand.
We can't pass these directly to result_ready_cost because it will
trigger a fatal error for unrecognizable insns. */
if (INSN_CODE (insn) < 0)
{
INSN_COST (insn) = 1;
return 1;
}
else
{
cost = result_ready_cost (insn);
if (cost < 1)
cost = 1;
INSN_COST (insn) = cost;
}
}
/* A USE insn should never require the value used to be computed. This
allows the computation of a function's result and parameter values to
overlap the return and call. */
recog_memoized (used);
if (INSN_CODE (used) < 0)
LINK_COST_FREE (link) = 1;
/* If some dependencies vary the cost, compute the adjustment. Most
commonly, the adjustment is complete: either the cost is ignored
(in the case of an output- or anti-dependence), or the cost is
unchanged. These values are cached in the link as LINK_COST_FREE
and LINK_COST_ZERO. */
if (LINK_COST_FREE (link))
cost = 1;
#ifdef ADJUST_COST
else if (! LINK_COST_ZERO (link))
{
int ncost = cost;
ADJUST_COST (used, link, insn, ncost);
if (ncost <= 1)
LINK_COST_FREE (link) = ncost = 1;
if (cost == ncost)
LINK_COST_ZERO (link) = 1;
cost = ncost;
}
#endif
return cost;
}
/* Compute the priority number for INSN. */
static int
priority (insn)
rtx insn;
{
if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
int prev_priority;
int max_priority;
int this_priority = INSN_PRIORITY (insn);
rtx prev;
if (this_priority > 0)
return this_priority;
max_priority = 1;
/* Nonzero if these insns must be scheduled together. */
if (SCHED_GROUP_P (insn))
{
prev = insn;
while (SCHED_GROUP_P (prev))
{
prev = PREV_INSN (prev);
INSN_REF_COUNT (prev) += 1;
}
}
for (prev = LOG_LINKS (insn); prev; prev = XEXP (prev, 1))
{
rtx x = XEXP (prev, 0);
/* If this was a duplicate of a dependence we already deleted,
ignore it. */
if (RTX_INTEGRATED_P (prev))
continue;
/* A dependence pointing to a note or deleted insn is always
obsolete, because sched_analyze_insn will have created any
necessary new dependences which replace it. Notes and deleted
insns can be created when instructions are deleted by insn
splitting, or by register allocation. */
if (GET_CODE (x) == NOTE || INSN_DELETED_P (x))
{
remove_dependence (insn, x);
continue;
}
/* Clear the link cost adjustment bits. */
LINK_COST_FREE (prev) = 0;
#ifdef ADJUST_COST
LINK_COST_ZERO (prev) = 0;
#endif
/* This priority calculation was chosen because it results in the
least instruction movement, and does not hurt the performance
of the resulting code compared to the old algorithm.
This makes the sched algorithm more stable, which results
in better code, because there is less register pressure,
cross jumping is more likely to work, and debugging is easier.
When all instructions have a latency of 1, there is no need to
move any instructions. Subtracting one here ensures that in such
cases all instructions will end up with a priority of one, and
hence no scheduling will be done.
The original code did not subtract the one, and added the
insn_cost of the current instruction to its priority (e.g.
move the insn_cost call down to the end). */
prev_priority = priority (x) + insn_cost (x, prev, insn) - 1;
if (prev_priority > max_priority)
max_priority = prev_priority;
INSN_REF_COUNT (x) += 1;
}
prepare_unit (insn_unit (insn));
INSN_PRIORITY (insn) = max_priority;
return INSN_PRIORITY (insn);
}
return 0;
}
/* Remove all INSN_LISTs and EXPR_LISTs from the pending lists and add
them to the unused_*_list variables, so that they can be reused. */
static void
free_pending_lists ()
{
register rtx link, prev_link;
if (pending_read_insns)
{
prev_link = pending_read_insns;
link = XEXP (prev_link, 1);
while (link)
{
prev_link = link;
link = XEXP (link, 1);
}
XEXP (prev_link, 1) = unused_insn_list;
unused_insn_list = pending_read_insns;
pending_read_insns = 0;
}
if (pending_write_insns)
{
prev_link = pending_write_insns;
link = XEXP (prev_link, 1);
while (link)
{
prev_link = link;
link = XEXP (link, 1);
}
XEXP (prev_link, 1) = unused_insn_list;
unused_insn_list = pending_write_insns;
pending_write_insns = 0;
}
if (pending_read_mems)
{
prev_link = pending_read_mems;
link = XEXP (prev_link, 1);
while (link)
{
prev_link = link;
link = XEXP (link, 1);
}
XEXP (prev_link, 1) = unused_expr_list;
unused_expr_list = pending_read_mems;
pending_read_mems = 0;
}
if (pending_write_mems)
{
prev_link = pending_write_mems;
link = XEXP (prev_link, 1);
while (link)
{
prev_link = link;
link = XEXP (link, 1);
}
XEXP (prev_link, 1) = unused_expr_list;
unused_expr_list = pending_write_mems;
pending_write_mems = 0;
}
}
/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST.
The MEM is a memory reference contained within INSN, which we are saving
so that we can do memory aliasing on it. */
static void
add_insn_mem_dependence (insn_list, mem_list, insn, mem)
rtx *insn_list, *mem_list, insn, mem;
{
register rtx link;
if (unused_insn_list)
{
link = unused_insn_list;
unused_insn_list = XEXP (link, 1);
}
else
link = rtx_alloc (INSN_LIST);
XEXP (link, 0) = insn;
XEXP (link, 1) = *insn_list;
*insn_list = link;
if (unused_expr_list)
{
link = unused_expr_list;
unused_expr_list = XEXP (link, 1);
}
else
link = rtx_alloc (EXPR_LIST);
XEXP (link, 0) = mem;
XEXP (link, 1) = *mem_list;
*mem_list = link;
pending_lists_length++;
}
/* Make a dependency between every memory reference on the pending lists
and INSN, thus flushing the pending lists. If ONLY_WRITE, don't flush
the read list. */
static void
flush_pending_lists (insn, only_write)
rtx insn;
int only_write;
{
rtx link;
while (pending_read_insns && ! only_write)
{
add_dependence (insn, XEXP (pending_read_insns, 0), REG_DEP_ANTI);
link = pending_read_insns;
pending_read_insns = XEXP (pending_read_insns, 1);
XEXP (link, 1) = unused_insn_list;
unused_insn_list = link;
link = pending_read_mems;
pending_read_mems = XEXP (pending_read_mems, 1);
XEXP (link, 1) = unused_expr_list;
unused_expr_list = link;
}
while (pending_write_insns)
{
add_dependence (insn, XEXP (pending_write_insns, 0), REG_DEP_ANTI);
link = pending_write_insns;
pending_write_insns = XEXP (pending_write_insns, 1);
XEXP (link, 1) = unused_insn_list;
unused_insn_list = link;
link = pending_write_mems;
pending_write_mems = XEXP (pending_write_mems, 1);
XEXP (link, 1) = unused_expr_list;
unused_expr_list = link;
}
pending_lists_length = 0;
if (last_pending_memory_flush)
add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI);
last_pending_memory_flush = insn;
}
/* Analyze a single SET or CLOBBER rtx, X, creating all dependencies generated
by the write to the destination of X, and reads of everything mentioned. */
static void
sched_analyze_1 (x, insn)
rtx x;
rtx insn;
{
register int regno;
register rtx dest = SET_DEST (x);
if (dest == 0)
return;
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
/* The second and third arguments are values read by this insn. */
sched_analyze_2 (XEXP (dest, 1), insn);
sched_analyze_2 (XEXP (dest, 2), insn);
}
dest = SUBREG_REG (dest);
}
if (GET_CODE (dest) == REG)
{
register int i;
regno = REGNO (dest);
/* A hard reg in a wide mode may really be multiple registers.
If so, mark all of them just like the first. */
if (regno < FIRST_PSEUDO_REGISTER)
{
i = HARD_REGNO_NREGS (regno, GET_MODE (dest));
while (--i >= 0)
{
rtx u;
for (u = reg_last_uses[regno+i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[regno + i] = 0;
if (reg_last_sets[regno + i])
add_dependence (insn, reg_last_sets[regno + i],
REG_DEP_OUTPUT);
SET_REGNO_REG_SET (reg_pending_sets, regno + i);
if ((call_used_regs[i] || global_regs[i])
&& last_function_call)
/* Function calls clobber all call_used regs. */
add_dependence (insn, last_function_call, REG_DEP_ANTI);
}
}
else
{
rtx u;
for (u = reg_last_uses[regno]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[regno] = 0;
if (reg_last_sets[regno])
add_dependence (insn, reg_last_sets[regno], REG_DEP_OUTPUT);
SET_REGNO_REG_SET (reg_pending_sets, regno);
/* Pseudos that are REG_EQUIV to something may be replaced
by that during reloading. We need only add dependencies for
the address in the REG_EQUIV note. */
if (! reload_completed
&& reg_known_equiv_p[regno]
&& GET_CODE (reg_known_value[regno]) == MEM)
sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
/* Don't let it cross a call after scheduling if it doesn't
already cross one. */
if (REG_N_CALLS_CROSSED (regno) == 0 && last_function_call)
add_dependence (insn, last_function_call, REG_DEP_ANTI);
}
}
else if (GET_CODE (dest) == MEM)
{
/* Writing memory. */
if (pending_lists_length > 32)
{
/* Flush all pending reads and writes to prevent the pending lists
from getting any larger. Insn scheduling runs too slowly when
these lists get long. The number 32 was chosen because it
seems like a reasonable number. When compiling GCC with itself,
this flush occurs 8 times for sparc, and 10 times for m88k using
the number 32. */
flush_pending_lists (insn, 0);
}
else
{
rtx pending, pending_mem;
pending = pending_read_insns;
pending_mem = pending_read_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (anti_dependence (XEXP (pending_mem, 0), dest))
add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
pending = pending_write_insns;
pending_mem = pending_write_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (output_dependence (XEXP (pending_mem, 0), dest))
add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
if (last_pending_memory_flush)
add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI);
add_insn_mem_dependence (&pending_write_insns, &pending_write_mems,
insn, dest);
}
sched_analyze_2 (XEXP (dest, 0), insn);
}
/* Analyze reads. */
if (GET_CODE (x) == SET)
sched_analyze_2 (SET_SRC (x), insn);
}
/* Analyze the uses of memory and registers in rtx X in INSN. */
static void
sched_analyze_2 (x, insn)
rtx x;
rtx insn;
{
register int i;
register int j;
register enum rtx_code code;
register char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
/* Ignore constants. Note that we must handle CONST_DOUBLE here
because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but
this does not mean that this insn is using cc0. */
return;
#ifdef HAVE_cc0
case CC0:
{
rtx link, prev;
/* User of CC0 depends on immediately preceding insn. */
SCHED_GROUP_P (insn) = 1;
/* There may be a note before this insn now, but all notes will
be removed before we actually try to schedule the insns, so
it won't cause a problem later. We must avoid it here though. */
prev = prev_nonnote_insn (insn);
/* Make a copy of all dependencies on the immediately previous insn,
and add to this insn. This is so that all the dependencies will
apply to the group. Remove an explicit dependence on this insn
as SCHED_GROUP_P now represents it. */
if (find_insn_list (prev, LOG_LINKS (insn)))
remove_dependence (insn, prev);
for (link = LOG_LINKS (prev); link; link = XEXP (link, 1))
add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
return;
}
#endif
case REG:
{
int regno = REGNO (x);
if (regno < FIRST_PSEUDO_REGISTER)
{
int i;
i = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--i >= 0)
{
reg_last_uses[regno + i]
= gen_rtx_INSN_LIST (VOIDmode,
insn, reg_last_uses[regno + i]);
if (reg_last_sets[regno + i])
add_dependence (insn, reg_last_sets[regno + i], 0);
if ((call_used_regs[regno + i] || global_regs[regno + i])
&& last_function_call)
/* Function calls clobber all call_used regs. */
add_dependence (insn, last_function_call, REG_DEP_ANTI);
}
}
else
{
reg_last_uses[regno]
= gen_rtx_INSN_LIST (VOIDmode, insn, reg_last_uses[regno]);
if (reg_last_sets[regno])
add_dependence (insn, reg_last_sets[regno], 0);
/* Pseudos that are REG_EQUIV to something may be replaced
by that during reloading. We need only add dependencies for
the address in the REG_EQUIV note. */
if (! reload_completed
&& reg_known_equiv_p[regno]
&& GET_CODE (reg_known_value[regno]) == MEM)
sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
/* If the register does not already cross any calls, then add this
insn to the sched_before_next_call list so that it will still
not cross calls after scheduling. */
if (REG_N_CALLS_CROSSED (regno) == 0)
add_dependence (sched_before_next_call, insn, REG_DEP_ANTI);
}
return;
}
case MEM:
{
/* Reading memory. */
rtx pending, pending_mem;
pending = pending_read_insns;
pending_mem = pending_read_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (read_dependence (XEXP (pending_mem, 0), x))
add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
pending = pending_write_insns;
pending_mem = pending_write_mems;
while (pending)
{
/* If a dependency already exists, don't create a new one. */
if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
if (true_dependence (XEXP (pending_mem, 0), VOIDmode,
x, rtx_varies_p))
add_dependence (insn, XEXP (pending, 0), 0);
pending = XEXP (pending, 1);
pending_mem = XEXP (pending_mem, 1);
}
if (last_pending_memory_flush)
add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI);
/* Always add these dependencies to pending_reads, since
this insn may be followed by a write. */
add_insn_mem_dependence (&pending_read_insns, &pending_read_mems,
insn, x);
/* Take advantage of tail recursion here. */
sched_analyze_2 (XEXP (x, 0), insn);
return;
}
case ASM_OPERANDS:
case ASM_INPUT:
case UNSPEC_VOLATILE:
case TRAP_IF:
{
rtx u;
/* Traditional and volatile asm instructions must be considered to use
and clobber all hard registers, all pseudo-registers and all of
memory. So must TRAP_IF and UNSPEC_VOLATILE operations.
Consider for instance a volatile asm that changes the fpu rounding
mode. An insn should not be moved across this even if it only uses
pseudo-regs because it might give an incorrectly rounded result. */
if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
{
int max_reg = max_reg_num ();
for (i = 0; i < max_reg; i++)
{
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
if (reg_last_sets[i])
add_dependence (insn, reg_last_sets[i], 0);
}
reg_pending_sets_all = 1;
flush_pending_lists (insn, 0);
}
/* For all ASM_OPERANDS, we must traverse the vector of input operands.
We can not just fall through here since then we would be confused
by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
traditional asms unlike their normal usage. */
if (code == ASM_OPERANDS)
{
for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
sched_analyze_2 (ASM_OPERANDS_INPUT (x, j), insn);
return;
}
break;
}
case PRE_DEC:
case POST_DEC:
case PRE_INC:
case POST_INC:
/* These both read and modify the result. We must handle them as writes
to get proper dependencies for following instructions. We must handle
them as reads to get proper dependencies from this to previous
instructions. Thus we need to pass them to both sched_analyze_1
and sched_analyze_2. We must call sched_analyze_2 first in order
to get the proper antecedent for the read. */
sched_analyze_2 (XEXP (x, 0), insn);
sched_analyze_1 (x, insn);
return;
default:
break;
}
/* Other cases: walk the insn. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
sched_analyze_2 (XEXP (x, i), insn);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
sched_analyze_2 (XVECEXP (x, i, j), insn);
}
}
/* Analyze an INSN with pattern X to find all dependencies. */
static void
sched_analyze_insn (x, insn, loop_notes)
rtx x, insn;
rtx loop_notes;
{
register RTX_CODE code = GET_CODE (x);
rtx link;
int maxreg = max_reg_num ();
int i;
if (code == SET || code == CLOBBER)
sched_analyze_1 (x, insn);
else if (code == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
sched_analyze_1 (XVECEXP (x, 0, i), insn);
else
sched_analyze_2 (XVECEXP (x, 0, i), insn);
}
}
else
sched_analyze_2 (x, insn);
/* Mark registers CLOBBERED or used by called function. */
if (GET_CODE (insn) == CALL_INSN)
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
{
if (GET_CODE (XEXP (link, 0)) == CLOBBER)
sched_analyze_1 (XEXP (link, 0), insn);
else
sched_analyze_2 (XEXP (link, 0), insn);
}
/* If there is a {LOOP,EHREGION}_{BEG,END} note in the middle of a basic block, then
we must be sure that no instructions are scheduled across it.
Otherwise, the reg_n_refs info (which depends on loop_depth) would
become incorrect. */
if (loop_notes)
{
int max_reg = max_reg_num ();
rtx link;
for (i = 0; i < max_reg; i++)
{
rtx u;
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
if (reg_last_sets[i])
add_dependence (insn, reg_last_sets[i], 0);
}
reg_pending_sets_all = 1;
flush_pending_lists (insn, 0);
link = loop_notes;
while (XEXP (link, 1))
link = XEXP (link, 1);
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = loop_notes;
}
EXECUTE_IF_SET_IN_REG_SET (reg_pending_sets, 0, i,
{
reg_last_sets[i] = insn;
});
CLEAR_REG_SET (reg_pending_sets);
if (reg_pending_sets_all)
{
for (i = 0; i < maxreg; i++)
reg_last_sets[i] = insn;
reg_pending_sets_all = 0;
}
/* Handle function calls and function returns created by the epilogue
threading code. */
if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
{
rtx dep_insn;
rtx prev_dep_insn;
/* When scheduling instructions, we make sure calls don't lose their
accompanying USE insns by depending them one on another in order.
Also, we must do the same thing for returns created by the epilogue
threading code. Note this code works only in this special case,
because other passes make no guarantee that they will never emit
an instruction between a USE and a RETURN. There is such a guarantee
for USE instructions immediately before a call. */
prev_dep_insn = insn;
dep_insn = PREV_INSN (insn);
while (GET_CODE (dep_insn) == INSN
&& GET_CODE (PATTERN (dep_insn)) == USE
&& GET_CODE (XEXP (PATTERN (dep_insn), 0)) == REG)
{
SCHED_GROUP_P (prev_dep_insn) = 1;
/* Make a copy of all dependencies on dep_insn, and add to insn.
This is so that all of the dependencies will apply to the
group. */
for (link = LOG_LINKS (dep_insn); link; link = XEXP (link, 1))
add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
prev_dep_insn = dep_insn;
dep_insn = PREV_INSN (dep_insn);
}
}
}
/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS
for every dependency. */
static int
sched_analyze (head, tail)
rtx head, tail;
{
register rtx insn;
register int n_insns = 0;
register rtx u;
register int luid = 0;
rtx loop_notes = 0;
for (insn = head; ; insn = NEXT_INSN (insn))
{
INSN_LUID (insn) = luid++;
if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
{
sched_analyze_insn (PATTERN (insn), insn, loop_notes);
loop_notes = 0;
n_insns += 1;
}
else if (GET_CODE (insn) == CALL_INSN)
{
rtx x;
register int i;
/* Any instruction using a hard register which may get clobbered
by a call needs to be marked as dependent on this call.
This prevents a use of a hard return reg from being moved
past a void call (i.e. it does not explicitly set the hard
return reg). */
/* If this call is followed by a NOTE_INSN_SETJMP, then assume that
all registers, not just hard registers, may be clobbered by this
call. */
/* Insn, being a CALL_INSN, magically depends on
`last_function_call' already. */
if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE
&& NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP)
{
int max_reg = max_reg_num ();
for (i = 0; i < max_reg; i++)
{
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
if (reg_last_sets[i])
add_dependence (insn, reg_last_sets[i], 0);
}
reg_pending_sets_all = 1;
/* Add a pair of fake REG_NOTEs which we will later
convert back into a NOTE_INSN_SETJMP note. See
reemit_notes for why we use a pair of NOTEs. */
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD,
GEN_INT (0),
REG_NOTES (insn));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_INSN_SETJMP),
REG_NOTES (insn));
}
else
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i] || global_regs[i])
{
for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
reg_last_uses[i] = 0;
if (reg_last_sets[i])
add_dependence (insn, reg_last_sets[i], REG_DEP_ANTI);
SET_REGNO_REG_SET (reg_pending_sets, i);
}
}
/* For each insn which shouldn't cross a call, add a dependence
between that insn and this call insn. */
x = LOG_LINKS (sched_before_next_call);
while (x)
{
add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI);
x = XEXP (x, 1);
}
LOG_LINKS (sched_before_next_call) = 0;
sched_analyze_insn (PATTERN (insn), insn, loop_notes);
loop_notes = 0;
/* In the absence of interprocedural alias analysis, we must flush
all pending reads and writes, and start new dependencies starting
from here. But only flush writes for constant calls (which may
be passed a pointer to something we haven't written yet). */
flush_pending_lists (insn, CONST_CALL_P (insn));
/* Depend this function call (actually, the user of this
function call) on all hard register clobberage. */
last_function_call = insn;
n_insns += 1;
}
/* See comments on reemit_notes as to why we do this. */
else if (GET_CODE (insn) == NOTE
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_START
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END
|| (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP
&& GET_CODE (PREV_INSN (insn)) != CALL_INSN)))
{
loop_notes = gen_rtx_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_BLOCK_NUMBER (insn)),
loop_notes);
loop_notes = gen_rtx_EXPR_LIST (REG_DEAD,
GEN_INT (NOTE_LINE_NUMBER (insn)),
loop_notes);
CONST_CALL_P (loop_notes) = CONST_CALL_P (insn);
}
if (insn == tail)
return n_insns;
}
abort ();
}
/* Called when we see a set of a register. If death is true, then we are
scanning backwards. Mark that register as unborn. If nobody says
otherwise, that is how things will remain. If death is false, then we
are scanning forwards. Mark that register as being born. */
static void
sched_note_set (x, death)
rtx x;
int death;
{
register int regno;
register rtx reg = SET_DEST (x);
int subreg_p = 0;
if (reg == 0)
return;
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART
|| GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT)
{
/* Must treat modification of just one hardware register of a multi-reg
value or just a byte field of a register exactly the same way that
mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg
does not kill the entire register. */
if (GET_CODE (reg) != SUBREG
|| REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg))
subreg_p = 1;
reg = SUBREG_REG (reg);
}
if (GET_CODE (reg) != REG)
return;
/* Global registers are always live, so the code below does not apply
to them. */
regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
{
if (death)
{
/* If we only set part of the register, then this set does not
kill it. */
if (subreg_p)
return;
/* Try killing this register. */
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno + j);
SET_REGNO_REG_SET (bb_dead_regs, regno + j);
}
}
else
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
SET_REGNO_REG_SET (bb_dead_regs, regno);
}
}
else
{
/* Make the register live again. */
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--j >= 0)
{
SET_REGNO_REG_SET (bb_live_regs, regno + j);
CLEAR_REGNO_REG_SET (bb_dead_regs, regno + j);
}
}
else
{
SET_REGNO_REG_SET (bb_live_regs, regno);
CLEAR_REGNO_REG_SET (bb_dead_regs, regno);
}
}
}
}
/* Macros and functions for keeping the priority queue sorted, and
dealing with queueing and dequeueing of instructions. */
#define SCHED_SORT(READY, NEW_READY, OLD_READY) \
do { if ((NEW_READY) - (OLD_READY) == 1) \
swap_sort (READY, NEW_READY); \
else if ((NEW_READY) - (OLD_READY) > 1) \
qsort (READY, NEW_READY, sizeof (rtx), rank_for_schedule); } \
while (0)
/* Returns a positive value if y is preferred; returns a negative value if
x is preferred. Should never return 0, since that will make the sort
unstable. */
static int
rank_for_schedule (x, y)
const GENERIC_PTR x;
const GENERIC_PTR y;
{
rtx tmp = *(rtx *)y;
rtx tmp2 = *(rtx *)x;
rtx link;
int tmp_class, tmp2_class;
int value;
/* Choose the instruction with the highest priority, if different. */
if ((value = INSN_PRIORITY (tmp) - INSN_PRIORITY (tmp2)))
return value;
if (last_scheduled_insn)
{
/* Classify the instructions into three classes:
1) Data dependent on last schedule insn.
2) Anti/Output dependent on last scheduled insn.
3) Independent of last scheduled insn, or has latency of one.
Choose the insn from the highest numbered class if different. */
link = find_insn_list (tmp, LOG_LINKS (last_scheduled_insn));
if (link == 0 || insn_cost (tmp, link, last_scheduled_insn) == 1)
tmp_class = 3;
else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
tmp_class = 1;
else
tmp_class = 2;
link = find_insn_list (tmp2, LOG_LINKS (last_scheduled_insn));
if (link == 0 || insn_cost (tmp2, link, last_scheduled_insn) == 1)
tmp2_class = 3;
else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
tmp2_class = 1;
else
tmp2_class = 2;
if ((value = tmp_class - tmp2_class))
return value;
}
/* If insns are equally good, sort by INSN_LUID (original insn order),
so that we make the sort stable. This minimizes instruction movement,
thus minimizing sched's effect on debugging and cross-jumping. */
return INSN_LUID (tmp) - INSN_LUID (tmp2);
}
/* Resort the array A in which only element at index N may be out of order. */
__inline static void
swap_sort (a, n)
rtx *a;
int n;
{
rtx insn = a[n-1];
int i = n-2;
while (i >= 0 && rank_for_schedule (a+i, &insn) >= 0)
{
a[i+1] = a[i];
i -= 1;
}
a[i+1] = insn;
}
static int max_priority;
/* Add INSN to the insn queue so that it fires at least N_CYCLES
before the currently executing insn. */
__inline static void
queue_insn (insn, n_cycles)
rtx insn;
int n_cycles;
{
int next_q = NEXT_Q_AFTER (q_ptr, n_cycles);
NEXT_INSN (insn) = insn_queue[next_q];
insn_queue[next_q] = insn;
q_size += 1;
}
/* Return nonzero if PAT is the pattern of an insn which makes a
register live. */
__inline static int
birthing_insn_p (pat)
rtx pat;
{
int j;
if (reload_completed == 1)
return 0;
if (GET_CODE (pat) == SET
&& GET_CODE (SET_DEST (pat)) == REG)
{
rtx dest = SET_DEST (pat);
int i = REGNO (dest);
/* It would be more accurate to use refers_to_regno_p or
reg_mentioned_p to determine when the dest is not live before this
insn. */
if (REGNO_REG_SET_P (bb_live_regs, i))
return (REG_N_SETS (i) == 1);
return 0;
}
if (GET_CODE (pat) == PARALLEL)
{
for (j = 0; j < XVECLEN (pat, 0); j++)
if (birthing_insn_p (XVECEXP (pat, 0, j)))
return 1;
}
return 0;
}
/* PREV is an insn that is ready to execute. Adjust its priority if that
will help shorten register lifetimes. */
__inline static void
adjust_priority (prev)
rtx prev;
{
/* Trying to shorten register lives after reload has completed
is useless and wrong. It gives inaccurate schedules. */
if (reload_completed == 0)
{
rtx note;
int n_deaths = 0;
/* ??? This code has no effect, because REG_DEAD notes are removed
before we ever get here. */
for (note = REG_NOTES (prev); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD)
n_deaths += 1;
/* Defer scheduling insns which kill registers, since that
shortens register lives. Prefer scheduling insns which
make registers live for the same reason. */
switch (n_deaths)
{
default:
INSN_PRIORITY (prev) >>= 3;
break;
case 3:
INSN_PRIORITY (prev) >>= 2;
break;
case 2:
case 1:
INSN_PRIORITY (prev) >>= 1;
break;
case 0:
if (birthing_insn_p (PATTERN (prev)))
{
int max = max_priority;
if (max > INSN_PRIORITY (prev))
INSN_PRIORITY (prev) = max;
}
break;
}
#ifdef ADJUST_PRIORITY
ADJUST_PRIORITY (prev);
#endif
}
}
/* INSN is the "currently executing insn". Launch each insn which was
waiting on INSN (in the backwards dataflow sense). READY is a
vector of insns which are ready to fire. N_READY is the number of
elements in READY. CLOCK is the current virtual cycle. */
static int
schedule_insn (insn, ready, n_ready, clock)
rtx insn;
rtx *ready;
int n_ready;
int clock;
{
rtx link;
int new_ready = n_ready;
if (MAX_BLOCKAGE > 1)
schedule_unit (insn_unit (insn), insn, clock);
if (LOG_LINKS (insn) == 0)
return n_ready;
/* This is used by the function adjust_priority above. */
if (n_ready > 0)
max_priority = MAX (INSN_PRIORITY (ready[0]), INSN_PRIORITY (insn));
else
max_priority = INSN_PRIORITY (insn);
for (link = LOG_LINKS (insn); link != 0; link = XEXP (link, 1))
{
rtx prev = XEXP (link, 0);
int cost = insn_cost (prev, link, insn);
if ((INSN_REF_COUNT (prev) -= 1) != 0)
{
/* We satisfied one requirement to fire PREV. Record the earliest
time when PREV can fire. No need to do this if the cost is 1,
because PREV can fire no sooner than the next cycle. */
if (cost > 1)
INSN_TICK (prev) = MAX (INSN_TICK (prev), clock + cost);
}
else
{
/* We satisfied the last requirement to fire PREV. Ensure that all
timing requirements are satisfied. */
if (INSN_TICK (prev) - clock > cost)
cost = INSN_TICK (prev) - clock;
/* Adjust the priority of PREV and either put it on the ready
list or queue it. */
adjust_priority (prev);
if (cost <= 1)
ready[new_ready++] = prev;
else
queue_insn (prev, cost);
}
}
return new_ready;
}
/* Given N_READY insns in the ready list READY at time CLOCK, queue
those that are blocked due to function unit hazards and rearrange
the remaining ones to minimize subsequent function unit hazards. */
static int
schedule_select (ready, n_ready, clock, file)
rtx *ready;
int n_ready, clock;
FILE *file;
{
int pri = INSN_PRIORITY (ready[0]);
int i, j, k, q, cost, best_cost, best_insn = 0, new_ready = n_ready;
rtx insn;
/* Work down the ready list in groups of instructions with the same
priority value. Queue insns in the group that are blocked and
select among those that remain for the one with the largest
potential hazard. */
for (i = 0; i < n_ready; i = j)
{
int opri = pri;
for (j = i + 1; j < n_ready; j++)
if ((pri = INSN_PRIORITY (ready[j])) != opri)
break;
/* Queue insns in the group that are blocked. */
for (k = i, q = 0; k < j; k++)
{
insn = ready[k];
if ((cost = actual_hazard (insn_unit (insn), insn, clock, 0)) != 0)
{
q++;
ready[k] = 0;
queue_insn (insn, cost);
if (file)
fprintf (file, "\n;; blocking insn %d for %d cycles",
INSN_UID (insn), cost);
}
}
new_ready -= q;
/* Check the next group if all insns were queued. */
if (j - i - q == 0)
continue;
/* If more than one remains, select the first one with the largest
potential hazard. */
else if (j - i - q > 1)
{
best_cost = -1;
for (k = i; k < j; k++)
{
if ((insn = ready[k]) == 0)
continue;
if ((cost = potential_hazard (insn_unit (insn), insn, 0))
> best_cost)
{
best_cost = cost;
best_insn = k;
}
}
}
/* We have found a suitable insn to schedule. */
break;
}
/* Move the best insn to be front of the ready list. */
if (best_insn != 0)
{
if (file)
{
fprintf (file, ", now");
for (i = 0; i < n_ready; i++)
if (ready[i])
fprintf (file, " %d", INSN_UID (ready[i]));
fprintf (file, "\n;; insn %d has a greater potential hazard",
INSN_UID (ready[best_insn]));
}
for (i = best_insn; i > 0; i--)
{
insn = ready[i-1];
ready[i-1] = ready[i];
ready[i] = insn;
}
}
/* Compact the ready list. */
if (new_ready < n_ready)
for (i = j = 0; i < n_ready; i++)
if (ready[i])
ready[j++] = ready[i];
return new_ready;
}
/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the
dead_notes list. */
static void
create_reg_dead_note (reg, insn)
rtx reg, insn;
{
rtx link;
/* The number of registers killed after scheduling must be the same as the
number of registers killed before scheduling. The number of REG_DEAD
notes may not be conserved, i.e. two SImode hard register REG_DEAD notes
might become one DImode hard register REG_DEAD note, but the number of
registers killed will be conserved.
We carefully remove REG_DEAD notes from the dead_notes list, so that
there will be none left at the end. If we run out early, then there
is a bug somewhere in flow, combine and/or sched. */
if (dead_notes == 0)
{
#if 1
abort ();
#else
link = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (link, REG_DEAD);
#endif
}
else
{
/* Number of regs killed by REG. */
int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
/* Number of regs killed by REG_DEAD notes taken off the list. */
int reg_note_regs;
link = dead_notes;
reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
GET_MODE (XEXP (link, 0))));
while (reg_note_regs < regs_killed)
{
/* LINK might be zero if we killed more registers after scheduling
than before, and the last hard register we kill is actually
multiple hard regs. */
if (link == NULL_RTX)
abort ();
link = XEXP (link, 1);
reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
: HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
GET_MODE (XEXP (link, 0))));
}
dead_notes = XEXP (link, 1);
/* If we took too many regs kills off, put the extra ones back. */
while (reg_note_regs > regs_killed)
{
rtx temp_reg, temp_link;
temp_reg = gen_rtx_REG (word_mode, 0);
temp_link = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (temp_link, REG_DEAD);
XEXP (temp_link, 0) = temp_reg;
XEXP (temp_link, 1) = dead_notes;
dead_notes = temp_link;
reg_note_regs--;
}
}
XEXP (link, 0) = reg;
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = link;
}
/* Subroutine on attach_deaths_insn--handles the recursive search
through INSN. If SET_P is true, then x is being modified by the insn. */
static void
attach_deaths (x, insn, set_p)
rtx x;
rtx insn;
int set_p;
{
register int i;
register int j;
register enum rtx_code code;
register char *fmt;
if (x == 0)
return;
code = GET_CODE (x);
switch (code)
{
case CONST_INT:
case CONST_DOUBLE:
case LABEL_REF:
case SYMBOL_REF:
case CONST:
case CODE_LABEL:
case PC:
case CC0:
/* Get rid of the easy cases first. */
return;
case REG:
{
/* If the register dies in this insn, queue that note, and mark
this register as needing to die. */
/* This code is very similar to mark_used_1 (if set_p is false)
and mark_set_1 (if set_p is true) in flow.c. */
register int regno;
int some_needed;
int all_needed;
if (set_p)
return;
regno = REGNO (x);
all_needed = some_needed = REGNO_REG_SET_P (old_live_regs, regno);
if (regno < FIRST_PSEUDO_REGISTER)
{
int n;
n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n > 0)
{
int needed = (REGNO_REG_SET_P (old_live_regs, regno + n));
some_needed |= needed;
all_needed &= needed;
}
}
/* If it wasn't live before we started, then add a REG_DEAD note.
We must check the previous lifetime info not the current info,
because we may have to execute this code several times, e.g.
once for a clobber (which doesn't add a note) and later
for a use (which does add a note).
Always make the register live. We must do this even if it was
live before, because this may be an insn which sets and uses
the same register, in which case the register has already been
killed, so we must make it live again.
Global registers are always live, and should never have a REG_DEAD
note added for them, so none of the code below applies to them. */
if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
{
/* Never add REG_DEAD notes for STACK_POINTER_REGNUM
since it's always considered to be live. Similarly
for FRAME_POINTER_REGNUM if a frame pointer is needed
and for ARG_POINTER_REGNUM if it is fixed. */
if (! (regno == FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == HARD_FRAME_POINTER_REGNUM
&& (! reload_completed || frame_pointer_needed))
#endif
#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
&& ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
&& regno != STACK_POINTER_REGNUM)
{
if (! all_needed && ! dead_or_set_p (insn, x))
{
/* Check for the case where the register dying partially
overlaps the register set by this insn. */
if (regno < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
{
int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n >= 0)
some_needed |= dead_or_set_regno_p (insn, regno + n);
}
/* If none of the words in X is needed, make a REG_DEAD
note. Otherwise, we must make partial REG_DEAD
notes. */
if (! some_needed)
create_reg_dead_note (x, insn);
else
{
int i;
/* Don't make a REG_DEAD note for a part of a
register that is set in the insn. */
for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
i >= 0; i--)
if (! REGNO_REG_SET_P (old_live_regs, regno + i)
&& ! dead_or_set_regno_p (insn, regno + i))
create_reg_dead_note (gen_rtx_REG (reg_raw_mode[regno + i],
regno + i),
insn);
}
}
}
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_dead_regs, regno + j);
SET_REGNO_REG_SET (bb_live_regs, regno + j);
}
}
else
{
CLEAR_REGNO_REG_SET (bb_dead_regs, regno);
SET_REGNO_REG_SET (bb_live_regs, regno);
}
}
return;
}
case MEM:
/* Handle tail-recursive case. */
attach_deaths (XEXP (x, 0), insn, 0);
return;
case SUBREG:
attach_deaths (SUBREG_REG (x), insn,
set_p && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
<= UNITS_PER_WORD)
|| (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
== GET_MODE_SIZE (GET_MODE ((x))))));
return;
case STRICT_LOW_PART:
attach_deaths (XEXP (x, 0), insn, 0);
return;
case ZERO_EXTRACT:
case SIGN_EXTRACT:
attach_deaths (XEXP (x, 0), insn, 0);
attach_deaths (XEXP (x, 1), insn, 0);
attach_deaths (XEXP (x, 2), insn, 0);
return;
default:
/* Other cases: walk the insn. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
attach_deaths (XEXP (x, i), insn, 0);
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
attach_deaths (XVECEXP (x, i, j), insn, 0);
}
}
}
/* After INSN has executed, add register death notes for each register
that is dead after INSN. */
static void
attach_deaths_insn (insn)
rtx insn;
{
rtx x = PATTERN (insn);
register RTX_CODE code = GET_CODE (x);
rtx link;
if (code == SET)
{
attach_deaths (SET_SRC (x), insn, 0);
/* A register might die here even if it is the destination, e.g.
it is the target of a volatile read and is otherwise unused.
Hence we must always call attach_deaths for the SET_DEST. */
attach_deaths (SET_DEST (x), insn, 1);
}
else if (code == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET)
{
attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0);
attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
}
/* Flow does not add REG_DEAD notes to registers that die in
clobbers, so we can't either. */
else if (code != CLOBBER)
attach_deaths (XVECEXP (x, 0, i), insn, 0);
}
}
/* If this is a CLOBBER, only add REG_DEAD notes to registers inside a
MEM being clobbered, just like flow. */
else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == MEM)
attach_deaths (XEXP (XEXP (x, 0), 0), insn, 0);
/* Otherwise don't add a death note to things being clobbered. */
else if (code != CLOBBER)
attach_deaths (x, insn, 0);
/* Make death notes for things used in the called function. */
if (GET_CODE (insn) == CALL_INSN)
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
attach_deaths (XEXP (XEXP (link, 0), 0), insn,
GET_CODE (XEXP (link, 0)) == CLOBBER);
}
/* Delete notes beginning with INSN and maybe put them in the chain
of notes ended by NOTE_LIST.
Returns the insn following the notes. */
static rtx
unlink_notes (insn, tail)
rtx insn, tail;
{
rtx prev = PREV_INSN (insn);
while (insn != tail && GET_CODE (insn) == NOTE)
{
rtx next = NEXT_INSN (insn);
/* Delete the note from its current position. */
if (prev)
NEXT_INSN (prev) = next;
if (next)
PREV_INSN (next) = prev;
if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0)
/* Record line-number notes so they can be reused. */
LINE_NOTE (insn) = insn;
/* Don't save away NOTE_INSN_SETJMPs, because they must remain
immediately after the call they follow. We use a fake
(REG_DEAD (const_int -1)) note to remember them.
Likewise with NOTE_INSN_{LOOP,EHREGION}_{BEG, END}. */
else if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_SETJMP
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_START
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_END)
{
/* Insert the note at the end of the notes list. */
PREV_INSN (insn) = note_list;
if (note_list)
NEXT_INSN (note_list) = insn;
note_list = insn;
}
insn = next;
}
return insn;
}
/* Constructor for `sometimes' data structure. */
static int
new_sometimes_live (regs_sometimes_live, regno, sometimes_max)
struct sometimes *regs_sometimes_live;
int regno;
int sometimes_max;
{
register struct sometimes *p;
/* There should never be a register greater than max_regno here. If there
is, it means that a define_split has created a new pseudo reg. This
is not allowed, since there will not be flow info available for any
new register, so catch the error here. */
if (regno >= max_regno)
abort ();
p = &regs_sometimes_live[sometimes_max];
p->regno = regno;
p->live_length = 0;
p->calls_crossed = 0;
sometimes_max++;
return sometimes_max;
}
/* Count lengths of all regs we are currently tracking,
and find new registers no longer live. */
static void
finish_sometimes_live (regs_sometimes_live, sometimes_max)
struct sometimes *regs_sometimes_live;
int sometimes_max;
{
int i;
for (i = 0; i < sometimes_max; i++)
{
register struct sometimes *p = &regs_sometimes_live[i];
int regno = p->regno;
sched_reg_live_length[regno] += p->live_length;
sched_reg_n_calls_crossed[regno] += p->calls_crossed;
}
}
/* Search INSN for fake REG_DEAD note pairs for NOTE_INSN_SETJMP,
NOTE_INSN_{LOOP,EHREGION}_{BEG,END}; and convert them back into
NOTEs. The REG_DEAD note following first one is contains the saved
value for NOTE_BLOCK_NUMBER which is useful for
NOTE_INSN_EH_REGION_{BEG,END} NOTEs. LAST is the last instruction
output by the instruction scheduler. Return the new value of LAST. */
static rtx
reemit_notes (insn, last)
rtx insn;
rtx last;
{
rtx note;
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (XEXP (note, 0)) == CONST_INT)
{
if (INTVAL (XEXP (note, 0)) == NOTE_INSN_SETJMP)
{
CONST_CALL_P (emit_note_after (INTVAL (XEXP (note, 0)), insn))
= CONST_CALL_P (note);
remove_note (insn, note);
note = XEXP (note, 1);
}
else
{
last = emit_note_before (INTVAL (XEXP (note, 0)), last);
remove_note (insn, note);
note = XEXP (note, 1);
NOTE_BLOCK_NUMBER (last) = INTVAL (XEXP (note, 0));
}
remove_note (insn, note);
}
}
return last;
}
/* Use modified list scheduling to rearrange insns in basic block
B. FILE, if nonzero, is where we dump interesting output about
this pass. */
static void
schedule_block (b, file)
int b;
FILE *file;
{
rtx insn, last;
rtx *ready, link;
int i, j, n_ready = 0, new_ready, n_insns;
int sched_n_insns = 0;
int clock;
#define NEED_NOTHING 0
#define NEED_HEAD 1
#define NEED_TAIL 2
int new_needs;
/* HEAD and TAIL delimit the region being scheduled. */
rtx head = BLOCK_HEAD (b);
rtx tail = BLOCK_END (b);
/* PREV_HEAD and NEXT_TAIL are the boundaries of the insns
being scheduled. When the insns have been ordered,
these insns delimit where the new insns are to be
spliced back into the insn chain. */
rtx next_tail;
rtx prev_head;
/* Keep life information accurate. */
register struct sometimes *regs_sometimes_live;
int sometimes_max;
if (file)
fprintf (file, ";;\t -- basic block number %d from %d to %d --\n",
b, INSN_UID (BLOCK_HEAD (b)), INSN_UID (BLOCK_END (b)));
i = max_reg_num ();
reg_last_uses = (rtx *) alloca (i * sizeof (rtx));
bzero ((char *) reg_last_uses, i * sizeof (rtx));
reg_last_sets = (rtx *) alloca (i * sizeof (rtx));
bzero ((char *) reg_last_sets, i * sizeof (rtx));
reg_pending_sets = ALLOCA_REG_SET ();
CLEAR_REG_SET (reg_pending_sets);
reg_pending_sets_all = 0;
clear_units ();
#if 0
/* We used to have code to avoid getting parameters moved from hard
argument registers into pseudos.
However, it was removed when it proved to be of marginal benefit and
caused problems because of different notions of what the "head" insn
was. */
/* Remove certain insns at the beginning from scheduling,
by advancing HEAD. */
/* At the start of a function, before reload has run, don't delay getting
parameters from hard registers into pseudo registers. */
if (reload_completed == 0 && b == 0)
{
while (head != tail
&& GET_CODE (head) == NOTE
&& NOTE_LINE_NUMBER (head) != NOTE_INSN_FUNCTION_BEG)
head = NEXT_INSN (head);
while (head != tail
&& GET_CODE (head) == INSN
&& GET_CODE (PATTERN (head)) == SET)
{
rtx src = SET_SRC (PATTERN (head));
while (GET_CODE (src) == SUBREG
|| GET_CODE (src) == SIGN_EXTEND
|| GET_CODE (src) == ZERO_EXTEND
|| GET_CODE (src) == SIGN_EXTRACT
|| GET_CODE (src) == ZERO_EXTRACT)
src = XEXP (src, 0);
if (GET_CODE (src) != REG
|| REGNO (src) >= FIRST_PSEUDO_REGISTER)
break;
/* Keep this insn from ever being scheduled. */
INSN_REF_COUNT (head) = 1;
head = NEXT_INSN (head);
}
}
#endif
/* Don't include any notes or labels at the beginning of the
basic block, or notes at the ends of basic blocks. */
while (head != tail)
{
if (GET_CODE (head) == NOTE)
head = NEXT_INSN (head);
else if (GET_CODE (tail) == NOTE)
tail = PREV_INSN (tail);
else if (GET_CODE (head) == CODE_LABEL)
head = NEXT_INSN (head);
else break;
}
/* If the only insn left is a NOTE or a CODE_LABEL, then there is no need
to schedule this block. */
if (head == tail
&& (GET_CODE (head) == NOTE || GET_CODE (head) == CODE_LABEL))
goto ret;
#if 0
/* This short-cut doesn't work. It does not count call insns crossed by
registers in reg_sometimes_live. It does not mark these registers as
dead if they die in this block. It does not mark these registers live
(or create new reg_sometimes_live entries if necessary) if they are born
in this block.
The easy solution is to just always schedule a block. This block only
has one insn, so this won't slow down this pass by much. */
if (head == tail)
goto ret;
#endif
/* Now HEAD through TAIL are the insns actually to be rearranged;
Let PREV_HEAD and NEXT_TAIL enclose them. */
prev_head = PREV_INSN (head);
next_tail = NEXT_INSN (tail);
/* Initialize basic block data structures. */
dead_notes = 0;
pending_read_insns = 0;
pending_read_mems = 0;
pending_write_insns = 0;
pending_write_mems = 0;
pending_lists_length = 0;
last_pending_memory_flush = 0;
last_function_call = 0;
last_scheduled_insn = 0;
LOG_LINKS (sched_before_next_call) = 0;
n_insns = sched_analyze (head, tail);
if (n_insns == 0)
{
free_pending_lists ();
goto ret;
}
/* Allocate vector to hold insns to be rearranged (except those
insns which are controlled by an insn with SCHED_GROUP_P set).
All these insns are included between ORIG_HEAD and ORIG_TAIL,
as those variables ultimately are set up. */
ready = (rtx *) alloca ((n_insns+1) * sizeof (rtx));
/* TAIL is now the last of the insns to be rearranged.
Put those insns into the READY vector. */
insn = tail;
/* For all branches, calls, uses, and cc0 setters, force them to remain
in order at the end of the block by adding dependencies and giving
the last a high priority. There may be notes present, and prev_head
may also be a note.
Branches must obviously remain at the end. Calls should remain at the
end since moving them results in worse register allocation. Uses remain
at the end to ensure proper register allocation. cc0 setters remaim
at the end because they can't be moved away from their cc0 user. */
last = 0;
while (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
|| (GET_CODE (insn) == INSN
&& (GET_CODE (PATTERN (insn)) == USE
#ifdef HAVE_cc0
|| sets_cc0_p (PATTERN (insn))
#endif
))
|| GET_CODE (insn) == NOTE)
{
if (GET_CODE (insn) != NOTE)
{
priority (insn);
if (last == 0)
{
ready[n_ready++] = insn;
INSN_PRIORITY (insn) = TAIL_PRIORITY - i;
INSN_REF_COUNT (insn) = 0;
}
else if (! find_insn_list (insn, LOG_LINKS (last)))
{
add_dependence (last, insn, REG_DEP_ANTI);
INSN_REF_COUNT (insn)++;
}
last = insn;
/* Skip over insns that are part of a group. */
while (SCHED_GROUP_P (insn))
{
insn = prev_nonnote_insn (insn);
priority (insn);
}
}
insn = PREV_INSN (insn);
/* Don't overrun the bounds of the basic block. */
if (insn == prev_head)
break;
}
/* Assign priorities to instructions. Also check whether they
are in priority order already. If so then I will be nonnegative.
We use this shortcut only before reloading. */
#if 0
i = reload_completed ? DONE_PRIORITY : MAX_PRIORITY;
#endif
for (; insn != prev_head; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
priority (insn);
if (INSN_REF_COUNT (insn) == 0)
{
if (last == 0)
ready[n_ready++] = insn;
else
{
/* Make this dependent on the last of the instructions
that must remain in order at the end of the block. */
add_dependence (last, insn, REG_DEP_ANTI);
INSN_REF_COUNT (insn) = 1;
}
}
if (SCHED_GROUP_P (insn))
{
while (SCHED_GROUP_P (insn))
{
insn = prev_nonnote_insn (insn);
priority (insn);
}
continue;
}
#if 0
if (i < 0)
continue;
if (INSN_PRIORITY (insn) < i)
i = INSN_PRIORITY (insn);
else if (INSN_PRIORITY (insn) > i)
i = DONE_PRIORITY;
#endif
}
}
#if 0
/* This short-cut doesn't work. It does not count call insns crossed by
registers in reg_sometimes_live. It does not mark these registers as
dead if they die in this block. It does not mark these registers live
(or create new reg_sometimes_live entries if necessary) if they are born
in this block.
The easy solution is to just always schedule a block. These blocks tend
to be very short, so this doesn't slow down this pass by much. */
/* If existing order is good, don't bother to reorder. */
if (i != DONE_PRIORITY)
{
if (file)
fprintf (file, ";; already scheduled\n");
if (reload_completed == 0)
{
for (i = 0; i < sometimes_max; i++)
regs_sometimes_live[i].live_length += n_insns;
finish_sometimes_live (regs_sometimes_live, sometimes_max);
}
free_pending_lists ();
goto ret;
}
#endif
/* Scan all the insns to be scheduled, removing NOTE insns
and register death notes.
Line number NOTE insns end up in NOTE_LIST.
Register death notes end up in DEAD_NOTES.
Recreate the register life information for the end of this basic
block. */
if (reload_completed == 0)
{
COPY_REG_SET (bb_live_regs, BASIC_BLOCK (b)->global_live_at_start);
CLEAR_REG_SET (bb_dead_regs);
if (b == 0)
{
/* This is the first block in the function. There may be insns
before head that we can't schedule. We still need to examine
them though for accurate register lifetime analysis. */
/* We don't want to remove any REG_DEAD notes as the code below
does. */
for (insn = BLOCK_HEAD (b); insn != head;
insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* See if the register gets born here. */
/* We must check for registers being born before we check for
registers dying. It is possible for a register to be born
and die in the same insn, e.g. reading from a volatile
memory location into an otherwise unused register. Such
a register must be marked as dead after this insn. */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
sched_note_set (PATTERN (insn), 0);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
/* ??? This code is obsolete and should be deleted. It
is harmless though, so we will leave it in for now. */
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
}
/* Each call clobbers (makes live) all call-clobbered regs
that are not global or fixed. Note that the function-value
reg is a call_clobbered reg. */
if (GET_CODE (insn) == CALL_INSN)
{
int j;
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
if (call_used_regs[j] && ! global_regs[j]
&& ! fixed_regs[j])
{
SET_REGNO_REG_SET (bb_live_regs, j);
CLEAR_REGNO_REG_SET (bb_dead_regs, j);
}
}
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
{
if ((REG_NOTE_KIND (link) == REG_DEAD
|| REG_NOTE_KIND (link) == REG_UNUSED)
/* Verify that the REG_NOTE has a valid value. */
&& GET_CODE (XEXP (link, 0)) == REG)
{
register int regno = REGNO (XEXP (link, 0));
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno,
GET_MODE (XEXP (link, 0)));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno + j);
SET_REGNO_REG_SET (bb_dead_regs, regno + j);
}
}
else
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
SET_REGNO_REG_SET (bb_dead_regs, regno);
}
}
}
}
}
}
/* If debugging information is being produced, keep track of the line
number notes for each insn. */
if (write_symbols != NO_DEBUG)
{
/* We must use the true line number for the first insn in the block
that was computed and saved at the start of this pass. We can't
use the current line number, because scheduling of the previous
block may have changed the current line number. */
rtx line = line_note_head[b];
for (insn = BLOCK_HEAD (b);
insn != next_tail;
insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
line = insn;
else
LINE_NOTE (insn) = line;
}
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
{
rtx prev, next, link;
/* Farm out notes. This is needed to keep the debugger from
getting completely deranged. */
if (GET_CODE (insn) == NOTE)
{
prev = insn;
insn = unlink_notes (insn, next_tail);
if (prev == tail)
abort ();
if (prev == head)
abort ();
if (insn == next_tail)
abort ();
}
if (reload_completed == 0
&& GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* See if the register gets born here. */
/* We must check for registers being born before we check for
registers dying. It is possible for a register to be born and
die in the same insn, e.g. reading from a volatile memory
location into an otherwise unused register. Such a register
must be marked as dead after this insn. */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
sched_note_set (PATTERN (insn), 0);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
/* ??? This code is obsolete and should be deleted. It
is harmless though, so we will leave it in for now. */
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
}
/* Each call clobbers (makes live) all call-clobbered regs that are
not global or fixed. Note that the function-value reg is a
call_clobbered reg. */
if (GET_CODE (insn) == CALL_INSN)
{
int j;
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
if (call_used_regs[j] && ! global_regs[j]
&& ! fixed_regs[j])
{
SET_REGNO_REG_SET (bb_live_regs, j);
CLEAR_REGNO_REG_SET (bb_dead_regs, j);
}
}
/* Need to know what registers this insn kills. */
for (prev = 0, link = REG_NOTES (insn); link; link = next)
{
next = XEXP (link, 1);
if ((REG_NOTE_KIND (link) == REG_DEAD
|| REG_NOTE_KIND (link) == REG_UNUSED)
/* Verify that the REG_NOTE has a valid value. */
&& GET_CODE (XEXP (link, 0)) == REG)
{
register int regno = REGNO (XEXP (link, 0));
/* Only unlink REG_DEAD notes; leave REG_UNUSED notes
alone. */
if (REG_NOTE_KIND (link) == REG_DEAD)
{
if (prev)
XEXP (prev, 1) = next;
else
REG_NOTES (insn) = next;
XEXP (link, 1) = dead_notes;
dead_notes = link;
}
else
prev = link;
if (regno < FIRST_PSEUDO_REGISTER)
{
int j = HARD_REGNO_NREGS (regno,
GET_MODE (XEXP (link, 0)));
while (--j >= 0)
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno + j);
SET_REGNO_REG_SET (bb_dead_regs, regno + j);
}
}
else
{
CLEAR_REGNO_REG_SET (bb_live_regs, regno);
SET_REGNO_REG_SET (bb_dead_regs, regno);
}
}
else
prev = link;
}
}
}
if (reload_completed == 0)
{
/* Keep track of register lives. */
old_live_regs = ALLOCA_REG_SET ();
regs_sometimes_live
= (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
sometimes_max = 0;
/* Start with registers live at end. */
COPY_REG_SET (old_live_regs, bb_live_regs);
EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, 0, j,
{
sometimes_max
= new_sometimes_live (regs_sometimes_live,
j, sometimes_max);
});
}
SCHED_SORT (ready, n_ready, 1);
if (file)
{
fprintf (file, ";; ready list initially:\n;; ");
for (i = 0; i < n_ready; i++)
fprintf (file, "%d ", INSN_UID (ready[i]));
fprintf (file, "\n\n");
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
if (INSN_PRIORITY (insn) > 0)
fprintf (file, ";; insn[%4d]: priority = %4d, ref_count = %4d\n",
INSN_UID (insn), INSN_PRIORITY (insn),
INSN_REF_COUNT (insn));
}
/* Now HEAD and TAIL are going to become disconnected
entirely from the insn chain. */
tail = 0;
/* Q_SIZE will always be zero here. */
q_ptr = 0; clock = 0;
bzero ((char *) insn_queue, sizeof (insn_queue));
/* Now, perform list scheduling. */
/* Where we start inserting insns is after TAIL. */
last = next_tail;
new_needs = (NEXT_INSN (prev_head) == BLOCK_HEAD (b)
? NEED_HEAD : NEED_NOTHING);
if (PREV_INSN (next_tail) == BLOCK_END (b))
new_needs |= NEED_TAIL;
new_ready = n_ready;
while (sched_n_insns < n_insns)
{
q_ptr = NEXT_Q (q_ptr); clock++;
/* Add all pending insns that can be scheduled without stalls to the
ready list. */
for (insn = insn_queue[q_ptr]; insn; insn = NEXT_INSN (insn))
{
if (file)
fprintf (file, ";; launching %d before %d with no stalls at T-%d\n",
INSN_UID (insn), INSN_UID (last), clock);
ready[new_ready++] = insn;
q_size -= 1;
}
insn_queue[q_ptr] = 0;
/* If there are no ready insns, stall until one is ready and add all
of the pending insns at that point to the ready list. */
if (new_ready == 0)
{
register int stalls;
for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++)
if ((insn = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]))
{
for (; insn; insn = NEXT_INSN (insn))
{
if (file)
fprintf (file, ";; launching %d before %d with %d stalls at T-%d\n",
INSN_UID (insn), INSN_UID (last), stalls, clock);
ready[new_ready++] = insn;
q_size -= 1;
}
insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0;
break;
}
q_ptr = NEXT_Q_AFTER (q_ptr, stalls); clock += stalls;
}
/* There should be some instructions waiting to fire. */
if (new_ready == 0)
abort ();
if (file)
{
fprintf (file, ";; ready list at T-%d:", clock);
for (i = 0; i < new_ready; i++)
fprintf (file, " %d (%x)",
INSN_UID (ready[i]), INSN_PRIORITY (ready[i]));
}
/* Sort the ready list and choose the best insn to schedule. Select
which insn should issue in this cycle and queue those that are
blocked by function unit hazards.
N_READY holds the number of items that were scheduled the last time,
minus the one instruction scheduled on the last loop iteration; it
is not modified for any other reason in this loop. */
SCHED_SORT (ready, new_ready, n_ready);
if (MAX_BLOCKAGE > 1)
{
new_ready = schedule_select (ready, new_ready, clock, file);
if (new_ready == 0)
{
if (file)
fprintf (file, "\n");
/* We must set n_ready here, to ensure that sorting always
occurs when we come back to the SCHED_SORT line above. */
n_ready = 0;
continue;
}
}
n_ready = new_ready;
last_scheduled_insn = insn = ready[0];
/* The first insn scheduled becomes the new tail. */
if (tail == 0)
tail = insn;
if (file)
{
fprintf (file, ", now");
for (i = 0; i < n_ready; i++)
fprintf (file, " %d", INSN_UID (ready[i]));
fprintf (file, "\n");
}
if (DONE_PRIORITY_P (insn))
abort ();
if (reload_completed == 0)
{
/* Process this insn, and each insn linked to this one which must
be immediately output after this insn. */
do
{
/* First we kill registers set by this insn, and then we
make registers used by this insn live. This is the opposite
order used above because we are traversing the instructions
backwards. */
/* Strictly speaking, we should scan REG_UNUSED notes and make
every register mentioned there live, however, we will just
kill them again immediately below, so there doesn't seem to
be any reason why we bother to do this. */
/* See if this is the last notice we must take of a register. */
if (GET_CODE (PATTERN (insn)) == SET
|| GET_CODE (PATTERN (insn)) == CLOBBER)
sched_note_set (PATTERN (insn), 1);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
|| GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
sched_note_set (XVECEXP (PATTERN (insn), 0, j), 1);
}
/* This code keeps life analysis information up to date. */
if (GET_CODE (insn) == CALL_INSN)
{
register struct sometimes *p;
/* A call kills all call used registers that are not
global or fixed, except for those mentioned in the call
pattern which will be made live again later. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (call_used_regs[i] && ! global_regs[i]
&& ! fixed_regs[i])
{
CLEAR_REGNO_REG_SET (bb_live_regs, i);
SET_REGNO_REG_SET (bb_dead_regs, i);
}
/* Regs live at the time of a call instruction must not
go in a register clobbered by calls. Record this for
all regs now live. Note that insns which are born or
die in a call do not cross a call, so this must be done
after the killings (above) and before the births
(below). */
p = regs_sometimes_live;
for (i = 0; i < sometimes_max; i++, p++)
if (REGNO_REG_SET_P (bb_live_regs, p->regno))
p->calls_crossed += 1;
}
/* Make every register used live, and add REG_DEAD notes for
registers which were not live before we started. */
attach_deaths_insn (insn);
/* Find registers now made live by that instruction. */
EXECUTE_IF_AND_COMPL_IN_REG_SET (bb_live_regs, old_live_regs, 0, i,
{
sometimes_max
= new_sometimes_live (regs_sometimes_live,
i, sometimes_max);
});
IOR_REG_SET (old_live_regs, bb_live_regs);
/* Count lengths of all regs we are worrying about now,
and handle registers no longer live. */
for (i = 0; i < sometimes_max; i++)
{
register struct sometimes *p = &regs_sometimes_live[i];
int regno = p->regno;
p->live_length += 1;
if (!REGNO_REG_SET_P (bb_live_regs, p->regno))
{
/* This is the end of one of this register's lifetime
segments. Save the lifetime info collected so far,
and clear its bit in the old_live_regs entry. */
sched_reg_live_length[regno] += p->live_length;
sched_reg_n_calls_crossed[regno] += p->calls_crossed;
CLEAR_REGNO_REG_SET (old_live_regs, p->regno);
/* Delete the reg_sometimes_live entry for this reg by
copying the last entry over top of it. */
*p = regs_sometimes_live[--sometimes_max];
/* ...and decrement i so that this newly copied entry
will be processed. */
i--;
}
}
link = insn;
insn = PREV_INSN (insn);
}
while (SCHED_GROUP_P (link));
/* Set INSN back to the insn we are scheduling now. */
insn = ready[0];
}
/* Schedule INSN. Remove it from the ready list. */
ready += 1;
n_ready -= 1;
sched_n_insns += 1;
NEXT_INSN (insn) = last;
PREV_INSN (last) = insn;
/* Everything that precedes INSN now either becomes "ready", if
it can execute immediately before INSN, or "pending", if
there must be a delay. Give INSN high enough priority that
at least one (maybe more) reg-killing insns can be launched
ahead of all others. Mark INSN as scheduled by changing its
priority to -1. */
INSN_PRIORITY (insn) = LAUNCH_PRIORITY;
new_ready = schedule_insn (insn, ready, n_ready, clock);
INSN_PRIORITY (insn) = DONE_PRIORITY;
/* Schedule all prior insns that must not be moved. */
if (SCHED_GROUP_P (insn))
{
/* Disable these insns from being launched, in case one of the
insns in the group has a dependency on an earlier one. */
link = insn;
while (SCHED_GROUP_P (link))
{
/* Disable these insns from being launched by anybody. */
link = PREV_INSN (link);
INSN_REF_COUNT (link) = 0;
}
/* Now handle each group insn like the main insn was handled
above. */
link = insn;
while (SCHED_GROUP_P (link))
{
link = PREV_INSN (link);
sched_n_insns += 1;
/* ??? Why don't we set LAUNCH_PRIORITY here? */
new_ready = schedule_insn (link, ready, new_ready, clock);
INSN_PRIORITY (link) = DONE_PRIORITY;
}
}
/* Put back NOTE_INSN_SETJMP,
NOTE_INSN_{LOOP,EHREGION}_{BEGIN,END} notes. */
/* To prime the loop. We need to handle INSN and all the insns in the
sched group. */
last = NEXT_INSN (insn);
do
{
insn = PREV_INSN (last);
/* Maintain a valid chain so emit_note_before works.
This is necessary because PREV_INSN (insn) isn't valid
(if ! SCHED_GROUP_P) and if it points to an insn already
scheduled, a circularity will result. */
if (! SCHED_GROUP_P (insn))
{
NEXT_INSN (prev_head) = insn;
PREV_INSN (insn) = prev_head;
}
last = reemit_notes (insn, insn);
}
while (SCHED_GROUP_P (insn));
}
if (q_size != 0)
abort ();
if (reload_completed == 0)
finish_sometimes_live (regs_sometimes_live, sometimes_max);
/* HEAD is now the first insn in the chain of insns that
been scheduled by the loop above.
TAIL is the last of those insns. */
head = last;
/* NOTE_LIST is the end of a chain of notes previously found
among the insns. Insert them at the beginning of the insns. */
if (note_list != 0)
{
rtx note_head = note_list;
while (PREV_INSN (note_head))
note_head = PREV_INSN (note_head);
PREV_INSN (head) = note_list;
NEXT_INSN (note_list) = head;
head = note_head;
}
/* There should be no REG_DEAD notes leftover at the end.
In practice, this can occur as the result of bugs in flow, combine.c,
and/or sched.c. The values of the REG_DEAD notes remaining are
meaningless, because dead_notes is just used as a free list. */
#if 1
if (dead_notes != 0)
abort ();
#endif
if (new_needs & NEED_HEAD)
BLOCK_HEAD (b) = head;
PREV_INSN (head) = prev_head;
NEXT_INSN (prev_head) = head;
if (new_needs & NEED_TAIL)
BLOCK_END (b) = tail;
NEXT_INSN (tail) = next_tail;
PREV_INSN (next_tail) = tail;
/* Restore the line-number notes of each insn. */
if (write_symbols != NO_DEBUG)
{
rtx line, note, prev, new;
int notes = 0;
head = BLOCK_HEAD (b);
next_tail = NEXT_INSN (BLOCK_END (b));
/* Determine the current line-number. We want to know the current
line number of the first insn of the block here, in case it is
different from the true line number that was saved earlier. If
different, then we need a line number note before the first insn
of this block. If it happens to be the same, then we don't want to
emit another line number note here. */
for (line = head; line; line = PREV_INSN (line))
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
break;
/* Walk the insns keeping track of the current line-number and inserting
the line-number notes as needed. */
for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
line = insn;
/* This used to emit line number notes before every non-deleted note.
However, this confuses a debugger, because line notes not separated
by real instructions all end up at the same address. I can find no
use for line number notes before other notes, so none are emitted. */
else if (GET_CODE (insn) != NOTE
&& (note = LINE_NOTE (insn)) != 0
&& note != line
&& (line == 0
|| NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line)
|| NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line)))
{
line = note;
prev = PREV_INSN (insn);
if (LINE_NOTE (note))
{
/* Re-use the original line-number note. */
LINE_NOTE (note) = 0;
PREV_INSN (note) = prev;
NEXT_INSN (prev) = note;
PREV_INSN (insn) = note;
NEXT_INSN (note) = insn;
}
else
{
notes++;
new = emit_note_after (NOTE_LINE_NUMBER (note), prev);
NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note);
RTX_INTEGRATED_P (new) = RTX_INTEGRATED_P (note);
}
}
if (file && notes)
fprintf (file, ";; added %d line-number notes\n", notes);
}
if (file)
{
fprintf (file, ";; total time = %d\n;; new basic block head = %d\n;; new basic block end = %d\n\n",
clock, INSN_UID (BLOCK_HEAD (b)), INSN_UID (BLOCK_END (b)));
}
/* Yow! We're done! */
free_pending_lists ();
ret:
FREE_REG_SET (reg_pending_sets);
FREE_REG_SET (old_live_regs);
return;
}
/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are
needed for the hard register mentioned in the note. This can happen
if the reference to the hard register in the original insn was split into
several smaller hard register references in the split insns. */
static void
split_hard_reg_notes (note, first, last)
rtx note, first, last;
{
rtx reg, temp, link;
int n_regs, i, new_reg;
rtx insn;
/* Assume that this is a REG_DEAD note. */
if (REG_NOTE_KIND (note) != REG_DEAD)
abort ();
reg = XEXP (note, 0);
n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg));
for (i = 0; i < n_regs; i++)
{
new_reg = REGNO (reg) + i;
/* Check for references to new_reg in the split insns. */
for (insn = last; ; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (temp = regno_use_in (new_reg, PATTERN (insn))))
{
/* Create a new reg dead note here. */
link = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (link, REG_DEAD);
XEXP (link, 0) = temp;
XEXP (link, 1) = REG_NOTES (insn);
REG_NOTES (insn) = link;
/* If killed multiple registers here, then add in the excess. */
i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1;
break;
}
/* It isn't mentioned anywhere, so no new reg note is needed for
this register. */
if (insn == first)
break;
}
}
}
/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an
insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */
static void
new_insn_dead_notes (pat, insn, last, orig_insn)
rtx pat, insn, last, orig_insn;
{
rtx dest, tem, set;
/* PAT is either a CLOBBER or a SET here. */
dest = XEXP (pat, 0);
while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == SIGN_EXTRACT)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG)
{
/* If the original insn already used this register, we may not add new
notes for it. One example for a split that needs this test is
when a multi-word memory access with register-indirect addressing
is split into multiple memory accesses with auto-increment and
one adjusting add instruction for the address register. */
if (reg_referenced_p (dest, PATTERN (orig_insn)))
return;
for (tem = last; tem != insn; tem = PREV_INSN (tem))
{
if (GET_RTX_CLASS (GET_CODE (tem)) == 'i'
&& reg_overlap_mentioned_p (dest, PATTERN (tem))
&& (set = single_set (tem)))
{
rtx tem_dest = SET_DEST (set);
while (GET_CODE (tem_dest) == ZERO_EXTRACT
|| GET_CODE (tem_dest) == SUBREG
|| GET_CODE (tem_dest) == STRICT_LOW_PART
|| GET_CODE (tem_dest) == SIGN_EXTRACT)
tem_dest = XEXP (tem_dest, 0);
if (! rtx_equal_p (tem_dest, dest))
{
/* Use the same scheme as combine.c, don't put both REG_DEAD
and REG_UNUSED notes on the same insn. */
if (! find_regno_note (tem, REG_UNUSED, REGNO (dest))
&& ! find_regno_note (tem, REG_DEAD, REGNO (dest)))
{
rtx note = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (note, REG_DEAD);
XEXP (note, 0) = dest;
XEXP (note, 1) = REG_NOTES (tem);
REG_NOTES (tem) = note;
}
/* The reg only dies in one insn, the last one that uses
it. */
break;
}
else if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
/* We found an instruction that both uses the register,
and sets it, so no new REG_NOTE is needed for this set. */
break;
}
}
/* If this is a set, it must die somewhere, unless it is the dest of
the original insn, and hence is live after the original insn. Abort
if it isn't supposed to be live after the original insn.
If this is a clobber, then just add a REG_UNUSED note. */
if (tem == insn)
{
int live_after_orig_insn = 0;
rtx pattern = PATTERN (orig_insn);
int i;
if (GET_CODE (pat) == CLOBBER)
{
rtx note = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (note, REG_UNUSED);
XEXP (note, 0) = dest;
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
return;
}
/* The original insn could have multiple sets, so search the
insn for all sets. */
if (GET_CODE (pattern) == SET)
{
if (reg_overlap_mentioned_p (dest, SET_DEST (pattern)))
live_after_orig_insn = 1;
}
else if (GET_CODE (pattern) == PARALLEL)
{
for (i = 0; i < XVECLEN (pattern, 0); i++)
if (GET_CODE (XVECEXP (pattern, 0, i)) == SET
&& reg_overlap_mentioned_p (dest,
SET_DEST (XVECEXP (pattern,
0, i))))
live_after_orig_insn = 1;
}
if (! live_after_orig_insn)
abort ();
}
}
}
/* Subroutine of update_flow_info. Update the value of reg_n_sets for all
registers modified by X. INC is -1 if the containing insn is being deleted,
and is 1 if the containing insn is a newly generated insn. */
static void
update_n_sets (x, inc)
rtx x;
int inc;
{
rtx dest = SET_DEST (x);
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
{
int regno = REGNO (dest);
if (regno < FIRST_PSEUDO_REGISTER)
{
register int i;
int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest));
for (i = regno; i < endregno; i++)
REG_N_SETS (i) += inc;
}
else
REG_N_SETS (regno) += inc;
}
}
/* Updates all flow-analysis related quantities (including REG_NOTES) for
the insns from FIRST to LAST inclusive that were created by splitting
ORIG_INSN. NOTES are the original REG_NOTES. */
void
update_flow_info (notes, first, last, orig_insn)
rtx notes;
rtx first, last;
rtx orig_insn;
{
rtx insn, note;
rtx next;
rtx orig_dest, temp;
rtx set;
/* Get and save the destination set by the original insn. */
orig_dest = single_set (orig_insn);
if (orig_dest)
orig_dest = SET_DEST (orig_dest);
/* Move REG_NOTES from the original insn to where they now belong. */
for (note = notes; note; note = next)
{
next = XEXP (note, 1);
switch (REG_NOTE_KIND (note))
{
case REG_DEAD:
case REG_UNUSED:
/* Move these notes from the original insn to the last new insn where
the register is now set. */
for (insn = last; ; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
{
/* If this note refers to a multiple word hard register, it
may have been split into several smaller hard register
references, so handle it specially. */
temp = XEXP (note, 0);
if (REG_NOTE_KIND (note) == REG_DEAD
&& GET_CODE (temp) == REG
&& REGNO (temp) < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1)
split_hard_reg_notes (note, first, last);
else
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
}
/* Sometimes need to convert REG_UNUSED notes to REG_DEAD
notes. */
/* ??? This won't handle multiple word registers correctly,
but should be good enough for now. */
if (REG_NOTE_KIND (note) == REG_UNUSED
&& GET_CODE (XEXP (note, 0)) != SCRATCH
&& ! dead_or_set_p (insn, XEXP (note, 0)))
PUT_REG_NOTE_KIND (note, REG_DEAD);
/* The reg only dies in one insn, the last one that uses
it. */
break;
}
/* It must die somewhere, fail it we couldn't find where it died.
If this is a REG_UNUSED note, then it must be a temporary
register that was not needed by this instantiation of the
pattern, so we can safely ignore it. */
if (insn == first)
{
if (REG_NOTE_KIND (note) != REG_UNUSED)
abort ();
break;
}
}
break;
case REG_WAS_0:
/* If the insn that set the register to 0 was deleted, this
note cannot be relied on any longer. The destination might
even have been moved to memory.
This was observed for SH4 with execute/920501-6.c compilation,
-O2 -fomit-frame-pointer -finline-functions . */
if (GET_CODE (XEXP (note, 0)) == NOTE
|| INSN_DELETED_P (XEXP (note, 0)))
break;
/* This note applies to the dest of the original insn. Find the
first new insn that now has the same dest, and move the note
there. */
if (! orig_dest)
abort ();
for (insn = first; ; insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (temp = single_set (insn))
&& rtx_equal_p (SET_DEST (temp), orig_dest))
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* The reg is only zero before one insn, the first that
uses it. */
break;
}
/* If this note refers to a multiple word hard
register, it may have been split into several smaller
hard register references. We could split the notes,
but simply dropping them is good enough. */
if (GET_CODE (orig_dest) == REG
&& REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (REGNO (orig_dest),
GET_MODE (orig_dest)) > 1)
break;
/* It must be set somewhere, fail if we couldn't find where it
was set. */
if (insn == last)
abort ();
}
break;
case REG_EQUAL:
case REG_EQUIV:
/* A REG_EQUIV or REG_EQUAL note on an insn with more than one
set is meaningless. Just drop the note. */
if (! orig_dest)
break;
case REG_NO_CONFLICT:
/* These notes apply to the dest of the original insn. Find the last
new insn that now has the same dest, and move the note there. */
if (! orig_dest)
abort ();
for (insn = last; ; insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& (temp = single_set (insn))
&& rtx_equal_p (SET_DEST (temp), orig_dest))
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* Only put this note on one of the new insns. */
break;
}
/* The original dest must still be set someplace. Abort if we
couldn't find it. */
if (insn == first)
{
/* However, if this note refers to a multiple word hard
register, it may have been split into several smaller
hard register references. We could split the notes,
but simply dropping them is good enough. */
if (GET_CODE (orig_dest) == REG
&& REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
&& HARD_REGNO_NREGS (REGNO (orig_dest),
GET_MODE (orig_dest)) > 1)
break;
/* Likewise for multi-word memory references. */
if (GET_CODE (orig_dest) == MEM
&& SIZE_FOR_MODE (orig_dest) > MOVE_MAX)
break;
abort ();
}
}
break;
case REG_LIBCALL:
/* Move a REG_LIBCALL note to the first insn created, and update
the corresponding REG_RETVAL note. */
XEXP (note, 1) = REG_NOTES (first);
REG_NOTES (first) = note;
insn = XEXP (note, 0);
note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
if (note)
XEXP (note, 0) = first;
break;
case REG_EXEC_COUNT:
/* Move a REG_EXEC_COUNT note to the first insn created. */
XEXP (note, 1) = REG_NOTES (first);
REG_NOTES (first) = note;
break;
case REG_RETVAL:
/* Move a REG_RETVAL note to the last insn created, and update
the corresponding REG_LIBCALL note. */
XEXP (note, 1) = REG_NOTES (last);
REG_NOTES (last) = note;
insn = XEXP (note, 0);
note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
if (note)
XEXP (note, 0) = last;
break;
case REG_NONNEG:
case REG_BR_PROB:
/* This should be moved to whichever instruction is a JUMP_INSN. */
for (insn = last; ; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN)
{
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* Only put this note on one of the new insns. */
break;
}
/* Fail if we couldn't find a JUMP_INSN. */
if (insn == first)
abort ();
}
break;
case REG_INC:
/* reload sometimes leaves obsolete REG_INC notes around. */
if (reload_completed)
break;
/* This should be moved to whichever instruction now has the
increment operation. */
abort ();
case REG_LABEL:
/* Should be moved to the new insn(s) which use the label. */
for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL,
XEXP (note, 0),
REG_NOTES (insn));
break;
case REG_CC_SETTER:
case REG_CC_USER:
/* These two notes will never appear until after reorg, so we don't
have to handle them here. */
default:
abort ();
}
}
/* Each new insn created, except the last, has a new set. If the destination
is a register, then this reg is now live across several insns, whereas
previously the dest reg was born and died within the same insn. To
reflect this, we now need a REG_DEAD note on the insn where this
dest reg dies.
Similarly, the new insns may have clobbers that need REG_UNUSED notes. */
for (insn = first; insn != last; insn = NEXT_INSN (insn))
{
rtx pat;
int i;
pat = PATTERN (insn);
if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER)
new_insn_dead_notes (pat, insn, last, orig_insn);
else if (GET_CODE (pat) == PARALLEL)
{
for (i = 0; i < XVECLEN (pat, 0); i++)
if (GET_CODE (XVECEXP (pat, 0, i)) == SET
|| GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER)
new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn);
}
}
/* If any insn, except the last, uses the register set by the last insn,
then we need a new REG_DEAD note on that insn. In this case, there
would not have been a REG_DEAD note for this register in the original
insn because it was used and set within one insn. */
set = single_set (last);
if (set)
{
rtx dest = SET_DEST (set);
while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == STRICT_LOW_PART
|| GET_CODE (dest) == SIGN_EXTRACT)
dest = XEXP (dest, 0);
if (GET_CODE (dest) == REG
/* Global registers are always live, so the code below does not
apply to them. */
&& (REGNO (dest) >= FIRST_PSEUDO_REGISTER
|| ! global_regs[REGNO (dest)]))
{
rtx stop_insn = PREV_INSN (first);
/* If the last insn uses the register that it is setting, then
we don't want to put a REG_DEAD note there. Search backwards
to find the first insn that sets but does not use DEST. */
insn = last;
if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
{
for (insn = PREV_INSN (insn); insn != first;
insn = PREV_INSN (insn))
{
if ((set = single_set (insn))
&& reg_mentioned_p (dest, SET_DEST (set))
&& ! reg_overlap_mentioned_p (dest, SET_SRC (set)))
break;
}
}
/* Now find the first insn that uses but does not set DEST. */
for (insn = PREV_INSN (insn); insn != stop_insn;
insn = PREV_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (dest, PATTERN (insn))
&& (set = single_set (insn)))
{
rtx insn_dest = SET_DEST (set);
while (GET_CODE (insn_dest) == ZERO_EXTRACT
|| GET_CODE (insn_dest) == SUBREG
|| GET_CODE (insn_dest) == STRICT_LOW_PART
|| GET_CODE (insn_dest) == SIGN_EXTRACT)
insn_dest = XEXP (insn_dest, 0);
if (insn_dest != dest)
{
note = rtx_alloc (EXPR_LIST);
PUT_REG_NOTE_KIND (note, REG_DEAD);
XEXP (note, 0) = dest;
XEXP (note, 1) = REG_NOTES (insn);
REG_NOTES (insn) = note;
/* The reg only dies in one insn, the last one
that uses it. */
break;
}
}
}
}
}
/* If the original dest is modifying a multiple register target, and the
original instruction was split such that the original dest is now set
by two or more SUBREG sets, then the split insns no longer kill the
destination of the original insn.
In this case, if there exists an instruction in the same basic block,
before the split insn, which uses the original dest, and this use is
killed by the original insn, then we must remove the REG_DEAD note on
this insn, because it is now superfluous.
This does not apply when a hard register gets split, because the code
knows how to handle overlapping hard registers properly. */
if (orig_dest && GET_CODE (orig_dest) == REG)
{
int found_orig_dest = 0;
int found_split_dest = 0;
for (insn = first; ; insn = NEXT_INSN (insn))
{
rtx pat = PATTERN (insn);
int i = GET_CODE (pat) == PARALLEL ? XVECLEN (pat, 0) : 0;
set = pat;
for (;;)
{
if (GET_CODE (set) == SET)
{
if (GET_CODE (SET_DEST (set)) == REG
&& REGNO (SET_DEST (set)) == REGNO (orig_dest))
{
found_orig_dest = 1;
break;
}
else if (GET_CODE (SET_DEST (set)) == SUBREG
&& SUBREG_REG (SET_DEST (set)) == orig_dest)
{
found_split_dest = 1;
break;
}
}
if (--i < 0)
break;
set = XVECEXP (pat, 0, i);
}
if (insn == last)
break;
}
if (found_split_dest)
{
/* Search backwards from FIRST, looking for the first insn that uses
the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN.
If we find an insn, and it has a REG_DEAD note, then delete the
note. */
for (insn = first; insn; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == JUMP_INSN)
break;
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& reg_mentioned_p (orig_dest, insn))
{
note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest));
if (note)
remove_note (insn, note);
}
}
}
else if (! found_orig_dest)
{
int i, regno;
/* Should never reach here for a pseudo reg. */
if (REGNO (orig_dest) >= FIRST_PSEUDO_REGISTER)
abort ();
/* This can happen for a hard register, if the splitter
does not bother to emit instructions which would be no-ops.
We try to verify that this is the case by checking to see if
the original instruction uses all of the registers that it
set. This case is OK, because deleting a no-op can not affect
REG_DEAD notes on other insns. If this is not the case, then
abort. */
regno = REGNO (orig_dest);
for (i = HARD_REGNO_NREGS (regno, GET_MODE (orig_dest)) - 1;
i >= 0; i--)
if (! refers_to_regno_p (regno + i, regno + i + 1, orig_insn,
NULL_PTR))
break;
if (i >= 0)
abort ();
}
}
/* Update reg_n_sets. This is necessary to prevent local alloc from
converting REG_EQUAL notes to REG_EQUIV when splitting has modified
a reg from set once to set multiple times. */
{
rtx x = PATTERN (orig_insn);
RTX_CODE code = GET_CODE (x);
if (code == SET || code == CLOBBER)
update_n_sets (x, -1);
else if (code == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
update_n_sets (XVECEXP (x, 0, i), -1);
}
}
for (insn = first; ; insn = NEXT_INSN (insn))
{
x = PATTERN (insn);
code = GET_CODE (x);
if (code == SET || code == CLOBBER)
update_n_sets (x, 1);
else if (code == PARALLEL)
{
int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
update_n_sets (XVECEXP (x, 0, i), 1);
}
}
if (insn == last)
break;
}
}
}
/* The one entry point in this file. DUMP_FILE is the dump file for
this pass. */
void
schedule_insns (dump_file)
FILE *dump_file;
{
int max_uid = MAX_INSNS_PER_SPLIT * (get_max_uid () + 1);
int b;
rtx insn;
/* Taking care of this degenerate case makes the rest of
this code simpler. */
if (n_basic_blocks == 0)
return;
/* Create an insn here so that we can hang dependencies off of it later. */
sched_before_next_call
= gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,
NULL_RTX, 0, NULL_RTX, NULL_RTX);
/* Initialize the unused_*_lists. We can't use the ones left over from
the previous function, because gcc has freed that memory. We can use
the ones left over from the first sched pass in the second pass however,
so only clear them on the first sched pass. The first pass is before
reload if flag_schedule_insns is set, otherwise it is afterwards. */
if (reload_completed == 0 || ! flag_schedule_insns)
{
unused_insn_list = 0;
unused_expr_list = 0;
}
/* We create no insns here, only reorder them, so we
remember how far we can cut back the stack on exit. */
/* Allocate data for this pass. See comments, above,
for what these vectors do.
We use xmalloc instead of alloca, because max_uid can be very large
when there is a lot of function inlining. If we used alloca, we could
exceed stack limits on some hosts for some inputs. */
insn_luid = (int *) xmalloc (max_uid * sizeof (int));
insn_priority = (int *) xmalloc (max_uid * sizeof (int));
insn_tick = (int *) xmalloc (max_uid * sizeof (int));
insn_costs = (short *) xmalloc (max_uid * sizeof (short));
insn_units = (short *) xmalloc (max_uid * sizeof (short));
insn_blockage = (unsigned int *) xmalloc (max_uid * sizeof (unsigned int));
insn_ref_count = (int *) xmalloc (max_uid * sizeof (int));
if (reload_completed == 0)
{
sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int));
sched_reg_live_length = (int *) alloca (max_regno * sizeof (int));
bb_dead_regs = ALLOCA_REG_SET ();
bb_live_regs = ALLOCA_REG_SET ();
bzero ((char *) sched_reg_n_calls_crossed, max_regno * sizeof (int));
bzero ((char *) sched_reg_live_length, max_regno * sizeof (int));
}
else
{
sched_reg_n_calls_crossed = 0;
sched_reg_live_length = 0;
bb_dead_regs = 0;
bb_live_regs = 0;
}
init_alias_analysis ();
if (write_symbols != NO_DEBUG)
{
rtx line;
line_note = (rtx *) xmalloc (max_uid * sizeof (rtx));
bzero ((char *) line_note, max_uid * sizeof (rtx));
line_note_head = (rtx *) alloca (n_basic_blocks * sizeof (rtx));
bzero ((char *) line_note_head, n_basic_blocks * sizeof (rtx));
/* Determine the line-number at the start of each basic block.
This must be computed and saved now, because after a basic block's
predecessor has been scheduled, it is impossible to accurately
determine the correct line number for the first insn of the block. */
for (b = 0; b < n_basic_blocks; b++)
for (line = BLOCK_HEAD (b); line; line = PREV_INSN (line))
if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
{
line_note_head[b] = line;
break;
}
}
bzero ((char *) insn_luid, max_uid * sizeof (int));
bzero ((char *) insn_priority, max_uid * sizeof (int));
bzero ((char *) insn_tick, max_uid * sizeof (int));
bzero ((char *) insn_costs, max_uid * sizeof (short));
bzero ((char *) insn_units, max_uid * sizeof (short));
bzero ((char *) insn_blockage, max_uid * sizeof (unsigned int));
bzero ((char *) insn_ref_count, max_uid * sizeof (int));
/* Schedule each basic block, block by block. */
/* ??? Add a NOTE after the last insn of the last basic block. It is not
known why this is done. */
/* ??? Perhaps it's done to ensure NEXT_TAIL in schedule_block is a
valid insn. */
insn = BLOCK_END (n_basic_blocks-1);
if (NEXT_INSN (insn) == 0
|| (GET_CODE (insn) != NOTE
&& GET_CODE (insn) != CODE_LABEL
/* Don't emit a NOTE if it would end up between an unconditional
jump and a BARRIER. */
&& ! (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (NEXT_INSN (insn)) == BARRIER)))
emit_note_after (NOTE_INSN_DELETED, BLOCK_END (n_basic_blocks-1));
for (b = 0; b < n_basic_blocks; b++)
{
note_list = 0;
split_block_insns (b, reload_completed == 0 || ! flag_schedule_insns);
schedule_block (b, dump_file);
#ifdef USE_C_ALLOCA
alloca (0);
#endif
}
/* Reposition the prologue and epilogue notes in case we moved the
prologue/epilogue insns. */
if (reload_completed)
reposition_prologue_and_epilogue_notes (get_insns ());
if (write_symbols != NO_DEBUG)
{
rtx line = 0;
rtx insn = get_insns ();
int active_insn = 0;
int notes = 0;
/* Walk the insns deleting redundant line-number notes. Many of these
are already present. The remainder tend to occur at basic
block boundaries. */
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
{
/* If there are no active insns following, INSN is redundant. */
if (active_insn == 0)
{
notes++;
NOTE_SOURCE_FILE (insn) = 0;
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
}
/* If the line number is unchanged, LINE is redundant. */
else if (line
&& NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn)
&& NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn))
{
notes++;
NOTE_SOURCE_FILE (line) = 0;
NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED;
line = insn;
}
else
line = insn;
active_insn = 0;
}
else if (! ((GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
|| (GET_CODE (insn) == INSN
&& (GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER))))
active_insn++;
if (dump_file && notes)
fprintf (dump_file, ";; deleted %d line-number notes\n", notes);
}
if (reload_completed == 0)
{
int regno;
for (regno = 0; regno < max_regno; regno++)
if (sched_reg_live_length[regno])
{
if (dump_file)
{
if (REG_LIVE_LENGTH (regno) > sched_reg_live_length[regno])
fprintf (dump_file,
";; register %d life shortened from %d to %d\n",
regno, REG_LIVE_LENGTH (regno),
sched_reg_live_length[regno]);
/* Negative values are special; don't overwrite the current
reg_live_length value if it is negative. */
else if (REG_LIVE_LENGTH (regno) < sched_reg_live_length[regno]
&& REG_LIVE_LENGTH (regno) >= 0)
fprintf (dump_file,
";; register %d life extended from %d to %d\n",
regno, REG_LIVE_LENGTH (regno),
sched_reg_live_length[regno]);
if (! REG_N_CALLS_CROSSED (regno)
&& sched_reg_n_calls_crossed[regno])
fprintf (dump_file,
";; register %d now crosses calls\n", regno);
else if (REG_N_CALLS_CROSSED (regno)
&& ! sched_reg_n_calls_crossed[regno]
&& REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
fprintf (dump_file,
";; register %d no longer crosses calls\n", regno);
}
/* Negative values are special; don't overwrite the current
reg_live_length value if it is negative. */
if (REG_LIVE_LENGTH (regno) >= 0)
REG_LIVE_LENGTH (regno) = sched_reg_live_length[regno];
/* We can't change the value of reg_n_calls_crossed to zero for
pseudos which are live in more than one block.
This is because combine might have made an optimization which
invalidated basic_block_live_at_start and reg_n_calls_crossed,
but it does not update them. If we update reg_n_calls_crossed
here, the two variables are now inconsistent, and this might
confuse the caller-save code into saving a register that doesn't
need to be saved. This is only a problem when we zero calls
crossed for a pseudo live in multiple basic blocks.
Alternatively, we could try to correctly update basic block live
at start here in sched, but that seems complicated. */
if (sched_reg_n_calls_crossed[regno]
|| REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
REG_N_CALLS_CROSSED (regno) = sched_reg_n_calls_crossed[regno];
}
}
free (insn_luid);
free (insn_priority);
free (insn_tick);
free (insn_costs);
free (insn_units);
free (insn_blockage);
free (insn_ref_count);
if (write_symbols != NO_DEBUG)
free (line_note);
if (reload_completed == 0)
{
FREE_REG_SET (bb_dead_regs);
FREE_REG_SET (bb_live_regs);
}
}
#endif /* INSN_SCHEDULING */
Index: head/contrib/gcc/tlink.c
===================================================================
--- head/contrib/gcc/tlink.c (revision 52750)
+++ head/contrib/gcc/tlink.c (revision 52751)
@@ -1,728 +1,732 @@
/* Scan linker error messages for missing template instantiations and provide
them.
Copyright (C) 1995, 1998 Free Software Foundation, Inc.
Contributed by Jason Merrill (jason@cygnus.com).
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
#include "hash.h"
#include "demangle.h"
#include "toplev.h"
#include "collect2.h"
#define MAX_ITERATIONS 17
/* Obstack allocation and deallocation routines. */
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
/* Defined in collect2.c. */
extern int vflag, debug;
extern char *ldout;
extern char *c_file_name;
extern struct obstack temporary_obstack;
extern struct obstack permanent_obstack;
extern char * temporary_firstobj;
/* Defined in the automatically-generated underscore.c. */
extern int prepends_underscore;
static int tlink_verbose;
/* Hash table boilerplate for working with hash.[ch]. We have hash tables
for symbol names, file names, and demangled symbols. */
typedef struct symbol_hash_entry
{
struct hash_entry root;
struct file_hash_entry *file;
int chosen;
int tweaking;
int tweaked;
} symbol;
typedef struct file_hash_entry
{
struct hash_entry root;
const char *args;
const char *dir;
const char *main;
int tweaking;
} file;
typedef struct demangled_hash_entry
{
struct hash_entry root;
const char *mangled;
} demangled;
static struct hash_table symbol_table;
static struct hash_entry * symbol_hash_newfunc PARAMS ((struct hash_entry *,
struct hash_table *,
hash_table_key));
static struct symbol_hash_entry * symbol_hash_lookup PARAMS ((const char *,
boolean));
static struct hash_entry * file_hash_newfunc PARAMS ((struct hash_entry *,
struct hash_table *,
hash_table_key));
static struct file_hash_entry * file_hash_lookup PARAMS ((const char *));
static struct hash_entry * demangled_hash_newfunc PARAMS ((struct hash_entry *,
struct hash_table *,
hash_table_key));
static struct demangled_hash_entry *
demangled_hash_lookup PARAMS ((const char *, boolean));
static void symbol_push PARAMS ((symbol *));
static symbol * symbol_pop PARAMS ((void));
static void file_push PARAMS ((file *));
static file * file_pop PARAMS ((void));
static void tlink_init PARAMS ((void));
static int tlink_execute PARAMS ((char *, char **, char *));
static char * frob_extension PARAMS ((char *, const char *));
static char * obstack_fgets PARAMS ((FILE *, struct obstack *));
static char * tfgets PARAMS ((FILE *));
static char * pfgets PARAMS ((FILE *));
static void freadsym PARAMS ((FILE *, file *, int));
static void read_repo_file PARAMS ((file *));
static void maybe_tweak PARAMS ((char *, file *));
static int recompile_files PARAMS ((void));
static int read_repo_files PARAMS ((char **));
static void demangle_new_symbols PARAMS ((void));
static int scan_linker_output PARAMS ((const char *));
/* Create a new entry for the symbol hash table.
Passed to hash_table_init. */
static struct hash_entry *
symbol_hash_newfunc (entry, table, string)
struct hash_entry *entry;
struct hash_table *table;
hash_table_key string;
{
struct symbol_hash_entry *ret = (struct symbol_hash_entry *) entry;
if (ret == NULL)
{
ret = ((struct symbol_hash_entry *)
hash_allocate (table, sizeof (struct symbol_hash_entry)));
if (ret == NULL)
return NULL;
}
ret->file = NULL;
ret->chosen = 0;
ret->tweaking = 0;
ret->tweaked = 0;
return (struct hash_entry *) ret;
}
/* Look up an entry in the symbol hash table. */
static struct symbol_hash_entry *
symbol_hash_lookup (string, create)
const char *string;
boolean create;
{
return ((struct symbol_hash_entry *)
hash_lookup (&symbol_table, (hash_table_key) string,
create, &string_copy));
}
static struct hash_table file_table;
/* Create a new entry for the file hash table.
Passed to hash_table_init. */
static struct hash_entry *
file_hash_newfunc (entry, table, string)
struct hash_entry *entry;
struct hash_table *table;
hash_table_key string;
{
struct file_hash_entry *ret = (struct file_hash_entry *) entry;
if (ret == NULL)
{
ret = ((struct file_hash_entry *)
hash_allocate (table, sizeof (struct file_hash_entry)));
if (ret == NULL)
return NULL;
}
ret->args = NULL;
ret->dir = NULL;
ret->main = NULL;
ret->tweaking = 0;
return (struct hash_entry *) ret;
}
/* Look up an entry in the file hash table. */
static struct file_hash_entry *
file_hash_lookup (string)
const char *string;
{
return ((struct file_hash_entry *)
hash_lookup (&file_table, (hash_table_key) string, true,
&string_copy));
}
static struct hash_table demangled_table;
/* Create a new entry for the demangled name hash table.
Passed to hash_table_init. */
static struct hash_entry *
demangled_hash_newfunc (entry, table, string)
struct hash_entry *entry;
struct hash_table *table;
hash_table_key string;
{
struct demangled_hash_entry *ret = (struct demangled_hash_entry *) entry;
if (ret == NULL)
{
ret = ((struct demangled_hash_entry *)
hash_allocate (table, sizeof (struct demangled_hash_entry)));
if (ret == NULL)
return NULL;
}
ret->mangled = NULL;
return (struct hash_entry *) ret;
}
/* Look up an entry in the demangled name hash table. */
static struct demangled_hash_entry *
demangled_hash_lookup (string, create)
const char *string;
boolean create;
{
return ((struct demangled_hash_entry *)
hash_lookup (&demangled_table, (hash_table_key) string,
create, &string_copy));
}
/* Stack code. */
struct symbol_stack_entry
{
symbol *value;
struct symbol_stack_entry *next;
};
struct obstack symbol_stack_obstack;
struct symbol_stack_entry *symbol_stack;
struct file_stack_entry
{
file *value;
struct file_stack_entry *next;
};
struct obstack file_stack_obstack;
struct file_stack_entry *file_stack;
static void
symbol_push (p)
symbol *p;
{
struct symbol_stack_entry *ep = (struct symbol_stack_entry *) obstack_alloc
(&symbol_stack_obstack, sizeof (struct symbol_stack_entry));
ep->value = p;
ep->next = symbol_stack;
symbol_stack = ep;
}
static symbol *
symbol_pop ()
{
struct symbol_stack_entry *ep = symbol_stack;
symbol *p;
if (ep == NULL)
return NULL;
p = ep->value;
symbol_stack = ep->next;
obstack_free (&symbol_stack_obstack, ep);
return p;
}
static void
file_push (p)
file *p;
{
struct file_stack_entry *ep;
if (p->tweaking)
return;
ep = (struct file_stack_entry *) obstack_alloc
(&file_stack_obstack, sizeof (struct file_stack_entry));
ep->value = p;
ep->next = file_stack;
file_stack = ep;
p->tweaking = 1;
}
static file *
file_pop ()
{
struct file_stack_entry *ep = file_stack;
file *p;
if (ep == NULL)
return NULL;
p = ep->value;
file_stack = ep->next;
obstack_free (&file_stack_obstack, ep);
p->tweaking = 0;
return p;
}
/* Other machinery. */
/* Initialize the tlink machinery. Called from do_tlink. */
static void
tlink_init ()
{
char *p;
hash_table_init (&symbol_table, symbol_hash_newfunc, &string_hash,
&string_compare);
hash_table_init (&file_table, file_hash_newfunc, &string_hash,
&string_compare);
hash_table_init (&demangled_table, demangled_hash_newfunc,
&string_hash, &string_compare);
obstack_begin (&symbol_stack_obstack, 0);
obstack_begin (&file_stack_obstack, 0);
p = getenv ("TLINK_VERBOSE");
if (p)
tlink_verbose = atoi (p);
else
{
tlink_verbose = 1;
if (vflag)
tlink_verbose = 2;
if (debug)
tlink_verbose = 3;
}
}
static int
tlink_execute (prog, argv, redir)
char *prog;
char **argv;
char *redir;
{
collect_execute (prog, argv, redir);
return collect_wait (prog);
}
static char *
frob_extension (s, ext)
char *s;
const char *ext;
{
char *p = rindex (s, '/');
if (! p)
p = s;
p = rindex (p, '.');
if (! p)
p = s + strlen (s);
obstack_grow (&temporary_obstack, s, p - s);
return obstack_copy0 (&temporary_obstack, ext, strlen (ext));
}
static char *
obstack_fgets (stream, ob)
FILE *stream;
struct obstack *ob;
{
int c;
while ((c = getc (stream)) != EOF && c != '\n')
obstack_1grow (ob, c);
if (obstack_object_size (ob) == 0)
return NULL;
obstack_1grow (ob, '\0');
return obstack_finish (ob);
}
static char *
tfgets (stream)
FILE *stream;
{
return obstack_fgets (stream, &temporary_obstack);
}
static char *
pfgets (stream)
FILE *stream;
{
return obstack_fgets (stream, &permanent_obstack);
}
/* Real tlink code. */
/* Subroutine of read_repo_file. We are reading the repo file for file F,
which is coming in on STREAM, and the symbol that comes next in STREAM
is offerred, chosen or provided if CHOSEN is 0, 1 or 2, respectively.
XXX "provided" is unimplemented, both here and in the compiler. */
static void
freadsym (stream, f, chosen)
FILE *stream;
file *f;
int chosen;
{
symbol *sym;
{
char *name = tfgets (stream);
sym = symbol_hash_lookup (name, true);
}
if (sym->file == NULL)
{
/* We didn't have this symbol already, so we choose this file. */
symbol_push (sym);
sym->file = f;
sym->chosen = chosen;
}
else if (chosen)
{
/* We want this file; cast aside any pretender. */
if (sym->chosen && sym->file != f)
{
if (sym->chosen == 1)
file_push (sym->file);
else
{
file_push (f);
f = sym->file;
chosen = sym->chosen;
}
}
sym->file = f;
sym->chosen = chosen;
}
}
/* Read in the repo file denoted by F, and record all its information. */
static void
read_repo_file (f)
file *f;
{
char c;
FILE *stream = fopen ((char*) f->root.key, "r");
if (tlink_verbose >= 2)
fprintf (stderr, "collect: reading %s\n",
(char*) f->root.key);
while (fscanf (stream, "%c ", &c) == 1)
{
switch (c)
{
case 'A':
f->args = pfgets (stream);
break;
case 'D':
f->dir = pfgets (stream);
break;
case 'M':
f->main = pfgets (stream);
break;
case 'P':
freadsym (stream, f, 2);
break;
case 'C':
freadsym (stream, f, 1);
break;
case 'O':
freadsym (stream, f, 0);
break;
}
obstack_free (&temporary_obstack, temporary_firstobj);
}
fclose (stream);
if (f->args == NULL)
f->args = getenv ("COLLECT_GCC_OPTIONS");
if (f->dir == NULL)
f->dir = ".";
}
/* We might want to modify LINE, which is a symbol line from file F. We do
this if either we saw an error message referring to the symbol in
question, or we have already allocated the symbol to another file and
this one wants to emit it as well. */
static void
maybe_tweak (line, f)
char *line;
file *f;
{
symbol *sym = symbol_hash_lookup (line + 2, false);
if ((sym->file == f && sym->tweaking)
|| (sym->file != f && line[0] == 'C'))
{
sym->tweaking = 0;
sym->tweaked = 1;
if (line[0] == 'O')
line[0] = 'C';
else
line[0] = 'O';
}
}
/* Update the repo files for each of the object files we have adjusted and
recompile.
XXX Should this use collect_execute instead of system? */
static int
recompile_files ()
{
file *f;
while ((f = file_pop ()) != NULL)
{
char *line, *command;
FILE *stream = fopen ((char*) f->root.key, "r");
char *outname = frob_extension ((char*) f->root.key, ".rnw");
FILE *output = fopen (outname, "w");
while ((line = tfgets (stream)) != NULL)
{
switch (line[0])
{
case 'C':
case 'O':
maybe_tweak (line, f);
}
fprintf (output, "%s\n", line);
}
fclose (stream);
fclose (output);
rename (outname, (char*) f->root.key);
obstack_grow (&temporary_obstack, "cd ", 3);
obstack_grow (&temporary_obstack, f->dir, strlen (f->dir));
obstack_grow (&temporary_obstack, "; ", 2);
obstack_grow (&temporary_obstack, c_file_name, strlen (c_file_name));
obstack_1grow (&temporary_obstack, ' ');
obstack_grow (&temporary_obstack, f->args, strlen (f->args));
obstack_1grow (&temporary_obstack, ' ');
command = obstack_copy0 (&temporary_obstack, f->main, strlen (f->main));
if (tlink_verbose)
fprintf (stderr, "collect: recompiling %s\n", f->main);
if (tlink_verbose >= 3)
fprintf (stderr, "%s\n", command);
if (system (command) != 0)
return 0;
read_repo_file (f);
obstack_free (&temporary_obstack, temporary_firstobj);
}
return 1;
}
/* The first phase of processing: determine which object files have
.rpo files associated with them, and read in the information. */
static int
read_repo_files (object_lst)
char **object_lst;
{
char **object = object_lst;
for (; *object; object++)
{
char *p = frob_extension (*object, ".rpo");
file *f;
if (! file_exists (p))
continue;
f = file_hash_lookup (p);
read_repo_file (f);
}
if (file_stack != NULL && ! recompile_files ())
return 0;
return (symbol_stack != NULL);
}
/* Add the demangled forms of any new symbols to the hash table. */
static void
demangle_new_symbols ()
{
symbol *sym;
while ((sym = symbol_pop ()) != NULL)
{
demangled *dem;
char *p = cplus_demangle ((char*) sym->root.key,
DMGL_PARAMS | DMGL_ANSI);
if (! p)
continue;
dem = demangled_hash_lookup (p, true);
dem->mangled = (char*) sym->root.key;
}
}
/* Step through the output of the linker, in the file named FNAME, and
adjust the settings for each symbol encountered. */
static int
scan_linker_output (fname)
const char *fname;
{
FILE *stream = fopen (fname, "r");
char *line;
while ((line = tfgets (stream)) != NULL)
{
char *p = line, *q;
symbol *sym;
int end;
while (*p && ISSPACE ((unsigned char)*p))
++p;
if (! *p)
continue;
for (q = p; *q && ! ISSPACE ((unsigned char)*q); ++q)
;
/* Try the first word on the line. */
if (*p == '.')
++p;
if (*p == '_' && prepends_underscore)
++p;
end = ! *q;
*q = 0;
sym = symbol_hash_lookup (p, false);
if (! sym && ! end)
/* Try a mangled name in quotes. */
{
char *oldq = q+1;
demangled *dem = 0;
q = 0;
/* First try `GNU style'. */
p = index (oldq, '`');
if (p)
p++, q = index (p, '\'');
/* Then try "double quotes". */
else if (p = index (oldq, '"'), p)
p++, q = index (p, '"');
if (q)
{
*q = 0;
dem = demangled_hash_lookup (p, false);
if (dem)
sym = symbol_hash_lookup (dem->mangled, false);
else
- sym = symbol_hash_lookup (p, false);
+ {
+ if (*p == '_' && prepends_underscore)
+ ++p;
+ sym = symbol_hash_lookup (p, false);
+ }
}
}
if (sym && sym->tweaked)
{
fclose (stream);
return 0;
}
if (sym && !sym->tweaking)
{
if (tlink_verbose >= 2)
fprintf (stderr, "collect: tweaking %s in %s\n",
(char*) sym->root.key, (char*) sym->file->root.key);
sym->tweaking = 1;
file_push (sym->file);
}
obstack_free (&temporary_obstack, temporary_firstobj);
}
fclose (stream);
return (file_stack != NULL);
}
/* Entry point for tlink. Called from main in collect2.c.
Iteratively try to provide definitions for all the unresolved symbols
mentioned in the linker error messages.
LD_ARGV is an array of arguments for the linker.
OBJECT_LST is an array of object files that we may be able to recompile
to provide missing definitions. Currently ignored. */
void
do_tlink (ld_argv, object_lst)
char **ld_argv, **object_lst;
{
int exit = tlink_execute ("ld", ld_argv, ldout);
tlink_init ();
if (exit)
{
int i = 0;
/* Until collect does a better job of figuring out which are object
files, assume that everything on the command line could be. */
if (read_repo_files (ld_argv))
while (exit && i++ < MAX_ITERATIONS)
{
if (tlink_verbose >= 3)
dump_file (ldout);
demangle_new_symbols ();
if (! scan_linker_output (ldout))
break;
if (! recompile_files ())
break;
if (tlink_verbose)
fprintf (stderr, "collect: relinking\n");
exit = tlink_execute ("ld", ld_argv, ldout);
}
}
dump_file (ldout);
unlink (ldout);
if (exit)
{
error ("ld returned %d exit status", exit);
collect_exit (exit);
}
}
Index: head/contrib/gcc/version.c
===================================================================
--- head/contrib/gcc/version.c (revision 52750)
+++ head/contrib/gcc/version.c (revision 52751)
@@ -1 +1 @@
-char *version_string = "2.95.1 19990816 (release)";
+char *version_string = "2.95.2 19991024 (release)";

File Metadata

Mime Type
application/octet-stream
Expires
Tue, Oct 14, 1:23 AM (1 d, 23 h)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
Jh3vvSDrBuoz
Default Alt Text
(4 MB)

Event Timeline