diff options
Diffstat (limited to 'gcc/config/s390/s390.c')
-rw-r--r-- | gcc/config/s390/s390.c | 799 |
1 files changed, 736 insertions, 63 deletions
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 3f6ec452b1c..05b72f70876 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 *)); @@ -910,6 +939,7 @@ larl_operand (op, mode) if (GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == SYMBOL_REF + && !tls_symbolic_operand (op) && (!flag_pic || SYMBOL_REF_FLAG (op) || CONSTANT_POOL_ADDRESS_P (op))) return 1; @@ -932,17 +962,22 @@ larl_operand (op, mode) if (GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == SYMBOL_REF + && !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 +1126,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 +1302,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 +1347,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 +1362,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 +1489,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 @@ -1546,6 +1685,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 +1779,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 +1833,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 +1923,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 +1939,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; } @@ -2098,18 +2244,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 +2497,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 @@ -2544,6 +2916,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 +3030,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 +3099,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 +3125,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 +4585,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 +4711,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 +4899,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 +4923,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 +4964,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 +5193,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 +5241,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 +5260,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); @@ -4870,14 +5358,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 +5935,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 +6222,85 @@ 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 (!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 (GET_CODE (rtl) == MEM) + 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); } } +/* 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; + 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 |