Commit 457eeaae by Jakub Jelinek Committed by Jakub Jelinek

re PR debug/43051 (VTA causes a stack living parameter unavailable in most of the function)

	PR debug/43051
	PR debug/43092
	* cselib.c (cselib_preserve_constants,
	cfa_base_preserved_val): New static variables.
	(preserve_only_constants): New function.
	(cselib_reset_table): If cfa_base_preserved_val is non-NULL, don't
	clear its REG_VALUES.  If cselib_preserve_constants, don't 
	empty the whole hash table, but preserve there VALUEs with constants,
	cfa_base_preserved_val and cfa_base_preserved_val plus constant.
	(cselib_preserve_cfa_base_value): New function.
	(cselib_invalidate_regno): Don't invalidate cfa_base_preserved_val.
	(cselib_init): Change argument to int bitfield.  Set
	cselib_preserve_constants to whether CSELIB_PRESERVE_CONSTANTS
	is in it.
	(cselib_finish): Clear cselib_preserve_constants and
	cfa_base_preserved_val.
	* cselib.h (enum cselib_record_what): New enum.
	(cselib_init): Change argument to int.
	(cselib_preserve_cfa_base_value): New prototype.
	* postreload.c (reload_cse_regs_1): Adjust cselib_init caller.
	* dse.c (dse_step1): Likewise.
	* cfgcleanup.c (thread_jump): Likewise.
	* sched-deps.c (sched_analyze): Likewise.
	* gcse.c (local_cprop_pass): Likewise.
	* simplify-rtx.c (simplify_replace_fn_rtx): Add argument to callback.
	If FN is non-NULL, call the callback always and whenever it returns
	non-NULL just return that.  Only do rtx_equal_p if FN is NULL.
	* rtl.h (simplify_replace_fn_rtx): Add argument to callback.
	* combine.c (propagate_for_debug_subst): Add old_rtx argument,
	compare from with old_rtx and if it isn't rtx_equal_p, return NULL.
	* Makefile.in (var-tracking.o): Depend on $(RECOG_H).
	* var-tracking.c: Include recog.h.
	(bb_stack_adjust_offset): Remove.
	(vt_stack_adjustments): Don't call it, instead just gather the
	adjustments using insn_stack_adjust_offset_pre_post on each bb insn.
	(adjust_stack_reference): Remove.
	(compute_cfa_pointer): New function.
	(hard_frame_pointer_adjustment, cfa_base_rtx): New static variables.
	(struct adjust_mem_data): New type.
	(adjust_mems, adjust_mem_uses, adjust_mem_stores, adjust_insn): New
	functions.
	(get_address_mode): New function.
	(replace_expr_with_values): Use it.
	(use_type): Don't do cselib_lookup for VAR_LOC_UNKNOWN_P.
	Use get_address_mode.  For cfa_base_rtx return MO_CLOBBER.
	(adjust_sets): Remove.
	(add_uses): Don't add extra MO_VAL_USE for cfa_base_rtx plus constant.
	Use get_address_mode.
	(get_adjusted_src): Remove.
	(add_stores): Don't call it.  Never reuse expr SET.  Don't add extra
	MO_VAL_USE for cfa_base_rtx plus constant.  Use get_address_mode.
	(add_with_sets): Don't call adjust_sets.
	(fp_setter, vt_init_cfa_base): New functions.
	(vt_initialize): Change return type to bool.  Move most of pool etc.
	initialization to the beginning of the function from end.  Pass
	CSELIB_RECORD_MEMORY | CSELIB_PRESERVE_CONSTANTS to cselib_init.
	If !frame_pointer_needed, call vt_stack_adjustment before mos
	vector is filled, call vt_init_cfa_base if argp/framep has been
	eliminated to sp.  If frame_pointer_needed and argp/framep has
	been eliminated to hard frame pointer, set
	hard_frame_pointer_adjustment and call vt_init_cfa_base after
	encountering fp setter in the prologue.  For MO_ADJUST, call
	log_op_type before pusing the op into mos vector, not afterwards.
	Call adjust_insn before cselib_process_insn/add_with_sets,
	call cancel_changes (0) afterwards.
	(variable_tracking_main_1): Adjust for vt_initialize calling
	vt_stack_adjustments and returning whether it succeeded or not.

	* gcc.dg/guality/pr43051-1.c: New test.

