* [PATCH improve early strlen range folding (PR 83671)
@ 2018-01-06 22:04 Martin Sebor
2018-01-08 11:37 ` Richard Biener
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Martin Sebor @ 2018-01-06 22:04 UTC (permalink / raw)
To: Gcc Patch List
[-- Attachment #1: Type: text/plain, Size: 1137 bytes --]
Bug 83671 - Fix for false positive reported by -Wstringop-overflow
does not work at -O1, points out that the string length range
optimization implemented as a solution for bug 83373 doesn't help
at -O1. The root cause is that the fix was added to the strlen
pass that doesn't run at -O1.
The string length range computation doesn't depend on the strlen
pass, and so the range can be set earlier, in gimple-fold, and
its results made available even at -O1. The attached patch
changes the gimple_fold_builtin_strlen() function to do that.
While testing the change I came across a number of other simple
strlen cases that currently aren't handled, some at -O1, others
at all. I added code to handle some of the simplest of them
and opened bugs to remind us/myself to get back to the rest in
the future (pr83693 and pr83702). The significant enhancement
is handling arrays of arrays with non-constant indices and
pointers to such things, such as in:
char a[2][7];
void f (int i)
{
if (strlen (a[i]) > 6) // eliminated with the patch
abort ();
}
Attached is a near-minimal patch to handle PR 83671.
Martin
[-- Attachment #2: gcc-83671.diff --]
[-- Type: text/x-patch, Size: 21751 bytes --]
PR tree-optimization/83671 - Fix for false positive reported by -Wstringop-overflow does not work with inlining
gcc/testsuite/ChangeLog:
PR tree-optimization/83671
* gcc.dg/strlenopt-40.c: New test.
* gcc.dg/strlenopt-41.c: New test.
gcc/ChangeLog:
PR tree-optimization/83671
* builtins.c (c_strlen): Unconditionally return zero for the empty
string.
Use -Warray-bounds for warnings.
* gimple-fold.c (get_range_strlen): Handle non-constant lengths
for non-constant array indices with COMPONENT_REF, arrays of
arrays, and pointers to arrays.
(gimple_fold_builtin_strlen): Determine and set length range for
non-constant character arrays.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 1d6e69d..fa07a48 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -621,6 +621,9 @@ c_strlen (tree src, int only_value)
return NULL_TREE;
}
+ if (!maxelts)
+ return ssize_int (0);
+
/* We don't know the starting offset, but we do know that the string
has no internal zero bytes. We can assume that the offset falls
within the bounds of the string; otherwise, the programmer deserves
@@ -651,7 +654,8 @@ c_strlen (tree src, int only_value)
if (only_value != 2
&& !TREE_NO_WARNING (src))
{
- warning_at (loc, 0, "offset %qwi outside bounds of constant string",
+ warning_at (loc, OPT_Warray_bounds,
+ "offset %qwi outside bounds of constant string",
eltoff);
TREE_NO_WARNING (src) = 1;
}
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 7e4cb74..2dead65 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1299,7 +1299,7 @@ static bool
get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
bool fuzzy, bool *flexp)
{
- tree var, val;
+ tree var, val = NULL_TREE;
gimple *def_stmt;
/* The minimum and maximum length. The MAXLEN pointer stays unchanged
@@ -1311,14 +1311,30 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
{
/* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
if (TREE_CODE (arg) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
- && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+ && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
{
- tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
- if (TREE_CODE (aop0) == INDIRECT_REF
- && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
- return get_range_strlen (TREE_OPERAND (aop0, 0),
- length, visited, type, fuzzy, flexp);
+ tree op = TREE_OPERAND (arg, 0);
+ if (integer_zerop (TREE_OPERAND (op, 1)))
+ {
+ tree aop0 = TREE_OPERAND (op, 0);
+ if (TREE_CODE (aop0) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
+ return get_range_strlen (TREE_OPERAND (aop0, 0),
+ length, visited, type, fuzzy, flexp);
+ }
+ else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
+ {
+ tree idx = TREE_OPERAND (op, 1);
+
+ arg = TREE_OPERAND (op, 0);
+ tree optype = TREE_TYPE (arg);
+ if (tree dom = TYPE_DOMAIN (optype))
+ if (tree bound = TYPE_MAX_VALUE (dom))
+ if (TREE_CODE (bound) == INTEGER_CST
+ && TREE_CODE (idx) == INTEGER_CST
+ && tree_int_cst_lt (bound, idx))
+ return false;
+ }
}
if (type == 2)
@@ -1337,21 +1353,48 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
return get_range_strlen (TREE_OPERAND (arg, 0), length,
visited, type, fuzzy, flexp);
- if (TREE_CODE (arg) == COMPONENT_REF
+ if (TREE_CODE (arg) == ARRAY_REF)
+ {
+ tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+
+ while (TREE_CODE (type) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+
+ val = TYPE_SIZE_UNIT (type);
+ if (!val || integer_zerop (val))
+ return false;
+
+ val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+ integer_one_node);
+ /* Set the minimum size to zero since the string in
+ the array could have zero length. */
+ *minlen = ssize_int (0);
+ }
+ else if (TREE_CODE (arg) == COMPONENT_REF
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
{
/* Use the type of the member array to determine the upper
bound on the length of the array. This may be overly
optimistic if the array itself isn't NUL-terminated and
the caller relies on the subsequent member to contain
- the NUL.
+ the NUL but that would only be considered valid if
+ the array were the last member of a struct.
Set *FLEXP to true if the array whose bound is being
used is at the end of a struct. */
if (array_at_struct_end_p (arg))
*flexp = true;
arg = TREE_OPERAND (arg, 1);
- val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
+
+ tree type = TREE_TYPE (arg);
+
+ while (TREE_CODE (type) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+
+ /* Fail when the array bound is unknown or zero. */
+ val = TYPE_SIZE_UNIT (type);
if (!val || integer_zerop (val))
return false;
val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
@@ -1361,17 +1404,25 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
*minlen = ssize_int (0);
}
- if (VAR_P (arg)
- && TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+ if (VAR_P (arg))
{
- val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
- if (!val || TREE_CODE (val) != INTEGER_CST || integer_zerop (val))
- return false;
- val = wide_int_to_tree (TREE_TYPE (val),
- wi::sub(wi::to_wide (val), 1));
- /* Set the minimum size to zero since the string in
- the array could have zero length. */
- *minlen = ssize_int (0);
+ tree type = TREE_TYPE (arg);
+ if (POINTER_TYPE_P (type))
+ type = TREE_TYPE (type);
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ val = TYPE_SIZE_UNIT (type);
+ if (!val
+ || TREE_CODE (val) != INTEGER_CST
+ || integer_zerop (val))
+ return false;
+ val = wide_int_to_tree (TREE_TYPE (val),
+ wi::sub(wi::to_wide (val), 1));
+ /* Set the minimum size to zero since the string in
+ the array could have zero length. */
+ *minlen = ssize_int (0);
+ }
}
}
@@ -3462,12 +3513,44 @@ static bool
gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
{
gimple *stmt = gsi_stmt (*gsi);
- tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
- if (!len)
- return false;
- len = force_gimple_operand_gsi (gsi, len, true, NULL, true, GSI_SAME_STMT);
- replace_call_with_value (gsi, len);
- return true;
+
+ wide_int minlen;
+ wide_int maxlen;
+
+ tree lenrange[2];
+ if (!get_range_strlen (gimple_call_arg (stmt, 0), lenrange)
+ && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
+ && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
+ {
+ /* The range of lengths refers to either a single constant
+ string or to the longest and shortest constant string
+ referenced by the argument of the strlen() call, or to
+ the strings that can possibly be stored in the arrays
+ the argument refers to. */
+ minlen = wi::to_wide (lenrange[0]);
+ maxlen = wi::to_wide (lenrange[1]);
+ }
+ else
+ {
+ unsigned prec = TYPE_PRECISION (sizetype);
+
+ minlen = wi::shwi (0, prec);
+ maxlen = wi::to_wide (max_object_size (), prec) - 2;
+ }
+
+ if (minlen == maxlen)
+ {
+ lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
+ true, GSI_SAME_STMT);
+ replace_call_with_value (gsi, lenrange[0]);
+ return true;
+ }
+
+ tree lhs = gimple_call_lhs (stmt);
+ if (lhs && TREE_CODE (lhs) == SSA_NAME)
+ set_range_info (lhs, VR_RANGE, minlen, maxlen);
+
+ return false;
}
/* Fold a call to __builtin_acc_on_device. */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-40.c b/gcc/testsuite/gcc.dg/strlenopt-40.c
new file mode 100644
index 0000000..f4577d6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-40.c
@@ -0,0 +1,393 @@
+/* PR tree-optimization/83671 - fix for false positive reported by
+ -Wstringop-overflow does not work with inlining
+ { dg-do compile }
+ { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do { \
+ extern void FAILNAME (name) (void); \
+ FAILNAME (name)(); \
+ } while (0)
+
+/* Macros to emit a call to funcation named
+ call_in_{true,false}_branch_not_eliminated_on_line_NNN()
+ for each call that's expected to be eliminated. The dg-final
+ scan-tree-dump-time directive at the bottom of the test verifies
+ that no such call appears in output. */
+#define ELIM_TRUE(expr) \
+ if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define ELIM_FALSE(expr) \
+ if (!!(expr)) FAIL (in_false_branch_not_eliminated); else (void)0
+
+/* Macro to emit a call to a function named
+ call_made_in_{true,false}_branch_on_line_NNN()
+ for each call that's expected to be retained. The dg-final
+ scan-tree-dump-time directive at the bottom of the test verifies
+ that the expected number of both kinds of calls appears in output
+ (a pair for each line with the invocation of the KEEP() macro. */
+#define KEEP(expr) \
+ if (expr) \
+ FAIL (made_in_true_branch); \
+ else \
+ FAIL (made_in_false_branch)
+
+typedef char A3[3], A5[5], A7[7], AX[];
+
+typedef A3 A7_3[7];
+typedef A3 AX_3[];
+typedef A5 A7_5[7];
+typedef A7 A5_7[5];
+
+extern A7_3 a7_3;
+extern A5_7 a5_7;
+extern AX_3 ax_3;
+
+extern A3 a3;
+extern A7 a5;
+extern A7 a7;
+extern AX ax;
+
+extern A3 *pa3;
+extern A5 *pa5;
+extern A7 *pa7;
+
+extern A7_3 *pa7_3;
+extern AX_3 *pax_3;
+extern A5_7 *pa5_7;
+extern A7_5 *pa7_5;
+
+extern char *ptr;
+
+struct MemArrays0 {
+ A7_3 a7_3;
+ A5_7 a5_7;
+ char a3[3], a5[5], a0[0];
+};
+struct MemArraysX { char a3[3], a5[5], ax[]; };
+struct MemArrays7 { char a3[3], a5[5], a7[7]; };
+
+struct MemArrays0 ma0_3_5_7[3][5][7];
+
+void elim_strings (int i)
+{
+ ELIM_TRUE (strlen (i < 0 ? "123" : "321") == 3);
+ ELIM_FALSE (strlen (i < 0 ? "123" : "321") > 3);
+ ELIM_FALSE (strlen (i < 0 ? "123" : "321") < 3);
+
+ ELIM_TRUE (strlen (i < 0 ? "123" : "4321") >= 3);
+ ELIM_FALSE (strlen (i < 0 ? "123" : "4321") > 4);
+ ELIM_FALSE (strlen (i < 0 ? "123" : "4321") < 3);
+
+ ELIM_TRUE (strlen (i < 0 ? "1234" : "321") >= 3);
+ ELIM_FALSE (strlen (i < 0 ? "1234" : "321") < 3);
+ ELIM_FALSE (strlen (i < 0 ? "1234" : "321") > 4);
+
+ ELIM_TRUE (strlen (i < 0 ? "123" : "4321") <= 4);
+ ELIM_TRUE (strlen (i < 0 ? "1234" : "321") <= 4);
+
+ ELIM_TRUE (strlen (i < 0 ? "1" : "123456789") <= 9);
+ ELIM_TRUE (strlen (i < 0 ? "1" : "123456789") >= 1);
+}
+
+/* Verify that strlen calls involving uninitialized global arrays
+ of known size are eliminated when they appear in expressions
+ that test for results that must be true. */
+void elim_global_arrays (int i)
+{
+ /* Verify that the expression involving the strlen call as well
+ as whatever depends on it is eliminated from the test output.
+ All these expressions must be trivially true. */
+ ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3[0]);
+ ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3[1]);
+ ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3[6]);
+ ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3[i]);
+
+ ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7[0]);
+ ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7[1]);
+ ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7[4]);
+ ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7[0]);
+
+ ELIM_TRUE (strlen (ax_3[0]) < sizeof ax_3[0]);
+ ELIM_TRUE (strlen (ax_3[1]) < sizeof ax_3[1]);
+ ELIM_TRUE (strlen (ax_3[9]) < sizeof ax_3[9]);
+ ELIM_TRUE (strlen (ax_3[i]) < sizeof ax_3[i]);
+
+ ELIM_TRUE (strlen (a3) < sizeof a3);
+ ELIM_TRUE (strlen (a7) < sizeof a7);
+
+ ELIM_TRUE (strlen (ax) != DIFF_MAX);
+ ELIM_TRUE (strlen (ax) != DIFF_MAX - 1);
+ ELIM_TRUE (strlen (ax) < DIFF_MAX - 1);
+}
+
+void elim_pointer_to_arrays (void)
+{
+ ELIM_TRUE (strlen (*pa7) < 7);
+ ELIM_TRUE (strlen (*pa5) < 5);
+ ELIM_TRUE (strlen (*pa3) < 3);
+
+ ELIM_TRUE (strlen ((*pa7_3)[0]) < 3);
+ ELIM_TRUE (strlen ((*pa7_3)[1]) < 3);
+ ELIM_TRUE (strlen ((*pa7_3)[6]) < 3);
+
+ ELIM_TRUE (strlen ((*pax_3)[0]) < 3);
+ ELIM_TRUE (strlen ((*pax_3)[1]) < 3);
+ ELIM_TRUE (strlen ((*pax_3)[9]) < 3);
+
+ ELIM_TRUE (strlen ((*pa5_7)[0]) < 7);
+ ELIM_TRUE (strlen ((*pa5_7)[1]) < 7);
+ ELIM_TRUE (strlen ((*pa5_7)[4]) < 7);
+}
+
+void elim_global_arrays_and_strings (int i)
+{
+ ELIM_TRUE (strlen (i < 0 ? a3 : "") < 3);
+ ELIM_TRUE (strlen (i < 0 ? a3 : "1") < 3);
+ ELIM_TRUE (strlen (i < 0 ? a3 : "12") < 3);
+ ELIM_TRUE (strlen (i < 0 ? a3 : "123") < 4);
+
+ ELIM_FALSE (strlen (i < 0 ? a3 : "") > 3);
+ ELIM_FALSE (strlen (i < 0 ? a3 : "1") > 3);
+ ELIM_FALSE (strlen (i < 0 ? a3 : "12") > 3);
+ ELIM_FALSE (strlen (i < 0 ? a3 : "123") > 4);
+
+ ELIM_TRUE (strlen (i < 0 ? a7 : "") < 7);
+ ELIM_TRUE (strlen (i < 0 ? a7 : "1") < 7);
+ ELIM_TRUE (strlen (i < 0 ? a7 : "12") < 7);
+ ELIM_TRUE (strlen (i < 0 ? a7 : "123") < 7);
+ ELIM_TRUE (strlen (i < 0 ? a7 : "123456") < 7);
+ ELIM_TRUE (strlen (i < 0 ? a7 : "1234567") < 8);
+
+ ELIM_FALSE (strlen (i < 0 ? a7 : "") > 6);
+ ELIM_FALSE (strlen (i < 0 ? a7 : "1") > 6);
+ ELIM_FALSE (strlen (i < 0 ? a7 : "12") > 6);
+ ELIM_FALSE (strlen (i < 0 ? a7 : "123") > 6);
+ ELIM_FALSE (strlen (i < 0 ? a7 : "123456") > 7);
+ ELIM_FALSE (strlen (i < 0 ? a7 : "1234567") > 8);
+}
+
+void elim_member_arrays_obj (int i)
+{
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < 3);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < 3);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < 3);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < 5);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < 5);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < 5);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 3);
+
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 7);
+}
+
+void elim_member_arrays_ptr (struct MemArrays0 *ma0,
+ struct MemArraysX *max,
+ struct MemArrays7 *ma7,
+ int i)
+{
+ ELIM_TRUE (strlen (ma0->a7_3[0]) < 3);
+ ELIM_TRUE (strlen (ma0->a7_3[1]) < 3);
+ ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
+ ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
+ ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+ ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+
+ ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
+ ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
+ ELIM_TRUE (strlen (ma0[1].a5_7[0]) < 7);
+ ELIM_TRUE (strlen (ma0[1].a5_7[4]) < 7);
+ ELIM_TRUE (strlen (ma0[9].a5_7[0]) < 7);
+ ELIM_TRUE (strlen (ma0[9].a5_7[4]) < 7);
+
+ ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
+ ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
+ ELIM_TRUE (strlen (ma0->a0) < DIFF_MAX - 1);
+
+ ELIM_TRUE (strlen (max->a3) < sizeof max->a3);
+ ELIM_TRUE (strlen (max->a5) < sizeof max->a5);
+ ELIM_TRUE (strlen (max->ax) < DIFF_MAX - 1);
+
+ ELIM_TRUE (strlen (ma7->a3) < sizeof max->a3);
+ ELIM_TRUE (strlen (ma7->a5) < sizeof max->a5);
+ ELIM_TRUE (strlen (ma7->a7) < DIFF_MAX - 1);
+}
+
+
+#line 1000
+
+/* Verify that strlen calls involving uninitialized global arrays
+ of unknown size are not eliminated when they appear in expressions
+ that test for results that need not be true. */
+void keep_global_arrays (int i)
+{
+ KEEP (strlen (a7_3[0]) < 2);
+ KEEP (strlen (a7_3[1]) < 2);
+ KEEP (strlen (a7_3[6]) < 2);
+ KEEP (strlen (a7_3[i]) < 2);
+
+ KEEP (strlen (a5_7[0]) < 6);
+ KEEP (strlen (a5_7[1]) < 6);
+ KEEP (strlen (a5_7[4]) < 6);
+ KEEP (strlen (a5_7[i]) < 6);
+
+ KEEP (strlen (ax_3[0]) < 2);
+ KEEP (strlen (ax_3[1]) < 2);
+ KEEP (strlen (ax_3[2]) < 2);
+ KEEP (strlen (ax_3[i]) < 2);
+
+ KEEP (strlen (a3) < 2);
+ KEEP (strlen (a7) < 6);
+
+ KEEP (strlen (a3 + i) < 2);
+ KEEP (strlen (a7 + i) < 2);
+
+ /* The length of an array of unknown size may be as large as
+ DIFF_MAX - 2. */
+ KEEP (strlen (ax) != DIFF_MAX - 2);
+ KEEP (strlen (ax) < DIFF_MAX - 2);
+ KEEP (strlen (ax) < 999);
+ KEEP (strlen (ax) < 1);
+}
+
+void keep_pointer_to_arrays (void)
+{
+ KEEP (strlen (*pa7) < 6);
+ KEEP (strlen (*pa5) < 4);
+ KEEP (strlen (*pa3) < 2);
+
+ KEEP (strlen ((*pa7_3)[0]) < 2);
+ KEEP (strlen ((*pa7_3)[1]) < 2);
+ KEEP (strlen ((*pa7_3)[6]) < 2);
+
+ KEEP (strlen ((*pax_3)[0]) < 2);
+ KEEP (strlen ((*pax_3)[1]) < 2);
+ KEEP (strlen ((*pax_3)[9]) < 2);
+
+ KEEP (strlen ((*pa5_7)[0]) < 6);
+ KEEP (strlen ((*pa5_7)[1]) < 6);
+ KEEP (strlen ((*pa5_7)[4]) < 6);
+}
+
+void keep_global_arrays_and_strings (int i)
+{
+ KEEP (strlen (i < 0 ? a3 : "") < 2);
+ KEEP (strlen (i < 0 ? a3 : "1") < 2);
+ KEEP (strlen (i < 0 ? a3 : "12") < 2);
+ KEEP (strlen (i < 0 ? a3 : "123") < 3);
+
+ KEEP (strlen (i < 0 ? a7 : "") < 5);
+ KEEP (strlen (i < 0 ? a7 : "1") < 5);
+ KEEP (strlen (i < 0 ? a7 : "12") < 5);
+ KEEP (strlen (i < 0 ? a7 : "123") < 5);
+ KEEP (strlen (i < 0 ? a7 : "123456") < 6);
+ KEEP (strlen (i < 0 ? a7 : "1234567") < 6);
+}
+
+void keep_member_arrays_obj (int i)
+{
+ KEEP (strlen (ma0_3_5_7[0][0][0].a3) < 2);
+ KEEP (strlen (ma0_3_5_7[0][0][1].a3) < 2);
+ KEEP (strlen (ma0_3_5_7[0][0][2].a3) < 2);
+ KEEP (strlen (ma0_3_5_7[0][0][6].a3) < 2);
+
+ KEEP (strlen (ma0_3_5_7[1][0][0].a3) < 2);
+ KEEP (strlen (ma0_3_5_7[2][0][1].a3) < 2);
+
+ KEEP (strlen (ma0_3_5_7[1][1][0].a3) < 2);
+ KEEP (strlen (ma0_3_5_7[2][4][6].a3) < 2);
+
+ KEEP (strlen (ma0_3_5_7[0][0][0].a5) < 4);
+ KEEP (strlen (ma0_3_5_7[0][0][1].a5) < 4);
+ KEEP (strlen (ma0_3_5_7[0][0][2].a5) < 4);
+ KEEP (strlen (ma0_3_5_7[0][0][6].a5) < 4);
+
+ KEEP (strlen (ma0_3_5_7[1][0][0].a5) < 4);
+ KEEP (strlen (ma0_3_5_7[2][0][1].a5) < 4);
+
+ KEEP (strlen (ma0_3_5_7[1][1][0].a5) < 4);
+ KEEP (strlen (ma0_3_5_7[2][4][6].a5) < 4);
+
+ KEEP (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 2);
+ KEEP (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 2);
+
+ KEEP (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 6);
+ KEEP (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 6);
+}
+
+void keep_member_arrays_ptr (struct MemArrays0 *ma0,
+ struct MemArraysX *max,
+ struct MemArrays7 *ma7,
+ int i)
+{
+ KEEP (strlen (ma0->a7_3[0]) > 0);
+ KEEP (strlen (ma0->a7_3[0]) < 2);
+ KEEP (strlen (ma0->a7_3[1]) < 2);
+ KEEP (strlen (ma0->a7_3[6]) < 2);
+ KEEP (strlen (ma0->a7_3[6]) < 2);
+ KEEP (strlen (ma0->a7_3[i]) > 0);
+ KEEP (strlen (ma0->a7_3[i]) < 2);
+ KEEP (strlen (ma0->a7_3[i]) < 2);
+
+ KEEP (strlen (ma0->a5_7[0]) < 5);
+ KEEP (strlen (ma0[0].a5_7[0]) < 5);
+ KEEP (strlen (ma0[1].a5_7[0]) < 5);
+ KEEP (strlen (ma0[9].a5_7[0]) < 5);
+ KEEP (strlen (ma0[9].a5_7[4]) < 5);
+ KEEP (strlen (ma0[i].a5_7[4]) < 5);
+ KEEP (strlen (ma0[i].a5_7[i]) < 5);
+
+ KEEP (strlen (ma0->a0) < DIFF_MAX - 2);
+ KEEP (strlen (ma0->a0) < 999);
+ KEEP (strlen (ma0->a0) < 1);
+
+ KEEP (strlen (max->ax) < DIFF_MAX - 2);
+ KEEP (strlen (max->ax) < 999);
+ KEEP (strlen (max->ax) < 1);
+
+ KEEP (strlen (ma7->a7) < DIFF_MAX - 2);
+ KEEP (strlen (ma7->a7) < 999);
+ KEEP (strlen (ma7->a7) < 1);
+}
+
+void keep_pointers (const char *s)
+{
+ KEEP (strlen (ptr) < DIFF_MAX - 2);
+ KEEP (strlen (ptr) < 999);
+ KEEP (strlen (ptr) < 1);
+
+ KEEP (strlen (s) < DIFF_MAX - 2);
+ KEEP (strlen (s) < 999);
+ KEEP (strlen (s) < 1);
+}
+
+
+/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
+ { dg-final { scan-tree-dump-times "call_in_false_branch_not_eliminated_" 0 "optimized" } }
+
+ { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } }
+ { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-41.c b/gcc/testsuite/gcc.dg/strlenopt-41.c
new file mode 100644
index 0000000..c5e8eb6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-41.c
@@ -0,0 +1,34 @@
+/* PR tree-optimization/83671 - fix for false positive reported by
+ -Wstringop-overflow does not work with inlining
+ Verify that the length the empty string is folded to zero even at -O1
+ regardless of offset into it.
+ Also verify that the length of a non-empty string isn't folded given
+ a variable offset.
+ { dg-do compile }
+ { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+inline unsigned length (const char *s)
+{
+ return __builtin_strlen (s);
+}
+
+void check_length_cst (int i)
+{
+ unsigned len = length (&""[i]);
+
+ if (len)
+ __builtin_abort ();
+}
+
+void check_length_var (int i)
+{
+ unsigned len = length (&"1"[i]);
+
+ if (len != 1)
+ __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-times "abort" 1 "optimized" } }
+ { dg-final { scan-tree-dump-times "strlen" 1 "optimized" } } */
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH improve early strlen range folding (PR 83671)
2018-01-06 22:04 [PATCH improve early strlen range folding (PR 83671) Martin Sebor
@ 2018-01-08 11:37 ` Richard Biener
2018-01-09 3:21 ` Martin Sebor
2018-01-10 19:44 ` Jeff Law
2018-01-11 1:51 ` H.J. Lu
2 siblings, 1 reply; 7+ messages in thread
From: Richard Biener @ 2018-01-08 11:37 UTC (permalink / raw)
To: Martin Sebor; +Cc: Gcc Patch List
On Sat, Jan 6, 2018 at 11:04 PM, Martin Sebor <msebor@gmail.com> wrote:
> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
> does not work at -O1, points out that the string length range
> optimization implemented as a solution for bug 83373 doesn't help
> at -O1. The root cause is that the fix was added to the strlen
> pass that doesn't run at -O1.
>
> The string length range computation doesn't depend on the strlen
> pass, and so the range can be set earlier, in gimple-fold, and
> its results made available even at -O1. The attached patch
> changes the gimple_fold_builtin_strlen() function to do that.
>
> While testing the change I came across a number of other simple
> strlen cases that currently aren't handled, some at -O1, others
> at all. I added code to handle some of the simplest of them
> and opened bugs to remind us/myself to get back to the rest in
> the future (pr83693 and pr83702). The significant enhancement
> is handling arrays of arrays with non-constant indices and
> pointers to such things, such as in:
>
> char a[2][7];
>
> void f (int i)
> {
> if (strlen (a[i]) > 6) // eliminated with the patch
> abort ();
> }
>
> Attached is a near-minimal patch to handle PR 83671.
Please don't use set_range_info form insinde fold_stmt (), this is
IMHO a layering violation.
Why not restrict -Wstrinop-overflow to -O2+?
Richard.
> Martin
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH improve early strlen range folding (PR 83671)
2018-01-08 11:37 ` Richard Biener
@ 2018-01-09 3:21 ` Martin Sebor
2018-01-10 1:18 ` Jeff Law
0 siblings, 1 reply; 7+ messages in thread
From: Martin Sebor @ 2018-01-09 3:21 UTC (permalink / raw)
To: Richard Biener; +Cc: Gcc Patch List
On 01/08/2018 04:28 AM, Richard Biener wrote:
> On Sat, Jan 6, 2018 at 11:04 PM, Martin Sebor <msebor@gmail.com> wrote:
>> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
>> does not work at -O1, points out that the string length range
>> optimization implemented as a solution for bug 83373 doesn't help
>> at -O1. The root cause is that the fix was added to the strlen
>> pass that doesn't run at -O1.
>>
>> The string length range computation doesn't depend on the strlen
>> pass, and so the range can be set earlier, in gimple-fold, and
>> its results made available even at -O1. The attached patch
>> changes the gimple_fold_builtin_strlen() function to do that.
>>
>> While testing the change I came across a number of other simple
>> strlen cases that currently aren't handled, some at -O1, others
>> at all. I added code to handle some of the simplest of them
>> and opened bugs to remind us/myself to get back to the rest in
>> the future (pr83693 and pr83702). The significant enhancement
>> is handling arrays of arrays with non-constant indices and
>> pointers to such things, such as in:
>>
>> char a[2][7];
>>
>> void f (int i)
>> {
>> if (strlen (a[i]) > 6) // eliminated with the patch
>> abort ();
>> }
>>
>> Attached is a near-minimal patch to handle PR 83671.
>
> Please don't use set_range_info form insinde fold_stmt (), this is
> IMHO a layering violation.
>
> Why not restrict -Wstrinop-overflow to -O2+?
For simple cases, -Wstrinop-overflow works even without
optimization. Given the notoriety of buffer overflows and
because not all software is or can always be compiled with
-O2 or even with -O1, I think it's important to keep it working.
I initially did consider resolving PR 83671 as won't fix because
at -O1 neither the strlen pass not VRP runs. But as I explained
above, because the range doesn't depend on the strlen pass and
can be determined and set anywhere, regardless of the optimization
level, doing it outside the pass seems like a win-win. Exposing
the range early improves downstream transformations before strlen
runs or even when it doesn't, and as a bonus, the warning also
disappears.
But I don't understand the concern. The range is only set when
the expression is an SSA_NAME, and setting in this function here
seems no different than setting it in one if its callers (which
may be, indirectly. in a pass like strlen). If you have
a suggestion for a better place to set it I'd be happy to move
it, I just don't see the problem.
In case it wasn't clear from my description (and got obscured
by the example above), this change is not about just arrays of
arrays (that's a minor improvement I included in it). It's
bout letting GCC optimize all strlen(array) expressions at -O1,
such as the following:
char a[32];
void f (void)
{
if (strlen (a) > 31)
abort ();
}
Martin
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH improve early strlen range folding (PR 83671)
2018-01-09 3:21 ` Martin Sebor
@ 2018-01-10 1:18 ` Jeff Law
0 siblings, 0 replies; 7+ messages in thread
From: Jeff Law @ 2018-01-10 1:18 UTC (permalink / raw)
To: Martin Sebor, Richard Biener; +Cc: Gcc Patch List
On 01/08/2018 08:05 PM, Martin Sebor wrote:
> On 01/08/2018 04:28 AM, Richard Biener wrote:
>> On Sat, Jan 6, 2018 at 11:04 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
>>> does not work at -O1, points out that the string length range
>>> optimization implemented as a solution for bug 83373 doesn't help
>>> at -O1. The root cause is that the fix was added to the strlen
>>> pass that doesn't run at -O1.
>>>
>>> The string length range computation doesn't depend on the strlen
>>> pass, and so the range can be set earlier, in gimple-fold, and
>>> its results made available even at -O1. The attached patch
>>> changes the gimple_fold_builtin_strlen() function to do that.
>>>
>>> While testing the change I came across a number of other simple
>>> strlen cases that currently aren't handled, some at -O1, others
>>> at all. I added code to handle some of the simplest of them
>>> and opened bugs to remind us/myself to get back to the rest in
>>> the future (pr83693 and pr83702). The significant enhancement
>>> is handling arrays of arrays with non-constant indices and
>>> pointers to such things, such as in:
>>>
>>> Â char a[2][7];
>>>
>>> Â void f (int i)
>>> Â {
>>> Â Â Â if (strlen (a[i]) > 6)Â Â // eliminated with the patch
>>> Â Â Â Â Â abort ();
>>> Â }
>>>
>>> Attached is a near-minimal patch to handle PR 83671.
>>
>> Please don't use set_range_info form insinde fold_stmt (), this is
>> IMHO a layering violation.
>>
>> Why not restrict -Wstrinop-overflow to -O2+?
>
> For simple cases, -Wstrinop-overflow works even without
> optimization. Given the notoriety of buffer overflows and
> because not all software is or can always be compiled with
> -O2 or even with -O1, I think it's important to keep it working.
If something can't be compiled with -O2, then it can't really be
compiled with -O1. In that kind of scenario someone is papering over a bug.
While I am sympathetic to the desire to provide as many and as accurate
of warnings at -O1 as -O2, we have to balance that against the cost,
both in terms of compile-time and in terms of maintenance if we end up
having to duplicate code. We have to evaluate each case individually.
>
> I initially did consider resolving PR 83671 as won't fix because
> at -O1 neither the strlen pass not VRP runs. But as I explained
> above, because the range doesn't depend on the strlen pass and
> can be determined and set anywhere, regardless of the optimization
> level, doing it outside the pass seems like a win-win. Exposing
> the range early improves downstream transformations before strlen
> runs or even when it doesn't, and as a bonus, the warning also
> disappears.
Outside is a lose if we have to duplicate the analysis or significant
chunks of code, or even small amounts of code if they are complex.
>
> But I don't understand the concern. The range is only set when
> the expression is an SSA_NAME, and setting in this function here
> seems no different than setting it in one if its callers (which
> may be, indirectly. in a pass like strlen). If you have
> a suggestion for a better place to set it I'd be happy to move
> it, I just don't see the problem.So identifying and setting the range is analysis. Folding the statement
is an optimization. Setting the range from inside a folding routine is
a layering violation in general. Though frankly we have not been
particularly good at separation of analysis from optimization.
But I suspect this is a truly trivial part of the patch, so let's just
drop it as controversial.
I'll try to look at the actual patch in detail tomorrow.
Jeff
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH improve early strlen range folding (PR 83671)
2018-01-06 22:04 [PATCH improve early strlen range folding (PR 83671) Martin Sebor
2018-01-08 11:37 ` Richard Biener
@ 2018-01-10 19:44 ` Jeff Law
2018-01-11 1:51 ` H.J. Lu
2 siblings, 0 replies; 7+ messages in thread
From: Jeff Law @ 2018-01-10 19:44 UTC (permalink / raw)
To: Martin Sebor, Gcc Patch List
On 01/06/2018 03:04 PM, Martin Sebor wrote:
> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
> does not work at -O1, points out that the string length range
> optimization implemented as a solution for bug 83373 doesn't help
> at -O1.  The root cause is that the fix was added to the strlen
> pass that doesn't run at -O1.
>
> The string length range computation doesn't depend on the strlen
> pass, and so the range can be set earlier, in gimple-fold, and
> its results made available even at -O1.  The attached patch
> changes the gimple_fold_builtin_strlen() function to do that.
>
> While testing the change I came across a number of other simple
> strlen cases that currently aren't handled, some at -O1, others
> at all.  I added code to handle some of the simplest of them
> and opened bugs to remind us/myself to get back to the rest in
> the future (pr83693 and pr83702).  The significant enhancement
> is handling arrays of arrays with non-constant indices and
> pointers to such things, such as in:
>
>   char a[2][7];
>
>   void f (int i)
> Â Â {
>     if (strlen (a[i]) > 6)   // eliminated with the patch
>       abort ();
> Â Â }
>
> Attached is a near-minimal patch to handle PR 83671.
>
> Martin
>
> gcc-83671.diff
>
>
> PR tree-optimization/83671 - Fix for false positive reported by -Wstringop-overflow does not work with inlining
>
> gcc/testsuite/ChangeLog:
>
> PR tree-optimization/83671
> * gcc.dg/strlenopt-40.c: New test.
> * gcc.dg/strlenopt-41.c: New test.
>
> gcc/ChangeLog:
>
> PR tree-optimization/83671
> * builtins.c (c_strlen): Unconditionally return zero for the empty
> string.
> Use -Warray-bounds for warnings.
> * gimple-fold.c (get_range_strlen): Handle non-constant lengths
> for non-constant array indices with COMPONENT_REF, arrays of
> arrays, and pointers to arrays.
> (gimple_fold_builtin_strlen): Determine and set length range for
> non-constant character arrays.
>
> @@ -1311,14 +1311,30 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
[ ... ]
> + else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
> + {
> + tree idx = TREE_OPERAND (op, 1);
> +
> + arg = TREE_OPERAND (op, 0);
> + tree optype = TREE_TYPE (arg);
> + if (tree dom = TYPE_DOMAIN (optype))
> + if (tree bound = TYPE_MAX_VALUE (dom))
> + if (TREE_CODE (bound) == INTEGER_CST
> + && TREE_CODE (idx) == INTEGER_CST
> + && tree_int_cst_lt (bound, idx))
> + return false;
This deserves a comment what are you looking for and why do you return
false when you find it. I think I know the answers, but it'd be clearer
to future readers to spell it out in a comment.
With that comment and removal of the controversial set_range_info, I
think this is OK.
Jeff
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH improve early strlen range folding (PR 83671)
2018-01-06 22:04 [PATCH improve early strlen range folding (PR 83671) Martin Sebor
2018-01-08 11:37 ` Richard Biener
2018-01-10 19:44 ` Jeff Law
@ 2018-01-11 1:51 ` H.J. Lu
2018-01-11 6:29 ` Martin Sebor
2 siblings, 1 reply; 7+ messages in thread
From: H.J. Lu @ 2018-01-11 1:51 UTC (permalink / raw)
To: Martin Sebor; +Cc: Gcc Patch List
On Sat, Jan 6, 2018 at 2:04 PM, Martin Sebor <msebor@gmail.com> wrote:
> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
> does not work at -O1, points out that the string length range
> optimization implemented as a solution for bug 83373 doesn't help
> at -O1. The root cause is that the fix was added to the strlen
> pass that doesn't run at -O1.
>
> The string length range computation doesn't depend on the strlen
> pass, and so the range can be set earlier, in gimple-fold, and
> its results made available even at -O1. The attached patch
> changes the gimple_fold_builtin_strlen() function to do that.
>
> While testing the change I came across a number of other simple
> strlen cases that currently aren't handled, some at -O1, others
> at all. I added code to handle some of the simplest of them
> and opened bugs to remind us/myself to get back to the rest in
> the future (pr83693 and pr83702). The significant enhancement
> is handling arrays of arrays with non-constant indices and
> pointers to such things, such as in:
>
> char a[2][7];
>
> void f (int i)
> {
> if (strlen (a[i]) > 6) // eliminated with the patch
> abort ();
> }
>
> Attached is a near-minimal patch to handle PR 83671.
>
This may have caused:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83781.
--
H.J.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH improve early strlen range folding (PR 83671)
2018-01-11 1:51 ` H.J. Lu
@ 2018-01-11 6:29 ` Martin Sebor
0 siblings, 0 replies; 7+ messages in thread
From: Martin Sebor @ 2018-01-11 6:29 UTC (permalink / raw)
To: H.J. Lu; +Cc: Gcc Patch List
On 01/10/2018 06:30 PM, H.J. Lu wrote:
> On Sat, Jan 6, 2018 at 2:04 PM, Martin Sebor <msebor@gmail.com> wrote:
>> Bug 83671 - Fix for false positive reported by -Wstringop-overflow
>> does not work at -O1, points out that the string length range
>> optimization implemented as a solution for bug 83373 doesn't help
>> at -O1. The root cause is that the fix was added to the strlen
>> pass that doesn't run at -O1.
>>
>> The string length range computation doesn't depend on the strlen
>> pass, and so the range can be set earlier, in gimple-fold, and
>> its results made available even at -O1. The attached patch
>> changes the gimple_fold_builtin_strlen() function to do that.
>>
>> While testing the change I came across a number of other simple
>> strlen cases that currently aren't handled, some at -O1, others
>> at all. I added code to handle some of the simplest of them
>> and opened bugs to remind us/myself to get back to the rest in
>> the future (pr83693 and pr83702). The significant enhancement
>> is handling arrays of arrays with non-constant indices and
>> pointers to such things, such as in:
>>
>> char a[2][7];
>>
>> void f (int i)
>> {
>> if (strlen (a[i]) > 6) // eliminated with the patch
>> abort ();
>> }
>>
>> Attached is a near-minimal patch to handle PR 83671.
>>
>
> This may have caused:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83781.
Yes, it did. I committed r256477 to fix the problem. With
it, plain x86_64 bootstrap as well as with --with-arch=corei7
--with-cpu=corei7 succeed.
Sorry for the breakage.
Martin
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2018-01-11 5:20 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-06 22:04 [PATCH improve early strlen range folding (PR 83671) Martin Sebor
2018-01-08 11:37 ` Richard Biener
2018-01-09 3:21 ` Martin Sebor
2018-01-10 1:18 ` Jeff Law
2018-01-10 19:44 ` Jeff Law
2018-01-11 1:51 ` H.J. Lu
2018-01-11 6:29 ` Martin Sebor
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).