From 6343b6bf3bb83c87cfc80eb32400b45bebd571e0 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Thu, 25 Jul 2019 11:36:38 +0200 Subject: [PATCH] Extend DCE to remove unnecessary new/delete-pairs (PR c++/23383). 2019-07-25 Martin Liska Dominik Infuhr PR c++/23383 * common.opt: Add -fallocation-dce * gimple.c (gimple_call_operator_delete_p): New. * gimple.h (gimple_call_operator_delete_p): Likewise. * tree-core.h (enum function_decl_type): Add OPERATOR_DELETE. * tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Handle DECL_IS_OPERATOR_DELETE_P. (mark_all_reaching_defs_necessary_1): Likewise. (propagate_necessity): Likewise. (eliminate_unnecessary_stmts): Handle gimple_call_operator_delete_p. * tree-streamer-in.c (unpack_ts_function_decl_value_fields): Add packing of OPERATOR_DELETE. * tree-streamer-out.c (pack_ts_function_decl_value_fields): Similarly here. * tree.h (DECL_IS_OPERATOR_DELETE_P): New. (DECL_SET_IS_OPERATOR_DELETE): New. (DECL_IS_REPLACEABLE_OPERATOR_NEW_P): Likewise. 2019-07-25 Martin Liska Dominik Infuhr PR c++/23383 * c-decl.c (merge_decls): Merge OPERATOR_DELETE flag. 2019-07-25 Martin Liska Dominik Infuhr PR c++/23383 * decl.c (cxx_init_decl_processing): Mark delete operators with DECL_SET_IS_OPERATOR_DELETE. 2019-07-25 Martin Liska PR c++/23383 * g++.dg/cpp1y/new1.C: New test. 2019-07-25 Martin Liska Dominik Infuhr PR c++/23383 * testsuite/ext/bitmap_allocator/check_delete.cc: Add -fno-allocation-dce. * testsuite/ext/bitmap_allocator/check_new.cc: Likewise. * testsuite/ext/new_allocator/check_delete.cc: Likewise. * testsuite/ext/new_allocator/check_new.cc: Likewise. Co-Authored-By: Dominik Infuehr From-SVN: r273791 --- gcc/ChangeLog | 22 +++++++ gcc/c/ChangeLog | 6 ++ gcc/c/c-decl.c | 2 + gcc/common.opt | 4 ++ gcc/cp/ChangeLog | 7 ++ gcc/cp/decl.c | 24 ++++--- gcc/gimple.c | 12 ++++ gcc/gimple.h | 1 + gcc/testsuite/ChangeLog | 6 ++ gcc/testsuite/g++.dg/cpp1y/new1.C | 65 +++++++++++++++++++ gcc/tree-core.h | 1 + gcc/tree-ssa-dce.c | 34 ++++++++-- gcc/tree-streamer-in.c | 1 + gcc/tree-streamer-out.c | 1 + gcc/tree.h | 11 ++++ libstdc++-v3/ChangeLog | 10 +++ .../ext/bitmap_allocator/check_delete.cc | 2 + .../ext/bitmap_allocator/check_new.cc | 2 + .../ext/new_allocator/check_delete.cc | 2 + .../testsuite/ext/new_allocator/check_new.cc | 2 + 20 files changed, 201 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/new1.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 89ba868f6b7..2fae3238878 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,25 @@ +2019-07-25 Martin Liska + Dominik Infuhr + + PR c++/23383 + * common.opt: Add -fallocation-dce + * gimple.c (gimple_call_operator_delete_p): New. + * gimple.h (gimple_call_operator_delete_p): Likewise. + * tree-core.h (enum function_decl_type): Add OPERATOR_DELETE. + * tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Handle + DECL_IS_OPERATOR_DELETE_P. + (mark_all_reaching_defs_necessary_1): Likewise. + (propagate_necessity): Likewise. + (eliminate_unnecessary_stmts): Handle + gimple_call_operator_delete_p. + * tree-streamer-in.c (unpack_ts_function_decl_value_fields): + Add packing of OPERATOR_DELETE. + * tree-streamer-out.c (pack_ts_function_decl_value_fields): + Similarly here. + * tree.h (DECL_IS_OPERATOR_DELETE_P): New. + (DECL_SET_IS_OPERATOR_DELETE): New. + (DECL_IS_REPLACEABLE_OPERATOR_NEW_P): Likewise. + 2019-07-25 Martin Liska * calls.c (maybe_warn_alloc_args_overflow): Use new macros diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 77ffbfb5930..d07051b15a3 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,9 @@ +2019-07-25 Martin Liska + Dominik Infuhr + + PR c++/23383 + * c-decl.c (merge_decls): Merge OPERATOR_DELETE flag. + 2019-07-25 Martin Liska * c-decl.c (merge_decls): Use new macros diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 92cc06ed652..f85f481eb51 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -2641,6 +2641,8 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl); if (DECL_IS_OPERATOR_NEW_P (olddecl)) DECL_SET_IS_OPERATOR_NEW (newdecl, true); + if (DECL_IS_OPERATOR_DELETE_P (olddecl)) + DECL_SET_IS_OPERATOR_DELETE (newdecl, true); TREE_READONLY (newdecl) |= TREE_READONLY (olddecl); DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl); DECL_IS_NOVOPS (newdecl) |= DECL_IS_NOVOPS (olddecl); diff --git a/gcc/common.opt b/gcc/common.opt index b998b25522b..11637c8cab0 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2211,6 +2211,10 @@ Enum(live_patching_level) String(inline-only-static) Value(LIVE_PATCHING_INLINE_ EnumValue Enum(live_patching_level) String(inline-clone) Value(LIVE_PATCHING_INLINE_CLONE) +fallocation-dce +Common Report Var(flag_allocation_dce) Init(1) Optimization +Tell DCE to remove unused C++ allocations. + flive-range-shrinkage Common Report Var(flag_live_range_shrinkage) Init(0) Optimization Relief of register pressure through live range shrinkage. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 6c081214abe..eeed3dcef55 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2019-07-25 Martin Liska + Dominik Infuhr + + PR c++/23383 + * decl.c (cxx_init_decl_processing): Mark delete operators + with DECL_SET_IS_OPERATOR_DELETE. + 2019-07-25 Martin Liska * decl.c (duplicate_decls): Use new macros diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 836bb14894e..9fa090c8767 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4363,8 +4363,10 @@ cxx_init_decl_processing (void) opnew = push_cp_library_fn (VEC_NEW_EXPR, newtype, 0); DECL_IS_MALLOC (opnew) = 1; DECL_SET_IS_OPERATOR_NEW (opnew, true); - push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); - push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + tree opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); + opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); if (flag_sized_deallocation) { /* Also push the sized deallocation variants: @@ -4376,8 +4378,10 @@ cxx_init_decl_processing (void) deltype = cp_build_type_attribute_variant (void_ftype_ptr_size, extvisattr); deltype = build_exception_variant (deltype, empty_except_spec); - push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); - push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); + opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); } if (aligned_new_threshold) @@ -4405,8 +4409,10 @@ cxx_init_decl_processing (void) align_type_node, NULL_TREE); deltype = cp_build_type_attribute_variant (deltype, extvisattr); deltype = build_exception_variant (deltype, empty_except_spec); - push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); - push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); + opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); if (flag_sized_deallocation) { @@ -4416,8 +4422,10 @@ cxx_init_decl_processing (void) NULL_TREE); deltype = cp_build_type_attribute_variant (deltype, extvisattr); deltype = build_exception_variant (deltype, empty_except_spec); - push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); - push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); + opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); + DECL_SET_IS_OPERATOR_DELETE (opdel, true); } } diff --git a/gcc/gimple.c b/gcc/gimple.c index 513bde209e2..a0eac8703bd 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -2695,6 +2695,18 @@ gimple_builtin_call_types_compatible_p (const gimple *stmt, tree fndecl) return true; } +/* Return true when STMT is operator delete call. */ + +bool +gimple_call_operator_delete_p (const gcall *stmt) +{ + tree fndecl; + + if ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE) + return DECL_IS_OPERATOR_DELETE_P (fndecl); + return false; +} + /* Return true when STMT is builtins call. */ bool diff --git a/gcc/gimple.h b/gcc/gimple.h index 442a121819e..c82fdf71316 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -1547,6 +1547,7 @@ extern alias_set_type gimple_get_alias_set (tree); extern bool gimple_ior_addresses_taken (bitmap, gimple *); extern bool gimple_builtin_call_types_compatible_p (const gimple *, tree); extern combined_fn gimple_call_combined_fn (const gimple *); +extern bool gimple_call_operator_delete_p (const gcall *); extern bool gimple_call_builtin_p (const gimple *); extern bool gimple_call_builtin_p (const gimple *, enum built_in_class); extern bool gimple_call_builtin_p (const gimple *, enum built_in_function); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0712be97dfa..4dad677d41d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2019-07-25 Martin Liska + + PR c++/23383 + * g++.dg/cpp1y/new1.C: New test. + 2019-07-25 Eric Botcazou PR testsuite/91245 diff --git a/gcc/testsuite/g++.dg/cpp1y/new1.C b/gcc/testsuite/g++.dg/cpp1y/new1.C new file mode 100644 index 00000000000..a95dd4d1ee3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/new1.C @@ -0,0 +1,65 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-cddce-details" } */ + +#include + +void +new_without_use() { + int *x = new int; +} + +void +new_array_without_use() { + int *x = new int[5]; +} + +void +new_primitive() { + int *x = new int; + delete x; +} + +void +new_array() { + int *x = new int[10]; + delete [] x; +} + +void +new_primitive_store() { + int *x = new int; + *x = 10; + delete x; +} + +void +new_primitive_load() { + int *x = new int; + int tmp = *x; + delete x; +} + +int +new_primitive_load_with_use() { + int *x = new int; + int tmp = *x; + delete x; + return tmp; +} + +void +new_array_store() { + int *x = new int[10]; + x[4] = 10; + delete [] x; +} + +void +new_array_load() { + int *x = new int[10]; + int tmp = x[4]; + delete [] x; +} + +/* { dg-final { scan-tree-dump-times "Deleting : operator delete" 5 "cddce1"} } */ +/* { dg-final { scan-tree-dump-times "Deleting : _\\d+ = operator new" 7 "cddce1"} } */ diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 372738e5499..60d8c684171 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1826,6 +1826,7 @@ enum function_decl_type { NONE, OPERATOR_NEW, + OPERATOR_DELETE, LAMBDA_FUNCTION /* 0 values left */ diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index 6398c1e4457..90b3f4d7c45 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -237,6 +237,12 @@ mark_stmt_if_obviously_necessary (gimple *stmt, bool aggressive) default:; } + + if (callee != NULL_TREE + && flag_allocation_dce + && DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee)) + return; + /* Most, but not all function calls are required. Function calls that produce no result and have no side effects (i.e. const pure functions) are unnecessary. */ @@ -588,6 +594,11 @@ mark_all_reaching_defs_necessary_1 (ao_ref *ref ATTRIBUTE_UNUSED, default:; } + + if (callee != NULL_TREE + && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee) + || DECL_IS_OPERATOR_DELETE_P (callee))) + return false; } if (! gimple_clobber_p (def_stmt)) @@ -774,7 +785,10 @@ propagate_necessity (bool aggressive) /* If this is a call to free which is directly fed by an allocation function do not mark that necessary through processing the argument. */ - if (gimple_call_builtin_p (stmt, BUILT_IN_FREE)) + if (gimple_call_builtin_p (stmt, BUILT_IN_FREE) + || (is_gimple_call (stmt) + && gimple_call_operator_delete_p (as_a (stmt)))) + { tree ptr = gimple_call_arg (stmt, 0); gimple *def_stmt; @@ -784,10 +798,11 @@ propagate_necessity (bool aggressive) if (TREE_CODE (ptr) == SSA_NAME && is_gimple_call (def_stmt = SSA_NAME_DEF_STMT (ptr)) && (def_callee = gimple_call_fndecl (def_stmt)) - && DECL_BUILT_IN_CLASS (def_callee) == BUILT_IN_NORMAL - && (DECL_FUNCTION_CODE (def_callee) == BUILT_IN_ALIGNED_ALLOC - || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_MALLOC - || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_CALLOC)) + && ((DECL_BUILT_IN_CLASS (def_callee) == BUILT_IN_NORMAL + && (DECL_FUNCTION_CODE (def_callee) == BUILT_IN_ALIGNED_ALLOC + || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_MALLOC + || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_CALLOC)) + || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (def_callee))) continue; } @@ -842,6 +857,11 @@ propagate_necessity (bool aggressive) || DECL_FUNCTION_CODE (callee) == BUILT_IN_ASSUME_ALIGNED)) continue; + if (callee != NULL_TREE + && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee) + || DECL_IS_OPERATOR_DELETE_P (callee))) + continue; + /* Calls implicitly load from memory, their arguments in addition may explicitly perform memory loads. */ mark_all_reaching_defs_necessary (stmt); @@ -1262,7 +1282,9 @@ eliminate_unnecessary_stmts (void) defining statement of its argument is not necessary (and thus is getting removed). */ if (gimple_plf (stmt, STMT_NECESSARY) - && gimple_call_builtin_p (stmt, BUILT_IN_FREE)) + && (gimple_call_builtin_p (stmt, BUILT_IN_FREE) + || (is_gimple_call (stmt) + && gimple_call_operator_delete_p (as_a (stmt))))) { tree ptr = gimple_call_arg (stmt, 0); if (TREE_CODE (ptr) == SSA_NAME) diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c index f2880f1021f..7ab72a7e0d7 100644 --- a/gcc/tree-streamer-in.c +++ b/gcc/tree-streamer-in.c @@ -334,6 +334,7 @@ unpack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr) DECL_IS_RETURNS_TWICE (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_IS_MALLOC (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_SET_IS_OPERATOR_NEW (expr, (unsigned) bp_unpack_value (bp, 1)); + DECL_SET_IS_OPERATOR_DELETE (expr, (unsigned) bp_unpack_value (bp, 1)); DECL_DECLARED_INLINE_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_STATIC_CHAIN (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_NO_INLINE_WARNING_P (expr) = (unsigned) bp_unpack_value (bp, 1); diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c index 7e93e6c23bb..dbdc5d5e736 100644 --- a/gcc/tree-streamer-out.c +++ b/gcc/tree-streamer-out.c @@ -296,6 +296,7 @@ pack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr) bp_pack_value (bp, DECL_IS_RETURNS_TWICE (expr), 1); bp_pack_value (bp, DECL_IS_MALLOC (expr), 1); bp_pack_value (bp, DECL_IS_OPERATOR_NEW_P (expr), 1); + bp_pack_value (bp, DECL_IS_OPERATOR_DELETE_P (expr), 1); bp_pack_value (bp, DECL_DECLARED_INLINE_P (expr), 1); bp_pack_value (bp, DECL_STATIC_CHAIN (expr), 1); bp_pack_value (bp, DECL_NO_INLINE_WARNING_P (expr), 1); diff --git a/gcc/tree.h b/gcc/tree.h index 81c11a470f9..4aa2c4a8739 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3020,9 +3020,20 @@ set_function_decl_type (tree decl, function_decl_type t, bool set) #define DECL_IS_OPERATOR_NEW_P(NODE) \ (FUNCTION_DECL_CHECK (NODE)->function_decl.decl_type == OPERATOR_NEW) +#define DECL_IS_REPLACEABLE_OPERATOR_NEW_P(NODE) \ + (DECL_IS_OPERATOR_NEW_P (NODE) && DECL_IS_MALLOC (NODE)) + #define DECL_SET_IS_OPERATOR_NEW(NODE, VAL) \ set_function_decl_type (FUNCTION_DECL_CHECK (NODE), OPERATOR_NEW, VAL) +/* Nonzero in a FUNCTION_DECL means this function should be treated as + C++ operator delete. */ +#define DECL_IS_OPERATOR_DELETE_P(NODE) \ + (FUNCTION_DECL_CHECK (NODE)->function_decl.decl_type == OPERATOR_DELETE) + +#define DECL_SET_IS_OPERATOR_DELETE(NODE, VAL) \ + set_function_decl_type (FUNCTION_DECL_CHECK (NODE), OPERATOR_DELETE, VAL) + /* Nonzero in a FUNCTION_DECL means this function may return more than once. */ #define DECL_IS_RETURNS_TWICE(NODE) \ diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 099314d7e85..a92e7a7783b 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,13 @@ +2019-07-25 Martin Liska + Dominik Infuhr + + PR c++/23383 + * testsuite/ext/bitmap_allocator/check_delete.cc: Add + -fno-allocation-dce. + * testsuite/ext/bitmap_allocator/check_new.cc: Likewise. + * testsuite/ext/new_allocator/check_delete.cc: Likewise. + * testsuite/ext/new_allocator/check_new.cc: Likewise. + 2019-07-22 Jonathan Wakely * testsuite/26_numerics/bit/bitops.count/*: Rename to ... diff --git a/libstdc++-v3/testsuite/ext/bitmap_allocator/check_delete.cc b/libstdc++-v3/testsuite/ext/bitmap_allocator/check_delete.cc index 1b445643599..1ad1f3c242c 100644 --- a/libstdc++-v3/testsuite/ext/bitmap_allocator/check_delete.cc +++ b/libstdc++-v3/testsuite/ext/bitmap_allocator/check_delete.cc @@ -15,6 +15,8 @@ // with this library; see the file COPYING3. If not see // . +// { dg-options "-fno-allocation-dce" } + // 20.4.1.1 allocator members #include diff --git a/libstdc++-v3/testsuite/ext/bitmap_allocator/check_new.cc b/libstdc++-v3/testsuite/ext/bitmap_allocator/check_new.cc index 0c5f9b6da7c..be16952c10d 100644 --- a/libstdc++-v3/testsuite/ext/bitmap_allocator/check_new.cc +++ b/libstdc++-v3/testsuite/ext/bitmap_allocator/check_new.cc @@ -15,6 +15,8 @@ // with this library; see the file COPYING3. If not see // . +// { dg-options "-fno-allocation-dce" } + // 20.4.1.1 allocator members #include diff --git a/libstdc++-v3/testsuite/ext/new_allocator/check_delete.cc b/libstdc++-v3/testsuite/ext/new_allocator/check_delete.cc index 8778bc9ccaa..dccee1d5293 100644 --- a/libstdc++-v3/testsuite/ext/new_allocator/check_delete.cc +++ b/libstdc++-v3/testsuite/ext/new_allocator/check_delete.cc @@ -17,6 +17,8 @@ // with this library; see the file COPYING3. If not see // . +// { dg-options "-fno-allocation-dce" } + // 20.4.1.1 allocator members #include diff --git a/libstdc++-v3/testsuite/ext/new_allocator/check_new.cc b/libstdc++-v3/testsuite/ext/new_allocator/check_new.cc index fd90d284224..a1d164a6d49 100644 --- a/libstdc++-v3/testsuite/ext/new_allocator/check_new.cc +++ b/libstdc++-v3/testsuite/ext/new_allocator/check_new.cc @@ -17,6 +17,8 @@ // with this library; see the file COPYING3. If not see // . +// { dg-options "-fno-allocation-dce" } + // 20.4.1.1 allocator members #include -- 2.30.2