public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH, fortran] PR20441 -finit-local-zero
@ 2007-08-17 20:12 Asher Langton
  2007-08-20 16:33 ` Tobias Burnus
  2007-08-31 22:14 ` Asher Langton
  0 siblings, 2 replies; 20+ messages in thread
From: Asher Langton @ 2007-08-17 20:12 UTC (permalink / raw)
  To: fortran, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1640 bytes --]

:ADDPATCH fortran:


Hi all,

Here's a patch to add -finit-local-zero, as well as some finer-grained
options.  I have a working version of the runtime-controlled
initialization as discussed previously, but it still needs to be
cleaned up a bit, so I'll submit it as a separate patch:

http://gcc.gnu.org/ml/fortran/2007-06/msg00028.html

I've bootstrapped and regression tested this on x86_64-linux.
Everything passes in the ordinary regression tests, and with
RUNTESTFLAGS="--tool_opts -finit-local-zero" added, the only failures
are tests that invoke cc1 ("cc1: warning: command line option
"-finit-local-zero" is valid for Fortran but not for C").

-Asher


2007-08-17  Asher Langton  <langton2@llnl.gov>

	PR fortran/20441
        * gfortran.h : Add init_local_* enums and init_flag_* flags to
	gfc_option_t.
	* lang.opt: Add -finit-local-zero, -finit-real, -finit-integer,
	and -finit-logical flags.
	* invoke.texi: Document new options.
	* resolve.c (build_init_assign): New function.
	(apply_init_assign): Move part of function into build_init_assign.
	(build_default_init_expr): Build local initializer (-finit-*).
	(apple_default_init_local): Apply local initializer (-finit-*).
	(resolve_fl_variable): Try to add local initializer (-finit-*).
	* options.c (gfc_init_options, gfc_handle_option): Handle
	-finit-local-zero, -finit-real, -finit-integer, and -finit-logical
	flags.

2007-08-17  Asher Langton  <langton2@llnl.gov>

	PR fortran/20441
	* gfortran.dg/init_flag_1.f90: New.
	* gfortran.dg/init_flag_2.f90: New.
	* gfortran.dg/init_flag_3.f90: New.
	* gfortran.dg/init_flag_4.f90: New.
	* gfortran.dg/init_flag_5.f90: New.

[-- Attachment #2: gfc070817_123829.diff --]
[-- Type: application/octet-stream, Size: 12347 bytes --]

Index: gfortran.h
===================================================================
--- gfortran.h	(revision 127568)
+++ gfortran.h	(working copy)
@@ -564,6 +564,37 @@ typedef enum
 }
 ioerror_codes;
 
+typedef enum
+{
+  GFC_INIT_REAL_OFF = 0,
+  GFC_INIT_REAL_ZERO,
+  GFC_INIT_REAL_NAN,
+  GFC_INIT_REAL_INF,
+  GFC_INIT_REAL_NEG_INF
+}
+init_local_real;
+
+typedef enum
+{
+  GFC_INIT_LOGICAL_OFF = 0,
+  GFC_INIT_LOGICAL_FALSE,
+  GFC_INIT_LOGICAL_TRUE
+}
+init_local_logical;
+
+typedef enum
+{
+  GFC_INIT_CHARACTER_OFF = 0,
+  GFC_INIT_CHARACTER_ZERO
+}
+init_local_character;
+
+typedef enum
+{
+  GFC_INIT_INTEGER_OFF = 0,
+  GFC_INIT_INTEGER_ON
+}
+init_local_integer;
 
 /************************* Structures *****************************/
 
@@ -1867,6 +1898,12 @@ typedef struct
   int flag_openmp;
   int flag_sign_zero;
   int flag_module_private;
+  int flag_init_local_zero;
+  int flag_init_integer;
+  int flag_init_integer_value;
+  int flag_init_real;
+  int flag_init_logical;
+  int flag_init_character;
 
   int fpe;
 
Index: lang.opt
===================================================================
--- lang.opt	(revision 127568)
+++ lang.opt	(working copy)
@@ -196,6 +196,22 @@ fimplicit-none
 Fortran
 Specify that no implicit typing is allowed, unless overridden by explicit IMPLICIT statements
 
+finit-integer=
+Fortran RejectNegative Joined
+-finit-integer=<n> Initialize local integer variables to n
+
+finit-local-zero
+Fortran
+Initialize local variables to zero (from g77)
+
+finit-logical=
+Fortran RejectNegative Joined
+-finit-logical=<true|false> Initialize local logical variables
+
+finit-real=
+Fortran RejectNegative Joined
+-finit-real=<zero|nan|inf|-inf> Initialize local real variables
+
 fmax-errors=
 Fortran RejectNegative Joined UInteger
 -fmax-errors=<n>	Maximum number of errors to report
Index: invoke.texi
===================================================================
--- invoke.texi	(revision 127568)
+++ invoke.texi	(working copy)
@@ -156,7 +156,8 @@ and warnings}.
 -fsecond-underscore @gol
 -fbounds-check  -fmax-stack-var-size=@var{n} @gol
 -fpack-derived  -frepack-arrays  -fshort-enums  -fexternal-blas @gol