From-SVN: r157476
parent 31b2eb0f
2010-03-16 Jakub Jelinek <jakub@redhat.com>
PR debug/43051
PR debug/43092
* cselib.c (cselib_preserve_constants,
cfa_base_preserved_val): New static variables.
(preserve_only_constants): New function.
(cselib_reset_table): If cfa_base_preserved_val is non-NULL, don't
clear its REG_VALUES. If cselib_preserve_constants, don't
empty the whole hash table, but preserve there VALUEs with constants,
cfa_base_preserved_val and cfa_base_preserved_val plus constant.
(cselib_preserve_cfa_base_value): New function.
(cselib_invalidate_regno): Don't invalidate cfa_base_preserved_val.
(cselib_init): Change argument to int bitfield. Set
cselib_preserve_constants to whether CSELIB_PRESERVE_CONSTANTS
is in it.
(cselib_finish): Clear cselib_preserve_constants and
cfa_base_preserved_val.
* cselib.h (enum cselib_record_what): New enum.
(cselib_init): Change argument to int.
(cselib_preserve_cfa_base_value): New prototype.
* postreload.c (reload_cse_regs_1): Adjust cselib_init caller.
* dse.c (dse_step1): Likewise.
* cfgcleanup.c (thread_jump): Likewise.
* sched-deps.c (sched_analyze): Likewise.
* gcse.c (local_cprop_pass): Likewise.
* simplify-rtx.c (simplify_replace_fn_rtx): Add argument to callback.
If FN is non-NULL, call the callback always and whenever it returns
non-NULL just return that. Only do rtx_equal_p if FN is NULL.
* rtl.h (simplify_replace_fn_rtx): Add argument to callback.
* combine.c (propagate_for_debug_subst): Add old_rtx argument,
compare from with old_rtx and if it isn't rtx_equal_p, return NULL.
* Makefile.in (var-tracking.o): Depend on $(RECOG_H).
* var-tracking.c: Include recog.h.
(bb_stack_adjust_offset): Remove.
(vt_stack_adjustments): Don't call it, instead just gather the
adjustments using insn_stack_adjust_offset_pre_post on each bb insn.
(adjust_stack_reference): Remove.
(compute_cfa_pointer): New function.
(hard_frame_pointer_adjustment, cfa_base_rtx): New static variables.
(struct adjust_mem_data): New type.
(adjust_mems, adjust_mem_uses, adjust_mem_stores, adjust_insn): New
functions.
(get_address_mode): New function.
(replace_expr_with_values): Use it.
(use_type): Don't do cselib_lookup for VAR_LOC_UNKNOWN_P.
Use get_address_mode. For cfa_base_rtx return MO_CLOBBER.
(adjust_sets): Remove.
(add_uses): Don't add extra MO_VAL_USE for cfa_base_rtx plus constant.
Use get_address_mode.
(get_adjusted_src): Remove.
(add_stores): Don't call it. Never reuse expr SET. Don't add extra
MO_VAL_USE for cfa_base_rtx plus constant. Use get_address_mode.
(add_with_sets): Don't call adjust_sets.
(fp_setter, vt_init_cfa_base): New functions.
(vt_initialize): Change return type to bool. Move most of pool etc.
initialization to the beginning of the function from end. Pass
CSELIB_RECORD_MEMORY | CSELIB_PRESERVE_CONSTANTS to cselib_init.
If !frame_pointer_needed, call vt_stack_adjustment before mos
vector is filled, call vt_init_cfa_base if argp/framep has been
eliminated to sp. If frame_pointer_needed and argp/framep has
been eliminated to hard frame pointer, set
hard_frame_pointer_adjustment and call vt_init_cfa_base after
encountering fp setter in the prologue. For MO_ADJUST, call
log_op_type before pusing the op into mos vector, not afterwards.
Call adjust_insn before cselib_process_insn/add_with_sets,
call cancel_changes (0) afterwards.
(variable_tracking_main_1): Adjust for vt_initialize calling
vt_stack_adjustments and returning whether it succeeded or not.
2010-03-15 Aldy Hernandez <aldyh@redhat.com>
* graphite-sese-to-poly.c (rewrite_cross_bb_scalar_deps): Skip
......
......@@ -3032,7 +3032,8 @@ var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(FLAGS_H) \
$(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \
$(REGS_H) $(EXPR_H) $(TIMEVAR_H) $(TREE_PASS_H) $(TREE_FLOW_H) \
cselib.h $(TARGET_H) $(TOPLEV_H) $(PARAMS_H) $(DIAGNOSTIC_H) pointer-set.h
cselib.h $(TARGET_H) $(TOPLEV_H) $(PARAMS_H) $(DIAGNOSTIC_H) pointer-set.h \
$(RECOG_H)
profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) $(FUNCTION_H) \
$(TOPLEV_H) $(COVERAGE_H) $(TREE_FLOW_H) value-prof.h cfghooks.h \
......
/* Control flow optimization code for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010
Free Software Foundation, Inc.
This file is part of GCC.
......@@ -337,7 +337,7 @@ thread_jump (edge e, basic_block b)
return NULL;
}
cselib_init (false);
cselib_init (0);
/* First process all values computed in the source basic block. */
for (insn = NEXT_INSN (BB_HEAD (e->src));
......
/* Optimize by combining instructions for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GCC.
......@@ -2286,10 +2286,12 @@ struct rtx_subst_pair
substituted. */
static rtx
propagate_for_debug_subst (rtx from ATTRIBUTE_UNUSED, void *data)
propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
{
struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
if (!rtx_equal_p (from, old_rtx))
return NULL_RTX;
if (!pair->adjusted)
{
pair->adjusted = true;
......
......@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see
#include "target.h"
static bool cselib_record_memory;
static bool cselib_preserve_constants;
static int entry_and_rtx_equal_p (const void *, const void *);
static hashval_t get_value_hash (const void *);
static struct elt_list *new_elt_list (struct elt_list *, cselib_val *);
......@@ -135,6 +136,11 @@ static int values_became_useless;
presence in the list by checking the next pointer. */
static cselib_val dummy_val;
/* If non-NULL, value of the eliminated arg_pointer_rtx or frame_pointer_rtx
that is constant through the whole function and should never be
eliminated. */
static cselib_val *cfa_base_preserved_val;
/* Used to list all values that contain memory reference.
May or may not contain the useless values - the list is compacted
each time memory is invalidated. */
......@@ -229,6 +235,35 @@ cselib_clear_table (void)
cselib_reset_table (1);
}
/* Remove from hash table all VALUEs except constants. */
static int
preserve_only_constants (void **x, void *info ATTRIBUTE_UNUSED)
{
cselib_val *v = (cselib_val *)*x;
if (v->locs != NULL
&& v->locs->next == NULL)
{
if (CONSTANT_P (v->locs->loc)
&& (GET_CODE (v->locs->loc) != CONST
|| !references_value_p (v->locs->loc, 0)))
return 1;
if (cfa_base_preserved_val)
{
if (v == cfa_base_preserved_val)
return 1;
if (GET_CODE (v->locs->loc) == PLUS
&& CONST_INT_P (XEXP (v->locs->loc, 1))
&& XEXP (v->locs->loc, 0) == cfa_base_preserved_val->val_rtx)
return 1;
}
}
htab_clear_slot (cselib_hash_table, x);
return 1;
}
/* Remove all entries from the hash table, arranging for the next
value to be numbered NUM. */
......@@ -237,15 +272,37 @@ cselib_reset_table (unsigned int num)
{
unsigned int i;
for (i = 0; i < n_used_regs; i++)
REG_VALUES (used_regs[i]) = 0;
max_value_regs = 0;
n_used_regs = 0;
if (cfa_base_preserved_val)
{
unsigned int regno = REGNO (cfa_base_preserved_val->locs->loc);
unsigned int new_used_regs = 0;
for (i = 0; i < n_used_regs; i++)
if (used_regs[i] == regno)
{
new_used_regs = 1;
continue;
}
else
REG_VALUES (used_regs[i]) = 0;
gcc_assert (new_used_regs == 1);
n_used_regs = new_used_regs;
used_regs[0] = regno;
max_value_regs
= hard_regno_nregs[regno][GET_MODE (cfa_base_preserved_val->locs->loc)];
}
else
{
for (i = 0; i < n_used_regs; i++)
REG_VALUES (used_regs[i]) = 0;
n_used_regs = 0;
}
/* ??? Preserve constants? */
htab_empty (cselib_hash_table);
if (cselib_preserve_constants)
htab_traverse (cselib_hash_table, preserve_only_constants, NULL);
else
htab_empty (cselib_hash_table);
n_useless_values = 0;
......@@ -434,6 +491,18 @@ cselib_preserved_value_p (cselib_val *v)
return PRESERVED_VALUE_P (v->val_rtx);
}
/* Arrange for a REG value to be assumed constant through the whole function,
never invalidated and preserved across cselib_reset_table calls. */
void
cselib_preserve_cfa_base_value (cselib_val *v)
{
if (cselib_preserve_constants
&& v->locs
&& REG_P (v->locs->loc))
cfa_base_preserved_val = v;
}
/* Clean all non-constant expressions in the hash table, but retain
their values. */
......@@ -1600,7 +1669,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
if (i < FIRST_PSEUDO_REGISTER && v != NULL)
this_last = end_hard_regno (GET_MODE (v->val_rtx), i) - 1;
if (this_last < regno || v == NULL)
if (this_last < regno || v == NULL || v == cfa_base_preserved_val)
{
l = &(*l)->next;
continue;
......@@ -2018,7 +2087,7 @@ cselib_process_insn (rtx insn)
init_alias_analysis. */
void
cselib_init (bool record_memory)
cselib_init (int record_what)
{
elt_list_pool = create_alloc_pool ("elt_list",
sizeof (struct elt_list), 10);
......@@ -2027,7 +2096,8 @@ cselib_init (bool record_memory)
cselib_val_pool = create_alloc_pool ("cselib_val_list",
sizeof (cselib_val), 10);
value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100);
cselib_record_memory = record_memory;
cselib_record_memory = record_what & CSELIB_RECORD_MEMORY;
cselib_preserve_constants = record_what & CSELIB_PRESERVE_CONSTANTS;
/* (mem:BLK (scratch)) is a special mechanism to conflict with everything,
see canon_true_dependence. This is only created once. */
......@@ -2061,6 +2131,8 @@ void
cselib_finish (void)
{
cselib_discard_hook = NULL;
cselib_preserve_constants = false;
cfa_base_preserved_val = NULL;
free_alloc_pool (elt_list_pool);
free_alloc_pool (elt_loc_list_pool);
free_alloc_pool (cselib_val_pool);
......
......@@ -66,12 +66,18 @@ struct cselib_set
cselib_val *dest_addr_elt;
};
enum cselib_record_what
{
CSELIB_RECORD_MEMORY = 1,
CSELIB_PRESERVE_CONSTANTS = 2
};
extern void (*cselib_discard_hook) (cselib_val *);
extern void (*cselib_record_sets_hook) (rtx insn, struct cselib_set *sets,
int n_sets);
extern cselib_val *cselib_lookup (rtx, enum machine_mode, int);
extern void cselib_init (bool record_memory);
extern void cselib_init (int);
extern void cselib_clear_table (void);
extern void cselib_finish (void);
extern void cselib_process_insn (rtx);
......@@ -92,5 +98,6 @@ extern unsigned int cselib_get_next_uid (void);
extern void cselib_preserve_value (cselib_val *);
extern bool cselib_preserved_value_p (cselib_val *);
extern void cselib_preserve_only_values (void);
extern void cselib_preserve_cfa_base_value (cselib_val *);
extern void dump_cselib_table (FILE *);
/* RTL dead store elimination.
Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by Richard Sandiford <rsandifor@codesourcery.com>
and Kenneth Zadeck <zadeck@naturalbridge.com>
......@@ -2616,7 +2617,7 @@ dse_step1 (void)
basic_block bb;
bitmap regs_live = BITMAP_ALLOC (NULL);
cselib_init (false);
cselib_init (0);
all_blocks = BITMAP_ALLOC (NULL);
bitmap_set_bit (all_blocks, ENTRY_BLOCK);
bitmap_set_bit (all_blocks, EXIT_BLOCK);
......
/* Global common subexpression elimination/Partial redundancy elimination
and global constant/copy propagation for GNU compiler.
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GCC.
......@@ -2724,7 +2724,7 @@ local_cprop_pass (void)
struct reg_use *reg_used;
bool changed = false;
cselib_init (false);
cselib_init (0);
FOR_EACH_BB (bb)
{
FOR_BB_INSNS (bb, insn)
......
/* Perform simple optimizations to clean up the result of reload.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GCC.
......@@ -198,7 +198,7 @@ reload_cse_regs_1 (rtx first)
rtx insn;
rtx testreg = gen_rtx_REG (VOIDmode, -1);
cselib_init (true);
cselib_init (CSELIB_RECORD_MEMORY);
init_alias_analysis ();
for (insn = first; insn; insn = NEXT_INSN (insn))
......
......@@ -1778,7 +1778,7 @@ extern rtx simplify_subreg (enum machine_mode, rtx, enum machine_mode,
extern rtx simplify_gen_subreg (enum machine_mode, rtx, enum machine_mode,
unsigned int);
extern rtx simplify_replace_fn_rtx (rtx, const_rtx,
rtx (*fn) (rtx, void *), void *);
rtx (*fn) (rtx, const_rtx, void *), void *);
extern rtx simplify_replace_rtx (rtx, const_rtx, rtx);
extern rtx simplify_rtx (const_rtx);
extern rtx avoid_constant_pool_reference (rtx);
......
/* Instruction scheduling pass. This file computes dependencies between
instructions.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by,
and currently maintained by, Jim Wilson (wilson@cygnus.com)
......@@ -3383,7 +3383,7 @@ sched_analyze (struct deps *deps, rtx head, rtx tail)
rtx insn;
if (sched_deps_info->use_cselib)
cselib_init (true);
cselib_init (CSELIB_RECORD_MEMORY);
deps_start_bb (deps, head);
......
......@@ -350,15 +350,14 @@ simplify_gen_relational (enum rtx_code code, enum machine_mode mode,
return gen_rtx_fmt_ee (code, mode, op0, op1);
}
/* Replace all occurrences of OLD_RTX in X with FN (X', DATA), where X'
is an expression in X that is equal to OLD_RTX. Canonicalize and
simplify the result.
If FN is null, assume FN (X', DATA) == copy_rtx (DATA). */
/* If FN is NULL, replace all occurrences of OLD_RTX in X with copy_rtx (DATA)
and simplify the result. If FN is non-NULL, call this callback on each
X, if it returns non-NULL, replace X with its return value and simplify the
result. */
rtx
simplify_replace_fn_rtx (rtx x, const_rtx old_rtx,
rtx (*fn) (rtx, void *), void *data)
rtx (*fn) (rtx, const_rtx, void *), void *data)
{
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
......@@ -368,17 +367,14 @@ simplify_replace_fn_rtx (rtx x, const_rtx old_rtx,
rtvec vec, newvec;
int i, j;
/* If X is OLD_RTX, return FN (X, DATA), with a null FN. Otherwise,
if this is an expression, try to build a new expression, substituting
recursively. If we can't do anything, return our input. */
if (rtx_equal_p (x, old_rtx))
if (__builtin_expect (fn != NULL, 0))
{
if (fn)
return fn (x, data);
else
return copy_rtx ((rtx) data);
newx = fn (x, old_rtx, data);
if (newx)
return newx;
}
else if (rtx_equal_p (x, old_rtx))
return copy_rtx ((rtx) data);
switch (GET_RTX_CLASS (code))
{
......
2010-03-16 Jakub Jelinek <jakub@redhat.com>
PR debug/43051
* gcc.dg/guality/pr43051-1.c: New test.
2010-03-15 Janis Johnson <janis187@us.ibm.com>
PR testsuite/43363
......
/* PR debug/43051 */
/* { dg-do run } */
/* { dg-options "-g" } */
extern void abort (void);
static void __attribute__ ((noinline))
foo (const char *x, long long y, int z)
{
asm volatile ("" : : "r" (x), "r" ((int) y), "r" (z) : "memory");
}
struct S
{
struct S *n;
int v;
};
struct S a[10];
struct S * __attribute__ ((noinline))
bar (struct S *c, int v, struct S *e)
{
#ifdef __i386__
register int si asm ("esi"), di asm ("edi"), bx
# if !defined (__pic__) && !defined (__APPLE__)
asm ("ebx")
# endif
;
asm volatile ("" : "=r" (si), "=r" (di), "=r" (bx));
#endif
while (c < e)
{
foo ("c", (__UINTPTR_TYPE__) c, 0); /* { dg-final { gdb-test 34 "c" "\&a\[0\]" } } */
foo ("v", v, 1); /* { dg-final { gdb-test 35 "v" "1" } } */
foo ("e", (__UINTPTR_TYPE__) e, 2); /* { dg-final { gdb-test 36 "e" "\&a\[1\]" } } */
if (c->v == v)
return c;
foo ("c", (__UINTPTR_TYPE__) c, 3); /* { dg-final { gdb-test 39 "c" "\&a\[0\]" } } */
foo ("v", v, 4); /* { dg-final { gdb-test 40 "v" "1" } } */
foo ("e", (__UINTPTR_TYPE__) e, 5); /* { dg-final { gdb-test 41 "e" "\&a\[1\]" } } */
c++;
}
#ifdef __i386__
asm volatile ("" : : "r" (si), "r" (di), "r" (bx));
#endif
return 0;
}
int
main ()
{
asm volatile ("" : : "r" (&a[0]) : "memory");
if (bar (&a[a[0].v], a[0].v + 1, &a[a[0].v + 1]))
abort ();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment