diff options
author | kan <kan@FreeBSD.org> | 2003-07-11 03:40:53 +0000 |
---|---|---|
committer | kan <kan@FreeBSD.org> | 2003-07-11 03:40:53 +0000 |
commit | b2a8872fbe1ec1c49094559ac7b78e6ea4ab7180 (patch) | |
tree | f6b0610f4a17fd26aa234354f050080f789861a4 /contrib/gcc/config/s390 | |
parent | 52e69d78eee5612ac195e0701a5cebe40d1ab0e1 (diff) | |
download | FreeBSD-src-b2a8872fbe1ec1c49094559ac7b78e6ea4ab7180.zip FreeBSD-src-b2a8872fbe1ec1c49094559ac7b78e6ea4ab7180.tar.gz |
Gcc 3.3.1-pre as of 2003-07-11.
Diffstat (limited to 'contrib/gcc/config/s390')
-rw-r--r-- | contrib/gcc/config/s390/fixdfdi.h | 7 | ||||
-rw-r--r-- | contrib/gcc/config/s390/libgcc-glibc.ver | 20 | ||||
-rw-r--r-- | contrib/gcc/config/s390/linux.h | 302 | ||||
-rw-r--r-- | contrib/gcc/config/s390/s390-modes.def | 38 | ||||
-rw-r--r-- | contrib/gcc/config/s390/s390-protos.h | 27 | ||||
-rw-r--r-- | contrib/gcc/config/s390/s390.c | 3534 | ||||
-rw-r--r-- | contrib/gcc/config/s390/s390.h | 1354 | ||||
-rw-r--r-- | contrib/gcc/config/s390/s390.md | 3328 | ||||
-rw-r--r-- | contrib/gcc/config/s390/t-crtstuff | 4 | ||||
-rw-r--r-- | contrib/gcc/config/s390/t-linux64 | 9 |
10 files changed, 5509 insertions, 3114 deletions
diff --git a/contrib/gcc/config/s390/fixdfdi.h b/contrib/gcc/config/s390/fixdfdi.h index 4f1fb35..1f82a9c 100644 --- a/contrib/gcc/config/s390/fixdfdi.h +++ b/contrib/gcc/config/s390/fixdfdi.h @@ -43,6 +43,7 @@ union double_long { UDItype_x ll; }; +UDItype_x __fixunsdfdi (double a1); /* convert double to unsigned int */ UDItype_x @@ -114,6 +115,8 @@ union double_long { UDItype_x ll; }; +DItype_x __fixdfdi (double a1); + /* convert double to int */ DItype_x __fixdfdi (double a1) @@ -184,6 +187,8 @@ union float_long USItype_x l; }; +UDItype_x __fixunssfdi (float a1); + /* convert float to unsigned int */ UDItype_x __fixunssfdi (float a1) @@ -250,6 +255,8 @@ union float_long USItype_x l; }; +DItype_x __fixsfdi (float a1); + /* convert double to int */ DItype_x __fixsfdi (float a1) diff --git a/contrib/gcc/config/s390/libgcc-glibc.ver b/contrib/gcc/config/s390/libgcc-glibc.ver index 9a42151..1d9e229 100644 --- a/contrib/gcc/config/s390/libgcc-glibc.ver +++ b/contrib/gcc/config/s390/libgcc-glibc.ver @@ -8,6 +8,25 @@ # because GLIBC_2.0 does not exist on this architecture, as the first # ever glibc release on the platform was GLIBC_2.2. +%ifndef __s390x__ +%inherit GCC_3.0 GLIBC_2.0 +GLIBC_2.0 { + __divdi3 + __moddi3 + __udivdi3 + __umoddi3 + + __register_frame + __register_frame_table + __deregister_frame + __register_frame_info + __deregister_frame_info + __frame_state_for + __register_frame_info_table +} +%endif + +%ifdef __s390x__ %inherit GCC_3.0 GLIBC_2.2 GLIBC_2.2 { __register_frame @@ -18,4 +37,5 @@ GLIBC_2.2 { __frame_state_for __register_frame_info_table } +%endif diff --git a/contrib/gcc/config/s390/linux.h b/contrib/gcc/config/s390/linux.h index a0336bf..cbb7710 100644 --- a/contrib/gcc/config/s390/linux.h +++ b/contrib/gcc/config/s390/linux.h @@ -50,35 +50,22 @@ Boston, MA 02111-1307, USA. */ /* Target specific preprocessor settings. */ -#define NO_BUILTIN_SIZE_TYPE -#define NO_BUILTIN_PTRDIFF_TYPE - -#define CPP_PREDEFINES \ - "-Dunix -Asystem(unix) -D__gnu_linux__ -Dlinux -Asystem(linux) -D__ELF__ \ - -Acpu(s390) -Amachine(s390) -D__s390__" - -#define CPP_ARCH31_SPEC \ - "-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=int" -#define CPP_ARCH64_SPEC \ - "-D__SIZE_TYPE__=long\\ unsigned\\ int -D__PTRDIFF_TYPE__=long\\ int \ - -D__s390x__ -D__LONG_MAX__=9223372036854775807L" - -#ifdef DEFAULT_TARGET_64BIT -#undef CPP_SPEC -#define CPP_SPEC "%{m31:%(cpp_arch31)} %{!m31:%(cpp_arch64)}" -#else -#undef CPP_SPEC -#define CPP_SPEC "%{m64:%(cpp_arch64)} %{!m64:%(cpp_arch31)}" -#endif - - -/* Target specific compiler settings. */ - -/* ??? -fcaller-saves sometimes doesn't work. Fix this! */ -#undef CC1_SPEC -#define CC1_SPEC "-fno-caller-saves" -#undef CC1PLUS_SPEC -#define CC1PLUS_SPEC "-fno-caller-saves" +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + builtin_define_std ("linux"); \ + builtin_define_std ("unix"); \ + builtin_assert ("system=linux"); \ + builtin_assert ("system=unix"); \ + builtin_define ("__ELF__"); \ + builtin_define ("__gnu_linux__"); \ + if (flag_pic) \ + { \ + builtin_define ("__PIC__"); \ + builtin_define ("__pic__"); \ + } \ + } \ + while (0) /* Target specific assembler settings. */ @@ -94,6 +81,12 @@ Boston, MA 02111-1307, USA. */ /* Target specific linker settings. */ +#ifdef DEFAULT_TARGET_64BIT +#define MULTILIB_DEFAULTS { "m64" } +#else +#define MULTILIB_DEFAULTS { "m31" } +#endif + #define LINK_ARCH31_SPEC \ "-m elf_s390 \ %{shared:-shared} \ @@ -126,183 +119,86 @@ Boston, MA 02111-1307, USA. */ is an initializer with a subgrouping for each command option. */ #define EXTRA_SPECS \ - { "cpp_arch31", CPP_ARCH31_SPEC }, \ - { "cpp_arch64", CPP_ARCH64_SPEC }, \ { "link_arch31", LINK_ARCH31_SPEC }, \ { "link_arch64", LINK_ARCH64_SPEC }, \ -/* Character to start a comment. */ - -#define ASM_COMMENT_START "#" - - -/* Assembler pseudos to introduce constants of various size. */ - -#define ASM_DOUBLE "\t.double" - -/* The LOCAL_LABEL_PREFIX variable is used by dbxelf.h. */ -#define LOCAL_LABEL_PREFIX "." - -/* Prefix for internally generated assembler labels. */ -#define LPREFIX ".L" - - -/* This is how to output the definition of a user-level label named NAME, - such as the label on a static function or variable NAME. */ - -#undef ASM_OUTPUT_LABEL -#define ASM_OUTPUT_LABEL(FILE, NAME) \ - (assemble_name (FILE, NAME), fputs (":\n", FILE)) - -/* Store in OUTPUT a string (made with alloca) containing - an assembler-name for a local static variable named NAME. - LABELNO is an integer which is different for each call. */ - -#undef ASM_FORMAT_PRIVATE_NAME -#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ -( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \ - sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO))) - - - /* internal macro to output long */ -#define _ASM_OUTPUT_LONG(FILE, VALUE) \ - fprintf (FILE, "\t.long\t0x%lX\n", VALUE); - - -/* This is how to output an element of a case-vector that is absolute. */ - -#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ - fprintf (FILE, "%s%s%d\n", integer_asm_op (UNITS_PER_WORD, TRUE), \ - LPREFIX, VALUE) - -/* This is how to output an element of a case-vector that is relative. */ - -#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ - fprintf (FILE, "%s%s%d-%s%d\n", integer_asm_op (UNITS_PER_WORD, TRUE), \ - LPREFIX, VALUE, LPREFIX, REL) - - - -/* This is how to output an assembler line - that says to advance the location counter - to a multiple of 2**LOG bytes. */ - -#undef ASM_OUTPUT_ALIGN -#define ASM_OUTPUT_ALIGN(FILE, LOG) \ - if ((LOG)!=0) fprintf ((FILE), "\t.align\t%d\n", 1<<(LOG)) - -/* This is how to output an assembler line - that says to advance the location counter by SIZE bytes. */ - -#undef ASM_OUTPUT_SKIP -#define ASM_OUTPUT_SKIP(FILE, SIZE) \ - fprintf ((FILE), "\t.set\t.,.+%u\n", (SIZE)) - -/* This is how to output assembler code to declare an - uninitialized external linkage data object. */ - -#undef ASM_OUTPUT_ALIGNED_BSS -#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ - asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) - -/* Output before read-only data. */ - -#define TEXT_SECTION_ASM_OP ".text" - -/* Output before writable (initialized) data. */ - -#define DATA_SECTION_ASM_OP ".data" - -/* Output before writable (uninitialized) data. */ - -#define BSS_SECTION_ASM_OP ".bss" - -/* This is how to output a command to make the user-level label named NAME - defined for reference from other files. */ - -#define ASM_GLOBALIZE_LABEL(FILE, NAME) \ - (fputs (".globl ", FILE), assemble_name (FILE, NAME), fputs ("\n", FILE)) - -/* Select section for constant in constant pool. - We are in the right section. - undef for 64 bit mode (linux64.h). - */ - -#undef SELECT_RTX_SECTION -#define SELECT_RTX_SECTION(MODE, X, ALIGN) - - -/* Output code to add DELTA to the first argument, and then jump to FUNCTION. - Used for C++ multiple inheritance. */ -#define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \ -do { \ - if (TARGET_64BIT) \ - { \ - if (flag_pic) \ - { \ - fprintf (FILE, "\tlarl 1,0f\n"); \ - fprintf (FILE, "\tagf %d,0(1)\n", \ - aggregate_value_p (TREE_TYPE \ - (TREE_TYPE (FUNCTION))) ? 3 :2 ); \ - fprintf (FILE, "\tlarl 1,"); \ - assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ - fprintf (FILE, "@GOTENT\n"); \ - fprintf (FILE, "\tlg 1,0(1)\n"); \ - fprintf (FILE, "\tbr 1\n"); \ - fprintf (FILE, "0:\t.long "); \ - fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, (DELTA)); \ - fprintf (FILE, "\n"); \ - } \ - else \ - { \ - fprintf (FILE, "\tlarl 1,0f\n"); \ - fprintf (FILE, "\tagf %d,0(1)\n", \ - aggregate_value_p (TREE_TYPE \ - (TREE_TYPE (FUNCTION))) ? 3 :2 ); \ - fprintf (FILE, "\tjg "); \ - assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ - fprintf (FILE, "\n"); \ - fprintf (FILE, "0:\t.long "); \ - fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, (DELTA)); \ - fprintf (FILE, "\n"); \ - } \ - } \ - else \ - { \ - if (flag_pic) \ - { \ - fprintf (FILE, "\tbras 1,0f\n"); \ - fprintf (FILE, "\t.long _GLOBAL_OFFSET_TABLE_-.\n"); \ - fprintf (FILE, "\t.long "); \ - assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ - fprintf (FILE, "@GOT\n"); \ - fprintf (FILE, "\t.long "); \ - fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, (DELTA)); \ - fprintf (FILE, "\n"); \ - fprintf (FILE, "0:\tal %d,8(1)\n", \ - aggregate_value_p (TREE_TYPE \ - (TREE_TYPE (FUNCTION))) ? 3 : 2 ); \ - fprintf (FILE, "\tl 0,4(1)\n"); \ - fprintf (FILE, "\tal 1,0(1)\n"); \ - fprintf (FILE, "\talr 1,0\n"); \ - fprintf (FILE, "\tl 1,0(1)\n"); \ - fprintf (FILE, "\tbr 1\n"); \ - } else { \ - fprintf (FILE, "\tbras 1,0f\n"); \ - fprintf (FILE, "\t.long "); \ - assemble_name (FILE, XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0)); \ - fprintf (FILE, "-.\n"); \ - fprintf (FILE, "\t.long "); \ - fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, (DELTA)); \ - fprintf (FILE, "\n"); \ - fprintf (FILE, "0:\tal %d,4(1)\n", \ - aggregate_value_p (TREE_TYPE \ - (TREE_TYPE (FUNCTION))) ? 3 : 2 ); \ - fprintf (FILE, "\tal 1,0(1)\n"); \ - fprintf (FILE, "\tbr 1\n"); \ - } \ - } \ -} while (0) +/* Do code reading to identify a signal frame, and set the frame + state data appropriately. See unwind-dw2.c for the structs. */ + +#define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \ + do { \ + unsigned char *pc_ = (CONTEXT)->ra; \ + long new_cfa_; \ + int i_; \ + \ + typedef struct \ + { \ + unsigned long psw_mask; \ + unsigned long psw_addr; \ + unsigned long gprs[16]; \ + unsigned int acrs[16]; \ + unsigned int fpc; \ + unsigned int __pad; \ + double fprs[16]; \ + } __attribute__ ((__aligned__ (8))) sigregs_; \ + \ + sigregs_ *regs_; \ + \ + /* svc $__NR_sigreturn or svc $__NR_rt_sigreturn */ \ + if (pc_[0] != 0x0a || (pc_[1] != 119 && pc_[1] != 173)) \ + break; \ + \ + /* New-style RT frame: \ + retcode + alignment (8 bytes) \ + siginfo (128 bytes) \ + ucontext (contains sigregs) */ \ + if ((CONTEXT)->ra == (CONTEXT)->cfa) \ + { \ + struct ucontext_ \ + { \ + unsigned long uc_flags; \ + struct ucontext_ *uc_link; \ + unsigned long uc_stack[3]; \ + sigregs_ uc_mcontext; \ + } *uc_ = (CONTEXT)->cfa + 8 + 128; \ + \ + regs_ = &uc_->uc_mcontext; \ + } \ + \ + /* Old-style RT frame and all non-RT frames: \ + old signal mask (8 bytes) \ + pointer to sigregs */ \ + else \ + { \ + regs_ = *(sigregs_ **)((CONTEXT)->cfa + 8); \ + } \ + \ + new_cfa_ = regs_->gprs[15] + 16*sizeof(long) + 32; \ + (FS)->cfa_how = CFA_REG_OFFSET; \ + (FS)->cfa_reg = 15; \ + (FS)->cfa_offset = \ + new_cfa_ - (long) (CONTEXT)->cfa + 16*sizeof(long) + 32; \ + \ + for (i_ = 0; i_ < 16; i_++) \ + { \ + (FS)->regs.reg[i_].how = REG_SAVED_OFFSET; \ + (FS)->regs.reg[i_].loc.offset = \ + (long)®s_->gprs[i_] - new_cfa_; \ + } \ + for (i_ = 0; i_ < 16; i_++) \ + { \ + (FS)->regs.reg[16+i_].how = REG_SAVED_OFFSET; \ + (FS)->regs.reg[16+i_].loc.offset = \ + (long)®s_->fprs[i_] - new_cfa_; \ + } \ + \ + /* Load return addr from PSW into dummy register 32. */ \ + (FS)->regs.reg[32].how = REG_SAVED_OFFSET; \ + (FS)->regs.reg[32].loc.offset = (long)®s_->psw_addr - new_cfa_; \ + (FS)->retaddr_column = 32; \ + \ + goto SUCCESS; \ + } while (0) #endif diff --git a/contrib/gcc/config/s390/s390-modes.def b/contrib/gcc/config/s390/s390-modes.def new file mode 100644 index 0000000..9f9d526 --- /dev/null +++ b/contrib/gcc/config/s390/s390-modes.def @@ -0,0 +1,38 @@ +/* Definitions of target machine for GNU compiler, for IBM S/390 + Copyright (C) 2002 Free Software Foundation, Inc. + Contributed by Hartmut Penner (hpenner@de.ibm.com) and + Ulrich Weigand (uweigand@de.ibm.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. */ + +/* Add any extra modes needed to represent the condition code. */ + +CC (CCZ) +CC (CCA) +CC (CCAP) +CC (CCAN) +CC (CCL) +CC (CCL1) +CC (CCL2) +CC (CCU) +CC (CCUR) +CC (CCS) +CC (CCSR) +CC (CCT) +CC (CCT1) +CC (CCT2) +CC (CCT3) diff --git a/contrib/gcc/config/s390/s390-protos.h b/contrib/gcc/config/s390/s390-protos.h index cd4ac10..1f8b144 100644 --- a/contrib/gcc/config/s390/s390-protos.h +++ b/contrib/gcc/config/s390/s390-protos.h @@ -1,5 +1,5 @@ /* Definitions of target machine for GNU compiler, for IBM S/390. - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000, 2002, 2003 Free Software Foundation, Inc. Contributed by Hartmut Penner (hpenner@de.ibm.com) This file is part of GNU CC. @@ -24,17 +24,16 @@ Boston, MA 02111-1307, USA. */ extern void optimization_options PARAMS ((int, int)); extern void override_options PARAMS ((void)); extern int s390_arg_frame_offset PARAMS ((void)); -extern void s390_function_prologue PARAMS ((FILE *, HOST_WIDE_INT)); -extern void s390_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT)); extern void s390_emit_prologue PARAMS ((void)); extern void s390_emit_epilogue PARAMS ((void)); extern void s390_function_profiler PARAMS ((FILE *, int)); #ifdef RTX_CODE +extern int s390_address_cost PARAMS ((rtx)); +extern int q_constraint PARAMS ((rtx)); extern int const0_operand PARAMS ((rtx, enum machine_mode)); extern int consttable_operand PARAMS ((rtx, enum machine_mode)); extern int larl_operand PARAMS ((rtx, enum machine_mode)); -extern int fp_operand PARAMS ((rtx, enum machine_mode)); extern int s_operand PARAMS ((rtx, enum machine_mode)); extern int s_imm_operand PARAMS ((rtx, enum machine_mode)); extern int bras_sym_operand PARAMS ((rtx, enum machine_mode)); @@ -44,12 +43,17 @@ extern int s390_single_hi PARAMS ((rtx, enum machine_mode, int)); extern int s390_extract_hi PARAMS ((rtx, enum machine_mode, int)); extern int s390_single_qi PARAMS ((rtx, enum machine_mode, int)); extern int s390_extract_qi PARAMS ((rtx, enum machine_mode, int)); +extern bool s390_split_ok_p PARAMS ((rtx, rtx, enum machine_mode, int)); +extern int tls_symbolic_operand PARAMS ((rtx)); extern int s390_match_ccmode PARAMS ((rtx, enum machine_mode)); +extern enum machine_mode s390_tm_ccmode PARAMS ((rtx, rtx, int)); extern enum machine_mode s390_select_ccmode PARAMS ((enum rtx_code, rtx, rtx)); extern int symbolic_reference_mentioned_p PARAMS ((rtx)); +extern int tls_symbolic_reference_mentioned_p PARAMS ((rtx)); +extern rtx s390_tls_get_offset PARAMS ((void)); extern int legitimate_la_operand_p PARAMS ((rtx)); -extern rtx legitimize_la_operand PARAMS ((rtx)); +extern int preferred_la_operand_p PARAMS ((rtx)); extern int legitimate_pic_operand_p PARAMS ((rtx)); extern int legitimate_constant_p PARAMS ((rtx)); extern int legitimate_reload_constant_p PARAMS ((rtx)); @@ -58,18 +62,25 @@ extern rtx legitimize_pic_address PARAMS ((rtx, rtx)); extern rtx legitimize_address PARAMS ((rtx, rtx, enum machine_mode)); extern enum reg_class s390_preferred_reload_class PARAMS ((rtx, enum reg_class)); extern enum reg_class s390_secondary_input_reload_class PARAMS ((enum reg_class, enum machine_mode, rtx)); +extern enum reg_class s390_secondary_output_reload_class PARAMS ((enum reg_class, enum machine_mode, rtx)); extern int s390_plus_operand PARAMS ((rtx, enum machine_mode)); extern void s390_expand_plus_operand PARAMS ((rtx, rtx, rtx)); -extern void emit_pic_move PARAMS ((rtx *, enum machine_mode)); +extern void emit_symbolic_move PARAMS ((rtx *)); +extern void s390_load_address PARAMS ((rtx, rtx)); +extern void s390_expand_movstr PARAMS ((rtx, rtx, rtx)); +extern void s390_expand_clrstr PARAMS ((rtx, rtx)); +extern void s390_expand_cmpstr PARAMS ((rtx, rtx, rtx, rtx)); +extern rtx s390_return_addr_rtx PARAMS ((int, rtx)); extern void s390_output_symbolic_const PARAMS ((FILE *, rtx)); extern void print_operand_address PARAMS ((FILE *, rtx)); extern void print_operand PARAMS ((FILE *, rtx, int)); -extern void s390_output_constant_pool PARAMS ((FILE *)); +extern void s390_output_constant_pool PARAMS ((rtx, rtx)); extern void s390_trampoline_template PARAMS ((FILE *)); extern void s390_initialize_trampoline PARAMS ((rtx, rtx, rtx)); extern rtx s390_gen_rtx_const_DI PARAMS ((int, int)); extern rtx s390_simplify_dwarf_addr PARAMS ((rtx)); +extern void s390_machine_dependent_reorg PARAMS ((rtx)); #endif /* RTX_CODE */ #ifdef TREE_CODE @@ -78,7 +89,7 @@ extern void s390_function_arg_advance PARAMS ((CUMULATIVE_ARGS *, enum machine_m extern tree s390_build_va_list PARAMS ((void)); #ifdef RTX_CODE extern rtx s390_function_arg PARAMS ((CUMULATIVE_ARGS *, enum machine_mode, tree, int)); -extern void s390_va_start PARAMS ((int, tree, rtx)); +extern void s390_va_start PARAMS ((tree, rtx)); extern rtx s390_va_arg PARAMS ((tree, tree)); #endif /* RTX_CODE */ #endif /* TREE_CODE */ diff --git a/contrib/gcc/config/s390/s390.c b/contrib/gcc/config/s390/s390.c index 4c96dce..d6fda65 100644 --- a/contrib/gcc/config/s390/s390.c +++ b/contrib/gcc/config/s390/s390.c @@ -1,5 +1,5 @@ /* Subroutines used for code generation on IBM S/390 and zSeries - Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Contributed by Hartmut Penner (hpenner@de.ibm.com) and Ulrich Weigand (uweigand@de.ibm.com). @@ -45,11 +45,22 @@ Boston, MA 02111-1307, USA. */ #include "target.h" #include "target-def.h" #include "debug.h" - +#include "langhooks.h" +#include "optabs.h" static bool s390_assemble_integer PARAMS ((rtx, unsigned int, int)); static int s390_adjust_cost PARAMS ((rtx, rtx, rtx, int)); static int s390_adjust_priority PARAMS ((rtx, int)); +static void s390_select_rtx_section PARAMS ((enum machine_mode, rtx, + unsigned HOST_WIDE_INT)); +static void s390_encode_section_info PARAMS ((tree, int)); +static const char *s390_strip_name_encoding PARAMS ((const char *)); +static bool s390_cannot_force_const_mem PARAMS ((rtx)); +static void s390_init_builtins PARAMS ((void)); +static rtx s390_expand_builtin PARAMS ((tree, rtx, rtx, + enum machine_mode, int)); +static void s390_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, + HOST_WIDE_INT, tree)); #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" @@ -58,24 +69,43 @@ static int s390_adjust_priority PARAMS ((rtx, int)); #undef TARGET_ASM_INTEGER #define TARGET_ASM_INTEGER s390_assemble_integer -#undef TARGET_ASM_FUNCTION_PROLOGUE -#define TARGET_ASM_FUNCTION_PROLOGUE s390_function_prologue - -#undef TARGET_ASM_FUNCTION_EPILOGUE -#define TARGET_ASM_FUNCTION_EPILOGUE s390_function_epilogue - #undef TARGET_ASM_OPEN_PAREN #define TARGET_ASM_OPEN_PAREN "" #undef TARGET_ASM_CLOSE_PAREN #define TARGET_ASM_CLOSE_PAREN "" +#undef TARGET_ASM_SELECT_RTX_SECTION +#define TARGET_ASM_SELECT_RTX_SECTION s390_select_rtx_section + #undef TARGET_SCHED_ADJUST_COST #define TARGET_SCHED_ADJUST_COST s390_adjust_cost #undef TARGET_SCHED_ADJUST_PRIORITY #define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info +#undef TARGET_STRIP_NAME_ENCODING +#define TARGET_STRIP_NAME_ENCODING s390_strip_name_encoding + +#ifdef HAVE_AS_TLS +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS true +#endif +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS s390_init_builtins +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN s390_expand_builtin + +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true + struct gcc_target targetm = TARGET_INITIALIZER; extern int reload_completed; @@ -83,13 +113,13 @@ extern int reload_completed; /* The alias set for prologue/epilogue register save/restore. */ static int s390_sr_alias_set = 0; -/* Function count for creating unique internal labels in a compile unit. */ -int s390_function_count = 0; - /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ rtx s390_compare_op0, s390_compare_op1; +/* The encoding characters for the four TLS models present in ELF. */ +static char const tls_model_chars[] = " GLil"; + /* Structure used to hold the components of a S/390 memory address. A legitimate address on S/390 is of the general form @@ -107,19 +137,26 @@ struct s390_address int pointer; }; -/* Structure containing information for prologue and epilogue. */ +/* Define the structure for the machine field in struct function. */ -struct s390_frame +struct machine_function GTY(()) { - int frame_pointer_p; - int return_reg_saved_p; + /* Label of start of initial literal pool. */ + rtx literal_pool_label; + + /* Set, if some of the fprs 8-15 need to be saved (64 bit abi). */ int save_fprs_p; + + /* Number of first and last gpr to be saved, restored. */ int first_save_gpr; int first_restore_gpr; int last_save_gpr; - int arg_frame_offset; + /* Size of stack frame. */ HOST_WIDE_INT frame_size; + + /* Some local-dynamic TLS symbol name. */ + const char *some_ld_name; }; static int s390_match_ccmode_set PARAMS ((rtx, enum machine_mode)); @@ -127,20 +164,29 @@ static int s390_branch_condition_mask PARAMS ((rtx)); static const char *s390_branch_condition_mnemonic PARAMS ((rtx, int)); static int check_mode PARAMS ((rtx, enum machine_mode *)); static int general_s_operand PARAMS ((rtx, enum machine_mode, int)); -static int s390_decompose_address PARAMS ((rtx, struct s390_address *, int)); +static int s390_decompose_address PARAMS ((rtx, struct s390_address *)); +static rtx get_thread_pointer PARAMS ((void)); +static rtx legitimize_tls_address PARAMS ((rtx, rtx)); +static const char *get_some_local_dynamic_name PARAMS ((void)); +static int get_some_local_dynamic_name_1 PARAMS ((rtx *, void *)); static int reg_used_in_mem_p PARAMS ((int, rtx)); static int addr_generation_dependency_p PARAMS ((rtx, rtx)); -static void s390_split_branches PARAMS ((void)); +static int s390_split_branches PARAMS ((rtx, bool *)); static void find_constant_pool_ref PARAMS ((rtx, rtx *)); static void replace_constant_pool_ref PARAMS ((rtx *, rtx, rtx)); -static void s390_chunkify_pool PARAMS ((void)); -static int save_fprs_p PARAMS ((void)); +static int find_base_register_in_addr PARAMS ((struct s390_address *)); +static bool find_base_register_ref PARAMS ((rtx)); +static void replace_base_register_ref PARAMS ((rtx *, rtx)); +static void s390_optimize_prolog PARAMS ((int)); +static bool s390_fixup_clobbered_return_reg PARAMS ((rtx)); static int find_unused_clobbered_reg PARAMS ((void)); -static void s390_frame_info PARAMS ((struct s390_frame *)); +static void s390_frame_info PARAMS ((void)); static rtx save_fpr PARAMS ((rtx, int, int)); static rtx restore_fpr PARAMS ((rtx, int, int)); +static rtx save_gprs PARAMS ((rtx, int, int, int)); +static rtx restore_gprs PARAMS ((rtx, int, int, int)); static int s390_function_arg_size PARAMS ((enum machine_mode, tree)); - +static struct machine_function * s390_init_machine_status PARAMS ((void)); /* Return true if SET either doesn't set the CC register, or else the source and destination have matching CC modes and that @@ -163,19 +209,28 @@ s390_match_ccmode_set (set, req_mode) switch (set_mode) { case CCSmode: - if (req_mode != CCSmode) - return 0; - break; + case CCSRmode: case CCUmode: - if (req_mode != CCUmode) - return 0; - break; + case CCURmode: case CCLmode: - if (req_mode != CCLmode) + case CCL1mode: + case CCL2mode: + case CCT1mode: + case CCT2mode: + case CCT3mode: + if (req_mode != set_mode) return 0; break; + case CCZmode: - if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode) + if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode + && req_mode != CCSRmode && req_mode != CCURmode) + return 0; + break; + + case CCAPmode: + case CCANmode: + if (req_mode != CCAmode) return 0; break; @@ -188,7 +243,8 @@ s390_match_ccmode_set (set, req_mode) /* Return true if every SET in INSN that sets the CC register has source and destination with matching CC modes and that - CC mode is at least as constrained as REQ_MODE. */ + CC mode is at least as constrained as REQ_MODE. + If REQ_MODE is VOIDmode, always return false. */ int s390_match_ccmode (insn, req_mode) @@ -197,6 +253,10 @@ s390_match_ccmode (insn, req_mode) { int i; + /* s390_tm_ccmode returns VOIDmode to indicate failure. */ + if (req_mode == VOIDmode) + return 0; + if (GET_CODE (PATTERN (insn)) == SET) return s390_match_ccmode_set (PATTERN (insn), req_mode); @@ -212,6 +272,45 @@ s390_match_ccmode (insn, req_mode) return 1; } +/* If a test-under-mask instruction can be used to implement + (compare (and ... OP1) OP2), return the CC mode required + to do that. Otherwise, return VOIDmode. + MIXED is true if the instruction can distinguish between + CC1 and CC2 for mixed selected bits (TMxx), it is false + if the instruction cannot (TM). */ + +enum machine_mode +s390_tm_ccmode (op1, op2, mixed) + rtx op1; + rtx op2; + int mixed; +{ + int bit0, bit1; + + /* ??? Fixme: should work on CONST_DOUBLE as well. */ + if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT) + return VOIDmode; + + /* Selected bits all zero: CC0. */ + if (INTVAL (op2) == 0) + return CCTmode; + + /* Selected bits all one: CC3. */ + if (INTVAL (op2) == INTVAL (op1)) + return CCT3mode; + + /* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. */ + if (mixed) + { + bit1 = exact_log2 (INTVAL (op2)); + bit0 = exact_log2 (INTVAL (op1) ^ INTVAL (op2)); + if (bit0 != -1 && bit1 != -1) + return bit0 > bit1 ? CCT1mode : CCT2mode; + } + + return VOIDmode; +} + /* Given a comparison code OP (EQ, NE, etc.) and the operands OP0 and OP1 of a COMPARE, return the mode to be used for the comparison. */ @@ -226,16 +325,49 @@ s390_select_ccmode (code, op0, op1) { case EQ: case NE: + if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op0, 1)), 'K')) + return CCAPmode; if (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS || GET_CODE (op1) == NEG) return CCLmode; + if (GET_CODE (op0) == AND) + { + /* Check whether we can potentially do it via TM. */ + enum machine_mode ccmode; + ccmode = s390_tm_ccmode (XEXP (op0, 1), op1, 1); + if (ccmode != VOIDmode) + { + /* Relax CCTmode to CCZmode to allow fall-back to AND + if that turns out to be beneficial. */ + return ccmode == CCTmode ? CCZmode : ccmode; + } + } + + if (register_operand (op0, HImode) + && GET_CODE (op1) == CONST_INT + && (INTVAL (op1) == -1 || INTVAL (op1) == 65535)) + return CCT3mode; + if (register_operand (op0, QImode) + && GET_CODE (op1) == CONST_INT + && (INTVAL (op1) == -1 || INTVAL (op1) == 255)) + return CCT3mode; + return CCZmode; case LE: case LT: case GE: case GT: + if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op0, 1)), 'K')) + { + if (INTVAL (XEXP((op0), 1)) < 0) + return CCANmode; + else + return CCAPmode; + } case UNORDERED: case ORDERED: case UNEQ: @@ -244,12 +376,29 @@ s390_select_ccmode (code, op0, op1) case UNGE: case UNGT: case LTGT: + if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (op1) != CONST_INT) + return CCSRmode; return CCSmode; - case LEU: case LTU: case GEU: + if (GET_CODE (op0) == PLUS) + return CCL1mode; + + if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (op1) != CONST_INT) + return CCURmode; + return CCUmode; + + case LEU: case GTU: + if (GET_CODE (op0) == MINUS) + return CCL2mode; + + if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (op1) != CONST_INT) + return CCURmode; return CCUmode; default: @@ -286,13 +435,61 @@ s390_branch_condition_mask (code) } break; + case CCT1mode: + switch (GET_CODE (code)) + { + case EQ: return CC1; + case NE: return CC0 | CC2 | CC3; + default: + abort (); + } + break; + + case CCT2mode: + switch (GET_CODE (code)) + { + case EQ: return CC2; + case NE: return CC0 | CC1 | CC3; + default: + abort (); + } + break; + + case CCT3mode: + switch (GET_CODE (code)) + { + case EQ: return CC3; + case NE: return CC0 | CC1 | CC2; + default: + abort (); + } + break; + case CCLmode: switch (GET_CODE (code)) { case EQ: return CC0 | CC2; case NE: return CC1 | CC3; - case UNORDERED: return CC2 | CC3; /* carry */ - case ORDERED: return CC0 | CC1; /* no carry */ + default: + abort (); + } + break; + + case CCL1mode: + switch (GET_CODE (code)) + { + case LTU: return CC2 | CC3; /* carry */ + case GEU: return CC0 | CC1; /* no carry */ + default: + abort (); + } + break; + + case CCL2mode: + switch (GET_CODE (code)) + { + case GTU: return CC0 | CC1; /* borrow */ + case LEU: return CC2 | CC3; /* no borrow */ default: abort (); } @@ -312,6 +509,48 @@ s390_branch_condition_mask (code) } break; + case CCURmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC2 | CC1 | CC3; + case LTU: return CC2; + case GTU: return CC1; + case LEU: return CC0 | CC2; + case GEU: return CC0 | CC1; + default: + abort (); + } + break; + + case CCAPmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1 | CC3; + case GT: return CC2; + case LE: return CC0 | CC1 | CC3; + case GE: return CC0 | CC2; + default: + abort (); + } + break; + + case CCANmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1; + case GT: return CC2 | CC3; + case LE: return CC0 | CC1; + case GE: return CC0 | CC2 | CC3; + default: + abort (); + } + break; + case CCSmode: switch (GET_CODE (code)) { @@ -332,6 +571,29 @@ s390_branch_condition_mask (code) default: abort (); } + break; + + case CCSRmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC2 | CC1 | CC3; + case LT: return CC2; + case GT: return CC1; + case LE: return CC0 | CC2; + case GE: return CC0 | CC1; + case UNORDERED: return CC3; + case ORDERED: return CC0 | CC2 | CC1; + case UNEQ: return CC0 | CC3; + case UNLT: return CC2 | CC3; + case UNGT: return CC1 | CC3; + case UNLE: return CC0 | CC2 | CC3; + case UNGE: return CC0 | CC1 | CC3; + case LTGT: return CC2 | CC1; + default: + abort (); + } + break; default: abort (); @@ -347,7 +609,7 @@ s390_branch_condition_mnemonic (code, inv) rtx code; int inv; { - static const char *mnemonic[16] = + static const char *const mnemonic[16] = { NULL, "o", "h", "nle", "l", "nhe", "lh", "ne", @@ -379,7 +641,7 @@ s390_single_hi (op, mode, def) { if (GET_CODE (op) == CONST_INT) { - unsigned HOST_WIDE_INT value; + unsigned HOST_WIDE_INT value = 0; int n_parts = GET_MODE_SIZE (mode) / 2; int i, part = -1; @@ -405,7 +667,7 @@ s390_single_hi (op, mode, def) else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode) { - unsigned HOST_WIDE_INT value; + unsigned HOST_WIDE_INT value = 0; int n_parts = GET_MODE_SIZE (mode) / 2; int i, part = -1; @@ -482,7 +744,7 @@ s390_single_qi (op, mode, def) { if (GET_CODE (op) == CONST_INT) { - unsigned HOST_WIDE_INT value; + unsigned HOST_WIDE_INT value = 0; int n_parts = GET_MODE_SIZE (mode); int i, part = -1; @@ -508,7 +770,7 @@ s390_single_qi (op, mode, def) else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode) { - unsigned HOST_WIDE_INT value; + unsigned HOST_WIDE_INT value = 0; int n_parts = GET_MODE_SIZE (mode); int i, part = -1; @@ -572,6 +834,42 @@ s390_extract_qi (op, mode, part) abort (); } +/* Check whether we can (and want to) split a double-word + move in mode MODE from SRC to DST into two single-word + moves, moving the subword FIRST_SUBWORD first. */ + +bool +s390_split_ok_p (dst, src, mode, first_subword) + rtx dst; + rtx src; + enum machine_mode mode; + int first_subword; +{ + /* Floating point registers cannot be split. */ + if (FP_REG_P (src) || FP_REG_P (dst)) + return false; + + /* We don't need to split if operands are directly accessable. */ + if (s_operand (src, mode) || s_operand (dst, mode)) + return false; + + /* Non-offsettable memory references cannot be split. */ + if ((GET_CODE (src) == MEM && !offsettable_memref_p (src)) + || (GET_CODE (dst) == MEM && !offsettable_memref_p (dst))) + return false; + + /* Moving the first subword must not clobber a register + needed to move the second subword. */ + if (register_operand (dst, mode)) + { + rtx subreg = operand_subword (dst, first_subword, 0, mode); + if (reg_overlap_mentioned_p (subreg, src)) + return false; + } + + return true; +} + /* Change optimizations to be performed, depending on the optimization level. @@ -579,18 +877,19 @@ s390_extract_qi (op, mode, part) LEVEL is the optimization level specified; 2 if `-O2' is specified, 1 if `-O' is specified, and 0 if neither is specified. - SIZE is non-zero if `-Os' is specified and zero otherwise. */ + SIZE is nonzero if `-Os' is specified and zero otherwise. */ void optimization_options (level, size) int level ATTRIBUTE_UNUSED; int size ATTRIBUTE_UNUSED; { -#ifdef HAVE_decrement_and_branch_on_count - /* When optimizing, enable use of BRCT instruction. */ - if (level >= 1) - flag_branch_on_count_reg = 1; -#endif + /* ??? There are apparently still problems with -fcaller-saves. */ + flag_caller_saves = 0; + + /* By default, always emit DWARF-2 unwind info. This allows debugging + without maintaining a stack frame back-chain. */ + flag_asynchronous_unwind_tables = 1; } void @@ -598,12 +897,14 @@ override_options () { /* Acquire a unique set number for our register saves and restores. */ s390_sr_alias_set = new_alias_set (); -} + /* Set up function hooks. */ + init_machine_status = s390_init_machine_status; +} /* Map for smallest class containing reg regno. */ -enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = +const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = { GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, @@ -674,6 +975,8 @@ larl_operand (op, mode) if (GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == SYMBOL_REF + && XSTR (op, 0)[0] != '@' + && !tls_symbolic_operand (op) && (!flag_pic || SYMBOL_REF_FLAG (op) || CONSTANT_POOL_ADDRESS_P (op))) return 1; @@ -696,39 +999,27 @@ larl_operand (op, mode) if (GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == SYMBOL_REF + && XSTR (op, 0)[0] != '@' + && !tls_symbolic_operand (op) && (!flag_pic || SYMBOL_REF_FLAG (op) || CONSTANT_POOL_ADDRESS_P (op))) return 1; - /* Now we must have a @GOTENT offset or @PLT stub. */ + /* Now we must have a @GOTENT offset or @PLT stub + or an @INDNTPOFF TLS offset. */ if (GET_CODE (op) == UNSPEC && XINT (op, 1) == 111) return 1; if (GET_CODE (op) == UNSPEC && XINT (op, 1) == 113) return 1; + if (GET_CODE (op) == UNSPEC + && XINT (op, 1) == UNSPEC_INDNTPOFF) + return 1; return 0; } -/* Return true if OP is a valid FP-Register. - OP is the current operation. - MODE is the current operation mode. */ - -int -fp_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - register enum rtx_code code = GET_CODE (op); - if (! check_mode (op, &mode)) - return 0; - if (code == REG && REGNO_OK_FOR_FP_P (REGNO (op))) - return 1; - else - return 0; -} - /* Helper routine to implement s_operand and s_imm_operand. OP is the current operation. MODE is the current operation mode. @@ -777,7 +1068,7 @@ general_s_operand (op, mode, allow_immediate) case MEM: if (GET_CODE (XEXP (op, 0)) == ADDRESSOF) return 1; - if (s390_decompose_address (XEXP (op, 0), &addr, FALSE) + if (s390_decompose_address (XEXP (op, 0), &addr) && !addr.indx) return 1; break; @@ -815,6 +1106,41 @@ s_imm_operand (op, mode) return general_s_operand (op, mode, 1); } +/* Return true if OP is a valid operand for a 'Q' constraint. + This differs from s_operand in that only memory operands + without index register are accepted, nothing else. */ + +int +q_constraint (op) + register rtx op; +{ + struct s390_address addr; + + if (GET_CODE (op) != MEM) + return 0; + + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return 0; + + if (addr.indx) + return 0; + + return 1; +} + +/* Return the cost of an address rtx ADDR. */ + +int +s390_address_cost (addr) + rtx addr; +{ + struct s390_address ad; + if (!s390_decompose_address (addr, &ad)) + return 1000; + + return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1); +} + /* Return true if OP is a valid operand for the BRAS instruction. OP is the current operation. MODE is the current operation mode. */ @@ -838,6 +1164,23 @@ bras_sym_operand (op, mode) return 0; } +/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode, + otherwise return 0. */ + +int +tls_symbolic_operand (op) + register rtx op; +{ + const char *symbol_str; + + if (GET_CODE (op) != SYMBOL_REF) + return 0; + symbol_str = XSTR (op, 0); + + if (symbol_str[0] != '%') + return 0; + return strchr (tls_model_chars, symbol_str[1]) - tls_model_chars; +} /* Return true if OP is a load multiple operation. It is known to be a PARALLEL and the first section will be tested. @@ -997,6 +1340,37 @@ symbolic_reference_mentioned_p (op) return 0; } +/* Return true if OP contains a reference to a thread-local symbol. */ + +int +tls_symbolic_reference_mentioned_p (op) + rtx op; +{ + register const char *fmt; + register int i; + + if (GET_CODE (op) == SYMBOL_REF) + return tls_symbolic_operand (op); + + fmt = GET_RTX_FORMAT (GET_CODE (op)); + for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (op, i) - 1; j >= 0; j--) + if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j))) + return 1; + } + + else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i))) + return 1; + } + + return 0; +} + /* Return true if OP is a legitimate general operand when generating PIC code. It is given that flag_pic is on @@ -1011,7 +1385,7 @@ legitimate_pic_operand_p (op) return 1; /* Reject everything else; must be handled - via emit_pic_move. */ + via emit_symbolic_move. */ return 0; } @@ -1026,22 +1400,87 @@ legitimate_constant_p (op) if (!SYMBOLIC_CONST (op)) return 1; + /* Accept immediate LARL operands. */ + if (TARGET_64BIT && larl_operand (op, VOIDmode)) + return 1; + + /* Thread-local symbols are never legal constants. This is + so that emit_call knows that computing such addresses + might require a function call. */ + if (TLS_SYMBOLIC_CONST (op)) + return 0; + /* In the PIC case, symbolic constants must *not* be forced into the literal pool. We accept them here, - so that they will be handled by emit_pic_move. */ + so that they will be handled by emit_symbolic_move. */ if (flag_pic) return 1; - /* Even in the non-PIC case, we can accept immediate - LARL operands here. */ - if (TARGET_64BIT) - return larl_operand (op, VOIDmode); - /* All remaining non-PIC symbolic constants are forced into the literal pool. */ return 0; } +/* Determine if it's legal to put X into the constant pool. This + is not possible if X contains the address of a symbol that is + not constant (TLS) or not known at final link time (PIC). */ + +static bool +s390_cannot_force_const_mem (x) + rtx x; +{ + switch (GET_CODE (x)) + { + case CONST_INT: + case CONST_DOUBLE: + /* Accept all non-symbolic constants. */ + return false; + + case LABEL_REF: + /* Labels are OK iff we are non-PIC. */ + return flag_pic != 0; + + case SYMBOL_REF: + /* 'Naked' TLS symbol references are never OK, + non-TLS symbols are OK iff we are non-PIC. */ + if (tls_symbolic_operand (x)) + return true; + else + return flag_pic != 0; + + case CONST: + return s390_cannot_force_const_mem (XEXP (x, 0)); + case PLUS: + case MINUS: + return s390_cannot_force_const_mem (XEXP (x, 0)) + || s390_cannot_force_const_mem (XEXP (x, 1)); + + case UNSPEC: + switch (XINT (x, 1)) + { + /* Only lt-relative or GOT-relative UNSPECs are OK. */ + case 100: + case 104: + case 112: + case 114: + case UNSPEC_TLSGD: + case UNSPEC_TLSLDM: + case UNSPEC_NTPOFF: + case UNSPEC_DTPOFF: + case UNSPEC_GOTNTPOFF: + case UNSPEC_INDNTPOFF: + return false; + + default: + return true; + } + break; + + default: + abort (); + } +} + /* Returns true if the constant value OP is a legitimate general operand during and after reload. The difference to legitimate_constant_p is that this function will not accept @@ -1067,15 +1506,6 @@ legitimate_reload_constant_p (op) && larl_operand (op, VOIDmode)) return 1; - /* If reload is completed, and we do not already have a - literal pool, and OP must be forced to the literal - pool, then something must have gone wrong earlier. - We *cannot* force the constant any more, because the - prolog generation already decided we don't need to - set up the base register. */ - if (reload_completed && !regs_ever_live[BASE_REGISTER]) - abort (); - /* Everything else cannot be handled without reload. */ return 0; } @@ -1097,23 +1527,14 @@ s390_preferred_reload_class (op, class) switch (GET_CODE (op)) { /* Constants we cannot reload must be forced into the - literal pool. For constants we *could* handle directly, - it might still be preferable to put them in the pool and - use a memory-to-memory instruction. - - However, try to avoid needlessly allocating a literal - pool in a routine that wouldn't otherwise need any. - Heuristically, we assume that 64-bit leaf functions - typically don't need a literal pool, all others do. */ + literal pool. */ + case CONST_DOUBLE: case CONST_INT: - if (!legitimate_reload_constant_p (op)) - return NO_REGS; - - if (TARGET_64BIT && current_function_is_leaf) + if (legitimate_reload_constant_p (op)) return class; - - return NO_REGS; + else + return NO_REGS; /* If a symbolic constant or a PLUS is reloaded, it is most likely being used as an address, so @@ -1153,6 +1574,29 @@ s390_secondary_input_reload_class (class, mode, in) return NO_REGS; } +/* Return the register class of a scratch register needed to + store a register of class CLASS in MODE into OUT: + + We need a temporary when storing a double-word to a + non-offsettable memory address. */ + +enum reg_class +s390_secondary_output_reload_class (class, mode, out) + enum reg_class class; + enum machine_mode mode; + rtx out; +{ + if ((TARGET_64BIT ? mode == TImode + : (mode == DImode || mode == DFmode)) + && reg_classes_intersect_p (GENERAL_REGS, class) + && GET_CODE (out) == MEM + && !offsettable_memref_p (out) + && !s_operand (out, VOIDmode)) + return ADDR_REGS; + + return NO_REGS; +} + /* Return true if OP is a PLUS that is not a legitimate operand for the LA instruction. OP is the current operation. @@ -1180,21 +1624,13 @@ s390_plus_operand (op, mode) SCRATCH may be used as scratch register. */ void -s390_expand_plus_operand (target, src, scratch_in) +s390_expand_plus_operand (target, src, scratch) register rtx target; register rtx src; - register rtx scratch_in; + register rtx scratch; { - rtx sum1, sum2, scratch; - - /* ??? reload apparently does not ensure that the scratch register - and the target do not overlap. We absolutely require this to be - the case, however. Therefore the reload_in[sd]i patterns ask for - a double-sized scratch register, and if one part happens to be - equal to the target, we use the other one. */ - scratch = gen_rtx_REG (Pmode, REGNO (scratch_in)); - if (rtx_equal_p (scratch, target)) - scratch = gen_rtx_REG (Pmode, REGNO (scratch_in) + 1); + rtx sum1, sum2; + struct s390_address ad; /* src must be a PLUS; get its two operands. */ if (GET_CODE (src) != PLUS || GET_MODE (src) != Pmode) @@ -1205,58 +1641,49 @@ s390_expand_plus_operand (target, src, scratch_in) float registers occur in an address. */ sum1 = find_replacement (&XEXP (src, 0)); sum2 = find_replacement (&XEXP (src, 1)); + src = gen_rtx_PLUS (Pmode, sum1, sum2); - /* If one of the two operands is equal to the target, - make it the first one. If one is a constant, make - it the second one. */ - if (rtx_equal_p (target, sum2) - || GET_CODE (sum1) == CONST_INT) + /* If the address is already strictly valid, there's nothing to do. */ + if (!s390_decompose_address (src, &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx))) { - rtx tem = sum2; - sum2 = sum1; - sum1 = tem; - } + /* Otherwise, one of the operands cannot be an address register; + we reload its value into the scratch register. */ + if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15) + { + emit_move_insn (scratch, sum1); + sum1 = scratch; + } + if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15) + { + emit_move_insn (scratch, sum2); + sum2 = scratch; + } - /* If the first operand is not an address register, - we reload it into the target. */ - if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15) - { - emit_move_insn (target, sum1); - sum1 = target; - } + /* According to the way these invalid addresses are generated + in reload.c, it should never happen (at least on s390) that + *neither* of the PLUS components, after find_replacements + was applied, is an address register. */ + if (sum1 == scratch && sum2 == scratch) + { + debug_rtx (src); + abort (); + } - /* Likewise for the second operand. However, take - care not to clobber the target if we already used - it for the first operand. Use the scratch instead. - Also, allow an immediate offset if it is in range. */ - if ((true_regnum (sum2) < 1 || true_regnum (sum2) > 15) - && !(GET_CODE (sum2) == CONST_INT - && INTVAL (sum2) >= 0 && INTVAL (sum2) < 4096)) - { - if (!rtx_equal_p (target, sum1)) - { - emit_move_insn (target, sum2); - sum2 = target; - } - else - { - emit_move_insn (scratch, sum2); - sum2 = scratch; - } + src = gen_rtx_PLUS (Pmode, sum1, sum2); } /* Emit the LOAD ADDRESS pattern. Note that reload of PLUS is only ever performed on addresses, so we can mark the sum as legitimate for LA in any case. */ - src = gen_rtx_PLUS (Pmode, sum1, sum2); - src = legitimize_la_operand (src); - emit_insn (gen_rtx_SET (VOIDmode, target, src)); + s390_load_address (target, src); } /* Decompose a RTL expression ADDR for a memory address into - its components, returned in OUT. The boolean STRICT - specifies whether strict register checking applies. + its components, returned in OUT. + Returns 0 if ADDR is not a valid memory address, nonzero otherwise. If OUT is NULL, don't return the components, but check for validity only. @@ -1266,10 +1693,9 @@ s390_expand_plus_operand (target, src, scratch_in) canonical form so that they will be recognized. */ static int -s390_decompose_address (addr, out, strict) +s390_decompose_address (addr, out) register rtx addr; struct s390_address *out; - int strict; { rtx base = NULL_RTX; rtx indx = NULL_RTX; @@ -1320,6 +1746,22 @@ s390_decompose_address (addr, out, strict) disp = addr; /* displacement */ + /* Prefer to use pointer as base, not index. */ + if (base && indx) + { + int base_ptr = GET_CODE (base) == UNSPEC + || (REG_P (base) && REG_POINTER (base)); + int indx_ptr = GET_CODE (indx) == UNSPEC + || (REG_P (indx) && REG_POINTER (indx)); + + if (!base_ptr && indx_ptr) + { + rtx tmp = base; + base = indx; + indx = tmp; + } + } + /* Validate base register. */ if (base) { @@ -1334,16 +1776,15 @@ s390_decompose_address (addr, out, strict) if (GET_CODE (base) != REG || GET_MODE (base) != Pmode) return FALSE; - if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base)) - || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base))) - return FALSE; - if (REGNO (base) == BASE_REGISTER || REGNO (base) == STACK_POINTER_REGNUM || REGNO (base) == FRAME_POINTER_REGNUM || ((reload_completed || reload_in_progress) && frame_pointer_needed && REGNO (base) == HARD_FRAME_POINTER_REGNUM) + || REGNO (base) == ARG_POINTER_REGNUM + || (REGNO (base) >= FIRST_VIRTUAL_REGISTER + && REGNO (base) <= LAST_VIRTUAL_REGISTER) || (flag_pic && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) pointer = TRUE; @@ -1363,16 +1804,15 @@ s390_decompose_address (addr, out, strict) if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode) return FALSE; - if ((strict && ! REG_OK_FOR_BASE_STRICT_P (indx)) - || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (indx))) - return FALSE; - if (REGNO (indx) == BASE_REGISTER || REGNO (indx) == STACK_POINTER_REGNUM || REGNO (indx) == FRAME_POINTER_REGNUM || ((reload_completed || reload_in_progress) && frame_pointer_needed && REGNO (indx) == HARD_FRAME_POINTER_REGNUM) + || REGNO (indx) == ARG_POINTER_REGNUM + || (REGNO (indx) >= FIRST_VIRTUAL_REGISTER + && REGNO (indx) <= LAST_VIRTUAL_REGISTER) || (flag_pic && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) pointer = TRUE; @@ -1384,15 +1824,27 @@ s390_decompose_address (addr, out, strict) /* Allow integer constant in range. */ if (GET_CODE (disp) == CONST_INT) { - if (INTVAL (disp) < 0 || INTVAL (disp) >= 4096) - return FALSE; + /* If the argument pointer is involved, the displacement will change + later anyway as the argument pointer gets eliminated. This could + make a valid displacement invalid, but it is more likely to make + an invalid displacement valid, because we sometimes access the + register save area via negative offsets to the arg pointer. + Thus we don't check the displacement for validity here. If after + elimination the displacement turns out to be invalid after all, + this is fixed up by reload in any case. */ + if (base != arg_pointer_rtx && indx != arg_pointer_rtx) + { + if (INTVAL (disp) < 0 || INTVAL (disp) >= 4096) + return FALSE; + } } /* In the small-PIC case, the linker converts @GOT12 - offsets to possible displacements. */ + and @GOTNTPOFF offsets to possible displacements. */ else if (GET_CODE (disp) == CONST && GET_CODE (XEXP (disp, 0)) == UNSPEC - && XINT (XEXP (disp, 0), 1) == 110) + && (XINT (XEXP (disp, 0), 1) == 110 + || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF)) { if (flag_pic != 1) return FALSE; @@ -1442,12 +1894,6 @@ s390_decompose_address (addr, out, strict) || !CONSTANT_POOL_ADDRESS_P (disp)) return FALSE; - /* In 64-bit PIC mode we cannot accept symbolic - constants in the constant pool. */ - if (TARGET_64BIT && flag_pic - && SYMBOLIC_CONST (get_pool_constant (disp))) - return FALSE; - /* If we have an offset, make sure it does not exceed the size of the constant pool entry. */ if (offset && offset >= GET_MODE_SIZE (get_pool_mode (disp))) @@ -1497,7 +1943,26 @@ legitimate_address_p (mode, addr, strict) register rtx addr; int strict; { - return s390_decompose_address (addr, NULL, strict); + struct s390_address ad; + if (!s390_decompose_address (addr, &ad)) + return FALSE; + + if (strict) + { + if (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + return FALSE; + if (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx)) + return FALSE; + } + else + { + if (ad.base && !REG_OK_FOR_BASE_NONSTRICT_P (ad.base)) + return FALSE; + if (ad.indx && !REG_OK_FOR_INDEX_NONSTRICT_P (ad.indx)) + return FALSE; + } + + return TRUE; } /* Return 1 if OP is a valid operand for the LA instruction. @@ -1509,7 +1974,7 @@ legitimate_la_operand_p (op) register rtx op; { struct s390_address addr; - if (!s390_decompose_address (op, &addr, FALSE)) + if (!s390_decompose_address (op, &addr)) return FALSE; if (TARGET_64BIT || addr.pointer) @@ -1518,30 +1983,43 @@ legitimate_la_operand_p (op) return FALSE; } -/* Return a modified variant of OP that is guaranteed to - be accepted by legitimate_la_operand_p. */ - -rtx -legitimize_la_operand (op) +/* Return 1 if OP is a valid operand for the LA instruction, + and we prefer to use LA over addition to compute it. */ + +int +preferred_la_operand_p (op) register rtx op; { struct s390_address addr; - if (!s390_decompose_address (op, &addr, FALSE)) - abort (); + if (!s390_decompose_address (op, &addr)) + return FALSE; - if (TARGET_64BIT || addr.pointer) - return op; + if (!TARGET_64BIT && !addr.pointer) + return FALSE; - if (!addr.base) - abort (); + if (addr.pointer) + return TRUE; - op = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr.base), 101); - if (addr.indx) - op = gen_rtx_PLUS (Pmode, op, addr.indx); - if (addr.disp) - op = gen_rtx_PLUS (Pmode, op, addr.disp); + if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base)) + || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx))) + return TRUE; - return op; + return FALSE; +} + +/* Emit a forced load-address operation to load SRC into DST. + This will use the LOAD ADDRESS instruction even in situations + where legitimate_la_operand_p (SRC) returns false. */ + +void +s390_load_address (dst, src) + rtx dst; + rtx src; +{ + if (TARGET_64BIT) + emit_move_insn (dst, src); + else + emit_insn (gen_force_la_31 (dst, src)); } /* Return a legitimate reference for ORIG (an address) using the @@ -1577,7 +2055,7 @@ legitimize_pic_address (orig, reg) || CONSTANT_POOL_ADDRESS_P (addr)))) { /* This is a local symbol. */ - if (TARGET_64BIT) + if (TARGET_64BIT && larl_operand (addr, VOIDmode)) { /* Access local symbols PC-relative via LARL. This is the same as in the non-PIC case, so it is @@ -1589,9 +2067,9 @@ legitimize_pic_address (orig, reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); - addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 100); - addr = gen_rtx_CONST (SImode, addr); - addr = force_const_mem (SImode, addr); + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 100); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); emit_move_insn (temp, addr); base = gen_rtx_REG (Pmode, BASE_REGISTER); @@ -1615,7 +2093,8 @@ legitimize_pic_address (orig, reg) /* Assume GOT offset < 4k. This is handled the same way in both 31- and 64-bit code (@GOT12). */ - current_function_uses_pic_offset_table = 1; + if (reload_in_progress || reload_completed) + regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 110); new = gen_rtx_CONST (Pmode, new); @@ -1648,11 +2127,12 @@ legitimize_pic_address (orig, reg) rtx temp = gen_reg_rtx (Pmode); - current_function_uses_pic_offset_table = 1; + if (reload_in_progress || reload_completed) + regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; - addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 112); - addr = gen_rtx_CONST (SImode, addr); - addr = force_const_mem (SImode, addr); + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 112); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); emit_move_insn (temp, addr); new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); @@ -1678,7 +2158,7 @@ legitimize_pic_address (orig, reg) case 100: case 112: case 114: - new = force_const_mem (SImode, orig); + new = force_const_mem (Pmode, orig); break; /* @GOTENT is OK as is. */ @@ -1693,9 +2173,9 @@ legitimize_pic_address (orig, reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); addr = XVECEXP (addr, 0, 0); - addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 114); - addr = gen_rtx_CONST (SImode, addr); - addr = force_const_mem (SImode, addr); + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 114); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); emit_move_insn (temp, addr); base = gen_rtx_REG (Pmode, BASE_REGISTER); @@ -1729,7 +2209,7 @@ legitimize_pic_address (orig, reg) || CONSTANT_POOL_ADDRESS_P (op0)))) && GET_CODE (op1) == CONST_INT) { - if (TARGET_64BIT) + if (TARGET_64BIT && larl_operand (op0, VOIDmode)) { if (INTVAL (op1) & 1) { @@ -1766,10 +2246,10 @@ legitimize_pic_address (orig, reg) rtx temp = reg? reg : gen_reg_rtx (Pmode); - addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, op0), 100); - addr = gen_rtx_PLUS (SImode, addr, op1); - addr = gen_rtx_CONST (SImode, addr); - addr = force_const_mem (SImode, addr); + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), 100); + addr = gen_rtx_PLUS (Pmode, addr, op1); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); emit_move_insn (temp, addr); base = gen_rtx_REG (Pmode, BASE_REGISTER); @@ -1795,7 +2275,7 @@ legitimize_pic_address (orig, reg) if (XINT (op0, 1) != 100) abort (); - new = force_const_mem (SImode, orig); + new = force_const_mem (Pmode, orig); } /* Otherwise, compute the sum. */ @@ -1825,18 +2305,237 @@ legitimize_pic_address (orig, reg) return new; } +/* Load the thread pointer into a register. */ + +static rtx +get_thread_pointer () +{ + rtx tp; + + tp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TP); + tp = force_reg (Pmode, tp); + mark_reg_pointer (tp, BITS_PER_WORD); + + return tp; +} + +/* Construct the SYMBOL_REF for the tls_get_offset function. */ + +static GTY(()) rtx s390_tls_symbol; +rtx +s390_tls_get_offset () +{ + if (!s390_tls_symbol) + s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset"); + + return s390_tls_symbol; +} + +/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute + this (thread-local) address. REG may be used as temporary. */ + +static rtx +legitimize_tls_address (addr, reg) + rtx addr; + rtx reg; +{ + rtx new, tls_call, temp, base, r2, insn; + + if (GET_CODE (addr) == SYMBOL_REF) + switch (tls_symbolic_operand (addr)) + { + case TLS_MODEL_GLOBAL_DYNAMIC: + start_sequence (); + r2 = gen_rtx_REG (Pmode, 2); + tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD); + new = gen_rtx_CONST (Pmode, tls_call); + new = force_const_mem (Pmode, new); + emit_move_insn (r2, new); + emit_call_insn (gen_call_value_tls (r2, tls_call)); + insn = get_insns (); + end_sequence (); + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF); + temp = gen_reg_rtx (Pmode); + emit_libcall_block (insn, temp, r2, new); + + new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp); + if (reg != 0) + { + s390_load_address (reg, new); + new = reg; + } + break; + + case TLS_MODEL_LOCAL_DYNAMIC: + start_sequence (); + r2 = gen_rtx_REG (Pmode, 2); + tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM); + new = gen_rtx_CONST (Pmode, tls_call); + new = force_const_mem (Pmode, new); + emit_move_insn (r2, new); + emit_call_insn (gen_call_value_tls (r2, tls_call)); + insn = get_insns (); + end_sequence (); + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF); + temp = gen_reg_rtx (Pmode); + emit_libcall_block (insn, temp, r2, new); + + new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp); + base = gen_reg_rtx (Pmode); + s390_load_address (base, new); + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF); + new = gen_rtx_CONST (Pmode, new); + new = force_const_mem (Pmode, new); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + + new = gen_rtx_PLUS (Pmode, base, temp); + if (reg != 0) + { + s390_load_address (reg, new); + new = reg; + } + break; + + case TLS_MODEL_INITIAL_EXEC: + if (flag_pic == 1) + { + /* Assume GOT offset < 4k. This is handled the same way + in both 31- and 64-bit code. */ + + if (reload_in_progress || reload_completed) + regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); + new = gen_rtx_CONST (Pmode, new); + new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new); + new = gen_rtx_MEM (Pmode, new); + RTX_UNCHANGING_P (new) = 1; + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + } + else if (TARGET_64BIT) + { + /* If the GOT offset might be >= 4k, we determine the position + of the GOT entry via a PC-relative LARL. */ + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF); + new = gen_rtx_CONST (Pmode, new); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + + new = gen_rtx_MEM (Pmode, temp); + RTX_UNCHANGING_P (new) = 1; + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + } + else if (flag_pic) + { + /* If the GOT offset might be >= 4k, we have to load it + from the literal pool. */ + + if (reload_in_progress || reload_completed) + regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); + new = gen_rtx_CONST (Pmode, new); + new = force_const_mem (Pmode, new); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + + new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); + new = gen_rtx_MEM (Pmode, new); + RTX_UNCHANGING_P (new) = 1; + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD); + temp = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (Pmode, temp, new)); + } + else + { + /* In position-dependent code, load the absolute address of + the GOT entry from the literal pool. */ + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF); + new = gen_rtx_CONST (Pmode, new); + new = force_const_mem (Pmode, new); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + + new = temp; + new = gen_rtx_MEM (Pmode, new); + RTX_UNCHANGING_P (new) = 1; + + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD); + temp = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (Pmode, temp, new)); + } + + new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp); + if (reg != 0) + { + s390_load_address (reg, new); + new = reg; + } + break; + + case TLS_MODEL_LOCAL_EXEC: + new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF); + new = gen_rtx_CONST (Pmode, new); + new = force_const_mem (Pmode, new); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new); + + new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp); + if (reg != 0) + { + s390_load_address (reg, new); + new = reg; + } + break; + + default: + abort (); + } + + else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC) + { + switch (XINT (XEXP (addr, 0), 1)) + { + case UNSPEC_INDNTPOFF: + if (TARGET_64BIT) + new = addr; + else + abort (); + break; + + default: + abort (); + } + } + + else + abort (); /* for now ... */ + + return new; +} + /* Emit insns to move operands[1] into operands[0]. */ void -emit_pic_move (operands, mode) +emit_symbolic_move (operands) rtx *operands; - enum machine_mode mode ATTRIBUTE_UNUSED; { rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode); - if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1])) + if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (Pmode, operands[1]); - else + else if (TLS_SYMBOLIC_CONST (operands[1])) + operands[1] = legitimize_tls_address (operands[1], temp); + else if (flag_pic) operands[1] = legitimize_pic_address (operands[1], temp); } @@ -1859,7 +2558,14 @@ legitimize_address (x, oldx, mode) { rtx constant_term = const0_rtx; - if (flag_pic) + if (TLS_SYMBOLIC_CONST (x)) + { + x = legitimize_tls_address (x, 0); + + if (legitimate_address_p (mode, x, FALSE)) + return x; + } + else if (flag_pic) { if (SYMBOLIC_CONST (x) || (GET_CODE (x) == PLUS @@ -1873,6 +2579,31 @@ legitimize_address (x, oldx, mode) x = eliminate_constant_term (x, &constant_term); + /* Optimize loading of large displacements by splitting them + into the multiple of 4K and the rest; this allows the + former to be CSE'd if possible. + + Don't do this if the displacement is added to a register + pointing into the stack frame, as the offsets will + change later anyway. */ + + if (GET_CODE (constant_term) == CONST_INT + && (INTVAL (constant_term) < 0 + || INTVAL (constant_term) >= 4096) + && !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x)))) + { + HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff; + HOST_WIDE_INT upper = INTVAL (constant_term) ^ lower; + + rtx temp = gen_reg_rtx (Pmode); + rtx val = force_operand (GEN_INT (upper), temp); + if (val != temp) + emit_move_insn (temp, val); + + x = gen_rtx_PLUS (Pmode, x, temp); + constant_term = GEN_INT (lower); + } + if (GET_CODE (x) == PLUS) { if (GET_CODE (XEXP (x, 0)) == REG) @@ -1902,6 +2633,316 @@ legitimize_address (x, oldx, mode) return x; } +/* Emit code to move LEN bytes from DST to SRC. */ + +void +s390_expand_movstr (dst, src, len) + rtx dst; + rtx src; + rtx len; +{ + rtx (*gen_short) PARAMS ((rtx, rtx, rtx)) = + TARGET_64BIT ? gen_movstr_short_64 : gen_movstr_short_31; + rtx (*gen_long) PARAMS ((rtx, rtx, rtx, rtx)) = + TARGET_64BIT ? gen_movstr_long_64 : gen_movstr_long_31; + + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + emit_insn ((*gen_short) (dst, src, GEN_INT (INTVAL (len) - 1))); + } + + else if (TARGET_MVCLE) + { + enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; + enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; + rtx reg0 = gen_reg_rtx (double_mode); + rtx reg1 = gen_reg_rtx (double_mode); + + emit_move_insn (gen_highpart (single_mode, reg0), + force_operand (XEXP (dst, 0), NULL_RTX)); + emit_move_insn (gen_highpart (single_mode, reg1), + force_operand (XEXP (src, 0), NULL_RTX)); + + convert_move (gen_lowpart (single_mode, reg0), len, 1); + convert_move (gen_lowpart (single_mode, reg1), len, 1); + + emit_insn ((*gen_long) (reg0, reg1, reg0, reg1)); + } + + else + { + rtx dst_addr, src_addr, count, blocks, temp; + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + tree type; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = word_mode; + + type = (*lang_hooks.types.type_for_mode) (mode, 1); + if (!type) + abort (); + + dst_addr = gen_reg_rtx (Pmode); + src_addr = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); + emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX)); + dst = change_address (dst, VOIDmode, dst_addr); + src = change_address (src, VOIDmode, src_addr); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_start_loop (1); + expand_exit_loop_top_cond (0, build (NE_EXPR, type, + make_tree (type, blocks), + make_tree (type, const0_rtx))); + + emit_insn ((*gen_short) (dst, src, GEN_INT (255))); + s390_load_address (dst_addr, + gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); + s390_load_address (src_addr, + gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_end_loop (); + + emit_insn ((*gen_short) (dst, src, convert_to_mode (word_mode, count, 1))); + emit_label (end_label); + } +} + +/* Emit code to clear LEN bytes at DST. */ + +void +s390_expand_clrstr (dst, len) + rtx dst; + rtx len; +{ + rtx (*gen_short) PARAMS ((rtx, rtx)) = + TARGET_64BIT ? gen_clrstr_short_64 : gen_clrstr_short_31; + rtx (*gen_long) PARAMS ((rtx, rtx, rtx)) = + TARGET_64BIT ? gen_clrstr_long_64 : gen_clrstr_long_31; + + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + emit_insn ((*gen_short) (dst, GEN_INT (INTVAL (len) - 1))); + } + + else if (TARGET_MVCLE) + { + enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; + enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; + rtx reg0 = gen_reg_rtx (double_mode); + rtx reg1 = gen_reg_rtx (double_mode); + + emit_move_insn (gen_highpart (single_mode, reg0), + force_operand (XEXP (dst, 0), NULL_RTX)); + convert_move (gen_lowpart (single_mode, reg0), len, 1); + + emit_move_insn (gen_highpart (single_mode, reg1), const0_rtx); + emit_move_insn (gen_lowpart (single_mode, reg1), const0_rtx); + + emit_insn ((*gen_long) (reg0, reg1, reg0)); + } + + else + { + rtx dst_addr, src_addr, count, blocks, temp; + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + tree type; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = word_mode; + + type = (*lang_hooks.types.type_for_mode) (mode, 1); + if (!type) + abort (); + + dst_addr = gen_reg_rtx (Pmode); + src_addr = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); + dst = change_address (dst, VOIDmode, dst_addr); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_start_loop (1); + expand_exit_loop_top_cond (0, build (NE_EXPR, type, + make_tree (type, blocks), + make_tree (type, const0_rtx))); + + emit_insn ((*gen_short) (dst, GEN_INT (255))); + s390_load_address (dst_addr, + gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_end_loop (); + + emit_insn ((*gen_short) (dst, convert_to_mode (word_mode, count, 1))); + emit_label (end_label); + } +} + +/* Emit code to compare LEN bytes at OP0 with those at OP1, + and return the result in TARGET. */ + +void +s390_expand_cmpstr (target, op0, op1, len) + rtx target; + rtx op0; + rtx op1; + rtx len; +{ + rtx (*gen_short) PARAMS ((rtx, rtx, rtx)) = + TARGET_64BIT ? gen_cmpstr_short_64 : gen_cmpstr_short_31; + rtx (*gen_long) PARAMS ((rtx, rtx, rtx, rtx)) = + TARGET_64BIT ? gen_cmpstr_long_64 : gen_cmpstr_long_31; + rtx (*gen_result) PARAMS ((rtx)) = + GET_MODE (target) == DImode ? gen_cmpint_di : gen_cmpint_si; + + op0 = protect_from_queue (op0, 0); + op1 = protect_from_queue (op1, 0); + len = protect_from_queue (len, 0); + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + { + emit_insn ((*gen_short) (op0, op1, GEN_INT (INTVAL (len) - 1))); + emit_insn ((*gen_result) (target)); + } + else + emit_move_insn (target, const0_rtx); + } + + else /* if (TARGET_MVCLE) */ + { + enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; + enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; + rtx reg0 = gen_reg_rtx (double_mode); + rtx reg1 = gen_reg_rtx (double_mode); + + emit_move_insn (gen_highpart (single_mode, reg0), + force_operand (XEXP (op0, 0), NULL_RTX)); + emit_move_insn (gen_highpart (single_mode, reg1), + force_operand (XEXP (op1, 0), NULL_RTX)); + + convert_move (gen_lowpart (single_mode, reg0), len, 1); + convert_move (gen_lowpart (single_mode, reg1), len, 1); + + emit_insn ((*gen_long) (reg0, reg1, reg0, reg1)); + emit_insn ((*gen_result) (target)); + } + +#if 0 + /* Deactivate for now as profile code cannot cope with + CC being live across basic block boundaries. */ + else + { + rtx addr0, addr1, count, blocks, temp; + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + tree type; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = word_mode; + + type = (*lang_hooks.types.type_for_mode) (mode, 1); + if (!type) + abort (); + + addr0 = gen_reg_rtx (Pmode); + addr1 = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX)); + emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX)); + op0 = change_address (op0, VOIDmode, addr0); + op1 = change_address (op1, VOIDmode, addr1); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_start_loop (1); + expand_exit_loop_top_cond (0, build (NE_EXPR, type, + make_tree (type, blocks), + make_tree (type, const0_rtx))); + + emit_insn ((*gen_short) (op0, op1, GEN_INT (255))); + temp = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCSmode, 33), const0_rtx); + temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp, + gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx); + temp = gen_rtx_SET (VOIDmode, pc_rtx, temp); + emit_jump_insn (temp); + + s390_load_address (addr0, + gen_rtx_PLUS (Pmode, addr0, GEN_INT (256))); + s390_load_address (addr1, + gen_rtx_PLUS (Pmode, addr1, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0); + if (temp != blocks) + emit_move_insn (blocks, temp); + + expand_end_loop (); + + emit_insn ((*gen_short) (op0, op1, convert_to_mode (word_mode, count, 1))); + emit_label (end_label); + + emit_insn ((*gen_result) (target)); + } +#endif +} + /* In the name of slightly smaller debug output, and to cater to general assembler losage, recognize various UNSPEC sequences and turn them back into a direct symbol reference. */ @@ -1940,6 +2981,48 @@ s390_simplify_dwarf_addr (orig_x) return orig_x; } +/* Locate some local-dynamic symbol still in use by this function + so that we can print its name in local-dynamic base patterns. */ + +static const char * +get_some_local_dynamic_name () +{ + rtx insn; + + if (cfun->machine->some_ld_name) + return cfun->machine->some_ld_name; + + for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) + if (INSN_P (insn) + && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) + return cfun->machine->some_ld_name; + + abort (); +} + +static int +get_some_local_dynamic_name_1 (px, data) + rtx *px; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *px; + + if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) + { + x = get_pool_constant (x); + return for_each_rtx (&x, get_some_local_dynamic_name_1, 0); + } + + if (GET_CODE (x) == SYMBOL_REF + && tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC) + { + cfun->machine->some_ld_name = XSTR (x, 0); + return 1; + } + + return 0; +} + /* Output symbolic constant X in assembler syntax to stdio stream FILE. */ @@ -1981,8 +3064,15 @@ s390_output_symbolic_const (file, x) switch (XINT (x, 1)) { case 100: + case 104: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "-"); + s390_output_symbolic_const (file, cfun->machine->literal_pool_label); + break; + case 105: + s390_output_symbolic_const (file, cfun->machine->literal_pool_label); + fprintf (file, "-"); s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); - fprintf (file, "-.LT%X", s390_function_count); break; case 110: s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); @@ -2002,7 +3092,32 @@ s390_output_symbolic_const (file, x) break; case 114: s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); - fprintf (file, "@PLT-.LT%X", s390_function_count); + fprintf (file, "@PLT-"); + s390_output_symbolic_const (file, cfun->machine->literal_pool_label); + break; + case UNSPEC_TLSGD: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@TLSGD"); + break; + case UNSPEC_TLSLDM: + assemble_name (file, get_some_local_dynamic_name ()); + fprintf (file, "@TLSLDM"); + break; + case UNSPEC_DTPOFF: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@DTPOFF"); + break; + case UNSPEC_NTPOFF: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@NTPOFF"); + break; + case UNSPEC_GOTNTPOFF: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@GOTNTPOFF"); + break; + case UNSPEC_INDNTPOFF: + s390_output_symbolic_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@INDNTPOFF"); break; default: output_operand_lossage ("invalid UNSPEC as operand (2)"); @@ -2026,7 +3141,9 @@ print_operand_address (file, addr) { struct s390_address ad; - if (!s390_decompose_address (addr, &ad, TRUE)) + if (!s390_decompose_address (addr, &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) + || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx))) output_operand_lossage ("Cannot decompose address."); if (ad.disp) @@ -2047,6 +3164,7 @@ print_operand_address (file, addr) 'C': print opcode suffix for branch condition. 'D': print opcode suffix for inverse branch condition. + 'J': print tls_load/tls_gdcall/tls_ldcall suffix 'O': print only the displacement of a memory reference. 'R': print only the base register of a memory reference. 'N': print the second word of a DImode operand. @@ -2072,12 +3190,33 @@ print_operand (file, x, code) fprintf (file, s390_branch_condition_mnemonic (x, TRUE)); return; + case 'J': + if (GET_CODE (x) == SYMBOL_REF) + { + fprintf (file, "%s", ":tls_load:"); + output_addr_const (file, x); + } + else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD) + { + fprintf (file, "%s", ":tls_gdcall:"); + output_addr_const (file, XVECEXP (x, 0, 0)); + } + else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM) + { + fprintf (file, "%s", ":tls_ldcall:"); + assemble_name (file, get_some_local_dynamic_name ()); + } + else + abort (); + return; + case 'O': { struct s390_address ad; if (GET_CODE (x) != MEM - || !s390_decompose_address (XEXP (x, 0), &ad, TRUE) + || !s390_decompose_address (XEXP (x, 0), &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) || ad.indx) abort (); @@ -2093,7 +3232,8 @@ print_operand (file, x, code) struct s390_address ad; if (GET_CODE (x) != MEM - || !s390_decompose_address (XEXP (x, 0), &ad, TRUE) + || !s390_decompose_address (XEXP (x, 0), &ad) + || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base)) || ad.indx) abort (); @@ -2248,7 +3388,11 @@ addr_generation_dependency_p (dep_rtx, insn) if (GET_CODE (dep_rtx) == SET) { target = SET_DEST (dep_rtx); - + if (GET_CODE (target) == STRICT_LOW_PART) + target = XEXP (target, 0); + while (GET_CODE (target) == SUBREG) + target = SUBREG_REG (target); + if (GET_CODE (target) == REG) { int regno = REGNO (target); @@ -2389,18 +3533,25 @@ s390_adjust_priority (insn, priority) } -/* Split all branches that exceed the maximum distance. */ +/* Split all branches that exceed the maximum distance. + Returns true if this created a new literal pool entry. + + Code generated by this routine is allowed to use + TEMP_REG as temporary scratch register. If this is + done, TEMP_USED is set to true. */ -static void -s390_split_branches (void) +static int +s390_split_branches (temp_reg, temp_used) + rtx temp_reg; + bool *temp_used; { - rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); - rtx insn, pat, label, target, jump, tmp; + int new_literal = 0; + rtx insn, pat, tmp, target; + rtx *label; - /* In 64-bit mode we can jump +- 4GB. */ + /* We need correct insn addresses. */ - if (TARGET_64BIT) - return; + shorten_branches (get_insns ()); /* Find all branches that exceed 64KB, and split them. */ @@ -2410,61 +3561,66 @@ s390_split_branches (void) continue; pat = PATTERN (insn); - if (GET_CODE (pat) != SET) + if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) + pat = XVECEXP (pat, 0, 0); + if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx) continue; if (GET_CODE (SET_SRC (pat)) == LABEL_REF) { - label = SET_SRC (pat); + label = &SET_SRC (pat); } else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) { if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) - label = XEXP (SET_SRC (pat), 1); + label = &XEXP (SET_SRC (pat), 1); else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) - label = XEXP (SET_SRC (pat), 2); + label = &XEXP (SET_SRC (pat), 2); else continue; } else continue; - if (get_attr_length (insn) == 4) + if (get_attr_length (insn) <= (TARGET_64BIT ? 6 : 4)) continue; - if (flag_pic) + *temp_used = 1; + + if (TARGET_64BIT) { - target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, label), 100); - target = gen_rtx_CONST (SImode, target); - target = force_const_mem (SImode, target); - jump = gen_rtx_REG (Pmode, BASE_REGISTER); - jump = gen_rtx_PLUS (Pmode, jump, temp_reg); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, *label), insn); + INSN_ADDRESSES_NEW (tmp, -1); + + target = temp_reg; } - else + else if (!flag_pic) { - target = force_const_mem (Pmode, label); - jump = temp_reg; - } + new_literal = 1; + tmp = force_const_mem (Pmode, *label); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); + INSN_ADDRESSES_NEW (tmp, -1); - if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) + target = temp_reg; + } + else { - if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) - jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0), - jump, pc_rtx); - else - jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0), - pc_rtx, jump); + new_literal = 1; + tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 104); + tmp = gen_rtx_CONST (SImode, tmp); + tmp = force_const_mem (SImode, tmp); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); + INSN_ADDRESSES_NEW (tmp, -1); + + target = gen_rtx_REG (Pmode, BASE_REGISTER); + target = gen_rtx_PLUS (Pmode, target, temp_reg); } - tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn); - INSN_ADDRESSES_NEW (tmp, -1); - - tmp = emit_jump_insn_before (gen_rtx_SET (VOIDmode, pc_rtx, jump), insn); - INSN_ADDRESSES_NEW (tmp, -1); - - remove_insn (insn); - insn = tmp; + if (!validate_change (insn, label, target, 0)) + abort (); } + + return new_literal; } @@ -2582,6 +3738,153 @@ replace_constant_pool_ref (x, ref, addr) } } +/* Check whether ADDR is an address that uses the base register, + without actually constituting a literal pool access. (This happens + in 31-bit PIC mode, where the base register is used as anchor for + relative addressing of local symbols.) + + Returns 1 if the base register occupies the base slot, + returns 2 if the base register occupies the index slot, + returns 0 if the address is not of this form. */ + +static int +find_base_register_in_addr (addr) + struct s390_address *addr; +{ + /* If DISP is complex, we might have a literal pool reference. */ + if (addr->disp && GET_CODE (addr->disp) != CONST_INT) + return 0; + + if (addr->base && REG_P (addr->base) && REGNO (addr->base) == BASE_REGISTER) + return 1; + + if (addr->indx && REG_P (addr->indx) && REGNO (addr->indx) == BASE_REGISTER) + return 2; + + return 0; +} + +/* Return true if X contains an address that uses the base register, + without actually constituting a literal pool access. */ + +static bool +find_base_register_ref (x) + rtx x; +{ + bool retv = FALSE; + struct s390_address addr; + int i, j; + const char *fmt; + + /* Addresses can only occur inside a MEM ... */ + if (GET_CODE (x) == MEM) + { + if (s390_decompose_address (XEXP (x, 0), &addr) + && find_base_register_in_addr (&addr)) + return TRUE; + } + + /* ... or a load-address type pattern. */ + if (GET_CODE (x) == SET && GET_CODE (SET_DEST (x)) == REG) + { + if (s390_decompose_address (SET_SRC (x), &addr) + && find_base_register_in_addr (&addr)) + return TRUE; + } + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + retv |= find_base_register_ref (XEXP (x, i)); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (x, i); j++) + retv |= find_base_register_ref (XVECEXP (x, i, j)); + } + } + + return retv; +} + +/* If X contains an address that uses the base register, + without actually constituting a literal pool access, + replace the base register with REPL in all such cases. + + Handles both MEMs and load address patterns. */ + +static void +replace_base_register_ref (x, repl) + rtx *x; + rtx repl; +{ + struct s390_address addr; + rtx new_addr; + int i, j, pos; + const char *fmt; + + /* Addresses can only occur inside a MEM ... */ + if (GET_CODE (*x) == MEM) + { + if (s390_decompose_address (XEXP (*x, 0), &addr) + && (pos = find_base_register_in_addr (&addr))) + { + if (pos == 1) + addr.base = repl; + else + addr.indx = repl; + + new_addr = addr.base; + if (addr.indx) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx); + if (addr.disp) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp); + + *x = replace_equiv_address (*x, new_addr); + return; + } + } + + /* ... or a load-address type pattern. */ + if (GET_CODE (*x) == SET && GET_CODE (SET_DEST (*x)) == REG) + { + if (s390_decompose_address (SET_SRC (*x), &addr) + && (pos = find_base_register_in_addr (&addr))) + { + if (pos == 1) + addr.base = repl; + else + addr.indx = repl; + + new_addr = addr.base; + if (addr.indx) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx); + if (addr.disp) + new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp); + + SET_SRC (*x) = new_addr; + return; + } + } + + fmt = GET_RTX_FORMAT (GET_CODE (*x)); + for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + replace_base_register_ref (&XEXP (*x, i), repl); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (*x, i); j++) + replace_base_register_ref (&XVECEXP (*x, i, j), repl); + } + } +} + + /* We keep a list of constants we which we have to add to internal constant tables in the middle of large functions. */ @@ -2613,17 +3916,26 @@ struct constant_pool { struct constant_pool *next; rtx first_insn; - rtx last_insn; + rtx pool_insn; + bitmap insns; struct constant *constants[NR_C_MODES]; rtx label; int size; + bool anchor; }; +static struct constant_pool * s390_chunkify_start PARAMS ((rtx, bool *)); +static void s390_chunkify_finish PARAMS ((struct constant_pool *, rtx)); +static void s390_chunkify_cancel PARAMS ((struct constant_pool *)); + static struct constant_pool *s390_start_pool PARAMS ((struct constant_pool **, rtx)); static void s390_end_pool PARAMS ((struct constant_pool *, rtx)); +static void s390_add_pool_insn PARAMS ((struct constant_pool *, rtx)); static struct constant_pool *s390_find_pool PARAMS ((struct constant_pool *, rtx)); -static rtx s390_add_pool PARAMS ((struct constant_pool *, rtx, enum machine_mode)); +static void s390_add_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode)); +static rtx s390_find_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode)); +static void s390_add_anchor PARAMS ((struct constant_pool *)); static rtx s390_dump_pool PARAMS ((struct constant_pool *)); static void s390_free_pool PARAMS ((struct constant_pool *)); @@ -2645,9 +3957,11 @@ s390_start_pool (pool_list, insn) pool->label = gen_label_rtx (); pool->first_insn = insn; - pool->last_insn = NULL_RTX; + pool->pool_insn = NULL_RTX; + pool->insns = BITMAP_XMALLOC (); pool->size = 0; - + pool->anchor = FALSE; + for (prev = pool_list; *prev; prev = &(*prev)->next) ; *prev = pool; @@ -2655,14 +3969,31 @@ s390_start_pool (pool_list, insn) return pool; } -/* End range of instructions covered by POOL at INSN. */ +/* End range of instructions covered by POOL at INSN and emit + placeholder insn representing the pool. */ static void s390_end_pool (pool, insn) struct constant_pool *pool; rtx insn; { - pool->last_insn = insn; + rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */); + + if (!insn) + insn = get_last_insn (); + + pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn); + INSN_ADDRESSES_NEW (pool->pool_insn, -1); +} + +/* Add INSN to the list of insns covered by POOL. */ + +static void +s390_add_pool_insn (pool, insn) + struct constant_pool *pool; + rtx insn; +{ + bitmap_set_bit (pool->insns, INSN_UID (insn)); } /* Return pool out of POOL_LIST that covers INSN. */ @@ -2672,33 +4003,24 @@ s390_find_pool (pool_list, insn) struct constant_pool *pool_list; rtx insn; { - int addr = INSN_ADDRESSES (INSN_UID (insn)); struct constant_pool *pool; - if (addr == -1) - return NULL; - for (pool = pool_list; pool; pool = pool->next) - if (INSN_ADDRESSES (INSN_UID (pool->first_insn)) <= addr - && (pool->last_insn == NULL_RTX - || INSN_ADDRESSES (INSN_UID (pool->last_insn)) > addr)) + if (bitmap_bit_p (pool->insns, INSN_UID (insn))) break; return pool; } -/* Add constant VAL of mode MODE to the constant pool POOL. - Return an RTX describing the distance from the start of - the pool to the location of the new constant. */ +/* Add constant VAL of mode MODE to the constant pool POOL. */ -static rtx -s390_add_pool (pool, val, mode) +static void +s390_add_constant (pool, val, mode) struct constant_pool *pool; rtx val; enum machine_mode mode; { struct constant *c; - rtx offset; int i; for (i = 0; i < NR_C_MODES; i++) @@ -2720,13 +4042,54 @@ s390_add_pool (pool, val, mode) pool->constants[i] = c; pool->size += GET_MODE_SIZE (mode); } +} - offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), - gen_rtx_LABEL_REF (Pmode, pool->label)); +/* Find constant VAL of mode MODE in the constant pool POOL. + Return an RTX describing the distance from the start of + the pool to the location of the new constant. */ + +static rtx +s390_find_constant (pool, val, mode) + struct constant_pool *pool; + rtx val; + enum machine_mode mode; +{ + struct constant *c; + rtx offset; + int i; + + for (i = 0; i < NR_C_MODES; i++) + if (constant_modes[i] == mode) + break; + if (i == NR_C_MODES) + abort (); + + for (c = pool->constants[i]; c != NULL; c = c->next) + if (rtx_equal_p (val, c->value)) + break; + + if (c == NULL) + abort (); + + offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), + gen_rtx_LABEL_REF (Pmode, pool->label)); offset = gen_rtx_CONST (Pmode, offset); return offset; } +/* Set 'anchor' flag in POOL. */ + +static void +s390_add_anchor (pool) + struct constant_pool *pool; +{ + if (!pool->anchor) + { + pool->anchor = TRUE; + pool->size += 4; + } +} + /* Dump out the constants in POOL. */ static rtx @@ -2737,31 +4100,47 @@ s390_dump_pool (pool) rtx insn; int i; - /* Select location to put literal pool. */ - if (TARGET_64BIT) - insn = get_last_insn (); - else - insn = pool->last_insn? pool->last_insn : get_last_insn (); - /* Pool start insn switches to proper section and guarantees necessary alignment. */ if (TARGET_64BIT) - insn = emit_insn_after (gen_pool_start_64 (), insn); + insn = emit_insn_after (gen_pool_start_64 (), pool->pool_insn); else - insn = emit_insn_after (gen_pool_start_31 (), insn); + insn = emit_insn_after (gen_pool_start_31 (), pool->pool_insn); INSN_ADDRESSES_NEW (insn, -1); insn = emit_label_after (pool->label, insn); INSN_ADDRESSES_NEW (insn, -1); + /* Emit anchor if we need one. */ + if (pool->anchor) + { + rtx anchor = gen_rtx_LABEL_REF (VOIDmode, pool->label); + anchor = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, anchor), 105); + anchor = gen_rtx_CONST (VOIDmode, anchor); + insn = emit_insn_after (gen_consttable_si (anchor), insn); + INSN_ADDRESSES_NEW (insn, -1); + } + /* Dump constants in descending alignment requirement order, ensuring proper alignment for every constant. */ for (i = 0; i < NR_C_MODES; i++) for (c = pool->constants[i]; c; c = c->next) { + /* Convert 104 unspecs to pool-relative references. */ + rtx value = c->value; + if (GET_CODE (value) == CONST + && GET_CODE (XEXP (value, 0)) == UNSPEC + && XINT (XEXP (value, 0), 1) == 104 + && XVECLEN (XEXP (value, 0), 0) == 1) + { + value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0), + gen_rtx_LABEL_REF (VOIDmode, pool->label)); + value = gen_rtx_CONST (VOIDmode, value); + } + insn = emit_label_after (c->label, insn); INSN_ADDRESSES_NEW (insn, -1); - insn = emit_insn_after (gen_consttable[i] (c->value), insn); + insn = emit_insn_after (gen_consttable[i] (value), insn); INSN_ADDRESSES_NEW (insn, -1); } @@ -2776,6 +4155,9 @@ s390_dump_pool (pool) insn = emit_barrier_after (insn); INSN_ADDRESSES_NEW (insn, -1); + /* Remove placeholder insn. */ + remove_insn (pool->pool_insn); + return insn; } @@ -2798,57 +4180,86 @@ s390_free_pool (pool) } } + BITMAP_XFREE (pool->insns); free (pool); } -/* Used in s390.md for branch length calculation. */ -int s390_pool_overflow = 0; -/* Chunkify the literal pool if required. */ +/* Chunkify the literal pool if required. + + Code generated by this routine is allowed to use + TEMP_REG as temporary scratch register. If this is + done, TEMP_USED is set to true. */ #define S390_POOL_CHUNK_MIN 0xc00 #define S390_POOL_CHUNK_MAX 0xe00 -static void -s390_chunkify_pool (void) +static struct constant_pool * +s390_chunkify_start (temp_reg, temp_used) + rtx temp_reg; + bool *temp_used; { - rtx base_reg = gen_rtx_REG (Pmode, - TARGET_64BIT? BASE_REGISTER : RETURN_REGNUM); + rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER); struct constant_pool *curr_pool = NULL, *pool_list = NULL; int extra_size = 0; bitmap far_labels; rtx insn; + rtx (*gen_reload_base) PARAMS ((rtx, rtx)) = + TARGET_64BIT? gen_reload_base_64 : gen_reload_base_31; + + /* Do we need to chunkify the literal pool? */ if (get_pool_size () < S390_POOL_CHUNK_MAX) - return; + return NULL; + + /* We need correct insn addresses. */ + + shorten_branches (get_insns ()); /* Scan all insns and move literals to pool chunks. - Replace all occurrances of literal pool references - by explicit references to pool chunk entries. */ + Also, emit anchor reload insns before every insn that uses + the literal pool base register as anchor pointer. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { - if (GET_CODE (insn) == INSN) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) { - rtx addr, pool_ref = NULL_RTX; + rtx pool_ref = NULL_RTX; find_constant_pool_ref (PATTERN (insn), &pool_ref); if (pool_ref) { if (!curr_pool) curr_pool = s390_start_pool (&pool_list, insn); - addr = s390_add_pool (curr_pool, get_pool_constant (pool_ref), - get_pool_mode (pool_ref)); + s390_add_constant (curr_pool, get_pool_constant (pool_ref), + get_pool_mode (pool_ref)); + s390_add_pool_insn (curr_pool, insn); + } - addr = gen_rtx_PLUS (Pmode, base_reg, addr); - replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); - INSN_CODE (insn) = -1; + else if (!TARGET_64BIT && flag_pic + && find_base_register_ref (PATTERN (insn))) + { + rtx new = gen_reload_anchor (temp_reg, base_reg); + new = emit_insn_before (new, insn); + INSN_ADDRESSES_NEW (new, INSN_ADDRESSES (INSN_UID (insn))); + extra_size += 8; + *temp_used = 1; + + if (!curr_pool) + curr_pool = s390_start_pool (&pool_list, new); + + s390_add_anchor (curr_pool); + s390_add_pool_insn (curr_pool, insn); } } + if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL) + if (curr_pool) + s390_add_pool_insn (curr_pool, insn); + if (!curr_pool || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn) || INSN_ADDRESSES (INSN_UID (insn)) == -1) @@ -2859,7 +4270,7 @@ s390_chunkify_pool (void) if (curr_pool->size < S390_POOL_CHUNK_MAX) continue; - s390_end_pool (curr_pool, insn); + s390_end_pool (curr_pool, NULL_RTX); curr_pool = NULL; } else @@ -2872,11 +4283,8 @@ s390_chunkify_pool (void) Those will have an effect on code size, which we need to consider here. This calculation makes rather pessimistic worst-case assumptions. */ - if (GET_CODE (insn) == CODE_LABEL - || GET_CODE (insn) == JUMP_INSN) + if (GET_CODE (insn) == CODE_LABEL) extra_size += 6; - else if (GET_CODE (insn) == CALL_INSN) - extra_size += 4; if (chunk_size < S390_POOL_CHUNK_MIN && curr_pool->size < S390_POOL_CHUNK_MIN) @@ -2892,12 +4300,22 @@ s390_chunkify_pool (void) /* ... so if we don't find one in time, create one. */ else if ((chunk_size > S390_POOL_CHUNK_MAX - || curr_pool->size > S390_POOL_CHUNK_MAX) - && (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)) + || curr_pool->size > S390_POOL_CHUNK_MAX)) { - int addr = INSN_ADDRESSES (INSN_UID (insn)); rtx label, jump, barrier; + /* We can insert the barrier only after a 'real' insn. */ + if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + continue; + if (get_attr_length (insn) == 0) + continue; + + /* Don't separate insns created by s390_split_branches. */ + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && rtx_equal_p (SET_DEST (PATTERN (insn)), temp_reg)) + continue; + label = gen_label_rtx (); jump = emit_jump_insn_after (gen_jump (label), insn); barrier = emit_barrier_after (jump); @@ -2905,8 +4323,8 @@ s390_chunkify_pool (void) JUMP_LABEL (jump) = label; LABEL_NUSES (label) = 1; - INSN_ADDRESSES_NEW (jump, addr+1); - INSN_ADDRESSES_NEW (barrier, addr+1); + INSN_ADDRESSES_NEW (jump, -1); + INSN_ADDRESSES_NEW (barrier, -1); INSN_ADDRESSES_NEW (insn, -1); s390_end_pool (curr_pool, barrier); @@ -2916,10 +4334,8 @@ s390_chunkify_pool (void) } } - /* Dump out all literal pools. */ - - for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) - s390_dump_pool (curr_pool); + if (curr_pool) + s390_end_pool (curr_pool, NULL_RTX); /* Find all labels that are branched into @@ -2953,22 +4369,12 @@ s390_chunkify_pool (void) else if (GET_CODE (insn) == JUMP_INSN) { rtx pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) + pat = XVECEXP (pat, 0, 0); + if (GET_CODE (pat) == SET) { - rtx label = 0; - - if (GET_CODE (SET_SRC (pat)) == LABEL_REF) - { - label = XEXP (SET_SRC (pat), 0); - } - else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) - { - if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) - label = XEXP (XEXP (SET_SRC (pat), 1), 0); - else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) - label = XEXP (XEXP (SET_SRC (pat), 2), 0); - } - + rtx label = JUMP_LABEL (insn); if (label) { if (s390_find_pool (pool_list, label) @@ -3009,19 +4415,11 @@ s390_chunkify_pool (void) /* Insert base register reload insns before every pool. */ for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) - if (TARGET_64BIT) - { - rtx pool_ref = gen_rtx_LABEL_REF (Pmode, curr_pool->label); - rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref); - rtx insn = curr_pool->first_insn; - INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); - } - else - { - rtx new_insn = gen_reload_base (base_reg, curr_pool->label); - rtx insn = curr_pool->first_insn; - INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); - } + { + rtx new_insn = gen_reload_base (base_reg, curr_pool->label); + rtx insn = curr_pool->first_insn; + INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); + } /* Insert base register reload insns at every far label. */ @@ -3032,60 +4430,137 @@ s390_chunkify_pool (void) struct constant_pool *pool = s390_find_pool (pool_list, insn); if (pool) { - if (TARGET_64BIT) - { - rtx pool_ref = gen_rtx_LABEL_REF (Pmode, pool->label); - rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); - } - else - { - rtx new_insn = gen_reload_base (base_reg, pool->label); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); - } + rtx new_insn = gen_reload_base (base_reg, pool->label); + INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); } } - /* Insert base register reload insns after every call if necessary. */ - - if (REGNO (base_reg) == RETURN_REGNUM) - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - { - struct constant_pool *pool = s390_find_pool (pool_list, insn); - if (pool) - { - rtx new_insn = gen_reload_base2 (base_reg, pool->label); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); - } - } + + BITMAP_XFREE (far_labels); /* Recompute insn addresses. */ - s390_pool_overflow = 1; init_insn_lengths (); shorten_branches (get_insns ()); - s390_pool_overflow = 0; - /* Insert base register reload insns after far branches. */ + return pool_list; +} - if (!TARGET_64BIT) - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == SET - && get_attr_length (insn) >= 12) - { - struct constant_pool *pool = s390_find_pool (pool_list, insn); - if (pool) +/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. + After we have decided to use this list, finish implementing + all changes to the current function as required. + + Code generated by this routine is allowed to use + TEMP_REG as temporary scratch register. */ + +static void +s390_chunkify_finish (pool_list, temp_reg) + struct constant_pool *pool_list; + rtx temp_reg; +{ + rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER); + struct constant_pool *curr_pool = NULL; + rtx insn; + + + /* Replace all literal pool references. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + curr_pool = s390_find_pool (pool_list, insn); + if (!curr_pool) + continue; + + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx addr, pool_ref = NULL_RTX; + find_constant_pool_ref (PATTERN (insn), &pool_ref); + if (pool_ref) + { + addr = s390_find_constant (curr_pool, get_pool_constant (pool_ref), + get_pool_mode (pool_ref)); + addr = gen_rtx_PLUS (Pmode, base_reg, addr); + replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); + INSN_CODE (insn) = -1; + } + + else if (!TARGET_64BIT && flag_pic + && find_base_register_ref (PATTERN (insn))) { - rtx new_insn = gen_reload_base (base_reg, pool->label); - INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); + replace_base_register_ref (&PATTERN (insn), temp_reg); } + } + } + + /* Dump out all literal pools. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + s390_dump_pool (curr_pool); + + /* Free pool list. */ + + while (pool_list) + { + struct constant_pool *next = pool_list->next; + s390_free_pool (pool_list); + pool_list = next; + } +} + +/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. + We have decided we cannot use this list, so revert all changes + to the current function that were done by s390_chunkify_start. */ + +static void +s390_chunkify_cancel (pool_list) + struct constant_pool *pool_list; +{ + struct constant_pool *curr_pool = NULL; + rtx insn; + + /* Remove all pool placeholder insns. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + { + /* Did we insert an extra barrier? Remove it. */ + rtx barrier = PREV_INSN (curr_pool->pool_insn); + rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX; + rtx label = NEXT_INSN (curr_pool->pool_insn); + + if (jump && GET_CODE (jump) == JUMP_INSN + && barrier && GET_CODE (barrier) == BARRIER + && label && GET_CODE (label) == CODE_LABEL + && GET_CODE (PATTERN (jump)) == SET + && SET_DEST (PATTERN (jump)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF + && XEXP (SET_SRC (PATTERN (jump)), 0) == label) + { + remove_insn (jump); + remove_insn (barrier); + remove_insn (label); } + remove_insn (curr_pool->pool_insn); + } - /* Free all memory. */ + /* Remove all base/anchor register reload insns. */ + + for (insn = get_insns (); insn; ) + { + rtx next_insn = NEXT_INSN (insn); + + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC + && (XINT (SET_SRC (PATTERN (insn)), 1) == 210 + || XINT (SET_SRC (PATTERN (insn)), 1) == 211)) + remove_insn (insn); + + insn = next_insn; + } + + /* Free pool list. */ while (pool_list) { @@ -3093,8 +4568,6 @@ s390_chunkify_pool (void) s390_free_pool (pool_list); pool_list = next; } - - BITMAP_XFREE (far_labels); } @@ -3108,54 +4581,359 @@ int s390_nr_constants; /* Output main constant pool to stdio stream FILE. */ void -s390_output_constant_pool (file) - FILE *file; +s390_output_constant_pool (start_label, end_label) + rtx start_label; + rtx end_label; { - /* Output constant pool. */ - if (s390_nr_constants) + if (TARGET_64BIT) { - if (TARGET_64BIT) + readonly_data_section (); + ASM_OUTPUT_ALIGN (asm_out_file, 3); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (start_label)); + } + else + { + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (start_label)); + ASM_OUTPUT_ALIGN (asm_out_file, 2); + } + + s390_pool_count = 0; + output_constant_pool (current_function_name, current_function_decl); + s390_pool_count = -1; + if (TARGET_64BIT) + function_section (current_function_decl); + else + { + ASM_OUTPUT_ALIGN (asm_out_file, 1); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (end_label)); + } +} + +/* Rework the prolog/epilog to avoid saving/restoring + registers unnecessarily. If TEMP_REGNO is nonnegative, + it specifies the number of a caller-saved register used + as temporary scratch register by code emitted during + machine dependent reorg. */ + +static void +s390_optimize_prolog (temp_regno) + int temp_regno; +{ + int save_first, save_last, restore_first, restore_last; + int i, j; + rtx insn, new_insn, next_insn; + + /* Recompute regs_ever_live data for special registers. */ + regs_ever_live[BASE_REGISTER] = 0; + regs_ever_live[RETURN_REGNUM] = 0; + regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0; + + /* If there is (possibly) any pool entry, we need to + load the base register. + ??? FIXME: this should be more precise. */ + if (get_pool_size ()) + regs_ever_live[BASE_REGISTER] = 1; + + /* In non-leaf functions, the prolog/epilog code relies + on RETURN_REGNUM being saved in any case. */ + if (!current_function_is_leaf) + regs_ever_live[RETURN_REGNUM] = 1; + + /* We need to save/restore the temporary register. */ + if (temp_regno >= 0) + regs_ever_live[temp_regno] = 1; + + + /* Find first and last gpr to be saved. */ + + for (i = 6; i < 16; i++) + if (regs_ever_live[i]) + if (!global_regs[i] + || i == STACK_POINTER_REGNUM + || i == RETURN_REGNUM + || i == BASE_REGISTER + || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM)) + break; + + for (j = 15; j > i; j--) + if (regs_ever_live[j]) + if (!global_regs[j] + || j == STACK_POINTER_REGNUM + || j == RETURN_REGNUM + || j == BASE_REGISTER + || (flag_pic && j == (int)PIC_OFFSET_TABLE_REGNUM)) + break; + + if (i == 16) + { + /* Nothing to save/restore. */ + save_first = restore_first = -1; + save_last = restore_last = -1; + } + else + { + /* Save/restore from i to j. */ + save_first = restore_first = i; + save_last = restore_last = j; + } + + /* Varargs functions need to save gprs 2 to 6. */ + if (current_function_stdarg) + { + save_first = 2; + if (save_last < 6) + save_last = 6; + } + + + /* If all special registers are in fact used, there's nothing we + can do, so no point in walking the insn list. */ + if (i <= BASE_REGISTER && j >= BASE_REGISTER + && i <= RETURN_REGNUM && j >= RETURN_REGNUM) + return; + + + /* Search for prolog/epilog insns and replace them. */ + + for (insn = get_insns (); insn; insn = next_insn) + { + int first, last, off; + rtx set, base, offset; + + next_insn = NEXT_INSN (insn); + + if (GET_CODE (insn) != INSN) + continue; + if (GET_CODE (PATTERN (insn)) != PARALLEL) + continue; + + if (store_multiple_operation (PATTERN (insn), VOIDmode)) { - fprintf (file, "\tlarl\t%s,.LT%X\n", reg_names[BASE_REGISTER], - s390_function_count); - readonly_data_section (); - ASM_OUTPUT_ALIGN (file, 3); + set = XVECEXP (PATTERN (insn), 0, 0); + first = REGNO (SET_SRC (set)); + last = first + XVECLEN (PATTERN (insn), 0) - 1; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); + off = INTVAL (offset) - first * UNITS_PER_WORD; + + if (GET_CODE (base) != REG || off < 0) + continue; + if (first > BASE_REGISTER && first > RETURN_REGNUM) + continue; + if (last < BASE_REGISTER && last < RETURN_REGNUM) + continue; + + if (save_first != -1) + { + new_insn = save_gprs (base, off, save_first, save_last); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } + + remove_insn (insn); } - else + + if (load_multiple_operation (PATTERN (insn), VOIDmode)) { - fprintf (file, "\tbras\t%s,.LTN%X\n", reg_names[BASE_REGISTER], - s390_function_count); + set = XVECEXP (PATTERN (insn), 0, 0); + first = REGNO (SET_DEST (set)); + last = first + XVECLEN (PATTERN (insn), 0) - 1; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); + off = INTVAL (offset) - first * UNITS_PER_WORD; + + if (GET_CODE (base) != REG || off < 0) + continue; + if (first > BASE_REGISTER && first > RETURN_REGNUM) + continue; + if (last < BASE_REGISTER && last < RETURN_REGNUM) + continue; + + if (restore_first != -1) + { + new_insn = restore_gprs (base, off, restore_first, restore_last); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } + + remove_insn (insn); } - fprintf (file, ".LT%X:\n", s390_function_count); + } +} - s390_pool_count = 0; - output_constant_pool (current_function_name, current_function_decl); - s390_pool_count = -1; +/* Check whether any insn in the function makes use of the original + value of RETURN_REG (e.g. for __builtin_return_address). + If so, insert an insn reloading that value. - if (TARGET_64BIT) - function_section (current_function_decl); + Return true if any such insn was found. */ + +static bool +s390_fixup_clobbered_return_reg (return_reg) + rtx return_reg; +{ + bool replacement_done = 0; + rtx insn; + + /* If we never called __builtin_return_address, register 14 + might have been used as temp during the prolog; we do + not want to touch those uses. */ + if (!has_hard_reg_initial_val (Pmode, REGNO (return_reg))) + return false; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + rtx reg, off, new_insn; + + if (GET_CODE (insn) != INSN) + continue; + if (!reg_referenced_p (return_reg, PATTERN (insn))) + continue; + if (GET_CODE (PATTERN (insn)) == PARALLEL + && store_multiple_operation (PATTERN (insn), VOIDmode)) + continue; + + if (frame_pointer_needed) + reg = hard_frame_pointer_rtx; else - fprintf (file, ".LTN%X:\n", s390_function_count); + reg = stack_pointer_rtx; + + off = GEN_INT (cfun->machine->frame_size + REGNO (return_reg) * UNITS_PER_WORD); + if (INTVAL (off) >= 4096) + { + off = force_const_mem (Pmode, off); + new_insn = gen_rtx_SET (Pmode, return_reg, off); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + off = return_reg; + } + + new_insn = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, reg, off)); + new_insn = gen_rtx_SET (Pmode, return_reg, new_insn); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + + replacement_done = 1; } -} + return replacement_done; +} -/* Return true if floating point registers need to be saved. */ +/* Perform machine-dependent processing. */ -static int -save_fprs_p () +void +s390_machine_dependent_reorg (first) + rtx first ATTRIBUTE_UNUSED; { - int i; - if (!TARGET_64BIT) - return 0; - for (i=24; i<=31; i++) + bool fixed_up_clobbered_return_reg = 0; + rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + bool temp_used = 0; + + /* Make sure all splits have been performed; splits after + machine_dependent_reorg might confuse insn length counts. */ + split_all_insns_noflow (); + + + /* There are two problematic situations we need to correct: + + - the literal pool might be > 4096 bytes in size, so that + some of its elements cannot be directly accessed + + - a branch target might be > 64K away from the branch, so that + it is not possible to use a PC-relative instruction. + + To fix those, we split the single literal pool into multiple + pool chunks, reloading the pool base register at various + points throughout the function to ensure it always points to + the pool chunk the following code expects, and / or replace + PC-relative branches by absolute branches. + + However, the two problems are interdependent: splitting the + literal pool can move a branch further away from its target, + causing the 64K limit to overflow, and on the other hand, + replacing a PC-relative branch by an absolute branch means + we need to put the branch target address into the literal + pool, possibly causing it to overflow. + + So, we loop trying to fix up both problems until we manage + to satisfy both conditions at the same time. Note that the + loop is guaranteed to terminate as every pass of the loop + strictly decreases the total number of PC-relative branches + in the function. (This is not completely true as there + might be branch-over-pool insns introduced by chunkify_start. + Those never need to be split however.) */ + + for (;;) { - if (regs_ever_live[i] == 1) - return 1; + struct constant_pool *pool_list; + + /* Try to chunkify the literal pool. */ + pool_list = s390_chunkify_start (temp_reg, &temp_used); + + /* Split out-of-range branches. If this has created new + literal pool entries, cancel current chunk list and + recompute it. */ + if (s390_split_branches (temp_reg, &temp_used)) + { + if (pool_list) + s390_chunkify_cancel (pool_list); + + continue; + } + + /* Check whether we have clobbered a use of the return + register (e.g. for __builtin_return_address). If so, + add insns reloading the register where necessary. */ + if (temp_used && !fixed_up_clobbered_return_reg + && s390_fixup_clobbered_return_reg (temp_reg)) + { + fixed_up_clobbered_return_reg = 1; + + /* The fixup insns might have caused a jump to overflow. */ + if (pool_list) + s390_chunkify_cancel (pool_list); + + continue; + } + + /* If we made it up to here, both conditions are satisfied. + Finish up pool chunkification if required. */ + if (pool_list) + s390_chunkify_finish (pool_list, temp_reg); + + break; } - return 0; + + s390_optimize_prolog (temp_used? RETURN_REGNUM : -1); } + +/* Return an RTL expression representing the value of the return address + for the frame COUNT steps up from the current frame. FRAME is the + frame pointer of that frame. */ + +rtx +s390_return_addr_rtx (count, frame) + int count; + rtx frame; +{ + rtx addr; + + /* For the current frame, we use the initial value of RETURN_REGNUM. + This works both in leaf and non-leaf functions. */ + + if (count == 0) + return get_hard_reg_initial_val (Pmode, RETURN_REGNUM); + + /* For frames farther back, we read the stack slot where the + corresponding RETURN_REGNUM value was saved. */ + + addr = plus_constant (frame, RETURN_REGNUM * UNITS_PER_WORD); + addr = memory_address (Pmode, addr); + return gen_rtx_MEM (Pmode, addr); +} + /* Find first call clobbered register unsused in a function. This could be used as base register in a leaf function or for holding the return address before epilogue. */ @@ -3173,9 +4951,9 @@ find_unused_clobbered_reg () /* Fill FRAME with info about frame of current function. */ static void -s390_frame_info (frame) - struct s390_frame *frame; +s390_frame_info () { + char gprs_ever_live[16]; int i, j; HOST_WIDE_INT fsize = get_frame_size (); @@ -3183,79 +4961,59 @@ s390_frame_info (frame) fatal_error ("Total size of local variables exceeds architecture limit."); /* fprs 8 - 15 are caller saved for 64 Bit ABI. */ - frame->save_fprs_p = save_fprs_p (); + cfun->machine->save_fprs_p = 0; + if (TARGET_64BIT) + for (i = 24; i < 32; i++) + if (regs_ever_live[i] && !global_regs[i]) + { + cfun->machine->save_fprs_p = 1; + break; + } - frame->frame_size = fsize + frame->save_fprs_p * 64; + cfun->machine->frame_size = fsize + cfun->machine->save_fprs_p * 64; /* Does function need to setup frame and save area. */ if (! current_function_is_leaf - || frame->frame_size > 0 + || cfun->machine->frame_size > 0 || current_function_calls_alloca - || current_function_stdarg - || current_function_varargs) - frame->frame_size += STARTING_FRAME_OFFSET; - - /* If we need to allocate a frame, the stack pointer is changed. */ + || current_function_stdarg) + cfun->machine->frame_size += STARTING_FRAME_OFFSET; - if (frame->frame_size > 0) - regs_ever_live[STACK_POINTER_REGNUM] = 1; + /* Find first and last gpr to be saved. Note that at this point, + we assume the return register and the base register always + need to be saved. This is done because the usage of these + register might change even after the prolog was emitted. + If it turns out later that we really don't need them, the + prolog/epilog code is modified again. */ - /* If the literal pool might overflow, the return register might - be used as temp literal pointer. */ - - if (!TARGET_64BIT && get_pool_size () >= S390_POOL_CHUNK_MAX / 2) - regs_ever_live[RETURN_REGNUM] = 1; + for (i = 0; i < 16; i++) + gprs_ever_live[i] = regs_ever_live[i] && !global_regs[i]; - /* If there is (possibly) any pool entry, we need to - load base register. */ - - if (get_pool_size () - || !CONST_OK_FOR_LETTER_P (frame->frame_size, 'K') - || (!TARGET_64BIT && current_function_uses_pic_offset_table)) - regs_ever_live[BASE_REGISTER] = 1; - - /* If we need the GOT pointer, remember to save/restore it. */ - - if (current_function_uses_pic_offset_table) - regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1; - - /* Frame pointer needed. */ - - frame->frame_pointer_p = frame_pointer_needed; - - /* Find first and last gpr to be saved. */ + if (flag_pic) + gprs_ever_live[PIC_OFFSET_TABLE_REGNUM] = + regs_ever_live[PIC_OFFSET_TABLE_REGNUM]; + gprs_ever_live[BASE_REGISTER] = 1; + gprs_ever_live[RETURN_REGNUM] = 1; + gprs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0; for (i = 6; i < 16; i++) - if (regs_ever_live[i]) + if (gprs_ever_live[i]) break; for (j = 15; j > i; j--) - if (regs_ever_live[j]) + if (gprs_ever_live[j]) break; - - if (i == 16) - { - /* Nothing to save / restore. */ - frame->first_save_gpr = -1; - frame->first_restore_gpr = -1; - frame->last_save_gpr = -1; - frame->return_reg_saved_p = 0; - } - else - { - /* Save / Restore from gpr i to j. */ - frame->first_save_gpr = i; - frame->first_restore_gpr = i; - frame->last_save_gpr = j; - frame->return_reg_saved_p = (j >= RETURN_REGNUM && i <= RETURN_REGNUM); - } - if (current_function_stdarg || current_function_varargs) - { - /* Varargs function need to save from gpr 2 to gpr 15. */ - frame->first_save_gpr = 2; - } + + /* Save / Restore from gpr i to j. */ + cfun->machine->first_save_gpr = i; + cfun->machine->first_restore_gpr = i; + cfun->machine->last_save_gpr = j; + + /* Varargs functions need to save gprs 2 to 6. */ + if (current_function_stdarg) + cfun->machine->first_save_gpr = 2; } /* Return offset between argument pointer and frame pointer @@ -3264,13 +5022,29 @@ s390_frame_info (frame) int s390_arg_frame_offset () { - struct s390_frame frame; + HOST_WIDE_INT fsize = get_frame_size (); + int save_fprs_p, i; - /* Compute frame_info. */ + /* fprs 8 - 15 are caller saved for 64 Bit ABI. */ + save_fprs_p = 0; + if (TARGET_64BIT) + for (i = 24; i < 32; i++) + if (regs_ever_live[i] && !global_regs[i]) + { + save_fprs_p = 1; + break; + } - s390_frame_info (&frame); + fsize = fsize + save_fprs_p * 64; - return frame.frame_size + STACK_POINTER_OFFSET; + /* Does function need to setup frame and save area. */ + + if (! current_function_is_leaf + || fsize > 0 + || current_function_calls_alloca + || current_function_stdarg) + fsize += STARTING_FRAME_OFFSET; + return fsize + STACK_POINTER_OFFSET; } /* Emit insn to save fpr REGNUM at offset OFFSET relative @@ -3305,30 +5079,118 @@ restore_fpr (base, offset, regnum) return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr); } -/* Output the function prologue assembly code to the - stdio stream FILE. The local frame size is passed - in LSIZE. */ +/* Generate insn to save registers FIRST to LAST into + the register save area located at offset OFFSET + relative to register BASE. */ -void -s390_function_prologue (file, lsize) - FILE *file ATTRIBUTE_UNUSED; - HOST_WIDE_INT lsize ATTRIBUTE_UNUSED; +static rtx +save_gprs (base, offset, first, last) + rtx base; + int offset; + int first; + int last; { - s390_chunkify_pool (); - s390_split_branches (); + rtx addr, insn, note; + int i; + + addr = plus_constant (base, offset + first * UNITS_PER_WORD); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, s390_sr_alias_set); + + /* Special-case single register. */ + if (first == last) + { + if (TARGET_64BIT) + insn = gen_movdi (addr, gen_rtx_REG (Pmode, first)); + else + insn = gen_movsi (addr, gen_rtx_REG (Pmode, first)); + + RTX_FRAME_RELATED_P (insn) = 1; + return insn; + } + + + insn = gen_store_multiple (addr, + gen_rtx_REG (Pmode, first), + GEN_INT (last - first + 1)); + + + /* We need to set the FRAME_RELATED flag on all SETs + inside the store-multiple pattern. + + However, we must not emit DWARF records for registers 2..5 + if they are stored for use by variable arguments ... + + ??? Unfortunately, it is not enough to simply not the the + FRAME_RELATED flags for those SETs, because the first SET + of the PARALLEL is always treated as if it had the flag + set, even if it does not. Therefore we emit a new pattern + without those registers as REG_FRAME_RELATED_EXPR note. */ + + if (first >= 6) + { + rtx pat = PATTERN (insn); + + for (i = 0; i < XVECLEN (pat, 0); i++) + if (GET_CODE (XVECEXP (pat, 0, i)) == SET) + RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (last >= 6) + { + addr = plus_constant (base, offset + 6 * UNITS_PER_WORD); + note = gen_store_multiple (gen_rtx_MEM (Pmode, addr), + gen_rtx_REG (Pmode, 6), + GEN_INT (last - 6 + 1)); + note = PATTERN (note); + + REG_NOTES (insn) = + gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, + note, REG_NOTES (insn)); + + for (i = 0; i < XVECLEN (note, 0); i++) + if (GET_CODE (XVECEXP (note, 0, i)) == SET) + RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; + } + + return insn; } -/* Output the function epilogue assembly code to the - stdio stream FILE. The local frame size is passed - in LSIZE. */ +/* Generate insn to restore registers FIRST to LAST from + the register save area located at offset OFFSET + relative to register BASE. */ -void -s390_function_epilogue (file, lsize) - FILE *file ATTRIBUTE_UNUSED; - HOST_WIDE_INT lsize ATTRIBUTE_UNUSED; +static rtx +restore_gprs (base, offset, first, last) + rtx base; + int offset; + int first; + int last; { - current_function_uses_pic_offset_table = 0; - s390_function_count++; + rtx addr, insn; + + addr = plus_constant (base, offset + first * UNITS_PER_WORD); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, s390_sr_alias_set); + + /* Special-case single register. */ + if (first == last) + { + if (TARGET_64BIT) + insn = gen_movdi (gen_rtx_REG (Pmode, first), addr); + else + insn = gen_movsi (gen_rtx_REG (Pmode, first), addr); + + return insn; + } + + insn = gen_load_multiple (gen_rtx_REG (Pmode, first), + addr, + GEN_INT (last - first + 1)); + return insn; } /* Expand the prologue into a bunch of separate insns. */ @@ -3336,18 +5198,18 @@ s390_function_epilogue (file, lsize) void s390_emit_prologue () { - struct s390_frame frame; rtx insn, addr; rtx temp_reg; + rtx pool_start_label, pool_end_label; int i; /* Compute frame_info. */ - s390_frame_info (&frame); + s390_frame_info (); /* Choose best register to use for temp use within prologue. */ - if (frame.return_reg_saved_p + if (!current_function_is_leaf && !has_hard_reg_initial_val (Pmode, RETURN_REGNUM) && get_pool_size () < S390_POOL_CHUNK_MAX / 2) temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); @@ -3356,75 +5218,26 @@ s390_emit_prologue () /* Save call saved gprs. */ - if (frame.first_save_gpr != -1) - { - addr = plus_constant (stack_pointer_rtx, - frame.first_save_gpr * UNITS_PER_WORD); - addr = gen_rtx_MEM (Pmode, addr); - set_mem_alias_set (addr, s390_sr_alias_set); - - if (frame.first_save_gpr != frame.last_save_gpr ) - { - insn = emit_insn (gen_store_multiple (addr, - gen_rtx_REG (Pmode, frame.first_save_gpr), - GEN_INT (frame.last_save_gpr - - frame.first_save_gpr + 1))); - - /* We need to set the FRAME_RELATED flag on all SETs - inside the store-multiple pattern. + insn = save_gprs (stack_pointer_rtx, 0, + cfun->machine->first_save_gpr, cfun->machine->last_save_gpr); + emit_insn (insn); - However, we must not emit DWARF records for registers 2..5 - if they are stored for use by variable arguments ... + /* Dump constant pool and set constant pool register. */ - ??? Unfortunately, it is not enough to simply not the the - FRAME_RELATED flags for those SETs, because the first SET - of the PARALLEL is always treated as if it had the flag - set, even if it does not. Therefore we emit a new pattern - without those registers as REG_FRAME_RELATED_EXPR note. */ - - if (frame.first_save_gpr >= 6) - { - rtx pat = PATTERN (insn); - - for (i = 0; i < XVECLEN (pat, 0); i++) - if (GET_CODE (XVECEXP (pat, 0, i)) == SET) - RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; - - RTX_FRAME_RELATED_P (insn) = 1; - } - else if (frame.last_save_gpr >= 6) - { - rtx note, naddr; - naddr = plus_constant (stack_pointer_rtx, 6 * UNITS_PER_WORD); - note = gen_store_multiple (gen_rtx_MEM (Pmode, naddr), - gen_rtx_REG (Pmode, 6), - GEN_INT (frame.last_save_gpr - 6 + 1)); - REG_NOTES (insn) = - gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, - note, REG_NOTES (insn)); - - for (i = 0; i < XVECLEN (note, 0); i++) - if (GET_CODE (XVECEXP (note, 0, i)) == SET) - RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; - - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - insn = emit_move_insn (addr, - gen_rtx_REG (Pmode, frame.first_save_gpr)); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - /* Dump constant pool and set constant pool register (13). */ - - insn = emit_insn (gen_lit ()); + pool_start_label = gen_label_rtx(); + pool_end_label = gen_label_rtx(); + cfun->machine->literal_pool_label = pool_start_label; + + if (TARGET_64BIT) + insn = emit_insn (gen_literal_pool_64 (gen_rtx_REG (Pmode, BASE_REGISTER), + pool_start_label, pool_end_label)); + else + insn = emit_insn (gen_literal_pool_31 (gen_rtx_REG (Pmode, BASE_REGISTER), + pool_start_label, pool_end_label)); /* Save fprs for variable args. */ - if (current_function_stdarg || current_function_varargs) + if (current_function_stdarg) { /* Save fpr 0 and 2. */ @@ -3445,12 +5258,12 @@ s390_emit_prologue () if (!TARGET_64BIT) { /* Save fpr 4 and 6. */ - if (regs_ever_live[18]) + if (regs_ever_live[18] && !global_regs[18]) { insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18); RTX_FRAME_RELATED_P (insn) = 1; } - if (regs_ever_live[19]) + if (regs_ever_live[19] && !global_regs[19]) { insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19); RTX_FRAME_RELATED_P (insn) = 1; @@ -3459,21 +5272,21 @@ s390_emit_prologue () /* Decrement stack pointer. */ - if (frame.frame_size > 0) + if (cfun->machine->frame_size > 0) { - rtx frame_off = GEN_INT (-frame.frame_size); + rtx frame_off = GEN_INT (-cfun->machine->frame_size); /* Save incoming stack pointer into temp reg. */ - if (TARGET_BACKCHAIN || frame.save_fprs_p) + if (TARGET_BACKCHAIN || cfun->machine->save_fprs_p) { insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx)); } /* Substract frame size from stack pointer. */ - frame_off = GEN_INT (-frame.frame_size); - if (!CONST_OK_FOR_LETTER_P (-frame.frame_size, 'K')) + frame_off = GEN_INT (-cfun->machine->frame_size); + if (!CONST_OK_FOR_LETTER_P (-cfun->machine->frame_size, 'K')) frame_off = force_const_mem (Pmode, frame_off); insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off)); @@ -3482,7 +5295,7 @@ s390_emit_prologue () gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, gen_rtx_SET (VOIDmode, stack_pointer_rtx, gen_rtx_PLUS (Pmode, stack_pointer_rtx, - GEN_INT (-frame.frame_size))), + GEN_INT (-cfun->machine->frame_size))), REG_NOTES (insn)); /* Set backchain. */ @@ -3493,19 +5306,29 @@ s390_emit_prologue () set_mem_alias_set (addr, s390_sr_alias_set); insn = emit_insn (gen_move_insn (addr, temp_reg)); } + + /* If we support asynchronous exceptions (e.g. for Java), + we need to make sure the backchain pointer is set up + before any possibly trapping memory access. */ + + if (TARGET_BACKCHAIN && flag_non_call_exceptions) + { + addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)); + emit_insn (gen_rtx_CLOBBER (VOIDmode, addr)); + } } /* Save fprs 8 - 15 (64 bit ABI). */ - if (frame.save_fprs_p) + if (cfun->machine->save_fprs_p) { insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT(-64))); for (i = 24; i < 32; i++) - if (regs_ever_live[i]) + if (regs_ever_live[i] && !global_regs[i]) { rtx addr = plus_constant (stack_pointer_rtx, - frame.frame_size - 64 + (i-24)*8); + cfun->machine->frame_size - 64 + (i-24)*8); insn = save_fpr (temp_reg, (i-24)*8, i); RTX_FRAME_RELATED_P (insn) = 1; @@ -3520,7 +5343,7 @@ s390_emit_prologue () /* Set frame pointer, if needed. */ - if (frame.frame_pointer_p) + if (frame_pointer_needed) { insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); RTX_FRAME_RELATED_P (insn) = 1; @@ -3528,7 +5351,7 @@ s390_emit_prologue () /* Set up got pointer, if needed. */ - if (current_function_uses_pic_offset_table) + if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) { rtx got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); SYMBOL_REF_FLAG (got_symbol) = 1; @@ -3553,8 +5376,10 @@ s390_emit_prologue () REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, REG_NOTES (insn)); - insn = emit_insn (gen_add2_insn (pic_offset_table_rtx, - gen_rtx_REG (Pmode, BASE_REGISTER))); + got_symbol = gen_rtx_REG (Pmode, BASE_REGISTER); + got_symbol = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol), 101); + got_symbol = gen_rtx_PLUS (Pmode, got_symbol, pic_offset_table_rtx); + insn = emit_move_insn (pic_offset_table_rtx, got_symbol); REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, REG_NOTES (insn)); } @@ -3566,26 +5391,21 @@ s390_emit_prologue () void s390_emit_epilogue () { - struct s390_frame frame; rtx frame_pointer, return_reg; - int area_bottom, area_top, offset; + int area_bottom, area_top, offset = 0; rtvec p; - /* Compute frame_info. */ - - s390_frame_info (&frame); - /* Check whether to use frame or stack pointer for restore. */ - frame_pointer = frame.frame_pointer_p ? + frame_pointer = frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx; /* Compute which parts of the save area we need to access. */ - if (frame.first_restore_gpr != -1) + if (cfun->machine->first_restore_gpr != -1) { - area_bottom = frame.first_restore_gpr * UNITS_PER_WORD; - area_top = (frame.last_save_gpr + 1) * UNITS_PER_WORD; + area_bottom = cfun->machine->first_restore_gpr * UNITS_PER_WORD; + area_top = (cfun->machine->last_save_gpr + 1) * UNITS_PER_WORD; } else { @@ -3595,7 +5415,7 @@ s390_emit_epilogue () if (TARGET_64BIT) { - if (frame.save_fprs_p) + if (cfun->machine->save_fprs_p) { if (area_bottom > -64) area_bottom = -64; @@ -3605,14 +5425,14 @@ s390_emit_epilogue () } else { - if (regs_ever_live[18]) + if (regs_ever_live[18] && !global_regs[18]) { if (area_bottom > STACK_POINTER_OFFSET - 16) area_bottom = STACK_POINTER_OFFSET - 16; if (area_top < STACK_POINTER_OFFSET - 8) area_top = STACK_POINTER_OFFSET - 8; } - if (regs_ever_live[19]) + if (regs_ever_live[19] && !global_regs[19]) { if (area_bottom > STACK_POINTER_OFFSET - 8) area_bottom = STACK_POINTER_OFFSET - 8; @@ -3628,18 +5448,18 @@ s390_emit_epilogue () { /* Nothing to restore. */ } - else if (frame.frame_size + area_bottom >= 0 - && frame.frame_size + area_top <= 4096) + else if (cfun->machine->frame_size + area_bottom >= 0 + && cfun->machine->frame_size + area_top <= 4096) { /* Area is in range. */ - offset = frame.frame_size; + offset = cfun->machine->frame_size; } else { rtx insn, frame_off; offset = area_bottom < 0 ? -area_bottom : 0; - frame_off = GEN_INT (frame.frame_size - offset); + frame_off = GEN_INT (cfun->machine->frame_size - offset); if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K')) frame_off = force_const_mem (Pmode, frame_off); @@ -3653,7 +5473,7 @@ s390_emit_epilogue () { int i; - if (frame.save_fprs_p) + if (cfun->machine->save_fprs_p) for (i = 24; i < 32; i++) if (regs_ever_live[i] && !global_regs[i]) restore_fpr (frame_pointer, @@ -3673,16 +5493,16 @@ s390_emit_epilogue () /* Restore call saved gprs. */ - if (frame.first_restore_gpr != -1) + if (cfun->machine->first_restore_gpr != -1) { - rtx addr; + rtx insn, addr; int i; /* Check for global register and save them to stack location from where they get restored. */ - for (i = frame.first_restore_gpr; - i <= frame.last_save_gpr; + for (i = cfun->machine->first_restore_gpr; + i <= cfun->machine->last_save_gpr; i++) { /* These registers are special and need to be @@ -3690,7 +5510,7 @@ s390_emit_epilogue () if (i == STACK_POINTER_REGNUM || i == RETURN_REGNUM || i == BASE_REGISTER - || (flag_pic && i == PIC_OFFSET_TABLE_REGNUM)) + || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM)) continue; if (global_regs[i]) @@ -3706,8 +5526,7 @@ s390_emit_epilogue () /* Fetch return address from stack before load multiple, this will do good for scheduling. */ - if (frame.last_save_gpr >= RETURN_REGNUM - && frame.first_restore_gpr < RETURN_REGNUM) + if (!current_function_is_leaf) { int return_regnum = find_unused_clobbered_reg(); if (!return_regnum) @@ -3727,23 +5546,10 @@ s390_emit_epilogue () emit_insn (gen_blockage()); - addr = plus_constant (frame_pointer, - offset + frame.first_restore_gpr * UNITS_PER_WORD); - addr = gen_rtx_MEM (Pmode, addr); - set_mem_alias_set (addr, s390_sr_alias_set); - - if (frame.first_restore_gpr != frame.last_save_gpr) - { - emit_insn (gen_load_multiple ( - gen_rtx_REG (Pmode, frame.first_restore_gpr), - addr, - GEN_INT (frame.last_save_gpr - frame.first_restore_gpr + 1))); - } - else - { - emit_move_insn (gen_rtx_REG (Pmode, frame.first_restore_gpr), - addr); - } + insn = restore_gprs (frame_pointer, offset, + cfun->machine->first_restore_gpr, + cfun->machine->last_save_gpr); + emit_insn (insn); } /* Return to caller. */ @@ -3906,7 +5712,7 @@ s390_build_va_list () { tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl; - record = make_lang_type (RECORD_TYPE); + record = (*lang_hooks.types.make_type) (RECORD_TYPE); type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); @@ -3939,9 +5745,8 @@ s390_build_va_list () } /* Implement va_start by filling the va_list structure VALIST. - STDARG_P is true if implementing __builtin_stdarg_va_start, - false if implementing __builtin_varargs_va_start. NEXTARG - points to the first anonymous stack argument. + STDARG_P is always true, and ignored. + NEXTARG points to the first anonymous stack argument. The following global variables are used to initialize the va_list structure: @@ -3953,8 +5758,7 @@ s390_build_va_list () (relative to the virtual arg pointer). */ void -s390_va_start (stdarg_p, valist, nextarg) - int stdarg_p; +s390_va_start (valist, nextarg) tree valist; rtx nextarg ATTRIBUTE_UNUSED; { @@ -3992,8 +5796,6 @@ s390_va_start (stdarg_p, valist, nextarg) off = INTVAL (current_function_arg_offset_rtx); off = off < 0 ? 0 : off; - if (! stdarg_p) - off = off > 0 ? off - UNITS_PER_WORD : off; if (TARGET_DEBUG_ARG) fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n", (int)n_gpr, (int)n_fpr, off); @@ -4200,6 +6002,134 @@ s390_va_arg (valist, type) } +/* Builtins. */ + +enum s390_builtin +{ + S390_BUILTIN_THREAD_POINTER, + S390_BUILTIN_SET_THREAD_POINTER, + + S390_BUILTIN_max +}; + +static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = { + CODE_FOR_get_tp_64, + CODE_FOR_set_tp_64 +}; + +static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = { + CODE_FOR_get_tp_31, + CODE_FOR_set_tp_31 +}; + +static void +s390_init_builtins () +{ + tree ftype; + + ftype = build_function_type (ptr_type_node, void_list_node); + builtin_function ("__builtin_thread_pointer", ftype, + S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + builtin_function ("__builtin_set_thread_pointer", ftype, + S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +static rtx +s390_expand_builtin (exp, target, subtarget, mode, ignore) + tree exp; + rtx target; + rtx subtarget ATTRIBUTE_UNUSED; + enum machine_mode mode ATTRIBUTE_UNUSED; + int ignore ATTRIBUTE_UNUSED; +{ +#define MAX_ARGS 2 + + unsigned int const *code_for_builtin = + TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31; + + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + tree arglist = TREE_OPERAND (exp, 1); + enum insn_code icode; + rtx op[MAX_ARGS], pat; + int arity; + bool nonvoid; + + if (fcode >= S390_BUILTIN_max) + internal_error ("bad builtin fcode"); + icode = code_for_builtin[fcode]; + if (icode == 0) + internal_error ("bad builtin fcode"); + + nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; + + for (arglist = TREE_OPERAND (exp, 1), arity = 0; + arglist; + arglist = TREE_CHAIN (arglist), arity++) + { + const struct insn_operand_data *insn_op; + + tree arg = TREE_VALUE (arglist); + if (arg == error_mark_node) + return NULL_RTX; + if (arity > MAX_ARGS) + return NULL_RTX; + + insn_op = &insn_data[icode].operand[arity + nonvoid]; + + op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0); + + if (!(*insn_op->predicate) (op[arity], insn_op->mode)) + op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); + } + + if (nonvoid) + { + enum machine_mode tmode = insn_data[icode].operand[0].mode; + if (!target + || GET_MODE (target) != tmode + || !(*insn_data[icode].operand[0].predicate) (target, tmode)) + target = gen_reg_rtx (tmode); + } + + switch (arity) + { + case 0: + pat = GEN_FCN (icode) (target); + break; + case 1: + if (nonvoid) + pat = GEN_FCN (icode) (target, op[0]); + else + pat = GEN_FCN (icode) (op[0]); + break; + case 2: + pat = GEN_FCN (icode) (target, op[0], op[1]); + break; + default: + abort (); + } + if (!pat) + return NULL_RTX; + emit_insn (pat); + + if (nonvoid) + return target; + else + return const0_rtx; +} + + /* Output assembly code for the trampoline template to stdio stream FILE. @@ -4285,7 +6215,7 @@ s390_function_profiler (file, labelno) rtx op[7]; char label[128]; - sprintf (label, "%sP%d", LPREFIX, labelno); + ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); fprintf (file, "# function profiler \n"); @@ -4344,3 +6274,353 @@ s390_function_profiler (file, labelno) } } +/* Select section for constant in constant pool. In 32-bit mode, + constants go in the function section; in 64-bit mode in .rodata. */ + +static void +s390_select_rtx_section (mode, x, align) + enum machine_mode mode ATTRIBUTE_UNUSED; + rtx x ATTRIBUTE_UNUSED; + unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED; +{ + if (TARGET_64BIT) + readonly_data_section (); + else + function_section (current_function_decl); +} + +/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF + into its name and SYMBOL_REF_FLAG. */ + +static void +s390_encode_section_info (decl, first) + tree decl; + int first ATTRIBUTE_UNUSED; +{ + bool local_p = (*targetm.binds_local_p) (decl); + rtx rtl, symbol; + + rtl = DECL_P (decl) ? DECL_RTL (decl) : TREE_CST_RTL (decl); + if (GET_CODE (rtl) != MEM) + return; + symbol = XEXP (rtl, 0); + if (GET_CODE (symbol) != SYMBOL_REF) + return; + + /* When using PIC, SYMBOL_REF_FLAG marks non-global symbols + that can be accessed directly. */ + if (flag_pic) + SYMBOL_REF_FLAG (symbol) = local_p; + + /* Encode thread-local data with %[GLil] for "global dynamic", + "local dynamic", "initial exec" or "local exec" TLS models, + respectively. */ + + if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl)) + { + const char *symbol_str = XSTR (symbol, 0); + char *newstr; + size_t len; + enum tls_model kind = decl_tls_model (decl); + + if (!flag_pic) + { + /* We don't allow non-pic code for shared libraries, + so don't generate GD/LD TLS models for non-pic code. */ + switch (kind) + { + case TLS_MODEL_GLOBAL_DYNAMIC: + kind = TLS_MODEL_INITIAL_EXEC; break; + case TLS_MODEL_LOCAL_DYNAMIC: + kind = TLS_MODEL_LOCAL_EXEC; break; + default: + break; + } + } + + if (symbol_str[0] == '%') + { + if (symbol_str[1] == tls_model_chars[kind]) + return; + symbol_str += 2; + } + len = strlen (symbol_str) + 1; + newstr = alloca (len + 2); + + newstr[0] = '%'; + newstr[1] = tls_model_chars[kind]; + memcpy (newstr + 2, symbol_str, len); + + XSTR (symbol, 0) = ggc_alloc_string (newstr, len + 2 - 1); + } + + /* If a variable has a forced alignment to < 2 bytes, mark it + with '@' to prevent it from being used as LARL operand. */ + + else if (TREE_CODE (decl) == VAR_DECL + && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16 + && XSTR (symbol, 0)[0] != '@') + { + const char *symbol_str = XSTR (symbol, 0); + size_t len = strlen (symbol_str) + 1; + char *newstr = alloca (len + 1); + + newstr[0] = '@'; + memcpy (newstr + 1, symbol_str, len); + + XSTR (symbol, 0) = ggc_alloc_string (newstr, len + 1 - 1); + } +} + +/* Undo the above when printing symbol names. */ + +static const char * +s390_strip_name_encoding (str) + const char *str; +{ + if (str[0] == '%') + str += 2; + if (str[0] == '@') + str += 1; + if (str[0] == '*') + str += 1; + return str; +} + +/* Output thunk to FILE that implements a C++ virtual function call (with + multiple inheritance) to FUNCTION. The thunk adjusts the this pointer + by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment + stored at VCALL_OFFSET in the vtable whose address is located at offset 0 + relative to the resulting this pointer. */ + +static void +s390_output_mi_thunk (file, thunk, delta, vcall_offset, function) + FILE *file; + tree thunk ATTRIBUTE_UNUSED; + HOST_WIDE_INT delta; + HOST_WIDE_INT vcall_offset; + tree function; +{ + rtx op[10]; + int nonlocal = 0; + + /* Operand 0 is the target function. */ + op[0] = XEXP (DECL_RTL (function), 0); + if (flag_pic && !SYMBOL_REF_FLAG (op[0])) + { + nonlocal = 1; + op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), + TARGET_64BIT ? 113 : flag_pic == 2 ? 112 : 110); + op[0] = gen_rtx_CONST (Pmode, op[0]); + } + + /* Operand 1 is the 'this' pointer. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)))) + op[1] = gen_rtx_REG (Pmode, 3); + else + op[1] = gen_rtx_REG (Pmode, 2); + + /* Operand 2 is the delta. */ + op[2] = GEN_INT (delta); + + /* Operand 3 is the vcall_offset. */ + op[3] = GEN_INT (vcall_offset); + + /* Operand 4 is the temporary register. */ + op[4] = gen_rtx_REG (Pmode, 1); + + /* Operands 5 to 8 can be used as labels. */ + op[5] = NULL_RTX; + op[6] = NULL_RTX; + op[7] = NULL_RTX; + op[8] = NULL_RTX; + + /* Operand 9 can be used for temporary register. */ + op[9] = NULL_RTX; + + /* Generate code. */ + if (TARGET_64BIT) + { + /* Setup literal pool pointer if required. */ + if (!CONST_OK_FOR_LETTER_P (delta, 'K') + || !CONST_OK_FOR_LETTER_P (vcall_offset, 'K')) + { + op[5] = gen_label_rtx (); + output_asm_insn ("larl\t%4,%5", op); + } + + /* Add DELTA to this pointer. */ + if (delta) + { + if (CONST_OK_FOR_LETTER_P (delta, 'J')) + output_asm_insn ("la\t%1,%2(%1)", op); + else if (CONST_OK_FOR_LETTER_P (delta, 'K')) + output_asm_insn ("aghi\t%1,%2", op); + else + { + op[6] = gen_label_rtx (); + output_asm_insn ("agf\t%1,%6-%5(%4)", op); + } + } + + /* Perform vcall adjustment. */ + if (vcall_offset) + { + if (CONST_OK_FOR_LETTER_P (vcall_offset, 'J')) + { + output_asm_insn ("lg\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,%3(%4)", op); + } + else if (CONST_OK_FOR_LETTER_P (vcall_offset, 'K')) + { + output_asm_insn ("lghi\t%4,%3", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + else + { + op[7] = gen_label_rtx (); + output_asm_insn ("llgf\t%4,%7-%5(%4)", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + } + + /* Jump to target. */ + output_asm_insn ("jg\t%0", op); + + /* Output literal pool if required. */ + if (op[5]) + { + output_asm_insn (".align\t4", op); + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[5])); + } + if (op[6]) + { + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6])); + output_asm_insn (".long\t%2", op); + } + if (op[7]) + { + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[7])); + output_asm_insn (".long\t%3", op); + } + } + else + { + /* Setup base pointer if required. */ + if (!vcall_offset + || !CONST_OK_FOR_LETTER_P (delta, 'K') + || !CONST_OK_FOR_LETTER_P (vcall_offset, 'K')) + { + op[5] = gen_label_rtx (); + output_asm_insn ("basr\t%4,0", op); + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[5])); + } + + /* Add DELTA to this pointer. */ + if (delta) + { + if (CONST_OK_FOR_LETTER_P (delta, 'J')) + output_asm_insn ("la\t%1,%2(%1)", op); + else if (CONST_OK_FOR_LETTER_P (delta, 'K')) + output_asm_insn ("ahi\t%1,%2", op); + else + { + op[6] = gen_label_rtx (); + output_asm_insn ("a\t%1,%6-%5(%4)", op); + } + } + + /* Perform vcall adjustment. */ + if (vcall_offset) + { + if (CONST_OK_FOR_LETTER_P (vcall_offset, 'J')) + { + output_asm_insn ("lg\t%4,0(%1)", op); + output_asm_insn ("a\t%1,%3(%4)", op); + } + else if (CONST_OK_FOR_LETTER_P (vcall_offset, 'K')) + { + output_asm_insn ("lhi\t%4,%3", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } + else + { + op[7] = gen_label_rtx (); + output_asm_insn ("l\t%4,%7-%5(%4)", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } + + /* We had to clobber the base pointer register. + Re-setup the base pointer (with a different base). */ + op[5] = gen_label_rtx (); + output_asm_insn ("basr\t%4,0", op); + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[5])); + } + + /* Jump to target. */ + op[8] = gen_label_rtx (); + + if (!flag_pic) + output_asm_insn ("l\t%4,%8-%5(%4)", op); + else if (!nonlocal) + output_asm_insn ("a\t%4,%8-%5(%4)", op); + /* We cannot call through .plt, since .plt requires %r12 loaded. */ + else if (flag_pic == 1) + { + output_asm_insn ("a\t%4,%8-%5(%4)", op); + output_asm_insn ("l\t%4,%0(%4)", op); + } + else if (flag_pic == 2) + { + op[9] = gen_rtx_REG (Pmode, 0); + output_asm_insn ("l\t%9,%8-4-%5(%4)", op); + output_asm_insn ("a\t%4,%8-%5(%4)", op); + output_asm_insn ("ar\t%4,%9", op); + output_asm_insn ("l\t%4,0(%4)", op); + } + + output_asm_insn ("br\t%4", op); + + /* Output literal pool. */ + output_asm_insn (".align\t4", op); + + if (nonlocal && flag_pic == 2) + output_asm_insn (".long\t%0", op); + if (nonlocal) + { + op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + SYMBOL_REF_FLAG (op[0]) = 1; + } + + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[8])); + if (!flag_pic) + output_asm_insn (".long\t%0", op); + else + output_asm_insn (".long\t%0-%5", op); + + if (op[6]) + { + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6])); + output_asm_insn (".long\t%2", op); + } + if (op[7]) + { + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[7])); + output_asm_insn (".long\t%3", op); + } + } +} + +/* How to allocate a 'struct machine_function'. */ + +static struct machine_function * +s390_init_machine_status () +{ + return ggc_alloc_cleared (sizeof (struct machine_function)); +} + +#include "gt-s390.h" diff --git a/contrib/gcc/config/s390/s390.h b/contrib/gcc/config/s390/s390.h index f3218e9..c3dad68 100644 --- a/contrib/gcc/config/s390/s390.h +++ b/contrib/gcc/config/s390/s390.h @@ -1,5 +1,5 @@ /* Definitions of target machine for GNU compiler, for IBM S/390 - Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Contributed by Hartmut Penner (hpenner@de.ibm.com) and Ulrich Weigand (uweigand@de.ibm.com). This file is part of GNU CC. @@ -22,13 +22,29 @@ Boston, MA 02111-1307, USA. */ #ifndef _S390_H #define _S390_H -extern int flag_pic; +/* Override the __fixdfdi etc. routines when building libgcc2. + ??? This should be done in a cleaner way ... */ +#if defined (IN_LIBGCC2) && !defined (__s390x__) +#include <s390/fixdfdi.h> +#endif -/* Run-time compilation parameters selecting different hardware subsets. */ -extern int target_flags; +/* Run-time target specification. */ -/* Target macros checked at runtime of compiler. */ +/* Target CPU builtins. */ +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_assert ("cpu=s390"); \ + builtin_assert ("machine=s390"); \ + builtin_define ("__s390__"); \ + if (TARGET_64BIT) \ + builtin_define ("__s390x__"); \ + } \ + while (0) + +/* Optional target features. */ +extern int target_flags; #define TARGET_HARD_FLOAT (target_flags & 1) #define TARGET_SOFT_FLOAT (!(target_flags & 1)) @@ -38,26 +54,22 @@ extern int target_flags; #define TARGET_64BIT (target_flags & 16) #define TARGET_MVCLE (target_flags & 32) +/* ??? Once this actually works, it could be made a runtime option. */ +#define TARGET_IBM_FLOAT 0 +#define TARGET_IEEE_FLOAT 1 + #ifdef DEFAULT_TARGET_64BIT #define TARGET_DEFAULT 0x13 -#define TARGET_VERSION fprintf (stderr, " (zSeries)"); #else #define TARGET_DEFAULT 0x3 -#define TARGET_VERSION fprintf (stderr, " (S/390)"); #endif - -/* Macro to define tables used to set the flags. This is a list in braces - of pairs in braces, each pair being { "NAME", VALUE } - where VALUE is the bits to set or minus the bits to clear. - An empty string NAME is used to identify the default VALUE. */ - #define TARGET_SWITCHES \ { { "hard-float", 1, N_("Use hardware fp")}, \ { "soft-float", -1, N_("Don't use hardware fp")}, \ { "backchain", 2, N_("Set backchain")}, \ { "no-backchain", -2, N_("Don't set backchain (faster, but debug harder")}, \ - { "small-exec", 4, N_("Use bras for execucable < 64k")}, \ + { "small-exec", 4, N_("Use bras for executable < 64k")}, \ { "no-small-exec",-4, N_("Don't use bras")}, \ { "debug", 8, N_("Additional debug prints")}, \ { "no-debug", -8, N_("Don't print additional debug prints")}, \ @@ -67,97 +79,50 @@ extern int target_flags; { "no-mvcle", -32, N_("mvc&ex")}, \ { "", TARGET_DEFAULT, 0 } } -/* Define this to change the optimizations performed by default. */ -#define OPTIMIZATION_OPTIONS(LEVEL, SIZE) optimization_options(LEVEL, SIZE) +/* Target version string. Overridden by the OS header. */ +#ifdef DEFAULT_TARGET_64BIT +#define TARGET_VERSION fprintf (stderr, " (zSeries)"); +#else +#define TARGET_VERSION fprintf (stderr, " (S/390)"); +#endif -/* Sometimes certain combinations of command options do not make sense - on a particular target machine. You can define a macro - `OVERRIDE_OPTIONS' to take account of this. This macro, if - defined, is executed once just after all the command options have - been parsed. */ +/* Hooks to override options. */ +#define OPTIMIZATION_OPTIONS(LEVEL, SIZE) optimization_options(LEVEL, SIZE) #define OVERRIDE_OPTIONS override_options () +/* Frame pointer is not used for debugging. */ +#define CAN_DEBUG_WITHOUT_FP -/* Defines for REAL_ARITHMETIC. */ -#define IEEE_FLOAT 1 -#define TARGET_IBM_FLOAT 0 -#define TARGET_IEEE_FLOAT 1 - -/* The current function count for create unique internal labels. */ - -extern int s390_function_count; -/* The amount of space used for outgoing arguments. */ +/* In libgcc2, determine target settings as compile-time constants. */ +#ifdef IN_LIBGCC2 +#undef TARGET_64BIT +#ifdef __s390x__ +#define TARGET_64BIT 1 +#else +#define TARGET_64BIT 0 +#endif +#endif -extern int current_function_outgoing_args_size; /* Target machine storage layout. */ -/* Define this if most significant bit is lowest numbered in instructions - that operate on numbered bit-fields. */ - +/* Everything is big-endian. */ #define BITS_BIG_ENDIAN 1 - -/* Define this if most significant byte of a word is the lowest numbered. */ - #define BYTES_BIG_ENDIAN 1 - -/* Define this if MS word of a multiword is the lowest numbered. */ - #define WORDS_BIG_ENDIAN 1 -/* Number of bits in an addressable storage unit. */ - -#define BITS_PER_UNIT 8 - -/* Width in bits of a "word", which is the contents of a machine register. */ - -#define BITS_PER_WORD (TARGET_64BIT ? 64 : 32) -#define MAX_BITS_PER_WORD 64 - /* Width of a word, in units (bytes). */ - #define UNITS_PER_WORD (TARGET_64BIT ? 8 : 4) +#ifndef IN_LIBGCC2 #define MIN_UNITS_PER_WORD 4 +#endif +#define MAX_BITS_PER_WORD 64 -/* Width in bits of a pointer. See also the macro `Pmode' defined below. */ - -#define POINTER_SIZE (TARGET_64BIT ? 64 : 32) - -/* A C expression for the size in bits of the type `short' on the - target machine. If you don't define this, the default is half a - word. (If this would be less than one storage unit, it is - rounded up to one unit.) */ -#define SHORT_TYPE_SIZE 16 - -/* A C expression for the size in bits of the type `int' on the - target machine. If you don't define this, the default is one - word. */ -#define INT_TYPE_SIZE 32 - -/* A C expression for the size in bits of the type `long' on the - target machine. If you don't define this, the default is one - word. */ -#define LONG_TYPE_SIZE (TARGET_64BIT ? 64 : 32) -#define MAX_LONG_TYPE_SIZE 64 - -/* A C expression for the size in bits of the type `long long' on the - target machine. If you don't define this, the default is two - words. */ -#define LONG_LONG_TYPE_SIZE 64 - -/* Right now we only support two floating point formats, the - 32 and 64 bit ieee formats. */ - -#define FLOAT_TYPE_SIZE 32 -#define DOUBLE_TYPE_SIZE 64 -#define LONG_DOUBLE_TYPE_SIZE 64 - -/* Define this macro if it is advisable to hold scalars in registers - in a wider mode than that declared by the program. In such cases, - the value is constrained to be within the bounds of the declared - type, but kept valid in the wider mode. The signedness of the - extension may differ from that of the type. */ +/* Function arguments and return values are promoted to word size. */ +#define PROMOTE_FUNCTION_ARGS +#define PROMOTE_FUNCTION_RETURN +#define PROMOTE_FOR_CALL_ONLY #define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ if (INTEGRAL_MODE_P (MODE) && \ @@ -165,109 +130,109 @@ if (INTEGRAL_MODE_P (MODE) && \ (MODE) = Pmode; \ } -/* Defining PROMOTE_FUNCTION_ARGS eliminates some unnecessary zero/sign - extensions applied to char/short functions arguments. Defining - PROMOTE_FUNCTION_RETURN does the same for function returns. */ - -#define PROMOTE_FUNCTION_ARGS -#define PROMOTE_FUNCTION_RETURN -#define PROMOTE_FOR_CALL_ONLY - -/* Allocation boundary (in *bits*) for storing pointers in memory. */ - -#define POINTER_BOUNDARY 32 - /* Allocation boundary (in *bits*) for storing arguments in argument list. */ - #define PARM_BOUNDARY (TARGET_64BIT ? 64 : 32) /* Boundary (in *bits*) on which stack pointer should be aligned. */ - #define STACK_BOUNDARY 64 /* Allocation boundary (in *bits*) for the code of a function. */ - #define FUNCTION_BOUNDARY 32 /* There is no point aligning anything to a rounder boundary than this. */ - #define BIGGEST_ALIGNMENT 64 /* Alignment of field after `int : 0' in a structure. */ - #define EMPTY_FIELD_BOUNDARY 32 /* Alignment on even addresses for LARL instruction. */ - #define CONSTANT_ALIGNMENT(EXP, ALIGN) (ALIGN) < 16 ? 16 : (ALIGN) - #define DATA_ALIGNMENT(TYPE, ALIGN) (ALIGN) < 16 ? 16 : (ALIGN) -/* Define this if move instructions will actually fail to work when given - unaligned data. */ - +/* Alignment is not required by the hardware. */ #define STRICT_ALIGNMENT 0 -/* real arithmetic */ - -#define REAL_ARITHMETIC +/* Mode of stack savearea. + FUNCTION is VOIDmode because calling convention maintains SP. + BLOCK needs Pmode for SP. + NONLOCAL needs twice Pmode to maintain both backchain and SP. */ +#define STACK_SAVEAREA_MODE(LEVEL) \ + (LEVEL == SAVE_FUNCTION ? VOIDmode \ + : LEVEL == SAVE_NONLOCAL ? (TARGET_64BIT ? TImode : DImode) : Pmode) /* Define target floating point format. */ +#define TARGET_FLOAT_FORMAT \ + (TARGET_IEEE_FLOAT? IEEE_FLOAT_FORMAT : IBM_FLOAT_FORMAT) -#undef TARGET_FLOAT_FORMAT -#ifdef IEEE_FLOAT -#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT -#else -#define TARGET_FLOAT_FORMAT IBM_FLOAT_FORMAT -#endif -/* Define if special allocation order desired. */ +/* Type layout. */ -#define REG_ALLOC_ORDER \ -{ 1, 2, 3, 4, 5, 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, \ - 16, 17, 18, 19, 20, 21, 22, 23, \ - 24, 25, 26, 27, 28, 29, 30, 31, \ - 15, 32, 33, 34 } +/* Sizes in bits of the source language data types. */ +#define SHORT_TYPE_SIZE 16 +#define INT_TYPE_SIZE 32 +#define LONG_TYPE_SIZE (TARGET_64BIT ? 64 : 32) +#define MAX_LONG_TYPE_SIZE 64 +#define LONG_LONG_TYPE_SIZE 64 +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 +#define LONG_DOUBLE_TYPE_SIZE 64 /* ??? Should support extended format. */ -/* Standard register usage. */ - -#define INT_REGNO_P(N) ( (int)(N) >= 0 && (N) < 16 ) -#ifdef IEEE_FLOAT -#define FLOAT_REGNO_P(N) ( (N) >= 16 && (N) < 32 ) -#else -#define FLOAT_REGNO_P(N) ( (N) >= 16 && (N) < 20 ) -#endif -#define CC_REGNO_P(N) ( (N) == 33 ) - -/* Number of actual hardware registers. The hardware registers are - assigned numbers for the compiler from 0 to just below - FIRST_PSEUDO_REGISTER. - All registers that the compiler knows about must be given numbers, - even those that are not normally considered general registers. - For the 390, we give the data registers numbers 0-15, - and the floating point registers numbers 16-19. - G5 and following have 16 IEEE floating point register, - which get numbers 16-31. */ +/* We use "unsigned char" as default. */ +#define DEFAULT_SIGNED_CHAR 0 -#define FIRST_PSEUDO_REGISTER 35 -/* Number of hardware registers that go into the DWARF-2 unwind info. - If not defined, equals FIRST_PSEUDO_REGISTER. */ +/* Register usage. */ -#define DWARF_FRAME_REGISTERS 34 +/* We have 16 general purpose registers (registers 0-15), + and 16 floating point registers (registers 16-31). + (On non-IEEE machines, we have only 4 fp registers.) + + Amongst the general purpose registers, some are used + for specific purposes: + GPR 11: Hard frame pointer (if needed) + GPR 12: Global offset table pointer (if needed) + GPR 13: Literal pool base register + GPR 14: Return address register + GPR 15: Stack pointer + + Registers 32-34 are 'fake' hard registers that do not + correspond to actual hardware: + Reg 32: Argument pointer + Reg 33: Condition code + Reg 34: Frame pointer */ -/* The following register have a fix usage - GPR 12: GOT register points to the GOT, setup in prologue, - GOT contains pointer to variables in shared libraries - GPR 13: Base register setup in prologue to point to the - literal table of each function - GPR 14: Return registers holds the return address - GPR 15: Stack pointer */ +#define FIRST_PSEUDO_REGISTER 35 + +/* Standard register usage. */ +#define GENERAL_REGNO_P(N) ((int)(N) >= 0 && (N) < 16) +#define ADDR_REGNO_P(N) ((N) >= 1 && (N) < 16) +#define FP_REGNO_P(N) ((N) >= 16 && (N) < (TARGET_IEEE_FLOAT? 32 : 20)) +#define CC_REGNO_P(N) ((N) == 33) +#define FRAME_REGNO_P(N) ((N) == 32 || (N) == 34) + +#define GENERAL_REG_P(X) (REG_P (X) && GENERAL_REGNO_P (REGNO (X))) +#define ADDR_REG_P(X) (REG_P (X) && ADDR_REGNO_P (REGNO (X))) +#define FP_REG_P(X) (REG_P (X) && FP_REGNO_P (REGNO (X))) +#define CC_REG_P(X) (REG_P (X) && CC_REGNO_P (REGNO (X))) +#define FRAME_REG_P(X) (REG_P (X) && FRAME_REGNO_P (REGNO (X))) -#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? 12 : INVALID_REGNUM) #define BASE_REGISTER 13 #define RETURN_REGNUM 14 -#define STACK_POINTER_REGNUM 15 +#define CC_REGNUM 33 + +/* Set up fixed registers and calling convention: + + GPRs 0-5 are always call-clobbered, + GPRs 6-15 are always call-saved. + GPR 12 is fixed if used as GOT pointer. + GPR 13 is always fixed (as literal pool pointer). + GPR 14 is always fixed (as return address). + GPR 15 is always fixed (as stack pointer). + The 'fake' hard registers are call-clobbered and fixed. + + On 31-bit, FPRs 18-19 are call-clobbered; + on 64-bit, FPRs 24-31 are call-clobbered. + The remaining FPRs are call-saved. */ #define FIXED_REGISTERS \ { 0, 0, 0, 0, \ @@ -280,12 +245,6 @@ if (INTEGRAL_MODE_P (MODE) && \ 0, 0, 0, 0, \ 1, 1, 1 } -/* 1 for registers not available across function calls. These must include - the FIXED_REGISTERS and also any registers that can be used without being - saved. - The latter must include the registers where values are returned - and the register where structure-value addresses are passed. */ - #define CALL_USED_REGISTERS \ { 1, 1, 1, 1, \ 1, 1, 0, 0, \ @@ -297,10 +256,6 @@ if (INTEGRAL_MODE_P (MODE) && \ 1, 1, 1, 1, \ 1, 1, 1 } -/* Like `CALL_USED_REGISTERS' except this macro doesn't require that - the entire set of `FIXED_REGISTERS' be included. - (`CALL_USED_REGISTERS' must be a superset of `FIXED_REGISTERS'). */ - #define CALL_REALLY_USED_REGISTERS \ { 1, 1, 1, 1, \ 1, 1, 0, 0, \ @@ -312,8 +267,6 @@ if (INTEGRAL_MODE_P (MODE) && \ 1, 1, 1, 1, \ 1, 1, 1 } -/* Macro to conditionally modify fixed_regs/call_used_regs. */ - #define CONDITIONAL_REGISTER_USAGE \ do \ { \ @@ -336,160 +289,85 @@ do \ } \ } while (0) -/* The following register have a special usage - GPR 11: Frame pointer if needed to point to automatic variables. - GPR 32: In functions with more the 5 args this register - points to that arguments, it is always eliminated - with stack- or frame-pointer. - GPR 33: Condition code 'register' */ - -#define HARD_FRAME_POINTER_REGNUM 11 -#define FRAME_POINTER_REGNUM 34 - -#define ARG_POINTER_REGNUM 32 - -#define CC_REGNUM 33 - -/* We use the register %r0 to pass the static chain to a nested function. - - Note: It is assumed that this register is call-clobbered! - We can't use any of the function-argument registers either, - and register 1 is needed by the trampoline code, so we have - no other choice but using this one ... */ +/* Preferred register allocation order. */ +#define REG_ALLOC_ORDER \ +{ 1, 2, 3, 4, 5, 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, \ + 16, 17, 18, 19, 20, 21, 22, 23, \ + 24, 25, 26, 27, 28, 29, 30, 31, \ + 15, 32, 33, 34 } -#define STATIC_CHAIN_REGNUM 0 -/* Return number of consecutive hard regs needed starting at reg REGNO - to hold something of mode MODE. - This is ordinarily the length in words of a value of mode MODE - but can be less for certain modes in special long registers. */ +/* Fitting values into registers. */ + +/* Integer modes <= word size fit into any GPR. + Integer modes > word size fit into successive GPRs, starting with + an even-numbered register. + SImode and DImode fit into FPRs as well. + + Floating point modes <= word size fit into any FPR or GPR. + Floating point modes > word size (i.e. DFmode on 32-bit) fit + into any FPR, or an even-odd GPR pair. + + Complex floating point modes fit either into two FPRs, or into + successive GPRs (again starting with an even number). + + Condition code modes fit only into the CC register. */ #define HARD_REGNO_NREGS(REGNO, MODE) \ - (FLOAT_REGNO_P(REGNO)? \ + (FP_REGNO_P(REGNO)? \ (GET_MODE_CLASS(MODE) == MODE_COMPLEX_FLOAT ? 2 : 1) : \ - INT_REGNO_P(REGNO)? \ + GENERAL_REGNO_P(REGNO)? \ ((GET_MODE_SIZE(MODE)+UNITS_PER_WORD-1) / UNITS_PER_WORD) : \ 1) -/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. - The gprs can hold QI, HI, SI, SF, DF, SC and DC. - Even gprs can hold DI. - The floating point registers can hold DF, SF, DC and SC. */ - #define HARD_REGNO_MODE_OK(REGNO, MODE) \ - (FLOAT_REGNO_P(REGNO)? \ - (GET_MODE_CLASS(MODE) == MODE_FLOAT || \ - GET_MODE_CLASS(MODE) == MODE_COMPLEX_FLOAT || \ - (MODE) == SImode || (MODE) == DImode) : \ - INT_REGNO_P(REGNO)? \ + (FP_REGNO_P(REGNO)? \ + ((MODE) == SImode || (MODE) == DImode || \ + GET_MODE_CLASS(MODE) == MODE_FLOAT || \ + GET_MODE_CLASS(MODE) == MODE_COMPLEX_FLOAT) : \ + GENERAL_REGNO_P(REGNO)? \ (HARD_REGNO_NREGS(REGNO, MODE) == 1 || !((REGNO) & 1)) : \ CC_REGNO_P(REGNO)? \ GET_MODE_CLASS (MODE) == MODE_CC : \ + FRAME_REGNO_P(REGNO)? \ + (enum machine_mode) (MODE) == Pmode : \ 0) -/* Value is 1 if it is a good idea to tie two pseudo registers when one has - mode MODE1 and one has mode MODE2. - If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, - for any hard reg, then this must be 0 for correct output. */ - #define MODES_TIEABLE_P(MODE1, MODE2) \ (((MODE1) == SFmode || (MODE1) == DFmode) \ == ((MODE2) == SFmode || (MODE2) == DFmode)) -/* If defined, gives a class of registers that cannot be used as the - operand of a SUBREG that changes the mode of the object illegally. */ - -#define CLASS_CANNOT_CHANGE_MODE FP_REGS - -/* Defines illegal mode changes for CLASS_CANNOT_CHANGE_MODE. */ - -#define CLASS_CANNOT_CHANGE_MODE_P(FROM,TO) \ - (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO)) - -/* Define this macro if references to a symbol must be treated - differently depending on something about the variable or - function named by the symbol (such as what section it is in). - - On s390, if using PIC, mark a SYMBOL_REF for a non-global symbol - so that we may access it directly in the GOT. */ - -#define ENCODE_SECTION_INFO(DECL) \ -do \ - { \ - if (flag_pic) \ - { \ - rtx rtl = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \ - ? TREE_CST_RTL (DECL) : DECL_RTL (DECL)); \ - \ - if (GET_CODE (rtl) == MEM) \ - { \ - SYMBOL_REF_FLAG (XEXP (rtl, 0)) \ - = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \ - || ! TREE_PUBLIC (DECL)); \ - } \ - } \ - } \ -while (0) - - -/* This is an array of structures. Each structure initializes one pair - of eliminable registers. The "from" register number is given first, - followed by "to". Eliminations of the same "from" register are listed - in order of preference. */ - -#define ELIMINABLE_REGS \ -{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ - { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \ - { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ - { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}} - -#define CAN_ELIMINATE(FROM, TO) (1) - -#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ -{ if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \ - { (OFFSET) = 0; } \ - else if ((FROM) == FRAME_POINTER_REGNUM \ - && (TO) == HARD_FRAME_POINTER_REGNUM) \ - { (OFFSET) = 0; } \ - else if ((FROM) == ARG_POINTER_REGNUM \ - && (TO) == HARD_FRAME_POINTER_REGNUM) \ - { (OFFSET) = s390_arg_frame_offset (); } \ - else if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \ - { (OFFSET) = s390_arg_frame_offset (); } \ - else \ - abort(); \ -} - -#define CAN_DEBUG_WITHOUT_FP - -/* Value should be nonzero if functions must have frame pointers. - Zero means the frame pointer need not be set up (and parms may be - accessed via the stack pointer) in functions that seem suitable. - This is computed in `reload', in reload1.c. */ +/* Maximum number of registers to represent a value of mode MODE + in a register of class CLASS. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((CLASS) == FP_REGS ? \ + (GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT ? 2 : 1) : \ + (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) -#define FRAME_POINTER_REQUIRED 0 +/* If a 4-byte value is loaded into a FPR, it is placed into the + *upper* half of the register, not the lower. Therefore, we + cannot use SUBREGs to switch between modes in FP registers. */ +#define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \ + (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO) \ + ? reg_classes_intersect_p (FP_REGS, CLASS) : 0) -/* Define the classes of registers for register constraints in the - machine description. Also define ranges of constants. - - One of the classes must always be named ALL_REGS and include all hard regs. - If there is more than one class, another class must be named NO_REGS - and contain no registers. +/* Register classes. */ - The name GENERAL_REGS must be the name of a class (or an alias for - another name such as ALL_REGS). This is the class of registers - that is allowed by "g" or "r" in a register constraint. - Also, registers outside this class are allocated only when - instructions express preferences for them. - - The classes must be numbered in nondecreasing order; that is, - a larger-numbered class must never be contained completely - in a smaller-numbered class. - - For any two classes, it is very desirable that there be another - class that represents their union. */ - -/*#define SMALL_REGISTER_CLASSES 1*/ +/* We use the following register classes: + GENERAL_REGS All general purpose registers + ADDR_REGS All general purpose registers except %r0 + (These registers can be used in address generation) + FP_REGS All floating point registers + + GENERAL_FP_REGS Union of GENERAL_REGS and FP_REGS + ADDR_FP_REGS Union of ADDR_REGS and FP_REGS + + NO_REGS No registers + ALL_REGS All registers + + Note that the 'fake' frame pointer and argument pointer registers + are included amongst the address registers here. The condition + code register is only included in ALL_REGS. */ enum reg_class { @@ -497,19 +375,13 @@ enum reg_class FP_REGS, ADDR_FP_REGS, GENERAL_FP_REGS, ALL_REGS, LIM_REG_CLASSES }; - #define N_REG_CLASSES (int) LIM_REG_CLASSES -/* Give names of register classes as strings for dump file. */ - #define REG_CLASS_NAMES \ { "NO_REGS", "ADDR_REGS", "GENERAL_REGS", \ "FP_REGS", "ADDR_FP_REGS", "GENERAL_FP_REGS", "ALL_REGS" } -/* Define which registers fit in which classes. This is an initializer for - a vector of HARD_REG_SET of length N_REG_CLASSES. - G5 and latter have 16 register and support IEEE floating point operations. */ - +/* Class -> register mapping. */ #define REG_CLASS_CONTENTS \ { \ { 0x00000000, 0x00000000 }, /* NO_REGS */ \ @@ -521,105 +393,102 @@ enum reg_class { 0xffffffff, 0x00000007 }, /* ALL_REGS */ \ } +/* Register -> class mapping. */ +extern const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER]; +#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO]) -/* The same information, inverted: - Return the class number of the smallest class containing - reg number REGNO. This could be a conditional expression - or could index an array. */ +/* ADDR_REGS can be used as base or index register. */ +#define INDEX_REG_CLASS ADDR_REGS +#define BASE_REG_CLASS ADDR_REGS -#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO]) +/* Check whether REGNO is a hard register of the suitable class + or a pseudo register currently allocated to one such. */ +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + (((REGNO) < FIRST_PSEUDO_REGISTER \ + && REGNO_REG_CLASS ((REGNO)) == ADDR_REGS) \ + || (reg_renumber[REGNO] > 0 && reg_renumber[REGNO] < 16)) +#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_INDEX_P (REGNO) -extern enum reg_class regclass_map[FIRST_PSEUDO_REGISTER]; /* smalled class containing REGNO */ -/* The class value for index registers, and the one for base regs. */ +/* Given an rtx X being reloaded into a reg required to be in class CLASS, + return the class of reg to actually use. */ +#define PREFERRED_RELOAD_CLASS(X, CLASS) \ + s390_preferred_reload_class ((X), (CLASS)) -#define INDEX_REG_CLASS ADDR_REGS -#define BASE_REG_CLASS ADDR_REGS +/* We need a secondary reload when loading a PLUS which is + not a valid operand for LOAD ADDRESS. */ +#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, IN) \ + s390_secondary_input_reload_class ((CLASS), (MODE), (IN)) + +/* We need a secondary reload when storing a double-word + to a non-offsettable memory address. */ +#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, OUT) \ + s390_secondary_output_reload_class ((CLASS), (MODE), (OUT)) -/* Get reg_class from a letter such as appears in the machine description. */ +/* We need secondary memory to move data between GPRs and FPRs. */ +#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ + ((CLASS1) != (CLASS2) && ((CLASS1) == FP_REGS || (CLASS2) == FP_REGS)) + +/* Get_secondary_mem widens its argument to BITS_PER_WORD which loses on 64bit + because the movsi and movsf patterns don't handle r/f moves. */ +#define SECONDARY_MEMORY_NEEDED_MODE(MODE) \ + (GET_MODE_BITSIZE (MODE) < 32 \ + ? mode_for_size (32, GET_MODE_CLASS (MODE), 0) \ + : MODE) + + +/* Define various machine-dependent constraint letters. */ #define REG_CLASS_FROM_LETTER(C) \ ((C) == 'a' ? ADDR_REGS : \ (C) == 'd' ? GENERAL_REGS : \ (C) == 'f' ? FP_REGS : NO_REGS) -/* The letters I, J, K, L and M in a register constraint string can be used - to stand for particular ranges of immediate operands. - This macro defines what the ranges are. - C is the letter, and VALUE is a constant value. - Return 1 if VALUE is in the range specified by C. */ - #define CONST_OK_FOR_LETTER_P(VALUE, C) \ ((C) == 'I' ? (unsigned long) (VALUE) < 256 : \ (C) == 'J' ? (unsigned long) (VALUE) < 4096 : \ (C) == 'K' ? (VALUE) >= -32768 && (VALUE) < 32768 : \ (C) == 'L' ? (unsigned long) (VALUE) < 65536 : 0) -/* Similar, but for floating constants, and defining letters G and H. - Here VALUE is the CONST_DOUBLE rtx itself. */ - #define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) 1 -/* 'Q' means a memory-reference for a S-type operand. */ - #define EXTRA_CONSTRAINT(OP, C) \ - ((C) == 'Q' ? s_operand (OP, GET_MODE (OP)) : \ + ((C) == 'Q' ? q_constraint (OP) : \ (C) == 'S' ? larl_operand (OP, GET_MODE (OP)) : 0) -/* Given an rtx X being reloaded into a reg required to be in class CLASS, - return the class of reg to actually use. In general this is just CLASS; - but on some machines in some cases it is preferable to use a more - restrictive class. */ - -#define PREFERRED_RELOAD_CLASS(X, CLASS) \ - s390_preferred_reload_class ((X), (CLASS)) - -/* Return the maximum number of consecutive registers needed to represent - mode MODE in a register of class CLASS. */ - -#define CLASS_MAX_NREGS(CLASS, MODE) \ - ((CLASS) == FP_REGS ? \ - (GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT ? 2 : 1) : \ - (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) - -/* We need a secondary reload when loading a PLUS which is - not a valid operand for LOAD ADDRESS. */ - -#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, IN) \ - s390_secondary_input_reload_class ((CLASS), (MODE), (IN)) +#define EXTRA_MEMORY_CONSTRAINT(C) ((C) == 'Q') -/* If we are copying between FP registers and anything else, we need a memory - location. */ -#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ - ((CLASS1) != (CLASS2) && ((CLASS1) == FP_REGS || (CLASS2) == FP_REGS)) - -/* Get_secondary_mem widens its argument to BITS_PER_WORD which loses on 64bit - because the movsi and movsf patterns don't handle r/f moves. */ +/* Stack layout and calling conventions. */ + +/* Our stack grows from higher to lower addresses. However, local variables + are accessed by positive offsets, and function arguments are stored at + increasing addresses. */ +#define STACK_GROWS_DOWNWARD +/* #undef FRAME_GROWS_DOWNWARD */ +/* #undef ARGS_GROW_DOWNWARD */ -#define SECONDARY_MEMORY_NEEDED_MODE(MODE) \ - (GET_MODE_BITSIZE (MODE) < 32 \ - ? mode_for_size (32, GET_MODE_CLASS (MODE), 0) \ - : MODE) +/* The basic stack layout looks like this: the stack pointer points + to the register save area for called functions. Above that area + is the location to place outgoing arguments. Above those follow + dynamic allocations (alloca), and finally the local variables. */ +/* Offset from stack-pointer to first location of outgoing args. */ +#define STACK_POINTER_OFFSET (TARGET_64BIT ? 160 : 96) -/* A C expression whose value is nonzero if pseudos that have been - assigned to registers of class CLASS would likely be spilled - because registers of CLASS are needed for spill registers. +/* Offset within stack frame to start allocating local variables at. */ +extern int current_function_outgoing_args_size; +#define STARTING_FRAME_OFFSET \ + (STACK_POINTER_OFFSET + current_function_outgoing_args_size) - The default value of this macro returns 1 if CLASS has exactly one - register and zero otherwise. On most machines, this default - should be used. Only define this macro to some other expression - if pseudo allocated by `local-alloc.c' end up in memory because - their hard registers were needed for spill registers. If this - macro returns nonzero for those classes, those pseudos will only - be allocated by `global.c', which knows how to reallocate the - pseudo to another register. If there would not be another - register available for reallocation, you should not change the - definition of this macro since the only effect of such a - definition would be to slow down register allocation. */ +/* Offset from the stack pointer register to an item dynamically + allocated on the stack, e.g., by `alloca'. */ +#define STACK_DYNAMIC_OFFSET(FUNDECL) (STARTING_FRAME_OFFSET) -/* Stack layout; function entry, exit and calling. */ +/* Offset of first parameter from the argument pointer register value. + We have a fake argument pointer register that points directly to + the argument area. */ +#define FIRST_PARM_OFFSET(FNDECL) 0 /* The return address of the current frame is retrieved from the initial value of register RETURN_REGNUM. @@ -631,108 +500,91 @@ extern enum reg_class regclass_map[FIRST_PSEUDO_REGISTER]; /* smalled class cont plus_constant (arg_pointer_rtx, -STACK_POINTER_OFFSET)) #define RETURN_ADDR_RTX(COUNT, FRAME) \ - ((COUNT) == 0 ? get_hard_reg_initial_val (Pmode, RETURN_REGNUM) : \ - gen_rtx_MEM (Pmode, \ - memory_address (Pmode, \ - plus_constant (DYNAMIC_CHAIN_ADDRESS ((FRAME)), \ - RETURN_REGNUM * UNITS_PER_WORD)))) - -/* The following macros will turn on dwarf2 exception hndling - Other code location for this exception handling are - in s390.md (eh_return insn) and in linux.c in the prologue. */ - -#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, RETURN_REGNUM) - -/* We have 31 bit mode. */ + s390_return_addr_rtx ((COUNT), DYNAMIC_CHAIN_ADDRESS ((FRAME))) +/* In 31-bit mode, we need to mask off the high bit of return addresses. */ #define MASK_RETURN_ADDR (TARGET_64BIT ? GEN_INT (-1) : GEN_INT (0x7fffffff)) -/* The offset from the incoming value of %sp to the top of the stack frame - for the current function. */ +/* Exception handling. */ + +/* Describe calling conventions for DWARF-2 exception handling. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, RETURN_REGNUM) #define INCOMING_FRAME_SP_OFFSET STACK_POINTER_OFFSET - -/* Location, from where return address to load. */ - #define DWARF_FRAME_RETURN_COLUMN 14 /* Describe how we implement __builtin_eh_return. */ #define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + 6 : INVALID_REGNUM) -#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 10) #define EH_RETURN_HANDLER_RTX \ gen_rtx_MEM (Pmode, plus_constant (arg_pointer_rtx, \ TARGET_64BIT? -48 : -40)) -/* Define this if pushing a word on the stack makes the stack pointer a - smaller address. */ - -#define STACK_GROWS_DOWNWARD - -/* Define this if the nominal address of the stack frame is at the - high-address end of the local variables; that is, each additional local - variable allocated goes at a more negative offset in the frame. */ - -/* #define FRAME_GROWS_DOWNWARD */ - -/* Offset from stack-pointer to first location of outgoing args. */ +/* Select a format to encode pointers in exception handling data. */ +#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) \ + (flag_pic \ + ? ((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_pcrel | DW_EH_PE_sdata4 \ + : DW_EH_PE_absptr) -#define STACK_POINTER_OFFSET (TARGET_64BIT ? 160 : 96) -/* Offset within stack frame to start allocating local variables at. - If FRAME_GROWS_DOWNWARD, this is the offset to the END of the - first local allocated. Otherwise, it is the offset to the BEGINNING - of the first local allocated. */ +/* Frame registers. */ -#define STARTING_FRAME_OFFSET \ - (STACK_POINTER_OFFSET + current_function_outgoing_args_size) +#define STACK_POINTER_REGNUM 15 +#define FRAME_POINTER_REGNUM 34 +#define HARD_FRAME_POINTER_REGNUM 11 +#define ARG_POINTER_REGNUM 32 -#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) (DEPTH) = 0 +/* The static chain must be call-clobbered, but not used for + function argument passing. As register 1 is clobbered by + the trampoline code, we only have one option. */ +#define STATIC_CHAIN_REGNUM 0 -/* If we generate an insn to push BYTES bytes, this says how many the stack - pointer really advances by. On S/390, we have no push instruction. */ +/* Number of hardware registers that go into the DWARF-2 unwind info. + To avoid ABI incompatibility, this number must not change even as + 'fake' hard registers are added or removed. */ +#define DWARF_FRAME_REGISTERS 34 -/* #define PUSH_ROUNDING(BYTES) */ -/* Accumulate the outgoing argument count so we can request the right - DSA size and determine stack offset. */ +/* Frame pointer and argument pointer elimination. */ -#define ACCUMULATE_OUTGOING_ARGS 1 +#define FRAME_POINTER_REQUIRED 0 -/* Offset from the stack pointer register to an item dynamically - allocated on the stack, e.g., by `alloca'. +#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) (DEPTH) = 0 - The default value for this macro is `STACK_POINTER_OFFSET' plus the - length of the outgoing arguments. The default is correct for most - machines. See `function.c' for details. */ -#define STACK_DYNAMIC_OFFSET(FUNDECL) (STARTING_FRAME_OFFSET) +#define ELIMINABLE_REGS \ +{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \ + { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}} -/* Offset of first parameter from the argument pointer register value. - On the S/390, we define the argument pointer to the start of the fixed - area. */ -#define FIRST_PARM_OFFSET(FNDECL) 0 +#define CAN_ELIMINATE(FROM, TO) (1) -/* Define this if stack space is still allocated for a parameter passed - in a register. The value is the number of bytes allocated to this - area. */ -/* #define REG_PARM_STACK_SPACE(FNDECL) 32 */ +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ +{ if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \ + { (OFFSET) = 0; } \ + else if ((FROM) == FRAME_POINTER_REGNUM \ + && (TO) == HARD_FRAME_POINTER_REGNUM) \ + { (OFFSET) = 0; } \ + else if ((FROM) == ARG_POINTER_REGNUM \ + && (TO) == HARD_FRAME_POINTER_REGNUM) \ + { (OFFSET) = s390_arg_frame_offset (); } \ + else if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \ + { (OFFSET) = s390_arg_frame_offset (); } \ + else \ + abort(); \ +} -/* Define this if the above stack space is to be considered part of the - space allocated by the caller. */ -/* #define OUTGOING_REG_PARM_STACK_SPACE */ -/* 1 if N is a possible register number for function argument passing. - On S390, general registers 2 - 6 and floating point register 0 and 2 - are used in this way. */ +/* Stack arguments. */ + +/* We need current_function_outgoing_args to be valid. */ +#define ACCUMULATE_OUTGOING_ARGS 1 -#define FUNCTION_ARG_REGNO_P(N) (((N) >=2 && (N) <7) || \ - (N) == 16 || (N) == 17) +/* Return doesn't modify the stack. */ +#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, SIZE) 0 -/* Define a data type for recording info about an argument list during - the scan of that argument list. This data type should hold all - necessary information about the function itself and about the args - processed so far, enough to enable macros such as FUNCTION_ARG to - determine where the next arg should go. */ +/* Register arguments. */ + typedef struct s390_arg_structure { int gprs; /* gpr so far */ @@ -740,84 +592,30 @@ typedef struct s390_arg_structure } CUMULATIVE_ARGS; - -/* 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. */ - #define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, NN) \ ((CUM).gprs=0, (CUM).fprs=0) -/* 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.) */ - #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ s390_function_arg_advance (&CUM, MODE, TYPE, NAMED) -/* Define where to put the arguments to a function. Value is zero to push - the argument on the stack, or a hard register in which to store the - argument. */ - #define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ s390_function_arg (&CUM, MODE, TYPE, NAMED) -/* Define where to expect the arguments of a function. Value is zero, if - the argument is on the stack, or a hard register in which the argument - is stored. It is the same like FUNCTION_ARG, except for unnamed args - That means, that all in case of varargs used, the arguments are expected - from the stack. - S/390 has already space on the stack for args coming in registers, - they are pushed in prologue, if needed. */ - - -/* Define the `__builtin_va_list' type. */ - -#define BUILD_VA_LIST_TYPE(VALIST) \ - (VALIST) = s390_build_va_list () - -/* Implement `va_start' for varargs and stdarg. */ - -#define EXPAND_BUILTIN_VA_START(stdarg, valist, nextarg) \ - s390_va_start (stdarg, valist, nextarg) - -/* Implement `va_arg'. */ - -#define EXPAND_BUILTIN_VA_ARG(valist, type) \ - s390_va_arg (valist, type) - -/* 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. */ - -#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0 - - -/* Define if returning from a function call automatically pops the - arguments described by the number-of-args field in the call. */ - -#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, SIZE) 0 - - -/* Define how to find the value returned by a function. VALTYPE is the - data type of the value (as a tree). - If the precise function being called is known, FUNC is its FUNCTION_DECL; - otherwise, FUNC is 15. */ - -#define RET_REG(MODE) ((GET_MODE_CLASS (MODE) == MODE_INT \ - || TARGET_SOFT_FLOAT ) ? 2 : 16) - - -/* for structs the address is passed, and the Callee makes a - copy, only if needed */ - #define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ s390_function_arg_pass_by_reference (MODE, TYPE) +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0 -/* Register 2 (and 3) for integral values - or floating point register 0 (and 2) for fp values are used. */ +/* Arguments can be placed in general registers 2 to 6, + or in floating point registers 0 and 2. */ +#define FUNCTION_ARG_REGNO_P(N) (((N) >=2 && (N) <7) || \ + (N) == 16 || (N) == 17) + +/* Scalar return values. */ + +/* We return scalars in general purpose register 2 for integral values, + and floating point register 0 for fp values. */ #define FUNCTION_VALUE(VALTYPE, FUNC) \ gen_rtx_REG ((INTEGRAL_TYPE_P (VALTYPE) \ && TYPE_PRECISION (VALTYPE) < BITS_PER_WORD) \ @@ -827,133 +625,77 @@ CUMULATIVE_ARGS; /* Define how to find the value returned by a library function assuming the value has mode MODE. */ - +#define RET_REG(MODE) ((GET_MODE_CLASS (MODE) == MODE_INT \ + || TARGET_SOFT_FLOAT ) ? 2 : 16) #define LIBCALL_VALUE(MODE) gen_rtx (REG, MODE, RET_REG (MODE)) -/* 1 if N is a possible register number for a function value. */ - +/* Only gpr 2 and fpr 0 are ever used as return registers. */ #define FUNCTION_VALUE_REGNO_P(N) ((N) == 2 || (N) == 16) + +/* Aggregate return values. */ + /* The definition of this macro implies that there are cases where a scalar value cannot be returned in registers. */ - #define RETURN_IN_MEMORY(type) \ (TYPE_MODE (type) == BLKmode || \ GET_MODE_CLASS (TYPE_MODE (type)) == MODE_COMPLEX_INT || \ GET_MODE_CLASS (TYPE_MODE (type)) == MODE_COMPLEX_FLOAT) -/* Mode of stack savearea. - FUNCTION is VOIDmode because calling convention maintains SP. - BLOCK needs Pmode for SP. - NONLOCAL needs twice Pmode to maintain both backchain and SP. */ - -#define STACK_SAVEAREA_MODE(LEVEL) \ - (LEVEL == SAVE_FUNCTION ? VOIDmode \ - : LEVEL == SAVE_NONLOCAL ? (TARGET_64BIT ? TImode : DImode) : Pmode) - /* Structure value address is passed as invisible first argument (gpr 2). */ - #define STRUCT_VALUE 0 -/* This macro definition sets up a default value for `main' to return. */ - -#define DEFAULT_MAIN_RETURN c_expand_return (integer_zero_node) - -/* Length in units of the trampoline for entering a nested function. */ - -#define TRAMPOLINE_SIZE (TARGET_64BIT ? 36 : 20) - -/* Initialize the dynamic part of trampoline. */ - -#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, CXT) \ - s390_initialize_trampoline ((ADDR), (FNADDR), (CXT)) -/* Template for constant part of trampoline. */ +/* Function entry and exit. */ + +/* When returning from a function, the stack pointer does not matter. */ +#define EXIT_IGNORE_STACK 1 -#define TRAMPOLINE_TEMPLATE(FILE) \ - s390_trampoline_template (FILE) -/* Output assembler code to FILE to increment profiler label # LABELNO - for profiling a function entry. */ +/* Profiling. */ #define FUNCTION_PROFILER(FILE, LABELNO) \ - s390_function_profiler ((FILE), ((LABELNO))) + s390_function_profiler ((FILE), ((LABELNO))) #define PROFILE_BEFORE_PROLOGUE 1 -/* Define EXIT_IGNORE_STACK if, when returning from a function, the stack - pointer does not matter (provided there is a frame pointer). */ - -#define EXIT_IGNORE_STACK 1 - -/* Addressing modes, and classification of registers for them. */ - -/* #define HAVE_POST_INCREMENT */ -/* #define HAVE_POST_DECREMENT */ -/* #define HAVE_PRE_DECREMENT */ -/* #define HAVE_PRE_INCREMENT */ +/* Implementing the varargs macros. */ -/* These assume that REGNO is a hard or pseudo reg number. They give - nonzero only if REGNO is a hard reg of the suitable class or a pseudo - reg currently allocated to a suitable hard reg. - These definitions are NOT overridden anywhere. */ - -#define REGNO_OK_FOR_INDEX_P(REGNO) \ - (((REGNO) < FIRST_PSEUDO_REGISTER \ - && REGNO_REG_CLASS ((REGNO)) == ADDR_REGS) \ - || (reg_renumber[REGNO] > 0 && reg_renumber[REGNO] < 16)) - -#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_INDEX_P (REGNO) +#define BUILD_VA_LIST_TYPE(VALIST) \ + (VALIST) = s390_build_va_list () -#define REGNO_OK_FOR_DATA_P(REGNO) \ - ((REGNO) < 16 || (unsigned) reg_renumber[REGNO] < 16) +#define EXPAND_BUILTIN_VA_START(valist, nextarg) \ + s390_va_start (valist, nextarg) -#define REGNO_OK_FOR_FP_P(REGNO) \ - FLOAT_REGNO_P (REGNO) +#define EXPAND_BUILTIN_VA_ARG(valist, type) \ + s390_va_arg (valist, type) -/* Now macros that check whether X is a register and also, - strictly, whether it is in a specified class. */ -/* 1 if X is a data register. */ +/* Trampolines for nested functions. */ -#define DATA_REG_P(X) (REG_P (X) && REGNO_OK_FOR_DATA_P (REGNO (X))) +#define TRAMPOLINE_SIZE (TARGET_64BIT ? 36 : 20) -/* 1 if X is an fp register. */ +#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, CXT) \ + s390_initialize_trampoline ((ADDR), (FNADDR), (CXT)) -#define FP_REG_P(X) (REG_P (X) && REGNO_OK_FOR_FP_P (REGNO (X))) +#define TRAMPOLINE_TEMPLATE(FILE) \ + s390_trampoline_template (FILE) -/* 1 if X is an address register. */ -#define ADDRESS_REG_P(X) (REG_P (X) && REGNO_OK_FOR_BASE_P (REGNO (X))) +/* Library calls. */ + +/* We should use memcpy, not bcopy. */ +#define TARGET_MEM_FUNCTIONS -/* Maximum number of registers that can appear in a valid memory address. */ -#define MAX_REGS_PER_ADDRESS 2 +/* Addressing modes, and classification of registers for them. */ /* Recognize any constant value that is a valid address. */ - #define CONSTANT_ADDRESS_P(X) 0 -#define SYMBOLIC_CONST(X) \ -(GET_CODE (X) == SYMBOL_REF \ - || GET_CODE (X) == LABEL_REF \ - || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X))) - -/* General operand is everything except SYMBOL_REF, CONST and CONST_DOUBLE - they have to be forced to constant pool - CONST_INT have to be forced into constant pool, if greater than - 64k. Depending on the insn they have to be force into constant pool - for smaller value; in this case we have to work with nonimmediate operand. */ - -#define LEGITIMATE_PIC_OPERAND_P(X) \ - legitimate_pic_operand_p (X) - -/* Nonzero if the constant value X is a legitimate general operand. - It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ - -#define LEGITIMATE_CONSTANT_P(X) \ - legitimate_constant_p (X) +/* Maximum number of registers that can appear in a valid memory address. */ +#define MAX_REGS_PER_ADDRESS 2 /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its validity for a certain class. We have two alternate definitions @@ -966,32 +708,19 @@ CUMULATIVE_ARGS; Some source files that are used after register allocation need to be strict. */ -/* - * Nonzero if X is a hard reg that can be used as an index or if it is - * a pseudo reg. - */ - #define REG_OK_FOR_INDEX_NONSTRICT_P(X) \ ((GET_MODE (X) == Pmode) && \ ((REGNO (X) >= FIRST_PSEUDO_REGISTER) \ || REGNO_REG_CLASS (REGNO (X)) == ADDR_REGS)) -/* Nonzero if X is a hard reg that can be used as a base reg or if it is - a pseudo reg. */ - #define REG_OK_FOR_BASE_NONSTRICT_P(X) REG_OK_FOR_INDEX_NONSTRICT_P (X) -/* Nonzero if X is a hard reg that can be used as an index. */ - #define REG_OK_FOR_INDEX_STRICT_P(X) \ ((GET_MODE (X) == Pmode) && (REGNO_OK_FOR_INDEX_P (REGNO (X)))) -/* Nonzero if X is a hard reg that can be used as a base reg. */ - #define REG_OK_FOR_BASE_STRICT_P(X) \ ((GET_MODE (X) == Pmode) && (REGNO_OK_FOR_BASE_P (REGNO (X)))) - #ifndef REG_OK_STRICT #define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_NONSTRICT_P(X) #define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_NONSTRICT_P(X) @@ -1000,15 +729,13 @@ CUMULATIVE_ARGS; #define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_STRICT_P(X) #endif +/* S/390 has no mode dependent addresses. */ +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) /* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a valid memory address for an instruction. The MODE argument is the machine mode for the MEM expression - that wants to use this address. - - The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS, - except for CONSTANT_ADDRESS_P which is actually machine-independent. */ - + that wants to use this address. */ #ifdef REG_OK_STRICT #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ { \ @@ -1023,15 +750,9 @@ CUMULATIVE_ARGS; } #endif - -/* S/390 has no mode dependent addresses. */ - -#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) - /* Try machine-dependent ways of modifying an illegitimate address to be legitimate. If we find one, return the new, valid address. This macro is used in only one place: `memory_address' in explow.c. */ - #define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ { \ (X) = legitimize_address (X, OLDX, MODE); \ @@ -1039,73 +760,35 @@ CUMULATIVE_ARGS; goto WIN; \ } -/* Specify the machine mode that this machine uses for the index in the - tablejump instruction. */ - -#define CASE_VECTOR_MODE (TARGET_64BIT ? DImode : SImode) - -/* Define this if the tablejump instruction expects the table to contain - offsets from the address of the table. - Do not define this if the table should contain absolute addresses. */ - -/* #define CASE_VECTOR_PC_RELATIVE */ - -/* Load from integral MODE < SI from memory into register makes sign_extend - or zero_extend - In our case sign_extension happens for Halfwords, other no extension. */ - -#define LOAD_EXTEND_OP(MODE) \ -(TARGET_64BIT ? ((MODE) == QImode ? ZERO_EXTEND : \ - (MODE) == HImode ? SIGN_EXTEND : NIL) \ - : ((MODE) == HImode ? SIGN_EXTEND : NIL)) - -/* Define this if fixuns_trunc is the same as fix_trunc. */ - -/* #define FIXUNS_TRUNC_LIKE_FIX_TRUNC */ - -/* We use "unsigned char" as default. */ - -#define DEFAULT_SIGNED_CHAR 0 - -/* Max number of bytes we can move from memory to memory in one reasonably - fast instruction. */ - -#define MOVE_MAX 256 - -/* Nonzero if access to memory by bytes is slow and undesirable. */ - -#define SLOW_BYTE_ACCESS 1 - -/* Define if shifts truncate the shift count which implies one can omit - a sign-extension or zero-extension of a shift count. */ - -/* #define SHIFT_COUNT_TRUNCATED */ - -/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits - is done just by pretending it is already truncated. */ - -#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 - -/* We assume that the store-condition-codes instructions store 0 for false - and some other value for true. This is the value stored for true. */ - -/* #define STORE_FLAG_VALUE -1 */ +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ +#define LEGITIMATE_CONSTANT_P(X) \ + legitimate_constant_p (X) -/* Don't perform CSE on function addresses. */ +/* Helper macro for s390.c and s390.md to check for symbolic constants. */ +#define SYMBOLIC_CONST(X) \ +(GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X))) -#define NO_FUNCTION_CSE +#define TLS_SYMBOLIC_CONST(X) \ +((GET_CODE (X) == SYMBOL_REF && tls_symbolic_operand (X)) \ + || (GET_CODE (X) == CONST && tls_symbolic_reference_mentioned_p (X))) -/* Specify the machine mode that pointers have. - After generation of rtl, the compiler makes no further distinction - between pointers and any other objects of this machine mode. */ -#define Pmode ((enum machine_mode) (TARGET_64BIT ? DImode : SImode)) +/* Condition codes. */ -/* A function address in a call instruction is a byte address (for - indexing purposes) so give the MEM rtx a byte's mode. */ +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. */ +#define SELECT_CC_MODE(OP, X, Y) s390_select_ccmode ((OP), (X), (Y)) + +/* Define the information needed to generate branch and scc insns. This is + stored from the compare operation. Note that we can't use "rtx" here + since it hasn't been defined! */ +extern struct rtx_def *s390_compare_op0, *s390_compare_op1; -#define FUNCTION_MODE QImode +/* Relative costs of operations. */ /* A part of a C `switch' statement that describes the relative costs of constant RTL expressions. It must contain `case' labels for @@ -1126,7 +809,6 @@ CUMULATIVE_ARGS; than a short. Because of that we give an addition of greater constants a cost of 3 (reload1.c 10096). */ - #define CONST_COSTS(RTX, CODE, OUTER_CODE) \ case CONST: \ if ((GET_CODE (XEXP (RTX, 0)) == MINUS) && \ @@ -1148,10 +830,7 @@ CUMULATIVE_ARGS; instruction is. In writing this macro, you can use the construct `COSTS_N_INSNS (N)' to specify a cost equal to N fast instructions. OUTER_CODE is the code of the expression in which X - is contained. - - This macro is optional; do not define it if the default cost - assumptions are adequate for the target machine. */ + is contained. */ #define RTX_COSTS(X, CODE, OUTER_CODE) \ case ASHIFT: \ @@ -1164,155 +843,72 @@ CUMULATIVE_ARGS; case MINUS: \ case NEG: \ case NOT: \ - return 1; \ + return COSTS_N_INSNS (1); \ case MULT: \ if (GET_MODE (XEXP (X, 0)) == DImode) \ - return 40; \ - else \ - return 7; \ + return COSTS_N_INSNS (40); \ + else \ + return COSTS_N_INSNS (7); \ case DIV: \ case UDIV: \ case MOD: \ case UMOD: \ - return 33; + return COSTS_N_INSNS (33); /* An expression giving the cost of an addressing mode that contains ADDRESS. If not defined, the cost is computed from the ADDRESS - expression and the `CONST_COSTS' values. - - For most CISC machines, the default cost is a good approximation - of the true cost of the addressing mode. However, on RISC - machines, all instructions normally have the same length and - execution time. Hence all addresses will have equal costs. - - In cases where more than one form of an address is known, the form - with the lowest cost will be used. If multiple forms have the - same, lowest, cost, the one that is the most complex will be used. - - For example, suppose an address that is equal to the sum of a - register and a constant is used twice in the same basic block. - When this macro is not defined, the address will be computed in a - register and memory references will be indirect through that - register. On machines where the cost of the addressing mode - containing the sum is no higher than that of a simple indirect - reference, this will produce an additional instruction and - possibly require an additional register. Proper specification of - this macro eliminates this overhead for such machines. - - Similar use of this macro is made in strength reduction of loops. - - ADDRESS need not be valid as an address. In such a case, the cost - is not relevant and can be any value; invalid addresses need not be - assigned a different cost. - - On machines where an address involving more than one register is as - cheap as an address computation involving only one register, - defining `ADDRESS_COST' to reflect this can cause two registers to - be live over a region of code where only one would have been if - `ADDRESS_COST' were not defined in that manner. This effect should - be considered in the definition of this macro. Equivalent costs - should probably only be given to addresses with different numbers - of registers on machines with lots of registers. - - This macro will normally either not be defined or be defined as a - constant. - - On s390 symbols are expensive if compiled with fpic - lifetimes. */ - -#define ADDRESS_COST(RTX) \ - ((flag_pic && GET_CODE (RTX) == SYMBOL_REF) ? 2 : 1) + expression and the `CONST_COSTS' values. */ +#define ADDRESS_COST(RTX) s390_address_cost ((RTX)) /* On s390, copy between fprs and gprs is expensive. */ - #define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \ (( ( reg_classes_intersect_p ((CLASS1), GENERAL_REGS) \ && reg_classes_intersect_p ((CLASS2), FP_REGS)) \ || ( reg_classes_intersect_p ((CLASS1), FP_REGS) \ && reg_classes_intersect_p ((CLASS2), GENERAL_REGS))) ? 10 : 1) - /* A C expression for the cost of moving data of mode M between a register and memory. A value of 2 is the default; this cost is - relative to those in `REGISTER_MOVE_COST'. - - If moving between registers and memory is more expensive than - between two registers, you should define this macro to express the - relative cost. */ - + relative to those in `REGISTER_MOVE_COST'. */ #define MEMORY_MOVE_COST(M, C, I) 1 /* A C expression for the cost of a branch instruction. A value of 1 is the default; other values are interpreted relative to that. */ - #define BRANCH_COST 1 -/* Add any extra modes needed to represent the condition code. */ -#define EXTRA_CC_MODES \ - CC (CCZmode, "CCZ") \ - CC (CCAmode, "CCA") \ - CC (CCLmode, "CCL") \ - CC (CCUmode, "CCU") \ - CC (CCSmode, "CCS") \ - CC (CCTmode, "CCT") - -/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, - return the mode to be used for the comparison. */ - -#define SELECT_CC_MODE(OP, X, Y) s390_select_ccmode ((OP), (X), (Y)) - - -/* Define the information needed to generate branch and scc insns. This is - stored from the compare operation. Note that we can't use "rtx" here - since it hasn't been defined! */ - -extern struct rtx_def *s390_compare_op0, *s390_compare_op1; - - -/* How to refer to registers in assembler output. This sequence is - indexed by compiler's hard-register-number (see above). */ - -#define REGISTER_NAMES \ -{ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", \ - "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", \ - "%f0", "%f2", "%f4", "%f6", "%f1", "%f3", "%f5", "%f7", \ - "%f8", "%f10", "%f12", "%f14", "%f9", "%f11", "%f13", "%f15", \ - "%ap", "%cc", "%fp" \ -} +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 1 -/* implicit call of memcpy, not bcopy */ +/* The maximum number of bytes that a single instruction can move quickly + between memory and registers or between two memory locations. */ +#define MOVE_MAX (TARGET_64BIT ? 16 : 8) +#define MAX_MOVE_MAX 16 -#define TARGET_MEM_FUNCTIONS +/* Determine whether to use move_by_pieces or block move insn. */ +#define MOVE_BY_PIECES_P(SIZE, ALIGN) \ + ( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 \ + || (TARGET_64BIT && (SIZE) == 8) ) -/* Either simplify a location expression, or return the original. */ +/* Determine whether to use clear_by_pieces or block clear insn. */ +#define CLEAR_BY_PIECES_P(SIZE, ALIGN) \ + ( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 \ + || (TARGET_64BIT && (SIZE) == 8) ) -#define ASM_SIMPLIFY_DWARF_ADDR(X) \ - s390_simplify_dwarf_addr (X) - -/* 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. */ - -#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) +/* Don't perform CSE on function addresses. */ +#define NO_FUNCTION_CSE -#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) +/* Sections. */ -/* Define the codes that are matched by predicates in aux-output.c. */ +/* Output before read-only data. */ +#define TEXT_SECTION_ASM_OP ".text" -#define PREDICATE_CODES \ - {"s_operand", { SUBREG, MEM }}, \ - {"s_imm_operand", { CONST_INT, CONST_DOUBLE, SUBREG, MEM }}, \ - {"bras_sym_operand",{ SYMBOL_REF, CONST }}, \ - {"larl_operand", { SYMBOL_REF, CONST, CONST_INT, CONST_DOUBLE }}, \ - {"load_multiple_operation", {PARALLEL}}, \ - {"store_multiple_operation", {PARALLEL}}, \ - {"const0_operand", { CONST_INT, CONST_DOUBLE }}, \ - {"consttable_operand", { SYMBOL_REF, LABEL_REF, CONST, \ - CONST_INT, CONST_DOUBLE }}, \ - {"s390_plus_operand", { PLUS }}, +/* Output before writable (initialized) data. */ +#define DATA_SECTION_ASM_OP ".data" +/* Output before writable (uninitialized) data. */ +#define BSS_SECTION_ASM_OP ".bss" /* S/390 constant pool breaks the devices in crtstuff.c to control section in where code resides. We have to write it as asm code. */ @@ -1326,12 +922,97 @@ extern struct rtx_def *s390_compare_op0, *s390_compare_op1; .previous"); #endif + +/* Position independent code. */ + +extern int flag_pic; + +#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? 12 : INVALID_REGNUM) + +#define LEGITIMATE_PIC_OPERAND_P(X) legitimate_pic_operand_p (X) + + +/* Assembler file format. */ + +/* Character to start a comment. */ +#define ASM_COMMENT_START "#" + +/* Declare an uninitialized external linkage data object. */ +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP ".globl " + +/* Advance the location counter to a multiple of 2**LOG bytes. */ +#define ASM_OUTPUT_ALIGN(FILE, LOG) \ + if ((LOG)) fprintf ((FILE), "\t.align\t%d\n", 1 << (LOG)) + +/* Advance the location counter by SIZE bytes. */ +#define ASM_OUTPUT_SKIP(FILE, SIZE) \ + fprintf ((FILE), "\t.set\t.,.+%u\n", (SIZE)) + +/* Output a reference to a user-level label named NAME. */ +#define ASM_OUTPUT_LABELREF(FILE, NAME) \ + asm_fprintf ((FILE), "%U%s", (*targetm.strip_name_encoding) (NAME)) + +/* Store in OUTPUT a string (made with alloca) containing + an assembler-name for a local static variable named NAME. + LABELNO is an integer which is different for each call. */ +#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ + ((OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \ + sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO))) + +/* The LOCAL_LABEL_PREFIX variable is used by dbxelf.h. */ +#define LOCAL_LABEL_PREFIX "." + +/* Either simplify a location expression, or return the original. */ +#define ASM_SIMPLIFY_DWARF_ADDR(X) \ + s390_simplify_dwarf_addr (X) + +/* How to refer to registers in assembler output. This sequence is + indexed by compiler's hard-register-number (see above). */ +#define REGISTER_NAMES \ +{ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", \ + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", \ + "%f0", "%f2", "%f4", "%f6", "%f1", "%f3", "%f5", "%f7", \ + "%f8", "%f10", "%f12", "%f14", "%f9", "%f11", "%f13", "%f15", \ + "%ap", "%cc", "%fp" \ +} + +/* Print operand X (an rtx) in assembler syntax to file FILE. */ +#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) + +/* Output an element of a case-vector that is absolute. */ +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ +do { \ + char buf[32]; \ + fputs (integer_asm_op (UNITS_PER_WORD, TRUE), (FILE)); \ + ASM_GENERATE_INTERNAL_LABEL (buf, "L", (VALUE)); \ + assemble_name ((FILE), buf); \ + fputc ('\n', (FILE)); \ +} while (0) + +/* Output an element of a case-vector that is relative. */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ +do { \ + char buf[32]; \ + fputs (integer_asm_op (UNITS_PER_WORD, TRUE), (FILE)); \ + ASM_GENERATE_INTERNAL_LABEL (buf, "L", (VALUE)); \ + assemble_name ((FILE), buf); \ + fputc ('-', (FILE)); \ + ASM_GENERATE_INTERNAL_LABEL (buf, "L", (REL)); \ + assemble_name ((FILE), buf); \ + fputc ('\n', (FILE)); \ +} while (0) + + /* Constant Pool for all symbols operands which are changed with force_const_mem during insn generation (expand_insn). */ extern int s390_pool_count; extern int s390_nr_constants; -extern int s390_pool_overflow; #define ASM_OUTPUT_POOL_PROLOGUE(FILE, FUNNAME, fndecl, size) \ { \ @@ -1357,16 +1038,15 @@ extern int s390_pool_overflow; if (GET_CODE (EXP) != CONST_DOUBLE) \ abort (); \ \ - memcpy ((char *) &u, (char *) &CONST_DOUBLE_LOW (EXP), sizeof u); \ - assemble_real (u.d, MODE, ALIGN); \ + REAL_VALUE_FROM_CONST_DOUBLE (r, EXP); \ + assemble_real (r, MODE, ALIGN); \ break; \ \ case MODE_INT: \ case MODE_PARTIAL_INT: \ - if (flag_pic \ - && (GET_CODE (EXP) == CONST \ - || GET_CODE (EXP) == SYMBOL_REF \ - || GET_CODE (EXP) == LABEL_REF )) \ + if (GET_CODE (EXP) == CONST \ + || GET_CODE (EXP) == SYMBOL_REF \ + || GET_CODE (EXP) == LABEL_REF) \ { \ fputs (integer_asm_op (UNITS_PER_WORD, TRUE), FILE); \ s390_output_symbolic_const (FILE, EXP); \ @@ -1386,4 +1066,54 @@ extern int s390_pool_overflow; goto WIN; \ } + +/* Miscellaneous parameters. */ + +/* Define the codes that are matched by predicates in aux-output.c. */ +#define PREDICATE_CODES \ + {"s_operand", { SUBREG, MEM }}, \ + {"s_imm_operand", { CONST_INT, CONST_DOUBLE, SUBREG, MEM }}, \ + {"bras_sym_operand",{ SYMBOL_REF, CONST }}, \ + {"larl_operand", { SYMBOL_REF, CONST, CONST_INT, CONST_DOUBLE }}, \ + {"load_multiple_operation", {PARALLEL}}, \ + {"store_multiple_operation", {PARALLEL}}, \ + {"const0_operand", { CONST_INT, CONST_DOUBLE }}, \ + {"consttable_operand", { SYMBOL_REF, LABEL_REF, CONST, \ + CONST_INT, CONST_DOUBLE }}, \ + {"s390_plus_operand", { PLUS }}, + +/* Specify the machine mode that this machine uses for the index in the + tablejump instruction. */ +#define CASE_VECTOR_MODE (TARGET_64BIT ? DImode : SImode) + +/* Load from integral MODE < SI from memory into register makes sign_extend + or zero_extend + In our case sign_extension happens for Halfwords, other no extension. */ +#define LOAD_EXTEND_OP(MODE) \ +(TARGET_64BIT ? ((MODE) == QImode ? ZERO_EXTEND : \ + (MODE) == HImode ? SIGN_EXTEND : NIL) \ + : ((MODE) == HImode ? SIGN_EXTEND : NIL)) + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +#define Pmode ((enum machine_mode) (TARGET_64BIT ? DImode : SImode)) + +/* A function address in a call instruction is a byte address (for + indexing purposes) so give the MEM rtx a byte's mode. */ +#define FUNCTION_MODE QImode + +/* This macro definition sets up a default value for `main' to return. */ +#define DEFAULT_MAIN_RETURN c_expand_return (integer_zero_node) + +/* In rare cases, correct code generation requires extra machine dependent + processing between the second jump optimization pass and delayed branch + scheduling. On those machines, define this macro as a C statement to act on + the code starting at INSN. */ +#define MACHINE_DEPENDENT_REORG(INSN) s390_machine_dependent_reorg (INSN) + #endif diff --git a/contrib/gcc/config/s390/s390.md b/contrib/gcc/config/s390/s390.md index 2742638..8178516 100644 --- a/contrib/gcc/config/s390/s390.md +++ b/contrib/gcc/config/s390/s390.md @@ -1,5 +1,5 @@ ;;- Machine description for GNU compiler -- S/390 / zSeries version. -;; Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. +;; Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. ;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and ;; Ulrich Weigand (uweigand@de.ibm.com). ;; This file is part of GNU CC. @@ -44,6 +44,34 @@ ;; s_operand -- Matches a valid S operand in a RS, SI or SS type instruction. ;; +;; +;; UNSPEC usage +;; + +(define_constants + [; TLS relocation specifiers + (UNSPEC_TLSGD 500) + (UNSPEC_TLSLDM 501) + (UNSPEC_NTPOFF 502) + (UNSPEC_DTPOFF 503) + (UNSPEC_GOTNTPOFF 504) + (UNSPEC_INDNTPOFF 505) + + ; TLS support + (UNSPEC_TP 510) + (UNSPEC_TLSLDM_NTPOFF 511) + (UNSPEC_TLS_LOAD 512) + ]) + +;; +;; UNSPEC_VOLATILE usage +;; + +(define_constants + [; TLS support + (UNSPECV_SET_TP 500) + ]) + ;; Define an insn type attribute. This is used in function unit delay ;; computations. @@ -191,30 +219,6 @@ DONE; }") -;(define_expand "cmphi" -; [(set (reg:CC 33) -; (compare:CC (match_operand:HI 0 "register_operand" "") -; (match_operand:HI 1 "general_operand" "")))] -; "" -; " -;{ -; s390_compare_op0 = operands[0]; -; s390_compare_op1 = operands[1]; -; DONE; -;}") - -;(define_expand "cmpqi" -; [(set (reg:CC 33) -; (compare:CC (match_operand:QI 0 "register_operand" "") -; (match_operand:QI 1 "general_operand" "")))] -; "" -; " -;{ -; s390_compare_op0 = operands[0]; -; s390_compare_op1 = operands[1]; -; DONE; -;}") - (define_expand "cmpdf" [(set (reg:CC 33) (compare:CC (match_operand:DF 0 "register_operand" "") @@ -240,9 +244,9 @@ }") -; DI instructions +; Test-under-Mask (zero_extract) instructions -(define_insn "*cmpdi_tm2" +(define_insn "*tmdi_ext" [(set (reg 33) (compare (zero_extract:DI (match_operand:DI 0 "register_operand" "d") (match_operand:DI 1 "const_int_operand" "n") @@ -272,35 +276,63 @@ }" [(set_attr "op_type" "RI")]) -(define_insn "*cmpdi_tm_reg" +(define_insn "*tmsi_ext" [(set (reg 33) - (compare (and:DI (match_operand:DI 0 "register_operand" "%d") - (match_operand:DI 1 "immediate_operand" "n")) + (compare (zero_extract:SI (match_operand:SI 0 "register_operand" "d") + (match_operand:SI 1 "const_int_operand" "n") + (match_operand:SI 2 "const_int_operand" "n")) (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) && TARGET_64BIT - && s390_single_hi (operands[1], DImode, 0) >= 0" + "s390_match_ccmode(insn, CCTmode) + && INTVAL (operands[1]) >= 1 && INTVAL (operands[2]) >= 0 + && INTVAL (operands[1]) + INTVAL (operands[2]) <= 32 + && (INTVAL (operands[1]) + INTVAL (operands[2]) - 1) >> 4 + == INTVAL (operands[2]) >> 4" "* { - int part = s390_single_hi (operands[1], DImode, 0); - operands[1] = GEN_INT (s390_extract_hi (operands[1], DImode, part)); + int part = INTVAL (operands[2]) >> 4; + int block = (1 << INTVAL (operands[1])) - 1; + int shift = 16 - INTVAL (operands[1]) - (INTVAL (operands[2]) & 15); + + operands[2] = GEN_INT (block << shift); switch (part) { - case 0: return \"tmhh\\t%0,%x1\"; - case 1: return \"tmhl\\t%0,%x1\"; - case 2: return \"tmlh\\t%0,%x1\"; - case 3: return \"tmll\\t%0,%x1\"; + case 0: return \"tmh\\t%0,%x2\"; + case 1: return \"tml\\t%0,%x2\"; default: abort (); } }" [(set_attr "op_type" "RI")]) -(define_insn "*cmpdi_tm_mem" +(define_insn "*tmqi_ext" [(set (reg 33) - (compare (and:DI (match_operand:DI 0 "s_operand" "%Qo") - (match_operand:DI 1 "immediate_operand" "n")) + (compare (zero_extract:SI (match_operand:QI 0 "memory_operand" "Q") + (match_operand:SI 1 "const_int_operand" "n") + (match_operand:SI 2 "const_int_operand" "n")) (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) && TARGET_64BIT + "s390_match_ccmode(insn, CCTmode) + && INTVAL (operands[1]) >= 1 && INTVAL (operands[2]) >= 0 + && INTVAL (operands[1]) + INTVAL (operands[2]) <= 8" + "* +{ + int block = (1 << INTVAL (operands[1])) - 1; + int shift = 8 - INTVAL (operands[1]) - INTVAL (operands[2]); + + operands[2] = GEN_INT (block << shift); + return \"tm\\t%0,%b2\"; +}" + [(set_attr "op_type" "SI") + (set_attr "atype" "mem")]) + +; Test-under-Mask instructions + +(define_insn "*tmdi_mem" + [(set (reg 33) + (compare (and:DI (match_operand:DI 0 "memory_operand" "Q") + (match_operand:DI 1 "immediate_operand" "n")) + (match_operand:DI 2 "immediate_operand" "n")))] + "TARGET_64BIT + && s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], 0)) && s390_single_qi (operands[1], DImode, 0) >= 0" "* { @@ -314,100 +346,84 @@ [(set_attr "op_type" "SI") (set_attr "atype" "mem")]) -(define_insn "*ltgr" +(define_insn "*tmsi_mem" [(set (reg 33) - (compare (match_operand:DI 0 "register_operand" "d") - (match_operand:DI 1 "const0_operand" ""))) - (set (match_operand:DI 2 "register_operand" "=d") - (match_dup 0))] - "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" - "ltgr\\t%2,%0" - [(set_attr "op_type" "RRE")]) + (compare (and:SI (match_operand:SI 0 "memory_operand" "Q") + (match_operand:SI 1 "immediate_operand" "n")) + (match_operand:SI 2 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], 0)) + && s390_single_qi (operands[1], SImode, 0) >= 0" + "* +{ + int part = s390_single_qi (operands[1], SImode, 0); + operands[1] = GEN_INT (s390_extract_qi (operands[1], SImode, part)); -(define_insn "*cmpdi_ccs_0_64" - [(set (reg 33) - (compare (match_operand:DI 0 "register_operand" "d") - (match_operand:DI 1 "const0_operand" "")))] - "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" - "ltgr\\t%0,%0" - [(set_attr "op_type" "RRE")]) + operands[0] = gen_rtx_MEM (QImode, + plus_constant (XEXP (operands[0], 0), part)); + return \"tm\\t%0,%b1\"; +}" + [(set_attr "op_type" "SI") + (set_attr "atype" "mem")]) -(define_insn "*cmpdi_ccs_0_31" +(define_insn "*tmhi_mem" [(set (reg 33) - (compare (match_operand:DI 0 "register_operand" "d") - (match_operand:DI 1 "const0_operand" "")))] - "s390_match_ccmode(insn, CCSmode)" - "srda\\t%0,0" - [(set_attr "op_type" "RS")]) + (compare (and:SI (subreg:SI (match_operand:HI 0 "memory_operand" "Q") 0) + (match_operand:SI 1 "immediate_operand" "n")) + (match_operand:SI 2 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], 0)) + && s390_single_qi (operands[1], HImode, 0) >= 0" + "* +{ + int part = s390_single_qi (operands[1], HImode, 0); + operands[1] = GEN_INT (s390_extract_qi (operands[1], HImode, part)); -(define_insn "*cmpdi_ccs" - [(set (reg 33) - (compare (match_operand:DI 0 "register_operand" "d,d,d") - (match_operand:DI 1 "general_operand" "d,K,m")))] - "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" - "@ - cgr\\t%0,%1 - cghi\\t%0,%c1 - cg\\t%0,%1" - [(set_attr "op_type" "RRE,RI,RXE") - (set_attr "atype" "reg,reg,mem")]) - -(define_insn "*cmpdi_ccu" - [(set (reg 33) - (compare (match_operand:DI 0 "register_operand" "d,d") - (match_operand:DI 1 "general_operand" "d,m")))] - "s390_match_ccmode(insn, CCUmode) && TARGET_64BIT" - "@ - clgr\\t%0,%1 - clg\\t%0,%1" - [(set_attr "op_type" "RRE,RXE") - (set_attr "atype" "reg,mem")]) + operands[0] = gen_rtx_MEM (QImode, + plus_constant (XEXP (operands[0], 0), part)); + return \"tm\\t%0,%b1\"; +}" + [(set_attr "op_type" "SI") + (set_attr "atype" "mem")]) -(define_insn "*cmpdi_ccu_mem" +(define_insn "*tmqi_mem" [(set (reg 33) - (compare (match_operand:DI 0 "s_operand" "oQ") - (match_operand:DI 1 "s_imm_operand" "oQ")))] - "s390_match_ccmode(insn, CCUmode)" - "clc\\t%O0(8,%R0),%1" - [(set_attr "op_type" "SS") + (compare (and:SI (subreg:SI (match_operand:QI 0 "memory_operand" "Q") 0) + (match_operand:SI 1 "immediate_operand" "n")) + (match_operand:SI 2 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], 0))" + "tm\\t%0,%b1" + [(set_attr "op_type" "SI") (set_attr "atype" "mem")]) -; SI instructions - -(define_insn "*cmpsi_tm2" +(define_insn "*tmdi_reg" [(set (reg 33) - (compare (zero_extract:SI (match_operand:SI 0 "register_operand" "d") - (match_operand:SI 1 "const_int_operand" "n") - (match_operand:SI 2 "const_int_operand" "n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) - && INTVAL (operands[1]) >= 1 && INTVAL (operands[2]) >= 0 - && INTVAL (operands[1]) + INTVAL (operands[2]) <= 32 - && (INTVAL (operands[1]) + INTVAL (operands[2]) - 1) >> 4 - == INTVAL (operands[2]) >> 4" + (compare (and:DI (match_operand:DI 0 "nonimmediate_operand" "d") + (match_operand:DI 1 "immediate_operand" "n")) + (match_operand:DI 2 "immediate_operand" "n")))] + "TARGET_64BIT + && s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], 1)) + && s390_single_hi (operands[1], DImode, 0) >= 0" "* { - int part = INTVAL (operands[2]) >> 4; - int block = (1 << INTVAL (operands[1])) - 1; - int shift = 16 - INTVAL (operands[1]) - (INTVAL (operands[2]) & 15); - - operands[2] = GEN_INT (block << shift); + int part = s390_single_hi (operands[1], DImode, 0); + operands[1] = GEN_INT (s390_extract_hi (operands[1], DImode, part)); switch (part) { - case 0: return \"tmh\\t%0,%x2\"; - case 1: return \"tml\\t%0,%x2\"; + case 0: return \"tmhh\\t%0,%x1\"; + case 1: return \"tmhl\\t%0,%x1\"; + case 2: return \"tmlh\\t%0,%x1\"; + case 3: return \"tmll\\t%0,%x1\"; default: abort (); } }" [(set_attr "op_type" "RI")]) -(define_insn "*cmpsi_tm_reg" +(define_insn "*tmsi_reg" [(set (reg 33) - (compare (and:SI (match_operand:SI 0 "register_operand" "%d") + (compare (and:SI (match_operand:SI 0 "nonimmediate_operand" "d") (match_operand:SI 1 "immediate_operand" "n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) + (match_operand:SI 2 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], 1)) && s390_single_hi (operands[1], SImode, 0) >= 0" "* { @@ -423,57 +439,88 @@ }" [(set_attr "op_type" "RI")]) -(define_insn "*cmpsi_tm_mem" +(define_insn "*tmhi_full" [(set (reg 33) - (compare (and:SI (match_operand:SI 0 "s_operand" "%Qo") - (match_operand:SI 1 "immediate_operand" "n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) - && s390_single_qi (operands[1], SImode, 0) >= 0" - "* -{ - int part = s390_single_qi (operands[1], SImode, 0); - operands[1] = GEN_INT (s390_extract_qi (operands[1], SImode, part)); + (compare (match_operand:HI 0 "register_operand" "d") + (match_operand:HI 1 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (GEN_INT (-1), operands[1], 1))" + "tml\\t%0,65535" + [(set_attr "op_type" "RX")]) + +(define_insn "*tmqi_full" + [(set (reg 33) + (compare (match_operand:QI 0 "register_operand" "d") + (match_operand:QI 1 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (GEN_INT (-1), operands[1], 1))" + "tml\\t%0,255" + [(set_attr "op_type" "RI")]) - operands[0] = gen_rtx_MEM (QImode, - plus_constant (XEXP (operands[0], 0), part)); - return \"tm\\t%0,%b1\"; -}" - [(set_attr "op_type" "SI") - (set_attr "atype" "mem")]) -(define_insn "*ltr" +; Load-and-Test instructions + +(define_insn "*tstdi_sign" [(set (reg 33) - (compare (match_operand:SI 0 "register_operand" "d") - (match_operand:SI 1 "const0_operand" ""))) - (set (match_operand:SI 2 "register_operand" "=d") + (compare (ashiftrt:DI (ashift:DI (subreg:DI (match_operand:SI 0 "register_operand" "d") 0) + (const_int 32)) (const_int 32)) + (match_operand:DI 1 "const0_operand" ""))) + (set (match_operand:DI 2 "register_operand" "=d") + (sign_extend:DI (match_dup 0)))] + "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" + "ltgfr\\t%2,%0" + [(set_attr "op_type" "RRE")]) + +(define_insn "*tstdi" + [(set (reg 33) + (compare (match_operand:DI 0 "register_operand" "d") + (match_operand:DI 1 "const0_operand" ""))) + (set (match_operand:DI 2 "register_operand" "=d") (match_dup 0))] - "s390_match_ccmode(insn, CCSmode)" - "ltr\\t%2,%0" - [(set_attr "op_type" "RR")]) + "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" + "ltgr\\t%2,%0" + [(set_attr "op_type" "RRE")]) -(define_insn "*icm15" +(define_insn "*tstdi_cconly" [(set (reg 33) - (compare (match_operand:SI 0 "s_operand" "Qo") + (compare (match_operand:DI 0 "register_operand" "d") + (match_operand:DI 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" + "ltgr\\t%0,%0" + [(set_attr "op_type" "RRE")]) + +(define_insn "*tstdi_cconly_31" + [(set (reg 33) + (compare (match_operand:DI 0 "register_operand" "d") + (match_operand:DI 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCSmode) && !TARGET_64BIT" + "srda\\t%0,0" + [(set_attr "op_type" "RS")]) + +(define_insn "*tstsi" + [(set (reg 33) + (compare (match_operand:SI 0 "nonimmediate_operand" "d,Q") (match_operand:SI 1 "const0_operand" ""))) - (set (match_operand:SI 2 "register_operand" "=d") + (set (match_operand:SI 2 "register_operand" "=d,d") (match_dup 0))] "s390_match_ccmode(insn, CCSmode)" - "icm\\t%2,15,%0" - [(set_attr "op_type" "RS") - (set_attr "atype" "mem")]) + "@ + ltr\\t%2,%0 + icm\\t%2,15,%0" + [(set_attr "op_type" "RR,RS") + (set_attr "atype" "reg,mem")]) -(define_insn "*icm15_cconly" +(define_insn "*tstsi_cconly" [(set (reg 33) - (compare (match_operand:SI 0 "s_operand" "Qo") + (compare (match_operand:SI 0 "nonimmediate_operand" "d,Q") (match_operand:SI 1 "const0_operand" ""))) - (clobber (match_scratch:SI 2 "=d"))] + (clobber (match_scratch:SI 2 "=X,d"))] "s390_match_ccmode(insn, CCSmode)" - "icm\\t%2,15,%0" - [(set_attr "op_type" "RS") - (set_attr "atype" "mem")]) + "@ + ltr\\t%0,%0 + icm\\t%2,15,%0" + [(set_attr "op_type" "RR,RS") + (set_attr "atype" "reg,mem")]) -(define_insn "*cmpsi_ccs_0" +(define_insn "*tstsi_cconly2" [(set (reg 33) (compare (match_operand:SI 0 "register_operand" "d") (match_operand:SI 1 "const0_operand" "")))] @@ -481,72 +528,34 @@ "ltr\\t%0,%0" [(set_attr "op_type" "RR")]) -(define_insn "*cmpsidi_ccs" - [(set (reg 33) - (compare (match_operand:SI 0 "register_operand" "d") - (sign_extend:SI (match_operand:HI 1 "memory_operand" "m"))))] - "s390_match_ccmode(insn, CCSmode)" - "ch\\t%0,%1" - [(set_attr "op_type" "RR") - (set_attr "atype" "mem")]) - -(define_insn "*cmpsi_ccs" - [(set (reg 33) - (compare (match_operand:SI 0 "register_operand" "d,d,d") - (match_operand:SI 1 "general_operand" "d,K,m")))] - "s390_match_ccmode(insn, CCSmode)" - "@ - cr\\t%0,%1 - chi\\t%0,%c1 - c\\t%0,%1" - [(set_attr "op_type" "RR,RI,RX") - (set_attr "atype" "reg,reg,mem")]) - -(define_insn "*cmpsi_ccu" +(define_insn "*tsthiCCT" [(set (reg 33) - (compare (match_operand:SI 0 "register_operand" "d,d") - (match_operand:SI 1 "general_operand" "d,m")))] - "s390_match_ccmode(insn, CCUmode)" + (compare (match_operand:HI 0 "nonimmediate_operand" "?Q,d") + (match_operand:HI 1 "const0_operand" ""))) + (set (match_operand:HI 2 "register_operand" "=d,0") + (match_dup 0))] + "s390_match_ccmode(insn, CCTmode)" "@ - clr\\t%0,%1 - cl\\t%0,%1" - [(set_attr "op_type" "RR,RX") - (set_attr "atype" "reg,mem")]) + icm\\t%2,3,%0 + tml\\t%0,65535" + [(set_attr "op_type" "RS,RI") + (set_attr "atype" "mem,reg")]) -(define_insn "*cmpsi_ccu_mem" +(define_insn "*tsthiCCT_cconly" [(set (reg 33) - (compare (match_operand:SI 0 "s_operand" "oQ") - (match_operand:SI 1 "s_imm_operand" "oQ")))] - "s390_match_ccmode(insn, CCUmode)" - "clc\\t%O0(4,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem")]) - - -; HI instructions - -(define_insn "*cmphi_tm_sub" - [(set (reg 33) - (compare (and:SI (subreg:SI (match_operand:HI 0 "s_operand" "%Qo") 0) - (match_operand:SI 1 "immediate_operand" "n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) - && s390_single_qi (operands[1], HImode, 0) >= 0" - "* -{ - int part = s390_single_qi (operands[1], HImode, 0); - operands[1] = GEN_INT (s390_extract_qi (operands[1], HImode, part)); - - operands[0] = gen_rtx_MEM (QImode, - plus_constant (XEXP (operands[0], 0), part)); - return \"tm\\t%0,%b1\"; -}" - [(set_attr "op_type" "SI") - (set_attr "atype" "mem")]) + (compare (match_operand:HI 0 "nonimmediate_operand" "Q,d") + (match_operand:HI 1 "const0_operand" ""))) + (clobber (match_scratch:HI 2 "=d,X"))] + "s390_match_ccmode(insn, CCTmode)" + "@ + icm\\t%2,3,%0 + tml\\t%0,65535" + [(set_attr "op_type" "RS,RI") + (set_attr "atype" "mem,reg")]) -(define_insn "*icm3" +(define_insn "*tsthi" [(set (reg 33) - (compare (match_operand:HI 0 "s_operand" "Qo") + (compare (match_operand:HI 0 "s_operand" "Q") (match_operand:HI 1 "const0_operand" ""))) (set (match_operand:HI 2 "register_operand" "=d") (match_dup 0))] @@ -555,17 +564,9 @@ [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) -(define_insn "*cmphi_cct_0" +(define_insn "*tsthi_cconly" [(set (reg 33) - (compare (match_operand:HI 0 "register_operand" "d") - (match_operand:HI 1 "const0_operand" "")))] - "s390_match_ccmode(insn, CCTmode)" - "tml\\t%0,65535" - [(set_attr "op_type" "RX")]) - -(define_insn "*cmphi_ccs_0" - [(set (reg 33) - (compare (match_operand:HI 0 "s_operand" "Qo") + (compare (match_operand:HI 0 "s_operand" "Q") (match_operand:HI 1 "const0_operand" ""))) (clobber (match_scratch:HI 2 "=d"))] "s390_match_ccmode(insn, CCSmode)" @@ -573,141 +574,192 @@ [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) -(define_insn "*cmphi_ccu" +(define_insn "*tstqiCCT" [(set (reg 33) - (compare (match_operand:HI 0 "register_operand" "d") - (match_operand:HI 1 "s_imm_operand" "Qo")))] - "s390_match_ccmode(insn, CCUmode)" - "clm\\t%0,3,%1" + (compare (match_operand:QI 0 "nonimmediate_operand" "?Q,d") + (match_operand:QI 1 "const0_operand" ""))) + (set (match_operand:QI 2 "register_operand" "=d,0") + (match_dup 0))] + "s390_match_ccmode(insn, CCTmode)" + "@ + icm\\t%2,1,%0 + tml\\t%0,255" + [(set_attr "op_type" "RS,RI") + (set_attr "atype" "mem,reg")]) + +(define_insn "*tstqiCCT_cconly" + [(set (reg 33) + (compare (match_operand:QI 0 "nonimmediate_operand" "?Q,d") + (match_operand:QI 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCTmode)" + "@ + cli\\t%0,0 + tml\\t%0,255" + [(set_attr "op_type" "SI,RI") + (set_attr "atype" "mem,reg")]) + +(define_insn "*tstqi" + [(set (reg 33) + (compare (match_operand:QI 0 "s_operand" "Q") + (match_operand:QI 1 "const0_operand" ""))) + (set (match_operand:QI 2 "register_operand" "=d") + (match_dup 0))] + "s390_match_ccmode(insn, CCSmode)" + "icm\\t%2,1,%0" [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) -(define_insn "*cmphi_ccu_mem" +(define_insn "*tstqi_cconly" [(set (reg 33) - (compare (match_operand:HI 0 "s_operand" "oQ") - (match_operand:HI 1 "s_imm_operand" "oQ")))] - "s390_match_ccmode(insn, CCUmode)" - "clc\\t%O0(2,%R0),%1" - [(set_attr "op_type" "SS") + (compare (match_operand:QI 0 "s_operand" "Q") + (match_operand:QI 1 "const0_operand" ""))) + (clobber (match_scratch:QI 2 "=d"))] + "s390_match_ccmode(insn, CCSmode)" + "icm\\t%2,1,%0" + [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) -; QI instructions +; Compare (signed) instructions -(define_insn "*cmpqi_tm2" +(define_insn "*cmpdi_ccs_sign" [(set (reg 33) - (compare (zero_extract:SI (match_operand:QI 0 "s_operand" "Qo") - (match_operand:SI 1 "const_int_operand" "n") - (match_operand:SI 2 "const_int_operand" "n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) - && INTVAL (operands[1]) >= 1 && INTVAL (operands[2]) >= 0 - && INTVAL (operands[1]) + INTVAL (operands[2]) <= 8" - "* -{ - int block = (1 << INTVAL (operands[1])) - 1; - int shift = 8 - INTVAL (operands[1]) - INTVAL (operands[2]); - - operands[2] = GEN_INT (block << shift); - return \"tm\\t%0,%b2\"; -}" - [(set_attr "op_type" "SI") - (set_attr "atype" "mem")]) - -(define_insn "*cmpqi_tm" - [(set (reg 33) - (compare (and:QI (match_operand:QI 0 "nonimmediate_operand" "%d,Q") - (match_operand:QI 1 "immediate_operand" "n,n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode)" + (compare (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,m")) + (match_operand:DI 0 "register_operand" "d,d")))] + "s390_match_ccmode(insn, CCSRmode) && TARGET_64BIT" "@ - tml\\t%0,%b1 - tm\\t%0,%b1" - [(set_attr "op_type" "RI,SI") + cgfr\\t%0,%1 + cgf\\t%0,%1" + [(set_attr "op_type" "RRE,RXE") (set_attr "atype" "reg,mem")]) -(define_insn "*cmpqi_tm_sub" +(define_insn "*cmpdi_ccs" [(set (reg 33) - (compare (and:SI (subreg:SI (match_operand:QI 0 "s_operand" "%Qo") 0) - (match_operand:SI 1 "immediate_operand" "n")) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode)" - "tm\\t%0,%b1" - [(set_attr "op_type" "SI") + (compare (match_operand:DI 0 "register_operand" "d,d,d") + (match_operand:DI 1 "general_operand" "d,K,m")))] + "s390_match_ccmode(insn, CCSmode) && TARGET_64BIT" + "@ + cgr\\t%0,%1 + cghi\\t%0,%c1 + cg\\t%0,%1" + [(set_attr "op_type" "RRE,RI,RXE") + (set_attr "atype" "reg,reg,mem")]) + +(define_insn "*cmpsi_ccs_sign" + [(set (reg 33) + (compare (sign_extend:SI (match_operand:HI 1 "memory_operand" "m")) + (match_operand:SI 0 "register_operand" "d")))] + "s390_match_ccmode(insn, CCSRmode)" + "ch\\t%0,%1" + [(set_attr "op_type" "RX") (set_attr "atype" "mem")]) -(define_insn "*icm1" +(define_insn "*cmpsi_ccs" [(set (reg 33) - (compare (match_operand:QI 0 "s_operand" "Qo") - (match_operand:QI 1 "const0_operand" ""))) - (set (match_operand:QI 2 "register_operand" "=d") - (match_dup 0))] + (compare (match_operand:SI 0 "register_operand" "d,d,d") + (match_operand:SI 1 "general_operand" "d,K,m")))] "s390_match_ccmode(insn, CCSmode)" - "icm\\t%2,1,%0" - [(set_attr "op_type" "RS") - (set_attr "atype" "mem")]) + "@ + cr\\t%0,%1 + chi\\t%0,%c1 + c\\t%0,%1" + [(set_attr "op_type" "RR,RI,RX") + (set_attr "atype" "reg,reg,mem")]) + -(define_insn "*tm_0" +; Compare (unsigned) instructions + +(define_insn "*cmpdi_ccu_zero" [(set (reg 33) - (compare (zero_extend:SI (and:QI (match_operand:QI 0 "s_operand" "Qo") - (match_operand:QI 1 "immediate_operand" ""))) - (const_int 0)))] - "s390_match_ccmode(insn, CCTmode) && - INTVAL(operands[1]) >= 0 && INTVAL(operands[1]) < 256" - "tm\\t%0,%1" - [(set_attr "op_type" "RI") - (set_attr "atype" "mem")]) + (compare (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,m")) + (match_operand:DI 0 "register_operand" "d,d")))] + "s390_match_ccmode(insn, CCURmode) && TARGET_64BIT" + "@ + clgfr\\t%0,%1 + clgf\\t%0,%1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) -(define_insn "*cmpqi_cct_0" +(define_insn "*cmpdi_ccu" [(set (reg 33) - (compare (match_operand:QI 0 "register_operand" "d") - (match_operand:QI 1 "const0_operand" "")))] - "s390_match_ccmode(insn, CCTmode)" - "tml\\t%0,255" - [(set_attr "op_type" "RI")]) + (compare (match_operand:DI 0 "register_operand" "d,d") + (match_operand:DI 1 "general_operand" "d,m")))] + "s390_match_ccmode(insn, CCUmode) && TARGET_64BIT" + "@ + clgr\\t%0,%1 + clg\\t%0,%1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) -(define_insn "*cmpqi_ccs_0" +(define_insn "*cmpsi_ccu" [(set (reg 33) - (compare (match_operand:QI 0 "s_operand" "Qo") - (match_operand:QI 1 "const0_operand" ""))) - (clobber (match_scratch:QI 2 "=d"))] - "s390_match_ccmode(insn, CCSmode)" - "icm\\t%2,1,%0" - [(set_attr "op_type" "RS") - (set_attr "atype" "mem")]) + (compare (match_operand:SI 0 "register_operand" "d,d") + (match_operand:SI 1 "general_operand" "d,m")))] + "s390_match_ccmode(insn, CCUmode)" + "@ + clr\\t%0,%1 + cl\\t%0,%1" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) -(define_insn "*cmpqi_ccu_0" +(define_insn "*cmphi_ccu" [(set (reg 33) - (compare (match_operand:QI 0 "s_operand" "Qo") - (match_operand:QI 1 "const0_operand" "")))] + (compare (match_operand:HI 0 "register_operand" "d") + (match_operand:HI 1 "s_imm_operand" "Q")))] "s390_match_ccmode(insn, CCUmode)" - "cli\\t%0,0" - [(set_attr "op_type" "SI") + "clm\\t%0,3,%1" + [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) (define_insn "*cmpqi_ccu" [(set (reg 33) (compare (match_operand:QI 0 "register_operand" "d") - (match_operand:QI 1 "s_imm_operand" "Qo")))] + (match_operand:QI 1 "s_imm_operand" "Q")))] "s390_match_ccmode(insn, CCUmode)" "clm\\t%0,1,%1" [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) -(define_insn "*cmpqi_ccu_immed" +(define_insn "*cli" [(set (reg 33) - (compare (match_operand:QI 0 "s_operand" "Qo") - (match_operand:QI 1 "const_int_operand" "n")))] - "s390_match_ccmode(insn, CCUmode) && - INTVAL(operands[1]) >= 0 && INTVAL(operands[1]) < 256" - "cli\\t%0,%1" + (compare (match_operand:QI 0 "memory_operand" "Q") + (match_operand:QI 1 "immediate_operand" "n")))] + "s390_match_ccmode (insn, CCUmode)" + "cli\\t%0,%b1" [(set_attr "op_type" "SI") (set_attr "atype" "mem")]) +(define_insn "*cmpdi_ccu_mem" + [(set (reg 33) + (compare (match_operand:DI 0 "s_operand" "Q") + (match_operand:DI 1 "s_imm_operand" "Q")))] + "s390_match_ccmode(insn, CCUmode)" + "clc\\t%O0(8,%R0),%1" + [(set_attr "op_type" "SS") + (set_attr "atype" "mem")]) + +(define_insn "*cmpsi_ccu_mem" + [(set (reg 33) + (compare (match_operand:SI 0 "s_operand" "Q") + (match_operand:SI 1 "s_imm_operand" "Q")))] + "s390_match_ccmode(insn, CCUmode)" + "clc\\t%O0(4,%R0),%1" + [(set_attr "op_type" "SS") + (set_attr "atype" "mem")]) + +(define_insn "*cmphi_ccu_mem" + [(set (reg 33) + (compare (match_operand:HI 0 "s_operand" "Q") + (match_operand:HI 1 "s_imm_operand" "Q")))] + "s390_match_ccmode(insn, CCUmode)" + "clc\\t%O0(2,%R0),%1" + [(set_attr "op_type" "SS") + (set_attr "atype" "mem")]) + (define_insn "*cmpqi_ccu_mem" [(set (reg 33) - (compare (match_operand:QI 0 "s_operand" "oQ") - (match_operand:QI 1 "s_imm_operand" "oQ")))] + (compare (match_operand:QI 0 "s_operand" "Q") + (match_operand:QI 1 "s_imm_operand" "Q")))] "s390_match_ccmode(insn, CCUmode)" "clc\\t%O0(1,%R0),%1" [(set_attr "op_type" "SS") @@ -804,94 +856,91 @@ ; movti instruction pattern(s). ; -(define_insn "*movti_ss" - [(set (match_operand:TI 0 "s_operand" "=Qo") - (match_operand:TI 1 "s_imm_operand" "Qo"))] - "" - "mvc\\t%O0(16,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem")]) - (define_insn "movti" - [(set (match_operand:TI 0 "nonimmediate_operand" "=d,Q,d,m") - (match_operand:TI 1 "general_operand" "Q,d,dKm,d"))] + [(set (match_operand:TI 0 "nonimmediate_operand" "=d,Q,d,o,Q") + (match_operand:TI 1 "general_operand" "Q,d,dKm,d,Q"))] "TARGET_64BIT" "@ lmg\\t%0,%N0,%1 stmg\\t%1,%N1,%0 # - #" - [(set_attr "op_type" "RSE,RSE,NN,NN") + # + mvc\\t%O0(16,%R0),%1" + [(set_attr "op_type" "RSE,RSE,NN,NN,SS") (set_attr "atype" "mem")]) (define_split [(set (match_operand:TI 0 "nonimmediate_operand" "") (match_operand:TI 1 "general_operand" ""))] "TARGET_64BIT && reload_completed - && !s_operand (operands[0], VOIDmode) - && !s_operand (operands[1], VOIDmode) - && (register_operand (operands[0], VOIDmode) - || register_operand (operands[1], VOIDmode)) - && (!register_operand (operands[0], VOIDmode) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 0, 0, TImode), - operands[1]) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 1, 0, TImode), - operands[1]))" + && s390_split_ok_p (operands[0], operands[1], TImode, 0)" [(set (match_dup 2) (match_dup 4)) (set (match_dup 3) (match_dup 5))] - " { - if (!register_operand (operands[0], VOIDmode) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 0, 0, TImode), - operands[1])) - { - operands[2] = operand_subword (operands[0], 0, 0, TImode); - operands[3] = operand_subword (operands[0], 1, 0, TImode); - operands[4] = operand_subword (operands[1], 0, 0, TImode); - operands[5] = operand_subword (operands[1], 1, 0, TImode); - } - else - { - operands[2] = operand_subword (operands[0], 1, 0, TImode); - operands[3] = operand_subword (operands[0], 0, 0, TImode); - operands[4] = operand_subword (operands[1], 1, 0, TImode); - operands[5] = operand_subword (operands[1], 0, 0, TImode); - } -}") + operands[2] = operand_subword (operands[0], 0, 0, TImode); + operands[3] = operand_subword (operands[0], 1, 0, TImode); + operands[4] = operand_subword (operands[1], 0, 0, TImode); + operands[5] = operand_subword (operands[1], 1, 0, TImode); +}) + +(define_split + [(set (match_operand:TI 0 "nonimmediate_operand" "") + (match_operand:TI 1 "general_operand" ""))] + "TARGET_64BIT && reload_completed + && s390_split_ok_p (operands[0], operands[1], TImode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, TImode); + operands[3] = operand_subword (operands[0], 0, 0, TImode); + operands[4] = operand_subword (operands[1], 1, 0, TImode); + operands[5] = operand_subword (operands[1], 0, 0, TImode); +}) (define_split [(set (match_operand:TI 0 "register_operand" "") (match_operand:TI 1 "memory_operand" ""))] "TARGET_64BIT && reload_completed && !s_operand (operands[1], VOIDmode)" - [(set (match_dup 2) (match_dup 3)) - (set (match_dup 0) (mem:TI (match_dup 2)))] - "operands[2] = operand_subword (operands[0], 1, 0, TImode); - operands[3] = legitimize_la_operand (XEXP (operands[1], 0));") + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, TImode); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + +(define_expand "reload_outti" + [(parallel [(match_operand:TI 0 "memory_operand" "") + (match_operand:TI 1 "register_operand" "d") + (match_operand:DI 2 "register_operand" "=&a")])] + "TARGET_64BIT" +{ + s390_load_address (operands[2], XEXP (operands[0], 0)); + operands[0] = replace_equiv_address (operands[0], operands[2]); + emit_move_insn (operands[0], operands[1]); + DONE; +}) ; ; movdi instruction pattern(s). ; -;; If generating PIC code and operands[1] is a symbolic CONST, emit a -;; move to get the address of the symbolic object from the GOT. - (define_expand "movdi" [(set (match_operand:DI 0 "general_operand" "") (match_operand:DI 1 "general_operand" ""))] "" " { - /* Handle PIC symbolic constants. */ - if (TARGET_64BIT && flag_pic && SYMBOLIC_CONST (operands[1])) - emit_pic_move (operands, DImode); + /* Handle symbolic constants. */ + if (TARGET_64BIT && SYMBOLIC_CONST (operands[1])) + emit_symbolic_move (operands); /* During and after reload, we need to force constants to the literal pool ourselves, if necessary. */ if ((reload_in_progress || reload_completed) && CONSTANT_P (operands[1]) && (!legitimate_reload_constant_p (operands[1]) - || fp_operand (operands[0], VOIDmode))) + || FP_REG_P (operands[0]))) operands[1] = force_const_mem (DImode, operands[1]); }") @@ -901,7 +950,7 @@ "TARGET_64BIT && GET_CODE (operands[1]) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'K') - && !fp_operand (operands[0], VOIDmode)" + && !FP_REG_P (operands[0])" "lghi\\t%0,%h1" [(set_attr "op_type" "RI") (set_attr "atype" "reg")]) @@ -910,7 +959,7 @@ [(set (match_operand:DI 0 "register_operand" "=d") (match_operand:DI 1 "immediate_operand" "n"))] "TARGET_64BIT && s390_single_hi (operands[1], DImode, 0) >= 0 - && !fp_operand (operands[0], VOIDmode)" + && !FP_REG_P (operands[0])" "* { int part = s390_single_hi (operands[1], DImode, 0); @@ -932,23 +981,15 @@ [(set (match_operand:DI 0 "register_operand" "=d") (match_operand:DI 1 "larl_operand" "X"))] "TARGET_64BIT - && !fp_operand (operands[0], VOIDmode)" + && !FP_REG_P (operands[0])" "larl\\t%0,%1" [(set_attr "op_type" "RIL") (set_attr "atype" "reg") (set_attr "type" "la")]) -(define_insn "*movdi_ss" - [(set (match_operand:DI 0 "s_operand" "=Qo") - (match_operand:DI 1 "s_imm_operand" "Qo"))] - "" - "mvc\\t%O0(8,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem")]) - (define_insn "*movdi_64" - [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,m,!*f,!*f,!m") - (match_operand:DI 1 "general_operand" "d,m,d,*f,m,*f"))] + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,m,!*f,!*f,!m,?Q") + (match_operand:DI 1 "general_operand" "d,m,d,*f,m,*f,?Q"))] "TARGET_64BIT" "@ lgr\\t%0,%1 @@ -956,13 +997,14 @@ stg\\t%1,%0 ldr\\t%0,%1 ld\\t%0,%1 - std\\t%1,%0" - [(set_attr "op_type" "RRE,RXE,RXE,RR,RX,RX") - (set_attr "atype" "reg,mem,mem,reg,mem,mem")]) + std\\t%1,%0 + mvc\\t%O0(8,%R0),%1" + [(set_attr "op_type" "RRE,RXE,RXE,RR,RX,RX,SS") + (set_attr "atype" "reg,mem,mem,reg,mem,mem,mem")]) (define_insn "*movdi_31" - [(set (match_operand:DI 0 "nonimmediate_operand" "=d,Q,d,m,!*f,!*f,!m") - (match_operand:DI 1 "general_operand" "Q,d,dKm,d,*f,m,*f"))] + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,Q,d,o,!*f,!*f,!m,Q") + (match_operand:DI 1 "general_operand" "Q,d,dKm,d,*f,m,*f,Q"))] "!TARGET_64BIT" "@ lm\\t%0,%N0,%1 @@ -971,75 +1013,89 @@ # ldr\\t%0,%1 ld\\t%0,%1 - std\\t%1,%0" - [(set_attr "op_type" "RS,RS,NN,NN,RR,RX,RX") - (set_attr "atype" "mem,mem,*,*,reg,mem,mem")]) + std\\t%1,%0 + mvc\\t%O0(8,%R0),%1" + [(set_attr "op_type" "RS,RS,NN,NN,RR,RX,RX,SS") + (set_attr "atype" "mem,mem,*,*,reg,mem,mem,mem")]) (define_split [(set (match_operand:DI 0 "nonimmediate_operand" "") (match_operand:DI 1 "general_operand" ""))] "!TARGET_64BIT && reload_completed - && !fp_operand (operands[0], VOIDmode) - && !fp_operand (operands[1], VOIDmode) - && !s_operand (operands[0], VOIDmode) - && !s_operand (operands[1], VOIDmode) - && (register_operand (operands[0], VOIDmode) - || register_operand (operands[1], VOIDmode)) - && (!register_operand (operands[0], VOIDmode) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 0, 0, DImode), - operands[1]) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 1, 0, DImode), - operands[1]))" + && s390_split_ok_p (operands[0], operands[1], DImode, 0)" [(set (match_dup 2) (match_dup 4)) (set (match_dup 3) (match_dup 5))] - " { - if (!register_operand (operands[0], VOIDmode) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 0, 0, DImode), - operands[1])) - { - operands[2] = operand_subword (operands[0], 0, 0, DImode); - operands[3] = operand_subword (operands[0], 1, 0, DImode); - operands[4] = operand_subword (operands[1], 0, 0, DImode); - operands[5] = operand_subword (operands[1], 1, 0, DImode); - } - else - { - operands[2] = operand_subword (operands[0], 1, 0, DImode); - operands[3] = operand_subword (operands[0], 0, 0, DImode); - operands[4] = operand_subword (operands[1], 1, 0, DImode); - operands[5] = operand_subword (operands[1], 0, 0, DImode); - } -}") + operands[2] = operand_subword (operands[0], 0, 0, DImode); + operands[3] = operand_subword (operands[0], 1, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[1], 1, 0, DImode); +}) + +(define_split + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "!TARGET_64BIT && reload_completed + && s390_split_ok_p (operands[0], operands[1], DImode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, DImode); + operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 1, 0, DImode); + operands[5] = operand_subword (operands[1], 0, 0, DImode); +}) (define_split [(set (match_operand:DI 0 "register_operand" "") (match_operand:DI 1 "memory_operand" ""))] "!TARGET_64BIT && reload_completed - && !fp_operand (operands[0], VOIDmode) - && !fp_operand (operands[1], VOIDmode) + && !FP_REG_P (operands[0]) && !s_operand (operands[1], VOIDmode)" - [(set (match_dup 2) (match_dup 3)) - (set (match_dup 0) (mem:DI (match_dup 2)))] - "operands[2] = operand_subword (operands[0], 1, 0, DImode); - operands[3] = legitimize_la_operand (XEXP (operands[1], 0));") + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, DImode); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + +(define_expand "reload_outdi" + [(parallel [(match_operand:DI 0 "memory_operand" "") + (match_operand:DI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "=&a")])] + "!TARGET_64BIT" +{ + s390_load_address (operands[2], XEXP (operands[0], 0)); + operands[0] = replace_equiv_address (operands[0], operands[2]); + emit_move_insn (operands[0], operands[1]); + DONE; +}) + +(define_peephole2 + [(set (match_operand:DI 0 "register_operand" "") + (mem:DI (match_operand 1 "address_operand" "")))] + "TARGET_64BIT + && !FP_REG_P (operands[0]) + && GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == DImode + && legitimate_reload_constant_p (get_pool_constant (operands[1]))" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") ; ; movsi instruction pattern(s). ; -;; If generating PIC code and operands[1] is a symbolic CONST, emit a -;; move to get the address of the symbolic object from the GOT. - (define_expand "movsi" [(set (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))] "" " { - /* Handle PIC symbolic constants. */ - if (!TARGET_64BIT && flag_pic && SYMBOLIC_CONST (operands[1])) - emit_pic_move (operands, SImode); + /* Handle symbolic constants. */ + if (!TARGET_64BIT && SYMBOLIC_CONST (operands[1])) + emit_symbolic_move (operands); /* expr.c tries to load an effective address using force_reg. This fails because we don't have a @@ -1059,7 +1115,7 @@ if ((reload_in_progress || reload_completed) && CONSTANT_P (operands[1]) && (!legitimate_reload_constant_p (operands[1]) - || fp_operand (operands[0], VOIDmode))) + || FP_REG_P (operands[0]))) operands[1] = force_const_mem (SImode, operands[1]); }") @@ -1068,7 +1124,7 @@ (match_operand:SI 1 "immediate_operand" "K"))] "GET_CODE (operands[1]) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'K') - && !fp_operand (operands[0], VOIDmode)" + && !FP_REG_P (operands[0])" "lhi\\t%0,%h1" [(set_attr "op_type" "RI")]) @@ -1076,7 +1132,7 @@ [(set (match_operand:SI 0 "register_operand" "=d") (match_operand:SI 1 "immediate_operand" "n"))] "TARGET_64BIT && s390_single_hi (operands[1], SImode, 0) >= 0 - && !fp_operand (operands[0], VOIDmode)" + && !FP_REG_P (operands[0])" "* { int part = s390_single_hi (operands[1], SImode, 0); @@ -1091,17 +1147,9 @@ }" [(set_attr "op_type" "RI")]) -(define_insn "*movsi_ss" - [(set (match_operand:SI 0 "s_operand" "=Qo") - (match_operand:SI 1 "s_imm_operand" "Qo"))] - "" - "mvc\\t%O0(4,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem")]) - (define_insn "*movsi" - [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,m,!*f,!*f,!m") - (match_operand:SI 1 "general_operand" "d,m,d,*f,m,*f"))] + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,m,!*f,!*f,!m,?Q") + (match_operand:SI 1 "general_operand" "d,m,d,*f,m,*f,?Q"))] "" "@ lr\\t%0,%1 @@ -1109,62 +1157,94 @@ st\\t%1,%0 ler\\t%0,%1 le\\t%0,%1 - ste\\t%1,%0" - [(set_attr "op_type" "RR,RX,RX,RR,RX,RX") - (set_attr "atype" "reg,mem,mem,reg,mem,mem")]) + ste\\t%1,%0 + mvc\\t%O0(4,%R0),%1" + [(set_attr "op_type" "RR,RX,RX,RR,RX,RX,SS") + (set_attr "atype" "reg,mem,mem,reg,mem,mem,mem")]) +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (match_operand 1 "address_operand" "")))] + "!FP_REG_P (operands[0]) + && GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == SImode + && legitimate_reload_constant_p (get_pool_constant (operands[1]))" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") ; ; movhi instruction pattern(s). ; (define_insn "movhi" - [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,m") - (match_operand:HI 1 "general_operand" "d,n,m,d"))] + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,m,?Q") + (match_operand:HI 1 "general_operand" "d,n,m,d,?Q"))] "" "@ lr\\t%0,%1 lhi\\t%0,%h1 lh\\t%0,%1 - sth\\t%1,%0" - [(set_attr "op_type" "RR,RI,RX,RX") - (set_attr "atype" "reg,reg,mem,mem")]) + sth\\t%1,%0 + mvc\\t%O0(2,%R0),%1" + [(set_attr "op_type" "RR,RI,RX,RX,SS") + (set_attr "atype" "reg,reg,mem,mem,mem")]) +(define_peephole2 + [(set (match_operand:HI 0 "register_operand" "") + (mem:HI (match_operand 1 "address_operand" "")))] + "GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == HImode + && GET_CODE (get_pool_constant (operands[1])) == CONST_INT" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") ; ; movqi instruction pattern(s). ; (define_insn "movqi_64" - [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,m,Q") - (match_operand:QI 1 "general_operand" "d,n,m,d,n"))] + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,m,Q,?Q") + (match_operand:QI 1 "general_operand" "d,n,m,d,n,?Q"))] "TARGET_64BIT" "@ lr\\t%0,%1 lhi\\t%0,%b1 llgc\\t%0,%1 stc\\t%1,%0 - mvi\\t%0,%b1" - [(set_attr "op_type" "RR,RI,RXE,RX,SI") - (set_attr "atype" "reg,reg,mem,mem,mem")]) + mvi\\t%0,%b1 + mvc\\t%O0(1,%R0),%1" + [(set_attr "op_type" "RR,RI,RXE,RX,SI,SS") + (set_attr "atype" "reg,reg,mem,mem,mem,mem")]) (define_insn "movqi" - [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,m,Q") - (match_operand:QI 1 "general_operand" "d,n,m,d,n"))] + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,m,Q,?Q") + (match_operand:QI 1 "general_operand" "d,n,m,d,n,?Q"))] "" "@ lr\\t%0,%1 lhi\\t%0,%b1 ic\\t%0,%1 stc\\t%1,%0 - mvi\\t%0,%b1" - [(set_attr "op_type" "RR,RI,RX,RX,SI") - (set_attr "atype" "reg,reg,mem,mem,mem")]) + mvi\\t%0,%b1 + mvc\\t%O0(1,%R0),%1" + [(set_attr "op_type" "RR,RI,RX,RX,SI,SS") + (set_attr "atype" "reg,reg,mem,mem,mem,mem")]) +(define_peephole2 + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (mem:QI (match_operand 1 "address_operand" "")))] + "GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == QImode + && GET_CODE (get_pool_constant (operands[1])) == CONST_INT" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") ; -; moveqstrictqi instruction pattern(s). +; movstrictqi instruction pattern(s). ; (define_insn "*movstrictqi" @@ -1181,7 +1261,7 @@ (define_insn "*movstricthi" [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d")) - (match_operand:HI 1 "s_imm_operand" "Qo")) + (match_operand:HI 1 "s_imm_operand" "Q")) (clobber (reg:CC 33))] "" "icm\\t%0,3,%1" @@ -1193,7 +1273,7 @@ ; movstrictsi instruction pattern(s). ; -(define_insn "movestrictsi" +(define_insn "movstrictsi" [(set (strict_low_part (match_operand:SI 0 "register_operand" "+d,d")) (match_operand:SI 1 "general_operand" "d,m"))] "TARGET_64BIT" @@ -1221,17 +1301,9 @@ operands[1] = force_const_mem (DFmode, operands[1]); }") -(define_insn "*movdf_ss" - [(set (match_operand:DF 0 "s_operand" "=Qo") - (match_operand:DF 1 "s_imm_operand" "Qo"))] - "" - "mvc\\t%O0(8,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem")]) - (define_insn "*movdf_64" - [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,m,d,d,m") - (match_operand:DF 1 "general_operand" "f,m,f,d,m,d"))] + [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,m,d,d,m,?Q") + (match_operand:DF 1 "general_operand" "f,m,f,d,m,d,?Q"))] "TARGET_64BIT" "@ ldr\\t%0,%1 @@ -1239,13 +1311,14 @@ std\\t%1,%0 lgr\\t%0,%1 lg\\t%0,%1 - stg\\t%1,%0" - [(set_attr "op_type" "RR,RX,RX,RRE,RXE,RXE") - (set_attr "atype" "reg,mem,mem,reg,mem,mem")]) + stg\\t%1,%0 + mvc\\t%O0(8,%R0),%1" + [(set_attr "op_type" "RR,RX,RX,RRE,RXE,RXE,SS") + (set_attr "atype" "reg,mem,mem,reg,mem,mem,mem")]) (define_insn "*movdf_31" - [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,m,d,Q,d,m") - (match_operand:DF 1 "general_operand" "f,m,f,Q,d,dKm,d"))] + [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,m,d,Q,d,o,Q") + (match_operand:DF 1 "general_operand" "f,m,f,Q,d,dKm,d,Q"))] "!TARGET_64BIT" "@ ldr\\t%0,%1 @@ -1254,58 +1327,63 @@ lm\\t%0,%N0,%1 stm\\t%1,%N1,%0 # - #" - [(set_attr "op_type" "RR,RX,RX,RS,RS,NN,NN") - (set_attr "atype" "reg,mem,mem,mem,mem,*,*")]) + # + mvc\\t%O0(8,%R0),%1" + [(set_attr "op_type" "RR,RX,RX,RS,RS,NN,NN,SS") + (set_attr "atype" "reg,mem,mem,mem,mem,*,*,mem")]) (define_split [(set (match_operand:DF 0 "nonimmediate_operand" "") (match_operand:DF 1 "general_operand" ""))] "!TARGET_64BIT && reload_completed - && !fp_operand (operands[0], VOIDmode) - && !fp_operand (operands[1], VOIDmode) - && !s_operand (operands[0], VOIDmode) - && !s_operand (operands[1], VOIDmode) - && (register_operand (operands[0], VOIDmode) - || register_operand (operands[1], VOIDmode)) - && (!register_operand (operands[0], VOIDmode) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 0, 0, DFmode), - operands[1]) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 1, 0, DFmode), - operands[1]))" + && s390_split_ok_p (operands[0], operands[1], DFmode, 0)" [(set (match_dup 2) (match_dup 4)) (set (match_dup 3) (match_dup 5))] - " { - if (!register_operand (operands[0], VOIDmode) - || !reg_overlap_mentioned_p (operand_subword (operands[0], 0, 0, DFmode), - operands[1])) - { - operands[2] = operand_subword (operands[0], 0, 0, DFmode); - operands[3] = operand_subword (operands[0], 1, 0, DFmode); - operands[4] = operand_subword (operands[1], 0, 0, DFmode); - operands[5] = operand_subword (operands[1], 1, 0, DFmode); - } - else - { - operands[2] = operand_subword (operands[0], 1, 0, DFmode); - operands[3] = operand_subword (operands[0], 0, 0, DFmode); - operands[4] = operand_subword (operands[1], 1, 0, DFmode); - operands[5] = operand_subword (operands[1], 0, 0, DFmode); - } -}") + operands[2] = operand_subword (operands[0], 0, 0, DFmode); + operands[3] = operand_subword (operands[0], 1, 0, DFmode); + operands[4] = operand_subword (operands[1], 0, 0, DFmode); + operands[5] = operand_subword (operands[1], 1, 0, DFmode); +}) + +(define_split + [(set (match_operand:DF 0 "nonimmediate_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "!TARGET_64BIT && reload_completed + && s390_split_ok_p (operands[0], operands[1], DFmode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, DFmode); + operands[3] = operand_subword (operands[0], 0, 0, DFmode); + operands[4] = operand_subword (operands[1], 1, 0, DFmode); + operands[5] = operand_subword (operands[1], 0, 0, DFmode); +}) (define_split [(set (match_operand:DF 0 "register_operand" "") (match_operand:DF 1 "memory_operand" ""))] "!TARGET_64BIT && reload_completed - && !fp_operand (operands[0], VOIDmode) - && !fp_operand (operands[1], VOIDmode) + && !FP_REG_P (operands[0]) && !s_operand (operands[1], VOIDmode)" - [(set (match_dup 2) (match_dup 3)) - (set (match_dup 0) (mem:DI (match_dup 2)))] - "operands[2] = operand_subword (operands[0], 1, 0, DFmode); - operands[3] = legitimize_la_operand (XEXP (operands[1], 0));") + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, DFmode); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + +(define_expand "reload_outdf" + [(parallel [(match_operand:DF 0 "memory_operand" "") + (match_operand:DF 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "=&a")])] + "!TARGET_64BIT" +{ + s390_load_address (operands[2], XEXP (operands[0], 0)); + operands[0] = replace_equiv_address (operands[0], operands[2]); + emit_move_insn (operands[0], operands[1]); + DONE; +}) ; ; movsf instruction pattern(s). @@ -1324,17 +1402,9 @@ operands[1] = force_const_mem (SFmode, operands[1]); }") -(define_insn "*movsf_ss" - [(set (match_operand:SF 0 "s_operand" "=Qo") - (match_operand:SF 1 "s_imm_operand" "Qo"))] - "" - "mvc\\t%O0(4,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem")]) - (define_insn "*movsf" - [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,m,d,d,m") - (match_operand:SF 1 "general_operand" "f,m,f,d,m,d"))] + [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,m,d,d,m,?Q") + (match_operand:SF 1 "general_operand" "f,m,f,d,m,d,?Q"))] "" "@ ler\\t%0,%1 @@ -1342,9 +1412,10 @@ ste\\t%1,%0 lr\\t%0,%1 l\\t%0,%1 - st\\t%1,%0" - [(set_attr "op_type" "RR,RX,RX,RR,RX,RX") - (set_attr "atype" "reg,mem,mem,reg,mem,mem")]) + st\\t%1,%0 + mvc\\t%O0(4,%R0),%1" + [(set_attr "op_type" "RR,RX,RX,RR,RX,RX,SS") + (set_attr "atype" "reg,mem,mem,reg,mem,mem,mem")]) ; ; load_multiple pattern(s). @@ -1413,7 +1484,7 @@ (define_insn "*load_multiple_di" [(match_parallel 0 "load_multiple_operation" [(set (match_operand:DI 1 "register_operand" "=r") - (match_operand:DI 2 "s_operand" "oQ"))])] + (match_operand:DI 2 "s_operand" "Q"))])] "" "* { @@ -1432,7 +1503,7 @@ (define_insn "*load_multiple_si" [(match_parallel 0 "load_multiple_operation" [(set (match_operand:SI 1 "register_operand" "=r") - (match_operand:SI 2 "s_operand" "oQ"))])] + (match_operand:SI 2 "s_operand" "Q"))])] "" "* { @@ -1516,7 +1587,7 @@ (define_insn "*store_multiple_di" [(match_parallel 0 "store_multiple_operation" - [(set (match_operand:DI 1 "s_operand" "=oQ") + [(set (match_operand:DI 1 "s_operand" "=Q") (match_operand:DI 2 "register_operand" "r"))])] "" "* @@ -1536,7 +1607,7 @@ (define_insn "*store_multiple_si" [(match_parallel 0 "store_multiple_operation" - [(set (match_operand:SI 1 "s_operand" "=oQ") + [(set (match_operand:SI 1 "s_operand" "=Q") (match_operand:SI 2 "register_operand" "r"))])] "" "* @@ -1558,172 +1629,31 @@ ;; ; -; movstrdi instruction pattern(s). +; movstrM instruction pattern(s). ; (define_expand "movstrdi" - [(set (match_operand:BLK 0 "general_operand" "") - (match_operand:BLK 1 "general_operand" "")) - (use (match_operand:DI 2 "general_operand" "")) - (match_operand 3 "" "")] - "TARGET_64BIT" - " -{ - rtx addr0, addr1; - - addr0 = force_operand (XEXP (operands[0], 0), NULL_RTX); - addr1 = force_operand (XEXP (operands[1], 0), NULL_RTX); - - if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) <= 256) - { - operands[0] = change_address (operands[0], VOIDmode, addr0); - operands[1] = change_address (operands[1], VOIDmode, addr1); - operands[2] = GEN_INT (INTVAL (operands[2]) - 1); - - emit_insn (gen_movstrdi_short (operands[0], operands[1], operands[2])); - DONE; - } - else - { - if (TARGET_MVCLE) - { - /* implementation suggested by Richard Henderson <rth@cygnus.com> */ - rtx reg0 = gen_reg_rtx (TImode); - rtx reg1 = gen_reg_rtx (TImode); - rtx len = operands[2]; - - if (! CONSTANT_P (len)) - len = force_reg (DImode, len); - - /* Load up the address+length pairs. */ - - emit_move_insn (gen_highpart (DImode, reg0), addr0); - emit_move_insn (gen_lowpart (DImode, reg0), len); - - emit_move_insn (gen_highpart (DImode, reg1), addr1); - emit_move_insn (gen_lowpart (DImode, reg1), len); - - /* MOVE */ - emit_insn (gen_movstrdi_64 (reg0, reg1, reg0, reg1)); - DONE; - } - else - { - rtx label1 = gen_label_rtx (); - rtx label2 = gen_label_rtx (); - rtx reg0, reg1, len, blocks; - - reg0 = gen_reg_rtx (DImode); - reg1 = gen_reg_rtx (DImode); - len = gen_reg_rtx (DImode); - blocks = gen_reg_rtx (DImode); - - emit_move_insn (len, operands[2]); - emit_insn (gen_cmpdi (len, const0_rtx)); - emit_jump_insn (gen_beq (label1)); - emit_move_insn (reg0, addr0); - emit_move_insn (reg1, addr1); - emit_insn (gen_adddi3 (len, len, constm1_rtx)); - emit_insn (gen_ashrdi3 (blocks, len, GEN_INT (8))); - emit_insn (gen_cmpdi (blocks, const0_rtx)); - emit_jump_insn (gen_beq (label2)); - emit_insn (gen_movstrdi_long (reg0, reg1, reg0, reg1, blocks, blocks)); - emit_label (label2); - operands[0] = change_address (operands[0], VOIDmode, reg0); - operands[1] = change_address (operands[1], VOIDmode, reg1); - emit_insn (gen_movstrdi_short (operands[0], operands[1], len)); - emit_label (label1); - DONE; - } - } -}") - -; -; movstrsi instruction pattern(s). -; + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand:DI 2 "general_operand" "")) + (match_operand 3 "" "")] + "TARGET_64BIT" + "s390_expand_movstr (operands[0], operands[1], operands[2]); DONE;") (define_expand "movstrsi" - [(set (match_operand:BLK 0 "general_operand" "") - (match_operand:BLK 1 "general_operand" "")) - (use (match_operand:SI 2 "general_operand" "")) - (match_operand 3 "" "")] - "!TARGET_64BIT" - " -{ - rtx addr0 = force_operand (XEXP (operands[0], 0), NULL_RTX); - rtx addr1 = force_operand (XEXP (operands[1], 0), NULL_RTX); - - if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) <= 256) - { - operands[0] = change_address (operands[0], VOIDmode, addr0); - operands[1] = change_address (operands[1], VOIDmode, addr1); - operands[2] = GEN_INT (INTVAL (operands[2]) - 1); - - emit_insn (gen_movstrsi_short (operands[0], operands[1], operands[2])); - DONE; - } - else - { - if (TARGET_MVCLE) - { - /* implementation suggested by Richard Henderson <rth@cygnus.com> */ - rtx reg0 = gen_reg_rtx (DImode); - rtx reg1 = gen_reg_rtx (DImode); - rtx len = operands[2]; - - - if (! CONSTANT_P (len)) - len = force_reg (SImode, len); - - /* Load up the address+length pairs. */ - - emit_move_insn (gen_highpart (SImode, reg0), addr0); - emit_move_insn (gen_lowpart (SImode, reg0), len); - - emit_move_insn (gen_highpart (SImode, reg1), addr1); - emit_move_insn (gen_lowpart (SImode, reg1), len); - - /* MOVE */ - emit_insn (gen_movstrsi_31 (reg0, reg1, reg0, reg1)); - DONE; - } - else - { - rtx label1 = gen_label_rtx (); - rtx label2 = gen_label_rtx (); - rtx reg0, reg1, len, blocks; - - reg0 = gen_reg_rtx (SImode); - reg1 = gen_reg_rtx (SImode); - len = gen_reg_rtx (SImode); - blocks = gen_reg_rtx (SImode); - - emit_move_insn (len, operands[2]); - emit_insn (gen_cmpsi (len, const0_rtx)); - emit_jump_insn (gen_beq (label1)); - emit_move_insn (reg0, addr0); - emit_move_insn (reg1, addr1); - emit_insn (gen_addsi3 (len, len, constm1_rtx)); - emit_insn (gen_ashrsi3 (blocks, len, GEN_INT (8))); - emit_insn (gen_cmpsi (blocks, const0_rtx)); - emit_jump_insn (gen_beq (label2)); - emit_insn (gen_movstrsi_long (reg0, reg1, reg0, reg1, blocks, blocks)); - emit_label (label2); - operands[0] = change_address (operands[0], VOIDmode, reg0); - operands[1] = change_address (operands[1], VOIDmode, reg1); - emit_insn (gen_movstrsi_short (operands[0], operands[1], len)); - emit_label (label1); - DONE; - } - } -}") + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand:SI 2 "general_operand" "")) + (match_operand 3 "" "")] + "" + "s390_expand_movstr (operands[0], operands[1], operands[2]); DONE;") ; Move a block that is up to 256 bytes in length. ; The block length is taken as (operands[2] % 256) + 1. -(define_insn "movstrdi_short" - [(set (match_operand:BLK 0 "s_operand" "=oQ,oQ") - (match_operand:BLK 1 "s_operand" "oQ,oQ")) +(define_insn "movstr_short_64" + [(set (match_operand:BLK 0 "memory_operand" "=Q,Q") + (match_operand:BLK 1 "memory_operand" "Q,Q")) (use (match_operand:DI 2 "nonmemory_operand" "n,a")) (clobber (match_scratch:DI 3 "=X,&a"))] "TARGET_64BIT" @@ -1744,12 +1674,13 @@ } }" [(set_attr "op_type" "SS,NN") + (set_attr "type" "cs,cs") (set_attr "atype" "mem,mem") (set_attr "length" "*,14")]) -(define_insn "movstrsi_short" - [(set (match_operand:BLK 0 "s_operand" "=oQ,oQ") - (match_operand:BLK 1 "s_operand" "oQ,oQ")) +(define_insn "movstr_short_31" + [(set (match_operand:BLK 0 "memory_operand" "=Q,Q") + (match_operand:BLK 1 "memory_operand" "Q,Q")) (use (match_operand:SI 2 "nonmemory_operand" "n,a")) (clobber (match_scratch:SI 3 "=X,&a"))] "!TARGET_64BIT" @@ -1770,64 +1701,13 @@ } }" [(set_attr "op_type" "SS,NN") + (set_attr "type" "cs,cs") (set_attr "atype" "mem,mem") (set_attr "length" "*,14")]) -; Move a block that is a multiple of 256 bytes in length +; Move a block of arbitrary length. -(define_insn "movstrdi_long" - [(set (match_operand:DI 4 "register_operand" "=d") - (const_int 0)) - (set (match_operand:DI 0 "register_operand" "=a") - (plus:DI (match_operand:DI 2 "register_operand" "0") - (ashift:DI (match_operand:DI 5 "register_operand" "4") - (const_int 8)))) - (set (match_operand:DI 1 "register_operand" "=a") - (plus:DI (match_operand:DI 3 "register_operand" "1") - (ashift:DI (match_dup 5) (const_int 8)))) - (set (mem:BLK (match_dup 2)) - (mem:BLK (match_dup 3))) - (use (match_dup 5))] - "TARGET_64BIT" - "* -{ - output_asm_insn (\"mvc\\t0(256,%0),0(%1)\", operands); - output_asm_insn (\"la\\t%0,256(%0)\", operands); - output_asm_insn (\"la\\t%1,256(%1)\", operands); - return \"brct\\t%4,.-14\"; -}" - [(set_attr "op_type" "NN") - (set_attr "atype" "mem") - (set_attr "length" "18")]) - -(define_insn "movstrsi_long" - [(set (match_operand:SI 4 "register_operand" "=d") - (const_int 0)) - (set (match_operand:SI 0 "register_operand" "=a") - (plus:SI (match_operand:SI 2 "register_operand" "0") - (ashift:SI (match_operand:SI 5 "register_operand" "4") - (const_int 8)))) - (set (match_operand:SI 1 "register_operand" "=a") - (plus:SI (match_operand:SI 3 "register_operand" "1") - (ashift:SI (match_dup 5) (const_int 8)))) - (set (mem:BLK (match_dup 2)) - (mem:BLK (match_dup 3))) - (use (match_dup 5))] - "!TARGET_64BIT" - "* -{ - output_asm_insn (\"mvc\\t0(256,%0),0(%1)\", operands); - output_asm_insn (\"la\\t%0,256(%0)\", operands); - output_asm_insn (\"la\\t%1,256(%1)\", operands); - return \"brct\\t%4,.-14\"; -}" - [(set_attr "op_type" "NN") - (set_attr "atype" "mem") - (set_attr "length" "18")]) - -; Move a block that is larger than 255 bytes in length. - -(define_insn "movstrdi_64" +(define_insn "movstr_long_64" [(set (match_operand:TI 0 "register_operand" "=d") (ashift:TI (plus:TI (match_operand:TI 2 "register_operand" "0") (lshiftrt:TI (match_dup 2) (const_int 64))) @@ -1842,10 +1722,11 @@ "TARGET_64BIT" "mvcle\\t%0,%1,0\;jo\\t.-4" [(set_attr "op_type" "NN") + (set_attr "type" "vs") (set_attr "atype" "mem") (set_attr "length" "8")]) -(define_insn "movstrsi_31" +(define_insn "movstr_long_31" [(set (match_operand:DI 0 "register_operand" "=d") (ashift:DI (plus:DI (match_operand:DI 2 "register_operand" "0") (lshiftrt:DI (match_dup 2) (const_int 32))) @@ -1859,112 +1740,93 @@ (clobber (reg:CC 33))] "!TARGET_64BIT" "mvcle\\t%0,%1,0\;jo\\t.-4" - [(set_attr "op_type" "NN") - (set_attr "atype" "mem") - (set_attr "length" "8")]) + [(set_attr "op_type" "NN") + (set_attr "type" "vs") + (set_attr "atype" "mem") + (set_attr "length" "8")]) ; -; clrstrdi instruction pattern(s). +; clrstrM instruction pattern(s). ; (define_expand "clrstrdi" - [(set (match_operand:BLK 0 "general_operand" "") + [(set (match_operand:BLK 0 "memory_operand" "") (const_int 0)) (use (match_operand:DI 1 "general_operand" "")) (match_operand 2 "" "")] "TARGET_64BIT" - " -{ - rtx addr = force_operand (XEXP (operands[0], 0), NULL_RTX); - - operands[0] = change_address (operands[0], VOIDmode, addr); - - if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 256) - { - emit_insn (gen_clrstrsico (operands[0], operands[1])); - DONE; - } - else - { - rtx reg0 = gen_reg_rtx (TImode); - rtx reg1 = gen_reg_rtx (TImode); - rtx len = operands[1]; - - if (! CONSTANT_P (len)) - len = force_reg (DImode, len); - - /* Load up the address+length pairs. */ - - emit_move_insn (gen_highpart (DImode, reg0), addr); - emit_move_insn (gen_lowpart (DImode, reg0), len); - - emit_move_insn (gen_lowpart (DImode, reg1), const0_rtx); - - /* Clear! */ - emit_insn (gen_clrstrsi_64 (reg0, reg1, reg0)); - DONE; - } -}") - -; -; clrstrsi instruction pattern(s). -; + "s390_expand_clrstr (operands[0], operands[1]); DONE;") (define_expand "clrstrsi" - [(set (match_operand:BLK 0 "general_operand" "") + [(set (match_operand:BLK 0 "memory_operand" "") (const_int 0)) (use (match_operand:SI 1 "general_operand" "")) (match_operand 2 "" "")] - "!TARGET_64BIT" - " -{ - rtx addr = force_operand (XEXP (operands[0], 0), NULL_RTX); - - operands[0] = change_address (operands[0], VOIDmode, addr); - - if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 256) - { - emit_insn (gen_clrstrsico (operands[0], operands[1])); - DONE; - } - else - { - rtx reg0 = gen_reg_rtx (DImode); - rtx reg1 = gen_reg_rtx (DImode); - rtx len = operands[1]; - - if (! CONSTANT_P (len)) - len = force_reg (SImode, len); + "" + "s390_expand_clrstr (operands[0], operands[1]); DONE;") - /* Load up the address+length pairs. */ +; Clear a block that is up to 256 bytes in length. +; The block length is taken as (operands[2] % 256) + 1. - emit_move_insn (gen_highpart (SImode, reg0), addr); - emit_move_insn (gen_lowpart (SImode, reg0), len); +(define_insn "clrstr_short_64" + [(set (match_operand:BLK 0 "memory_operand" "=Q,Q") + (const_int 0)) + (use (match_operand:DI 1 "nonmemory_operand" "n,a")) + (clobber (match_scratch:DI 2 "=X,&a")) + (clobber (reg:CC 33))] + "TARGET_64BIT" + "* +{ + switch (which_alternative) + { + case 0: + return \"xc\\t%O0(%b1+1,%R0),%0\"; - emit_move_insn (gen_lowpart (SImode, reg1), const0_rtx); - - /* CLear! */ - emit_insn (gen_clrstrsi_31 (reg0, reg1, reg0)); - DONE; - } -}") + case 1: + output_asm_insn (\"bras\\t%2,.+10\", operands); + output_asm_insn (\"xc\\t%O0(1,%R0),%0\", operands); + return \"ex\\t%1,0(%2)\"; -; Clear memory with length less than 256 bytes + default: + abort (); + } +}" + [(set_attr "op_type" "SS,NN") + (set_attr "type" "cs,cs") + (set_attr "atype" "mem,mem") + (set_attr "length" "*,14")]) -(define_insn "clrstrsico" - [(set (match_operand:BLK 0 "s_operand" "=Qo") +(define_insn "clrstr_short_31" + [(set (match_operand:BLK 0 "memory_operand" "=Q,Q") (const_int 0)) - (use (match_operand 1 "immediate_operand" "I")) + (use (match_operand:SI 1 "nonmemory_operand" "n,a")) + (clobber (match_scratch:SI 2 "=X,&a")) (clobber (reg:CC 33))] - "" - "xc\\t%O0(%1,%R0),%0" - [(set_attr "op_type" "RS") - (set_attr "type" "cs") - (set_attr "atype" "mem")]) + "!TARGET_64BIT" + "* +{ + switch (which_alternative) + { + case 0: + return \"xc\\t%O0(%b1+1,%R0),%0\"; + + case 1: + output_asm_insn (\"bras\\t%2,.+10\", operands); + output_asm_insn (\"xc\\t%O0(1,%R0),%0\", operands); + return \"ex\\t%1,0(%2)\"; + + default: + abort (); + } +}" + [(set_attr "op_type" "SS,NN") + (set_attr "type" "cs,cs") + (set_attr "atype" "mem,mem") + (set_attr "length" "*,14")]) -; Clear memory with length greater 256 bytes or lenght not constant +; Clear a block of arbitrary length. -(define_insn "clrstrsi_64" +(define_insn "clrstr_long_64" [(set (match_operand:TI 0 "register_operand" "=d") (ashift:TI (plus:TI (match_operand:TI 2 "register_operand" "0") (lshiftrt:TI (match_dup 2) (const_int 64))) @@ -1980,7 +1842,7 @@ (set_attr "type" "vs") (set_attr "length" "8")]) -(define_insn "clrstrsi_31" +(define_insn "clrstr_long_31" [(set (match_operand:DI 0 "register_operand" "=d") (ashift:DI (plus:DI (match_operand:DI 2 "register_operand" "0") (lshiftrt:DI (match_dup 2) (const_int 32))) @@ -1997,171 +1859,112 @@ (set_attr "length" "8")]) ; -; cmpstrdi instruction pattern(s). +; cmpstrM instruction pattern(s). ; (define_expand "cmpstrdi" - [(set (match_operand:DI 0 "register_operand" "") - (compare:DI (match_operand:BLK 1 "s_operand" "") - (match_operand:BLK 2 "s_operand" "") ) ) - (use (match_operand:DI 3 "general_operand" "")) - (use (match_operand:DI 4 "" ""))] - "TARGET_64BIT" - " -{ - rtx addr0, addr1; + [(set (match_operand:DI 0 "register_operand" "") + (compare:DI (match_operand:BLK 1 "memory_operand" "") + (match_operand:BLK 2 "memory_operand" "") ) ) + (use (match_operand:DI 3 "general_operand" "")) + (use (match_operand:DI 4 "" ""))] + "TARGET_64BIT" + "s390_expand_cmpstr (operands[0], operands[1], + operands[2], operands[3]); DONE;") - /* for pre/post increment */ - operands[1] = protect_from_queue (operands[1], 0); - operands[2] = protect_from_queue (operands[2], 0); - operands[3] = protect_from_queue (operands[3], 0); +(define_expand "cmpstrsi" + [(set (match_operand:SI 0 "register_operand" "") + (compare:SI (match_operand:BLK 1 "memory_operand" "") + (match_operand:BLK 2 "memory_operand" "") ) ) + (use (match_operand:SI 3 "general_operand" "")) + (use (match_operand:SI 4 "" ""))] + "" + "s390_expand_cmpstr (operands[0], operands[1], + operands[2], operands[3]); DONE;") - addr0 = force_operand (XEXP (operands[1], 0), NULL_RTX); - addr1 = force_operand (XEXP (operands[2], 0), NULL_RTX); +; Compare a block that is up to 256 bytes in length. +; The block length is taken as (operands[2] % 256) + 1. - if (GET_CODE (operands[3]) == CONST_INT && INTVAL (operands[3]) < 256) +(define_insn "cmpstr_short_64" + [(set (reg:CCS 33) + (compare:CCS (match_operand:BLK 0 "memory_operand" "=Q,Q") + (match_operand:BLK 1 "memory_operand" "Q,Q"))) + (use (match_operand:DI 2 "nonmemory_operand" "n,a")) + (clobber (match_scratch:DI 3 "=X,&a"))] + "TARGET_64BIT" + "* +{ + switch (which_alternative) { - if (INTVAL (operands[3]) == 0) { - emit_move_insn (operands[0], operands[3]); - DONE; - } - - operands[1] = change_address (operands[1], VOIDmode, addr0); - operands[2] = change_address (operands[2], VOIDmode, addr1); - - emit_insn (gen_cmpstr_const (operands[1], operands[2], operands[3])); - emit_insn (gen_cmpint_di (operands[0])); - DONE; - } - else - { - /* implementation suggested by Richard Henderson <rth@cygnus.com> */ - rtx reg0 = gen_reg_rtx (TImode); - rtx reg1 = gen_reg_rtx (TImode); - rtx len = operands[3]; - - if (! CONSTANT_P (len)) - len = force_reg (DImode, len); - - /* Load up the address+length pairs. */ - emit_move_insn (gen_highpart (DImode, reg0), addr0); - emit_move_insn (gen_lowpart (DImode, reg0), len); + case 0: + return \"clc\\t%O0(%b2+1,%R0),%1\"; - emit_move_insn (gen_highpart (DImode, reg1), addr1); - emit_move_insn (gen_lowpart (DImode, reg1), len); + case 1: + output_asm_insn (\"bras\\t%3,.+10\", operands); + output_asm_insn (\"clc\\t%O0(1,%R0),%1\", operands); + return \"ex\\t%2,0(%3)\"; - /* Compare! */ - emit_insn (gen_cmpstr_64 (reg0, reg1, reg0, reg1)); - emit_insn (gen_cmpint_di (operands[0])); - DONE; + default: + abort (); } -}") - -; -; cmpstrsi instruction pattern(s). -; +}" + [(set_attr "op_type" "SS,NN") + (set_attr "type" "cs,cs") + (set_attr "atype" "mem,mem") + (set_attr "length" "*,14")]) -(define_expand "cmpstrsi" - [(set (match_operand:SI 0 "register_operand" "") - (compare:SI (match_operand:BLK 1 "s_operand" "") - (match_operand:BLK 2 "s_operand" "") ) ) - (use (match_operand:SI 3 "general_operand" "")) - (use (match_operand:SI 4 "" ""))] - "" - " +(define_insn "cmpstr_short_31" + [(set (reg:CCS 33) + (compare:CCS (match_operand:BLK 0 "memory_operand" "=Q,Q") + (match_operand:BLK 1 "memory_operand" "Q,Q"))) + (use (match_operand:SI 2 "nonmemory_operand" "n,a")) + (clobber (match_scratch:SI 3 "=X,&a"))] + "!TARGET_64BIT" + "* { - rtx addr0, addr1; - - /* for pre/post increment */ - operands[1] = protect_from_queue (operands[1], 0); - operands[2] = protect_from_queue (operands[2], 0); - operands[3] = protect_from_queue (operands[3], 0); - - addr0 = force_operand (XEXP (operands[1], 0), NULL_RTX); - addr1 = force_operand (XEXP (operands[2], 0), NULL_RTX); - - if (GET_CODE (operands[3]) == CONST_INT && INTVAL (operands[3]) < 256) + switch (which_alternative) { - if (INTVAL (operands[3]) == 0) { - emit_move_insn (operands[0], operands[3]); - DONE; - } - - operands[1] = change_address (operands[1], VOIDmode, addr0); - operands[2] = change_address (operands[2], VOIDmode, addr1); - - emit_insn (gen_cmpstr_const (operands[1], operands[2], operands[3])); - emit_insn (gen_cmpint_si (operands[0])); - DONE; - } - else - { - /* implementation suggested by Richard Henderson <rth@cygnus.com> */ - rtx reg0, reg1; - rtx len = operands[3]; - - if (TARGET_64BIT) - { - reg0 = gen_reg_rtx (TImode); - reg1 = gen_reg_rtx (TImode); - } - else - { - reg0 = gen_reg_rtx (DImode); - reg1 = gen_reg_rtx (DImode); - } - - /* Load up the address+length pairs. */ - emit_move_insn (gen_highpart (Pmode, reg0), addr0); - convert_move (gen_lowpart (Pmode, reg0), len, 1); - - emit_move_insn (gen_highpart (Pmode, reg1), addr1); - convert_move (gen_lowpart (Pmode, reg1), len, 1); + case 0: + return \"clc\\t%O0(%b2+1,%R0),%1\"; - /* Compare! */ - if (TARGET_64BIT) - emit_insn (gen_cmpstr_64 (reg0, reg1, reg0, reg1)); - else - emit_insn (gen_cmpstr_31 (reg0, reg1, reg0, reg1)); + case 1: + output_asm_insn (\"bras\\t%3,.+10\", operands); + output_asm_insn (\"clc\\t%O0(1,%R0),%1\", operands); + return \"ex\\t%2,0(%3)\"; - emit_insn (gen_cmpint_si (operands[0])); - DONE; + default: + abort (); } -}") - -; Compare a block that is less than 256 bytes in length. - -(define_insn "cmpstr_const" - [(set (reg:CCS 33) - (compare:CCS (match_operand:BLK 0 "s_operand" "oQ") - (match_operand:BLK 1 "s_operand" "oQ"))) - (use (match_operand 2 "immediate_operand" "I"))] - "(unsigned) INTVAL (operands[2]) < 256" - "clc\\t%O0(%c2,%R0),%1" - [(set_attr "op_type" "SS") - (set_attr "atype" "mem") - (set_attr "type" "cs")]) +}" + [(set_attr "op_type" "SS,NN") + (set_attr "type" "cs,cs") + (set_attr "atype" "mem,mem") + (set_attr "length" "*,14")]) -; Compare a block that is larger than 255 bytes in length. +; Compare a block of arbitrary length. -(define_insn "cmpstr_64" +(define_insn "cmpstr_long_64" [(clobber (match_operand:TI 0 "register_operand" "=d")) (clobber (match_operand:TI 1 "register_operand" "=d")) (set (reg:CCS 33) (compare:CCS (mem:BLK (subreg:DI (match_operand:TI 2 "register_operand" "0") 0)) - (mem:BLK (subreg:DI (match_operand:TI 3 "register_operand" "1") 0))))] + (mem:BLK (subreg:DI (match_operand:TI 3 "register_operand" "1") 0)))) + (use (match_dup 2)) + (use (match_dup 3))] "TARGET_64BIT" "clcl\\t%0,%1" [(set_attr "op_type" "RR") (set_attr "atype" "mem") (set_attr "type" "vs")]) -(define_insn "cmpstr_31" +(define_insn "cmpstr_long_31" [(clobber (match_operand:DI 0 "register_operand" "=d")) (clobber (match_operand:DI 1 "register_operand" "=d")) (set (reg:CCS 33) (compare:CCS (mem:BLK (subreg:SI (match_operand:DI 2 "register_operand" "0") 0)) - (mem:BLK (subreg:SI (match_operand:DI 3 "register_operand" "1") 0))))] + (mem:BLK (subreg:SI (match_operand:DI 3 "register_operand" "1") 0)))) + (use (match_dup 2)) + (use (match_dup 3))] "!TARGET_64BIT" "clcl\\t%0,%1" [(set_attr "op_type" "RR") @@ -2211,7 +2014,7 @@ (define_insn "*sethighqisi" [(set (match_operand:SI 0 "register_operand" "=d") - (unspec:SI [(match_operand:QI 1 "s_operand" "Qo")] 10)) + (unspec:SI [(match_operand:QI 1 "s_operand" "Q")] 10)) (clobber (reg:CC 33))] "" "icm\\t%0,8,%1" @@ -2220,7 +2023,7 @@ (define_insn "*sethighhisi" [(set (match_operand:SI 0 "register_operand" "=d") - (unspec:SI [(match_operand:HI 1 "s_operand" "Qo")] 10)) + (unspec:SI [(match_operand:HI 1 "s_operand" "Q")] 10)) (clobber (reg:CC 33))] "" "icm\\t%0,12,%1" @@ -2229,7 +2032,7 @@ (define_insn "*sethighqidi_64" [(set (match_operand:DI 0 "register_operand" "=d") - (unspec:DI [(match_operand:QI 1 "s_operand" "Qo")] 10)) + (unspec:DI [(match_operand:QI 1 "s_operand" "Q")] 10)) (clobber (reg:CC 33))] "TARGET_64BIT" "icmh\\t%0,8,%1" @@ -2238,20 +2041,23 @@ (define_insn "*sethighqidi_31" [(set (match_operand:DI 0 "register_operand" "=d") - (unspec:DI [(match_operand:QI 1 "s_operand" "Qo")] 10)) + (unspec:DI [(match_operand:QI 1 "s_operand" "Q")] 10)) (clobber (reg:CC 33))] "!TARGET_64BIT" "icm\\t%0,8,%1" [(set_attr "op_type" "RS") (set_attr "atype" "mem")]) -(define_split - [(set (match_operand:SI 0 "register_operand" "") - (zero_extract:SI (match_operand:QI 1 "s_operand" "") - (match_operand 2 "const_int_operand" "") - (const_int 0)))] - "!TARGET_64BIT && !reload_completed +(define_insn_and_split "*extractqi" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extract:SI (match_operand:QI 1 "s_operand" "Q") + (match_operand 2 "const_int_operand" "n") + (const_int 0))) + (clobber (reg:CC 33))] + "!TARGET_64BIT && INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 8" + "#" + "&& reload_completed" [(parallel [(set (match_dup 0) (unspec:SI [(match_dup 1)] 10)) (clobber (reg:CC 33))]) @@ -2260,15 +2066,19 @@ { operands[2] = GEN_INT (32 - INTVAL (operands[2])); operands[1] = change_address (operands[1], QImode, 0); -}") +}" + [(set_attr "atype" "mem")]) -(define_split - [(set (match_operand:SI 0 "register_operand" "") - (zero_extract:SI (match_operand:QI 1 "s_operand" "") - (match_operand 2 "const_int_operand" "") - (const_int 0)))] - "!TARGET_64BIT && !reload_completed +(define_insn_and_split "*extracthi" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extract:SI (match_operand:QI 1 "s_operand" "Q") + (match_operand 2 "const_int_operand" "n") + (const_int 0))) + (clobber (reg:CC 33))] + "!TARGET_64BIT && INTVAL (operands[2]) >= 8 && INTVAL (operands[2]) < 16" + "#" + "&& reload_completed" [(parallel [(set (match_dup 0) (unspec:SI [(match_dup 1)] 10)) (clobber (reg:CC 33))]) @@ -2277,7 +2087,8 @@ { operands[2] = GEN_INT (32 - INTVAL (operands[2])); operands[1] = change_address (operands[1], HImode, 0); -}") +}" + [(set_attr "atype" "mem")]) ; ; extendsidi2 instruction pattern(s). @@ -2566,6 +2377,20 @@ "llgh\\t%0,%1" [(set_attr "op_type" "RXE") (set_attr "atype" "mem")]) + +(define_insn_and_split "*zero_extendhisi2_31" + [(set (match_operand:SI 0 "register_operand" "=&d") + (zero_extend:SI (match_operand:HI 1 "memory_operand" "Q"))) + (clobber (reg:CC 33))] + "!TARGET_64BIT" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (parallel + [(set (strict_low_part (match_dup 2)) (match_dup 1)) + (clobber (reg:CC 33))])] + "operands[2] = gen_lowpart (HImode, operands[0]);" + [(set_attr "atype" "mem")]) ; ; zero_extendqisi2 instruction pattern(s). @@ -2590,6 +2415,17 @@ "llgc\\t%0,%1" [(set_attr "op_type" "RXE") (set_attr "atype" "mem")]) + +(define_insn_and_split "*zero_extendqisi2_31" + [(set (match_operand:SI 0 "register_operand" "=&d") + (zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))] + "!TARGET_64BIT" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (set (strict_low_part (match_dup 2)) (match_dup 1))] + "operands[2] = gen_lowpart (QImode, operands[0]);" + [(set_attr "atype" "mem")]) ; ; zero_extendqihi2 instruction pattern(s). @@ -2609,13 +2445,24 @@ (define_insn "*zero_extendqihi2_64" [(set (match_operand:HI 0 "register_operand" "=d") - (zero_extend:HI (match_operand:QI 1 "memory_operand" "m"))) - (clobber (reg:CC 33))] + (zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))] "TARGET_64BIT" "llgc\\t%0,%1" [(set_attr "op_type" "RXE") (set_attr "atype" "mem")]) +(define_insn_and_split "*zero_extendqihi2_31" + [(set (match_operand:HI 0 "register_operand" "=&d") + (zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))] + "!TARGET_64BIT" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (set (strict_low_part (match_dup 2)) (match_dup 1))] + "operands[2] = gen_lowpart (QImode, operands[0]);" + [(set_attr "atype" "mem")]) + + ; ; fixuns_truncdfdi2 and fix_truncdfsi2 instruction pattern(s). ; @@ -3062,24 +2909,113 @@ ; adddi3 instruction pattern(s). ; -(define_insn "addaddr_esame" - [(set (match_operand:DI 0 "register_operand" "=a,a") - (plus:DI (match_operand:DI 1 "register_operand" "%a,a") - (match_operand:DI 2 "nonmemory_operand" "J,a")))] - "TARGET_64BIT && (((REGNO (operands[1]) == STACK_POINTER_REGNUM ) || - (REGNO (operands[1]) == BASE_REGISTER)) && - (GET_CODE (operands[2]) == REG || - CONST_OK_FOR_LETTER_P (INTVAL (operands[2]),'J')))" +(define_insn "*adddi3_sign" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (sign_extend:DI (match_operand:SI 2 "general_operand" "d,m")) + (match_operand:DI 1 "register_operand" "0,0"))) + (clobber (reg:CC 33))] + "TARGET_64BIT" "@ - la\\t%0,%c2(,%1) - la\\t%0,0(%1,%2)" - [(set_attr "op_type" "RX") - (set_attr "atype" "mem") - (set_attr "type" "la")]) + agfr\\t%0,%2 + agf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*adddi3_zero_cc" + [(set (reg 33) + (compare (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")) + (match_operand:DI 1 "register_operand" "0,0")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (zero_extend:DI (match_dup 2)) (match_dup 1)))] + "s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" + "@ + algfr\\t%0,%2 + algf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) -(define_insn "adddi3_64" +(define_insn "*adddi3_zero_cconly" + [(set (reg 33) + (compare (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")) + (match_operand:DI 1 "register_operand" "0,0")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" + "@ + algfr\\t%0,%2 + algf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*adddi3_zero" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")) + (match_operand:DI 1 "register_operand" "0,0"))) + (clobber (reg:CC 33))] + "TARGET_64BIT" + "@ + algfr\\t%0,%2 + algf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*adddi3_imm_cc" + [(set (reg 33) + (compare (plus:DI (match_operand:DI 1 "nonimmediate_operand" "0") + (match_operand:DI 2 "const_int_operand" "K")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (plus:DI (match_dup 1) (match_dup 2)))] + "TARGET_64BIT + && s390_match_ccmode (insn, CCAmode) + && CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K')" + "aghi\\t%0,%h2" + [(set_attr "op_type" "RI") + (set_attr "atype" "reg")]) + +(define_insn "*adddi3_cc" + [(set (reg 33) + (compare (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") + (match_operand:DI 2 "general_operand" "d,m")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" + "@ + algr\\t%0,%2 + alg\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*adddi3_cconly" + [(set (reg 33) + (compare (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") + (match_operand:DI 2 "general_operand" "d,m")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" + "@ + algr\\t%0,%2 + alg\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*adddi3_cconly2" + [(set (reg 33) + (compare (match_operand:DI 1 "nonimmediate_operand" "%0,0") + (neg:SI (match_operand:DI 2 "general_operand" "d,m")))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode(insn, CCLmode) && TARGET_64BIT" + "@ + algr\\t%0,%2 + alg\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*adddi3_64" [(set (match_operand:DI 0 "register_operand" "=d,d,d") - (plus:DI (match_operand:DI 1 "register_operand" "%0,0,0") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0") (match_operand:DI 2 "general_operand" "d,K,m") ) ) (clobber (reg:CC 33))] "TARGET_64BIT" @@ -3090,51 +3026,47 @@ [(set_attr "op_type" "RRE,RI,RXE") (set_attr "atype" "reg,reg,mem")]) -(define_insn "adddi3_31" - [(set (match_operand:DI 0 "register_operand" "=d,d") - (plus:DI (match_operand:DI 1 "register_operand" "0,0") - (match_operand:DI 2 "general_operand" "d,m") ) ) +(define_insn_and_split "*adddi3_31" + [(set (match_operand:DI 0 "register_operand" "=&d") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0") + (match_operand:DI 2 "general_operand" "do") ) ) (clobber (reg:CC 33))] "!TARGET_64BIT" - "* -{ - switch (which_alternative) - { - case 0: /* d <- d */ - output_asm_insn (\"ar\\t%0,%2\", operands); - output_asm_insn (\"alr\\t%N0,%N2\", operands); - break; - - case 1: /* d <- m */ - output_asm_insn (\"a\\t%0,%2\", operands); - output_asm_insn (\"al\\t%N0,%N2\", operands); - break; - - default: - abort (); - } - - output_asm_insn (\"brc\\t12,.+8\", operands); - return \"ahi\\t%0,1\"; -}" - [(set_attr "op_type" "NN,NN") - (set_attr "atype" "reg,mem") - (set_attr "type" "o2,o2") - (set_attr "length" "12,16")]) + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 3) (plus:SI (match_dup 4) (match_dup 5))) + (clobber (reg:CC 33))]) + (parallel + [(set (reg:CCL1 33) + (compare:CCL1 (plus:SI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (plus:SI (match_dup 7) (match_dup 8)))]) + (set (pc) + (if_then_else (ltu (reg:CCL1 33) (const_int 0)) + (pc) + (label_ref (match_dup 9)))) + (parallel + [(set (match_dup 3) (plus:SI (match_dup 3) (const_int 1))) + (clobber (reg:CC 33))]) + (match_dup 9)] + "operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[2], 0, 0, DImode); + operands[6] = operand_subword (operands[0], 1, 0, DImode); + operands[7] = operand_subword (operands[1], 1, 0, DImode); + operands[8] = operand_subword (operands[2], 1, 0, DImode); + operands[9] = gen_label_rtx ();" + [(set_attr "op_type" "NN")]) (define_expand "adddi3" - [(set (match_operand:DI 0 "register_operand" "") - (plus:DI (match_operand:DI 1 "register_operand" "") - (match_operand:DI 2 "general_operand" "")))] + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "") + (match_operand:DI 2 "general_operand" ""))) + (clobber (reg:CC 33))])] "" - " -{ - if (TARGET_64BIT) - emit_insn(gen_adddi3_64 (operands[0],operands[1],operands[2])); - else - emit_insn(gen_adddi3_31 (operands[0],operands[1],operands[2])); - DONE; -}") + "") (define_insn "*la_64" [(set (match_operand:DI 0 "register_operand" "=d") @@ -3145,10 +3077,36 @@ (set_attr "atype" "mem") (set_attr "type" "la")]) +(define_peephole2 + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:QI 1 "address_operand" "")) + (clobber (reg:CC 33))])] + "TARGET_64BIT + && strict_memory_address_p (VOIDmode, operands[1]) + && preferred_la_operand_p (operands[1])" + [(set (match_dup 0) (match_dup 1))] + "") + +(define_peephole2 + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" "")) + (parallel + [(set (match_dup 0) + (plus:DI (match_dup 0) + (match_operand:DI 2 "nonmemory_operand" ""))) + (clobber (reg:CC 33))])] + "TARGET_64BIT + && !reg_overlap_mentioned_p (operands[0], operands[2]) + && strict_memory_address_p (VOIDmode, gen_rtx_PLUS (DImode, operands[1], operands[2])) + && preferred_la_operand_p (gen_rtx_PLUS (DImode, operands[1], operands[2]))" + [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))] + "") + (define_expand "reload_indi" [(parallel [(match_operand:DI 0 "register_operand" "=a") (match_operand:DI 1 "s390_plus_operand" "") - (match_operand:TI 2 "register_operand" "=&a")])] + (match_operand:DI 2 "register_operand" "=&a")])] "TARGET_64BIT" " { @@ -3161,24 +3119,81 @@ ; addsi3 instruction pattern(s). ; -(define_insn "*la_ccclobber" - [(set (match_operand:SI 0 "register_operand" "=d") - (match_operand:QI 1 "address_operand" "p")) - (clobber (reg:CC 33))] - "legitimate_la_operand_p (operands[1])" - "la\\t%0,%a1" - [(set_attr "op_type" "RX") - (set_attr "atype" "mem") - (set_attr "type" "la")]) +(define_insn "*addsi3_imm_cc" + [(set (reg 33) + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "0") + (match_operand:SI 2 "const_int_operand" "K")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCAmode) + && CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'K')" + "ahi\\t%0,%h2" + [(set_attr "op_type" "RI") + (set_attr "atype" "reg")]) + +(define_insn "*addsi3_carry1_cc" + [(set (reg 33) + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") + (match_operand:SI 2 "general_operand" "d,m")) + (match_dup 1))) + (set (match_operand:SI 0 "register_operand" "=d,d") + (plus:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + alr\\t%0,%2 + al\\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) + +(define_insn "*addsi3_carry1_cconly" + [(set (reg 33) + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") + (match_operand:SI 2 "general_operand" "d,m")) + (match_dup 1))) + (clobber (match_scratch:SI 0 "=d,d"))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + alr\\t%0,%2 + al\\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) + +(define_insn "*addsi3_carry2_cc" + [(set (reg 33) + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") + (match_operand:SI 2 "general_operand" "d,m")) + (match_dup 2))) + (set (match_operand:SI 0 "register_operand" "=d,d") + (plus:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + alr\\t%0,%2 + al\\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) + +(define_insn "*addsi3_carry2_cconly" + [(set (reg 33) + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") + (match_operand:SI 2 "general_operand" "d,m")) + (match_dup 2))) + (clobber (match_scratch:SI 0 "=d,d"))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + alr\\t%0,%2 + al\\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) (define_insn "*addsi3_cc" [(set (reg 33) - (compare (plus:SI (match_operand:SI 1 "register_operand" "%0,0") - (match_operand:SI 2 "nonimmediate_operand" "d,m")) + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") + (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=d,d") (plus:SI (match_dup 1) (match_dup 2)))] - "s390_match_ccmode(insn, CCLmode)" + "s390_match_ccmode (insn, CCLmode)" "@ alr\\t%0,%2 al\\t%0,%2" @@ -3187,11 +3202,11 @@ (define_insn "*addsi3_cconly" [(set (reg 33) - (compare (plus:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:SI 0 "=d,d"))] - "s390_match_ccmode(insn, CCLmode)" + "s390_match_ccmode (insn, CCLmode)" "@ alr\\t%0,%2 al\\t%0,%2" @@ -3200,7 +3215,7 @@ (define_insn "*addsi3_cconly2" [(set (reg 33) - (compare (match_operand:SI 1 "register_operand" "%0,0") + (compare (match_operand:SI 1 "nonimmediate_operand" "%0,0") (neg:SI (match_operand:SI 2 "general_operand" "d,m")))) (clobber (match_scratch:SI 0 "=d,d"))] "s390_match_ccmode(insn, CCLmode)" @@ -3210,9 +3225,29 @@ [(set_attr "op_type" "RR,RX") (set_attr "atype" "reg,mem")]) +(define_insn "*addsi3_sign" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_operand:SI 1 "register_operand" "0") + (sign_extend:SI (match_operand:HI 2 "memory_operand" "m")))) + (clobber (reg:CC 33))] + "" + "ah\\t%0,%2" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem")]) + +(define_insn "*addsi3_sub" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_operand:SI 1 "register_operand" "0") + (subreg:SI (match_operand:HI 2 "memory_operand" "m") 0))) + (clobber (reg:CC 33))] + "" + "ah\\t%0,%2" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem")]) + (define_insn "addsi3" [(set (match_operand:SI 0 "register_operand" "=d,d,d") - (plus:SI (match_operand:SI 1 "register_operand" "%0,0,0") + (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0") (match_operand:SI 2 "general_operand" "d,K,m"))) (clobber (reg:CC 33))] "" @@ -3223,23 +3258,71 @@ [(set_attr "op_type" "RR,RI,RX") (set_attr "atype" "reg,reg,mem")]) -(define_insn "*addsi3_inv" - [(set (match_operand:SI 0 "register_operand" "=d,d,d") - (plus:SI (match_operand:SI 1 "general_operand" "%d,K,m") - (match_operand:SI 2 "register_operand" "0,0,0"))) +(define_insn "*la_31" + [(set (match_operand:SI 0 "register_operand" "=d") + (match_operand:QI 1 "address_operand" "p"))] + "!TARGET_64BIT && legitimate_la_operand_p (operands[1])" + "la\\t%0,%a1" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem") + (set_attr "type" "la")]) + +(define_peephole2 + [(parallel + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:QI 1 "address_operand" "")) + (clobber (reg:CC 33))])] + "!TARGET_64BIT + && strict_memory_address_p (VOIDmode, operands[1]) + && preferred_la_operand_p (operands[1])" + [(set (match_dup 0) (match_dup 1))] + "") + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "register_operand" "")) + (parallel + [(set (match_dup 0) + (plus:SI (match_dup 0) + (match_operand:SI 2 "nonmemory_operand" ""))) + (clobber (reg:CC 33))])] + "!TARGET_64BIT + && !reg_overlap_mentioned_p (operands[0], operands[2]) + && strict_memory_address_p (VOIDmode, gen_rtx_PLUS (SImode, operands[1], operands[2])) + && preferred_la_operand_p (gen_rtx_PLUS (SImode, operands[1], operands[2]))" + [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))] + "") + +(define_insn "*la_31_and" + [(set (match_operand:SI 0 "register_operand" "=d") + (and:SI (match_operand:QI 1 "address_operand" "p") + (const_int 2147483647)))] + "!TARGET_64BIT" + "la\\t%0,%a1" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem") + (set_attr "type" "la")]) + +(define_insn_and_split "*la_31_and_cc" + [(set (match_operand:SI 0 "register_operand" "=d") + (and:SI (match_operand:QI 1 "address_operand" "p") + (const_int 2147483647))) (clobber (reg:CC 33))] + "!TARGET_64BIT" + "#" + "&& reload_completed" + [(set (match_dup 0) + (and:SI (match_dup 1) (const_int 2147483647)))] "" - "@ - ar\\t%0,%1 - ahi\\t%0,%h1 - a\\t%0,%1" - [(set_attr "op_type" "RR,RI,RX") - (set_attr "atype" "reg,reg,mem")]) + [(set_attr "op_type" "RX") + (set_attr "atype" "mem") + (set_attr "type" "la")]) -(define_insn "*la_31" +(define_insn "force_la_31" [(set (match_operand:SI 0 "register_operand" "=d") - (match_operand:QI 1 "address_operand" "p"))] - "legitimate_la_operand_p (operands[1])" + (match_operand:QI 1 "address_operand" "p")) + (use (const_int 0))] + "!TARGET_64BIT" "la\\t%0,%a1" [(set_attr "op_type" "RX") (set_attr "atype" "mem") @@ -3248,7 +3331,7 @@ (define_expand "reload_insi" [(parallel [(match_operand:SI 0 "register_operand" "=a") (match_operand:SI 1 "s390_plus_operand" "") - (match_operand:DI 2 "register_operand" "=&a")])] + (match_operand:SI 2 "register_operand" "=&a")])] "!TARGET_64BIT" " { @@ -3258,48 +3341,13 @@ ; -; addhi3 instruction pattern(s). -; - -(define_insn "addhi3" - [(set (match_operand:HI 0 "register_operand" "=d,d,d") - (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0") - (match_operand:HI 2 "general_operand" "d,K,m"))) - (clobber (reg:CC 33))] - "" - "@ - ar\\t%0,%2 - ahi\\t%0,%h2 - ah\\t%0,%2" - [(set_attr "op_type" "RR,RI,RX") - (set_attr "atype" "reg,reg,mem")]) - - -; -; addqi3 instruction pattern(s). -; - -(define_insn "addqi3" - [(set (match_operand:QI 0 "register_operand" "=d,d") - (plus:QI (match_operand:QI 1 "register_operand" "%0,0") - (match_operand:QI 2 "general_operand" "a,n"))) - (clobber (reg:CC 33))] - "" - "@ - ar\\t%0,%2 - ahi\\t%0,%h2" - [(set_attr "op_type" "RX,RX") - (set_attr "atype" "reg,mem")]) - - -; ; adddf3 instruction pattern(s). ; (define_expand "adddf3" [(parallel [(set (match_operand:DF 0 "register_operand" "=f,f") - (plus:DF (match_operand:DF 1 "register_operand" "%0,0") + (plus:DF (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))])] "TARGET_HARD_FLOAT" @@ -3307,7 +3355,7 @@ (define_insn "*adddf3" [(set (match_operand:DF 0 "register_operand" "=f,f") - (plus:DF (match_operand:DF 1 "register_operand" "%0,0") + (plus:DF (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" @@ -3319,7 +3367,7 @@ (define_insn "*adddf3_ibm" [(set (match_operand:DF 0 "register_operand" "=f,f") - (plus:DF (match_operand:DF 1 "register_operand" "%0,0") + (plus:DF (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" @@ -3336,7 +3384,7 @@ (define_expand "addsf3" [(parallel [(set (match_operand:SF 0 "register_operand" "=f,f") - (plus:SF (match_operand:SF 1 "register_operand" "%0,0") + (plus:SF (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))])] "TARGET_HARD_FLOAT" @@ -3344,7 +3392,7 @@ (define_insn "*addsf3" [(set (match_operand:SF 0 "register_operand" "=f,f") - (plus:SF (match_operand:SF 1 "register_operand" "%0,0") + (plus:SF (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" @@ -3356,7 +3404,7 @@ (define_insn "*addsf3" [(set (match_operand:SF 0 "register_operand" "=f,f") - (plus:SF (match_operand:SF 1 "register_operand" "%0,0") + (plus:SF (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" @@ -3375,6 +3423,84 @@ ; subdi3 instruction pattern(s). ; +(define_insn "*subdi3_sign" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (sign_extend:DI (match_operand:SI 2 "general_operand" "d,m")))) + (clobber (reg:CC 33))] + "TARGET_64BIT" + "@ + sgfr\\t%0,%2 + sgf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*subdi3_zero_cc" + [(set (reg 33) + (compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m"))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_dup 1) (zero_extend:DI (match_dup 2))))] + "s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" + "@ + slgfr\\t%0,%2 + slgf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*subdi3_zero_cconly" + [(set (reg 33) + (compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m"))) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode (insn, CCLmode) && TARGET_64BIT" + "@ + slgfr\\t%0,%2 + slgf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*subdi3_zero" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI (match_operand:SI 2 "general_operand" "d,m")))) + (clobber (reg:CC 33))] + "TARGET_64BIT" + "@ + slgfr\\t%0,%2 + slgf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*subdi3_cc" + [(set (reg 33) + (compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (match_operand:DI 2 "general_operand" "d,m")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCLmode)" + "@ + slgr\\t%0,%2 + slg\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + +(define_insn "*subdi3_cconly" + [(set (reg 33) + (compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (match_operand:DI 2 "general_operand" "d,m")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode (insn, CCLmode)" + "@ + slgr\\t%0,%2 + slg\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem")]) + (define_insn "*subdi3_64" [(set (match_operand:DI 0 "register_operand" "=d,d") (minus:DI (match_operand:DI 1 "register_operand" "0,0") @@ -3387,41 +3513,79 @@ [(set_attr "op_type" "RRE,RRE") (set_attr "atype" "reg,mem")]) -(define_insn "subdi3" - [(set (match_operand:DI 0 "register_operand" "=d,d") - (minus:DI (match_operand:DI 1 "register_operand" "0,0") - (match_operand:DI 2 "general_operand" "d,m"))) +(define_insn_and_split "*subdi3_31" + [(set (match_operand:DI 0 "register_operand" "=&d") + (minus:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:DI 2 "general_operand" "do") ) ) (clobber (reg:CC 33))] + "!TARGET_64BIT" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 3) (minus:SI (match_dup 4) (match_dup 5))) + (clobber (reg:CC 33))]) + (parallel + [(set (reg:CCL2 33) + (compare:CCL2 (minus:SI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (minus:SI (match_dup 7) (match_dup 8)))]) + (set (pc) + (if_then_else (gtu (reg:CCL2 33) (const_int 0)) + (pc) + (label_ref (match_dup 9)))) + (parallel + [(set (match_dup 3) (plus:SI (match_dup 3) (const_int -1))) + (clobber (reg:CC 33))]) + (match_dup 9)] + "operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[2], 0, 0, DImode); + operands[6] = operand_subword (operands[0], 1, 0, DImode); + operands[7] = operand_subword (operands[1], 1, 0, DImode); + operands[8] = operand_subword (operands[2], 1, 0, DImode); + operands[9] = gen_label_rtx ();" + [(set_attr "op_type" "NN")]) + +(define_expand "subdi3" + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "general_operand" ""))) + (clobber (reg:CC 33))])] "" - "* -{ - switch (which_alternative) - { - case 0: /* d <- d */ - output_asm_insn (\"sr\\t%0,%2\", operands); - output_asm_insn (\"slr\\t%N0,%N2\", operands); - break; - case 1: /* d <- m */ - output_asm_insn (\"s\\t%0,%2\", operands); - output_asm_insn (\"sl\\t%N0,%N2\", operands); - break; - - default: - abort (); - } - - output_asm_insn (\"brc\\t11,.+8\", operands); - return \"ahi\\t%0,-1\"; -}" - [(set_attr "op_type" "NN,NN") - (set_attr "atype" "reg,mem") - (set_attr "type" "other,other") - (set_attr "length" "12,16")]) + "") ; ; subsi3 instruction pattern(s). ; +(define_insn "*subsi3_borrow_cc" + [(set (reg 33) + (compare (minus:SI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "general_operand" "d,m")) + (match_dup 1))) + (set (match_operand:SI 0 "register_operand" "=d,d") + (minus:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCL2mode)" + "@ + slr\\t%0,%2 + sl\\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) + +(define_insn "*subsi3_borrow_cconly" + [(set (reg 33) + (compare (minus:SI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "general_operand" "d,m")) + (match_dup 1))) + (clobber (match_scratch:SI 0 "=d,d"))] + "s390_match_ccmode(insn, CCL2mode)" + "@ + slr\\t%0,%2 + sl\\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "atype" "reg,mem")]) + (define_insn "*subsi3_cc" [(set (reg 33) (compare (minus:SI (match_operand:SI 1 "register_operand" "0,0") @@ -3449,6 +3613,26 @@ [(set_attr "op_type" "RR,RX") (set_attr "atype" "reg,mem")]) +(define_insn "*subsi3_sign" + [(set (match_operand:SI 0 "register_operand" "=d") + (minus:SI (match_operand:SI 1 "register_operand" "0") + (sign_extend:SI (match_operand:HI 2 "memory_operand" "m")))) + (clobber (reg:CC 33))] + "" + "sh\\t%0,%2" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem")]) + +(define_insn "*subsi3_sub" + [(set (match_operand:SI 0 "register_operand" "=d") + (minus:SI (match_operand:SI 1 "register_operand" "0") + (subreg:SI (match_operand:HI 2 "memory_operand" "m") 0))) + (clobber (reg:CC 33))] + "" + "sh\\t%0,%2" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem")]) + (define_insn "subsi3" [(set (match_operand:SI 0 "register_operand" "=d,d") (minus:SI (match_operand:SI 1 "register_operand" "0,0") @@ -3461,34 +3645,6 @@ [(set_attr "op_type" "RR,RX") (set_attr "atype" "reg,mem")]) -; -; subhi3 instruction pattern(s). -; - -(define_insn "subhi3" - [(set (match_operand:HI 0 "register_operand" "=d,d") - (minus:HI (match_operand:HI 1 "register_operand" "0,0") - (match_operand:HI 2 "general_operand" "d,m"))) - (clobber (reg:CC 33))] - "" - "@ - sr\\t%0,%2 - sh\\t%0,%2" - [(set_attr "op_type" "RR,RX") - (set_attr "atype" "reg,mem")]) - -; -; subqi3 instruction pattern(s). -; - -(define_insn "subqi3" - [(set (match_operand:QI 0 "register_operand" "=d") - (minus:QI (match_operand:QI 1 "register_operand" "0") - (match_operand:QI 2 "register_operand" "d"))) - (clobber (reg:CC 33))] - "" - "sr\\t%0,%2" - [(set_attr "op_type" "RR")]) ; ; subdf3 instruction pattern(s). @@ -3573,17 +3729,29 @@ ; muldi3 instruction pattern(s). ; +(define_insn "*muldi3_sign" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (mult:DI (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "d,m")) + (match_operand:DI 1 "register_operand" "0,0")))] + "TARGET_64BIT" + "@ + msgfr\\t%0,%2 + msgf\\t%0,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "atype" "reg,mem") + (set_attr "type" "imul")]) + + (define_insn "muldi3" [(set (match_operand:DI 0 "register_operand" "=d,d,d") - (mult:DI (match_operand:DI 1 "register_operand" "%0,0,0") - (match_operand:DI 2 "general_operand" "d,K,m"))) - (clobber (reg:CC 33))] + (mult:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0") + (match_operand:DI 2 "general_operand" "d,K,m")))] "TARGET_64BIT" "@ msgr\\t%0,%2 mghi\\t%0,%h2 msg\\t%0,%2" - [(set_attr "op_type" "RRE,RI,RX") + [(set_attr "op_type" "RRE,RI,RXE") (set_attr "atype" "reg,reg,mem") (set_attr "type" "imul")]) @@ -3593,9 +3761,8 @@ (define_insn "mulsi3" [(set (match_operand:SI 0 "register_operand" "=d,d,d") - (mult:SI (match_operand:SI 1 "register_operand" "%0,0,0") - (match_operand:SI 2 "general_operand" "d,K,m"))) - (clobber (reg:CC 33))] + (mult:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0") + (match_operand:SI 2 "general_operand" "d,K,m")))] "" "@ msr\\t%0,%2 @@ -3611,7 +3778,7 @@ (define_expand "mulsidi3" [(set (match_operand:DI 0 "register_operand" "") - (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "")) + (mult:DI (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")) (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" ""))))] "!TARGET_64BIT" " @@ -3635,8 +3802,7 @@ (mult:DI (sign_extend:DI (truncate:SI (match_operand:DI 1 "register_operand" "0,0"))) (sign_extend:DI - (match_operand:SI 2 "nonimmediate_operand" "d,m")))) - (clobber (reg:CC 33))] + (match_operand:SI 2 "nonimmediate_operand" "d,m"))))] "!TARGET_64BIT" "@ mr\\t%0,%2 @@ -3652,7 +3818,7 @@ (define_expand "muldf3" [(parallel [(set (match_operand:DF 0 "register_operand" "=f,f") - (mult:DF (match_operand:DF 1 "register_operand" "%0,0") + (mult:DF (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))])] "TARGET_HARD_FLOAT" @@ -3660,7 +3826,7 @@ (define_insn "*muldf3" [(set (match_operand:DF 0 "register_operand" "=f,f") - (mult:DF (match_operand:DF 1 "register_operand" "%0,0") + (mult:DF (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" @@ -3673,7 +3839,7 @@ (define_insn "*muldf3_ibm" [(set (match_operand:DF 0 "register_operand" "=f,f") - (mult:DF (match_operand:DF 1 "register_operand" "%0,0") + (mult:DF (match_operand:DF 1 "nonimmediate_operand" "%0,0") (match_operand:DF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" @@ -3691,7 +3857,7 @@ (define_expand "mulsf3" [(parallel [(set (match_operand:SF 0 "register_operand" "=f,f") - (mult:SF (match_operand:SF 1 "register_operand" "%0,0") + (mult:SF (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))])] "TARGET_HARD_FLOAT" @@ -3699,7 +3865,7 @@ (define_insn "*mulsf3" [(set (match_operand:SF 0 "register_operand" "=f,f") - (mult:SF (match_operand:SF 1 "register_operand" "%0,0") + (mult:SF (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IEEE_FLOAT" @@ -3712,7 +3878,7 @@ (define_insn "*mulsf3_ibm" [(set (match_operand:SF 0 "register_operand" "=f,f") - (mult:SF (match_operand:SF 1 "register_operand" "%0,0") + (mult:SF (match_operand:SF 1 "nonimmediate_operand" "%0,0") (match_operand:SF 2 "general_operand" "f,m"))) (clobber (reg:CC 33))] "TARGET_HARD_FLOAT && TARGET_IBM_FLOAT" @@ -4211,7 +4377,7 @@ (define_insn "*anddi3_cc" [(set (reg 33) - (compare (and:DI (match_operand:DI 1 "register_operand" "%0,0") + (compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=d,d") @@ -4225,7 +4391,7 @@ (define_insn "*anddi3_cconly" [(set (reg 33) - (compare (and:DI (match_operand:DI 1 "register_operand" "%0,0") + (compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:DI 0 "=d,d"))] @@ -4238,7 +4404,7 @@ (define_insn "*anddi3_ni" [(set (match_operand:DI 0 "register_operand" "=d") - (and:DI (match_operand:DI 1 "register_operand" "%0") + (and:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:DI 2 "immediate_operand" "n"))) (clobber (reg:CC 33))] "TARGET_64BIT && s390_single_hi (operands[2], DImode, -1) >= 0" @@ -4261,7 +4427,7 @@ (define_insn "anddi3" [(set (match_operand:DI 0 "register_operand" "=d,d") - (and:DI (match_operand:DI 1 "register_operand" "%0,0") + (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m"))) (clobber (reg:CC 33))] "TARGET_64BIT" @@ -4272,9 +4438,9 @@ (set_attr "atype" "reg,mem")]) (define_insn "*anddi3_ss" - [(set (match_operand:DI 0 "s_operand" "=Qo") + [(set (match_operand:DI 0 "s_operand" "=Q") (and:DI (match_dup 0) - (match_operand:DI 1 "s_imm_operand" "Qo"))) + (match_operand:DI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "nc\\t%O0(8,%R0),%1" @@ -4282,8 +4448,8 @@ (set_attr "atype" "mem")]) (define_insn "*anddi3_ss_inv" - [(set (match_operand:DI 0 "s_operand" "=Qo") - (and:DI (match_operand:DI 1 "s_imm_operand" "Qo") + [(set (match_operand:DI 0 "s_operand" "=Q") + (and:DI (match_operand:DI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4297,7 +4463,7 @@ (define_insn "*andsi3_cc" [(set (reg 33) - (compare (and:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=d,d") @@ -4311,7 +4477,7 @@ (define_insn "*andsi3_cconly" [(set (reg 33) - (compare (and:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:SI 0 "=d,d"))] @@ -4324,7 +4490,7 @@ (define_insn "*andsi3_ni" [(set (match_operand:SI 0 "register_operand" "=d") - (and:SI (match_operand:SI 1 "register_operand" "%0") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:SI 2 "immediate_operand" "n"))) (clobber (reg:CC 33))] "TARGET_64BIT && s390_single_hi (operands[2], SImode, -1) >= 0" @@ -4345,7 +4511,7 @@ (define_insn "andsi3" [(set (match_operand:SI 0 "register_operand" "=d,d") - (and:SI (match_operand:SI 1 "register_operand" "%0,0") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m"))) (clobber (reg:CC 33))] "" @@ -4356,9 +4522,9 @@ (set_attr "atype" "reg,mem")]) (define_insn "*andsi3_ss" - [(set (match_operand:SI 0 "s_operand" "=Qo") + [(set (match_operand:SI 0 "s_operand" "=Q") (and:SI (match_dup 0) - (match_operand:SI 1 "s_imm_operand" "Qo"))) + (match_operand:SI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "nc\\t%O0(4,%R0),%1" @@ -4366,8 +4532,8 @@ (set_attr "atype" "mem")]) (define_insn "*andsi3_ss_inv" - [(set (match_operand:SI 0 "s_operand" "=Qo") - (and:SI (match_operand:SI 1 "s_imm_operand" "Qo") + [(set (match_operand:SI 0 "s_operand" "=Q") + (and:SI (match_operand:SI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4402,9 +4568,9 @@ (set_attr "atype" "reg")]) (define_insn "*andhi3_ss" - [(set (match_operand:HI 0 "s_operand" "=Qo") + [(set (match_operand:HI 0 "s_operand" "=Q") (and:HI (match_dup 0) - (match_operand:HI 1 "s_imm_operand" "Qo"))) + (match_operand:HI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "nc\\t%O0(2,%R0),%1" @@ -4412,8 +4578,8 @@ (set_attr "atype" "mem")]) (define_insn "*andhi3_ss_inv" - [(set (match_operand:HI 0 "s_operand" "=Qo") - (and:HI (match_operand:HI 1 "s_imm_operand" "Qo") + [(set (match_operand:HI 0 "s_operand" "=Q") + (and:HI (match_operand:HI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4448,9 +4614,9 @@ (set_attr "atype" "reg")]) (define_insn "*andqi3_ss" - [(set (match_operand:QI 0 "s_operand" "=Qo,Qo") + [(set (match_operand:QI 0 "s_operand" "=Q,Q") (and:QI (match_dup 0) - (match_operand:QI 1 "s_imm_operand" "n,Qo"))) + (match_operand:QI 1 "s_imm_operand" "n,Q"))) (clobber (reg:CC 33))] "" "@ @@ -4460,8 +4626,8 @@ (set_attr "atype" "mem")]) (define_insn "*andqi3_ss_inv" - [(set (match_operand:QI 0 "s_operand" "=Qo,Qo") - (and:QI (match_operand:QI 1 "s_imm_operand" "n,Qo") + [(set (match_operand:QI 0 "s_operand" "=Q,Q") + (and:QI (match_operand:QI 1 "s_imm_operand" "n,Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4482,7 +4648,7 @@ (define_insn "*iordi3_cc" [(set (reg 33) - (compare (ior:DI (match_operand:DI 1 "register_operand" "%0,0") + (compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=d,d") @@ -4496,7 +4662,7 @@ (define_insn "*iordi3_cconly" [(set (reg 33) - (compare (ior:DI (match_operand:DI 1 "register_operand" "%0,0") + (compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:DI 0 "=d,d"))] @@ -4509,7 +4675,7 @@ (define_insn "*iordi3_oi" [(set (match_operand:DI 0 "register_operand" "=d") - (ior:DI (match_operand:DI 1 "register_operand" "%0") + (ior:DI (match_operand:DI 1 "nonimmediate_operand" "0") (match_operand:DI 2 "immediate_operand" "n"))) (clobber (reg:CC 33))] "TARGET_64BIT && s390_single_hi (operands[2], DImode, 0) >= 0" @@ -4532,7 +4698,7 @@ (define_insn "iordi3" [(set (match_operand:DI 0 "register_operand" "=d,d") - (ior:DI (match_operand:DI 1 "register_operand" "%0,0") + (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m"))) (clobber (reg:CC 33))] "TARGET_64BIT" @@ -4543,9 +4709,9 @@ (set_attr "atype" "reg,mem")]) (define_insn "*iordi3_ss" - [(set (match_operand:DI 0 "s_operand" "=Qo") + [(set (match_operand:DI 0 "s_operand" "=Q") (ior:DI (match_dup 0) - (match_operand:DI 1 "s_imm_operand" "Qo"))) + (match_operand:DI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "oc\\t%O0(8,%R0),%1" @@ -4553,8 +4719,8 @@ (set_attr "atype" "mem")]) (define_insn "*iordi3_ss_inv" - [(set (match_operand:DI 0 "s_operand" "=Qo") - (ior:DI (match_operand:DI 1 "s_imm_operand" "Qo") + [(set (match_operand:DI 0 "s_operand" "=Q") + (ior:DI (match_operand:DI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4568,7 +4734,7 @@ (define_insn "*iorsi3_cc" [(set (reg 33) - (compare (ior:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=d,d") @@ -4582,7 +4748,7 @@ (define_insn "*iorsi3_cconly" [(set (reg 33) - (compare (ior:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:SI 0 "=d,d"))] @@ -4595,7 +4761,7 @@ (define_insn "*iorsi3_oi" [(set (match_operand:SI 0 "register_operand" "=d") - (ior:SI (match_operand:SI 1 "register_operand" "%0") + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "0") (match_operand:SI 2 "immediate_operand" "n"))) (clobber (reg:CC 33))] "TARGET_64BIT && s390_single_hi (operands[2], SImode, 0) >= 0" @@ -4616,7 +4782,7 @@ (define_insn "iorsi3" [(set (match_operand:SI 0 "register_operand" "=d,d") - (ior:SI (match_operand:SI 1 "register_operand" "%0,0") + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m"))) (clobber (reg:CC 33))] "" @@ -4627,9 +4793,9 @@ (set_attr "atype" "reg,mem")]) (define_insn "*iorsi3_ss" - [(set (match_operand:SI 0 "s_operand" "=Qo") + [(set (match_operand:SI 0 "s_operand" "=Q") (ior:SI (match_dup 0) - (match_operand:SI 1 "s_imm_operand" "Qo"))) + (match_operand:SI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "oc\\t%O0(4,%R0),%1" @@ -4637,8 +4803,8 @@ (set_attr "atype" "mem")]) (define_insn "*iorsi3_ss_inv" - [(set (match_operand:SI 0 "s_operand" "=Qo") - (ior:SI (match_operand:SI 1 "s_imm_operand" "Qo") + [(set (match_operand:SI 0 "s_operand" "=Q") + (ior:SI (match_operand:SI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4673,9 +4839,9 @@ (set_attr "atype" "reg")]) (define_insn "*iorhi3_ss" - [(set (match_operand:HI 0 "s_operand" "=Qo") + [(set (match_operand:HI 0 "s_operand" "=Q") (ior:HI (match_dup 0) - (match_operand:HI 1 "s_imm_operand" "Qo"))) + (match_operand:HI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "oc\\t%O0(2,%R0),%1" @@ -4683,8 +4849,8 @@ (set_attr "atype" "mem")]) (define_insn "*iorhi3_ss_inv" - [(set (match_operand:HI 0 "s_operand" "=Qo") - (ior:HI (match_operand:HI 1 "s_imm_operand" "Qo") + [(set (match_operand:HI 0 "s_operand" "=Q") + (ior:HI (match_operand:HI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4719,9 +4885,9 @@ (set_attr "atype" "reg")]) (define_insn "*iorqi3_ss" - [(set (match_operand:QI 0 "s_operand" "=Qo,Qo") + [(set (match_operand:QI 0 "s_operand" "=Q,Q") (ior:QI (match_dup 0) - (match_operand:QI 1 "s_imm_operand" "n,Qo"))) + (match_operand:QI 1 "s_imm_operand" "n,Q"))) (clobber (reg:CC 33))] "" "@ @@ -4731,8 +4897,8 @@ (set_attr "atype" "reg,mem")]) (define_insn "*iorqi3_ss_inv" - [(set (match_operand:QI 0 "s_operand" "=Qo,Qo") - (ior:QI (match_operand:QI 1 "s_imm_operand" "n,Qo") + [(set (match_operand:QI 0 "s_operand" "=Q,Q") + (ior:QI (match_operand:QI 1 "s_imm_operand" "n,Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4753,7 +4919,7 @@ (define_insn "*xordi3_cc" [(set (reg 33) - (compare (xor:DI (match_operand:DI 1 "register_operand" "%0,0") + (compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:DI 0 "register_operand" "=d,d") @@ -4767,7 +4933,7 @@ (define_insn "*xordi3_cconly" [(set (reg 33) - (compare (xor:DI (match_operand:DI 1 "register_operand" "%0,0") + (compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:DI 0 "=d,d"))] @@ -4780,7 +4946,7 @@ (define_insn "xordi3" [(set (match_operand:DI 0 "register_operand" "=d,d") - (xor:DI (match_operand:DI 1 "register_operand" "%0,0") + (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0") (match_operand:DI 2 "general_operand" "d,m"))) (clobber (reg:CC 33))] "TARGET_64BIT" @@ -4791,9 +4957,9 @@ (set_attr "atype" "reg,mem")]) (define_insn "*xordi3_ss" - [(set (match_operand:DI 0 "s_operand" "=Qo") + [(set (match_operand:DI 0 "s_operand" "=Q") (xor:DI (match_dup 0) - (match_operand:DI 1 "s_imm_operand" "Qo"))) + (match_operand:DI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "xc\\t%O0(8,%R0),%1" @@ -4801,8 +4967,8 @@ (set_attr "atype" "mem")]) (define_insn "*xordi3_ss_inv" - [(set (match_operand:DI 0 "s_operand" "=Qo") - (xor:DI (match_operand:DI 1 "s_imm_operand" "Qo") + [(set (match_operand:DI 0 "s_operand" "=Q") + (xor:DI (match_operand:DI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4816,7 +4982,7 @@ (define_insn "*xorsi3_cc" [(set (reg 33) - (compare (xor:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=d,d") @@ -4830,7 +4996,7 @@ (define_insn "*xorsi3_cconly" [(set (reg 33) - (compare (xor:SI (match_operand:SI 1 "register_operand" "%0,0") + (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m")) (const_int 0))) (clobber (match_scratch:SI 0 "=d,d"))] @@ -4843,7 +5009,7 @@ (define_insn "xorsi3" [(set (match_operand:SI 0 "register_operand" "=d,d") - (xor:SI (match_operand:SI 1 "register_operand" "%0,0") + (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0") (match_operand:SI 2 "general_operand" "d,m"))) (clobber (reg:CC 33))] "" @@ -4854,9 +5020,9 @@ (set_attr "atype" "reg,mem")]) (define_insn "*xorsi3_ss" - [(set (match_operand:SI 0 "s_operand" "=Qo") + [(set (match_operand:SI 0 "s_operand" "=Q") (xor:SI (match_dup 0) - (match_operand:SI 1 "s_imm_operand" "Qo"))) + (match_operand:SI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "xc\\t%O0(4,%R0),%1" @@ -4864,8 +5030,8 @@ (set_attr "atype" "mem")]) (define_insn "*xorsi3_ss_inv" - [(set (match_operand:SI 0 "s_operand" "=Qo") - (xor:SI (match_operand:SI 1 "s_imm_operand" "Qo") + [(set (match_operand:SI 0 "s_operand" "=Q") + (xor:SI (match_operand:SI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4888,9 +5054,9 @@ (set_attr "atype" "reg")]) (define_insn "*xorhi3_ss" - [(set (match_operand:HI 0 "s_operand" "=Qo") + [(set (match_operand:HI 0 "s_operand" "=Q") (xor:HI (match_dup 0) - (match_operand:HI 1 "s_imm_operand" "Qo"))) + (match_operand:HI 1 "s_imm_operand" "Q"))) (clobber (reg:CC 33))] "" "xc\\t%O0(2,%R0),%1" @@ -4898,8 +5064,8 @@ (set_attr "atype" "mem")]) (define_insn "*xorhi3_ss_inv" - [(set (match_operand:HI 0 "s_operand" "=Qo") - (xor:HI (match_operand:HI 1 "s_imm_operand" "Qo") + [(set (match_operand:HI 0 "s_operand" "=Q") + (xor:HI (match_operand:HI 1 "s_imm_operand" "Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -4922,9 +5088,9 @@ (set_attr "atype" "reg")]) (define_insn "*xorqi3_ss" - [(set (match_operand:QI 0 "s_operand" "=Qo,Qo") + [(set (match_operand:QI 0 "s_operand" "=Q,Q") (xor:QI (match_dup 0) - (match_operand:QI 1 "s_imm_operand" "n,Qo"))) + (match_operand:QI 1 "s_imm_operand" "n,Q"))) (clobber (reg:CC 33))] "" "@ @@ -4934,8 +5100,8 @@ (set_attr "atype" "mem")]) (define_insn "*xorqi3_ss_inv" - [(set (match_operand:QI 0 "s_operand" "=Qo,Qo") - (xor:QI (match_operand:QI 1 "s_imm_operand" "n,Qo") + [(set (match_operand:QI 0 "s_operand" "=Q,Q") + (xor:QI (match_operand:QI 1 "s_imm_operand" "n,Q") (match_dup 0))) (clobber (reg:CC 33))] "" @@ -5682,9 +5848,6 @@ (const_int 4) (ne (symbol_ref "TARGET_64BIT") (const_int 0)) (const_int 6) - (ne (symbol_ref "s390_pool_overflow") (const_int 0)) - (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) - (const_int 12) (const_int 14)) (eq (symbol_ref "flag_pic") (const_int 0)) (const_int 6)] (const_int 8)))]) @@ -5734,9 +5897,6 @@ (const_int 4) (ne (symbol_ref "TARGET_64BIT") (const_int 0)) (const_int 6) - (ne (symbol_ref "s390_pool_overflow") (const_int 0)) - (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) - (const_int 12) (const_int 14)) (eq (symbol_ref "flag_pic") (const_int 0)) (const_int 6)] (const_int 8)))]) @@ -5759,54 +5919,217 @@ (const_string "RR") (const_string "RX"))) (set_attr "atype" "mem")]) +;; +;;- Trap instructions. +;; + +(define_insn "trap" + [(trap_if (const_int 1) (const_int 0))] + "" + "j\\t.+2" + [(set_attr "op_type" "RX")]) + +(define_expand "conditional_trap" + [(set (match_dup 2) (match_dup 3)) + (trap_if (match_operator 0 "comparison_operator" + [(match_dup 2) (const_int 0)]) + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + enum machine_mode ccmode; + + if (operands[1] != const0_rtx) FAIL; + + ccmode = s390_select_ccmode (GET_CODE (operands[0]), + s390_compare_op0, s390_compare_op1); + operands[2] = gen_rtx_REG (ccmode, 33); + operands[3] = gen_rtx_COMPARE (ccmode, s390_compare_op0, s390_compare_op1); +}") + +(define_insn "*trap" + [(trap_if (match_operator 0 "comparison_operator" [(reg 33) (const_int 0)]) + (const_int 0))] + "" + "j%C0\\t.+2"; + [(set_attr "op_type" "RX")]) ;; -;;- Subtract one and jump if not zero. +;;- Loop instructions. ;; +;; This is all complicated by the fact that since this is a jump insn +;; we must handle our own output reloads. + +(define_expand "doloop_end" + [(use (match_operand 0 "" "")) ; loop pseudo + (use (match_operand 1 "" "")) ; iterations; zero if unknown + (use (match_operand 2 "" "")) ; max iterations + (use (match_operand 3 "" "")) ; loop level + (use (match_operand 4 "" ""))] ; label + "" + " +{ + if (GET_MODE (operands[0]) == SImode) + emit_jump_insn (gen_doloop_si (operands[4], operands[0], operands[0])); + else if (GET_MODE (operands[0]) == DImode && TARGET_64BIT) + emit_jump_insn (gen_doloop_di (operands[4], operands[0], operands[0])); + else + FAIL; -;(define_expand "decrement_and_branch_on_count" -; [(use (match_operand 0 "register_operand" "")) -; (use (label_ref (match_operand 1 "" "")))] -; "" -; " -;{ -;/* if (TARGET_64BIT) -; emit_jump_insn (gen_brctdi (operands[0], operands[1])); -; else */ -; emit_jump_insn (gen_brctsi (operands[0], operands[1])); -; DONE; -;}") -; -;(define_insn "brctsi" -; [(set (pc) -; (if_then_else -; (ne (match_operand:SI 0 "register_operand" "+a") -; (const_int 1)) -; (label_ref (match_operand 1 "" "")) -; (pc))) -; (set (match_dup 0) -; (plus:SI (match_dup 0) (const_int -1)))] -; "" -; "brct\\t%0,%l1" -; [(set_attr "op_type" "RI") -; (set_attr "type" "branch")] -;) -; -;(define_insn "ibrctsi" -; [(set (pc) -; (if_then_else -; (eq (match_operand:SI 0 "register_operand" "+a") -; (const_int 1)) -; (pc) -; (label_ref (match_operand 1 "" "")))) -; (set (match_dup 0) -; (plus:SI (match_dup 0) (const_int -1)))] -; "" -; "brct\\t%0,%l1" -; [(set_attr "op_type" "RI") -; (set_attr "type" "branch")] -;) + DONE; +}") + +(define_insn "doloop_si" + [(set (pc) + (if_then_else + (ne (match_operand:SI 1 "register_operand" "d,d") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:SI 2 "register_operand" "=1,?*m*d") + (plus:SI (match_dup 1) (const_int -1))) + (clobber (match_scratch:SI 3 "=X,&d")) + (clobber (reg:CC 33))] + "" + "* +{ + if (which_alternative != 0) + return \"#\"; + else if (get_attr_length (insn) == 4) + return \"brct\\t%1,%l0\"; + else + abort (); +}" + [(set_attr "op_type" "RI") + (set (attr "length") + (cond [(lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) + (ne (symbol_ref "TARGET_64BIT") (const_int 0)) + (const_int 10) + (eq (symbol_ref "flag_pic") (const_int 0)) + (const_int 6)] (const_int 8)))]) + +(define_insn "*doloop_si_long" + [(set (pc) + (if_then_else + (ne (match_operand:SI 1 "register_operand" "d,d") + (const_int 1)) + (match_operand 0 "address_operand" "p,p") + (pc))) + (set (match_operand:SI 2 "register_operand" "=1,?*m*d") + (plus:SI (match_dup 1) (const_int -1))) + (clobber (match_scratch:SI 3 "=X,&d")) + (clobber (reg:CC 33))] + "" + "* +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return \"bctr\\t%1,%0\"; + else + return \"bct\\t%1,%a0\"; +}" + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "atype" "mem")]) + +(define_split + [(set (pc) + (if_then_else (ne (match_operand:SI 1 "register_operand" "") + (const_int 1)) + (match_operand 0 "" "") + (pc))) + (set (match_operand:SI 2 "nonimmediate_operand" "") + (plus:SI (match_dup 1) (const_int -1))) + (clobber (match_scratch:SI 3 "")) + (clobber (reg:CC 33))] + "reload_completed + && (! REG_P (operands[2]) + || ! rtx_equal_p (operands[1], operands[2]))" + [(set (match_dup 3) (match_dup 1)) + (parallel [(set (reg:CCAN 33) + (compare:CCAN (plus:SI (match_dup 3) (const_int -1)) + (const_int 0))) + (set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))]) + (set (match_dup 2) (match_dup 3)) + (set (pc) (if_then_else (ne (reg:CCAN 33) (const_int 0)) + (match_dup 0) + (pc)))] + "") + +(define_insn "doloop_di" + [(set (pc) + (if_then_else + (ne (match_operand:DI 1 "register_operand" "d,d") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:DI 2 "register_operand" "=1,?*m*r") + (plus:DI (match_dup 1) (const_int -1))) + (clobber (match_scratch:DI 3 "=X,&d")) + (clobber (reg:CC 33))] + "TARGET_64BIT" + "* +{ + if (which_alternative != 0) + return \"#\"; + else if (get_attr_length (insn) == 4) + return \"brctg\\t%1,%l0\"; + else + abort (); +}" + [(set_attr "op_type" "RI") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 12)))]) + +(define_insn "*doloop_di_long" + [(set (pc) + (if_then_else + (ne (match_operand:DI 1 "register_operand" "d,d") + (const_int 1)) + (match_operand 0 "address_operand" "p,p") + (pc))) + (set (match_operand:DI 2 "register_operand" "=1,?*m*d") + (plus:DI (match_dup 1) (const_int -1))) + (clobber (match_scratch:DI 3 "=X,&d")) + (clobber (reg:CC 33))] + "" + "* +{ + if (get_attr_op_type (insn) == OP_TYPE_RRE) + return \"bctgr\\t%1,%0\"; + else + return \"bctg\\t%1,%a0\"; +}" + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RRE") (const_string "RXE"))) + (set_attr "atype" "mem")]) +(define_split + [(set (pc) + (if_then_else (ne (match_operand:DI 1 "register_operand" "") + (const_int 1)) + (match_operand 0 "" "") + (pc))) + (set (match_operand:DI 2 "nonimmediate_operand" "") + (plus:DI (match_dup 1) (const_int -1))) + (clobber (match_scratch:DI 3 "")) + (clobber (reg:CC 33))] + "reload_completed + && (! REG_P (operands[2]) + || ! rtx_equal_p (operands[1], operands[2]))" + [(set (match_dup 3) (match_dup 1)) + (parallel [(set (reg:CCAN 33) + (compare:CCAN (plus:DI (match_dup 3) (const_int -1)) + (const_int 0))) + (set (match_dup 3) (plus:DI (match_dup 3) (const_int -1)))]) + (set (match_dup 2) (match_dup 3)) + (set (pc) (if_then_else (ne (reg:CCAN 33) (const_int 0)) + (match_dup 0) + (pc)))] + "") ;; ;;- Unconditional jump instructions. @@ -5961,7 +6284,8 @@ [(unspec_volatile [(const_int 0)] 0)] "" "" - [(set_attr "type" "none")]) + [(set_attr "type" "none") + (set_attr "length" "0")]) @@ -6014,10 +6338,7 @@ compiler doesn't know about it, because the PLT glue code uses it. In 64-bit, this is not necessary. */ if (plt_call && !TARGET_64BIT) - { - current_function_uses_pic_offset_table = 1; - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); - } + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); DONE; }") @@ -6139,10 +6460,7 @@ compiler doesn't know about it, because the PLT glue code uses it. In 64-bit, this is not necessary. */ if (plt_call && !TARGET_64BIT) - { - current_function_uses_pic_offset_table = 1; - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); - } + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); DONE; }") @@ -6218,6 +6536,192 @@ (set_attr "type" "jsr") (set_attr "atype" "mem")]) +;; +;;- Thread-local storage support. +;; + +(define_insn "get_tp_64" + [(set (match_operand:DI 0 "nonimmediate_operand" "=??d,Q") + (unspec:DI [(const_int 0)] UNSPEC_TP))] + "TARGET_64BIT" + "@ + ear\\t%0,%%a0\;sllg\\t%0,%0,32\;ear\\t%0,%%a1 + stam\\t%%a0,%%a1,%0" + [(set_attr "op_type" "NN,RS") + (set_attr "atype" "reg,mem") + (set_attr "type" "o3,*") + (set_attr "length" "14,*")]) + +(define_insn "get_tp_31" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,Q") + (unspec:SI [(const_int 0)] UNSPEC_TP))] + "!TARGET_64BIT" + "@ + ear\\t%0,%%a0 + stam\\t%%a0,%%a0,%0" + [(set_attr "op_type" "RRE,RS") + (set_attr "atype" "reg,mem")]) + +(define_insn "set_tp_64" + [(unspec_volatile [(match_operand:DI 0 "general_operand" "??d,Q")] UNSPECV_SET_TP) + (clobber (match_scratch:SI 1 "=d,X"))] + "TARGET_64BIT" + "@ + sar\\t%%a1,%0\;srlg\\t%1,%0,32\;sar\\t%%a0,%1 + lam\\t%%a0,%%a1,%0" + [(set_attr "op_type" "NN,RS") + (set_attr "atype" "reg,mem") + (set_attr "type" "o3,*") + (set_attr "length" "14,*")]) + +(define_insn "set_tp_31" + [(unspec_volatile [(match_operand:SI 0 "general_operand" "d,Q")] UNSPECV_SET_TP)] + "!TARGET_64BIT" + "@ + sar\\t%%a0,%0 + lam\\t%%a0,%%a0,%0" + [(set_attr "op_type" "RRE,RS") + (set_attr "atype" "reg,mem")]) + +(define_insn "*tls_load_64" + [(set (match_operand:DI 0 "register_operand" "=d") + (unspec:DI [(match_operand:DI 1 "memory_operand" "m") + (match_operand:DI 2 "" "")] + UNSPEC_TLS_LOAD))] + "TARGET_64BIT" + "lg\\t%0,%1%J2" + [(set_attr "op_type" "RXE") + (set_attr "atype" "mem")]) + +(define_insn "*tls_load_31" + [(set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(match_operand:SI 1 "memory_operand" "m") + (match_operand:SI 2 "" "")] + UNSPEC_TLS_LOAD))] + "!TARGET_64BIT" + "l\\t%0,%1%J2" + [(set_attr "op_type" "RX") + (set_attr "atype" "mem")]) + +(define_expand "call_value_tls" + [(set (match_operand 0 "" "") + (call (const_int 0) (const_int 0))) + (use (match_operand 1 "" ""))] + "" + " +{ + rtx insn, sym; + + if (!flag_pic) + abort (); + + sym = s390_tls_get_offset (); + sym = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym), 113); + sym = gen_rtx_CONST (Pmode, sym); + + /* Unless we can use the bras(l) insn, force the + routine address into a register. */ + if (!TARGET_SMALL_EXEC && !TARGET_64BIT) + { + rtx target = gen_reg_rtx (Pmode); + emit_move_insn (target, sym); + sym = target; + } + + sym = gen_rtx_MEM (QImode, sym); + + /* Emit insn. */ + insn = emit_call_insn ( + gen_call_value_tls_exp (operands[0], sym, const0_rtx, + gen_rtx_REG (Pmode, RETURN_REGNUM), + operands[1])); + + /* The calling convention of __tls_get_offset uses the + GOT register implicitly. */ + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), operands[0]); + CONST_OR_PURE_CALL_P (insn) = 1; + + DONE; +}") + +(define_expand "call_value_tls_exp" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" ""))) + (clobber (match_operand 3 "" "")) + (use (match_operand 4 "" ""))])] + "" + "") + +(define_insn "brasl_tls" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:QI (match_operand:DI 1 "bras_sym_operand" "X")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand:DI 3 "register_operand" "=r")) + (use (match_operand:DI 4 "" ""))] + "TARGET_64BIT" + "brasl\\t%3,%1%J4" + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr")]) + +(define_insn "bras_tls" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:QI (match_operand:SI 1 "bras_sym_operand" "X")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand:SI 3 "register_operand" "=r")) + (use (match_operand:SI 4 "" ""))] + "TARGET_SMALL_EXEC" + "bras\\t%3,%1%J4" + [(set_attr "op_type" "RI") + (set_attr "type" "jsr")]) + +(define_insn "basr_tls_64" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:QI (match_operand:DI 1 "register_operand" "a")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand:DI 3 "register_operand" "=r")) + (use (match_operand:DI 4 "" ""))] + "TARGET_64BIT" + "basr\\t%3,%1%J4" + [(set_attr "op_type" "RR") + (set_attr "type" "jsr")]) + +(define_insn "basr_tls_31" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:QI (match_operand:SI 1 "register_operand" "a")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand:SI 3 "register_operand" "=r")) + (use (match_operand:SI 4 "" ""))] + "!TARGET_64BIT" + "basr\\t%3,%1%J4" + [(set_attr "op_type" "RR") + (set_attr "type" "jsr") + (set_attr "atype" "mem")]) + +(define_insn "bas_tls_64" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:QI (match_operand:QI 1 "address_operand" "p")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand:DI 3 "register_operand" "=r")) + (use (match_operand:DI 4 "" ""))] + "TARGET_64BIT" + "bas\\t%3,%a1%J4" + [(set_attr "op_type" "RX") + (set_attr "type" "jsr") + (set_attr "atype" "mem")]) + +(define_insn "bas_tls_31" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:QI (match_operand:QI 1 "address_operand" "p")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand:SI 3 "register_operand" "=r")) + (use (match_operand:SI 4 "" ""))] + "!TARGET_64BIT" + "bas\\t%3,%a1%J4" + [(set_attr "op_type" "RX") + (set_attr "type" "jsr") + (set_attr "atype" "mem")]) ;; ;;- Miscellaneous instructions. @@ -6505,7 +7009,7 @@ [(set_attr "op_type" "NN") (set_attr "length" "0")]) -(define_insn "reload_base" +(define_insn "reload_base_31" [(set (match_operand:SI 0 "register_operand" "=a") (unspec:SI [(label_ref (match_operand 1 "" ""))] 210))] "!TARGET_64BIT" @@ -6514,15 +7018,29 @@ (set_attr "type" "la") (set_attr "length" "6")]) -(define_insn "reload_base2" +(define_insn "reload_base_64" + [(set (match_operand:DI 0 "register_operand" "=a") + (unspec:DI [(label_ref (match_operand 1 "" ""))] 210))] + "TARGET_64BIT" + "larl\\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "la")]) + +(define_insn "reload_anchor" [(set (match_operand:SI 0 "register_operand" "=a") - (unspec:SI [(label_ref (match_operand 1 "" ""))] 211))] + (unspec:SI [(match_operand:SI 1 "register_operand" "a")] 211))] "!TARGET_64BIT" - "la\\t%0,%1-.(%0)" + "l\\t%0,0(%1)\;la\\t%0,0(%0,%1)" [(set_attr "op_type" "NN") (set_attr "type" "la") - (set_attr "length" "4")]) + (set_attr "length" "8")]) +(define_insn "pool" + [(unspec_volatile [(match_operand 0 "const_int_operand" "n")] 220)] + "" + "* abort ();" + [(set_attr "op_type" "NN") + (set (attr "length") (symbol_ref "INTVAL (operands[0])"))]) ;; ;; Insns related to generating the function prologue and epilogue. @@ -6566,162 +7084,44 @@ (set_attr "type" "jsr") (set_attr "atype" "mem")]) - -(define_insn "lit" - [(set (reg 13) (pc)) - (unspec_volatile [(const_int 0)] 200)] +(define_insn "literal_pool_31" + [(unspec_volatile [(const_int 0)] 300) + (set (match_operand:SI 0 "register_operand" "=a") + (label_ref (match_operand 1 "" ""))) + (use (label_ref (match_operand 2 "" "")))] "" - "* { - s390_output_constant_pool (asm_out_file); - return \"\"; -}" - [(set_attr "op_type" "NN") - (set_attr "type" "integer")]) - - -;; -;; Peephole optimization patterns. -;; - -(define_peephole - [(set (match_operand:SI 0 "memory_operand" "m") - (match_operand:SI 1 "register_operand" "d")) - (set (match_dup 1) - (match_dup 0))] - "" - "st\\t%1,%0") - -(define_peephole - [(set (match_operand:SI 0 "memory_operand" "m") - (match_operand:SI 1 "register_operand" "d")) - (set (match_dup 0) - (match_dup 1))] - "" - "st\\t%1,%0") - -(define_peephole - [(set (match_operand:SI 0 "register_operand" "") - (match_operand:SI 1 "register_operand" "")) - (parallel - [(set (match_dup 0) - (plus:SI (match_dup 0) - (match_operand:SI 2 "immediate_operand" ""))) - (clobber (reg:CC 33))])] - "(REGNO (operands[0]) == STACK_POINTER_REGNUM || - REGNO (operands[1]) == STACK_POINTER_REGNUM || - REGNO (operands[0]) == BASE_REGISTER || - REGNO (operands[1]) == BASE_REGISTER) && - INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 4096" - "la\\t%0,%c2(%1)") - -; -; peepholes for fast char instructions -; - -;(define_peephole -; [(set (match_operand:QI 0 "register_operand" "d") -; (match_operand:QI 1 "s_operand" "Q")) -; (set (match_operand:SI 2 "register_operand" "0") -; (zero_extend:SI (match_dup 0)))] -; "REGNO(operands[0]) == REGNO(operands[2])" -; "icm\\t%0,8,%1\;srl\\t%0,24") - -;(define_peephole -; [(set (match_operand:QI 0 "register_operand" "d") -; (match_operand:QI 1 "s_operand" "Q")) -; (set (match_operand:SI 2 "register_operand" "0") -; (sign_extend:SI (match_dup 0)))] -; "REGNO(operands[0]) == REGNO(operands[2])" -; "icm\\t%0,8,%1\;sra\\t%0,24") - -(define_peephole - [(set (match_operand:QI 0 "register_operand" "d") - (match_operand:QI 1 "immediate_operand" "J")) - (set (match_operand:SI 2 "register_operand" "0" ) - (sign_extend:SI (match_dup 0) ) )] - "REGNO(operands[0]) == REGNO(operands[2])" - "lhi\\t%0,%h1") - -; -; peepholes for fast short instructions -; - -;(define_peephole -; [(set (match_operand:HI 0 "register_operand" "d") -; (match_operand:HI 1 "s_operand" "Q")) -; (set (match_operand:SI 2 "register_operand" "0" ) -; (zero_extend:SI (match_dup 0)))] -; "REGNO(operands[0]) == REGNO(operands[2])" -; "icm\\t%0,12,%1\;srl\\t%0,16") - -(define_peephole - [(set (match_operand:HI 0 "register_operand" "d") - (match_operand:HI 1 "memory_operand" "m")) - (set (match_operand:SI 2 "register_operand" "0" ) - (sign_extend:SI (match_dup 0)))] - "REGNO(operands[0]) == REGNO(operands[2])" - "lh\\t%0,%1") - -(define_peephole - [(set (match_operand:HI 0 "register_operand" "d") - (match_operand:HI 1 "immediate_operand" "K")) - (set (match_operand:SI 2 "register_operand" "0" ) - (sign_extend:SI (match_dup 0) ) )] - "REGNO(operands[0]) == REGNO(operands[2])" - "lhi\\t%0,%h1") - -; -; peepholes for divide instructions -; - -(define_peephole - [(set (match_operand:DI 0 "register_operand" "d") - (match_operand:DI 1 "memory_operand" "m")) - (set (match_dup 0) - (lshiftrt:DI (match_dup 0) - (match_operand:SI 2 "immediate_operand" "J"))) - (set (match_dup 0) - (div:SI (match_dup 0) - (match_operand:SI 3 "nonimmediate_operand" "g"))) - (set (match_dup 1) - (match_dup 0))] - "" - "* -{ - output_asm_insn (\"l\\t%0,%1\", operands); - output_asm_insn (\"srdl\\t%0,%b2\", operands); - - if (REG_P (operands[3])) - output_asm_insn (\"dr\\t%0,%3\", operands); - else - output_asm_insn (\"d\\t%0,%3\", operands); + if (s390_nr_constants) + { + output_asm_insn ("bras\\t%0,%2", operands); + s390_output_constant_pool (operands[1], operands[2]); + } + else if (flag_pic) + { + /* We need the anchor label in any case. */ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (operands[1])); + } - return \"st\\t%N0,%N1\"; -}") + return ""; +} + [(set_attr "op_type" "NN") + (set_attr "type" "la")]) -(define_peephole - [(set (match_operand:DI 0 "register_operand" "d") - (match_operand:DI 1 "memory_operand" "m")) - (set (match_dup 0) - (lshiftrt:DI (match_dup 0) - (match_operand:SI 2 "immediate_operand" "J"))) - (set (match_dup 0) - (mod:SI (match_dup 0) - (match_operand:SI 3 "nonimmediate_operand" "g"))) - (set (match_dup 1) - (match_dup 0))] +(define_insn "literal_pool_64" + [(unspec_volatile [(const_int 0)] 300) + (set (match_operand:DI 0 "register_operand" "=a") + (label_ref (match_operand 1 "" ""))) + (use (label_ref (match_operand 2 "" "")))] "" - "* { - output_asm_insn (\"l\\t%0,%1\", operands); - output_asm_insn (\"srdl\\t%0,%b2\", operands); - - if (REG_P (operands[3])) - output_asm_insn (\"dr\\t%0,%3\", operands); - else - output_asm_insn (\"d\\t%0,%3\", operands); - - return \"st\\t%0,%1\"; -}") + if (s390_nr_constants) + { + output_asm_insn ("larl\\t%0,%1", operands); + s390_output_constant_pool (operands[1], operands[2]); + } + return ""; +} + [(set_attr "op_type" "NN") + (set_attr "type" "la")]) diff --git a/contrib/gcc/config/s390/t-crtstuff b/contrib/gcc/config/s390/t-crtstuff new file mode 100644 index 0000000..5572e6b --- /dev/null +++ b/contrib/gcc/config/s390/t-crtstuff @@ -0,0 +1,4 @@ +# crtend*.o cannot be compiled without -fno-asynchronous-unwind-tables, +# because then __FRAME_END__ might not be the last thing in .eh_frame +# section. +CRTSTUFF_T_CFLAGS = -fno-asynchronous-unwind-tables diff --git a/contrib/gcc/config/s390/t-linux64 b/contrib/gcc/config/s390/t-linux64 index d5a9278..db839c6 100644 --- a/contrib/gcc/config/s390/t-linux64 +++ b/contrib/gcc/config/s390/t-linux64 @@ -1,3 +1,12 @@ +MULTILIB_OPTIONS = m64/m31 +MULTILIB_DIRNAMES = 64 32 +MULTILIB_OSDIRNAMES = ../lib64 ../lib + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib + +EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o + # Override t-slibgcc-elf-ver to export some libgcc symbols with # the symbol versions that glibc used. SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver $(srcdir)/config/s390/libgcc-glibc.ver |