--fblas-matmul-limit=@var{n}}
+-fblas-matmul-limit=@var{n} -finit-local-zero -finit-integer=@var{n} @gol
+-finit-real=@var{<zero|inf|-inf|nan>} -finit-logical=@var{<true|false>} }
 @end table
 
 @menu
@@ -919,6 +920,24 @@ geometric mean of the dimensions of the 
 
 The default value for @var{n} is 30.
 
+@item -finit-local-zero
+@item -finit-integer=@var{n}
+@item -finit-real=@var{<zero|inf|-inf|nan>} 
+@item -finit-logical=@var{<true|false>}
+@opindex @code{finit-local-zero}
+@opindex @code{finit-integer}
+@opindex @code{finit-real}
+@opindex @code{finit-logical}
+The @option{-finit-local-zero} option instructs the compiler to
+initialize local @code{INTEGER}, @code{REAL}, and @code{COMPLEX}
+variables to zero, @code{LOGICAL} variables to false, and
+@code{CHARACTER} variables to a string of null bytes.  Finer-grained
+initialization options are provided by the
+@option{-finit-integer=@var{n}},
+@option{-finit-real=@var{<zero|inf|-inf|nan>}} (which also initializes
+the real and imaginary parts of local @code{COMPLEX} variables), and
+@option{-finit-logical=@var{<true|false>}} options.  
+
 @end table
 
 @xref{Code Gen Options,,Options for Code Generation Conventions,
Index: resolve.c
===================================================================
--- resolve.c	(revision 127568)
+++ resolve.c	(working copy)
@@ -6405,26 +6405,15 @@ is_non_constant_shape_array (gfc_symbol 
   return not_constant;
 }
 
-
-/* Assign the default initializer to a derived type variable or result.  */
-
+/* Given a symbol and an initialization expression, add code to initialize
+   the symbol to the function entry.  */
 static void
-apply_default_init (gfc_symbol *sym)
+build_init_assign (gfc_symbol *sym, gfc_expr *init)
 {
   gfc_expr *lval;
-  gfc_expr *init = NULL;
   gfc_code *init_st;
   gfc_namespace *ns = sym->ns;
 
-  if (sym->attr.flavor != FL_VARIABLE && !sym->attr.function)
-    return;
-
-  if (sym->ts.type == BT_DERIVED && sym->ts.derived)
-    init = gfc_default_initializer (&sym->ts);
-
-  if (init == NULL)
-    return;
-
   /* Search for the function namespace if this is a contained
      function without an explicit result.  */
   if (sym->attr.function && sym == sym->result
@@ -6457,6 +6446,197 @@ apply_default_init (gfc_symbol *sym)
   init_st->expr2 = init;
 }
 
+/* Assign the default initializer to a derived type variable or result.  */
+
+static void
+apply_default_init (gfc_symbol *sym)
+{
+  gfc_expr *init = NULL;
+
+  if (sym->attr.flavor != FL_VARIABLE && !sym->attr.function)
+    return;
+
+  if (sym->ts.type == BT_DERIVED && sym->ts.derived)
+    init = gfc_default_initializer (&sym->ts);
+
+  if (init == NULL)
+    return;
+
+  build_init_assign (sym, init);
+}
+
+/* Build an initializer for a local integer, real, complex, logical, or
+   character variable, based on the command line flags finit-local-zero,
+   finit-integer=, finit-real=, finit-logical=, and finit-runtime.  Returns 
+   null if the symbol should not have a default initialization.  */
+static gfc_expr *
+build_default_init_expr (gfc_symbol *sym)
+{
+  int char_len;
+  gfc_expr *init_expr;
+
+  /* These symbols should never have a default initialization.  */
+  if ((sym->attr.dimension && !gfc_is_compile_time_shape (sym->as))
+      || sym->attr.external
+      || sym->attr.dummy
+      || sym->attr.pointer
+      || sym->attr.in_equivalence
+      || sym->attr.in_common
+      || sym->attr.data
+      || sym->module
+      || sym->attr.cray_pointee
+      || sym->attr.cray_pointer)
+    return NULL;
+
+  /* Now we'll try to build an initializer expression.  */
+  init_expr = gfc_get_expr ();
+  init_expr->expr_type = EXPR_CONSTANT;
+  init_expr->ts.type = sym->ts.type;
+  init_expr->ts.kind = sym->ts.kind;
+  init_expr->where = sym->declared_at;
+  
+  /* We will only initialize integers, reals, complex, logicals, and
+     characters, and only if the corresponding command-line flags
+     were set.  Otherwise, we free init_expr and return null.  */
+  switch (sym->ts.type)
+    {    
+    case BT_INTEGER:
+      if (gfc_option.flag_init_integer != GFC_INIT_INTEGER_OFF)
+	mpz_init_set_si (init_expr->value.integer, 
+			 gfc_option.flag_init_integer_value);
+      else
+	{
+	  gfc_free_expr (init_expr);
+	  init_expr = NULL;
+	}
+      break;
+
+    case BT_REAL:
+      mpfr_init (init_expr->value.real);
+      switch (gfc_option.flag_init_real)
+	{
+	case GFC_INIT_REAL_NAN:
+	  mpfr_set_nan (init_expr->value.real);
+	  break;
+
+	case GFC_INIT_REAL_INF:
+	  mpfr_set_inf (init_expr->value.real, 1);
+	  break;
+
+	case GFC_INIT_REAL_NEG_INF:
+	  mpfr_set_inf (init_expr->value.real, -1);
+	  break;
+
+	case GFC_INIT_REAL_ZERO:
+	  mpfr_set_ui (init_expr->value.real, 0.0, GFC_RND_MODE);
+	  break;
+
+	default:
+	  gfc_free_expr (init_expr);
+	  init_expr = NULL;
+	  break;
+	}
+      break;
+	  
+    case BT_COMPLEX:
+      mpfr_init (init_expr->value.complex.r);
+      mpfr_init (init_expr->value.complex.i);
+      switch (gfc_option.flag_init_real)
+	{
+	case GFC_INIT_REAL_NAN:
+	  mpfr_set_nan (init_expr->value.complex.r);
+	  mpfr_set_nan (init_expr->value.complex.i);
+	  break;
+
+	case GFC_INIT_REAL_INF:
+	  mpfr_set_inf (init_expr->value.complex.r, 1);
+	  mpfr_set_inf (init_expr->value.complex.i, 1);
+	  break;
+
+	case GFC_INIT_REAL_NEG_INF:
+	  mpfr_set_inf (init_expr->value.complex.r, -1);
+	  mpfr_set_inf (init_expr->value.complex.i, -1);
+	  break;
+
+	case GFC_INIT_REAL_ZERO:
+	  mpfr_set_ui (init_expr->value.complex.r, 0.0, GFC_RND_MODE);
+	  mpfr_set_ui (init_expr->value.complex.i, 0.0, GFC_RND_MODE);
+	  break;
+
+	default:
+	  gfc_free_expr (init_expr);
+	  init_expr = NULL;
+	  break;
+	}
+      break;
+	  
+    case BT_LOGICAL:
+      if (gfc_option.flag_init_logical == GFC_INIT_LOGICAL_FALSE)
+	init_expr->value.logical = 0;
+      else if (gfc_option.flag_init_logical == GFC_INIT_LOGICAL_TRUE)
+	init_expr->value.logical = 1;
+      else
+	{
+	  gfc_free_expr (init_expr);
+	  init_expr = NULL;
+	}
+      break;
+	  
+    case BT_CHARACTER:
+      /* For characters, the length must be constant in order to 
+	 create a default initializer.  */
+      if (gfc_option.flag_init_character == GFC_INIT_CHARACTER_ZERO
+	  && sym->ts.cl->length
+	  && sym->ts.cl->length->expr_type == EXPR_CONSTANT)
+	{
+	  char_len = mpz_get_si (sym->ts.cl->length->value.integer);
+	  init_expr->value.character.length = char_len;
+	  /* Set initial value to be a string of null characters.  */
+	  init_expr->value.character.string = gfc_getmem (char_len+1);
+	}
+      else
+	{
+	  gfc_free_expr (init_expr);
+	  init_expr = NULL;
+	}
+      break;
+	  
+    default:
+     gfc_free_expr (init_expr);
+     init_expr = NULL;
+    }
+  return init_expr;
+}
+
+/* Add an initialization expression to a local variable.  */
+static void
+apply_default_init_local (gfc_symbol *sym)
+{
+  gfc_expr *init = NULL;
+
+  /* The symbol should be a variable or a function return value.  */
+  if ((sym->attr.flavor != FL_VARIABLE && !sym->attr.function)
+      || (sym->attr.function && sym->result != sym))
+    return;
+
+  /* Try to build the initializer expression.  If we can't initialize
+     this symbol, then init will be NULL.  */
+  init = build_default_init_expr (sym);
+  if (init == NULL)
+    return;
+
+  /* For saved variables, we don't want to add an initializer at 
+     function entry, so we just add a static initializer.  */
+  if (sym->attr.save || sym->ns->save_all)
+    {
+      /* Don't clobber an existing initializer!  */
+      gcc_assert (sym->value == NULL);
+      sym->value = init;
+      return;
+    }
+
+  build_init_assign (sym, init);
+}
 
 /* Resolution of common features of flavors variable and procedure.  */
 
@@ -6571,6 +6751,9 @@ resolve_fl_variable (gfc_symbol *sym, in
 	}
     }
 
+  if (sym->value == NULL && sym->attr.referenced)
+    apply_default_init_local (sym); /* Try to apply a default initialization.  */
+
   /* Can the symbol have an initializer?  */
   flag = 0;
   if (sym->attr.allocatable || sym->attr.external || sym->attr.dummy
Index: options.c
===================================================================
--- options.c	(revision 127568)
+++ options.c	(working copy)
@@ -103,6 +103,11 @@ gfc_init_options (unsigned int argc ATTR
   gfc_option.flag_d_lines = -1;
   gfc_option.flag_openmp = 0;
   gfc_option.flag_sign_zero = 1;
+  gfc_option.flag_init_integer = GFC_INIT_INTEGER_OFF;
+  gfc_option.flag_init_integer_value = 0;
+  gfc_option.flag_init_real = GFC_INIT_REAL_OFF;
+  gfc_option.flag_init_logical = GFC_INIT_LOGICAL_OFF;
+  gfc_option.flag_init_character = GFC_INIT_CHARACTER_OFF;
 
   gfc_option.fpe = 0;
 
@@ -615,6 +620,43 @@ gfc_handle_option (size_t scode, const c
       gfc_option.flag_default_double = value;
       break;
 
+    case OPT_finit_local_zero:
+      gfc_option.flag_init_integer = GFC_INIT_INTEGER_ON;
+      gfc_option.flag_init_integer_value = 0;
+      gfc_option.flag_init_real = GFC_INIT_REAL_ZERO;
+      gfc_option.flag_init_logical = GFC_INIT_LOGICAL_FALSE;
+      gfc_option.flag_init_character = GFC_INIT_CHARACTER_ZERO;
+      break;
+
+    case OPT_finit_logical_:
+      if (!strcmp (arg, "false"))
+	gfc_option.flag_init_logical = GFC_INIT_LOGICAL_FALSE;
+      else if (!strcmp (arg, "true"))
+	gfc_option.flag_init_logical = GFC_INIT_LOGICAL_TRUE;
+      else
+	gfc_fatal_error ("Unrecognized option to -finit-logical: %s",
+			 arg);
+      break;
+
+    case OPT_finit_real_:
+      if (!strcmp (arg, "zero"))
+	gfc_option.flag_init_real = GFC_INIT_REAL_ZERO;
+      else if (!strcmp (arg, "nan"))
+	gfc_option.flag_init_real = GFC_INIT_REAL_NAN;
+      else if (!strcmp (arg, "inf"))
+	gfc_option.flag_init_real = GFC_INIT_REAL_INF;
+      else if (!strcmp (arg, "-inf"))
+	gfc_option.flag_init_real = GFC_INIT_REAL_NEG_INF;
+      else
+	gfc_fatal_error ("Unrecognized option to -finit-real: %s",
+			 arg);
+      break;      
+
+    case OPT_finit_integer_:
+      gfc_option.flag_init_integer = GFC_INIT_INTEGER_ON;
+      gfc_option.flag_init_integer_value = atoi (arg);
+      break;
+
     case OPT_I:
       gfc_add_include_path (arg, true);
       break;

[-- Attachment #3: init_flag_1.f90 --]
[-- Type: application/octet-stream, Size: 1321 bytes --]

! { dg-do run }
! { dg-options "-finit-local-zero" }

program init_flag_1
  call real_test
  call logical_test
  call int_test
  call complex_test
  call char_test
end program init_flag_1

! Test some initializations for both implicitly and
! explicitly declared local variables.
subroutine real_test
  real r1
  real r2(10)
  dimension r3(10,10)
  if (r1 /= 0.0) call abort
  if (r2(2) /= 0.0) call abort
  if (r3(5,5) /= 0.0) call abort
  if (r4 /= 0.0) call abort
end subroutine real_test

subroutine logical_test
  logical l1
  logical l2(2)
  if (l1 .neqv. .false.) call abort
  if (l2(2) .neqv. .false.) call abort
end subroutine logical_test

subroutine int_test
  integer i1
  integer i2(10)
  dimension i3(10,10)
  if (i1 /= 0) call abort
  if (i2(2) /= 0) call abort
  if (i3(5,5) /= 0) call abort
  if (i4 /= 0) call abort
end subroutine int_test

subroutine complex_test
  complex c1
  complex c2(20,20)
  if (c1 /= (0.0,0.0)) call abort
  if (c2(1,1) /= (0.0,0.0)) call abort 
end subroutine complex_test

subroutine char_test
  character*1 c1
  character*8 c2, c3(5)
  character c4(10)
  if (c1 /= '\0') call abort
  if (c2 /= '\0\0\0\0\0\0\0\0') call abort
  if (c3(1) /= '\0\0\0\0\0\0\0\0') call abort
  if (c3(5) /= '\0\0\0\0\0\0\0\0') call abort
  if (c4(5) /= '\0') call abort
end subroutine char_test

[-- Attachment #4: init_flag_2.f90 --]
[-- Type: application/octet-stream, Size: 1035 bytes --]

! { dg-do run }
! { dg-options "-finit-integer=1 -finit-logical=true -finit-real=zero" }

program init_flag_2
  call real_test
  call logical_test
  call int_test
  call complex_test
end program init_flag_2

! Test some initializations for both implicitly and
! explicitly declared local variables.
subroutine real_test
  real r1
  real r2(10)
  dimension r3(10,10)
  if (r1 /= 0.0) call abort
  if (r2(2) /= 0.0) call abort
  if (r3(5,5) /= 0.0) call abort
  if (r4 /= 0.0) call abort
end subroutine real_test

subroutine logical_test
  logical l1
  logical l2(2)
  if (l1 .neqv. .true.) call abort
  if (l2(2) .neqv. .true.) call abort
end subroutine logical_test

subroutine int_test
  integer i1
  integer i2(10)
  dimension i3(10,10)
  if (i1 /= 1) call abort
  if (i2(2) /= 1) call abort
  if (i3(5,5) /= 1) call abort
  if (i4 /= 1) call abort
end subroutine int_test

subroutine complex_test
  complex c1
  complex c2(20,20)
  if (c1 /= (0.0,0.0)) call abort
  if (c2(1,1) /= (0.0,0.0)) call abort 
end subroutine complex_test

[-- Attachment #5: init_flag_3.f90 --]
[-- Type: application/octet-stream, Size: 1049 bytes --]

! { dg-do run }
! { dg-options "-finit-integer=-1 -finit-logical=false -finit-real=nan" }

program init_flag_3
  call real_test
  call logical_test
  call int_test
  call complex_test
end program init_flag_3

! Test some initializations for both implicitly and
! explicitly declared local variables.
subroutine real_test
  real r1
  real r2(10)
  dimension r3(10,10)
  if (r1 .eq. r1) call abort
  if (r2(2) .eq. r2(2)) call abort
  if (r3(5,5) .eq. r3(5,5)) call abort
  if (r4 .eq. r4) call abort
end subroutine real_test

subroutine logical_test
  logical l1
  logical l2(2)
  if (l1 .neqv. .false.) call abort
  if (l2(2) .neqv. .false.) call abort
end subroutine logical_test

subroutine int_test
  integer i1
  integer i2(10)
  dimension i3(10,10)
  if (i1 /= -1) call abort
  if (i2(2) /= -1) call abort
  if (i3(5,5) /= -1) call abort
  if (i4 /= -1) call abort
end subroutine int_test

subroutine complex_test
  complex c1
  complex c2(20,20)
  if (c1 .eq. c1) call abort
  if (c2(1,1) .eq. c2(1,1)) call abort 
end subroutine complex_test

[-- Attachment #6: init_flag_4.f90 --]
[-- Type: application/octet-stream, Size: 505 bytes --]

! { dg-do run }
! { dg-options "-finit-real=inf" }

program init_flag_4
  call real_test
end program init_flag_4

! Test some initializations for both implicitly and
! explicitly declared local variables.
subroutine real_test
  real r1
  real r2(10)
  dimension r3(10,10)
  if (r1 .le. 0 .or. r1 .ne. 2*r1) call abort
  if (r2(2) .le. 0 .or. r2(2) .ne. 2*r2(2)) call abort
  if (r3(5,5) .le. 0 .or. r3(5,5) .ne. 2*r3(5,5)) call abort
  if (r4 .le. 0 .or. r4 .ne. 2*r4) call abort
end subroutine real_test

[-- Attachment #7: init_flag_5.f90 --]
[-- Type: application/octet-stream, Size: 506 bytes --]

! { dg-do run }
! { dg-options "-finit-real=-inf" }

program init_flag_5
  call real_test
end program init_flag_5

! Test some initializations for both implicitly and
! explicitly declared local variables.
subroutine real_test
  real r1
  real r2(10)
  dimension r3(10,10)
  if (r1 .ge. 0 .or. r1 .ne. 2*r1) call abort
  if (r2(2) .ge. 0 .or. r2(2) .ne. 2*r2(2)) call abort
  if (r3(5,5) .ge. 0 .or. r3(5,5) .ne. 2*r3(5,5)) call abort
  if (r4 .ge. 0 .or. r4 .ne. 2*r4) call abort
end subroutine real_test

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2007-09-21  2:40 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-08-17 20:12 [PATCH, fortran] PR20441 -finit-local-zero Asher Langton
2007-08-20 16:33 ` Tobias Burnus
2007-08-20 17:49   ` Asher Langton
2007-08-21 11:41     ` François-Xavier Coudert
2007-08-21 15:58       ` Tobias Schlüter
2007-08-29  2:02   ` Asher Langton
2007-08-31 22:14 ` Asher Langton
2007-09-01 20:26   ` Thomas Koenig
2007-09-14 11:27   ` Tobias Burnus
2007-09-14 11:34     ` François-Xavier Coudert
2007-09-14 13:15     ` Asher Langton
     [not found]   ` <46E7B808.8070104@net-b.de>
2007-09-14 12:55     ` Asher Langton
2007-09-19 21:44       ` Thomas Koenig
2007-09-20  4:47         ` Asher Langton
2007-09-20 10:08           ` Asher Langton
2007-09-20 10:14             ` Tobias Burnus
2007-09-20 20:20               ` Thomas Koenig
2007-09-20 20:22                 ` FX Coudert
2007-09-20 20:54                   ` Thomas Koenig
2007-09-21  7:36                     ` Asher Langton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).