aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/sh/sh.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/sh/sh.c')
-rw-r--r--gcc/config/sh/sh.c315
1 files changed, 275 insertions, 40 deletions
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index a153845..530fe53 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -251,6 +251,7 @@ static rtx sh_expand_builtin (tree, rtx, rtx, machine_mode, int);
static void sh_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree);
static void sh_file_start (void);
+static bool sh_assemble_integer (rtx, unsigned int, int);
static bool flow_dependent_p (rtx, rtx);
static void flow_dependent_p_1 (rtx, const_rtx, void *);
static int shiftcosts (rtx);
@@ -259,6 +260,7 @@ static int addsubcosts (rtx);
static int multcosts (rtx);
static bool unspec_caller_rtx_p (rtx);
static bool sh_cannot_copy_insn_p (rtx_insn *);
+static bool sh_cannot_force_const_mem_p (machine_mode, rtx);
static bool sh_rtx_costs (rtx, machine_mode, int, int, int *, bool);
static int sh_address_cost (rtx, machine_mode, addr_space_t, bool);
static int sh_pr_n_sets (void);
@@ -404,6 +406,9 @@ static const struct attribute_spec sh_attribute_table[] =
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+#undef TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER sh_assemble_integer
+
#undef TARGET_REGISTER_MOVE_COST
#define TARGET_REGISTER_MOVE_COST sh_register_move_cost
@@ -662,6 +667,9 @@ static const struct attribute_spec sh_attribute_table[] =
#undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
#define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 0x80
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM sh_cannot_force_const_mem_p
+
struct gcc_target targetm = TARGET_INITIALIZER;
@@ -979,6 +987,13 @@ sh_option_override (void)
if (! global_options_set.x_TARGET_ZDCBRANCH && TARGET_HARD_SH4)
TARGET_ZDCBRANCH = 1;
+ /* FDPIC code is a special form of PIC, and the vast majority of code
+ generation constraints that apply to PIC also apply to FDPIC, so we
+ set flag_pic to avoid the need to check TARGET_FDPIC everywhere
+ flag_pic is checked. */
+ if (TARGET_FDPIC && !flag_pic)
+ flag_pic = 2;
+
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (! VALID_REGISTER_P (regno))
sh_register_names[regno][0] = '\0';
@@ -1670,6 +1685,14 @@ sh_asm_output_addr_const_extra (FILE *file, rtx x)
output_addr_const (file, XVECEXP (x, 0, 1));
fputs ("-.)", file);
break;
+ case UNSPEC_GOTFUNCDESC:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTFUNCDESC", file);
+ break;
+ case UNSPEC_GOTOFFFUNCDESC:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTOFFFUNCDESC", file);
+ break;
default:
return false;
}
@@ -1854,6 +1877,9 @@ prepare_move_operands (rtx operands[], machine_mode mode)
{
case TLS_MODEL_GLOBAL_DYNAMIC:
tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ if (TARGET_FDPIC)
+ emit_move_insn (gen_rtx_REG (Pmode, PIC_REG),
+ sh_get_fdpic_reg_initial_val ());
emit_call_insn (gen_tls_global_dynamic (tga_ret, op1));
tmp = gen_reg_rtx (Pmode);
emit_move_insn (tmp, tga_ret);
@@ -1862,6 +1888,9 @@ prepare_move_operands (rtx operands[], machine_mode mode)
case TLS_MODEL_LOCAL_DYNAMIC:
tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ if (TARGET_FDPIC)
+ emit_move_insn (gen_rtx_REG (Pmode, PIC_REG),
+ sh_get_fdpic_reg_initial_val ());
emit_call_insn (gen_tls_local_dynamic (tga_ret, op1));
tmp = gen_reg_rtx (Pmode);
@@ -1879,6 +1908,9 @@ prepare_move_operands (rtx operands[], machine_mode mode)
case TLS_MODEL_INITIAL_EXEC:
tga_op1 = !can_create_pseudo_p () ? op0 : gen_reg_rtx (Pmode);
tmp = gen_sym2GOTTPOFF (op1);
+ if (TARGET_FDPIC)
+ emit_move_insn (gen_rtx_REG (Pmode, PIC_REG),
+ sh_get_fdpic_reg_initial_val ());
emit_insn (gen_tls_initial_exec (tga_op1, tmp));
op1 = tga_op1;
break;
@@ -1905,6 +1937,22 @@ prepare_move_operands (rtx operands[], machine_mode mode)
operands[1] = op1;
}
}
+
+ if (SH_OFFSETS_MUST_BE_WITHIN_SECTIONS_P)
+ {
+ rtx base, offset;
+ split_const (operands[1], &base, &offset);
+
+ if (GET_CODE (base) == SYMBOL_REF
+ && !offset_within_block_p (base, INTVAL (offset)))
+ {
+ rtx tmp = can_create_pseudo_p () ? gen_reg_rtx (mode) : operands[0];
+ emit_move_insn (tmp, base);
+ if (!arith_operand (offset, mode))
+ offset = force_reg (mode, offset);
+ emit_insn (gen_add3_insn (operands[0], tmp, offset));
+ }
+ }
}
/* Implement the canonicalize_comparison target hook for the combine
@@ -3009,6 +3057,24 @@ sh_file_start (void)
}
}
+/* Implementation of TARGET_ASM_INTEGER for SH. Pointers to functions
+ need to be output as pointers to function descriptors for
+ FDPIC. */
+
+static bool
+sh_assemble_integer (rtx value, unsigned int size, int aligned_p)
+{
+ if (TARGET_FDPIC && size == UNITS_PER_WORD
+ && GET_CODE (value) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (value))
+ {
+ fputs ("\t.long\t", asm_out_file);
+ output_addr_const (asm_out_file, value);
+ fputs ("@FUNCDESC\n", asm_out_file);
+ return true;
+ }
+ return default_assemble_integer (value, size, aligned_p);
+}
+
/* Check if PAT includes UNSPEC_CALLER unspec pattern. */
static bool
unspec_caller_rtx_p (rtx pat)
@@ -3044,6 +3110,17 @@ sh_cannot_copy_insn_p (rtx_insn *insn)
return false;
pat = PATTERN (insn);
+
+ if (GET_CODE (pat) == CLOBBER || GET_CODE (pat) == USE)
+ return false;
+
+ if (TARGET_FDPIC && GET_CODE (pat) == PARALLEL)
+ {
+ rtx t = XVECEXP (pat, 0, XVECLEN (pat, 0) - 1);
+ if (GET_CODE (t) == USE && unspec_caller_rtx_p (XEXP (t, 0)))
+ return true;
+ }
+
if (GET_CODE (pat) != SET)
return false;
pat = SET_SRC (pat);
@@ -4085,8 +4162,8 @@ expand_ashiftrt (rtx *operands)
/* Load the value into an arg reg and call a helper. */
emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
sprintf (func, "__ashiftrt_r4_%d", value);
- function_symbol (wrk, func, SFUNC_STATIC);
- emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk));
+ rtx lab = function_symbol (wrk, func, SFUNC_STATIC).lab;
+ emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk, lab));
emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
return true;
}
@@ -7937,7 +8014,8 @@ sh_expand_prologue (void)
stack_usage += d;
}
- if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+ if (flag_pic && !TARGET_FDPIC
+ && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
emit_insn (gen_GOTaddr2picreg (const0_rtx));
if (SHMEDIA_REGS_STACK_ADJUST ())
@@ -10438,7 +10516,9 @@ nonpic_symbol_mentioned_p (rtx x)
|| XINT (x, 1) == UNSPEC_PLT
|| XINT (x, 1) == UNSPEC_PCREL
|| XINT (x, 1) == UNSPEC_SYMOFF
- || XINT (x, 1) == UNSPEC_PCREL_SYMOFF))
+ || XINT (x, 1) == UNSPEC_PCREL_SYMOFF
+ || XINT (x, 1) == UNSPEC_GOTFUNCDESC
+ || XINT (x, 1) == UNSPEC_GOTOFFFUNCDESC))
return false;
fmt = GET_RTX_FORMAT (GET_CODE (x));
@@ -10473,7 +10553,26 @@ legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED,
if (reg == NULL_RTX)
reg = gen_reg_rtx (Pmode);
- emit_insn (gen_symGOTOFF2reg (reg, orig));
+ if (TARGET_FDPIC
+ && GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (orig))
+ {
+ /* Weak functions may be NULL which doesn't work with
+ GOTOFFFUNCDESC because the runtime offset is not known. */
+ if (SYMBOL_REF_WEAK (orig))
+ emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+ else
+ emit_insn (gen_symGOTOFFFUNCDESC2reg (reg, orig));
+ }
+ else if (TARGET_FDPIC
+ && (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_DECL (orig)
+ && (TREE_READONLY (SYMBOL_REF_DECL (orig))
+ || SYMBOL_REF_EXTERNAL_P (orig)
+ || DECL_SECTION_NAME(SYMBOL_REF_DECL (orig))))))
+ /* In FDPIC, GOTOFF can only be used for writable data. */
+ emit_insn (gen_symGOT2reg (reg, orig));
+ else
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
return reg;
}
else if (GET_CODE (orig) == SYMBOL_REF)
@@ -10481,7 +10580,10 @@ legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED,
if (reg == NULL_RTX)
reg = gen_reg_rtx (Pmode);
- emit_insn (gen_symGOT2reg (reg, orig));
+ if (TARGET_FDPIC && SYMBOL_REF_FUNCTION_P (orig))
+ emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+ else
+ emit_insn (gen_symGOT2reg (reg, orig));
return reg;
}
return orig;
@@ -11519,8 +11621,39 @@ sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
5 0008 00000000 l1: .long area
6 000c 00000000 l2: .long function
+ FDPIC needs a form that includes a function descriptor and
+ code to load the GOT register:
+ 0 0000 00000000 .long l0
+ 1 0004 00000000 .long gotval
+ 2 0008 D302 l0: mov.l l1,r3
+ 3 000a D203 mov.l l2,r2
+ 4 000c 6122 mov.l @r2,r1
+ 5 000e 5C21 mov.l @(4,r2),r12
+ 6 0010 412B jmp @r1
+ 7 0012 0009 nop
+ 8 0014 00000000 l1: .long area
+ 9 0018 00000000 l2: .long function
+
SH5 (compact) uses r1 instead of r3 for the static chain. */
+/* Emit insns to store a value at memory address + offset. */
+static void
+sh_emit_storesi (rtx addr, HOST_WIDE_INT offset, rtx value)
+{
+ gcc_assert ((offset & 3) == 0);
+ emit_move_insn (offset == 0
+ ? change_address (addr, SImode, NULL_RTX)
+ : adjust_address (addr, SImode, offset), value);
+}
+
+/* Emit insns to store w0 at addr + offset and w1 at addr + offset + 2. */
+static void
+sh_emit_storehi (rtx addr, HOST_WIDE_INT offset, uint16_t w0, uint16_t w1)
+{
+ sh_emit_storesi (addr, offset, gen_int_mode (TARGET_LITTLE_ENDIAN
+ ? (w0 | (w1 << 16))
+ : (w1 | (w0 << 16)), SImode));
+}
/* Emit RTL insns to initialize the variable parts of a trampoline.
FNADDR is an RTX for the address of the function's pure code.
@@ -11655,20 +11788,34 @@ sh_trampoline_init (rtx tramp_mem, tree fndecl, rtx cxt)
emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
return;
}
- emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX),
- gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
- SImode));
- emit_move_insn (adjust_address (tramp_mem, SImode, 4),
- gen_int_mode (TARGET_LITTLE_ENDIAN ? 0x0009422b : 0x422b0009,
- SImode));
- emit_move_insn (adjust_address (tramp_mem, SImode, 8), cxt);
- emit_move_insn (adjust_address (tramp_mem, SImode, 12), fnaddr);
+ if (TARGET_FDPIC)
+ {
+ rtx a = force_reg (Pmode, plus_constant (Pmode, XEXP (tramp_mem, 0), 8));
+
+ sh_emit_storesi (tramp_mem, 0, a);
+ sh_emit_storesi (tramp_mem, 4, sh_get_fdpic_reg_initial_val ());
+
+ sh_emit_storehi (tramp_mem, 8, 0xd302, 0xd203);
+ sh_emit_storehi (tramp_mem, 12, 0x6122, 0x5c21);
+ sh_emit_storehi (tramp_mem, 16, 0x412b, 0x0009);
+
+ sh_emit_storesi (tramp_mem, 20, cxt);
+ sh_emit_storesi (tramp_mem, 24, fnaddr);
+ }
+ else
+ {
+ sh_emit_storehi (tramp_mem, 0, 0xd202, 0xd301);
+ sh_emit_storehi (tramp_mem, 4, 0x422b, 0x0009);
+
+ sh_emit_storesi (tramp_mem, 8, cxt);
+ sh_emit_storesi (tramp_mem, 12, fnaddr);
+ }
if (TARGET_HARD_SH4 || TARGET_SH5)
{
if (!TARGET_INLINE_IC_INVALIDATE
|| (!(TARGET_SH4A || TARGET_SH4_300) && TARGET_USERMODE))
emit_library_call (function_symbol (NULL, "__ic_invalidate",
- FUNCTION_ORDINARY),
+ FUNCTION_ORDINARY).sym,
LCT_NORMAL, VOIDmode, 1, tramp, SImode);
else
emit_insn (gen_ic_invalidate_line (tramp));
@@ -11698,7 +11845,7 @@ sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
&& (! TARGET_SHCOMPACT
|| crtl->args.info.stack_regs == 0)
&& ! sh_cfun_interrupt_handler_p ()
- && (! flag_pic
+ && (! flag_pic || TARGET_FDPIC
|| (decl && ! (TREE_PUBLIC (decl) || DECL_WEAK (decl)))
|| (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
}
@@ -11712,7 +11859,7 @@ sh_expand_sym_label2reg (rtx reg, rtx sym, rtx lab, bool sibcall_p)
if (!is_weak && SYMBOL_REF_LOCAL_P (sym))
emit_insn (gen_sym_label2reg (reg, sym, lab));
- else if (sibcall_p)
+ else if (sibcall_p && SYMBOL_REF_LOCAL_P (sym))
emit_insn (gen_symPCREL_label2reg (reg, sym, lab));
else
emit_insn (gen_symPLT_label2reg (reg, sym, lab));
@@ -12715,8 +12862,16 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
#endif
if (TARGET_SH2 && flag_pic)
{
- sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
- XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
+ if (TARGET_FDPIC)
+ {
+ sibcall = gen_sibcall_pcrel_fdpic (funexp, const0_rtx);
+ XEXP (XVECEXP (sibcall, 0, 3), 0) = scratch2;
+ }
+ else
+ {
+ sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
+ XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
+ }
}
else
{
@@ -12757,17 +12912,25 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
epilogue_completed = 0;
}
-rtx
-function_symbol (rtx target, const char *name, enum sh_function_kind kind)
-{
- rtx sym;
+/* Return an RTX pair for the address and call site label of a function
+ NAME of kind KIND, placing the result in TARGET if not NULL. For
+ SFUNC_STATIC, if FDPIC, the LAB member of result will be set to
+ (const_int 0) if jsr should be used, or a label_ref if bsrf should
+ be used. For FDPIC, both SFUNC_GOT and SFUNC_STATIC will return the
+ address of the function itself, not a function descriptor, so they
+ can only be used with functions not using the FDPIC register that
+ are known to be called directory without a PLT entry. */
+function_symbol_result
+function_symbol (rtx target, const char *name, sh_function_kind kind)
+{
/* If this is not an ordinary function, the name usually comes from a
string literal or an sprintf buffer. Make sure we use the same
string consistently, so that cse will be able to unify address loads. */
if (kind != FUNCTION_ORDINARY)
name = IDENTIFIER_POINTER (get_identifier (name));
- sym = gen_rtx_SYMBOL_REF (Pmode, name);
+ rtx sym = gen_rtx_SYMBOL_REF (Pmode, name);
+ rtx lab = const0_rtx;
SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
if (flag_pic)
switch (kind)
@@ -12784,14 +12947,25 @@ function_symbol (rtx target, const char *name, enum sh_function_kind kind)
}
case SFUNC_STATIC:
{
- /* ??? To allow cse to work, we use GOTOFF relocations.
- We could add combiner patterns to transform this into
- straight pc-relative calls with sym2PIC / bsrf when
- label load and function call are still 1:1 and in the
- same basic block during combine. */
rtx reg = target ? target : gen_reg_rtx (Pmode);
- emit_insn (gen_symGOTOFF2reg (reg, sym));
+ if (TARGET_FDPIC)
+ {
+ /* We use PC-relative calls, since GOTOFF can only refer
+ to writable data. This works along with sh_sfunc_call. */
+ lab = PATTERN (gen_call_site ());
+ emit_insn (gen_sym_label2reg (reg, sym, lab));
+ }
+ else
+ {
+ /* ??? To allow cse to work, we use GOTOFF relocations.
+ we could add combiner patterns to transform this into
+ straight pc-relative calls with sym2PIC / bsrf when
+ label load and function call are still 1:1 and in the
+ same basic block during combine. */
+ emit_insn (gen_symGOTOFF2reg (reg, sym));
+ }
+
sym = reg;
break;
}
@@ -12799,9 +12973,9 @@ function_symbol (rtx target, const char *name, enum sh_function_kind kind)
if (target && sym != target)
{
emit_move_insn (target, sym);
- return target;
+ return function_symbol_result (target, lab);
}
- return sym;
+ return function_symbol_result (sym, lab);
}
/* Find the number of a general purpose register in S. */
@@ -13414,6 +13588,12 @@ sh_conditional_register_usage (void)
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
}
+ if (TARGET_FDPIC)
+ {
+ fixed_regs[PIC_REG] = 1;
+ call_used_regs[PIC_REG] = 1;
+ call_really_used_regs[PIC_REG] = 1;
+ }
/* Renesas saves and restores mac registers on call. */
if (TARGET_HITACHI && ! TARGET_NOMACSAVE)
{
@@ -13442,14 +13622,32 @@ sh_conditional_register_usage (void)
static bool
sh_legitimate_constant_p (machine_mode mode, rtx x)
{
- return (TARGET_SHMEDIA
- ? ((mode != DFmode && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT)
- || x == CONST0_RTX (mode)
- || !TARGET_SHMEDIA_FPU
- || TARGET_SHMEDIA64)
- : (GET_CODE (x) != CONST_DOUBLE
- || mode == DFmode || mode == SFmode
- || mode == DImode || GET_MODE (x) == VOIDmode));
+ if (SH_OFFSETS_MUST_BE_WITHIN_SECTIONS_P)
+ {
+ rtx base, offset;
+ split_const (x, &base, &offset);
+
+ if (GET_CODE (base) == SYMBOL_REF
+ && !offset_within_block_p (base, INTVAL (offset)))
+ return false;
+ }
+
+ if (TARGET_FDPIC
+ && (SYMBOLIC_CONST_P (x)
+ || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
+ && SYMBOLIC_CONST_P (XEXP (XEXP (x, 0), 0)))))
+ return false;
+
+ if (TARGET_SHMEDIA
+ && ((mode != DFmode && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT)
+ || x == CONST0_RTX (mode)
+ || !TARGET_SHMEDIA_FPU
+ || TARGET_SHMEDIA64))
+ return false;
+
+ return GET_CODE (x) != CONST_DOUBLE
+ || mode == DFmode || mode == SFmode
+ || mode == DImode || GET_MODE (x) == VOIDmode;
}
enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
@@ -14540,4 +14738,41 @@ sh_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT size,
}
}
+bool
+sh_cannot_force_const_mem_p (machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x ATTRIBUTE_UNUSED)
+{
+ return TARGET_FDPIC;
+}
+
+/* Emit insns to load the function address from FUNCDESC (an FDPIC
+ function descriptor) into r1 and the GOT address into r12,
+ returning an rtx for r1. */
+
+rtx
+sh_load_function_descriptor (rtx funcdesc)
+{
+ rtx r1 = gen_rtx_REG (Pmode, R1_REG);
+ rtx pic_reg = gen_rtx_REG (Pmode, PIC_REG);
+ rtx fnaddr = gen_rtx_MEM (Pmode, funcdesc);
+ rtx gotaddr = gen_rtx_MEM (Pmode, plus_constant (Pmode, funcdesc, 4));
+
+ emit_move_insn (r1, fnaddr);
+ /* The ABI requires the entry point address to be loaded first, so
+ prevent the load from being moved after that of the GOT
+ address. */
+ emit_insn (gen_blockage ());
+ emit_move_insn (pic_reg, gotaddr);
+ return r1;
+}
+
+/* Return an rtx holding the initial value of the FDPIC register (the
+ FDPIC pointer passed in from the caller). */
+
+rtx
+sh_get_fdpic_reg_initial_val (void)
+{
+ return get_hard_reg_initial_val (Pmode, PIC_REG);
+}
+
#include "gt-sh.h"