[v2] c++: side effect in nullptr_t conversion fix
Checks
Commit Message
Hello,
You are absolutely right, we can't throw all side-effects away.
Example:
```c++
auto __attribute__ ((noinline)) bar()
{
volatile int* b = (int *)0xff;
*b = 10;
volatile auto n = nullptr;
return n; // Problem (1)
}
int* foo_2()
{
volatile auto a = bar(); // Problem (2)
return a;
}
```
There are 2 additional places, where std::nullptr_t causes side effects.
(1)
nullptr_t identical conversion produces:
mov QWORD PTR [rsp-8], 0
mov rax, QWORD PTR [rsp-8] // load n
ret
(2)
Initialization by not constant expression:
call bar()
mov QWORD PTR [rsp+8], rax
mov rax, QWORD PTR [rsp+8]
xor eax, eax
---
Comments
On 1/11/24 15:34, Dmitry Drozodv wrote:
> You are absolutely right, we can't throw all side-effects away.
>
> + /* C++ [conv.lval]p3:
> + If T is cv std::nullptr_t, the result is a null pointer constant. */
> + return ((TREE_SIDE_EFFECTS (expr) && !CONVERT_EXPR_P (expr))
> + ? build2 (COMPOUND_EXPR, type, expr, val)
> + : val);
This seems to assume that a CONVERT_EXPR can't have any other
side-effects nested in it.
It seems to me a better approach is the one in keep_unused_object_arg
and cp_gimplify_expr, basically
if (TREE_THIS_VOLATILE (e))
e = cp_build_addr_expr (e);
since the address of a volatile lvalue is safe to put on the lhs of a
COMPOUND_EXPR.
> - if (!TREE_SIDE_EFFECTS (e))
> + /* C++ [conv.lval]p3:
> + Convert expr from nullptr to nullptr doesn't have side effect. */
> + if (!TREE_SIDE_EFFECTS (e) || CONVERT_EXPR_P (expr))
I don't see why ocp_convert needs to change at all; if TREE_SIDE_EFFECTS
is set we'll eventually get to cp_convert_to_pointer and can do the
right thing there.
> @@ -770,6 +770,15 @@ split_nonconstant_init (tree dest, tree init)
> && array_of_runtime_bound_p (TREE_TYPE (dest)))
> code = build_vec_init (dest, NULL_TREE, init, /*value-init*/false,
> /*from array*/1, tf_warning_or_error);
> + else if (TREE_CODE (TREE_TYPE(init)) == NULLPTR_TYPE)
> + {
> + /* C++ [conv.lval]p3:
> + If T is cv std::nullptr_t, the result is a null pointer
> constant. */
> + tree val = build_int_cst(TREE_TYPE(dest), 0);
> + tree ie = cp_build_init_expr(dest, val);
Why not just
DECL_INITIAL (dest) = nullptr_node;
?
Also, your mail client is mangling your patch so it doesn't apply
properly. Please see https://gcc.gnu.org/contribute.html for more
information.
Jason
[PATCH v3] c++: side effect in nullptr_t conversion fix
Hi,
> This seems to assume that a CONVERT_EXPR can't have any other
> side-effects nested in it.
>
> It seems to me a better approach is the one in keep_unused_object_arg
> and cp_gimplify_expr, basically
>
> if (TREE_THIS_VOLATILE (e))
> e = cp_build_addr_expr (e);
>
> since the address of a volatile lvalue is safe to put on the lhs of a
> COMPOUND_EXPR.
Thank you for your review! I have implemented what you suggested with a
little modification:
I cut the conversion nodes using STRIP_NOPS (expr), since the value is not
actually converted anywhere and should be ignored.
> I don't see why ocp_convert needs to change at all; if TREE_SIDE_EFFECTS
> is set we'll eventually get to cp_convert_to_pointer and can do the
> right thing there.
Without changes in ocp_convert, it does not happen to call the
cp_convert_to_pointer function.
Due to the equality of the 'to' and 'from' types, identity conversion
occurs with preserving the side effect.
> Why not just
>
> DECL_INITIAL (dest) = nullptr_node;
>
> ?
Because it doesn't work.
This does not completely replace initialization; reading from volatile
nullptr remains.
I also added a test suite 'nullptr47.C' to check the original tree.
---
gcc/cp/cvt.cc | 20 ++++++++++++++---
gcc/cp/typeck2.cc | 8 +++++++
gcc/testsuite/g++.dg/cpp0x/nullptr47.C | 30 ++++++++++++++++++++++++++
3 files changed, 55 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/nullptr47.C
diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index cbed847b343..578c9b8ef20 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -218,8 +218,20 @@ cp_convert_to_pointer (tree type, tree expr, bool
dofold,
? build_int_cst_type (type, -1)
: build_int_cst (type, 0));
- return (TREE_SIDE_EFFECTS (expr)
- ? build2 (COMPOUND_EXPR, type, expr, val) : val);
+ tree e = STRIP_NOPS (expr);
+ if (TREE_THIS_VOLATILE (e))
+ {
+ /* can't drop expr with side effect (e.g. function call). */
+ e = cp_build_addr_expr(e, tf_warning_or_error);
+ return build2 (COMPOUND_EXPR, type, e, val);
+ }
+
+ /* C++ [conv.lval]p3:
+ If T is cv std::nullptr_t, the result is a null pointer constant. */
+ /* expr value must be ignored. */
+ return ((TREE_SIDE_EFFECTS(e))
+ ? build2(COMPOUND_EXPR, type, e, val)
+ : val);
}
else if (TYPE_PTRMEM_P (type) && INTEGRAL_CODE_P (form))
{
@@ -743,9 +755,11 @@ ocp_convert (tree type, tree expr, int convtype, int
flags,
{
if (complain & tf_warning)
maybe_warn_zero_as_null_pointer_constant (e, loc);
-
if (!TREE_SIDE_EFFECTS (e))
return nullptr_node;
+ else
+ /* process nullptr to nullptr conversion with side effect. */
+ return cp_convert_to_pointer(type, e, dofold, complain);
}
if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP))
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index ac0fefa24f2..cdabb32b289 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -770,6 +770,14 @@ split_nonconstant_init (tree dest, tree init)
&& array_of_runtime_bound_p (TREE_TYPE (dest)))
code = build_vec_init (dest, NULL_TREE, init, /*value-init*/false,
/*from array*/1, tf_warning_or_error);
+ else if (TREE_CODE (TREE_TYPE(init)) == NULLPTR_TYPE)
+ {
+ /* C++ [conv.lval]p3:
+ If T is cv std::nullptr_t, the result is a null pointer constant. */
+ tree ie = cp_build_init_expr(dest, nullptr_node);
+ // remain expr with side effect
+ code = add_stmt_to_compound(init, ie);
+ }
else
code = cp_build_init_expr (dest, init);
diff --git a/gcc/testsuite/g++.dg/cpp0x/nullptr47.C
b/gcc/testsuite/g++.dg/cpp0x/nullptr47.C
new file mode 100644
index 00000000000..f05e91465f7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nullptr47.C
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -fdump-tree-original" }
+
+int* foo()
+{
+ volatile auto a_0 = nullptr;
+ int* b_0 = a_0;
+ return b_0;
+}
+/* nullptr to int pointer conversion: no load from a_0. Init b_0 by zero.
*/
+// { dg-final { scan-tree-dump-times "cleanup_point int \\* b_0 = 0" 1
"original"} }
+
+auto __attribute__ ((noinline)) bar()
+{
+ /* side effect. Elimination of 'call bar()' will be incorrect. */
+ volatile int* b = (int *)0xff;
+ *b = 10;
+
+ volatile auto n = nullptr;
+ return n;
+}
+/* nullptr to nullptr conversion (identity): no load from n. Just return
zero. */
+// { dg-final { scan-tree-dump-times "return <retval> =\[^\\n\\r\]* 0;" 1
"original"} }
+
+void foo_2()
+{
+ volatile auto a_2 = bar();
+}
+/* non-constant init: no store bar() result to a_2. Call bar() and init
a_2 by zero. */
+// { dg-final { scan-tree-dump-times "bar \\(\\);, a_2 = 0;" 1 "original"}
}
@@ -218,8 +218,11 @@ cp_convert_to_pointer (tree type, tree expr, bool
dofold,
? build_int_cst_type (type, -1)
: build_int_cst (type, 0));
- return (TREE_SIDE_EFFECTS (expr)
- ? build2 (COMPOUND_EXPR, type, expr, val) : val);
+ /* C++ [conv.lval]p3:
+ If T is cv std::nullptr_t, the result is a null pointer constant.
*/
+ return ((TREE_SIDE_EFFECTS (expr) && !CONVERT_EXPR_P (expr))
+ ? build2 (COMPOUND_EXPR, type, expr, val)
+ : val);
}
else if (TYPE_PTRMEM_P (type) && INTEGRAL_CODE_P (form))
{
@@ -743,8 +746,9 @@ ocp_convert (tree type, tree expr, int convtype, int
flags,
{
if (complain & tf_warning)
maybe_warn_zero_as_null_pointer_constant (e, loc);
-
- if (!TREE_SIDE_EFFECTS (e))
+ /* C++ [conv.lval]p3:
+ Convert expr from nullptr to nullptr doesn't have side effect. */
+ if (!TREE_SIDE_EFFECTS (e) || CONVERT_EXPR_P (expr))
return nullptr_node;
}
@@ -770,6 +770,15 @@ split_nonconstant_init (tree dest, tree init)
&& array_of_runtime_bound_p (TREE_TYPE (dest)))
code = build_vec_init (dest, NULL_TREE, init, /*value-init*/false,
/*from array*/1, tf_warning_or_error);
+ else if (TREE_CODE (TREE_TYPE(init)) == NULLPTR_TYPE)
+ {
+ /* C++ [conv.lval]p3:
+ If T is cv std::nullptr_t, the result is a null pointer constant.
*/
+ tree val = build_int_cst(TREE_TYPE(dest), 0);
+ tree ie = cp_build_init_expr(dest, val);
+ // remain expr with side effect
+ code = add_stmt_to_compound(init, ie);
+ }
else
code = cp_build_init_expr (dest, init);