This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

S/390: Use %r14 freely on zSeries machines


Hello,

now that literal pool splitting no longer clobbers %r14, the only
'special' use of that register on zSeries machines was to handle
branch overflows for the BRANCH ON COUNT family of instructions.

However, this can be handled (even more efficiently) by splitting
an overflowing BRANCH ON COUNT into a A(G)HI / JGNE sequence.
Doing so makes it possible to enable the use of %r14 as a normal
call-saved register for the register allocator (on zSeries only,
on S/390 we still need to reserve %r14 for branch overflow).

Bootstrapped/regtested on s390-ibm-linux and s390x-ibm-linux,
committed to CVS head.

Bye,
Ulrich


ChangeLog:

	* config/s390/s390.c (struct machine_function): Use save_return_addr_p
	as a general flag that the return address register needs to be saved,
	not necessarily because of __builtin_return_addr (0).
	(s390_split_branches): Remove TEMP_REG and TEMP_USED arguments,
	remove special handling of zSeries machines.
	(s390_optimize_prolog): Remove TEMP_USED argument, treat the return 
	register as a regular register on zSeries machines.
	(s390_reorg): Adjust calls to s390_split_branches and 
	s390_optimize_prolog.
	(s390_frame_info): On zSeries machines, do not assume the return
	register is always used.  Update regs_ever_live with current data
	for the special registers.
	(s390_emit_epilogue): Use save_return_addr_p to determine whether
	the return register was saved.
	* config/s390/s390.h (CONDITIONAL_REGISTER_USAGE): Do not mark
	RETURN_REGNUM fixed on zSeries machines.
	(REG_ALLOC_ORDER): Use RETURN_REGNUM last.
	* config/s390/s390.md ("*doloop_si"): Handle branch overflow
	via ahi-jgne pair on zSeries machines.
	("*doloop_di"): Likewise.
	("*doloop_di_long"): Remove.


Index: gcc/config/s390/s390.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/s390/s390.c,v
retrieving revision 1.118
diff -c -p -r1.118 s390.c
*** gcc/config/s390/s390.c	24 Oct 2003 17:30:34 -0000	1.118
--- gcc/config/s390/s390.c	26 Oct 2003 18:39:17 -0000
*************** struct machine_function GTY(())
*** 186,193 ****
    /* Set, if some of the fprs 8-15 need to be saved (64 bit abi).  */
    int save_fprs_p;
  
!   /* Set if return address needs to be saved because the current
!      function uses __builtin_return_addr (0).  */
    bool save_return_addr_p;
  
    /* Number of first and last gpr to be saved, restored.  */
--- 186,192 ----
    /* Set, if some of the fprs 8-15 need to be saved (64 bit abi).  */
    int save_fprs_p;
  
!   /* Set if return address needs to be saved.  */
    bool save_return_addr_p;
  
    /* Number of first and last gpr to be saved, restored.  */
*************** static const char *get_some_local_dynami
*** 216,227 ****
  static int get_some_local_dynamic_name_1 (rtx *, void *);
  static int reg_used_in_mem_p (int, rtx);
  static int addr_generation_dependency_p (rtx, rtx);
! static int s390_split_branches (rtx, bool *);
  static void find_constant_pool_ref (rtx, rtx *);
  static void replace_constant_pool_ref (rtx *, rtx, rtx);
  static rtx find_ltrel_base (rtx);
  static void replace_ltrel_base (rtx *, rtx);
! static void s390_optimize_prolog (bool, bool);
  static int find_unused_clobbered_reg (void);
  static void s390_frame_info (void);
  static rtx save_fpr (rtx, int, int);
--- 215,226 ----
  static int get_some_local_dynamic_name_1 (rtx *, void *);
  static int reg_used_in_mem_p (int, rtx);
  static int addr_generation_dependency_p (rtx, rtx);
! static int s390_split_branches (void);
  static void find_constant_pool_ref (rtx, rtx *);
  static void replace_constant_pool_ref (rtx *, rtx, rtx);
  static rtx find_ltrel_base (rtx);
  static void replace_ltrel_base (rtx *, rtx);
! static void s390_optimize_prolog (bool);
  static int find_unused_clobbered_reg (void);
  static void s390_frame_info (void);
  static rtx save_fpr (rtx, int, int);
