diff options
Diffstat (limited to 'gcc/config/s390/s390.c')
-rw-r--r-- | gcc/config/s390/s390.c | 962 |
1 files changed, 876 insertions, 86 deletions
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 3f6ec452b1c..d6fda655a53 100644 --- a/gcc/config/s390/s390.c +++ b/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). @@ -54,6 +54,11 @@ 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)); @@ -81,6 +86,20 @@ static void s390_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, #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 @@ -98,6 +117,9 @@ static int s390_sr_alias_set = 0; 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 @@ -132,6 +154,9 @@ struct machine_function GTY(()) /* 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)); @@ -140,6 +165,10 @@ 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 *)); +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 int s390_split_branches PARAMS ((rtx, bool *)); @@ -805,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. @@ -910,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; @@ -932,17 +999,23 @@ 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; } @@ -1091,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. @@ -1250,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 @@ -1264,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; } @@ -1279,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 @@ -1341,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 @@ -1397,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. @@ -1546,6 +1746,22 @@ s390_decompose_address (addr, out) 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) { @@ -1624,10 +1840,11 @@ s390_decompose_address (addr, out) } /* 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; @@ -1677,12 +1894,6 @@ s390_decompose_address (addr, out) || !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))) @@ -1773,14 +1984,11 @@ legitimate_la_operand_p (op) } /* Return 1 if OP is a valid operand for the LA instruction, - and we prefer to use LA over addition to compute it. - If STRICT is true, only accept operands that will never - change to something we cannot recognize as preferred. */ + and we prefer to use LA over addition to compute it. */ int -preferred_la_operand_p (op, strict) +preferred_la_operand_p (op) register rtx op; - int strict; { struct s390_address addr; if (!s390_decompose_address (op, &addr)) @@ -1792,10 +2000,9 @@ preferred_la_operand_p (op, strict) if (addr.pointer) return TRUE; - if (!strict) - if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base)) - || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx))) - return TRUE; + if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base)) + || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx))) + return TRUE; return FALSE; } @@ -1848,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 @@ -1860,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); @@ -1923,9 +2130,9 @@ legitimize_pic_address (orig, reg) 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); @@ -1951,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. */ @@ -1966,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); @@ -2002,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) { @@ -2039,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); @@ -2068,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. */ @@ -2098,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); } @@ -2132,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 @@ -2420,7 +2853,7 @@ s390_expand_cmpstr (target, op0, op1, len) emit_move_insn (target, const0_rtx); } - else if (TARGET_MVCLE) + else /* if (TARGET_MVCLE) */ { enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode; enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode; @@ -2439,6 +2872,9 @@ s390_expand_cmpstr (target, op0, op1, len) 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; @@ -2504,6 +2940,7 @@ s390_expand_cmpstr (target, op0, op1, len) emit_insn ((*gen_result) (target)); } +#endif } /* In the name of slightly smaller debug output, and to cater to @@ -2544,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. */ @@ -2616,6 +3095,30 @@ s390_output_symbolic_const (file, x) 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)"); break; @@ -2661,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. @@ -2686,6 +3190,26 @@ 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; @@ -4126,11 +4650,21 @@ s390_optimize_prolog (temp_regno) for (i = 6; i < 16; i++) if (regs_ever_live[i]) - break; + 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]) - break; + 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) { @@ -4242,6 +4776,12 @@ s390_fixup_clobbered_return_reg (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; @@ -4424,7 +4964,7 @@ s390_frame_info () cfun->machine->save_fprs_p = 0; if (TARGET_64BIT) for (i = 24; i < 32; i++) - if (regs_ever_live[i]) + if (regs_ever_live[i] && !global_regs[i]) { cfun->machine->save_fprs_p = 1; break; @@ -4448,8 +4988,11 @@ s390_frame_info () prolog/epilog code is modified again. */ for (i = 0; i < 16; i++) - gprs_ever_live[i] = regs_ever_live[i]; + gprs_ever_live[i] = regs_ever_live[i] && !global_regs[i]; + 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; @@ -4486,7 +5029,7 @@ s390_arg_frame_offset () save_fprs_p = 0; if (TARGET_64BIT) for (i = 24; i < 32; i++) - if (regs_ever_live[i]) + if (regs_ever_live[i] && !global_regs[i]) { save_fprs_p = 1; break; @@ -4715,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; @@ -4763,6 +5306,16 @@ 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). */ @@ -4772,7 +5325,7 @@ s390_emit_prologue () 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, cfun->machine->frame_size - 64 + (i-24)*8); @@ -4823,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)); } @@ -4870,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; @@ -5447,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. @@ -5606,28 +6289,104 @@ s390_select_rtx_section (mode, x, align) function_section (current_function_decl); } -/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we - may access it directly in the GOT. */ +/* 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)) { - rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd' - ? TREE_CST_RTL (decl) : DECL_RTL (decl)); + const char *symbol_str = XSTR (symbol, 0); + char *newstr; + size_t len; + enum tls_model kind = decl_tls_model (decl); - if (GET_CODE (rtl) == MEM) + 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] == '%') { - SYMBOL_REF_FLAG (XEXP (rtl, 0)) - = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd' - || ! TREE_PUBLIC (decl)); + 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 @@ -5642,13 +6401,16 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function) HOST_WIDE_INT vcall_offset; tree function; { - rtx op[9]; + 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])) { - op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), 113); + 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]); } @@ -5673,6 +6435,9 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function) 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) { @@ -5798,14 +6563,39 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function) /* Jump to target. */ op[8] = gen_label_rtx (); + if (!flag_pic) output_asm_insn ("l\t%4,%8-%5(%4)", op); - else + 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); |