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]

[RFH] split {generic,gimple}-match.c files


The following patch^Whack splits $subject files into three, one
for the predicates (due to an implementation detail) and two for
the rest - for now into similar LOC size files.

I'd like to get help on the makefile changes to make them less
verbose, somehow globbing the -[12p] parts.

Also you can see the split point is manually chosen which means
it will bitrot.  Timings for the stage2 compiles on a x86_64
box are

gimple-match-p.c   5s
generic-match-p.c  3s
gimple-match-1.c  85s
generic-match-1.c 56s
gimple-match-2.c  82s
generic-match-2.c 31s

the required header files are quite big (and of course everything
needs to be exported without the analysis work becoming too cumbersome),
it's 3342 LOC for gimple-match-head.h and 1556 LOC for 
generic-match-head.h

The machine I tested is quite fast so the 80ish second timings are still
too slow I guess and thus splitting up into four files for gimple and
three files for generic looks better.

Note we lose some inlining/cloning capability in the splitting process
(I see quite a bit of constprop/isra work being done on the generated 
files).  I didn't try to measure the runtime impact though.

The patch still needs quite some TLC, it really is a bit hacky but I'd
like to get feedback on the approach and I didn't want to spend time
on programatically finding optimal split points (so everything is output
in the same semi-random order as before).

Richard.

<insert ChangeLog here>

Index: gcc/genmatch.c
===================================================================
--- gcc/genmatch.c	(revision 259638)
+++ gcc/genmatch.c	(working copy)
@@ -1641,7 +1641,7 @@ struct decision_tree
   dt_node *root;
 
   void insert (struct simplify *, unsigned);
-  void gen (FILE *f, bool gimple);
+  void gen (const char *, FILE *, vec<unsigned long> &, bool gimple);
   void print (FILE *f = stderr);
 
   decision_tree () { root = new dt_node (dt_node::DT_NODE, NULL); }
@@ -3608,12 +3608,25 @@ sinfo_hashmap_traits::equal_keys (const
   return compare_op (v->s->result, v->s, candidate->s->result, candidate->s);
 }
 
+/* Write the common header for the GIMPLE/GENERIC IL matching routines.  */
+
+static void
+write_header (FILE *f, bool gimple)
+{
+  fprintf (f, "/* Generated automatically by the program `genmatch' from\n");
+  fprintf (f, "   a IL pattern matching and simplification description.  */\n");
+
+  /* Include the header instead of writing it awkwardly quoted here.  */
+  fprintf (f, "\n#include \"%s-match-head.c\"\n",
+	   gimple ? "gimple" : "generic");
+}
 
 /* Main entry to generate code for matching GIMPLE IL off the decision
    tree.  */
 
 void