*************** s390_sched_reorder2 (FILE *dump ATTRIBUT
*** 3878,3892 ****
  
  
  /* 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 int
! s390_split_branches (rtx temp_reg, bool *temp_used)
  {
    int new_literal = 0;
    rtx insn, pat, tmp, target;
    rtx *label;
--- 3877,3888 ----
  
  
  /* Split all branches that exceed the maximum distance.
!    Returns true if this created a new literal pool entry.  */
  
  static int
! s390_split_branches (void)
  {
+   rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
    int new_literal = 0;
    rtx insn, pat, tmp, target;
    rtx *label;
*************** s390_split_branches (rtx temp_reg, bool 
*** 3924,3942 ****
        else
  	continue;
  
!       if (get_attr_length (insn) <= (TARGET_CPU_ZARCH ? 6 : 4))
  	continue;
  
!       *temp_used = 1;
  
!       if (TARGET_CPU_ZARCH)
! 	{
! 	  tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, *label), insn);
! 	  INSN_ADDRESSES_NEW (tmp, -1);
! 
! 	  target = temp_reg;
! 	}
!       else if (!flag_pic)
  	{
  	  new_literal = 1;
  	  tmp = force_const_mem (Pmode, *label);
--- 3920,3933 ----
        else
  	continue;
  
!       if (get_attr_length (insn) <= 4)
  	continue;
  
!       /* We are going to use the return register as scratch register,
! 	 make sure it will be saved/restored by the prologue/epilogue.  */
!       cfun->machine->save_return_addr_p = 1;
  
!       if (!flag_pic)
  	{
  	  new_literal = 1;
  	  tmp = force_const_mem (Pmode, *label);
*************** s390_output_pool_entry (FILE *file, rtx 
*** 5019,5030 ****
  
  /* Rework the prolog/epilog to avoid saving/restoring
     registers unnecessarily.  BASE_USED specifies whether
!    the literal pool base register needs to be saved, 
!    TEMP_USED specifies whether the return register needs
!    to be saved.  */
  
  static void
! s390_optimize_prolog (bool base_used, bool temp_used)
  {
    int save_first, save_last, restore_first, restore_last;
    int i, j;
--- 5010,5019 ----
  
  /* Rework the prolog/epilog to avoid saving/restoring
     registers unnecessarily.  BASE_USED specifies whether
!    the literal pool base register needs to be saved.  */
  
  static void
! s390_optimize_prolog (bool base_used)
  {
    int save_first, save_last, restore_first, restore_last;
    int i, j;
*************** s390_optimize_prolog (bool base_used, bo
*** 5032,5048 ****
  
    /* Recompute regs_ever_live data for special registers.  */
    regs_ever_live[BASE_REGISTER] = base_used;
!   regs_ever_live[RETURN_REGNUM] = temp_used;
    regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
  
-   /* In non-leaf functions, the prolog/epilog code relies
-      on RETURN_REGNUM being saved in any case.  We also need
-      to save the return register if __builtin_return_address (0)
-      was used in the current function.  */
-   if (!current_function_is_leaf 
-       || cfun->machine->save_return_addr_p)
-     regs_ever_live[RETURN_REGNUM] = 1;
- 
  
    /* Find first and last gpr to be saved.  */
  
--- 5021,5029 ----
  
    /* Recompute regs_ever_live data for special registers.  */
    regs_ever_live[BASE_REGISTER] = base_used;
!   regs_ever_live[RETURN_REGNUM] = cfun->machine->save_return_addr_p;
    regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
  
  
    /* Find first and last gpr to be saved.  */
  
*************** s390_optimize_prolog (bool base_used, bo
*** 5089,5095 ****
    /* 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;
  
  
--- 5070,5076 ----
    /* 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
!       && (TARGET_CPU_ZARCH || (i <= RETURN_REGNUM && j >= RETURN_REGNUM)))
      return;
  
  
*************** s390_optimize_prolog (bool base_used, bo
*** 5104,5113 ****
  
        if (GET_CODE (insn) != INSN)
  	continue;
-       if (GET_CODE (PATTERN (insn)) != PARALLEL)
- 	continue;
  
!       if (store_multiple_operation (PATTERN (insn), VOIDmode))
  	{
  	  set = XVECEXP (PATTERN (insn), 0, 0);
  	  first = REGNO (SET_SRC (set));
--- 5085,5093 ----
  
        if (GET_CODE (insn) != INSN)
  	continue;
  
!       if (GET_CODE (PATTERN (insn)) == PARALLEL
! 	  && store_multiple_operation (PATTERN (insn), VOIDmode))
  	{
  	  set = XVECEXP (PATTERN (insn), 0, 0);
  	  first = REGNO (SET_SRC (set));
*************** s390_optimize_prolog (bool base_used, bo
*** 5118,5126 ****
  
  	  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)
--- 5098,5128 ----
  
  	  if (GET_CODE (base) != REG || off < 0)
  	    continue;
! 	  if (first > BASE_REGISTER || last < BASE_REGISTER)
  	    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);
! 	  continue;
! 	}
! 
!       if (GET_CODE (PATTERN (insn)) == SET
! 	  && GET_CODE (SET_SRC (PATTERN (insn))) == REG
! 	  && REGNO (SET_SRC (PATTERN (insn))) == BASE_REGISTER
! 	  && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
! 	{
! 	  set = PATTERN (insn);
! 	  offset = const0_rtx;
! 	  base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
! 	  off = INTVAL (offset) - BASE_REGISTER * UNITS_PER_WORD;
! 
! 	  if (GET_CODE (base) != REG || off < 0)
  	    continue;
  
  	  if (save_first != -1)
*************** s390_optimize_prolog (bool base_used, bo
*** 5131,5139 ****
  	    }
  
  	  remove_insn (insn);
  	}
  
!       if (load_multiple_operation (PATTERN (insn), VOIDmode))
  	{
  	  set = XVECEXP (PATTERN (insn), 0, 0);
  	  first = REGNO (SET_DEST (set));
--- 5133,5143 ----
  	    }
  
  	  remove_insn (insn);
+ 	  continue;
  	}
  
!       if (GET_CODE (PATTERN (insn)) == PARALLEL
! 	  && load_multiple_operation (PATTERN (insn), VOIDmode))
  	{
  	  set = XVECEXP (PATTERN (insn), 0, 0);
  	  first = REGNO (SET_DEST (set));
*************** s390_optimize_prolog (bool base_used, bo
*** 5144,5152 ****
  
  	  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)
--- 5148,5178 ----
  
  	  if (GET_CODE (base) != REG || off < 0)
  	    continue;
! 	  if (first > BASE_REGISTER || last < BASE_REGISTER)
  	    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);
! 	  continue;
! 	}
! 
!       if (GET_CODE (PATTERN (insn)) == SET
! 	  && GET_CODE (SET_DEST (PATTERN (insn))) == REG
! 	  && REGNO (SET_DEST (PATTERN (insn))) == BASE_REGISTER
! 	  && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
! 	{
! 	  set = PATTERN (insn);
! 	  offset = const0_rtx;
! 	  base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
! 	  off = INTVAL (offset) - BASE_REGISTER * UNITS_PER_WORD;
! 
! 	  if (GET_CODE (base) != REG || off < 0)
  	    continue;
  
  	  if (restore_first != -1)
*************** s390_optimize_prolog (bool base_used, bo
*** 5157,5162 ****
--- 5183,5189 ----
  	    }
  
  	  remove_insn (insn);
+ 	  continue;
  	}
      }
  }
*************** s390_optimize_prolog (bool base_used, bo
*** 5166,5174 ****
  static void
  s390_reorg (void)
  {
-   rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
    rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
-   bool temp_used = false;
    bool base_used = false;
    bool pool_overflow = false;
  
--- 5193,5199 ----
*************** s390_reorg (void)
*** 5234,5241 ****
  
        /* 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_overflow)
              s390_chunkify_cancel (pool);
--- 5259,5267 ----
  
        /* Split out-of-range branches.  If this has created new
  	 literal pool entries, cancel current chunk list and
! 	 recompute it.  zSeries machines have large branch
! 	 instructions, so we never need to split a branch.  */
!       if (!TARGET_CPU_ZARCH && s390_split_branches ())
          {
            if (pool_overflow)
              s390_chunkify_cancel (pool);
*************** s390_reorg (void)
*** 5259,5265 ****
        break;
      }
  
!   s390_optimize_prolog (base_used, temp_used);
  }
  
  
--- 5285,5291 ----
        break;
      }
  
!   s390_optimize_prolog (base_used);
  }
  
  
*************** find_unused_clobbered_reg (void)
*** 5305,5311 ****
  static void
  s390_frame_info (void)
  {
-   char gprs_ever_live[16];
    int i, j;
    HOST_WIDE_INT fsize = get_frame_size ();
  
--- 5331,5336 ----
*************** s390_frame_info (void)
*** 5332,5362 ****
        || current_function_stdarg)
      cfun->machine->frame_size += STARTING_FRAME_OFFSET;
  
    /* 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.  */
  
!   for (i = 0; i < 16; 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;
  
    for (i = 6; i < 16; i++)
!     if (gprs_ever_live[i])
!       break;
  
    for (j = 15; j > i; j--)
!     if (gprs_ever_live[j])
!       break;
! 
  
    /* Save / Restore from gpr i to j.  */
    cfun->machine->first_save_gpr = i;
--- 5357,5398 ----
        || current_function_stdarg)
      cfun->machine->frame_size += STARTING_FRAME_OFFSET;
  
+   /* If we use the return register, we'll need to make sure
+      it is going to be saved/restored.  */
+ 
+   if (!current_function_is_leaf
+       || regs_ever_live[RETURN_REGNUM])
+     cfun->machine->save_return_addr_p = 1;
+ 
    /* Find first and last gpr to be saved.  Note that at this point,
!      we assume the base register and -on S/390- the return 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.  */
  
!   regs_ever_live[BASE_REGISTER] = 1;
!   if (!TARGET_CPU_ZARCH || cfun->machine->save_return_addr_p)
!     regs_ever_live[RETURN_REGNUM] = 1;
!   regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
  
    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;
  
    /* Save / Restore from gpr i to j.  */
    cfun->machine->first_save_gpr = i;
*************** s390_emit_epilogue (void)
*** 5907,5913 ****
        /* Fetch return address from stack before load multiple,
  	 this will do good for scheduling.  */
  
!       if (!current_function_is_leaf)
  	{
  	  int return_regnum = find_unused_clobbered_reg();
  	  if (!return_regnum)
--- 5943,5949 ----
        /* Fetch return address from stack before load multiple,
  	 this will do good for scheduling.  */
  
!       if (cfun->machine->save_return_addr_p)
  	{
  	  int return_regnum = find_unused_clobbered_reg();
  	  if (!return_regnum)
Index: gcc/config/s390/s390.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/s390/s390.h,v
retrieving revision 1.88
diff -c -p -r1.88 s390.h
*** gcc/config/s390/s390.h	18 Oct 2003 22:24:37 -0000	1.88
--- gcc/config/s390/s390.h	26 Oct 2003 18:39:17 -0000
*************** if (INTEGRAL_MODE_P (MODE) &&	        	 
*** 313,319 ****
     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.
  
--- 313,319 ----
     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 on S/390 machines (as return address).
     GPR 15 is always fixed (as stack pointer).
     The 'fake' hard registers are call-clobbered and fixed.
  
*************** do								\
*** 364,369 ****
--- 364,374 ----
  	fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;		\
  	call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;		\
        }								\
+     if (TARGET_CPU_ZARCH)					\
+       {								\
+ 	fixed_regs[RETURN_REGNUM] = 0;				\
+ 	call_used_regs[RETURN_REGNUM] = 0;			\
+       }								\
      if (TARGET_64BIT)						\
        {								\
          for (i = 24; i < 32; i++)				\
*************** do								\
*** 378,384 ****
  
  /* 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 }
--- 383,389 ----
  
  /* Preferred register allocation order.  */
  #define REG_ALLOC_ORDER                                         \
! {  1, 2, 3, 4, 5, 0, 13, 12, 11, 10, 9, 8, 7, 6, 14,            \
     16, 17, 18, 19, 20, 21, 22, 23,                              \
     24, 25, 26, 27, 28, 29, 30, 31,                              \
     15, 32, 33, 34 }
Index: gcc/config/s390/s390.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/s390/s390.md,v
retrieving revision 1.83
diff -c -p -r1.83 s390.md
*** gcc/config/s390/s390.md	24 Oct 2003 17:30:34 -0000	1.83
--- gcc/config/s390/s390.md	26 Oct 2003 18:39:20 -0000
***************
*** 6596,6601 ****
--- 6596,6603 ----
      return "#";
    else if (get_attr_length (insn) == 4)
      return "brct\t%1,%l0";
+   else if (TARGET_CPU_ZARCH)
+     return "ahi\t%1,-1\;jgne\t%l0";
    else
      abort ();
  }
***************
*** 6675,6711 ****
    else if (get_attr_length (insn) == 4)
      return "brctg\t%1,%l0";
    else
!     abort ();
  }
    [(set_attr "op_type"  "RI")
     (set_attr "type"  "branch")
     (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" "U,U")
!           (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 "type"  "branch")
!    (set_attr "atype" "agen")])
  
  (define_split
    [(set (pc)
--- 6677,6689 ----
    else if (get_attr_length (insn) == 4)
      return "brctg\t%1,%l0";
    else
!     return "aghi\t%1,-1\;jgne\t%l0";
  }
    [(set_attr "op_type"  "RI")
     (set_attr "type"  "branch")
     (set (attr "length")
          (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000))
!                       (const_int 4) (const_int 10)))])
  
  (define_split
    [(set (pc)
-- 
  Dr. Ulrich Weigand
  weigand@informatik.uni-erlangen.de


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]