-decision_tree::gen (FILE *f, bool gimple)
+decision_tree::gen (const char *output, FILE *headerf,
+		    vec<unsigned long> &pieces, bool gimple)
 {
   sinfo_map_t si;
 
@@ -3624,6 +3637,34 @@ decision_tree::gen (FILE *f, bool gimple
 	   gimple ? "GIMPLE" : "GENERIC", 
 	   root->num_leafs, root->max_level, root->total_size);
 
+  FILE *f;
+  char *outputtem = NULL;
+  if (output)
+    outputtem = XNEWVEC (char, strlen (output) + strlen ("-1.c") + 1);
+
+  unsigned do_header = headerf ? 2 : 1;
+  unsigned n_per_part = -1U;
+  unsigned file_n = output ? 1 : 2;
+  do
+    {
+      unsigned n_fn = 0;
+      do_header--;
+
+      if (do_header)
+	f = headerf;
+      else if (!output)
+	f = stdout;
+      else
+	{
+	  sprintf (outputtem, "%s-%d.c", output, file_n++);
+	  f = fopen (outputtem, "w");
+	  if (!f)
+	    {
+	      perror ("failed to open output file");
+	      exit(1);
+	    }
+	  write_header (f, gimple);
+	}
   /* First split out the transform part of equal leafs.  */
   unsigned rcnt = 0;
   unsigned fcnt = 1;
@@ -3643,21 +3684,22 @@ decision_tree::gen (FILE *f, bool gimple
 	}
 
       /* Generate a split out function with the leaf transform code.  */
+      if (do_header || !output)
       s->fname = xasprintf ("%s_simplify_%u", gimple ? "gimple" : "generic",
 			    fcnt++);
       if (gimple)
-	fprintf (f, "\nstatic bool\n"
+	fprintf (f, "\n%sbool\n"
 		 "%s (code_helper *res_code, tree *res_ops,\n"
 		 "                 gimple_seq *seq, tree (*valueize)(tree) "
 		 "ATTRIBUTE_UNUSED,\n"
 		 "                 const tree ARG_UNUSED (type), tree *ARG_UNUSED "
 		 "(captures)\n",
-		 s->fname);
+		 headerf ? "" : "static ", s->fname);
       else
 	{
-	  fprintf (f, "\nstatic tree\n"
+	  fprintf (f, "\n%stree\n"
 		   "%s (location_t ARG_UNUSED (loc), const tree ARG_UNUSED (type),\n",
-		   (*iter).second->fname);
+		   headerf ? "" : "static ", (*iter).second->fname);
 	  for (unsigned i = 0;
 	       i < as_a <expr *>(s->s->s->match)->ops.length (); ++i)
 	    fprintf (f, " tree ARG_UNUSED (op%d),", i);
@@ -3674,7 +3716,12 @@ decision_tree::gen (FILE *f, bool gimple
 	    fprintf (f, ", const combined_fn ARG_UNUSED (%s)",
 		     s->s->s->for_subst_vec[i].first->id);
 	}
-
+      n_fn++;
+      if (do_header)
+	{
+	  fprintf (f, ");\n");
+	  continue;
+	}
       fprintf (f, ")\n{\n");
       s->s->gen_1 (f, 2, gimple, s->s->s->result);
       if (gimple)
@@ -3682,7 +3729,22 @@ decision_tree::gen (FILE *f, bool gimple
       else
 	fprintf (f, "  return NULL_TREE;\n");
       fprintf (f, "}\n");
+
+      if (n_fn == pieces[file_n - 2])
+	{
+	  fclose (f);
+	  sprintf (outputtem, "%s-%d.c", output, file_n++);
+	  f = fopen (outputtem, "w");
+	  if (!f)
+	    {
+	      perror ("failed to open output file");
+	      exit(1);
+	    }
+	  write_header (f, gimple);
+	  n_fn = 0;
+	}
     }
+  if (!do_header)
   fprintf (stderr, "removed %u duplicate tails\n", rcnt);
 
   for (unsigned n = 1; n <= 3; ++n)
@@ -3702,20 +3764,26 @@ decision_tree::gen (FILE *f, bool gimple
 	    continue;
 
 	  if (gimple)
-	    fprintf (f, "\nstatic bool\n"
+	    fprintf (f, "\n%sbool\n"
 		     "gimple_simplify_%s (code_helper *res_code, tree *res_ops,\n"
 		     "                 gimple_seq *seq, tree (*valueize)(tree) "
 		     "ATTRIBUTE_UNUSED,\n"
 		     "                 code_helper ARG_UNUSED (code), tree "
 		     "ARG_UNUSED (type)\n",
-		     e->operation->id);
+		     headerf ? "" : "static ", e->operation->id);
 	  else
-	    fprintf (f, "\nstatic tree\n"
+	    fprintf (f, "\n%stree\n"
 		     "generic_simplify_%s (location_t ARG_UNUSED (loc), enum "
 		     "tree_code ARG_UNUSED (code), const tree ARG_UNUSED (type)",
-		     e->operation->id);
+		     headerf ? "" : "static ", e->operation->id);
 	  for (unsigned i = 0; i < n; ++i)
 	    fprintf (f, ", tree op%d", i);
+	  n_fn++;
+	  if (do_header)
+	    {
+	      fprintf (f, ");\n");
+	      continue;
+	    }
 	  fprintf (f, ")\n");
 	  fprintf (f, "{\n");
 	  dop->gen_kids (f, 2, gimple);
@@ -3724,21 +3792,43 @@ decision_tree::gen (FILE *f, bool gimple
 	  else
 	    fprintf (f, "  return NULL_TREE;\n");
 	  fprintf (f, "}\n");
+
+      if (n_fn == pieces[file_n - 2])
+	{
+	  fclose (f);
+	  sprintf (outputtem, "%s-%d.c", output, file_n++);
+	  f = fopen (outputtem, "w");
+	  if (!f)
+	    {
+	      perror ("failed to open output file");
+	      exit(1);
+	    }
+	  write_header (f, gimple);
+	  n_fn = 0;
+	}
+
 	}
 
       /* Then generate the main entry with the outermost switch and
          tail-calls to the split-out functions.  */
       if (gimple)
-	fprintf (f, "\nstatic bool\n"
+	fprintf (f, "\n%sbool\n"
 		 "gimple_simplify (code_helper *res_code, tree *res_ops,\n"
 		 "                 gimple_seq *seq, tree (*valueize)(tree),\n"
-		 "                 code_helper code, const tree type");
+		 "                 code_helper code, const tree type",
+		 headerf ? "" : "static ");
       else
 	fprintf (f, "\ntree\n"
 		 "generic_simplify (location_t loc, enum tree_code code, "
 		 "const tree type ATTRIBUTE_UNUSED");
       for (unsigned i = 0; i < n; ++i)
 	fprintf (f, ", tree op%d", i);
+      n_fn++;
+      if (do_header)
+	{
+	  fprintf (f, ");\n");
+	  continue;
+	}
       fprintf (f, ")\n");
       fprintf (f, "{\n");
 
@@ -3786,19 +3876,46 @@ decision_tree::gen (FILE *f, bool gimple
       else
 	fprintf (f, "  return NULL_TREE;\n");
       fprintf (f, "}\n");
+      if (n_fn == pieces[file_n - 2])
+	{
+	  fclose (f);
+	  sprintf (outputtem, "%s-%d.c", output, file_n++);
+	  f = fopen (outputtem, "w");
+	  if (!f)
+	    {
+	      perror ("failed to open output file");
+	      exit(1);
+	    }
+	  write_header (f, gimple);
+	  n_fn = 0;
+	}
+    }
+
+  n_per_part = n_fn / 4 + 1;
     }
+  while (do_header);
+  if (output)
+    fclose (f);
 }
 
 /* Output code to implement the predicate P from the decision tree DT.  */
 
 void
-write_predicate (FILE *f, predicate_id *p, decision_tree &dt, bool gimple)
+write_predicate (FILE *f, predicate_id *p, decision_tree &dt, bool gimple,
+		 bool for_header)
 {
   fprintf (f, "\nbool\n"
-	   "%s%s (tree t%s%s)\n"
-	   "{\n", gimple ? "gimple_" : "tree_", p->id,
+	   "%s%s (tree t%s%s)",
+	   gimple ? "gimple_" : "tree_", p->id,
 	   p->nargs > 0 ? ", tree *res_ops" : "",
 	   gimple ? ", tree (*valueize)(tree) ATTRIBUTE_UNUSED" : "");
+  if (for_header)
+    {
+      fprintf (f, ";\n");
+      return;
+    }
+
+  fprintf (f, "\n{\n");
   /* Conveniently make 'type' available.  */
   fprintf_indent (f, 2, "const tree type = TREE_TYPE (t);\n");
 
@@ -3810,18 +3927,6 @@ write_predicate (FILE *f, predicate_id *
 	   "}\n");
 }
 
-/* Write the common header for the GIMPLE/GENERIC IL matching routines.  */
-
-static void
-write_header (FILE *f, const char *head)
-{
-  fprintf (f, "/* Generated automatically by the program `genmatch' from\n");
-  fprintf (f, "   a IL pattern matching and simplification description.  */\n");
-
-  /* Include the header instead of writing it awkwardly quoted here.  */
-  fprintf (f, "\n#include \"%s\"\n", head);
-}
-
 
 
 /* AST parsing.  */
@@ -4969,6 +5074,9 @@ main (int argc, char **argv)
 
   bool gimple = true;
   char *input = argv[argc-1];
+  char *output = NULL;
+  char *header = NULL;
+  auto_vec<unsigned long> pieces;
   for (int i = 1; i < argc - 1; ++i)
     {
       if (strcmp (argv[i], "--gimple") == 0)
@@ -4979,13 +5087,25 @@ main (int argc, char **argv)
 	verbose = 1;
       else if (strcmp (argv[i], "-vv") == 0)
 	verbose = 2;
+      else if (strcmp (argv[i], "-c") == 0)
+	{
+	  char *endp;
+	  output = argv[++i];
+	  while (i + 1 < argc - 1
+		 && ISDIGIT (argv[i + 1][0]))
+	    pieces.safe_push (strtoul (argv[++i], &endp, 10));
+	}
+      else if (strcmp (argv[i], "-h") == 0)
+	header = argv[++i];
       else
 	{
 	  fprintf (stderr, "Usage: genmatch "
-		   "[--gimple] [--generic] [-v[v]] input\n");
+		   "[--gimple] [--generic] [-v[v]] "
+		   "[-c output num...] [-h header] input\n");
 	  return 1;
 	}
     }
+  pieces.safe_push (-1UL);
 
   line_table = XCNEW (struct line_maps);
   linemap_init (line_table, 0);
@@ -5039,10 +5159,32 @@ add_operator (VIEW_CONVERT2, "view_conve
   /* Parse ahead!  */
   parser p (r);
 
-  if (gimple)
-    write_header (stdout, "gimple-match-head.c");
+  FILE *f, *headerf = NULL;
+  if (!output)
+    f = stdout;
   else
-    write_header (stdout, "generic-match-head.c");
+    {
+      char *outputtem = XNEWVEC (char, strlen (output) + strlen ("-1.c") + 1);
+      sprintf (outputtem, "%s-p.c", output);
+      f = fopen (outputtem, "w");
+      if (!f)
+	{
+	  perror ("failed to open output file");
+	  exit(1);
+	}
+    }
+  if (header)
+    {
+      headerf = fopen (header, "w");
+      if (!headerf)
+	{
+	  perror ("failed to open output file");
+	  exit(1);
+	}
+    }
+
+  fprintf (f, "#define GENFOO_MAIN_FILE 1\n");
+  write_header (f, gimple);
 
   /* Go over all predicates defined with patterns and perform
      lowering and code generation.  */
@@ -5062,8 +5204,12 @@ add_operator (VIEW_CONVERT2, "view_conve
       if (verbose == 2)
 	dt.print (stderr);
 
-      write_predicate (stdout, pred, dt, gimple);
+      if (header)
+	write_predicate (headerf, pred, dt, gimple, true);
+      write_predicate (f, pred, dt, gimple, false);
     }
+  if (output)
+    fclose (f);
 
   /* Lower the main simplifiers and generate code for them.  */
   lower (p.simplifiers, gimple);
@@ -5079,7 +5225,10 @@ add_operator (VIEW_CONVERT2, "view_conve
   if (verbose == 2)
     dt.print (stderr);
 
-  dt.gen (stdout, gimple);
+  dt.gen (output, headerf, pieces, gimple);
+
+  if (header)
+    fclose (headerf);
 
   /* Finalize.  */
   cpp_finish (r, NULL);
Index: gcc/gimple-match-head.c
===================================================================
--- gcc/gimple-match-head.c	(revision 259638)
+++ gcc/gimple-match-head.c	(working copy)
@@ -40,21 +40,10 @@ along with GCC; see the file COPYING3.
 #include "case-cfn-macros.h"
 #include "gimplify.h"
 #include "optabs-tree.h"
+#include "gimple-match-head.h"
 
 
-/* Forward declarations of the private auto-generated matchers.
-   They expect valueized operands in canonical order and do not
-   perform simplification of all-constant operands.  */
-static bool gimple_simplify (code_helper *, tree *,
-			     gimple_seq *, tree (*)(tree),
-			     code_helper, tree, tree);
-static bool gimple_simplify (code_helper *, tree *,
-			     gimple_seq *, tree (*)(tree),
-			     code_helper, tree, tree, tree);
-static bool gimple_simplify (code_helper *, tree *,
-			     gimple_seq *, tree (*)(tree),
-			     code_helper, tree, tree, tree, tree);
-
+#if GENFOO_MAIN_FILE
 
 /* Return whether T is a constant that we'll dispatch to fold to
    evaluate fully constant expressions.  */
@@ -772,6 +761,8 @@ gimple_simplify (gimple *stmt,
   return false;
 }
 
+#endif
+
 
 /* Helper for the autogenerated code, valueize OP.  */
 
Index: gcc/generic-match-head.c
===================================================================
--- gcc/generic-match-head.c	(revision 259638)
+++ gcc/generic-match-head.c	(working copy)
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.
 #include "case-cfn-macros.h"
 #include "gimplify.h"
 #include "optabs-tree.h"
+#include "generic-match-head.h"
 
 
 /* Routine to determine if the types T1 and T2 are effectively
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 259638)
+++ gcc/Makefile.in	(working copy)
@@ -216,8 +216,12 @@ gengtype-lex.o-warn = -Wno-error
 libgcov-util.o-warn = -Wno-error
 libgcov-driver-tool.o-warn = -Wno-error
 libgcov-merge-tool.o-warn = -Wno-error
-gimple-match.o-warn = -Wno-unused
-generic-match.o-warn = -Wno-unused
+gimple-match-p.o-warn = -Wno-unused
+gimple-match-1.o-warn = -Wno-unused
+gimple-match-2.o-warn = -Wno-unused
+generic-match-p.o-warn = -Wno-unused
+generic-match-1.o-warn = -Wno-unused
+generic-match-2.o-warn = -Wno-unused
 dfp.o-warn = -Wno-strict-aliasing
 
 # All warnings have to be shut off in stage1 if the compiler used then
@@ -771,7 +775,7 @@ COMPILERS = @all_compilers@
 
 # List of things which should already be built whenever we try to use xgcc
 # to compile anything (without linking).
-GCC_PASSES=xgcc$(exeext) specs
+GCC_PASSES=xgcc$(exeext)
 
 # Directory to link to, when using the target `maketest'.
 DIR = ../gcc
@@ -1207,8 +1211,12 @@ C_COMMON_OBJS = c-family/c-common.o c-fa
 # will build them sooner, because they are large and otherwise tend to be
 # the last objects to finish building.
 OBJS = \
-	gimple-match.o \
-	generic-match.o \
+	gimple-match-p.o \
+	generic-match-p.o \
+	gimple-match-1.o \
+	generic-match-1.o \
+	gimple-match-2.o \
+	generic-match-2.o \
 	insn-attrtab.o \
 	insn-automata.o \
 	insn-dfatab.o \
@@ -1654,7 +1662,9 @@ MOSTLYCLEANFILES = insn-flags.h insn-con
  insn-output.c insn-recog.c insn-emit.c insn-extract.c insn-peep.c \
  insn-attr.h insn-attr-common.h insn-attrtab.c insn-dfatab.c \
  insn-latencytab.c insn-opinit.c insn-opinit.h insn-preds.c insn-constants.h \
- tm-preds.h tm-constrs.h checksum-options gimple-match.c generic-match.c \
+ tm-preds.h tm-constrs.h checksum-options gimple-match-head.h gimple-match-1.c \
+ gimple-match-2.c gimple-match-p.c generic-match-head.h generic-match-1.c \
+ generic-match-p.c generic-match-2.c \
  tree-check.h min-insn-modes.c insn-modes.c insn-modes.h insn-modes-inline.h \
  genrtl.h gt-*.h gtype-*.h gtype-desc.c gtyp-input.list \
  case-cfn-macros.h cfn-operators.pd \
@@ -1899,7 +1909,7 @@ all.internal: start.encap rest.encap doc
 all.cross: native gcc-cross$(exeext) cpp$(exeext) specs \
 	libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra
 # This is what must be made before installing GCC and converting libraries.
-start.encap: native xgcc$(exeext) cpp$(exeext) specs \
+start.encap: native xgcc$(exeext) cpp$(exeext) \
 	libgcc-support lang.start.encap @GENINSRC@ srcextra
 # These can't be made until after GCC can run.
 rest.encap: lang.rest.encap
@@ -2054,7 +2064,7 @@ checksum-options:
 libgcc-support: libgcc.mvars stmp-int-hdrs $(TCONFIG_H) \
 	$(MACHMODE_H) gcov-iov.h
 
-libgcc.mvars: config.status Makefile specs xgcc$(exeext)
+libgcc.mvars: config.status Makefile xgcc$(exeext)
 	: > tmp-libgcc.mvars
 	echo GCC_CFLAGS = '$(GCC_CFLAGS)' >> tmp-libgcc.mvars
 	echo INHIBIT_LIBC_CFLAGS = '$(INHIBIT_LIBC_CFLAGS)' >> tmp-libgcc.mvars
@@ -2271,8 +2281,9 @@ $(common_out_object_file): $(common_out_
 .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
   insn-emit.c insn-recog.c insn-extract.c insn-output.c insn-peep.c \
   insn-attr.h insn-attr-common.h insn-attrtab.c insn-dfatab.c \
-  insn-latencytab.c insn-preds.c gimple-match.c generic-match.c \
-  insn-target-def.h
+  insn-latencytab.c insn-preds.c gimple-match-head.h gimple-match-1.c \
+  gimple-match-2.c generic-match-head.h generic-match-1.c generic-match-2.c \
+  gimple-match-p.c generic-match-p.c insn-target-def.h
 
 # Dependencies for the md file.  The first time through, we just assume
 # the md file itself and the generated dependency file (in order to get
@@ -2504,18 +2515,36 @@ s-tm-texi: build/genhooks$(build_exeext)
 	  false; \
 	fi
 
-gimple-match.c: s-match gimple-match-head.c ; @true
-generic-match.c: s-match generic-match-head.c ; @true
+gimple-match-p.c: s-match gimple-match-head.c ; @true
+gimple-match-1.c: s-match gimple-match-head.c ; @true
+gimple-match-2.c: s-match gimple-match-head.c ; @true
+generic-match-p.c: s-match generic-match-head.c ; @true
+generic-match-1.c: s-match generic-match-head.c ; @true
+generic-match-2.c: s-match generic-match-head.c ; @true
 
 s-match: build/genmatch$(build_exeext) $(srcdir)/match.pd cfn-operators.pd
-	$(RUN_GEN) build/genmatch$(build_exeext) --gimple $(srcdir)/match.pd \
-	    > tmp-gimple-match.c
-	$(RUN_GEN) build/genmatch$(build_exeext) --generic $(srcdir)/match.pd \
-	    > tmp-generic-match.c
-	$(SHELL) $(srcdir)/../move-if-change tmp-gimple-match.c \
-	    					gimple-match.c
-	$(SHELL) $(srcdir)/../move-if-change tmp-generic-match.c \
-	    					generic-match.c
+	$(RUN_GEN) build/genmatch$(build_exeext) --gimple \
+	    -h tmp-gimple-match-head.h -c tmp-gimple-match 460 \
+	    $(srcdir)/match.pd
+	$(RUN_GEN) build/genmatch$(build_exeext) --generic \
+	    -h tmp-generic-match-head.h -c tmp-generic-match 290 \
+	    $(srcdir)/match.pd
+	$(SHELL) $(srcdir)/../move-if-change tmp-gimple-match-head.h \
+	    					gimple-match-head.h
+	$(SHELL) $(srcdir)/../move-if-change tmp-gimple-match-1.c \
+	    					gimple-match-1.c
+	$(SHELL) $(srcdir)/../move-if-change tmp-gimple-match-2.c \
+	    					gimple-match-2.c
+	$(SHELL) $(srcdir)/../move-if-change tmp-gimple-match-p.c \
+	    					gimple-match-p.c
+	$(SHELL) $(srcdir)/../move-if-change tmp-generic-match-head.h \
+	    					generic-match-head.h
+	$(SHELL) $(srcdir)/../move-if-change tmp-generic-match-1.c \
+	    					generic-match-1.c
+	$(SHELL) $(srcdir)/../move-if-change tmp-generic-match-2.c \
+	    					generic-match-2.c
+	$(SHELL) $(srcdir)/../move-if-change tmp-generic-match-p.c \
+	    					generic-match-p.c
 	$(STAMP) s-match
 
 GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \


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